Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/html/nsGenericHTMLElement.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "mozilla/ArrayUtils.h"
8
#include "mozilla/DeclarationBlock.h"
9
#include "mozilla/EventDispatcher.h"
10
#include "mozilla/EventListenerManager.h"
11
#include "mozilla/EventStateManager.h"
12
#include "mozilla/EventStates.h"
13
#include "mozilla/MappedDeclarations.h"
14
#include "mozilla/Likely.h"
15
#include "mozilla/MouseEvents.h"
16
#include "mozilla/TextEditor.h"
17
18
#include "nscore.h"
19
#include "nsGenericHTMLElement.h"
20
#include "nsAttrValueInlines.h"
21
#include "nsCOMPtr.h"
22
#include "nsAtom.h"
23
#include "nsQueryObject.h"
24
#include "nsIContentInlines.h"
25
#include "nsIContentViewer.h"
26
#include "nsIDocument.h"
27
#include "nsIDocumentEncoder.h"
28
#include "nsIDOMWindow.h"
29
#include "nsMappedAttributes.h"
30
#include "nsHTMLStyleSheet.h"
31
#include "nsIHTMLDocument.h"
32
#include "nsPIDOMWindow.h"
33
#include "nsIURL.h"
34
#include "nsEscape.h"
35
#include "nsIFrameInlines.h"
36
#include "nsIScrollableFrame.h"
37
#include "nsView.h"
38
#include "nsViewManager.h"
39
#include "nsIWidget.h"
40
#include "nsRange.h"
41
#include "nsIPresShell.h"
42
#include "nsPresContext.h"
43
#include "nsIDocShell.h"
44
#include "nsNameSpaceManager.h"
45
#include "nsError.h"
46
#include "nsIPrincipal.h"
47
#include "nsContainerFrame.h"
48
#include "nsStyleUtil.h"
49
50
#include "mozilla/PresState.h"
51
#include "nsILayoutHistoryState.h"
52
53
#include "nsHTMLParts.h"
54
#include "nsContentUtils.h"
55
#include "mozilla/dom/DirectionalityUtils.h"
56
#include "mozilla/dom/DocumentOrShadowRoot.h"
57
#include "nsString.h"
58
#include "nsUnicharUtils.h"
59
#include "nsGkAtoms.h"
60
#include "nsDOMCSSDeclaration.h"
61
#include "nsITextControlFrame.h"
62
#include "nsIForm.h"
63
#include "nsIFormControl.h"
64
#include "mozilla/dom/HTMLFormElement.h"
65
#include "nsFocusManager.h"
66
#include "nsAttrValueOrString.h"
67
68
#include "mozilla/InternalMutationEvent.h"
69
#include "nsDOMStringMap.h"
70
71
#include "nsLayoutUtils.h"
72
#include "mozAutoDocUpdate.h"
73
#include "nsHtml5Module.h"
74
#include "nsITextControlElement.h"
75
#include "mozilla/dom/ElementInlines.h"
76
#include "HTMLFieldSetElement.h"
77
#include "nsTextNode.h"
78
#include "HTMLBRElement.h"
79
#include "HTMLMenuElement.h"
80
#include "nsDOMMutationObserver.h"
81
#include "mozilla/Preferences.h"
82
#include "mozilla/dom/FromParser.h"
83
#include "mozilla/dom/Link.h"
84
#include "mozilla/BloomFilter.h"
85
#include "mozilla/dom/ScriptLoader.h"
86
87
#include "nsVariant.h"
88
#include "nsDOMTokenList.h"
89
#include "nsThreadUtils.h"
90
#include "nsTextFragment.h"
91
#include "mozilla/dom/BindingUtils.h"
92
#include "mozilla/dom/MouseEventBinding.h"
93
#include "mozilla/dom/TouchEvent.h"
94
#include "mozilla/ErrorResult.h"
95
#include "nsHTMLDocument.h"
96
#include "nsGlobalWindow.h"
97
#include "mozilla/dom/HTMLBodyElement.h"
98
#include "imgIContainer.h"
99
#include "nsComputedDOMStyle.h"
100
#include "ReferrerPolicy.h"
101
#include "mozilla/dom/HTMLLabelElement.h"
102
#include "mozilla/dom/HTMLInputElement.h"
103
104
using namespace mozilla;
105
using namespace mozilla::dom;
106
107
nsresult
108
nsGenericHTMLElement::CopyInnerTo(Element* aDst)
109
0
{
110
0
  MOZ_ASSERT(!aDst->GetUncomposedDoc(),
111
0
             "Should not CopyInnerTo an Element in a document");
112
0
113
0
  bool reparse = (aDst->OwnerDoc() != OwnerDoc());
114
0
115
0
  nsresult rv =
116
0
    static_cast<nsGenericHTMLElement*>(aDst)->mAttrs.EnsureCapacityToClone(mAttrs);
117
0
  NS_ENSURE_SUCCESS(rv, rv);
118
0
119
0
  int32_t i, count = GetAttrCount();
120
0
  for (i = 0; i < count; ++i) {
121
0
    const nsAttrName *name = mAttrs.AttrNameAt(i);
122
0
    const nsAttrValue *value = mAttrs.AttrAt(i);
123
0
124
0
    if (name->Equals(nsGkAtoms::style, kNameSpaceID_None) &&
125
0
        value->Type() == nsAttrValue::eCSSDeclaration) {
126
0
      // We still clone CSS attributes, even in the cross-document case.
127
0
      // https://github.com/w3c/webappsec-csp/issues/212
128
0
129
0
      // We can't just set this as a string, because that will fail
130
0
      // to reparse the string into style data until the node is
131
0
      // inserted into the document.  Clone the Rule instead.
132
0
      nsAttrValue valueCopy(*value);
133
0
      rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(),
134
0
                               name->GetPrefix(), valueCopy, false);
135
0
      NS_ENSURE_SUCCESS(rv, rv);
136
0
137
0
      DeclarationBlock* cssDeclaration = value->GetCSSDeclarationValue();
138
0
      cssDeclaration->SetImmutable();
139
0
    } else if (reparse) {
140
0
      nsAutoString valStr;
141
0
      value->ToString(valStr);
142
0
143
0
      rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
144
0
                         name->GetPrefix(), valStr, false);
145
0
      NS_ENSURE_SUCCESS(rv, rv);
146
0
    } else {
147
0
      nsAttrValue valueCopy(*value);
148
0
      rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(),
149
0
                               name->GetPrefix(), valueCopy, false);
150
0
      NS_ENSURE_SUCCESS(rv, rv);
151
0
    }
152
0
  }
153
0
154
0
  return NS_OK;
155
0
}
156
157
static const nsAttrValue::EnumTable kDirTable[] = {
158
  { "ltr", eDir_LTR },
159
  { "rtl", eDir_RTL },
160
  { "auto", eDir_Auto },
161
  { nullptr, 0 }
162
};
163
164
void
165
nsGenericHTMLElement::AddToNameTable(nsAtom* aName)
166
0
{
167
0
  MOZ_ASSERT(HasName(), "Node doesn't have name?");
168
0
  nsIDocument* doc = GetUncomposedDoc();
169
0
  if (doc && !IsInAnonymousSubtree()) {
170
0
    doc->AddToNameTable(this, aName);
171
0
  }
172
0
}
173
174
void
175
nsGenericHTMLElement::RemoveFromNameTable()
176
0
{
177
0
  if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
178
0
    if (nsIDocument* doc = GetUncomposedDoc()) {
179
0
      doc->RemoveFromNameTable(this,
180
0
                               GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
181
0
    }
182
0
  }
183
0
}
184
185
void
186
nsGenericHTMLElement::GetAccessKeyLabel(nsString& aLabel)
187
0
{
188
0
  nsAutoString suffix;
189
0
  GetAccessKey(suffix);
190
0
  if (!suffix.IsEmpty()) {
191
0
    EventStateManager::GetAccessKeyLabelPrefix(this, aLabel);
192
0
    aLabel.Append(suffix);
193
0
  }
194
0
}
195
196
static bool
197
IsTableCell(LayoutFrameType frameType)
198
0
{
199
0
  return LayoutFrameType::TableCell == frameType ||
200
0
         LayoutFrameType::BCTableCell == frameType;
201
0
}
202
203
static bool
204
IsOffsetParent(nsIFrame* aFrame)
205
0
{
206
0
  LayoutFrameType frameType = aFrame->Type();
207
0
208
0
  if (IsTableCell(frameType) || frameType == LayoutFrameType::Table) {
209
0
    // Per the IDL for Element, only td, th, and table are acceptable offsetParents
210
0
    // apart from body or positioned elements; we need to check the content type as
211
0
    // well as the frame type so we ignore anonymous tables created by an element
212
0
    // with display: table-cell with no actual table
213
0
    nsIContent* content = aFrame->GetContent();
214
0
215
0
    return content->IsAnyOfHTMLElements(nsGkAtoms::table,
216
0
                                        nsGkAtoms::td,
217
0
                                        nsGkAtoms::th);
218
0
  }
219
0
  return false;
220
0
}
221
222
Element*
223
nsGenericHTMLElement::GetOffsetRect(CSSIntRect& aRect)
224
0
{
225
0
  aRect = CSSIntRect();
226
0
227
0
  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
228
0
  if (!frame) {
229
0
    return nullptr;
230
0
  }
231
0
232
0
  nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(frame);
233
0
234
0
  nsIFrame* parent = frame->GetParent();
235
0
  nsPoint origin(0, 0);
236
0
237
0
  nsIContent* offsetParent = nullptr;
238
0
  Element* docElement = GetComposedDoc()->GetRootElement();
239
0
  nsIContent* content = frame->GetContent();
240
0
241
0
  if (content && (content->IsHTMLElement(nsGkAtoms::body) ||
242
0
                  content == docElement)) {
243
0
    parent = frame;
244
0
  }
245
0
  else {
246
0
    const bool isPositioned = styleFrame->IsAbsPosContainingBlock();
247
0
    const bool isAbsolutelyPositioned = styleFrame->IsAbsolutelyPositioned();
248
0
    origin += frame->GetPositionIgnoringScrolling();
249
0
250
0
    for ( ; parent ; parent = parent->GetParent()) {
251
0
      content = parent->GetContent();
252
0
253
0
      // Stop at the first ancestor that is positioned.
254
0
      if (parent->IsAbsPosContainingBlock()) {
255
0
        offsetParent = content;
256
0
        break;
257
0
      }
258
0
259
0
      // Add the parent's origin to our own to get to the
260
0
      // right coordinate system.
261
0
      const bool isOffsetParent = !isPositioned && IsOffsetParent(parent);
262
0
      if (!isOffsetParent) {
263
0
        origin += parent->GetPositionIgnoringScrolling();
264
0
      }
265
0
266
0
      if (content) {
267
0
        // If we've hit the document element, break here.
268
0
        if (content == docElement) {
269
0
          break;
270
0
        }
271
0
272
0
        // Break if the ancestor frame type makes it suitable as offset parent
273
0
        // and this element is *not* positioned or if we found the body element.
274
0
        if (isOffsetParent || content->IsHTMLElement(nsGkAtoms::body)) {
275
0
          offsetParent = content;
276
0
          break;
277
0
        }
278
0
      }
279
0
    }
280
0
281
0
    if (isAbsolutelyPositioned && !offsetParent) {
282
0
      // If this element is absolutely positioned, but we don't have
283
0
      // an offset parent it means this element is an absolutely
284
0
      // positioned child that's not nested inside another positioned
285
0
      // element, in this case the element's frame's parent is the
286
0
      // frame for the HTML element so we fail to find the body in the
287
0
      // parent chain. We want the offset parent in this case to be
288
0
      // the body, so we just get the body element from the document.
289
0
      //
290
0
      // We use GetBodyElement() here, not GetBody(), because we don't want to
291
0
      // end up with framesets here.
292
0
      offsetParent = GetComposedDoc()->GetBodyElement();
293
0
    }
294
0
  }
295
0
296
0
  // Subtract the parent border unless it uses border-box sizing.
297
0
  if (parent &&
298
0
      parent->StylePosition()->mBoxSizing != StyleBoxSizing::Border) {
299
0
    const nsStyleBorder* border = parent->StyleBorder();
300
0
    origin.x -= border->GetComputedBorderWidth(eSideLeft);
301
0
    origin.y -= border->GetComputedBorderWidth(eSideTop);
302
0
  }
303
0
304
0
  // XXX We should really consider subtracting out padding for
305
0
  // content-box sizing, but we should see what IE does....
306
0
307
0
  // Get the union of all rectangles in this and continuation frames.
308
0
  // It doesn't really matter what we use as aRelativeTo here, since
309
0
  // we only care about the size. We just have to use something non-null.
310
0
  nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, frame);
311
0
  rcFrame.MoveTo(origin);
312
0
  aRect = CSSIntRect::FromAppUnitsRounded(rcFrame);
313
0
314
0
  return offsetParent ? offsetParent->AsElement() : nullptr;
315
0
}
316
317
bool
318
nsGenericHTMLElement::Spellcheck()
319
0
{
320
0
  // Has the state has been explicitly set?
321
0
  nsIContent* node;
322
0
  for (node = this; node; node = node->GetParent()) {
323
0
    if (node->IsHTMLElement()) {
324
0
      static Element::AttrValuesArray strings[] =
325
0
        {&nsGkAtoms::_true, &nsGkAtoms::_false, nullptr};
326
0
      switch (node->AsElement()->FindAttrValueIn(kNameSpaceID_None,
327
0
                                                 nsGkAtoms::spellcheck, strings,
328
0
                                                 eCaseMatters)) {
329
0
        case 0:                         // spellcheck = "true"
330
0
          return true;
331
0
        case 1:                         // spellcheck = "false"
332
0
          return false;
333
0
      }
334
0
    }
335
0
  }
336
0
337
0
  // contenteditable/designMode are spellchecked by default
338
0
  if (IsEditable()) {
339
0
    return true;
340
0
  }
341
0
342
0
  // Is this a chrome element?
343
0
  if (nsContentUtils::IsChromeDoc(OwnerDoc())) {
344
0
    return false;                       // Not spellchecked by default
345
0
  }
346
0
347
0
  // Anything else that's not a form control is not spellchecked by default
348
0
  nsCOMPtr<nsIFormControl> formControl = do_QueryObject(this);
349
0
  if (!formControl) {
350
0
    return false;                       // Not spellchecked by default
351
0
  }
352
0
353
0
  // Is this a multiline plaintext input?
354
0
  int32_t controlType = formControl->ControlType();
355
0
  if (controlType == NS_FORM_TEXTAREA) {
356
0
    return true;             // Spellchecked by default
357
0
  }
358
0
359
0
  // Is this anything other than an input text?
360
0
  // Other inputs are not spellchecked.
361
0
  if (controlType != NS_FORM_INPUT_TEXT) {
362
0
    return false;                       // Not spellchecked by default
363
0
  }
364
0
365
0
  // Does the user want input text spellchecked by default?
366
0
  // NOTE: Do not reflect a pref value of 0 back to the DOM getter.
367
0
  // The web page should not know if the user has disabled spellchecking.
368
0
  // We'll catch this in the editor itself.
369
0
  int32_t spellcheckLevel = Preferences::GetInt("layout.spellcheckDefault", 1);
370
0
  return spellcheckLevel == 2;           // "Spellcheck multi- and single-line"
371
0
}
372
373
bool
374
nsGenericHTMLElement::InNavQuirksMode(nsIDocument* aDoc)
375
0
{
376
0
  return aDoc && aDoc->GetCompatibilityMode() == eCompatibility_NavQuirks;
377
0
}
378
379
void
380
nsGenericHTMLElement::UpdateEditableState(bool aNotify)
381
0
{
382
0
  // XXX Should we do this only when in a document?
383
0
  ContentEditableTristate value = GetContentEditableValue();
384
0
  if (value != eInherit) {
385
0
    DoSetEditableFlag(!!value, aNotify);
386
0
    return;
387
0
  }
388
0
389
0
  nsStyledElement::UpdateEditableState(aNotify);
390
0
}
391
392
EventStates
393
nsGenericHTMLElement::IntrinsicState() const
394
0
{
395
0
  EventStates state = nsGenericHTMLElementBase::IntrinsicState();
396
0
397
0
  if (GetDirectionality() == eDir_RTL) {
398
0
    state |= NS_EVENT_STATE_RTL;
399
0
    state &= ~NS_EVENT_STATE_LTR;
400
0
  } else { // at least for HTML, directionality is exclusively LTR or RTL
401
0
    NS_ASSERTION(GetDirectionality() == eDir_LTR,
402
0
                 "HTML element's directionality must be either RTL or LTR");
403
0
    state |= NS_EVENT_STATE_LTR;
404
0
    state &= ~NS_EVENT_STATE_RTL;
405
0
  }
406
0
407
0
  return state;
408
0
}
409
410
uint32_t
411
nsGenericHTMLElement::EditableInclusiveDescendantCount()
412
0
{
413
0
  bool isEditable = IsInComposedDoc() && HasFlag(NODE_IS_EDITABLE) &&
414
0
    GetContentEditableValue() == eTrue;
415
0
  return EditableDescendantCount() + isEditable;
416
0
}
417
418
nsresult
419
nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
420
                                 nsIContent* aBindingParent)
421
0
{
422
0
  nsresult rv = nsGenericHTMLElementBase::BindToTree(aDocument, aParent,
423
0
                                                     aBindingParent);
424
0
  NS_ENSURE_SUCCESS(rv, rv);
425
0
426
0
  if (aDocument) {
427
0
    RegAccessKey();
428
0
    if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
429
0
      aDocument->
430
0
        AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
431
0
    }
432
0
  }
433
0
434
0
  if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue &&
435
0
      IsInComposedDoc()) {
436
0
    nsCOMPtr<nsIHTMLDocument> htmlDocument =
437
0
      do_QueryInterface(GetComposedDoc());
438
0
    if (htmlDocument) {
439
0
      htmlDocument->ChangeContentEditableCount(this, +1);
440
0
    }
441
0
  }
442
0
443
0
  // We need to consider a labels element is moved to another subtree
444
0
  // with different root, it needs to update labels list and its root
445
0
  // as well.
446
0
  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
447
0
  if (slots && slots->mLabelsList) {
448
0
    slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
449
0
  }
450
0
451
0
  return rv;
452
0
}
453
454
void
455
nsGenericHTMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
456
0
{
457
0
  if (IsInUncomposedDoc()) {
458
0
    UnregAccessKey();
459
0
  }
460
0
461
0
  RemoveFromNameTable();
462
0
463
0
  if (GetContentEditableValue() == eTrue) {
464
0
    nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetComposedDoc());
465
0
    if (htmlDocument) {
466
0
      htmlDocument->ChangeContentEditableCount(this, -1);
467
0
    }
468
0
  }
469
0
470
0
  nsStyledElement::UnbindFromTree(aDeep, aNullParent);
471
0
472
0
  // Invalidate .labels list. It will be repopulated when used the next time.
473
0
  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
474
0
  if (slots && slots->mLabelsList) {
475
0
    slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
476
0
  }
477
0
}
478
479
HTMLFormElement*
480
nsGenericHTMLElement::FindAncestorForm(HTMLFormElement* aCurrentForm)
481
0
{
482
0
  NS_ASSERTION(!HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
483
0
               IsHTMLElement(nsGkAtoms::img),
484
0
               "FindAncestorForm should not be called if @form is set!");
485
0
486
0
  // Make sure we don't end up finding a form that's anonymous from
487
0
  // our point of view. See also nsGenericHTMLFormElement::UpdateFieldSet.
488
0
  nsIContent* bindingParent = GetBindingParent();
489
0
490
0
  nsIContent* content = this;
491
0
  while (content != bindingParent && content) {
492
0
    // If the current ancestor is a form, return it as our form
493
0
    if (content->IsHTMLElement(nsGkAtoms::form)) {
494
#ifdef DEBUG
495
      if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
496
        // It's possible that we started unbinding at |content| or
497
        // some ancestor of it, and |content| and |this| used to all be
498
        // anonymous.  Check for this the hard way.
499
        for (nsIContent* child = this; child != content;
500
             child = child->GetParent()) {
501
          NS_ASSERTION(child->GetParent()->ComputeIndexOf(child) != -1,
502
                       "Walked too far?");
503
        }
504
      }
505
#endif
506
      return static_cast<HTMLFormElement*>(content);
507
0
    }
508
0
509
0
    nsIContent *prevContent = content;
510
0
    content = prevContent->GetParent();
511
0
512
0
    if (!content && aCurrentForm) {
513
0
      // We got to the root of the subtree we're in, and we're being removed
514
0
      // from the DOM (the only time we get into this method with a non-null
515
0
      // aCurrentForm).  Check whether aCurrentForm is in the same subtree.  If
516
0
      // it is, we want to return aCurrentForm, since this case means that
517
0
      // we're one of those inputs-in-a-table that have a hacked mForm pointer
518
0
      // and a subtree containing both us and the form got removed from the
519
0
      // DOM.
520
0
      if (nsContentUtils::ContentIsDescendantOf(aCurrentForm, prevContent)) {
521
0
        return aCurrentForm;
522
0
      }
523
0
    }
524
0
  }
525
0
526
0
  return nullptr;
527
0
}
528
529
bool
530
nsGenericHTMLElement::CheckHandleEventForAnchorsPreconditions(
531
                        EventChainVisitor& aVisitor)
532
0
{
533
0
  MOZ_ASSERT(nsCOMPtr<Link>(do_QueryObject(this)),
534
0
             "should be called only when |this| implements |Link|");
535
0
536
0
  if (!aVisitor.mPresContext) {
537
0
    // We need a pres context to do link stuff. Some events (e.g. mutation
538
0
    // events) don't have one.
539
0
    // XXX: ideally, shouldn't we be able to do what we need without one?
540
0
    return false;
541
0
  }
542
0
543
0
  //Need to check if we hit an imagemap area and if so see if we're handling
544
0
  //the event on that map or on a link farther up the tree.  If we're on a
545
0
  //link farther up, do nothing.
546
0
  nsCOMPtr<nsIContent> target = aVisitor.mPresContext->EventStateManager()->
547
0
    GetEventTargetContent(aVisitor.mEvent);
548
0
549
0
  return !target || !target->IsHTMLElement(nsGkAtoms::area) ||
550
0
         IsHTMLElement(nsGkAtoms::area);
551
0
}
552
553
void
554
nsGenericHTMLElement::GetEventTargetParentForAnchors(EventChainPreVisitor& aVisitor)
555
0
{
556
0
  nsGenericHTMLElementBase::GetEventTargetParent(aVisitor);
557
0
558
0
  if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) {
559
0
    return;
560
0
  }
561
0
562
0
  GetEventTargetParentForLinks(aVisitor);
563
0
}
564
565
nsresult
566
nsGenericHTMLElement::PostHandleEventForAnchors(EventChainPostVisitor& aVisitor)
567
0
{
568
0
  if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) {
569
0
    return NS_OK;
570
0
  }
571
0
572
0
  return PostHandleEventForLinks(aVisitor);
573
0
}
574
575
bool
576
nsGenericHTMLElement::IsHTMLLink(nsIURI** aURI) const
577
0
{
578
0
  MOZ_ASSERT(aURI, "Must provide aURI out param");
579
0
580
0
  *aURI = GetHrefURIForAnchors().take();
581
0
  // We promise out param is non-null if we return true, so base rv on it
582
0
  return *aURI != nullptr;
583
0
}
584
585
already_AddRefed<nsIURI>
586
nsGenericHTMLElement::GetHrefURIForAnchors() const
587
0
{
588
0
  // This is used by the three Link implementations and
589
0
  // nsHTMLStyleElement.
590
0
591
0
  // Get href= attribute (relative URI).
592
0
593
0
  // We use the nsAttrValue's copy of the URI string to avoid copying.
594
0
  nsCOMPtr<nsIURI> uri;
595
0
  GetURIAttr(nsGkAtoms::href, nullptr, getter_AddRefs(uri));
596
0
597
0
  return uri.forget();
598
0
}
599
600
nsresult
601
nsGenericHTMLElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
602
                                    const nsAttrValueOrString* aValue,
603
                                    bool aNotify)
604
0
{
605
0
  if (aNamespaceID == kNameSpaceID_None) {
606
0
    if (aName == nsGkAtoms::accesskey) {
607
0
      // Have to unregister before clearing flag. See UnregAccessKey
608
0
      UnregAccessKey();
609
0
      if (!aValue) {
610
0
        UnsetFlags(NODE_HAS_ACCESSKEY);
611
0
      }
612
0
    } else if (aName == nsGkAtoms::name) {
613
0
      // Have to do this before clearing flag. See RemoveFromNameTable
614
0
      RemoveFromNameTable();
615
0
      if (!aValue || aValue->IsEmpty()) {
616
0
        ClearHasName();
617
0
      }
618
0
    } else if (aName == nsGkAtoms::contenteditable) {
619
0
      if (aValue) {
620
0
        // Set this before the attribute is set so that any subclass code that
621
0
        // runs before the attribute is set won't think we're missing a
622
0
        // contenteditable attr when we actually have one.
623
0
        SetMayHaveContentEditableAttr();
624
0
      }
625
0
    }
626
0
    if (!aValue && IsEventAttributeName(aName)) {
627
0
      if (EventListenerManager* manager = GetExistingListenerManager()) {
628
0
        manager->RemoveEventHandler(aName);
629
0
      }
630
0
    }
631
0
  }
632
0
633
0
  return nsGenericHTMLElementBase::BeforeSetAttr(aNamespaceID, aName, aValue,
634
0
                                                 aNotify);
635
0
}
636
637
nsresult
638
nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
639
                                   const nsAttrValue* aValue,
640
                                   const nsAttrValue* aOldValue,
641
                                   nsIPrincipal* aMaybeScriptedPrincipal,
642
                                   bool aNotify)
643
0
{
644
0
  if (aNamespaceID == kNameSpaceID_None) {
645
0
    if (IsEventAttributeName(aName) && aValue) {
646
0
      MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
647
0
                 "Expected string value for script body");
648
0
      nsresult rv = SetEventHandler(aName, aValue->GetStringValue());
649
0
      NS_ENSURE_SUCCESS(rv, rv);
650
0
    }
651
0
    else if (aNotify && aName == nsGkAtoms::spellcheck) {
652
0
      SyncEditorsOnSubtree(this);
653
0
    }
654
0
    else if (aName == nsGkAtoms::dir) {
655
0
      Directionality dir = eDir_LTR;
656
0
      // A boolean tracking whether we need to recompute our directionality.
657
0
      // This needs to happen after we update our internal "dir" attribute
658
0
      // state but before we call SetDirectionalityOnDescendants.
659
0
      bool recomputeDirectionality = false;
660
0
      // We don't want to have to keep getting the "dir" attribute in
661
0
      // IntrinsicState, so we manually recompute our dir-related event states
662
0
      // here and send the relevant update notifications.
663
0
      EventStates dirStates;
664
0
      if (aValue && aValue->Type() == nsAttrValue::eEnum) {
665
0
        SetHasValidDir();
666
0
        dirStates |= NS_EVENT_STATE_HAS_DIR_ATTR;
667
0
        Directionality dirValue = (Directionality)aValue->GetEnumValue();
668
0
        if (dirValue == eDir_Auto) {
669
0
          dirStates |= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO;
670
0
        } else {
671
0
          dir = dirValue;
672
0
          SetDirectionality(dir, aNotify);
673
0
          if (dirValue == eDir_LTR) {
674
0
            dirStates |= NS_EVENT_STATE_DIR_ATTR_LTR;
675
0
          } else {
676
0
            MOZ_ASSERT(dirValue == eDir_RTL);
677
0
            dirStates |= NS_EVENT_STATE_DIR_ATTR_RTL;
678
0
          }
679
0
        }
680
0
      } else {
681
0
        if (aValue) {
682
0
          // We have a value, just not a valid one.
683
0
          dirStates |= NS_EVENT_STATE_HAS_DIR_ATTR;
684
0
        }
685
0
        ClearHasValidDir();
686
0
        if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
687
0
          dirStates |= NS_EVENT_STATE_DIR_ATTR_LIKE_AUTO;
688
0
        } else {
689
0
          recomputeDirectionality = true;
690
0
        }
691
0
      }
692
0
      // Now figure out what's changed about our dir states.
693
0
      EventStates oldDirStates = State() & DIR_ATTR_STATES;
694
0
      EventStates changedStates = dirStates ^ oldDirStates;
695
0
      ToggleStates(changedStates, aNotify);
696
0
      if (recomputeDirectionality) {
697
0
        dir = RecomputeDirectionality(this, aNotify);
698
0
      }
699
0
      SetDirectionalityOnDescendants(this, dir, aNotify);
700
0
    } else if (aName == nsGkAtoms::contenteditable) {
701
0
      int32_t editableCountDelta = 0;
702
0
      if (aOldValue &&
703
0
          (aOldValue->Equals(NS_LITERAL_STRING("true"), eIgnoreCase) ||
704
0
          aOldValue->Equals(EmptyString(), eIgnoreCase))) {
705
0
        editableCountDelta = -1;
706
0
      }
707
0
      if (aValue && (aValue->Equals(NS_LITERAL_STRING("true"), eIgnoreCase) ||
708
0
                     aValue->Equals(EmptyString(), eIgnoreCase))) {
709
0
        ++editableCountDelta;
710
0
      }
711
0
      ChangeEditableState(editableCountDelta);
712
0
    } else if (aName == nsGkAtoms::accesskey) {
713
0
      if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase)) {
714
0
        SetFlags(NODE_HAS_ACCESSKEY);
715
0
        RegAccessKey();
716
0
      }
717
0
    } else if (aName == nsGkAtoms::name) {
718
0
      if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase)) {
719
0
        // This may not be quite right because we can have subclass code run
720
0
        // before here. But in practice subclasses don't care about this flag,
721
0
        // and in particular selector matching does not care.  Otherwise we'd
722
0
        // want to handle it like we handle id attributes (in PreIdMaybeChange
723
0
        // and PostIdMaybeChange).
724
0
        SetHasName();
725
0
        if (CanHaveName(NodeInfo()->NameAtom())) {
726
0
          AddToNameTable(aValue->GetAtomValue());
727
0
        }
728
0
      }
729
0
    }
730
0
  }
731
0
732
0
  return nsGenericHTMLElementBase::AfterSetAttr(aNamespaceID, aName,
733
0
                                                aValue, aOldValue,
734
0
                                                aMaybeScriptedPrincipal,
735
0
                                                aNotify);
736
0
}
737
738
EventListenerManager*
739
nsGenericHTMLElement::GetEventListenerManagerForAttr(nsAtom* aAttrName,
740
                                                     bool* aDefer)
741
0
{
742
0
  // Attributes on the body and frameset tags get set on the global object
743
0
  if ((mNodeInfo->Equals(nsGkAtoms::body) ||
744
0
       mNodeInfo->Equals(nsGkAtoms::frameset)) &&
745
0
      // We only forward some event attributes from body/frameset to window
746
0
      (0
747
0
#define EVENT(name_, id_, type_, struct_) /* nothing */
748
0
#define FORWARDED_EVENT(name_, id_, type_, struct_) \
749
0
       || nsGkAtoms::on##name_ == aAttrName
750
0
#define WINDOW_EVENT FORWARDED_EVENT
751
0
#include "mozilla/EventNameList.h" // IWYU pragma: keep
752
0
#undef WINDOW_EVENT
753
0
#undef FORWARDED_EVENT
754
0
#undef EVENT
755
0
       )
756
0
      ) {
757
0
    nsPIDOMWindowInner *win;
758
0
759
0
    // If we have a document, and it has a window, add the event
760
0
    // listener on the window (the inner window). If not, proceed as
761
0
    // normal.
762
0
    // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc() here,
763
0
    // override BindToTree for those classes and munge event listeners there?
764
0
    nsIDocument *document = OwnerDoc();
765
0
766
0
    *aDefer = false;
767
0
    if ((win = document->GetInnerWindow())) {
768
0
      nsCOMPtr<EventTarget> piTarget(do_QueryInterface(win));
769
0
770
0
      return piTarget->GetOrCreateListenerManager();
771
0
    }
772
0
773
0
    return nullptr;
774
0
  }
775
0
776
0
  return nsGenericHTMLElementBase::GetEventListenerManagerForAttr(aAttrName,
777
0
                                                                  aDefer);
778
0
}
779
780
#define EVENT(name_, id_, type_, struct_) /* nothing; handled by nsINode */
781
#define FORWARDED_EVENT(name_, id_, type_, struct_)                           \
782
EventHandlerNonNull*                                                          \
783
0
nsGenericHTMLElement::GetOn##name_()                                          \
784
0
{                                                                             \
785
0
  if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {            \
786
0
    /* XXXbz note to self: add tests for this! */                             \
787
0
    if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) {             \
788
0
      nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);        \
789
0
      return globalWin->GetOn##name_();                                       \
790
0
    }                                                                         \
791
0
    return nullptr;                                                           \
792
0
  }                                                                           \
793
0
                                                                              \
794
0
  return nsINode::GetOn##name_();                                             \
795
0
}                                                                             \
Unexecuted instantiation: nsGenericHTMLElement::GetOnblur()
Unexecuted instantiation: nsGenericHTMLElement::GetOnfocus()
Unexecuted instantiation: nsGenericHTMLElement::GetOnfocusin()
Unexecuted instantiation: nsGenericHTMLElement::GetOnfocusout()
Unexecuted instantiation: nsGenericHTMLElement::GetOnload()
Unexecuted instantiation: nsGenericHTMLElement::GetOnresize()
Unexecuted instantiation: nsGenericHTMLElement::GetOnscroll()
796
void                                                                          \
797
0
nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler)              \
798
0
{                                                                             \
799
0
  if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {            \
800
0
    nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();                   \
801
0
    if (!win) {                                                               \
802
0
      return;                                                                 \
803
0
    }                                                                         \
804
0
                                                                              \
805
0
    nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);          \
806
0
    return globalWin->SetOn##name_(handler);                                  \
807
0
  }                                                                           \
808
0
                                                                              \
809
0
  return nsINode::SetOn##name_(handler);                                      \
810
0
}
Unexecuted instantiation: nsGenericHTMLElement::SetOnblur(mozilla::dom::EventHandlerNonNull*)
Unexecuted instantiation: nsGenericHTMLElement::SetOnfocus(mozilla::dom::EventHandlerNonNull*)
Unexecuted instantiation: nsGenericHTMLElement::SetOnfocusin(mozilla::dom::EventHandlerNonNull*)
Unexecuted instantiation: nsGenericHTMLElement::SetOnfocusout(mozilla::dom::EventHandlerNonNull*)
Unexecuted instantiation: nsGenericHTMLElement::SetOnload(mozilla::dom::EventHandlerNonNull*)
Unexecuted instantiation: nsGenericHTMLElement::SetOnresize(mozilla::dom::EventHandlerNonNull*)
Unexecuted instantiation: nsGenericHTMLElement::SetOnscroll(mozilla::dom::EventHandlerNonNull*)
811
#define ERROR_EVENT(name_, id_, type_, struct_)                               \
812
already_AddRefed<EventHandlerNonNull>                                         \
813
0
nsGenericHTMLElement::GetOn##name_()                                          \
814
0
{                                                                             \
815
0
  if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {            \
816
0
    /* XXXbz note to self: add tests for this! */                             \
817
0
    if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) {             \
818
0
      nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);        \
819
0
      OnErrorEventHandlerNonNull* errorHandler = globalWin->GetOn##name_();   \
820
0
      if (errorHandler) {                                                     \
821
0
        RefPtr<EventHandlerNonNull> handler =                                 \
822
0
          new EventHandlerNonNull(errorHandler);                              \
823
0
        return handler.forget();                                              \
824
0
      }                                                                       \
825
0
    }                                                                         \
826
0
    return nullptr;                                                           \
827
0
  }                                                                           \
828
0
                                                                              \
829
0
  RefPtr<EventHandlerNonNull> handler = nsINode::GetOn##name_();              \
830
0
  return handler.forget();                                                    \
831
0
}                                                                             \
832
void                                                                          \
833
0
nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler)              \
834
0
{                                                                             \
835
0
  if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {            \
836
0
    nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();                   \
837
0
    if (!win) {                                                               \
838
0
      return;                                                                 \
839
0
    }                                                                         \
840
0
                                                                              \
841
0
    nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);          \
842
0
    RefPtr<OnErrorEventHandlerNonNull> errorHandler;                          \
843
0
    if (handler) {                                                            \
844
0
      errorHandler = new OnErrorEventHandlerNonNull(handler);                 \
845
0
    }                                                                         \
846
0
    return globalWin->SetOn##name_(errorHandler);                             \
847
0
  }                                                                           \
848
0
                                                                              \
849
0
  return nsINode::SetOn##name_(handler);                                      \
850
0
}
851
#include "mozilla/EventNameList.h" // IWYU pragma: keep
852
#undef ERROR_EVENT
853
#undef FORWARDED_EVENT
854
#undef EVENT
855
856
void
857
nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const
858
0
{
859
0
  OwnerDoc()->GetBaseTarget(aBaseTarget);
860
0
}
861
862
//----------------------------------------------------------------------
863
864
bool
865
nsGenericHTMLElement::ParseAttribute(int32_t aNamespaceID,
866
                                     nsAtom* aAttribute,
867
                                     const nsAString& aValue,
868
                                     nsIPrincipal* aMaybeScriptedPrincipal,
869
                                     nsAttrValue& aResult)
870
0
{
871
0
  if (aNamespaceID == kNameSpaceID_None) {
872
0
    if (aAttribute == nsGkAtoms::dir) {
873
0
      return aResult.ParseEnumValue(aValue, kDirTable, false);
874
0
    }
875
0
876
0
    if (aAttribute == nsGkAtoms::tabindex) {
877
0
      return aResult.ParseIntValue(aValue);
878
0
    }
879
0
880
0
    if (aAttribute == nsGkAtoms::referrerpolicy) {
881
0
      return ParseReferrerAttribute(aValue, aResult);
882
0
    }
883
0
884
0
    if (aAttribute == nsGkAtoms::name) {
885
0
      // Store name as an atom.  name="" means that the element has no name,
886
0
      // not that it has an empty string as the name.
887
0
      if (aValue.IsEmpty()) {
888
0
        return false;
889
0
      }
890
0
      aResult.ParseAtom(aValue);
891
0
      return true;
892
0
    }
893
0
894
0
    if (aAttribute == nsGkAtoms::contenteditable) {
895
0
      aResult.ParseAtom(aValue);
896
0
      return true;
897
0
    }
898
0
899
0
    if (aAttribute == nsGkAtoms::rel) {
900
0
      aResult.ParseAtomArray(aValue);
901
0
      return true;
902
0
    }
903
0
  }
904
0
905
0
  return nsGenericHTMLElementBase::ParseAttribute(aNamespaceID, aAttribute,
906
0
                                                  aValue, aMaybeScriptedPrincipal,
907
0
                                                  aResult);
908
0
}
909
910
bool
911
nsGenericHTMLElement::ParseBackgroundAttribute(int32_t aNamespaceID,
912
                                               nsAtom* aAttribute,
913
                                               const nsAString& aValue,
914
                                               nsAttrValue& aResult)
915
0
{
916
0
  if (aNamespaceID == kNameSpaceID_None &&
917
0
      aAttribute == nsGkAtoms::background &&
918
0
      !aValue.IsEmpty()) {
919
0
    // Resolve url to an absolute url
920
0
    nsIDocument* doc = OwnerDoc();
921
0
    nsCOMPtr<nsIURI> baseURI = GetBaseURI();
922
0
    nsCOMPtr<nsIURI> uri;
923
0
    nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
924
0
        getter_AddRefs(uri), aValue, doc, baseURI);
925
0
    if (NS_FAILED(rv)) {
926
0
      return false;
927
0
    }
928
0
    aResult.SetTo(uri, &aValue);
929
0
    return true;
930
0
  }
931
0
932
0
  return false;
933
0
}
934
935
bool
936
nsGenericHTMLElement::IsAttributeMapped(const nsAtom* aAttribute) const
937
0
{
938
0
  static const MappedAttributeEntry* const map[] = {
939
0
    sCommonAttributeMap
940
0
  };
941
0
942
0
  return FindAttributeDependence(aAttribute, map);
943
0
}
944
945
nsMapRuleToAttributesFunc
946
nsGenericHTMLElement::GetAttributeMappingFunction() const
947
0
{
948
0
  return &MapCommonAttributesInto;
949
0
}
950
951
nsIFormControlFrame*
952
nsGenericHTMLElement::GetFormControlFrame(bool aFlushFrames)
953
0
{
954
0
  if (aFlushFrames && IsInComposedDoc()) {
955
0
    // Cause a flush of the frames, so we get up-to-date frame information
956
0
    GetComposedDoc()->FlushPendingNotifications(FlushType::Frames);
957
0
  }
958
0
  nsIFrame* frame = GetPrimaryFrame();
959
0
  if (frame) {
960
0
    nsIFormControlFrame* form_frame = do_QueryFrame(frame);
961
0
    if (form_frame) {
962
0
      return form_frame;
963
0
    }
964
0
965
0
    // If we have generated content, the primary frame will be a
966
0
    // wrapper frame..  out real frame will be in its child list.
967
0
    for (frame = frame->PrincipalChildList().FirstChild();
968
0
         frame;
969
0
         frame = frame->GetNextSibling()) {
970
0
      form_frame = do_QueryFrame(frame);
971
0
      if (form_frame) {
972
0
        return form_frame;
973
0
      }
974
0
    }
975
0
  }
976
0
977
0
  return nullptr;
978
0
}
979
980
nsPresContext*
981
nsGenericHTMLElement::GetPresContext(PresContextFor aFor)
982
0
{
983
0
  // Get the document
984
0
  nsIDocument* doc = (aFor == eForComposedDoc) ?
985
0
    GetComposedDoc() : GetUncomposedDoc();
986
0
  if (doc) {
987
0
    return doc->GetPresContext();
988
0
  }
989
0
990
0
  return nullptr;
991
0
}
992
993
static const nsAttrValue::EnumTable kDivAlignTable[] = {
994
  { "left", NS_STYLE_TEXT_ALIGN_MOZ_LEFT },
995
  { "right", NS_STYLE_TEXT_ALIGN_MOZ_RIGHT },
996
  { "center", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
997
  { "middle", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
998
  { "justify", NS_STYLE_TEXT_ALIGN_JUSTIFY },
999
  { nullptr, 0 }
1000
};
1001
1002
static const nsAttrValue::EnumTable kFrameborderTable[] = {
1003
  { "yes", NS_STYLE_FRAME_YES },
1004
  { "no", NS_STYLE_FRAME_NO },
1005
  { "1", NS_STYLE_FRAME_1 },
1006
  { "0", NS_STYLE_FRAME_0 },
1007
  { nullptr, 0 }
1008
};
1009
1010
static const nsAttrValue::EnumTable kScrollingTable[] = {
1011
  { "yes", NS_STYLE_FRAME_YES },
1012
  { "no", NS_STYLE_FRAME_NO },
1013
  { "on", NS_STYLE_FRAME_ON },
1014
  { "off", NS_STYLE_FRAME_OFF },
1015
  { "scroll", NS_STYLE_FRAME_SCROLL },
1016
  { "noscroll", NS_STYLE_FRAME_NOSCROLL },
1017
  { "auto", NS_STYLE_FRAME_AUTO },
1018
  { nullptr, 0 }
1019
};
1020
1021
static const nsAttrValue::EnumTable kTableVAlignTable[] = {
1022
  { "top",     NS_STYLE_VERTICAL_ALIGN_TOP },
1023
  { "middle",  NS_STYLE_VERTICAL_ALIGN_MIDDLE },
1024
  { "bottom",  NS_STYLE_VERTICAL_ALIGN_BOTTOM },
1025
  { "baseline",NS_STYLE_VERTICAL_ALIGN_BASELINE },
1026
  { nullptr,   0 }
1027
};
1028
1029
bool
1030
nsGenericHTMLElement::ParseAlignValue(const nsAString& aString,
1031
                                      nsAttrValue& aResult)
1032
0
{
1033
0
  static const nsAttrValue::EnumTable kAlignTable[] = {
1034
0
    { "left",      NS_STYLE_TEXT_ALIGN_LEFT },
1035
0
    { "right",     NS_STYLE_TEXT_ALIGN_RIGHT },
1036
0
1037
0
    { "top",       NS_STYLE_VERTICAL_ALIGN_TOP },
1038
0
    { "middle",    NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE },
1039
0
    { "bottom",    NS_STYLE_VERTICAL_ALIGN_BASELINE },
1040
0
1041
0
    { "center",    NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE },
1042
0
    { "baseline",  NS_STYLE_VERTICAL_ALIGN_BASELINE },
1043
0
1044
0
    { "texttop",   NS_STYLE_VERTICAL_ALIGN_TEXT_TOP },
1045
0
    { "absmiddle", NS_STYLE_VERTICAL_ALIGN_MIDDLE },
1046
0
    { "abscenter", NS_STYLE_VERTICAL_ALIGN_MIDDLE },
1047
0
    { "absbottom", NS_STYLE_VERTICAL_ALIGN_BOTTOM },
1048
0
    { nullptr,     0 }
1049
0
  };
1050
0
1051
0
  return aResult.ParseEnumValue(aString, kAlignTable, false);
1052
0
}
1053
1054
//----------------------------------------
1055
1056
static const nsAttrValue::EnumTable kTableHAlignTable[] = {
1057
  { "left",   NS_STYLE_TEXT_ALIGN_LEFT },
1058
  { "right",  NS_STYLE_TEXT_ALIGN_RIGHT },
1059
  { "center", NS_STYLE_TEXT_ALIGN_CENTER },
1060
  { "char",   NS_STYLE_TEXT_ALIGN_CHAR },
1061
  { "justify",NS_STYLE_TEXT_ALIGN_JUSTIFY },
1062
  { nullptr,  0 }
1063
};
1064
1065
bool
1066
nsGenericHTMLElement::ParseTableHAlignValue(const nsAString& aString,
1067
                                            nsAttrValue& aResult)
1068
0
{
1069
0
  return aResult.ParseEnumValue(aString, kTableHAlignTable, false);
1070
0
}
1071
1072
//----------------------------------------
1073
1074
// This table is used for td, th, tr, col, thead, tbody and tfoot.
1075
static const nsAttrValue::EnumTable kTableCellHAlignTable[] = {
1076
  { "left",   NS_STYLE_TEXT_ALIGN_MOZ_LEFT },
1077
  { "right",  NS_STYLE_TEXT_ALIGN_MOZ_RIGHT },
1078
  { "center", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
1079
  { "char",   NS_STYLE_TEXT_ALIGN_CHAR },
1080
  { "justify",NS_STYLE_TEXT_ALIGN_JUSTIFY },
1081
  { "middle", NS_STYLE_TEXT_ALIGN_MOZ_CENTER },
1082
  { "absmiddle", NS_STYLE_TEXT_ALIGN_CENTER },
1083
  { nullptr,  0 }
1084
};
1085
1086
bool
1087
nsGenericHTMLElement::ParseTableCellHAlignValue(const nsAString& aString,
1088
                                                nsAttrValue& aResult)
1089
0
{
1090
0
  return aResult.ParseEnumValue(aString, kTableCellHAlignTable, false);
1091
0
}
1092
1093
//----------------------------------------
1094
1095
bool
1096
nsGenericHTMLElement::ParseTableVAlignValue(const nsAString& aString,
1097
                                            nsAttrValue& aResult)
1098
0
{
1099
0
  return aResult.ParseEnumValue(aString, kTableVAlignTable, false);
1100
0
}
1101
1102
bool
1103
nsGenericHTMLElement::ParseDivAlignValue(const nsAString& aString,
1104
                                         nsAttrValue& aResult)
1105
0
{
1106
0
  return aResult.ParseEnumValue(aString, kDivAlignTable, false);
1107
0
}
1108
1109
bool
1110
nsGenericHTMLElement::ParseImageAttribute(nsAtom* aAttribute,
1111
                                          const nsAString& aString,
1112
                                          nsAttrValue& aResult)
1113
0
{
1114
0
  if ((aAttribute == nsGkAtoms::width) ||
1115
0
      (aAttribute == nsGkAtoms::height)) {
1116
0
    return aResult.ParseSpecialIntValue(aString);
1117
0
  }
1118
0
  if ((aAttribute == nsGkAtoms::hspace) ||
1119
0
      (aAttribute == nsGkAtoms::vspace) ||
1120
0
      (aAttribute == nsGkAtoms::border)) {
1121
0
    return aResult.ParseIntWithBounds(aString, 0);
1122
0
  }
1123
0
  return false;
1124
0
}
1125
1126
bool
1127
nsGenericHTMLElement::ParseReferrerAttribute(const nsAString& aString,
1128
                                             nsAttrValue& aResult)
1129
0
{
1130
0
  static const nsAttrValue::EnumTable kReferrerTable[] = {
1131
0
    { net::kRPS_No_Referrer, static_cast<int16_t>(net::RP_No_Referrer) },
1132
0
    { net::kRPS_Origin, static_cast<int16_t>(net::RP_Origin) },
1133
0
    { net::kRPS_Origin_When_Cross_Origin, static_cast<int16_t>(net::RP_Origin_When_Crossorigin) },
1134
0
    { net::kRPS_No_Referrer_When_Downgrade, static_cast<int16_t>(net::RP_No_Referrer_When_Downgrade) },
1135
0
    { net::kRPS_Unsafe_URL, static_cast<int16_t>(net::RP_Unsafe_URL) },
1136
0
    { net::kRPS_Strict_Origin, static_cast<int16_t>(net::RP_Strict_Origin) },
1137
0
    { net::kRPS_Same_Origin, static_cast<int16_t>(net::RP_Same_Origin) },
1138
0
    { net::kRPS_Strict_Origin_When_Cross_Origin, static_cast<int16_t>(net::RP_Strict_Origin_When_Cross_Origin) },
1139
0
    { nullptr, 0 }
1140
0
  };
1141
0
  return aResult.ParseEnumValue(aString, kReferrerTable, false);
1142
0
}
1143
1144
bool
1145
nsGenericHTMLElement::ParseFrameborderValue(const nsAString& aString,
1146
                                            nsAttrValue& aResult)
1147
0
{
1148
0
  return aResult.ParseEnumValue(aString, kFrameborderTable, false);
1149
0
}
1150
1151
bool
1152
nsGenericHTMLElement::ParseScrollingValue(const nsAString& aString,
1153
                                          nsAttrValue& aResult)
1154
0
{
1155
0
  return aResult.ParseEnumValue(aString, kScrollingTable, false);
1156
0
}
1157
1158
static inline void
1159
MapLangAttributeInto(const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls)
1160
0
{
1161
0
  const nsAttrValue* langValue = aAttributes->GetAttr(nsGkAtoms::lang);
1162
0
  if (!langValue) {
1163
0
    return;
1164
0
  }
1165
0
  MOZ_ASSERT(langValue->Type() == nsAttrValue::eAtom);
1166
0
  aDecls.SetIdentAtomValueIfUnset(eCSSProperty__x_lang,
1167
0
                                  langValue->GetAtomValue());
1168
0
  if (!aDecls.PropertyIsSet(eCSSProperty_text_emphasis_position)) {
1169
0
    const nsAtom* lang = langValue->GetAtomValue();
1170
0
    if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) {
1171
0
      aDecls.SetKeywordValue(eCSSProperty_text_emphasis_position,
1172
0
                             NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT_ZH);
1173
0
    } else if (nsStyleUtil::MatchesLanguagePrefix(lang, u"ja") ||
1174
0
               nsStyleUtil::MatchesLanguagePrefix(lang, u"mn")) {
1175
0
      // This branch is currently no part of the spec.
1176
0
      // See bug 1040668 comment 69 and comment 75.
1177
0
      aDecls.SetKeywordValue(eCSSProperty_text_emphasis_position,
1178
0
                             NS_STYLE_TEXT_EMPHASIS_POSITION_DEFAULT);
1179
0
    }
1180
0
  }
1181
0
}
1182
1183
/**
1184
 * Handle attributes common to all html elements
1185
 */
1186
void
1187
nsGenericHTMLElement::MapCommonAttributesIntoExceptHidden(const nsMappedAttributes* aAttributes,
1188
                                                          MappedDeclarations& aDecls)
1189
0
{
1190
0
  if (!aDecls.PropertyIsSet(eCSSProperty__moz_user_modify)) {
1191
0
    const nsAttrValue* value =
1192
0
      aAttributes->GetAttr(nsGkAtoms::contenteditable);
1193
0
    if (value) {
1194
0
      if (value->Equals(nsGkAtoms::_empty, eCaseMatters) ||
1195
0
          value->Equals(nsGkAtoms::_true, eIgnoreCase)) {
1196
0
        aDecls.SetKeywordValue(eCSSProperty__moz_user_modify,
1197
0
                               StyleUserModify::ReadWrite);
1198
0
      }
1199
0
      else if (value->Equals(nsGkAtoms::_false, eIgnoreCase)) {
1200
0
          aDecls.SetKeywordValue(eCSSProperty__moz_user_modify,
1201
0
                                 StyleUserModify::ReadOnly);
1202
0
      }
1203
0
    }
1204
0
  }
1205
0
1206
0
  MapLangAttributeInto(aAttributes, aDecls);
1207
0
}
1208
1209
void
1210
nsGenericHTMLElement::MapCommonAttributesInto(const nsMappedAttributes* aAttributes,
1211
                                              MappedDeclarations& aDecls)
1212
0
{
1213
0
  MapCommonAttributesIntoExceptHidden(aAttributes, aDecls);
1214
0
1215
0
  if (!aDecls.PropertyIsSet(eCSSProperty_display)) {
1216
0
    if (aAttributes->IndexOfAttr(nsGkAtoms::hidden) >= 0) {
1217
0
      aDecls.SetKeywordValue(eCSSProperty_display, StyleDisplay::None);
1218
0
    }
1219
0
  }
1220
0
}
1221
1222
/* static */ const nsGenericHTMLElement::MappedAttributeEntry
1223
nsGenericHTMLElement::sCommonAttributeMap[] = {
1224
  { &nsGkAtoms::contenteditable },
1225
  { &nsGkAtoms::lang },
1226
  { &nsGkAtoms::hidden },
1227
  { nullptr }
1228
};
1229
1230
/* static */ const Element::MappedAttributeEntry
1231
nsGenericHTMLElement::sImageMarginSizeAttributeMap[] = {
1232
  { &nsGkAtoms::width },
1233
  { &nsGkAtoms::height },
1234
  { &nsGkAtoms::hspace },
1235
  { &nsGkAtoms::vspace },
1236
  { nullptr }
1237
};
1238
1239
/* static */ const Element::MappedAttributeEntry
1240
nsGenericHTMLElement::sImageAlignAttributeMap[] = {
1241
  { &nsGkAtoms::align },
1242
  { nullptr }
1243
};
1244
1245
/* static */ const Element::MappedAttributeEntry
1246
nsGenericHTMLElement::sDivAlignAttributeMap[] = {
1247
  { &nsGkAtoms::align },
1248
  { nullptr }
1249
};
1250
1251
/* static */ const Element::MappedAttributeEntry
1252
nsGenericHTMLElement::sImageBorderAttributeMap[] = {
1253
  { &nsGkAtoms::border },
1254
  { nullptr }
1255
};
1256
1257
/* static */ const Element::MappedAttributeEntry
1258
nsGenericHTMLElement::sBackgroundAttributeMap[] = {
1259
  { &nsGkAtoms::background },
1260
  { &nsGkAtoms::bgcolor },
1261
  { nullptr }
1262
};
1263
1264
/* static */ const Element::MappedAttributeEntry
1265
nsGenericHTMLElement::sBackgroundColorAttributeMap[] = {
1266
  { &nsGkAtoms::bgcolor },
1267
  { nullptr }
1268
};
1269
1270
void
1271
nsGenericHTMLElement::MapImageAlignAttributeInto(const nsMappedAttributes* aAttributes,
1272
                                                 MappedDeclarations& aDecls)
1273
0
{
1274
0
  const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
1275
0
  if (value && value->Type() == nsAttrValue::eEnum) {
1276
0
    int32_t align = value->GetEnumValue();
1277
0
    if (!aDecls.PropertyIsSet(eCSSProperty_float)) {
1278
0
      if (align == NS_STYLE_TEXT_ALIGN_LEFT) {
1279
0
        aDecls.SetKeywordValue(eCSSProperty_float, StyleFloat::Left);
1280
0
      } else if (align == NS_STYLE_TEXT_ALIGN_RIGHT) {
1281
0
        aDecls.SetKeywordValue(eCSSProperty_float, StyleFloat::Right);
1282
0
      }
1283
0
    }
1284
0
    if (!aDecls.PropertyIsSet(eCSSProperty_vertical_align)) {
1285
0
      switch (align) {
1286
0
      case NS_STYLE_TEXT_ALIGN_LEFT:
1287
0
      case NS_STYLE_TEXT_ALIGN_RIGHT:
1288
0
        break;
1289
0
      default:
1290
0
        aDecls.SetKeywordValue(eCSSProperty_vertical_align, align);
1291
0
        break;
1292
0
      }
1293
0
    }
1294
0
  }
1295
0
}
1296
1297
void
1298
nsGenericHTMLElement::MapDivAlignAttributeInto(const nsMappedAttributes* aAttributes,
1299
                                               MappedDeclarations& aDecls)
1300
0
{
1301
0
  if (!aDecls.PropertyIsSet(eCSSProperty_text_align)) {
1302
0
    // align: enum
1303
0
    const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::align);
1304
0
    if (value && value->Type() == nsAttrValue::eEnum)
1305
0
      aDecls.SetKeywordValue(eCSSProperty_text_align, value->GetEnumValue());
1306
0
  }
1307
0
}
1308
1309
void
1310
nsGenericHTMLElement::MapVAlignAttributeInto(const nsMappedAttributes* aAttributes,
1311
                                             MappedDeclarations& aDecls)
1312
0
{
1313
0
  if (!aDecls.PropertyIsSet(eCSSProperty_vertical_align)) {
1314
0
    // align: enum
1315
0
    const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::valign);
1316
0
    if (value && value->Type() == nsAttrValue::eEnum)
1317
0
      aDecls.SetKeywordValue(eCSSProperty_vertical_align, value->GetEnumValue());
1318
0
  }
1319
0
}
1320
1321
void
1322
nsGenericHTMLElement::MapImageMarginAttributeInto(const nsMappedAttributes* aAttributes,
1323
                                                  MappedDeclarations& aDecls)
1324
0
{
1325
0
  const nsAttrValue* value;
1326
0
1327
0
  // hspace: value
1328
0
  value = aAttributes->GetAttr(nsGkAtoms::hspace);
1329
0
  if (value) {
1330
0
    if (value->Type() == nsAttrValue::eInteger) {
1331
0
      aDecls.SetPixelValueIfUnset(eCSSProperty_margin_left,
1332
0
                                  (float)value->GetIntegerValue());
1333
0
      aDecls.SetPixelValueIfUnset(eCSSProperty_margin_right,
1334
0
                                  (float)value->GetIntegerValue());
1335
0
    } else if (value->Type() == nsAttrValue::ePercent) {
1336
0
      aDecls.SetPercentValueIfUnset(eCSSProperty_margin_left,
1337
0
                                    value->GetPercentValue());
1338
0
      aDecls.SetPercentValueIfUnset(eCSSProperty_margin_right,
1339
0
                                    value->GetPercentValue());
1340
0
    }
1341
0
  }
1342
0
1343
0
  // vspace: value
1344
0
  value = aAttributes->GetAttr(nsGkAtoms::vspace);
1345
0
  if (value) {
1346
0
    if (value->Type() == nsAttrValue::eInteger) {
1347
0
      aDecls.SetPixelValueIfUnset(eCSSProperty_margin_top,
1348
0
                                  (float)value->GetIntegerValue());
1349
0
      aDecls.SetPixelValueIfUnset(eCSSProperty_margin_bottom,
1350
0
                                  (float)value->GetIntegerValue());
1351
0
    } else if (value->Type() == nsAttrValue::ePercent) {
1352
0
      aDecls.SetPercentValueIfUnset(eCSSProperty_margin_top,
1353
0
                                    value->GetPercentValue());
1354
0
      aDecls.SetPercentValueIfUnset(eCSSProperty_margin_bottom,
1355
0
                                    value->GetPercentValue());
1356
0
    }
1357
0
  }
1358
0
}
1359
1360
void
1361
nsGenericHTMLElement::MapWidthAttributeInto(const nsMappedAttributes* aAttributes,
1362
                                            MappedDeclarations& aDecls)
1363
0
{
1364
0
  // width: value
1365
0
  if (!aDecls.PropertyIsSet(eCSSProperty_width)) {
1366
0
    const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::width);
1367
0
    if (value && value->Type() == nsAttrValue::eInteger) {
1368
0
      aDecls.SetPixelValue(eCSSProperty_width,
1369
0
                           (float)value->GetIntegerValue());
1370
0
    } else if (value && value->Type() == nsAttrValue::ePercent) {
1371
0
      aDecls.SetPercentValue(eCSSProperty_width,
1372
0
                             value->GetPercentValue());
1373
0
    }
1374
0
  }
1375
0
}
1376
1377
void
1378
nsGenericHTMLElement::MapHeightAttributeInto(const nsMappedAttributes* aAttributes,
1379
                                             MappedDeclarations& aDecls)
1380
0
{
1381
0
  // height: value
1382
0
  if (!aDecls.PropertyIsSet(eCSSProperty_height)) {
1383
0
    const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::height);
1384
0
    if (value && value->Type() == nsAttrValue::eInteger) {
1385
0
      aDecls.SetPixelValue(eCSSProperty_height,
1386
0
                           (float)value->GetIntegerValue());
1387
0
    } else if (value && value->Type() == nsAttrValue::ePercent) {
1388
0
      aDecls.SetPercentValue(eCSSProperty_height,
1389
0
                             value->GetPercentValue());
1390
0
    }
1391
0
  }
1392
0
}
1393
1394
void
1395
nsGenericHTMLElement::MapImageSizeAttributesInto(const nsMappedAttributes* aAttributes,
1396
                                                 MappedDeclarations& aDecls)
1397
0
{
1398
0
  nsGenericHTMLElement::MapWidthAttributeInto(aAttributes, aDecls);
1399
0
  nsGenericHTMLElement::MapHeightAttributeInto(aAttributes, aDecls);
1400
0
}
1401
1402
void
1403
nsGenericHTMLElement::MapImageBorderAttributeInto(const nsMappedAttributes* aAttributes,
1404
                                                  MappedDeclarations& aDecls)
1405
0
{
1406
0
  // border: pixels
1407
0
  const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::border);
1408
0
  if (!value)
1409
0
    return;
1410
0
1411
0
  nscoord val = 0;
1412
0
  if (value->Type() == nsAttrValue::eInteger)
1413
0
    val = value->GetIntegerValue();
1414
0
1415
0
  aDecls.SetPixelValueIfUnset(eCSSProperty_border_top_width, (float)val);
1416
0
  aDecls.SetPixelValueIfUnset(eCSSProperty_border_right_width, (float)val);
1417
0
  aDecls.SetPixelValueIfUnset(eCSSProperty_border_bottom_width, (float)val);
1418
0
  aDecls.SetPixelValueIfUnset(eCSSProperty_border_left_width, (float)val);
1419
0
1420
0
  aDecls.SetKeywordValueIfUnset(eCSSProperty_border_top_style,
1421
0
                                NS_STYLE_BORDER_STYLE_SOLID);
1422
0
  aDecls.SetKeywordValueIfUnset(eCSSProperty_border_right_style,
1423
0
                                NS_STYLE_BORDER_STYLE_SOLID);
1424
0
  aDecls.SetKeywordValueIfUnset(eCSSProperty_border_bottom_style,
1425
0
                                NS_STYLE_BORDER_STYLE_SOLID);
1426
0
  aDecls.SetKeywordValueIfUnset(eCSSProperty_border_left_style,
1427
0
                                NS_STYLE_BORDER_STYLE_SOLID);
1428
0
1429
0
  aDecls.SetCurrentColorIfUnset(eCSSProperty_border_top_color);
1430
0
  aDecls.SetCurrentColorIfUnset(eCSSProperty_border_right_color);
1431
0
  aDecls.SetCurrentColorIfUnset(eCSSProperty_border_bottom_color);
1432
0
  aDecls.SetCurrentColorIfUnset(eCSSProperty_border_left_color);
1433
0
}
1434
1435
void
1436
nsGenericHTMLElement::MapBackgroundInto(const nsMappedAttributes* aAttributes,
1437
                                        MappedDeclarations& aDecls)
1438
0
{
1439
0
1440
0
  if (!aDecls.PropertyIsSet(eCSSProperty_background_image)) {
1441
0
    // background
1442
0
    nsAttrValue* value =
1443
0
      const_cast<nsAttrValue*>(aAttributes->GetAttr(nsGkAtoms::background));
1444
0
    if (value) {
1445
0
      aDecls.SetBackgroundImage(*value);
1446
0
    }
1447
0
  }
1448
0
}
1449
1450
void
1451
nsGenericHTMLElement::MapBGColorInto(const nsMappedAttributes* aAttributes,
1452
                                     MappedDeclarations& aDecls)
1453
0
{
1454
0
  if (!aDecls.PropertyIsSet(eCSSProperty_background_color)) {
1455
0
    const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::bgcolor);
1456
0
    nscolor color;
1457
0
    if (value && value->GetColorValue(color)) {
1458
0
      aDecls.SetColorValue(eCSSProperty_background_color, color);
1459
0
    }
1460
0
  }
1461
0
}
1462
1463
void
1464
nsGenericHTMLElement::MapBackgroundAttributesInto(const nsMappedAttributes* aAttributes,
1465
                                                  MappedDeclarations& aDecls)
1466
0
{
1467
0
  MapBackgroundInto(aAttributes, aDecls);
1468
0
  MapBGColorInto(aAttributes, aDecls);
1469
0
}
1470
1471
//----------------------------------------------------------------------
1472
1473
int32_t
1474
nsGenericHTMLElement::GetIntAttr(nsAtom* aAttr, int32_t aDefault) const
1475
0
{
1476
0
  const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
1477
0
  if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
1478
0
    return attrVal->GetIntegerValue();
1479
0
  }
1480
0
  return aDefault;
1481
0
}
1482
1483
nsresult
1484
nsGenericHTMLElement::SetIntAttr(nsAtom* aAttr, int32_t aValue)
1485
0
{
1486
0
  nsAutoString value;
1487
0
  value.AppendInt(aValue);
1488
0
1489
0
  return SetAttr(kNameSpaceID_None, aAttr, value, true);
1490
0
}
1491
1492
uint32_t
1493
nsGenericHTMLElement::GetUnsignedIntAttr(nsAtom* aAttr,
1494
                                         uint32_t aDefault) const
1495
0
{
1496
0
  const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
1497
0
  if (!attrVal || attrVal->Type() != nsAttrValue::eInteger) {
1498
0
    return aDefault;
1499
0
  }
1500
0
1501
0
  return attrVal->GetIntegerValue();
1502
0
}
1503
1504
void
1505
nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr, nsAtom* aBaseAttr,
1506
                                 nsAString& aResult) const
1507
0
{
1508
0
  nsCOMPtr<nsIURI> uri;
1509
0
  bool hadAttr = GetURIAttr(aAttr, aBaseAttr, getter_AddRefs(uri));
1510
0
  if (!hadAttr) {
1511
0
    aResult.Truncate();
1512
0
    return;
1513
0
  }
1514
0
1515
0
  if (!uri) {
1516
0
    // Just return the attr value
1517
0
    GetAttr(kNameSpaceID_None, aAttr, aResult);
1518
0
    return;
1519
0
  }
1520
0
1521
0
  nsAutoCString spec;
1522
0
  uri->GetSpec(spec);
1523
0
  CopyUTF8toUTF16(spec, aResult);
1524
0
}
1525
1526
bool
1527
nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr, nsAtom* aBaseAttr, nsIURI** aURI) const
1528
0
{
1529
0
  *aURI = nullptr;
1530
0
1531
0
  const nsAttrValue* attr = mAttrs.GetAttr(aAttr);
1532
0
  if (!attr) {
1533
0
    return false;
1534
0
  }
1535
0
1536
0
  nsCOMPtr<nsIURI> baseURI = GetBaseURI();
1537
0
1538
0
  if (aBaseAttr) {
1539
0
    nsAutoString baseAttrValue;
1540
0
    if (GetAttr(kNameSpaceID_None, aBaseAttr, baseAttrValue)) {
1541
0
      nsCOMPtr<nsIURI> baseAttrURI;
1542
0
      nsresult rv =
1543
0
        nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(baseAttrURI),
1544
0
                                                  baseAttrValue, OwnerDoc(),
1545
0
                                                  baseURI);
1546
0
      if (NS_FAILED(rv)) {
1547
0
        return true;
1548
0
      }
1549
0
      baseURI.swap(baseAttrURI);
1550
0
    }
1551
0
  }
1552
0
1553
0
  // Don't care about return value.  If it fails, we still want to
1554
0
  // return true, and *aURI will be null.
1555
0
  nsContentUtils::NewURIWithDocumentCharset(aURI,
1556
0
                                            attr->GetStringValue(),
1557
0
                                            OwnerDoc(), baseURI);
1558
0
  return true;
1559
0
}
1560
1561
HTMLMenuElement*
1562
nsGenericHTMLElement::GetContextMenu() const
1563
0
{
1564
0
  nsAutoString value;
1565
0
  GetHTMLAttr(nsGkAtoms::contextmenu, value);
1566
0
  if (!value.IsEmpty()) {
1567
0
    //XXXsmaug How should this work in Shadow DOM?
1568
0
    nsIDocument* doc = GetUncomposedDoc();
1569
0
    if (doc) {
1570
0
      return HTMLMenuElement::FromNodeOrNull(doc->GetElementById(value));
1571
0
    }
1572
0
  }
1573
0
  return nullptr;
1574
0
}
1575
1576
bool
1577
nsGenericHTMLElement::IsLabelable() const
1578
0
{
1579
0
  return IsAnyOfHTMLElements(nsGkAtoms::progress, nsGkAtoms::meter);
1580
0
}
1581
1582
/* static */ bool
1583
nsGenericHTMLElement::MatchLabelsElement(Element* aElement, int32_t aNamespaceID,
1584
                                         nsAtom* aAtom, void* aData)
1585
0
{
1586
0
  HTMLLabelElement* element = HTMLLabelElement::FromNode(aElement);
1587
0
  return element && element->GetControl() == aData;
1588
0
}
1589
1590
already_AddRefed<nsINodeList>
1591
nsGenericHTMLElement::Labels()
1592
0
{
1593
0
  MOZ_ASSERT(IsLabelable(),
1594
0
             "Labels() only allow labelable elements to use it.");
1595
0
  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
1596
0
1597
0
  if (!slots->mLabelsList) {
1598
0
    slots->mLabelsList = new nsLabelsNodeList(SubtreeRoot(), MatchLabelsElement,
1599
0
                                              nullptr, this);
1600
0
  }
1601
0
1602
0
  RefPtr<nsLabelsNodeList> labels = slots->mLabelsList;
1603
0
  return labels.forget();
1604
0
}
1605
1606
bool
1607
nsGenericHTMLElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
1608
0
{
1609
0
  return IsAnyOfHTMLElements(nsGkAtoms::details, nsGkAtoms::embed,
1610
0
                             nsGkAtoms::keygen) ||
1611
0
         (!aIgnoreTabindex && HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex));
1612
0
}
1613
1614
// static
1615
bool
1616
nsGenericHTMLElement::TouchEventsEnabled(JSContext* aCx, JSObject* aGlobal)
1617
0
{
1618
0
  return TouchEvent::PrefEnabled(aCx, aGlobal);
1619
0
}
1620
1621
//----------------------------------------------------------------------
1622
1623
nsGenericHTMLFormElement::nsGenericHTMLFormElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
1624
                                                   uint8_t aType)
1625
  : nsGenericHTMLElement(std::move(aNodeInfo))
1626
  , nsIFormControl(aType)
1627
  , mForm(nullptr)
1628
  , mFieldSet(nullptr)
1629
0
{
1630
0
  // We should add the NS_EVENT_STATE_ENABLED bit here as needed, but
1631
0
  // that depends on our type, which is not initialized yet.  So we
1632
0
  // have to do this in subclasses.
1633
0
}
1634
1635
nsGenericHTMLFormElement::~nsGenericHTMLFormElement()
1636
0
{
1637
0
  if (mFieldSet) {
1638
0
    mFieldSet->RemoveElement(this);
1639
0
  }
1640
0
1641
0
  // Check that this element doesn't know anything about its form at this point.
1642
0
  NS_ASSERTION(!mForm, "mForm should be null at this point!");
1643
0
}
1644
1645
NS_IMPL_ISUPPORTS_INHERITED(nsGenericHTMLFormElement,
1646
                            nsGenericHTMLElement,
1647
                            nsIFormControl)
1648
1649
nsINode*
1650
nsGenericHTMLFormElement::GetScopeChainParent() const
1651
0
{
1652
0
  return mForm ? mForm : nsGenericHTMLElement::GetScopeChainParent();
1653
0
}
1654
1655
bool
1656
nsGenericHTMLFormElement::IsNodeOfType(uint32_t aFlags) const
1657
0
{
1658
0
  return !(aFlags & ~eHTML_FORM_CONTROL);
1659
0
}
1660
1661
void
1662
nsGenericHTMLFormElement::SaveSubtreeState()
1663
0
{
1664
0
  SaveState();
1665
0
1666
0
  nsGenericHTMLElement::SaveSubtreeState();
1667
0
}
1668
1669
void
1670
nsGenericHTMLFormElement::SetForm(HTMLFormElement* aForm)
1671
0
{
1672
0
  MOZ_ASSERT(aForm, "Don't pass null here");
1673
0
  NS_ASSERTION(!mForm,
1674
0
               "We don't support switching from one non-null form to another.");
1675
0
1676
0
  SetForm(aForm, false);
1677
0
}
1678
1679
void nsGenericHTMLFormElement::SetForm(HTMLFormElement* aForm, bool aBindToTree)
1680
0
{
1681
0
  if (aForm) {
1682
0
    BeforeSetForm(aBindToTree);
1683
0
  }
1684
0
1685
0
  // keep a *weak* ref to the form here
1686
0
  mForm = aForm;
1687
0
}
1688
1689
void
1690
nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm, bool aUnbindOrDelete)
1691
0
{
1692
0
  NS_ASSERTION((mForm != nullptr) == HasFlag(ADDED_TO_FORM),
1693
0
               "Form control should have had flag set correctly");
1694
0
1695
0
  if (!mForm) {
1696
0
    return;
1697
0
  }
1698
0
1699
0
  if (aRemoveFromForm) {
1700
0
    nsAutoString nameVal, idVal;
1701
0
    GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal);
1702
0
    GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal);
1703
0
1704
0
    mForm->RemoveElement(this, true);
1705
0
1706
0
    if (!nameVal.IsEmpty()) {
1707
0
      mForm->RemoveElementFromTable(this, nameVal);
1708
0
    }
1709
0
1710
0
    if (!idVal.IsEmpty()) {
1711
0
      mForm->RemoveElementFromTable(this, idVal);
1712
0
    }
1713
0
  }
1714
0
1715
0
  UnsetFlags(ADDED_TO_FORM);
1716
0
  mForm = nullptr;
1717
0
1718
0
  AfterClearForm(aUnbindOrDelete);
1719
0
}
1720
1721
Element*
1722
nsGenericHTMLFormElement::GetFormElement()
1723
0
{
1724
0
  return mForm;
1725
0
}
1726
1727
HTMLFieldSetElement*
1728
nsGenericHTMLFormElement::GetFieldSet()
1729
0
{
1730
0
  return mFieldSet;
1731
0
}
1732
1733
nsIContent::IMEState
1734
nsGenericHTMLFormElement::GetDesiredIMEState()
1735
0
{
1736
0
  TextEditor* textEditor = GetTextEditorInternal();
1737
0
  if (!textEditor) {
1738
0
    return nsGenericHTMLElement::GetDesiredIMEState();
1739
0
  }
1740
0
  IMEState state;
1741
0
  nsresult rv = textEditor->GetPreferredIMEState(&state);
1742
0
  if (NS_FAILED(rv)) {
1743
0
    return nsGenericHTMLElement::GetDesiredIMEState();
1744
0
  }
1745
0
  return state;
1746
0
}
1747
1748
nsresult
1749
nsGenericHTMLFormElement::BindToTree(nsIDocument* aDocument,
1750
                                     nsIContent* aParent,
1751
                                     nsIContent* aBindingParent)
1752
0
{
1753
0
  nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
1754
0
                                                 aBindingParent);
1755
0
  NS_ENSURE_SUCCESS(rv, rv);
1756
0
1757
0
  // An autofocus event has to be launched if the autofocus attribute is
1758
0
  // specified and the element accept the autofocus attribute. In addition,
1759
0
  // the document should not be already loaded and the "browser.autofocus"
1760
0
  // preference should be 'true'.
1761
0
  if (IsAutofocusable() && HasAttr(kNameSpaceID_None, nsGkAtoms::autofocus) &&
1762
0
      nsContentUtils::AutoFocusEnabled() && aDocument) {
1763
0
    aDocument->SetAutoFocusElement(this);
1764
0
  }
1765
0
1766
0
  // If @form is set, the element *has* to be in a composed document, otherwise
1767
0
  // it wouldn't be possible to find an element with the corresponding id.
1768
0
  // If @form isn't set, the element *has* to have a parent, otherwise it
1769
0
  // wouldn't be possible to find a form ancestor.
1770
0
  // We should not call UpdateFormOwner if none of these conditions are
1771
0
  // fulfilled.
1772
0
  if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ? IsInComposedDoc()
1773
0
                                                  : !!aParent) {
1774
0
    UpdateFormOwner(true, nullptr);
1775
0
  }
1776
0
1777
0
  // Set parent fieldset which should be used for the disabled state.
1778
0
  UpdateFieldSet(false);
1779
0
1780
0
  return NS_OK;
1781
0
}
1782
1783
void
1784
nsGenericHTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent)
1785
0
{
1786
0
  // Save state before doing anything
1787
0
  SaveState();
1788
0
1789
0
  if (mForm) {
1790
0
    // Might need to unset mForm
1791
0
    if (aNullParent) {
1792
0
      // No more parent means no more form
1793
0
      ClearForm(true, true);
1794
0
    } else {
1795
0
      // Recheck whether we should still have an mForm.
1796
0
      if (HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
1797
0
          !FindAncestorForm(mForm)) {
1798
0
        ClearForm(true, true);
1799
0
      } else {
1800
0
        UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
1801
0
      }
1802
0
    }
1803
0
1804
0
    if (!mForm) {
1805
0
      // Our novalidate state might have changed
1806
0
      UpdateState(false);
1807
0
    }
1808
0
  }
1809
0
1810
0
  // We have to remove the form id observer if there was one.
1811
0
  // We will re-add one later if needed (during bind to tree).
1812
0
  if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
1813
0
                                      nsGkAtoms::form)) {
1814
0
    RemoveFormIdObserver();
1815
0
  }
1816
0
1817
0
  nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
1818
0
1819
0
  // The element might not have a fieldset anymore.
1820
0
  UpdateFieldSet(false);
1821
0
}
1822
1823
nsresult
1824
nsGenericHTMLFormElement::BeforeSetAttr(int32_t aNameSpaceID, nsAtom* aName,
1825
                                        const nsAttrValueOrString* aValue,
1826
                                        bool aNotify)
1827
0
{
1828
0
  if (aNameSpaceID == kNameSpaceID_None) {
1829
0
    nsAutoString tmp;
1830
0
1831
0
    // remove the control from the hashtable as needed
1832
0
1833
0
    if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
1834
0
      GetAttr(kNameSpaceID_None, aName, tmp);
1835
0
1836
0
      if (!tmp.IsEmpty()) {
1837
0
        mForm->RemoveElementFromTable(this, tmp);
1838
0
      }
1839
0
    }
1840
0
1841
0
    if (mForm && aName == nsGkAtoms::type) {
1842
0
      GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp);
1843
0
1844
0
      if (!tmp.IsEmpty()) {
1845
0
        mForm->RemoveElementFromTable(this, tmp);
1846
0
      }
1847
0
1848
0
      GetAttr(kNameSpaceID_None, nsGkAtoms::id, tmp);
1849
0
1850
0
      if (!tmp.IsEmpty()) {
1851
0
        mForm->RemoveElementFromTable(this, tmp);
1852
0
      }
1853
0
1854
0
      mForm->RemoveElement(this, false);
1855
0
    }
1856
0
1857
0
    if (aName == nsGkAtoms::form) {
1858
0
      // If @form isn't set or set to the empty string, there were no observer
1859
0
      // so we don't have to remove it.
1860
0
      if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
1861
0
                                          nsGkAtoms::form)) {
1862
0
        // The current form id observer is no longer needed.
1863
0
        // A new one may be added in AfterSetAttr.
1864
0
        RemoveFormIdObserver();
1865
0
      }
1866
0
    }
1867
0
  }
1868
0
1869
0
  return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName,
1870
0
                                             aValue, aNotify);
1871
0
}
1872
1873
nsresult
1874
nsGenericHTMLFormElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
1875
                                       const nsAttrValue* aValue,
1876
                                       const nsAttrValue* aOldValue,
1877
                                       nsIPrincipal* aMaybeScriptedPrincipal,
1878
                                       bool aNotify)
1879
0
{
1880
0
  if (aNameSpaceID == kNameSpaceID_None) {
1881
0
    // add the control to the hashtable as needed
1882
0
1883
0
    if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id) &&
1884
0
        aValue && !aValue->IsEmptyString()) {
1885
0
      MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom,
1886
0
                 "Expected atom value for name/id");
1887
0
      mForm->AddElementToTable(this,
1888
0
        nsDependentAtomString(aValue->GetAtomValue()));
1889
0
    }
1890
0
1891
0
    if (mForm && aName == nsGkAtoms::type) {
1892
0
      nsAutoString tmp;
1893
0
1894
0
      GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp);
1895
0
1896
0
      if (!tmp.IsEmpty()) {
1897
0
        mForm->AddElementToTable(this, tmp);
1898
0
      }
1899
0
1900
0
      GetAttr(kNameSpaceID_None, nsGkAtoms::id, tmp);
1901
0
1902
0
      if (!tmp.IsEmpty()) {
1903
0
        mForm->AddElementToTable(this, tmp);
1904
0
      }
1905
0
1906
0
      mForm->AddElement(this, false, aNotify);
1907
0
    }
1908
0
1909
0
    if (aName == nsGkAtoms::form) {
1910
0
      // We need a new form id observer.
1911
0
      DocumentOrShadowRoot* docOrShadow =
1912
0
        GetUncomposedDocOrConnectedShadowRoot();
1913
0
      if (docOrShadow) {
1914
0
        Element* formIdElement = nullptr;
1915
0
        if (aValue && !aValue->IsEmptyString()) {
1916
0
          formIdElement = AddFormIdObserver();
1917
0
        }
1918
0
1919
0
        // Because we have a new @form value (or no more @form), we have to
1920
0
        // update our form owner.
1921
0
        UpdateFormOwner(false, formIdElement);
1922
0
      }
1923
0
    }
1924
0
  }
1925
0
1926
0
  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
1927
0
                                            aValue, aOldValue,
1928
0
                                            aMaybeScriptedPrincipal,
1929
0
                                            aNotify);
1930
0
}
1931
1932
void
1933
nsGenericHTMLFormElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
1934
0
{
1935
0
  if (aVisitor.mEvent->IsTrusted() && (aVisitor.mEvent->mMessage == eFocus ||
1936
0
                                       aVisitor.mEvent->mMessage == eBlur)) {
1937
0
    // We have to handle focus/blur event to change focus states in
1938
0
    // PreHandleEvent to prevent it breaks event target chain creation.
1939
0
    aVisitor.mWantsPreHandleEvent = true;
1940
0
  }
1941
0
  nsGenericHTMLElement::GetEventTargetParent(aVisitor);
1942
0
}
1943
1944
nsresult
1945
nsGenericHTMLFormElement::PreHandleEvent(EventChainVisitor& aVisitor)
1946
0
{
1947
0
  if (aVisitor.mEvent->IsTrusted()) {
1948
0
    switch (aVisitor.mEvent->mMessage) {
1949
0
      case eFocus: {
1950
0
        // Check to see if focus has bubbled up from a form control's
1951
0
        // child textfield or button.  If that's the case, don't focus
1952
0
        // this parent file control -- leave focus on the child.
1953
0
        nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
1954
0
        if (formControlFrame &&
1955
0
            aVisitor.mEvent->mOriginalTarget == static_cast<nsINode*>(this))
1956
0
          formControlFrame->SetFocus(true, true);
1957
0
        break;
1958
0
      }
1959
0
      case eBlur: {
1960
0
        nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
1961
0
        if (formControlFrame)
1962
0
          formControlFrame->SetFocus(false, false);
1963
0
        break;
1964
0
      }
1965
0
      default:
1966
0
        break;
1967
0
    }
1968
0
  }
1969
0
  return nsGenericHTMLElement::PreHandleEvent(aVisitor);
1970
0
}
1971
1972
void
1973
nsGenericHTMLFormElement::ForgetFieldSet(nsIContent* aFieldset)
1974
0
{
1975
0
  if (mFieldSet == aFieldset) {
1976
0
    mFieldSet = nullptr;
1977
0
  }
1978
0
}
1979
1980
bool
1981
nsGenericHTMLFormElement::CanBeDisabled() const
1982
0
{
1983
0
  int32_t type = ControlType();
1984
0
  // It's easier to test the types that _cannot_ be disabled
1985
0
  return
1986
0
    type != NS_FORM_OBJECT &&
1987
0
    type != NS_FORM_OUTPUT;
1988
0
}
1989
1990
bool
1991
nsGenericHTMLFormElement::DoesReadOnlyApply() const
1992
0
{
1993
0
  int32_t type = ControlType();
1994
0
  if (!(type & NS_FORM_INPUT_ELEMENT) && type != NS_FORM_TEXTAREA) {
1995
0
    return false;
1996
0
  }
1997
0
1998
0
  switch (type)
1999
0
  {
2000
0
    case NS_FORM_INPUT_HIDDEN:
2001
0
    case NS_FORM_INPUT_BUTTON:
2002
0
    case NS_FORM_INPUT_IMAGE:
2003
0
    case NS_FORM_INPUT_RESET:
2004
0
    case NS_FORM_INPUT_SUBMIT:
2005
0
    case NS_FORM_INPUT_RADIO:
2006
0
    case NS_FORM_INPUT_FILE:
2007
0
    case NS_FORM_INPUT_CHECKBOX:
2008
0
    case NS_FORM_INPUT_RANGE:
2009
0
    case NS_FORM_INPUT_COLOR:
2010
0
      return false;
2011
#ifdef DEBUG
2012
    case NS_FORM_TEXTAREA:
2013
    case NS_FORM_INPUT_TEXT:
2014
    case NS_FORM_INPUT_PASSWORD:
2015
    case NS_FORM_INPUT_SEARCH:
2016
    case NS_FORM_INPUT_TEL:
2017
    case NS_FORM_INPUT_EMAIL:
2018
    case NS_FORM_INPUT_URL:
2019
    case NS_FORM_INPUT_NUMBER:
2020
    case NS_FORM_INPUT_DATE:
2021
    case NS_FORM_INPUT_TIME:
2022
    case NS_FORM_INPUT_MONTH:
2023
    case NS_FORM_INPUT_WEEK:
2024
    case NS_FORM_INPUT_DATETIME_LOCAL:
2025
      return true;
2026
    default:
2027
      MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesReadOnlyApply()");
2028
      return true;
2029
#else // DEBUG
2030
0
    default:
2031
0
      return true;
2032
0
#endif // DEBUG
2033
0
  }
2034
0
}
2035
2036
bool
2037
nsGenericHTMLFormElement::IsHTMLFocusable(bool aWithMouse,
2038
                                          bool* aIsFocusable,
2039
                                          int32_t* aTabIndex)
2040
0
{
2041
0
  if (nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex)) {
2042
0
    return true;
2043
0
  }
2044
0
2045
#ifdef XP_MACOSX
2046
  *aIsFocusable =
2047
    (!aWithMouse || nsFocusManager::sMouseFocusesFormControl) && *aIsFocusable;
2048
#endif
2049
0
  return false;
2050
0
}
2051
2052
EventStates
2053
nsGenericHTMLFormElement::IntrinsicState() const
2054
0
{
2055
0
  // If you add attribute-dependent states here, you need to add them them to
2056
0
  // AfterSetAttr too.  And add them to AfterSetAttr for all subclasses that
2057
0
  // implement IntrinsicState() and are affected by that attribute.
2058
0
  EventStates state = nsGenericHTMLElement::IntrinsicState();
2059
0
2060
0
  if (mForm && mForm->IsDefaultSubmitElement(this)) {
2061
0
      NS_ASSERTION(IsSubmitControl(),
2062
0
                   "Default submit element that isn't a submit control.");
2063
0
      // We are the default submit element (:default)
2064
0
      state |= NS_EVENT_STATE_DEFAULT;
2065
0
  }
2066
0
2067
0
  // Make the text controls read-write
2068
0
  if (!state.HasState(NS_EVENT_STATE_MOZ_READWRITE) &&
2069
0
      DoesReadOnlyApply()) {
2070
0
    if (!GetBoolAttr(nsGkAtoms::readonly)) {
2071
0
      state |= NS_EVENT_STATE_MOZ_READWRITE;
2072
0
      state &= ~NS_EVENT_STATE_MOZ_READONLY;
2073
0
    }
2074
0
  }
2075
0
2076
0
  return state;
2077
0
}
2078
2079
nsGenericHTMLFormElement::FocusTristate
2080
nsGenericHTMLFormElement::FocusState()
2081
0
{
2082
0
  // We can't be focused if we aren't in a (composed) document
2083
0
  nsIDocument* doc = GetComposedDoc();
2084
0
  if (!doc)
2085
0
    return eUnfocusable;
2086
0
2087
0
  // first see if we are disabled or not. If disabled then do nothing.
2088
0
  if (IsDisabled()) {
2089
0
    return eUnfocusable;
2090
0
  }
2091
0
2092
0
  // If the window is not active, do not allow the focus to bring the
2093
0
  // window to the front.  We update the focus controller, but do
2094
0
  // nothing else.
2095
0
  if (nsPIDOMWindowOuter* win = doc->GetWindow()) {
2096
0
    nsCOMPtr<nsPIDOMWindowOuter> rootWindow = win->GetPrivateRoot();
2097
0
2098
0
    nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
2099
0
    if (fm && rootWindow) {
2100
0
      nsCOMPtr<mozIDOMWindowProxy> activeWindow;
2101
0
      fm->GetActiveWindow(getter_AddRefs(activeWindow));
2102
0
      if (activeWindow == rootWindow) {
2103
0
        return eActiveWindow;
2104
0
      }
2105
0
    }
2106
0
  }
2107
0
2108
0
  return eInactiveWindow;
2109
0
}
2110
2111
Element*
2112
nsGenericHTMLFormElement::AddFormIdObserver()
2113
0
{
2114
0
  nsAutoString formId;
2115
0
  DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
2116
0
  GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId);
2117
0
  NS_ASSERTION(!formId.IsEmpty(),
2118
0
               "@form value should not be the empty string!");
2119
0
  RefPtr<nsAtom> atom = NS_Atomize(formId);
2120
0
2121
0
  return docOrShadow->AddIDTargetObserver(atom, FormIdUpdated, this, false);
2122
0
}
2123
2124
void
2125
nsGenericHTMLFormElement::RemoveFormIdObserver()
2126
0
{
2127
0
  DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
2128
0
  if (!docOrShadow) {
2129
0
    return;
2130
0
  }
2131
0
2132
0
  nsAutoString formId;
2133
0
  GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId);
2134
0
  NS_ASSERTION(!formId.IsEmpty(),
2135
0
               "@form value should not be the empty string!");
2136
0
  RefPtr<nsAtom> atom = NS_Atomize(formId);
2137
0
2138
0
  docOrShadow->RemoveIDTargetObserver(atom, FormIdUpdated, this, false);
2139
0
}
2140
2141
2142
/* static */
2143
bool
2144
nsGenericHTMLFormElement::FormIdUpdated(Element* aOldElement,
2145
                                        Element* aNewElement,
2146
                                        void* aData)
2147
0
{
2148
0
  nsGenericHTMLFormElement* element =
2149
0
    static_cast<nsGenericHTMLFormElement*>(aData);
2150
0
2151
0
  NS_ASSERTION(element->IsHTMLElement(), "aData should be an HTML element");
2152
0
2153
0
  element->UpdateFormOwner(false, aNewElement);
2154
0
2155
0
  return true;
2156
0
}
2157
2158
bool
2159
nsGenericHTMLFormElement::IsElementDisabledForEvents(EventMessage aMessage,
2160
                                                     nsIFrame* aFrame)
2161
0
{
2162
0
  switch (aMessage) {
2163
0
    case eMouseMove:
2164
0
    case eMouseOver:
2165
0
    case eMouseOut:
2166
0
    case eMouseEnter:
2167
0
    case eMouseLeave:
2168
0
    case ePointerMove:
2169
0
    case ePointerOver:
2170
0
    case ePointerOut:
2171
0
    case ePointerEnter:
2172
0
    case ePointerLeave:
2173
0
    case eWheel:
2174
0
    case eLegacyMouseLineOrPageScroll:
2175
0
    case eLegacyMousePixelScroll:
2176
0
      return false;
2177
0
    default:
2178
0
      break;
2179
0
  }
2180
0
2181
0
  // FIXME(emilio): This poking at the style of the frame is slightly bogus
2182
0
  // unless we flush before every event, which we don't really want to do.
2183
0
  if (aFrame && aFrame->StyleUI()->mUserInput == StyleUserInput::None) {
2184
0
    return true;
2185
0
  }
2186
0
2187
0
  return IsDisabled();
2188
0
}
2189
2190
void
2191
nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree,
2192
                                          Element* aFormIdElement)
2193
0
{
2194
0
  MOZ_ASSERT(!aBindToTree || !aFormIdElement,
2195
0
             "aFormIdElement shouldn't be set if aBindToTree is true!");
2196
0
2197
0
  bool needStateUpdate = false;
2198
0
  if (!aBindToTree) {
2199
0
    needStateUpdate = mForm && mForm->IsDefaultSubmitElement(this);
2200
0
    ClearForm(true, false);
2201
0
  }
2202
0
2203
0
  HTMLFormElement *oldForm = mForm;
2204
0
2205
0
  if (!mForm) {
2206
0
    // If @form is set, we have to use that to find the form.
2207
0
    nsAutoString formId;
2208
0
    if (GetAttr(kNameSpaceID_None, nsGkAtoms::form, formId)) {
2209
0
      if (!formId.IsEmpty()) {
2210
0
        Element* element = nullptr;
2211
0
2212
0
        if (aBindToTree) {
2213
0
          element = AddFormIdObserver();
2214
0
        } else {
2215
0
          element = aFormIdElement;
2216
0
        }
2217
0
2218
0
        NS_ASSERTION(!IsInComposedDoc() ||
2219
0
                     element == GetUncomposedDocOrConnectedShadowRoot()->
2220
0
                       GetElementById(formId),
2221
0
                     "element should be equals to the current element "
2222
0
                     "associated with the id in @form!");
2223
0
2224
0
        if (element &&
2225
0
            element->IsHTMLElement(nsGkAtoms::form) &&
2226
0
            nsContentUtils::IsInSameAnonymousTree(this, element)) {
2227
0
          SetForm(static_cast<HTMLFormElement*>(element), aBindToTree);
2228
0
        }
2229
0
      }
2230
0
     } else {
2231
0
      // We now have a parent, so we may have picked up an ancestor form.  Search
2232
0
      // for it.  Note that if mForm is already set we don't want to do this,
2233
0
      // because that means someone (probably the content sink) has already set
2234
0
      // it to the right value.  Also note that even if being bound here didn't
2235
0
      // change our parent, we still need to search, since our parent chain
2236
0
      // probably changed _somewhere_.
2237
0
      SetForm(FindAncestorForm(), aBindToTree);
2238
0
    }
2239
0
  }
2240
0
2241
0
  if (mForm && !HasFlag(ADDED_TO_FORM)) {
2242
0
    // Now we need to add ourselves to the form
2243
0
    nsAutoString nameVal, idVal;
2244
0
    GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal);
2245
0
    GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal);
2246
0
2247
0
    SetFlags(ADDED_TO_FORM);
2248
0
2249
0
    // Notify only if we just found this mForm.
2250
0
    mForm->AddElement(this, true, oldForm == nullptr);
2251
0
2252
0
    if (!nameVal.IsEmpty()) {
2253
0
      mForm->AddElementToTable(this, nameVal);
2254
0
    }
2255
0
2256
0
    if (!idVal.IsEmpty()) {
2257
0
      mForm->AddElementToTable(this, idVal);
2258
0
    }
2259
0
  }
2260
0
2261
0
  if (mForm != oldForm || needStateUpdate) {
2262
0
    UpdateState(true);
2263
0
  }
2264
0
}
2265
2266
void
2267
nsGenericHTMLFormElement::UpdateFieldSet(bool aNotify)
2268
0
{
2269
0
  nsIContent* parent = nullptr;
2270
0
  nsIContent* prev = nullptr;
2271
0
2272
0
  // Don't walk out of anonymous subtrees. Note the similar code in
2273
0
  // nsGenericHTMLElement::FindAncestorForm.
2274
0
  nsIContent* bindingParent = GetBindingParent();
2275
0
2276
0
  for (parent = GetParent(); parent && parent != bindingParent;
2277
0
       prev = parent, parent = parent->GetParent()) {
2278
0
    HTMLFieldSetElement* fieldset =
2279
0
      HTMLFieldSetElement::FromNode(parent);
2280
0
    if (fieldset &&
2281
0
        (!prev || fieldset->GetFirstLegend() != prev)) {
2282
0
      if (mFieldSet == fieldset) {
2283
0
        // We already have the right fieldset;
2284
0
        return;
2285
0
      }
2286
0
2287
0
      if (mFieldSet) {
2288
0
        mFieldSet->RemoveElement(this);
2289
0
      }
2290
0
      mFieldSet = fieldset;
2291
0
      fieldset->AddElement(this);
2292
0
2293
0
      // The disabled state may have changed
2294
0
      FieldSetDisabledChanged(aNotify);
2295
0
      return;
2296
0
    }
2297
0
  }
2298
0
2299
0
  // No fieldset found.
2300
0
  if (mFieldSet) {
2301
0
    mFieldSet->RemoveElement(this);
2302
0
    mFieldSet = nullptr;
2303
0
    // The disabled state may have changed
2304
0
    FieldSetDisabledChanged(aNotify);
2305
0
  }
2306
0
}
2307
2308
void
2309
nsGenericHTMLFormElement::UpdateDisabledState(bool aNotify)
2310
0
{
2311
0
  if (!CanBeDisabled()) {
2312
0
    return;
2313
0
  }
2314
0
2315
0
  bool isDisabled = HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
2316
0
  if (!isDisabled && mFieldSet) {
2317
0
    isDisabled = mFieldSet->IsDisabled();
2318
0
  }
2319
0
2320
0
  EventStates disabledStates;
2321
0
  if (isDisabled) {
2322
0
    disabledStates |= NS_EVENT_STATE_DISABLED;
2323
0
  } else {
2324
0
    disabledStates |= NS_EVENT_STATE_ENABLED;
2325
0
  }
2326
0
2327
0
  EventStates oldDisabledStates = State() & DISABLED_STATES;
2328
0
  EventStates changedStates = disabledStates ^ oldDisabledStates;
2329
0
2330
0
  if (!changedStates.IsEmpty()) {
2331
0
    ToggleStates(changedStates, aNotify);
2332
0
  }
2333
0
}
2334
2335
void
2336
nsGenericHTMLFormElement::UpdateRequiredState(bool aIsRequired, bool aNotify)
2337
0
{
2338
#ifdef DEBUG
2339
  int32_t type = ControlType();
2340
#endif
2341
0
  MOZ_ASSERT((type & NS_FORM_INPUT_ELEMENT) ||
2342
0
              type == NS_FORM_SELECT ||
2343
0
              type == NS_FORM_TEXTAREA,
2344
0
             "This should be called only on types that @required applies");
2345
0
2346
#ifdef DEBUG
2347
  HTMLInputElement* input = HTMLInputElement::FromNode(this);
2348
  if (input) {
2349
    MOZ_ASSERT(input->DoesRequiredApply(),
2350
               "This should be called only on input types that @required applies");
2351
  }
2352
#endif
2353
2354
0
  EventStates requiredStates;
2355
0
  if (aIsRequired) {
2356
0
    requiredStates |= NS_EVENT_STATE_REQUIRED;
2357
0
  } else {
2358
0
    requiredStates |= NS_EVENT_STATE_OPTIONAL;
2359
0
  }
2360
0
2361
0
  EventStates oldRequiredStates = State() & REQUIRED_STATES;
2362
0
  EventStates changedStates = requiredStates ^ oldRequiredStates;
2363
0
2364
0
  if (!changedStates.IsEmpty()) {
2365
0
    ToggleStates(changedStates, aNotify);
2366
0
  }
2367
0
}
2368
2369
void
2370
nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify)
2371
0
{
2372
0
  UpdateDisabledState(aNotify);
2373
0
}
2374
2375
bool
2376
nsGenericHTMLFormElement::IsLabelable() const
2377
0
{
2378
0
  // TODO: keygen should be in that list, see bug 101019.
2379
0
  uint32_t type = ControlType();
2380
0
  return (type & NS_FORM_INPUT_ELEMENT && type != NS_FORM_INPUT_HIDDEN) ||
2381
0
         type & NS_FORM_BUTTON_ELEMENT ||
2382
0
         // type == NS_FORM_KEYGEN ||
2383
0
         type == NS_FORM_OUTPUT ||
2384
0
         type == NS_FORM_SELECT ||
2385
0
         type == NS_FORM_TEXTAREA;
2386
0
}
2387
2388
void
2389
nsGenericHTMLFormElement::GetFormAction(nsString& aValue)
2390
0
{
2391
0
  uint32_t type = ControlType();
2392
0
  if (!(type & NS_FORM_INPUT_ELEMENT) && !(type & NS_FORM_BUTTON_ELEMENT)) {
2393
0
    return;
2394
0
  }
2395
0
2396
0
  if (!GetAttr(kNameSpaceID_None, nsGkAtoms::formaction, aValue) ||
2397
0
      aValue.IsEmpty()) {
2398
0
    nsIDocument* document = OwnerDoc();
2399
0
    nsIURI* docURI = document->GetDocumentURI();
2400
0
    if (docURI) {
2401
0
      nsAutoCString spec;
2402
0
      nsresult rv = docURI->GetSpec(spec);
2403
0
      if (NS_FAILED(rv)) {
2404
0
        return;
2405
0
      }
2406
0
2407
0
      CopyUTF8toUTF16(spec, aValue);
2408
0
    }
2409
0
  } else {
2410
0
    GetURIAttr(nsGkAtoms::formaction, nullptr, aValue);
2411
0
  }
2412
0
}
2413
2414
//----------------------------------------------------------------------
2415
2416
void
2417
nsGenericHTMLElement::Click(CallerType aCallerType)
2418
0
{
2419
0
  if (HandlingClick())
2420
0
    return;
2421
0
2422
0
  // Strong in case the event kills it
2423
0
  nsCOMPtr<nsIDocument> doc = GetComposedDoc();
2424
0
2425
0
  RefPtr<nsPresContext> context;
2426
0
  if (doc) {
2427
0
    context = doc->GetPresContext();
2428
0
  }
2429
0
2430
0
  SetHandlingClick();
2431
0
2432
0
  // Mark this event trusted if Click() is called from system code.
2433
0
  WidgetMouseEvent event(aCallerType == CallerType::System,
2434
0
                         eMouseClick, nullptr, WidgetMouseEvent::eReal);
2435
0
  event.mFlags.mIsPositionless = true;
2436
0
  event.inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
2437
0
2438
0
  EventDispatcher::Dispatch(static_cast<nsIContent*>(this), context, &event);
2439
0
2440
0
  ClearHandlingClick();
2441
0
}
2442
2443
bool
2444
nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse,
2445
                                      bool *aIsFocusable,
2446
                                      int32_t *aTabIndex)
2447
0
{
2448
0
  nsIDocument* doc = GetComposedDoc();
2449
0
  if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) {
2450
0
    // In designMode documents we only allow focusing the document.
2451
0
    if (aTabIndex) {
2452
0
      *aTabIndex = -1;
2453
0
    }
2454
0
2455
0
    *aIsFocusable = false;
2456
0
2457
0
    return true;
2458
0
  }
2459
0
2460
0
  int32_t tabIndex = TabIndex();
2461
0
  bool disabled = false;
2462
0
  bool disallowOverridingFocusability = true;
2463
0
2464
0
  if (IsEditableRoot()) {
2465
0
    // Editable roots should always be focusable.
2466
0
    disallowOverridingFocusability = true;
2467
0
2468
0
    // Ignore the disabled attribute in editable contentEditable/designMode
2469
0
    // roots.
2470
0
    if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
2471
0
      // The default value for tabindex should be 0 for editable
2472
0
      // contentEditable roots.
2473
0
      tabIndex = 0;
2474
0
    }
2475
0
  }
2476
0
  else {
2477
0
    disallowOverridingFocusability = false;
2478
0
2479
0
    // Just check for disabled attribute on form controls
2480
0
    disabled = IsDisabled();
2481
0
    if (disabled) {
2482
0
      tabIndex = -1;
2483
0
    }
2484
0
  }
2485
0
2486
0
  if (aTabIndex) {
2487
0
    *aTabIndex = tabIndex;
2488
0
  }
2489
0
2490
0
  // If a tabindex is specified at all, or the default tabindex is 0, we're focusable
2491
0
  *aIsFocusable =
2492
0
    (tabIndex >= 0 || (!disabled && HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)));
2493
0
2494
0
  return disallowOverridingFocusability;
2495
0
}
2496
2497
void
2498
nsGenericHTMLElement::RegUnRegAccessKey(bool aDoReg)
2499
0
{
2500
0
  // first check to see if we have an access key
2501
0
  nsAutoString accessKey;
2502
0
  GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
2503
0
  if (accessKey.IsEmpty()) {
2504
0
    return;
2505
0
  }
2506
0
2507
0
  // We have an access key, so get the ESM from the pres context.
2508
0
  nsPresContext* presContext = GetPresContext(eForUncomposedDoc);
2509
0
2510
0
  if (presContext) {
2511
0
    EventStateManager* esm = presContext->EventStateManager();
2512
0
2513
0
    // Register or unregister as appropriate.
2514
0
    if (aDoReg) {
2515
0
      esm->RegisterAccessKey(this, (uint32_t)accessKey.First());
2516
0
    } else {
2517
0
      esm->UnregisterAccessKey(this, (uint32_t)accessKey.First());
2518
0
    }
2519
0
  }
2520
0
}
2521
2522
bool
2523
nsGenericHTMLElement::PerformAccesskey(bool aKeyCausesActivation,
2524
                                       bool aIsTrustedEvent)
2525
0
{
2526
0
  nsPresContext* presContext = GetPresContext(eForUncomposedDoc);
2527
0
  if (!presContext) {
2528
0
    return false;
2529
0
  }
2530
0
2531
0
  // It's hard to say what HTML4 wants us to do in all cases.
2532
0
  bool focused = true;
2533
0
  nsFocusManager* fm = nsFocusManager::GetFocusManager();
2534
0
  if (fm) {
2535
0
    fm->SetFocus(this, nsIFocusManager::FLAG_BYKEY);
2536
0
2537
0
    // Return true if the element became the current focus within its window.
2538
0
    nsPIDOMWindowOuter* window = OwnerDoc()->GetWindow();
2539
0
    focused = (window && window->GetFocusedElement());
2540
0
  }
2541
0
2542
0
  if (aKeyCausesActivation) {
2543
0
    // Click on it if the users prefs indicate to do so.
2544
0
    nsAutoPopupStatePusher popupStatePusher(aIsTrustedEvent ?
2545
0
                                            openAllowed : openAbused);
2546
0
    DispatchSimulatedClick(this, aIsTrustedEvent, presContext);
2547
0
  }
2548
0
2549
0
  return focused;
2550
0
}
2551
2552
nsresult
2553
nsGenericHTMLElement::DispatchSimulatedClick(nsGenericHTMLElement* aElement,
2554
                                             bool aIsTrusted,
2555
                                             nsPresContext* aPresContext)
2556
0
{
2557
0
  WidgetMouseEvent event(aIsTrusted, eMouseClick, nullptr,
2558
0
                         WidgetMouseEvent::eReal);
2559
0
  event.inputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
2560
0
  event.mFlags.mIsPositionless = true;
2561
0
  return EventDispatcher::Dispatch(ToSupports(aElement), aPresContext, &event);
2562
0
}
2563
2564
already_AddRefed<TextEditor>
2565
nsGenericHTMLElement::GetAssociatedEditor()
2566
0
{
2567
0
  // If contenteditable is ever implemented, it might need to do something different here?
2568
0
2569
0
  RefPtr<TextEditor> textEditor = GetTextEditorInternal();
2570
0
  return textEditor.forget();
2571
0
}
2572
2573
// static
2574
void
2575
nsGenericHTMLElement::SyncEditorsOnSubtree(nsIContent* content)
2576
0
{
2577
0
  /* Sync this node */
2578
0
  nsGenericHTMLElement* element = FromNode(content);
2579
0
  if (element) {
2580
0
    RefPtr<TextEditor> textEditor = element->GetAssociatedEditor();
2581
0
    if (textEditor) {
2582
0
      textEditor->SyncRealTimeSpell();
2583
0
    }
2584
0
  }
2585
0
2586
0
  /* Sync all children */
2587
0
  for (nsIContent* child = content->GetFirstChild();
2588
0
       child;
2589
0
       child = child->GetNextSibling()) {
2590
0
    SyncEditorsOnSubtree(child);
2591
0
  }
2592
0
}
2593
2594
void
2595
nsGenericHTMLElement::RecompileScriptEventListeners()
2596
0
{
2597
0
    int32_t i, count = mAttrs.AttrCount();
2598
0
    for (i = 0; i < count; ++i) {
2599
0
        const nsAttrName *name = mAttrs.AttrNameAt(i);
2600
0
2601
0
        // Eventlistenener-attributes are always in the null namespace
2602
0
        if (!name->IsAtom()) {
2603
0
            continue;
2604
0
        }
2605
0
2606
0
        nsAtom *attr = name->Atom();
2607
0
        if (!IsEventAttributeName(attr)) {
2608
0
            continue;
2609
0
        }
2610
0
2611
0
        nsAutoString value;
2612
0
        GetAttr(kNameSpaceID_None, attr, value);
2613
0
        SetEventHandler(attr, value, true);
2614
0
    }
2615
0
}
2616
2617
bool
2618
nsGenericHTMLElement::IsEditableRoot() const
2619
0
{
2620
0
  nsIDocument *document = GetComposedDoc();
2621
0
  if (!document) {
2622
0
    return false;
2623
0
  }
2624
0
2625
0
  if (document->HasFlag(NODE_IS_EDITABLE)) {
2626
0
    return false;
2627
0
  }
2628
0
2629
0
  if (GetContentEditableValue() != eTrue) {
2630
0
    return false;
2631
0
  }
2632
0
2633
0
  nsIContent *parent = GetParent();
2634
0
2635
0
  return !parent || !parent->HasFlag(NODE_IS_EDITABLE);
2636
0
}
2637
2638
static void
2639
MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument)
2640
0
{
2641
0
  // If aContent is not an element, we just need to update its
2642
0
  // internal editable state and don't need to notify anyone about
2643
0
  // that.  For elements, we need to send a ContentStateChanged
2644
0
  // notification.
2645
0
  if (!aContent->IsElement()) {
2646
0
    aContent->UpdateEditableState(false);
2647
0
    return;
2648
0
  }
2649
0
2650
0
  Element *element = aContent->AsElement();
2651
0
2652
0
  element->UpdateEditableState(true);
2653
0
2654
0
  for (nsIContent *child = aContent->GetFirstChild();
2655
0
       child;
2656
0
       child = child->GetNextSibling()) {
2657
0
    if (!child->IsElement() ||
2658
0
        !child->AsElement()->HasAttr(kNameSpaceID_None,
2659
0
                                     nsGkAtoms::contenteditable)) {
2660
0
      MakeContentDescendantsEditable(child, aDocument);
2661
0
    }
2662
0
  }
2663
0
}
2664
2665
void
2666
nsGenericHTMLElement::ChangeEditableState(int32_t aChange)
2667
0
{
2668
0
  nsIDocument* document = GetComposedDoc();
2669
0
  if (!document) {
2670
0
    return;
2671
0
  }
2672
0
2673
0
  if (aChange != 0) {
2674
0
    nsCOMPtr<nsIHTMLDocument> htmlDocument =
2675
0
      do_QueryInterface(document);
2676
0
    if (htmlDocument) {
2677
0
      htmlDocument->ChangeContentEditableCount(this, aChange);
2678
0
    }
2679
0
2680
0
    nsIContent* parent = GetParent();
2681
0
    // Don't update across Shadow DOM boundary.
2682
0
    while (parent && parent->IsElement()) {
2683
0
      parent->ChangeEditableDescendantCount(aChange);
2684
0
      parent = parent->GetParent();
2685
0
    }
2686
0
  }
2687
0
2688
0
  if (document->HasFlag(NODE_IS_EDITABLE)) {
2689
0
    document = nullptr;
2690
0
  }
2691
0
2692
0
  // MakeContentDescendantsEditable is going to call ContentStateChanged for
2693
0
  // this element and all descendants if editable state has changed.
2694
0
  // We might as well wrap it all in one script blocker.
2695
0
  nsAutoScriptBlocker scriptBlocker;
2696
0
  MakeContentDescendantsEditable(this, document);
2697
0
}
2698
2699
2700
//----------------------------------------------------------------------
2701
2702
nsGenericHTMLFormElementWithState::nsGenericHTMLFormElementWithState(
2703
    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, uint8_t aType
2704
  )
2705
  : nsGenericHTMLFormElement(std::move(aNodeInfo), aType)
2706
0
{
2707
0
  mStateKey.SetIsVoid(true);
2708
0
}
2709
2710
nsresult
2711
nsGenericHTMLFormElementWithState::GenerateStateKey()
2712
0
{
2713
0
  // Keep the key if already computed
2714
0
  if (!mStateKey.IsVoid()) {
2715
0
    return NS_OK;
2716
0
  }
2717
0
2718
0
  nsIDocument* doc = GetUncomposedDoc();
2719
0
  if (!doc) {
2720
0
    return NS_OK;
2721
0
  }
2722
0
2723
0
  // Generate the state key
2724
0
  nsresult rv = nsContentUtils::GenerateStateKey(this, doc, mStateKey);
2725
0
2726
0
  if (NS_FAILED(rv)) {
2727
0
    mStateKey.SetIsVoid(true);
2728
0
    return rv;
2729
0
  }
2730
0
2731
0
  // If the state key is blank, this is anonymous content or for whatever
2732
0
  // reason we are not supposed to save/restore state: keep it as such.
2733
0
  if (!mStateKey.IsEmpty()) {
2734
0
    // Add something unique to content so layout doesn't muck us up.
2735
0
    mStateKey += "-C";
2736
0
  }
2737
0
  return NS_OK;
2738
0
}
2739
2740
PresState*
2741
nsGenericHTMLFormElementWithState::GetPrimaryPresState()
2742
0
{
2743
0
  if (mStateKey.IsEmpty()) {
2744
0
    return nullptr;
2745
0
  }
2746
0
2747
0
  nsCOMPtr<nsILayoutHistoryState> history = GetLayoutHistory(false);
2748
0
2749
0
  if (!history) {
2750
0
    return nullptr;
2751
0
  }
2752
0
2753
0
  // Get the pres state for this key, if it doesn't exist, create one.
2754
0
  PresState* result = history->GetState(mStateKey);
2755
0
  if (!result) {
2756
0
    UniquePtr<PresState> newState = NewPresState();
2757
0
    result = newState.get();
2758
0
    history->AddState(mStateKey, std::move(newState));
2759
0
  }
2760
0
2761
0
  return result;
2762
0
}
2763
2764
already_AddRefed<nsILayoutHistoryState>
2765
nsGenericHTMLFormElementWithState::GetLayoutHistory(bool aRead)
2766
0
{
2767
0
  nsCOMPtr<nsIDocument> doc = GetUncomposedDoc();
2768
0
  if (!doc) {
2769
0
    return nullptr;
2770
0
  }
2771
0
2772
0
  //
2773
0
  // Get the history
2774
0
  //
2775
0
  nsCOMPtr<nsILayoutHistoryState> history = doc->GetLayoutHistoryState();
2776
0
  if (!history) {
2777
0
    return nullptr;
2778
0
  }
2779
0
2780
0
  if (aRead && !history->HasStates()) {
2781
0
    return nullptr;
2782
0
  }
2783
0
2784
0
  return history.forget();
2785
0
}
2786
2787
bool
2788
nsGenericHTMLFormElementWithState::RestoreFormControlState()
2789
0
{
2790
0
  if (mStateKey.IsEmpty()) {
2791
0
    return false;
2792
0
  }
2793
0
2794
0
  nsCOMPtr<nsILayoutHistoryState> history =
2795
0
    GetLayoutHistory(true);
2796
0
  if (!history) {
2797
0
    return false;
2798
0
  }
2799
0
2800
0
  // Get the pres state for this key
2801
0
  PresState* state = history->GetState(mStateKey);
2802
0
  if (state) {
2803
0
    bool result = RestoreState(state);
2804
0
    history->RemoveState(mStateKey);
2805
0
    return result;
2806
0
  }
2807
0
2808
0
  return false;
2809
0
}
2810
2811
void
2812
nsGenericHTMLFormElementWithState::NodeInfoChanged(nsIDocument* aOldDoc)
2813
0
{
2814
0
  nsGenericHTMLElement::NodeInfoChanged(aOldDoc);
2815
0
  mStateKey.SetIsVoid(true);
2816
0
}
2817
2818
nsSize
2819
nsGenericHTMLElement::GetWidthHeightForImage(RefPtr<imgRequestProxy>& aImageRequest)
2820
0
{
2821
0
  nsSize size(0,0);
2822
0
2823
0
  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
2824
0
2825
0
  if (frame) {
2826
0
    size = frame->GetContentRect().Size();
2827
0
2828
0
    size.width = nsPresContext::AppUnitsToIntCSSPixels(size.width);
2829
0
    size.height = nsPresContext::AppUnitsToIntCSSPixels(size.height);
2830
0
  } else {
2831
0
    const nsAttrValue* value;
2832
0
    nsCOMPtr<imgIContainer> image;
2833
0
    if (aImageRequest) {
2834
0
      aImageRequest->GetImage(getter_AddRefs(image));
2835
0
    }
2836
0
2837
0
    if ((value = GetParsedAttr(nsGkAtoms::width)) &&
2838
0
        value->Type() == nsAttrValue::eInteger) {
2839
0
      size.width = value->GetIntegerValue();
2840
0
    } else if (image) {
2841
0
      image->GetWidth(&size.width);
2842
0
    }
2843
0
2844
0
    if ((value = GetParsedAttr(nsGkAtoms::height)) &&
2845
0
        value->Type() == nsAttrValue::eInteger) {
2846
0
      size.height = value->GetIntegerValue();
2847
0
    } else if (image) {
2848
0
      image->GetHeight(&size.height);
2849
0
    }
2850
0
  }
2851
0
2852
0
  NS_ASSERTION(size.width >= 0, "negative width");
2853
0
  NS_ASSERTION(size.height >= 0, "negative height");
2854
0
  return size;
2855
0
}
2856
2857
bool
2858
nsGenericHTMLElement::IsEventAttributeNameInternal(nsAtom *aName)
2859
0
{
2860
0
  return nsContentUtils::IsEventAttributeName(aName, EventNameType_HTML);
2861
0
}
2862
2863
/**
2864
 * Construct a URI from a string, as an element.src attribute
2865
 * would be set to. Helper for the media elements.
2866
 */
2867
nsresult
2868
nsGenericHTMLElement::NewURIFromString(const nsAString& aURISpec,
2869
                                       nsIURI** aURI)
2870
0
{
2871
0
  NS_ENSURE_ARG_POINTER(aURI);
2872
0
2873
0
  *aURI = nullptr;
2874
0
2875
0
  nsCOMPtr<nsIDocument> doc = OwnerDoc();
2876
0
2877
0
  nsCOMPtr<nsIURI> baseURI = GetBaseURI();
2878
0
  nsresult rv = nsContentUtils::NewURIWithDocumentCharset(aURI, aURISpec,
2879
0
                                                          doc, baseURI);
2880
0
  NS_ENSURE_SUCCESS(rv, rv);
2881
0
2882
0
  bool equal;
2883
0
  if (aURISpec.IsEmpty() &&
2884
0
      doc->GetDocumentURI() &&
2885
0
      NS_SUCCEEDED(doc->GetDocumentURI()->Equals(*aURI, &equal)) &&
2886
0
      equal) {
2887
0
    // Assume an element can't point to a fragment of its embedding
2888
0
    // document. Fail here instead of returning the recursive URI
2889
0
    // and waiting for the subsequent load to fail.
2890
0
    NS_RELEASE(*aURI);
2891
0
    return NS_ERROR_DOM_INVALID_STATE_ERR;
2892
0
  }
2893
0
2894
0
  return NS_OK;
2895
0
}
2896
2897
// https://html.spec.whatwg.org/#being-rendered
2898
//
2899
// With a gotcha for display contents:
2900
//   https://github.com/whatwg/html/issues/3947
2901
static bool IsRendered(const Element& aElement)
2902
0
{
2903
0
  return aElement.GetPrimaryFrame() || aElement.IsDisplayContents();
2904
0
}
2905
2906
void
2907
nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue,
2908
                                   mozilla::ErrorResult& aError)
2909
0
{
2910
0
  // innerText depends on layout. For example, white space processing is
2911
0
  // something that happens during reflow and which must be reflected by
2912
0
  // innerText.  So for:
2913
0
  //
2914
0
  //   <div style="white-space:normal"> A     B C </div>
2915
0
  //
2916
0
  // innerText should give "A B C".
2917
0
  //
2918
0
  // The approach taken here to avoid the expense of reflow is to flush style
2919
0
  // and then see whether it's necessary to flush layout afterwards. Flushing
2920
0
  // layout can be skipped if we can detect that the element or its descendants
2921
0
  // are not dirty.
2922
0
2923
0
  // Obtain the composed doc to handle elements in Shadow DOM.
2924
0
  nsIDocument* doc = GetComposedDoc();
2925
0
  if (doc) {
2926
0
    doc->FlushPendingNotifications(FlushType::Style);
2927
0
  }
2928
0
2929
0
  // Elements with `display: content` will not have a frame. To handle Shadow
2930
0
  // DOM, walk the flattened tree looking for parent frame.
2931
0
  nsIFrame* frame = GetPrimaryFrame();
2932
0
  if (IsDisplayContents()) {
2933
0
    for (Element* parent = GetFlattenedTreeParentElement();
2934
0
         parent;
2935
0
         parent = parent->GetFlattenedTreeParentElement())
2936
0
    {
2937
0
      frame = parent->GetPrimaryFrame();
2938
0
      if (frame) {
2939
0
        break;
2940
0
      }
2941
0
    }
2942
0
  }
2943
0
2944
0
  // Check for dirty reflow roots in the subtree from targetFrame; this requires
2945
0
  // a reflow flush.
2946
0
  bool dirty = frame && frame->PresShell()->FrameIsAncestorOfDirtyRoot(frame);
2947
0
2948
0
  // The way we do that is by checking whether the element has either of the two
2949
0
  // dirty bits (NS_FRAME_IS_DIRTY or NS_FRAME_HAS_DIRTY_DESCENDANTS) or if any
2950
0
  // ancestor has NS_FRAME_IS_DIRTY.  We need to check for NS_FRAME_IS_DIRTY on
2951
0
  // ancestors since that is something that implies NS_FRAME_IS_DIRTY on all
2952
0
  // descendants.
2953
0
  dirty |= frame && frame->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
2954
0
  while (!dirty && frame) {
2955
0
    dirty |= frame->HasAnyStateBits(NS_FRAME_IS_DIRTY);
2956
0
    frame = frame->GetInFlowParent();
2957
0
  }
2958
0
2959
0
  // Flush layout if we determined a reflow is required.
2960
0
  if (dirty && doc) {
2961
0
    doc->FlushPendingNotifications(FlushType::Layout);
2962
0
  }
2963
0
2964
0
  if (!IsRendered(*this)) {
2965
0
    GetTextContentInternal(aValue, aError);
2966
0
  } else {
2967
0
    nsRange::GetInnerTextNoFlush(aValue, aError, this);
2968
0
  }
2969
0
}
2970
2971
void
2972
nsGenericHTMLElement::SetInnerText(const nsAString& aValue)
2973
0
{
2974
0
  // Batch possible DOMSubtreeModified events.
2975
0
  mozAutoSubtreeModified subtree(OwnerDoc(), nullptr);
2976
0
  FireNodeRemovedForChildren();
2977
0
2978
0
  // Might as well stick a batch around this since we're performing several
2979
0
  // mutations.
2980
0
  mozAutoDocUpdate updateBatch(GetComposedDoc(), true);
2981
0
  nsAutoMutationBatch mb;
2982
0
2983
0
  mb.Init(this, true, false);
2984
0
2985
0
  while (HasChildren()) {
2986
0
    RemoveChildNode(nsINode::GetFirstChild(), true);
2987
0
  }
2988
0
2989
0
  mb.RemovalDone();
2990
0
2991
0
  nsString str;
2992
0
  const char16_t* s = aValue.BeginReading();
2993
0
  const char16_t* end = aValue.EndReading();
2994
0
  while (true) {
2995
0
    if (s != end && *s == '\r' && s + 1 != end && s[1] == '\n') {
2996
0
      // a \r\n pair should only generate one <br>, so just skip the \r
2997
0
      ++s;
2998
0
    }
2999
0
    if (s == end || *s == '\r' || *s == '\n') {
3000
0
      if (!str.IsEmpty()) {
3001
0
        RefPtr<nsTextNode> textContent =
3002
0
          new nsTextNode(NodeInfo()->NodeInfoManager());
3003
0
        textContent->SetText(str, true);
3004
0
        AppendChildTo(textContent, true);
3005
0
      }
3006
0
      if (s == end) {
3007
0
        break;
3008
0
      }
3009
0
      str.Truncate();
3010
0
      RefPtr<mozilla::dom::NodeInfo> ni =
3011
0
        NodeInfo()->NodeInfoManager()->GetNodeInfo(nsGkAtoms::br,
3012
0
          nullptr, kNameSpaceID_XHTML, ELEMENT_NODE);
3013
0
      RefPtr<HTMLBRElement> br = new HTMLBRElement(ni.forget());
3014
0
      AppendChildTo(br, true);
3015
0
    } else {
3016
0
      str.Append(*s);
3017
0
    }
3018
0
    ++s;
3019
0
  }
3020
0
3021
0
  mb.NodesAdded();
3022
0
}