tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsGenericHTMLElement.cpp (142528B)


      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 "nsGenericHTMLElement.h"
      8 
      9 #include "HTMLBRElement.h"
     10 #include "HTMLFieldSetElement.h"
     11 #include "ReferrerInfo.h"
     12 #include "imgIContainer.h"
     13 #include "mozilla/EditorBase.h"
     14 #include "mozilla/ErrorResult.h"
     15 #include "mozilla/EventDispatcher.h"
     16 #include "mozilla/EventListenerManager.h"
     17 #include "mozilla/EventStateManager.h"
     18 #include "mozilla/FocusModel.h"
     19 #include "mozilla/HTMLEditor.h"
     20 #include "mozilla/IMEContentObserver.h"
     21 #include "mozilla/IMEStateManager.h"
     22 #include "mozilla/MappedDeclarationsBuilder.h"
     23 #include "mozilla/Maybe.h"
     24 #include "mozilla/MouseEvents.h"
     25 #include "mozilla/Preferences.h"
     26 #include "mozilla/PresShell.h"
     27 #include "mozilla/PresState.h"
     28 #include "mozilla/StaticPrefs_accessibility.h"
     29 #include "mozilla/StaticPrefs_dom.h"
     30 #include "mozilla/StaticPrefs_layout.h"
     31 #include "mozilla/TextEditor.h"
     32 #include "mozilla/TextEvents.h"
     33 #include "mozilla/dom/AncestorIterator.h"
     34 #include "mozilla/dom/BindContext.h"
     35 #include "mozilla/dom/BindingUtils.h"
     36 #include "mozilla/dom/CommandEvent.h"
     37 #include "mozilla/dom/CustomElementRegistry.h"
     38 #include "mozilla/dom/DirectionalityUtils.h"
     39 #include "mozilla/dom/Document.h"
     40 #include "mozilla/dom/DocumentInlines.h"
     41 #include "mozilla/dom/DocumentOrShadowRoot.h"
     42 #include "mozilla/dom/ElementBinding.h"
     43 #include "mozilla/dom/ElementInternals.h"
     44 #include "mozilla/dom/FetchPriority.h"
     45 #include "mozilla/dom/FormData.h"
     46 #include "mozilla/dom/FromParser.h"
     47 #include "mozilla/dom/HTMLBodyElement.h"
     48 #include "mozilla/dom/HTMLDialogElement.h"
     49 #include "mozilla/dom/HTMLElementBinding.h"
     50 #include "mozilla/dom/HTMLFormElement.h"
     51 #include "mozilla/dom/HTMLInputElement.h"
     52 #include "mozilla/dom/HTMLLabelElement.h"
     53 #include "mozilla/dom/InputEvent.h"
     54 #include "mozilla/dom/Link.h"
     55 #include "mozilla/dom/MouseEventBinding.h"
     56 #include "mozilla/dom/ScriptLoader.h"
     57 #include "mozilla/dom/ToggleEvent.h"
     58 #include "mozilla/dom/TouchEvent.h"
     59 #include "mozilla/dom/UnbindContext.h"
     60 #include "mozilla/intl/Locale.h"
     61 #include "nsAtom.h"
     62 #include "nsAttrValueOrString.h"
     63 #include "nsCOMPtr.h"
     64 #include "nsCaseTreatment.h"
     65 #include "nsComputedDOMStyle.h"
     66 #include "nsContainerFrame.h"
     67 #include "nsContentUtils.h"
     68 #include "nsDOMCSSDeclaration.h"
     69 #include "nsDOMMutationObserver.h"
     70 #include "nsDOMString.h"
     71 #include "nsDOMStringMap.h"
     72 #include "nsDOMTokenList.h"
     73 #include "nsError.h"
     74 #include "nsFocusManager.h"
     75 #include "nsGkAtoms.h"
     76 #include "nsGlobalWindowInner.h"
     77 #include "nsHTMLDocument.h"
     78 #include "nsHTMLParts.h"
     79 #include "nsIFormControl.h"
     80 #include "nsIFrameInlines.h"
     81 #include "nsILayoutHistoryState.h"
     82 #include "nsIPrincipal.h"
     83 #include "nsIWidget.h"
     84 #include "nsLayoutUtils.h"
     85 #include "nsPIDOMWindow.h"
     86 #include "nsPresContext.h"
     87 #include "nsQueryObject.h"
     88 #include "nsRange.h"
     89 #include "nsString.h"
     90 #include "nsStyleUtil.h"
     91 #include "nsTableCellFrame.h"
     92 #include "nsTextNode.h"
     93 #include "nsThreadUtils.h"
     94 #include "nscore.h"
     95 
     96 using namespace mozilla;
     97 using namespace mozilla::dom;
     98 
     99 static const uint8_t NS_INPUTMODE_NONE = 1;
    100 static const uint8_t NS_INPUTMODE_TEXT = 2;
    101 static const uint8_t NS_INPUTMODE_TEL = 3;
    102 static const uint8_t NS_INPUTMODE_URL = 4;
    103 static const uint8_t NS_INPUTMODE_EMAIL = 5;
    104 static const uint8_t NS_INPUTMODE_NUMERIC = 6;
    105 static const uint8_t NS_INPUTMODE_DECIMAL = 7;
    106 static const uint8_t NS_INPUTMODE_SEARCH = 8;
    107 
    108 static constexpr nsAttrValue::EnumTableEntry kInputmodeTable[] = {
    109    {"none", NS_INPUTMODE_NONE},       {"text", NS_INPUTMODE_TEXT},
    110    {"tel", NS_INPUTMODE_TEL},         {"url", NS_INPUTMODE_URL},
    111    {"email", NS_INPUTMODE_EMAIL},     {"numeric", NS_INPUTMODE_NUMERIC},
    112    {"decimal", NS_INPUTMODE_DECIMAL}, {"search", NS_INPUTMODE_SEARCH},
    113 };
    114 
    115 static const uint8_t NS_ENTERKEYHINT_ENTER = 1;
    116 static const uint8_t NS_ENTERKEYHINT_DONE = 2;
    117 static const uint8_t NS_ENTERKEYHINT_GO = 3;
    118 static const uint8_t NS_ENTERKEYHINT_NEXT = 4;
    119 static const uint8_t NS_ENTERKEYHINT_PREVIOUS = 5;
    120 static const uint8_t NS_ENTERKEYHINT_SEARCH = 6;
    121 static const uint8_t NS_ENTERKEYHINT_SEND = 7;
    122 
    123 static constexpr nsAttrValue::EnumTableEntry kEnterKeyHintTable[] = {
    124    {"enter", NS_ENTERKEYHINT_ENTER},
    125    {"done", NS_ENTERKEYHINT_DONE},
    126    {"go", NS_ENTERKEYHINT_GO},
    127    {"next", NS_ENTERKEYHINT_NEXT},
    128    {"previous", NS_ENTERKEYHINT_PREVIOUS},
    129    {"search", NS_ENTERKEYHINT_SEARCH},
    130    {"send", NS_ENTERKEYHINT_SEND},
    131 };
    132 
    133 static const uint8_t NS_AUTOCAPITALIZE_NONE = 1;
    134 static const uint8_t NS_AUTOCAPITALIZE_SENTENCES = 2;
    135 static const uint8_t NS_AUTOCAPITALIZE_WORDS = 3;
    136 static const uint8_t NS_AUTOCAPITALIZE_CHARACTERS = 4;
    137 
    138 static constexpr nsAttrValue::EnumTableEntry kAutocapitalizeTable[] = {
    139    {"none", NS_AUTOCAPITALIZE_NONE},
    140    {"sentences", NS_AUTOCAPITALIZE_SENTENCES},
    141    {"words", NS_AUTOCAPITALIZE_WORDS},
    142    {"characters", NS_AUTOCAPITALIZE_CHARACTERS},
    143    {"off", NS_AUTOCAPITALIZE_NONE},
    144    {"on", NS_AUTOCAPITALIZE_SENTENCES},
    145    {"", 0},
    146 };
    147 
    148 static constexpr const nsAttrValue::EnumTableEntry* kDefaultAutocapitalize =
    149    &kAutocapitalizeTable[1];
    150 
    151 nsresult nsGenericHTMLElement::CopyInnerTo(Element* aDst) {
    152  MOZ_ASSERT(!aDst->GetUncomposedDoc(),
    153             "Should not CopyInnerTo an Element in a document");
    154 
    155  auto reparse = aDst->OwnerDoc() == OwnerDoc() ? ReparseAttributes::No
    156                                                : ReparseAttributes::Yes;
    157  nsresult rv = Element::CopyInnerTo(aDst, reparse);
    158  NS_ENSURE_SUCCESS(rv, rv);
    159 
    160  // cloning a node must retain its internal nonce slot
    161  nsString* nonce = static_cast<nsString*>(GetProperty(nsGkAtoms::nonce));
    162  if (nonce) {
    163    static_cast<nsGenericHTMLElement*>(aDst)->SetNonce(*nonce);
    164  }
    165  return NS_OK;
    166 }
    167 
    168 static constexpr nsAttrValue::EnumTableEntry kDirTable[] = {
    169    {"ltr", Directionality::Ltr},
    170    {"rtl", Directionality::Rtl},
    171    {"auto", Directionality::Auto},
    172 };
    173 
    174 namespace {
    175 // See <https://html.spec.whatwg.org/#the-popover-attribute>.
    176 enum class PopoverAttributeKeyword : uint8_t { Auto, EmptyString, Manual };
    177 
    178 static constexpr const char kPopoverAttributeValueAuto[] = "auto";
    179 static constexpr const char kPopoverAttributeValueEmptyString[] = "";
    180 static constexpr const char kPopoverAttributeValueManual[] = "manual";
    181 
    182 static constexpr nsAttrValue::EnumTableEntry kPopoverTable[] = {
    183    {kPopoverAttributeValueAuto, PopoverAttributeKeyword::Auto},
    184    {kPopoverAttributeValueEmptyString, PopoverAttributeKeyword::EmptyString},
    185    {kPopoverAttributeValueManual, PopoverAttributeKeyword::Manual},
    186 };
    187 
    188 // See <https://html.spec.whatwg.org/#the-popover-attribute>.
    189 static const nsAttrValue::EnumTableEntry* kPopoverTableInvalidValueDefault =
    190    &kPopoverTable[2];
    191 }  // namespace
    192 
    193 void nsGenericHTMLElement::GetFetchPriority(nsAString& aFetchPriority) const {
    194  // <https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fetch-priority-attributes>.
    195  GetEnumAttr(nsGkAtoms::fetchpriority, kFetchPriorityAttributeValueAuto,
    196              aFetchPriority);
    197 }
    198 
    199 /* static */
    200 FetchPriority nsGenericHTMLElement::ToFetchPriority(const nsAString& aValue) {
    201  nsAttrValue attrValue;
    202  ParseFetchPriority(aValue, attrValue);
    203  MOZ_ASSERT(attrValue.Type() == nsAttrValue::eEnum);
    204  return FetchPriority(attrValue.GetEnumValue());
    205 }
    206 
    207 void nsGenericHTMLElement::AddToNameTable(nsAtom* aName) {
    208  MOZ_ASSERT(HasName(), "Node doesn't have name?");
    209  Document* doc = GetUncomposedDoc();
    210  if (doc && !IsInNativeAnonymousSubtree()) {
    211    doc->AddToNameTable(this, aName);
    212  }
    213 }
    214 
    215 void nsGenericHTMLElement::RemoveFromNameTable() {
    216  if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
    217    if (Document* doc = GetUncomposedDoc()) {
    218      doc->RemoveFromNameTable(this,
    219                               GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
    220    }
    221  }
    222 }
    223 
    224 void nsGenericHTMLElement::GetAccessKeyLabel(nsString& aLabel) {
    225  nsAutoString suffix;
    226  GetAccessKey(suffix);
    227  if (!suffix.IsEmpty()) {
    228    EventStateManager::GetAccessKeyLabelPrefix(this, aLabel);
    229    aLabel.Append(suffix);
    230  }
    231 }
    232 
    233 // https://html.spec.whatwg.org/#dom-hidden
    234 void nsGenericHTMLElement::GetHidden(
    235    Nullable<OwningBooleanOrUnrestrictedDoubleOrString>& aHidden) const {
    236  OwningBooleanOrUnrestrictedDoubleOrString value;
    237  // 1. If the hidden attribute is in the hidden until found state, then
    238  //    return "until-found".
    239  nsAutoString result;
    240  if (GetAttr(kNameSpaceID_None, nsGkAtoms::hidden, result)) {
    241    if (StaticPrefs::dom_hidden_until_found_enabled() &&
    242        result.LowerCaseEqualsLiteral("until-found")) {
    243      value.SetStringLiteral(u"until-found");
    244    } else {
    245      // 2. If the hidden attribute is set, then return true.
    246      value.SetAsBoolean() = true;
    247    }
    248  } else {
    249    // 3. Return false.
    250    value.SetAsBoolean() = false;
    251  }
    252 
    253  aHidden.SetValue(value);
    254 }
    255 
    256 // https://html.spec.whatwg.org/#dom-hidden
    257 void nsGenericHTMLElement::SetHidden(
    258    const Nullable<BooleanOrUnrestrictedDoubleOrString>& aHidden,
    259    ErrorResult& aRv) {
    260  // 4. Otherwise, if the given value is null, then remove the hidden attribute.
    261  if (aHidden.IsNull()) {
    262    return UnsetAttr(nsGkAtoms::hidden, aRv);
    263  }
    264  bool isHidden = true;
    265  const auto& value = aHidden.Value();
    266  // 1. If the given value is a string that is an ASCII case-insensitive match
    267  //    for "until-found", then set the hidden attribute to "until-found".
    268  if (value.IsString()) {
    269    const nsAString& stringValue = value.GetAsString();
    270    // 3. Otherwise, if the given value is the empty string, then remove the
    271    //    hidden attribute.
    272    if (stringValue.IsEmpty()) {
    273      isHidden = false;
    274    } else if (StaticPrefs::dom_hidden_until_found_enabled() &&
    275               stringValue.LowerCaseEqualsLiteral("until-found")) {
    276      return SetAttr(nsGkAtoms::hidden, u"until-found"_ns, aRv);
    277    }
    278  }
    279  // 2. Otherwise, if the given value is false, then remove the hidden
    280  //    attribute.
    281  else if (value.IsBoolean()) {
    282    if (!value.GetAsBoolean()) {
    283      isHidden = false;
    284    }
    285  }
    286  // 5. Otherwise, if the given value is 0, then remove the hidden attribute.
    287  // 6. Otherwise, if the given value is NaN, then remove the hidden attribute.
    288  else if (value.IsUnrestrictedDouble()) {
    289    double d = value.GetAsUnrestrictedDouble();
    290    if (d == 0.0 || std::isnan(d)) {
    291      isHidden = false;
    292    }
    293  }
    294 
    295  // 7. Otherwise, set the hidden attribute to the empty string.
    296  if (isHidden) {
    297    aRv = SetAttr(kNameSpaceID_None, nsGkAtoms::hidden, u""_ns, true);
    298  } else {
    299    aRv = UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true);
    300  }
    301 }
    302 
    303 bool nsGenericHTMLElement::Spellcheck() {
    304  // Has the state has been explicitly set?
    305  nsIContent* node;
    306  for (node = this; node; node = node->GetParent()) {
    307    if (node->IsHTMLElement()) {
    308      static Element::AttrValuesArray strings[] = {nsGkAtoms::_true,
    309                                                   nsGkAtoms::_false, nullptr};
    310      switch (node->AsElement()->FindAttrValueIn(
    311          kNameSpaceID_None, nsGkAtoms::spellcheck, strings, eCaseMatters)) {
    312        case 0:  // spellcheck = "true"
    313          return true;
    314        case 1:  // spellcheck = "false"
    315          return false;
    316      }
    317    }
    318  }
    319 
    320  // contenteditable/designMode are spellchecked by default
    321  if (IsEditable()) {
    322    return true;
    323  }
    324 
    325  // Is this a chrome element?
    326  if (nsContentUtils::IsChromeDoc(OwnerDoc())) {
    327    return false;  // Not spellchecked by default
    328  }
    329 
    330  // Anything else that's not a form control is not spellchecked by default
    331  const nsIFormControl* formControl = GetAsFormControl();
    332  if (!formControl) {
    333    return false;  // Not spellchecked by default
    334  }
    335 
    336  // Is this a multiline plaintext input?
    337  auto controlType = formControl->ControlType();
    338  if (controlType == FormControlType::Textarea) {
    339    return true;  // Spellchecked by default
    340  }
    341 
    342  // Is this anything other than an input text?
    343  // Other inputs are not spellchecked.
    344  if (controlType != FormControlType::InputText) {
    345    return false;  // Not spellchecked by default
    346  }
    347 
    348  // Does the user want input text spellchecked by default?
    349  // NOTE: Do not reflect a pref value of 0 back to the DOM getter.
    350  // The web page should not know if the user has disabled spellchecking.
    351  // We'll catch this in the editor itself.
    352  int32_t spellcheckLevel = StaticPrefs::layout_spellcheckDefault();
    353  return spellcheckLevel == 2;  // "Spellcheck multi- and single-line"
    354 }
    355 
    356 bool nsGenericHTMLElement::Autocorrect() const {
    357  return !AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocorrect, nsGkAtoms::OFF,
    358                      eIgnoreCase);
    359 }
    360 
    361 bool nsGenericHTMLElement::InNavQuirksMode(Document* aDoc) {
    362  return aDoc && aDoc->GetCompatibilityMode() == eCompatibility_NavQuirks;
    363 }
    364 
    365 void nsGenericHTMLElement::UpdateEditableState(bool aNotify) {
    366  // XXX Should we do this only when in a document?
    367  ContentEditableState state = GetContentEditableState();
    368  if (state != ContentEditableState::Inherit) {
    369    SetEditableFlag(IsEditableState(state));
    370    UpdateReadOnlyState(aNotify);
    371    return;
    372  }
    373  nsStyledElement::UpdateEditableState(aNotify);
    374 }
    375 
    376 nsresult nsGenericHTMLElement::BindToTree(BindContext& aContext,
    377                                          nsINode& aParent) {
    378  nsresult rv = nsGenericHTMLElementBase::BindToTree(aContext, aParent);
    379  NS_ENSURE_SUCCESS(rv, rv);
    380 
    381  if (IsInComposedDoc()) {
    382    RegUnRegAccessKey(true);
    383  }
    384 
    385  if (IsInUncomposedDoc()) {
    386    Document& doc = aContext.OwnerDoc();
    387    if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
    388      doc.AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
    389    }
    390 
    391    nsAtom* id = nullptr;
    392    if (ShouldExposeIdAsHTMLDocumentProperty(this)) {
    393      id = DoGetID();
    394      MOZ_ASSERT(id && id != nsGkAtoms::_empty);
    395      doc.AddToDocumentNameTable(this, id);
    396    }
    397    if (ShouldExposeNameAsHTMLDocumentProperty(this)) {
    398      nsAtom* name = GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
    399      MOZ_ASSERT(name && name != nsGkAtoms::_empty);
    400      // Make sure not to double-add if id and name are the same.
    401      if (id != name) {
    402        doc.AddToDocumentNameTable(this, name);
    403      }
    404    }
    405  }
    406 
    407  if (HasFlag(NODE_IS_EDITABLE) &&
    408      HasContentEditableAttrTrueOrPlainTextOnly() && IsInComposedDoc()) {
    409    aContext.OwnerDoc().ChangeContentEditableCount(this, +1);
    410  }
    411 
    412  // Hide any nonce from the DOM, but keep the internal value of the
    413  // nonce by copying and resetting the internal nonce value.
    414  if (!aContext.IsMove() && HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) &&
    415      IsInComposedDoc() && OwnerDoc()->GetBrowsingContext()) {
    416    nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
    417        "nsGenericHTMLElement::ResetNonce::Runnable",
    418        [self = RefPtr<nsGenericHTMLElement>(this)]() {
    419          nsAutoString nonce;
    420          self->GetNonce(nonce);
    421          self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, u""_ns, true);
    422          self->SetNonce(nonce);
    423        }));
    424  }
    425 
    426  // We need to consider a labels element is moved to another subtree
    427  // with different root, it needs to update labels list and its root
    428  // as well.
    429  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
    430  if (slots && slots->mLabelsList) {
    431    slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
    432  }
    433 
    434  return rv;
    435 }
    436 
    437 void nsGenericHTMLElement::UnbindFromTree(UnbindContext& aContext) {
    438  if (IsInComposedDoc()) {
    439    // https://html.spec.whatwg.org/#dom-trees:hide-popover-algorithm
    440    // If removedNode's popover attribute is not in the no popover state, then
    441    // run the hide popover algorithm given removedNode, false, false, and
    442    // false.
    443    if (!aContext.IsMove() && GetPopoverData()) {
    444      HidePopoverWithoutRunningScript();
    445    }
    446    RegUnRegAccessKey(false);
    447  }
    448 
    449  RemoveFromNameTable();
    450 
    451  if (Document* doc = GetUncomposedDoc()) {
    452    nsAtom* id = nullptr;
    453    if (ShouldExposeIdAsHTMLDocumentProperty(this)) {
    454      id = DoGetID();
    455      MOZ_ASSERT(id && id != nsGkAtoms::_empty);
    456      doc->RemoveFromDocumentNameTable(this, id);
    457    }
    458    if (ShouldExposeNameAsHTMLDocumentProperty(this)) {
    459      nsAtom* name = GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
    460      MOZ_ASSERT(name && name != nsGkAtoms::_empty);
    461      // Make sure not to double-remove if id and name are the same.
    462      if (id != name) {
    463        doc->RemoveFromDocumentNameTable(this, name);
    464      }
    465    }
    466  }
    467 
    468  if (HasContentEditableAttrTrueOrPlainTextOnly()) {
    469    if (Document* doc = GetComposedDoc()) {
    470      doc->ChangeContentEditableCount(this, -1);
    471    }
    472  }
    473 
    474  nsStyledElement::UnbindFromTree(aContext);
    475 
    476  // Invalidate .labels list. It will be repopulated when used the next time.
    477  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
    478  if (slots && slots->mLabelsList) {
    479    slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
    480  }
    481 }
    482 
    483 HTMLFormElement* nsGenericHTMLElement::FindAncestorForm(
    484    HTMLFormElement* aCurrentForm) {
    485  NS_ASSERTION(!HasAttr(nsGkAtoms::form) || IsHTMLElement(nsGkAtoms::img),
    486               "FindAncestorForm should not be called if @form is set!");
    487  if (IsInNativeAnonymousSubtree()) {
    488    return nullptr;
    489  }
    490 
    491  nsIContent* content = this;
    492  while (content) {
    493    // If the current ancestor is a form, return it as our form
    494    if (content->IsHTMLElement(nsGkAtoms::form)) {
    495 #ifdef DEBUG
    496      if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
    497        // It's possible that we started unbinding at |content| or
    498        // some ancestor of it, and |content| and |this| used to all be
    499        // anonymous.  Check for this the hard way.
    500        for (nsIContent* child = this; child != content;
    501             child = child->GetParent()) {
    502          NS_ASSERTION(child->ComputeIndexInParentContent().isSome(),
    503                       "Walked too far?");
    504        }
    505      }
    506 #endif
    507      return static_cast<HTMLFormElement*>(content);
    508    }
    509 
    510    nsIContent* prevContent = content;
    511    content = prevContent->GetParent();
    512 
    513    if (!content && aCurrentForm) {
    514      // We got to the root of the subtree we're in, and we're being removed
    515      // from the DOM (the only time we get into this method with a non-null
    516      // aCurrentForm).  Check whether aCurrentForm is in the same subtree.  If
    517      // it is, we want to return aCurrentForm, since this case means that
    518      // we're one of those inputs-in-a-table that have a hacked mForm pointer
    519      // and a subtree containing both us and the form got removed from the
    520      // DOM.
    521      if (aCurrentForm->IsInclusiveDescendantOf(prevContent)) {
    522        return aCurrentForm;
    523      }
    524    }
    525  }
    526 
    527  return nullptr;
    528 }
    529 
    530 bool nsGenericHTMLElement::CheckHandleEventForAnchorsPreconditions(
    531    EventChainVisitor& aVisitor) {
    532  MOZ_ASSERT(nsCOMPtr<Link>(do_QueryObject(this)),
    533             "should be called only when |this| implements |Link|");
    534  // When disconnected, only <a> should navigate away per
    535  // https://html.spec.whatwg.org/#cannot-navigate
    536  return IsInComposedDoc() || IsHTMLElement(nsGkAtoms::a);
    537 }
    538 
    539 void nsGenericHTMLElement::GetEventTargetParentForAnchors(
    540    EventChainPreVisitor& aVisitor) {
    541  nsGenericHTMLElementBase::GetEventTargetParent(aVisitor);
    542 
    543  if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) {
    544    return;
    545  }
    546 
    547  GetEventTargetParentForLinks(aVisitor);
    548 }
    549 
    550 nsresult nsGenericHTMLElement::PostHandleEventForAnchors(
    551    EventChainPostVisitor& aVisitor) {
    552  if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) {
    553    return NS_OK;
    554  }
    555 
    556  return PostHandleEventForLinks(aVisitor);
    557 }
    558 
    559 bool nsGenericHTMLElement::IsHTMLLink(nsIURI** aURI) const {
    560  MOZ_ASSERT(aURI, "Must provide aURI out param");
    561 
    562  *aURI = GetHrefURIForAnchors().take();
    563  // We promise out param is non-null if we return true, so base rv on it
    564  return *aURI != nullptr;
    565 }
    566 
    567 already_AddRefed<nsIURI> nsGenericHTMLElement::GetHrefURIForAnchors() const {
    568  // This is used by the three Link implementations and
    569  // nsHTMLStyleElement.
    570 
    571  // Get href= attribute (relative URI).
    572 
    573  // We use the nsAttrValue's copy of the URI string to avoid copying.
    574  nsCOMPtr<nsIURI> uri;
    575  GetURIAttr(nsGkAtoms::href, nullptr, getter_AddRefs(uri));
    576  return uri.forget();
    577 }
    578 
    579 void nsGenericHTMLElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
    580                                         const nsAttrValue* aValue,
    581                                         bool aNotify) {
    582  if (aNamespaceID == kNameSpaceID_None) {
    583    if (aName == nsGkAtoms::accesskey) {
    584      // Have to unregister before clearing flag. See UnregAccessKey
    585      RegUnRegAccessKey(false);
    586      if (!aValue) {
    587        UnsetFlags(NODE_HAS_ACCESSKEY);
    588      }
    589    } else if (aName == nsGkAtoms::name) {
    590      // Have to do this before clearing flag. See RemoveFromNameTable
    591      RemoveFromNameTable();
    592 
    593      nsAtom* exposedIdOnDocument = nullptr;
    594      Document* doc = GetUncomposedDoc();
    595      if (doc) {
    596        nsAtom* exposedNameOnDocument =
    597            ShouldExposeNameAsHTMLDocumentProperty(this)
    598                ? GetParsedAttr(nsGkAtoms::name)->GetAtomValue()
    599                : nullptr;
    600        exposedIdOnDocument =
    601            ShouldExposeIdAsHTMLDocumentProperty(this) ? DoGetID() : nullptr;
    602        if (exposedNameOnDocument &&
    603            exposedNameOnDocument != exposedIdOnDocument) {
    604          MOZ_ASSERT(exposedNameOnDocument != nsGkAtoms::_empty);
    605          doc->RemoveFromDocumentNameTable(this, exposedNameOnDocument);
    606        }
    607      }
    608      if (!aValue || aValue->IsEmptyString()) {
    609        ClearHasName();
    610        // The result of ShouldExposeIdAsHTMLDocumentProperty() might change
    611        // after clearing the hasName flag.
    612        if (doc && exposedIdOnDocument &&
    613            !ShouldExposeIdAsHTMLDocumentProperty(this)) {
    614          doc->RemoveFromDocumentNameTable(this, exposedIdOnDocument);
    615        }
    616      }
    617    } else if (aName == nsGkAtoms::id) {
    618      if (Document* doc = GetUncomposedDoc()) {
    619        nsAtom* exposedIdOnDocument =
    620            ShouldExposeIdAsHTMLDocumentProperty(this) ? DoGetID() : nullptr;
    621        nsAtom* exposedNameOnDocument =
    622            ShouldExposeNameAsHTMLDocumentProperty(this)
    623                ? GetParsedAttr(nsGkAtoms::name)->GetAtomValue()
    624                : nullptr;
    625        if (exposedIdOnDocument &&
    626            exposedIdOnDocument != exposedNameOnDocument) {
    627          doc->RemoveFromDocumentNameTable(this, exposedIdOnDocument);
    628        }
    629      }
    630    } else if (aName == nsGkAtoms::contenteditable) {
    631      if (aValue) {
    632        // Set this before the attribute is set so that any subclass code that
    633        // runs before the attribute is set won't think we're missing a
    634        // contenteditable attr when we actually have one.
    635        SetMayHaveContentEditableAttr();
    636      }
    637    }
    638    if (!aValue && IsEventAttributeName(aName)) {
    639      if (EventListenerManager* manager = GetExistingListenerManager()) {
    640        manager->RemoveEventHandler(GetEventNameForAttr(aName));
    641      }
    642    }
    643  }
    644 
    645  return nsGenericHTMLElementBase::BeforeSetAttr(aNamespaceID, aName, aValue,
    646                                                 aNotify);
    647 }
    648 
    649 namespace {
    650 constexpr PopoverAttributeState ToPopoverAttributeState(
    651    PopoverAttributeKeyword aPopoverAttributeKeyword) {
    652  // See <https://html.spec.whatwg.org/#the-popover-attribute>.
    653  switch (aPopoverAttributeKeyword) {
    654    case PopoverAttributeKeyword::Auto:
    655      return PopoverAttributeState::Auto;
    656    case PopoverAttributeKeyword::EmptyString:
    657      return PopoverAttributeState::Auto;
    658    case PopoverAttributeKeyword::Manual:
    659      return PopoverAttributeState::Manual;
    660    default: {
    661      MOZ_ASSERT_UNREACHABLE();
    662      return PopoverAttributeState::None;
    663    }
    664  }
    665 }
    666 }  // namespace
    667 
    668 void nsGenericHTMLElement::AfterSetPopoverAttr() {
    669  auto mapPopoverState = [](const nsAttrValue* value) -> PopoverAttributeState {
    670    if (value) {
    671      MOZ_ASSERT(value->Type() == nsAttrValue::eEnum);
    672      const auto popoverAttributeKeyword =
    673          static_cast<PopoverAttributeKeyword>(value->GetEnumValue());
    674      return ToPopoverAttributeState(popoverAttributeKeyword);
    675    }
    676 
    677    // The missing value default is the no popover state, see
    678    // <https://html.spec.whatwg.org/multipage/popover.html#attr-popover>.
    679    return PopoverAttributeState::None;
    680  };
    681 
    682  PopoverAttributeState newState =
    683      mapPopoverState(GetParsedAttr(nsGkAtoms::popover));
    684 
    685  const PopoverAttributeState oldState = GetPopoverAttributeState();
    686 
    687  if (newState != oldState) {
    688    PopoverPseudoStateUpdate(false, true);
    689 
    690    if (IsPopoverOpen()) {
    691      HidePopoverInternal(/* aFocusPreviousElement = */ true,
    692                          /* aFireEvents = */ true, /* aSource*/ nullptr,
    693                          IgnoreErrors());
    694      // Event handlers could have removed the popover attribute, or changed
    695      // its value.
    696      // https://github.com/whatwg/html/issues/9034
    697      newState = mapPopoverState(GetParsedAttr(nsGkAtoms::popover));
    698    }
    699 
    700    if (newState == PopoverAttributeState::None) {
    701      ClearPopoverData();
    702      RemoveStates(ElementState::POPOVER_OPEN);
    703    } else {
    704      // TODO: what if `HidePopoverInternal` called `ShowPopup()`?
    705      EnsurePopoverData().SetPopoverAttributeState(newState);
    706    }
    707  }
    708 }
    709 
    710 void nsGenericHTMLElement::OnAttrSetButNotChanged(
    711    int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString& aValue,
    712    bool aNotify) {
    713  if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::popovertarget) {
    714    ClearExplicitlySetAttrElement(aName);
    715  }
    716  return nsGenericHTMLElementBase::OnAttrSetButNotChanged(aNamespaceID, aName,
    717                                                          aValue, aNotify);
    718 }
    719 
    720 void nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
    721                                        const nsAttrValue* aValue,
    722                                        const nsAttrValue* aOldValue,
    723                                        nsIPrincipal* aMaybeScriptedPrincipal,
    724                                        bool aNotify) {
    725  if (aNamespaceID == kNameSpaceID_None) {
    726    if (IsEventAttributeName(aName) && aValue) {
    727      MOZ_ASSERT(aValue->Type() == nsAttrValue::eString ||
    728                     aValue->Type() == nsAttrValue::eAtom,
    729                 "Expected string or atom value for script body");
    730      SetEventHandler(GetEventNameForAttr(aName),
    731                      nsAttrValueOrString(aValue).String());
    732    } else if (aNotify && aName == nsGkAtoms::spellcheck) {
    733      SyncEditorsOnSubtree(this);
    734    } else if (aName == nsGkAtoms::popover) {
    735      nsContentUtils::AddScriptRunner(
    736          NewRunnableMethod("nsGenericHTMLElement::AfterSetPopoverAttr", this,
    737                            &nsGenericHTMLElement::AfterSetPopoverAttr));
    738    } else if (aName == nsGkAtoms::popovertarget) {
    739      ClearExplicitlySetAttrElement(aName);
    740    } else if (aName == nsGkAtoms::dir) {
    741      auto dir = Directionality::Ltr;
    742      // A boolean tracking whether we need to recompute our directionality.
    743      // This needs to happen after we update our internal "dir" attribute
    744      // state but before we call SetDirectionalityOnDescendants.
    745      bool recomputeDirectionality = false;
    746      ElementState dirStates;
    747      if (aValue && aValue->Type() == nsAttrValue::eEnum) {
    748        SetHasValidDir();
    749        dirStates |= ElementState::HAS_DIR_ATTR;
    750        auto dirValue = Directionality(aValue->GetEnumValue());
    751        if (dirValue == Directionality::Auto) {
    752          dirStates |= ElementState::HAS_DIR_ATTR_LIKE_AUTO;
    753        } else {
    754          dir = dirValue;
    755          SetDirectionality(dir, aNotify);
    756          if (dirValue == Directionality::Ltr) {
    757            dirStates |= ElementState::HAS_DIR_ATTR_LTR;
    758          } else {
    759            MOZ_ASSERT(dirValue == Directionality::Rtl);
    760            dirStates |= ElementState::HAS_DIR_ATTR_RTL;
    761          }
    762        }
    763      } else {
    764        if (aValue) {
    765          // We have a value, just not a valid one.
    766          dirStates |= ElementState::HAS_DIR_ATTR;
    767        }
    768        ClearHasValidDir();
    769        if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
    770          dirStates |= ElementState::HAS_DIR_ATTR_LIKE_AUTO;
    771        } else {
    772          recomputeDirectionality = true;
    773        }
    774      }
    775      // Now figure out what's changed about our dir states.
    776      ElementState oldDirStates = State() & ElementState::DIR_ATTR_STATES;
    777      ElementState changedStates = dirStates ^ oldDirStates;
    778      if (!changedStates.IsEmpty()) {
    779        ToggleStates(changedStates, aNotify);
    780      }
    781      if (recomputeDirectionality) {
    782        dir = RecomputeDirectionality(this, aNotify);
    783      }
    784      SetDirectionalityOnDescendants(this, dir, aNotify);
    785    } else if (aName == nsGkAtoms::contenteditable) {
    786      const auto IsEditableExceptInherit = [](const nsAttrValue& aValue) {
    787        return aValue.Equals(EmptyString(), eCaseMatters) ||
    788               aValue.Equals(u"true"_ns, eIgnoreCase) ||
    789               aValue.Equals(u"plaintext-only"_ns, eIgnoreCase);
    790      };
    791      // FYI: Now, both HasContentEditableAttrTrueOrPlainTextOnly() and
    792      // HasContentEditableAttrFalse() return true.  Therefore, we need to clear
    793      // one of them or both of them.
    794      int32_t editableCountDelta = 0;
    795      if (aOldValue && IsEditableExceptInherit(*aOldValue)) {
    796        editableCountDelta = -1;
    797        ClearHasContentEditableAttrTrueOrPlainTextOnly();
    798      }
    799      if (!aValue) {
    800        ClearMayHaveContentEditableAttr();
    801      } else if (IsEditableExceptInherit(*aValue)) {
    802        ++editableCountDelta;
    803        SetHasContentEditableAttrTrueOrPlainTextOnly();
    804      }
    805      ChangeEditableState(editableCountDelta);
    806    } else if (aName == nsGkAtoms::accesskey) {
    807      if (aValue && !aValue->Equals(u""_ns, eIgnoreCase)) {
    808        SetFlags(NODE_HAS_ACCESSKEY);
    809        RegUnRegAccessKey(true);
    810      }
    811    } else if (aName == nsGkAtoms::inert) {
    812      if (aValue) {
    813        AddStates(ElementState::INERT);
    814      } else {
    815        RemoveStates(ElementState::INERT);
    816      }
    817    } else if (aName == nsGkAtoms::name) {
    818      if (aValue && !aValue->Equals(u""_ns, eIgnoreCase)) {
    819        // This may not be quite right because we can have subclass code run
    820        // before here. But in practice subclasses don't care about this flag,
    821        // and in particular selector matching does not care.  Otherwise we'd
    822        // want to handle it like we handle id attributes (in PreIdMaybeChange
    823        // and PostIdMaybeChange).
    824        SetHasName();
    825        if (CanHaveName(NodeInfo()->NameAtom())) {
    826          AddToNameTable(aValue->GetAtomValue());
    827        }
    828        if (Document* doc = GetUncomposedDoc()) {
    829          if (ShouldExposeNameAsHTMLDocumentProperty(this)) {
    830            nsAtom* id = ShouldExposeIdAsHTMLDocumentProperty(this) ? DoGetID()
    831                                                                    : nullptr;
    832            nsAtom* name = aValue->GetAtomValue();
    833            // Make sure not to double-add if id and name are the same
    834            if (id != name) {
    835              doc->AddToDocumentNameTable(this, name);
    836            }
    837          }
    838        }
    839      }
    840    } else if (aName == nsGkAtoms::id) {
    841      if (Document* doc = GetUncomposedDoc()) {
    842        if (ShouldExposeIdAsHTMLDocumentProperty(this)) {
    843          nsAtom* id = aValue->GetAtomValue();
    844          nsAtom* name = ShouldExposeNameAsHTMLDocumentProperty(this)
    845                             ? GetParsedAttr(nsGkAtoms::name)->GetAtomValue()
    846                             : nullptr;
    847          if (id != name) {
    848            doc->AddToDocumentNameTable(this, id);
    849          }
    850        }
    851      }
    852    } else if (aName == nsGkAtoms::inputmode ||
    853               aName == nsGkAtoms::enterkeyhint) {
    854      if (nsFocusManager::GetFocusedElementStatic() == this) {
    855        if (const nsPresContext* presContext =
    856                GetPresContext(eForComposedDoc)) {
    857          IMEContentObserver* observer =
    858              IMEStateManager::GetActiveContentObserver();
    859          if (observer && observer->IsObserving(*presContext, this)) {
    860            if (const RefPtr<EditorBase> editorBase = GetExtantEditor()) {
    861              // If the TextControlState does not have a bound frame,
    862              // TextEditor::GetPreferredIMEState() may fail.  In such case,
    863              // TextEditor will update IME state when it's (re)initialized
    864              // later.
    865              Result<IMEState, nsresult> newStateOrError =
    866                  editorBase->GetPreferredIMEState();
    867              if (MOZ_LIKELY(newStateOrError.isOk())) {
    868                OwningNonNull<nsGenericHTMLElement> kungFuDeathGrip(*this);
    869                IMEStateManager::UpdateIMEState(
    870                    newStateOrError.unwrap(), kungFuDeathGrip, *editorBase,
    871                    {IMEStateManager::UpdateIMEStateOption::ForceUpdate,
    872                     IMEStateManager::UpdateIMEStateOption::
    873                         DontCommitComposition});
    874              }
    875            }
    876          }
    877        }
    878      }
    879    }
    880 
    881    // The nonce will be copied over to an internal slot and cleared from the
    882    // Element within BindToTree to avoid CSS Selector nonce exfiltration if
    883    // the CSP list contains a header-delivered CSP.
    884    if (nsGkAtoms::nonce == aName) {
    885      if (aValue) {
    886        SetNonce(nsAttrValueOrString(aValue).String());
    887        if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
    888          SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP);
    889        }
    890      } else {
    891        RemoveNonce();
    892      }
    893    }
    894  }
    895 
    896  return nsGenericHTMLElementBase::AfterSetAttr(
    897      aNamespaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
    898 }
    899 
    900 EventListenerManager* nsGenericHTMLElement::GetEventListenerManagerForAttr(
    901    nsAtom* aAttrName, bool* aDefer) {
    902  // Attributes on the body and frameset tags get set on the global object
    903  if ((mNodeInfo->Equals(nsGkAtoms::body) ||
    904       mNodeInfo->Equals(nsGkAtoms::frameset)) &&
    905      // We only forward some event attributes from body/frameset to window
    906      (0
    907 #define EVENT(name_, id_, type_, struct_) /* nothing */
    908 #define FORWARDED_EVENT(name_, id_, type_, struct_) \
    909  || nsGkAtoms::on##name_ == aAttrName
    910 #define WINDOW_EVENT FORWARDED_EVENT
    911 #include "mozilla/EventNameList.h"  // IWYU pragma: keep
    912 #undef WINDOW_EVENT
    913 #undef FORWARDED_EVENT
    914 #undef EVENT
    915       )) {
    916    nsPIDOMWindowInner* win;
    917 
    918    // If we have a document, and it has a window, add the event
    919    // listener on the window (the inner window). If not, proceed as
    920    // normal.
    921    // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc() here,
    922    // override BindToTree for those classes and munge event listeners there?
    923    Document* document = OwnerDoc();
    924 
    925    *aDefer = false;
    926    if ((win = document->GetInnerWindow())) {
    927      nsCOMPtr<EventTarget> piTarget(do_QueryInterface(win));
    928 
    929      return piTarget->GetOrCreateListenerManager();
    930    }
    931 
    932    return nullptr;
    933  }
    934 
    935  return nsGenericHTMLElementBase::GetEventListenerManagerForAttr(aAttrName,
    936                                                                  aDefer);
    937 }
    938 
    939 #define EVENT(name_, id_, type_, struct_) /* nothing; handled by nsINode */
    940 #define FORWARDED_EVENT(name_, id_, type_, struct_)                       \
    941  EventHandlerNonNull* nsGenericHTMLElement::GetOn##name_() {             \
    942    if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {      \
    943      /* XXXbz note to self: add tests for this! */                       \
    944      if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) {       \
    945        nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);  \
    946        return globalWin->GetOn##name_();                                 \
    947      }                                                                   \
    948      return nullptr;                                                     \
    949    }                                                                     \
    950                                                                          \
    951    return nsINode::GetOn##name_();                                       \
    952  }                                                                       \
    953  void nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler) { \
    954    if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {      \
    955      nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();             \
    956      if (!win) {                                                         \
    957        return;                                                           \
    958      }                                                                   \
    959                                                                          \
    960      nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);    \
    961      return globalWin->SetOn##name_(handler);                            \
    962    }                                                                     \
    963                                                                          \
    964    return nsINode::SetOn##name_(handler);                                \
    965  }
    966 #define ERROR_EVENT(name_, id_, type_, struct_)                                \
    967  already_AddRefed<EventHandlerNonNull> nsGenericHTMLElement::GetOn##name_() { \
    968    if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {           \
    969      /* XXXbz note to self: add tests for this! */                            \
    970      if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) {            \
    971        nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);       \
    972        OnErrorEventHandlerNonNull* errorHandler = globalWin->GetOn##name_();  \
    973        if (errorHandler) {                                                    \
    974          RefPtr<EventHandlerNonNull> handler =                                \
    975              new EventHandlerNonNull(errorHandler);                           \
    976          return handler.forget();                                             \
    977        }                                                                      \
    978      }                                                                        \
    979      return nullptr;                                                          \
    980    }                                                                          \
    981                                                                               \
    982    RefPtr<EventHandlerNonNull> handler = nsINode::GetOn##name_();             \
    983    return handler.forget();                                                   \
    984  }                                                                            \
    985  void nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler) {      \
    986    if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {           \
    987      nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();                  \
    988      if (!win) {                                                              \
    989        return;                                                                \
    990      }                                                                        \
    991                                                                               \
    992      nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);         \
    993      RefPtr<OnErrorEventHandlerNonNull> errorHandler;                         \
    994      if (handler) {                                                           \
    995        errorHandler = new OnErrorEventHandlerNonNull(handler);                \
    996      }                                                                        \
    997      return globalWin->SetOn##name_(errorHandler);                            \
    998    }                                                                          \
    999                                                                               \
   1000    return nsINode::SetOn##name_(handler);                                     \
   1001  }
   1002 #include "mozilla/EventNameList.h"  // IWYU pragma: keep
   1003 #undef ERROR_EVENT
   1004 #undef FORWARDED_EVENT
   1005 #undef EVENT
   1006 
   1007 void nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const {
   1008  OwnerDoc()->GetBaseTarget(aBaseTarget);
   1009 }
   1010 
   1011 //----------------------------------------------------------------------
   1012 
   1013 bool nsGenericHTMLElement::ParseAttribute(int32_t aNamespaceID,
   1014                                          nsAtom* aAttribute,
   1015                                          const nsAString& aValue,
   1016                                          nsIPrincipal* aMaybeScriptedPrincipal,
   1017                                          nsAttrValue& aResult) {
   1018  if (aNamespaceID == kNameSpaceID_None) {
   1019    if (aAttribute == nsGkAtoms::dir) {
   1020      return aResult.ParseEnumValue(aValue, kDirTable, false);
   1021    }
   1022 
   1023    if (aAttribute == nsGkAtoms::popover) {
   1024      return aResult.ParseEnumValue(aValue, kPopoverTable, false,
   1025                                    kPopoverTableInvalidValueDefault);
   1026    }
   1027 
   1028    if (aAttribute == nsGkAtoms::tabindex) {
   1029      return aResult.ParseIntValue(aValue);
   1030    }
   1031 
   1032    if (aAttribute == nsGkAtoms::referrerpolicy) {
   1033      return ParseReferrerAttribute(aValue, aResult);
   1034    }
   1035 
   1036    if (aAttribute == nsGkAtoms::name) {
   1037      // Store name as an atom.  name="" means that the element has no name,
   1038      // not that it has an empty string as the name.
   1039      if (aValue.IsEmpty()) {
   1040        return false;
   1041      }
   1042      aResult.ParseAtom(aValue);
   1043      return true;
   1044    }
   1045 
   1046    if (aAttribute == nsGkAtoms::contenteditable ||
   1047        aAttribute == nsGkAtoms::translate) {
   1048      aResult.ParseAtom(aValue);
   1049      return true;
   1050    }
   1051 
   1052    if (aAttribute == nsGkAtoms::rel) {
   1053      aResult.ParseAtomArray(aValue);
   1054      return true;
   1055    }
   1056 
   1057    if (aAttribute == nsGkAtoms::inputmode) {
   1058      return aResult.ParseEnumValue(aValue, kInputmodeTable, false);
   1059    }
   1060 
   1061    if (aAttribute == nsGkAtoms::enterkeyhint) {
   1062      return aResult.ParseEnumValue(aValue, kEnterKeyHintTable, false);
   1063    }
   1064 
   1065    if (aAttribute == nsGkAtoms::autocapitalize) {
   1066      return aResult.ParseEnumValue(aValue, kAutocapitalizeTable, false);
   1067    }
   1068  }
   1069 
   1070  return nsGenericHTMLElementBase::ParseAttribute(
   1071      aNamespaceID, aAttribute, aValue, aMaybeScriptedPrincipal, aResult);
   1072 }
   1073 
   1074 bool nsGenericHTMLElement::ParseBackgroundAttribute(int32_t aNamespaceID,
   1075                                                    nsAtom* aAttribute,
   1076                                                    const nsAString& aValue,
   1077                                                    nsAttrValue& aResult) {
   1078  if (aNamespaceID == kNameSpaceID_None &&
   1079      aAttribute == nsGkAtoms::background && !aValue.IsEmpty()) {
   1080    // Resolve url to an absolute url
   1081    Document* doc = OwnerDoc();
   1082    nsCOMPtr<nsIURI> uri;
   1083    nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
   1084        getter_AddRefs(uri), aValue, doc, GetBaseURI());
   1085    if (NS_FAILED(rv)) {
   1086      return false;
   1087    }
   1088    aResult.SetTo(uri, &aValue);
   1089    return true;
   1090  }
   1091 
   1092  return false;
   1093 }
   1094 
   1095 bool nsGenericHTMLElement::IsAttributeMapped(const nsAtom* aAttribute) const {
   1096  static const MappedAttributeEntry* const map[] = {sCommonAttributeMap};
   1097 
   1098  return FindAttributeDependence(aAttribute, map);
   1099 }
   1100 
   1101 nsMapRuleToAttributesFunc nsGenericHTMLElement::GetAttributeMappingFunction()
   1102    const {
   1103  return &MapCommonAttributesInto;
   1104 }
   1105 
   1106 // Represents a possible value for the "align" attribute. Different
   1107 // elements may support different subsets of these values and map
   1108 // them into different CSS properties.
   1109 enum class HTMLAlignValue : uint8_t {
   1110  Left,
   1111  Right,
   1112  Top,
   1113  Middle,
   1114  Bottom,
   1115  Center,
   1116  Baseline,
   1117  TextTop,
   1118  AbsMiddle,
   1119  AbsCenter,
   1120  AbsBottom,
   1121  Justify,
   1122 };
   1123 
   1124 // clang-format off
   1125 static constexpr nsAttrValue::EnumTableEntry kDivAlignTable[] = {
   1126    {"left", HTMLAlignValue::Left},
   1127    {"right", HTMLAlignValue::Right},
   1128    {"center", HTMLAlignValue::Center},
   1129    {"middle", HTMLAlignValue::Middle},
   1130    {"justify", HTMLAlignValue::Justify},
   1131 };
   1132 // clang-format on
   1133 
   1134 static constexpr nsAttrValue::EnumTableEntry kFrameborderTable[] = {
   1135    {"yes", FrameBorderProperty::Yes},
   1136    {"no", FrameBorderProperty::No},
   1137    {"1", FrameBorderProperty::One},
   1138    {"0", FrameBorderProperty::Zero},
   1139 };
   1140 
   1141 // TODO(emilio): Nobody uses the parsed attribute here.
   1142 static constexpr nsAttrValue::EnumTableEntry kScrollingTable[] = {
   1143    {"yes", ScrollingAttribute::Yes},
   1144    {"no", ScrollingAttribute::No},
   1145    {"on", ScrollingAttribute::On},
   1146    {"off", ScrollingAttribute::Off},
   1147    {"scroll", ScrollingAttribute::Scroll},
   1148    {"noscroll", ScrollingAttribute::Noscroll},
   1149    {"auto", ScrollingAttribute::Auto},
   1150 };
   1151 
   1152 static constexpr nsAttrValue::EnumTableEntry kTableVAlignTable[] = {
   1153    {"top", TableCellAlignment::Top},
   1154    {"middle", TableCellAlignment::Middle},
   1155    {"bottom", TableCellAlignment::Bottom},
   1156    {"baseline", TableCellAlignment::Baseline},
   1157 };
   1158 
   1159 static constexpr nsAttrValue::EnumTableEntry kAlignTable[] = {
   1160    {"left", HTMLAlignValue::Left},
   1161    {"right", HTMLAlignValue::Right},
   1162    {"top", HTMLAlignValue::Top},
   1163    {"middle", HTMLAlignValue::Middle},
   1164    {"bottom", HTMLAlignValue::Bottom},
   1165    {"center", HTMLAlignValue::Center},
   1166    {"baseline", HTMLAlignValue::Baseline},
   1167    {"texttop", HTMLAlignValue::TextTop},
   1168    {"absmiddle", HTMLAlignValue::AbsMiddle},
   1169    {"abscenter", HTMLAlignValue::AbsCenter},
   1170    {"absbottom", HTMLAlignValue::AbsBottom},
   1171 };
   1172 
   1173 bool nsGenericHTMLElement::ParseAlignValue(const nsAString& aString,
   1174                                           nsAttrValue& aResult) {
   1175  return aResult.ParseEnumValue(aString, kAlignTable, false);
   1176 }
   1177 
   1178 //----------------------------------------
   1179 
   1180 static constexpr nsAttrValue::EnumTableEntry kTableHAlignTable[] = {
   1181    {"left", HTMLAlignValue::Left},
   1182    {"right", HTMLAlignValue::Right},
   1183    {"center", HTMLAlignValue::Center},
   1184 };
   1185 
   1186 bool nsGenericHTMLElement::ParseTableHAlignValue(const nsAString& aString,
   1187                                                 nsAttrValue& aResult) {
   1188  return aResult.ParseEnumValue(aString, kTableHAlignTable, false);
   1189 }
   1190 
   1191 //----------------------------------------
   1192 
   1193 // This table is used for td, th, tr, col, thead, tbody and tfoot.
   1194 static constexpr nsAttrValue::EnumTableEntry kTableCellHAlignTable[] = {
   1195    {"left", HTMLAlignValue::Left},
   1196    {"right", HTMLAlignValue::Right},
   1197    {"center", HTMLAlignValue::Center},
   1198    {"justify", HTMLAlignValue::Justify},
   1199    {"middle", HTMLAlignValue::Middle},
   1200    {"absmiddle", HTMLAlignValue::AbsMiddle},
   1201 };
   1202 
   1203 bool nsGenericHTMLElement::ParseTableCellHAlignValue(const nsAString& aString,
   1204                                                     nsAttrValue& aResult) {
   1205  return aResult.ParseEnumValue(aString, kTableCellHAlignTable, false);
   1206 }
   1207 
   1208 //----------------------------------------
   1209 
   1210 bool nsGenericHTMLElement::ParseTableVAlignValue(const nsAString& aString,
   1211                                                 nsAttrValue& aResult) {
   1212  return aResult.ParseEnumValue(aString, kTableVAlignTable, false);
   1213 }
   1214 
   1215 bool nsGenericHTMLElement::ParseDivAlignValue(const nsAString& aString,
   1216                                              nsAttrValue& aResult) {
   1217  return aResult.ParseEnumValue(aString, kDivAlignTable, false);
   1218 }
   1219 
   1220 bool nsGenericHTMLElement::ParseImageAttribute(nsAtom* aAttribute,
   1221                                               const nsAString& aString,
   1222                                               nsAttrValue& aResult) {
   1223  if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
   1224      aAttribute == nsGkAtoms::hspace || aAttribute == nsGkAtoms::vspace) {
   1225    return aResult.ParseHTMLDimension(aString);
   1226  }
   1227  if (aAttribute == nsGkAtoms::border) {
   1228    return aResult.ParseNonNegativeIntValue(aString);
   1229  }
   1230  return false;
   1231 }
   1232 
   1233 static constexpr nsAttrValue::EnumTableEntry kReferrerPolicyTable[] = {
   1234    {GetEnumString(ReferrerPolicy::No_referrer).get(),
   1235     static_cast<int16_t>(ReferrerPolicy::No_referrer)},
   1236    {GetEnumString(ReferrerPolicy::Origin).get(),
   1237     static_cast<int16_t>(ReferrerPolicy::Origin)},
   1238    {GetEnumString(ReferrerPolicy::Origin_when_cross_origin).get(),
   1239     static_cast<int16_t>(ReferrerPolicy::Origin_when_cross_origin)},
   1240    {GetEnumString(ReferrerPolicy::No_referrer_when_downgrade).get(),
   1241     static_cast<int16_t>(ReferrerPolicy::No_referrer_when_downgrade)},
   1242    {GetEnumString(ReferrerPolicy::Unsafe_url).get(),
   1243     static_cast<int16_t>(ReferrerPolicy::Unsafe_url)},
   1244    {GetEnumString(ReferrerPolicy::Strict_origin).get(),
   1245     static_cast<int16_t>(ReferrerPolicy::Strict_origin)},
   1246    {GetEnumString(ReferrerPolicy::Same_origin).get(),
   1247     static_cast<int16_t>(ReferrerPolicy::Same_origin)},
   1248    {GetEnumString(ReferrerPolicy::Strict_origin_when_cross_origin).get(),
   1249     static_cast<int16_t>(ReferrerPolicy::Strict_origin_when_cross_origin)},
   1250 };
   1251 
   1252 bool nsGenericHTMLElement::ParseReferrerAttribute(const nsAString& aString,
   1253                                                  nsAttrValue& aResult) {
   1254  using mozilla::dom::ReferrerInfo;
   1255  return aResult.ParseEnumValue(aString, kReferrerPolicyTable, false);
   1256 }
   1257 
   1258 bool nsGenericHTMLElement::ParseFrameborderValue(const nsAString& aString,
   1259                                                 nsAttrValue& aResult) {
   1260  return aResult.ParseEnumValue(aString, kFrameborderTable, false);
   1261 }
   1262 
   1263 bool nsGenericHTMLElement::ParseScrollingValue(const nsAString& aString,
   1264                                               nsAttrValue& aResult) {
   1265  return aResult.ParseEnumValue(aString, kScrollingTable, false);
   1266 }
   1267 
   1268 static inline void MapLangAttributeInto(MappedDeclarationsBuilder& aBuilder) {
   1269  const nsAttrValue* langValue = aBuilder.GetAttr(nsGkAtoms::lang);
   1270  if (!langValue) {
   1271    return;
   1272  }
   1273  MOZ_ASSERT(langValue->Type() == nsAttrValue::eAtom);
   1274 
   1275  // Adaptor for nsCString to expose the Buffer interface to Locale::ToString.
   1276  class BufferAdaptor {
   1277   public:
   1278    using CharType = char;
   1279 
   1280    explicit BufferAdaptor(nsCString& aString) : mString(aString) {}
   1281    CharType* data() { return mString.BeginWriting(); }
   1282    size_t capacity() const { return mString.Length(); }
   1283    bool reserve(size_t aLen) { return mString.SetLength(aLen, fallible); }
   1284    void written(size_t aLen) { mString.SetLength(aLen); }
   1285 
   1286   private:
   1287    nsCString& mString;
   1288  };
   1289 
   1290  // Try parsing lang as a Locale and canonicalizing the subtags; if parsing
   1291  // succeeds, we record the canonicalized version rather than the original,
   1292  // so that code checking for particular codes can assume canonical casing.
   1293  // Note that in some cases this will also map 3-character ISO 639-3 tags to
   1294  // their corresponding 2-char ISO 639-1 tags.
   1295  RefPtr<nsAtom> lang = langValue->GetAtomValue();
   1296  nsAtomCString langStr(lang);
   1297  intl::Locale loc;
   1298  if (intl::LocaleParser::TryParse(langStr, loc).isOk() &&
   1299      loc.Canonicalize().isOk()) {
   1300    nsAutoCString canonical;
   1301    BufferAdaptor buffer(canonical);
   1302    if (loc.ToString(buffer).isOk() && canonical != langStr) {
   1303      lang = NS_Atomize(canonical);
   1304    }
   1305  }
   1306 
   1307  aBuilder.SetIdentAtomValueIfUnset(eCSSProperty__x_lang, lang);
   1308  if (!aBuilder.PropertyIsSet(eCSSProperty_text_emphasis_position)) {
   1309    if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) {
   1310      aBuilder.SetKeywordValue(eCSSProperty_text_emphasis_position,
   1311                               StyleTextEmphasisPosition::UNDER._0);
   1312    } else if (nsStyleUtil::MatchesLanguagePrefix(lang, u"ja") ||
   1313               nsStyleUtil::MatchesLanguagePrefix(lang, u"mn")) {
   1314      // This branch is currently no part of the spec.
   1315      // See bug 1040668 comment 69 and comment 75.
   1316      aBuilder.SetKeywordValue(eCSSProperty_text_emphasis_position,
   1317                               StyleTextEmphasisPosition::OVER._0);
   1318    }
   1319  }
   1320 }
   1321 
   1322 /**
   1323 * Handle attributes common to all html elements
   1324 */
   1325 void nsGenericHTMLElement::MapCommonAttributesIntoExceptHidden(
   1326    MappedDeclarationsBuilder& aBuilder) {
   1327  MapLangAttributeInto(aBuilder);
   1328 }
   1329 
   1330 void nsGenericHTMLElement::MapCommonAttributesInto(
   1331    MappedDeclarationsBuilder& aBuilder) {
   1332  MapCommonAttributesIntoExceptHidden(aBuilder);
   1333  MOZ_ASSERT(!aBuilder.PropertyIsSet(eCSSProperty_display));
   1334  MOZ_ASSERT(!aBuilder.PropertyIsSet(eCSSProperty_content_visibility));
   1335 
   1336  if (const nsAttrValue* hidden = aBuilder.GetAttr(nsGkAtoms::hidden)) {
   1337    if (StaticPrefs::dom_hidden_until_found_enabled() &&
   1338        hidden->Equals(nsGkAtoms::untilFound, eIgnoreCase)) {
   1339      aBuilder.SetKeywordValue(eCSSProperty_content_visibility,
   1340                               StyleContentVisibility::Hidden);
   1341    } else {
   1342      aBuilder.SetKeywordValue(eCSSProperty_display, StyleDisplay::None._0);
   1343    }
   1344  }
   1345 }
   1346 
   1347 /* static */
   1348 const nsGenericHTMLElement::MappedAttributeEntry
   1349    nsGenericHTMLElement::sCommonAttributeMap[] = {{nsGkAtoms::contenteditable},
   1350                                                   {nsGkAtoms::lang},
   1351                                                   {nsGkAtoms::hidden},
   1352                                                   {nullptr}};
   1353 
   1354 /* static */
   1355 const Element::MappedAttributeEntry
   1356    nsGenericHTMLElement::sImageMarginSizeAttributeMap[] = {{nsGkAtoms::width},
   1357                                                            {nsGkAtoms::height},
   1358                                                            {nsGkAtoms::hspace},
   1359                                                            {nsGkAtoms::vspace},
   1360                                                            {nullptr}};
   1361 
   1362 /* static */
   1363 const Element::MappedAttributeEntry
   1364    nsGenericHTMLElement::sImageAlignAttributeMap[] = {{nsGkAtoms::align},
   1365                                                       {nullptr}};
   1366 
   1367 /* static */
   1368 const Element::MappedAttributeEntry
   1369    nsGenericHTMLElement::sDivAlignAttributeMap[] = {{nsGkAtoms::align},
   1370                                                     {nullptr}};
   1371 
   1372 /* static */
   1373 const Element::MappedAttributeEntry
   1374    nsGenericHTMLElement::sImageBorderAttributeMap[] = {{nsGkAtoms::border},
   1375                                                        {nullptr}};
   1376 
   1377 /* static */
   1378 const Element::MappedAttributeEntry
   1379    nsGenericHTMLElement::sBackgroundAttributeMap[] = {
   1380        {nsGkAtoms::background}, {nsGkAtoms::bgcolor}, {nullptr}};
   1381 
   1382 /* static */
   1383 const Element::MappedAttributeEntry
   1384    nsGenericHTMLElement::sBackgroundColorAttributeMap[] = {
   1385        {nsGkAtoms::bgcolor}, {nullptr}};
   1386 
   1387 void nsGenericHTMLElement::MapImageAlignAttributeInto(
   1388    MappedDeclarationsBuilder& aBuilder) {
   1389  const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::align);
   1390  if (value && value->Type() == nsAttrValue::eEnum) {
   1391    switch (HTMLAlignValue(value->GetEnumValue())) {
   1392      case HTMLAlignValue::Left:
   1393        aBuilder.SetKeywordValue(eCSSProperty_float, StyleFloat::Left);
   1394        break;
   1395      case HTMLAlignValue::Right:
   1396        aBuilder.SetKeywordValue(eCSSProperty_float, StyleFloat::Right);
   1397        break;
   1398      case HTMLAlignValue::TextTop:
   1399        aBuilder.SetKeywordValue(eCSSProperty_vertical_align,
   1400                                 StyleVerticalAlignKeyword::TextTop);
   1401        break;
   1402      case HTMLAlignValue::Top:
   1403        aBuilder.SetKeywordValue(eCSSProperty_vertical_align,
   1404                                 StyleVerticalAlignKeyword::Top);
   1405        break;
   1406      case HTMLAlignValue::Middle:
   1407      case HTMLAlignValue::Center:
   1408        aBuilder.SetKeywordValue(
   1409            eCSSProperty_vertical_align,
   1410            StyleVerticalAlignKeyword::MozMiddleWithBaseline);
   1411        break;
   1412      case HTMLAlignValue::AbsMiddle:
   1413      case HTMLAlignValue::AbsCenter:
   1414        aBuilder.SetKeywordValue(eCSSProperty_vertical_align,
   1415                                 StyleVerticalAlignKeyword::Middle);
   1416        break;
   1417      case HTMLAlignValue::AbsBottom:
   1418        aBuilder.SetKeywordValue(eCSSProperty_vertical_align,
   1419                                 StyleVerticalAlignKeyword::Bottom);
   1420        break;
   1421      case HTMLAlignValue::Bottom:  // Intentionally mapped to `baseline`
   1422      case HTMLAlignValue::Baseline:
   1423        aBuilder.SetKeywordValue(eCSSProperty_vertical_align,
   1424                                 StyleVerticalAlignKeyword::Baseline);
   1425        break;
   1426      default:
   1427        MOZ_ASSERT_UNREACHABLE("Unexpected align value");
   1428        break;
   1429    }
   1430  }
   1431 }
   1432 
   1433 void nsGenericHTMLElement::MapDivAlignAttributeInto(
   1434    MappedDeclarationsBuilder& aBuilder) {
   1435  const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::align);
   1436  if (value && value->Type() == nsAttrValue::eEnum) {
   1437    switch (HTMLAlignValue(value->GetEnumValue())) {
   1438      case HTMLAlignValue::Left:
   1439        aBuilder.SetKeywordValue(eCSSProperty_text_align,
   1440                                 StyleTextAlign::MozLeft);
   1441        break;
   1442      case HTMLAlignValue::Right:
   1443        aBuilder.SetKeywordValue(eCSSProperty_text_align,
   1444                                 StyleTextAlign::MozRight);
   1445        break;
   1446      case HTMLAlignValue::Center:
   1447      case HTMLAlignValue::Middle:
   1448        aBuilder.SetKeywordValue(eCSSProperty_text_align,
   1449                                 StyleTextAlign::MozCenter);
   1450        break;
   1451      case HTMLAlignValue::Justify:
   1452        aBuilder.SetKeywordValue(eCSSProperty_text_align,
   1453                                 StyleTextAlign::Justify);
   1454        break;
   1455      default:
   1456        MOZ_ASSERT_UNREACHABLE("Unexpected align value");
   1457        break;
   1458    }
   1459  }
   1460 }
   1461 
   1462 void nsGenericHTMLElement::MapTableVAlignAttributeInto(
   1463    MappedDeclarationsBuilder& aBuilder) {
   1464  const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::valign);
   1465  if (value && value->Type() == nsAttrValue::eEnum) {
   1466    switch (TableCellAlignment(value->GetEnumValue())) {
   1467      case TableCellAlignment::Top:
   1468        aBuilder.SetKeywordValue(eCSSProperty_vertical_align,
   1469                                 StyleVerticalAlignKeyword::Top);
   1470        break;
   1471      case TableCellAlignment::Middle:
   1472        aBuilder.SetKeywordValue(eCSSProperty_vertical_align,
   1473                                 StyleVerticalAlignKeyword::Middle);
   1474        break;
   1475      case TableCellAlignment::Bottom:
   1476        aBuilder.SetKeywordValue(eCSSProperty_vertical_align,
   1477                                 StyleVerticalAlignKeyword::Bottom);
   1478        break;
   1479      case TableCellAlignment::Baseline:
   1480        aBuilder.SetKeywordValue(eCSSProperty_vertical_align,
   1481                                 StyleVerticalAlignKeyword::Baseline);
   1482        break;
   1483    }
   1484  }
   1485 }
   1486 
   1487 void nsGenericHTMLElement::MapTableHAlignAttributeInto(
   1488    MappedDeclarationsBuilder& aBuilder) {
   1489  const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::align);
   1490  if (value && value->Type() == nsAttrValue::eEnum) {
   1491    switch (HTMLAlignValue(value->GetEnumValue())) {
   1492      case HTMLAlignValue::Center:
   1493        aBuilder.SetAutoValueIfUnset(eCSSProperty_margin_left);
   1494        aBuilder.SetAutoValueIfUnset(eCSSProperty_margin_right);
   1495        break;
   1496      case HTMLAlignValue::Left:
   1497        aBuilder.SetKeywordValue(eCSSProperty_float, StyleFloat::Left);
   1498        break;
   1499      case HTMLAlignValue::Right:
   1500        aBuilder.SetKeywordValue(eCSSProperty_float, StyleFloat::Right);
   1501        break;
   1502      default:
   1503        MOZ_ASSERT_UNREACHABLE("Unexpected align value");
   1504        break;
   1505    }
   1506  }
   1507 }
   1508 
   1509 void nsGenericHTMLElement::MapTableCellHAlignAttributeInto(
   1510    MappedDeclarationsBuilder& aBuilder) {
   1511  const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::align);
   1512  if (value && value->Type() == nsAttrValue::eEnum) {
   1513    switch (HTMLAlignValue(value->GetEnumValue())) {
   1514      case HTMLAlignValue::Left:
   1515        aBuilder.SetKeywordValue(eCSSProperty_text_align,
   1516                                 StyleTextAlign::MozLeft);
   1517        break;
   1518      case HTMLAlignValue::Right:
   1519        aBuilder.SetKeywordValue(eCSSProperty_text_align,
   1520                                 StyleTextAlign::MozRight);
   1521        break;
   1522      case HTMLAlignValue::Center:
   1523      case HTMLAlignValue::Middle:
   1524        aBuilder.SetKeywordValue(eCSSProperty_text_align,
   1525                                 StyleTextAlign::MozCenter);
   1526        break;
   1527      case HTMLAlignValue::AbsMiddle:
   1528        aBuilder.SetKeywordValue(eCSSProperty_text_align,
   1529                                 StyleTextAlign::Center);
   1530        break;
   1531      case HTMLAlignValue::Justify:
   1532        aBuilder.SetKeywordValue(eCSSProperty_text_align,
   1533                                 StyleTextAlign::Justify);
   1534        break;
   1535      default:
   1536        MOZ_ASSERT_UNREACHABLE("Unexpected align value");
   1537        break;
   1538    }
   1539  }
   1540 }
   1541 
   1542 void nsGenericHTMLElement::MapDimensionAttributeInto(
   1543    MappedDeclarationsBuilder& aBuilder, NonCustomCSSPropertyId aProp,
   1544    const nsAttrValue& aValue) {
   1545  MOZ_ASSERT(!aBuilder.PropertyIsSet(aProp),
   1546             "Why mapping the same property twice?");
   1547  if (aValue.Type() == nsAttrValue::eInteger) {
   1548    return aBuilder.SetPixelValue(aProp, aValue.GetIntegerValue());
   1549  }
   1550  if (aValue.Type() == nsAttrValue::ePercent) {
   1551    return aBuilder.SetPercentValue(aProp, aValue.GetPercentValue());
   1552  }
   1553  if (aValue.Type() == nsAttrValue::eDoubleValue) {
   1554    return aBuilder.SetPixelValue(aProp, aValue.GetDoubleValue());
   1555  }
   1556 }
   1557 
   1558 void nsGenericHTMLElement::MapImageMarginAttributeInto(
   1559    MappedDeclarationsBuilder& aBuilder) {
   1560  // hspace: value
   1561  if (const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::hspace)) {
   1562    MapDimensionAttributeInto(aBuilder, eCSSProperty_margin_left, *value);
   1563    MapDimensionAttributeInto(aBuilder, eCSSProperty_margin_right, *value);
   1564  }
   1565 
   1566  // vspace: value
   1567  if (const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::vspace)) {
   1568    MapDimensionAttributeInto(aBuilder, eCSSProperty_margin_top, *value);
   1569    MapDimensionAttributeInto(aBuilder, eCSSProperty_margin_bottom, *value);
   1570  }
   1571 }
   1572 
   1573 void nsGenericHTMLElement::MapWidthAttributeInto(
   1574    MappedDeclarationsBuilder& aBuilder) {
   1575  if (const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::width)) {
   1576    MapDimensionAttributeInto(aBuilder, eCSSProperty_width, *value);
   1577  }
   1578 }
   1579 
   1580 void nsGenericHTMLElement::MapHeightAttributeInto(
   1581    MappedDeclarationsBuilder& aBuilder) {
   1582  if (const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::height)) {
   1583    MapDimensionAttributeInto(aBuilder, eCSSProperty_height, *value);
   1584  }
   1585 }
   1586 
   1587 void nsGenericHTMLElement::DoMapAspectRatio(
   1588    const nsAttrValue& aWidth, const nsAttrValue& aHeight,
   1589    MappedDeclarationsBuilder& aBuilder) {
   1590  Maybe<double> w;
   1591  if (aWidth.Type() == nsAttrValue::eInteger) {
   1592    w.emplace(aWidth.GetIntegerValue());
   1593  } else if (aWidth.Type() == nsAttrValue::eDoubleValue) {
   1594    w.emplace(aWidth.GetDoubleValue());
   1595  }
   1596 
   1597  Maybe<double> h;
   1598  if (aHeight.Type() == nsAttrValue::eInteger) {
   1599    h.emplace(aHeight.GetIntegerValue());
   1600  } else if (aHeight.Type() == nsAttrValue::eDoubleValue) {
   1601    h.emplace(aHeight.GetDoubleValue());
   1602  }
   1603 
   1604  if (w && h) {
   1605    aBuilder.SetAspectRatio(*w, *h);
   1606  }
   1607 }
   1608 
   1609 void nsGenericHTMLElement::MapImageSizeAttributesInto(
   1610    MappedDeclarationsBuilder& aBuilder, MapAspectRatio aMapAspectRatio) {
   1611  auto* width = aBuilder.GetAttr(nsGkAtoms::width);
   1612  auto* height = aBuilder.GetAttr(nsGkAtoms::height);
   1613  if (width) {
   1614    MapDimensionAttributeInto(aBuilder, eCSSProperty_width, *width);
   1615  }
   1616  if (height) {
   1617    MapDimensionAttributeInto(aBuilder, eCSSProperty_height, *height);
   1618  }
   1619  if (aMapAspectRatio == MapAspectRatio::Yes && width && height) {
   1620    DoMapAspectRatio(*width, *height, aBuilder);
   1621  }
   1622 }
   1623 
   1624 void nsGenericHTMLElement::MapAspectRatioInto(
   1625    MappedDeclarationsBuilder& aBuilder) {
   1626  auto* width = aBuilder.GetAttr(nsGkAtoms::width);
   1627  auto* height = aBuilder.GetAttr(nsGkAtoms::height);
   1628  if (width && height) {
   1629    DoMapAspectRatio(*width, *height, aBuilder);
   1630  }
   1631 }
   1632 
   1633 void nsGenericHTMLElement::MapImageBorderAttributeInto(
   1634    MappedDeclarationsBuilder& aBuilder) {
   1635  // border: pixels
   1636  const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::border);
   1637  if (!value) return;
   1638 
   1639  nscoord val = 0;
   1640  if (value->Type() == nsAttrValue::eInteger) val = value->GetIntegerValue();
   1641 
   1642  aBuilder.SetPixelValueIfUnset(eCSSProperty_border_top_width, (float)val);
   1643  aBuilder.SetPixelValueIfUnset(eCSSProperty_border_right_width, (float)val);
   1644  aBuilder.SetPixelValueIfUnset(eCSSProperty_border_bottom_width, (float)val);
   1645  aBuilder.SetPixelValueIfUnset(eCSSProperty_border_left_width, (float)val);
   1646 
   1647  aBuilder.SetKeywordValueIfUnset(eCSSProperty_border_top_style,
   1648                                  StyleBorderStyle::Solid);
   1649  aBuilder.SetKeywordValueIfUnset(eCSSProperty_border_right_style,
   1650                                  StyleBorderStyle::Solid);
   1651  aBuilder.SetKeywordValueIfUnset(eCSSProperty_border_bottom_style,
   1652                                  StyleBorderStyle::Solid);
   1653  aBuilder.SetKeywordValueIfUnset(eCSSProperty_border_left_style,
   1654                                  StyleBorderStyle::Solid);
   1655 
   1656  aBuilder.SetCurrentColorIfUnset(eCSSProperty_border_top_color);
   1657  aBuilder.SetCurrentColorIfUnset(eCSSProperty_border_right_color);
   1658  aBuilder.SetCurrentColorIfUnset(eCSSProperty_border_bottom_color);
   1659  aBuilder.SetCurrentColorIfUnset(eCSSProperty_border_left_color);
   1660 }
   1661 
   1662 void nsGenericHTMLElement::MapBackgroundInto(
   1663    MappedDeclarationsBuilder& aBuilder) {
   1664  if (!aBuilder.PropertyIsSet(eCSSProperty_background_image)) {
   1665    // background
   1666    if (const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::background)) {
   1667      aBuilder.SetBackgroundImage(*value);
   1668    }
   1669  }
   1670 }
   1671 
   1672 void nsGenericHTMLElement::MapBGColorInto(MappedDeclarationsBuilder& aBuilder) {
   1673  if (aBuilder.PropertyIsSet(eCSSProperty_background_color)) {
   1674    return;
   1675  }
   1676  const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::bgcolor);
   1677  nscolor color;
   1678  if (value && value->GetColorValue(color)) {
   1679    aBuilder.SetColorValue(eCSSProperty_background_color, color);
   1680  }
   1681 }
   1682 
   1683 void nsGenericHTMLElement::MapBackgroundAttributesInto(
   1684    MappedDeclarationsBuilder& aBuilder) {
   1685  MapBackgroundInto(aBuilder);
   1686  MapBGColorInto(aBuilder);
   1687 }
   1688 
   1689 //----------------------------------------------------------------------
   1690 
   1691 int32_t nsGenericHTMLElement::GetIntAttr(nsAtom* aAttr,
   1692                                         int32_t aDefault) const {
   1693  const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
   1694  if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
   1695    return attrVal->GetIntegerValue();
   1696  }
   1697  return aDefault;
   1698 }
   1699 
   1700 nsresult nsGenericHTMLElement::SetIntAttr(nsAtom* aAttr, int32_t aValue) {
   1701  nsAutoString value;
   1702  value.AppendInt(aValue);
   1703 
   1704  return SetAttr(kNameSpaceID_None, aAttr, value, true);
   1705 }
   1706 
   1707 uint32_t nsGenericHTMLElement::GetUnsignedIntAttr(nsAtom* aAttr,
   1708                                                  uint32_t aDefault) const {
   1709  const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
   1710  if (!attrVal || attrVal->Type() != nsAttrValue::eInteger) {
   1711    return aDefault;
   1712  }
   1713 
   1714  return attrVal->GetIntegerValue();
   1715 }
   1716 
   1717 uint32_t nsGenericHTMLElement::GetDimensionAttrAsUnsignedInt(
   1718    nsAtom* aAttr, uint32_t aDefault) const {
   1719  const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
   1720  if (!attrVal) {
   1721    return aDefault;
   1722  }
   1723 
   1724  if (attrVal->Type() == nsAttrValue::eInteger) {
   1725    return attrVal->GetIntegerValue();
   1726  }
   1727 
   1728  if (attrVal->Type() == nsAttrValue::ePercent) {
   1729    // This is a nasty hack.  When we parsed the value, we stored it as an
   1730    // ePercent, not eInteger, because there was a '%' after it in the string.
   1731    // But the spec says to basically re-parse the string as an integer.
   1732    // Luckily, we can just return the value we have stored.  But
   1733    // GetPercentValue() divides it by 100, so we need to multiply it back.
   1734    return uint32_t(attrVal->GetPercentValue() * 100.0f);
   1735  }
   1736 
   1737  if (attrVal->Type() == nsAttrValue::eDoubleValue) {
   1738    return uint32_t(attrVal->GetDoubleValue());
   1739  }
   1740 
   1741  // Unfortunately, the set of values that are valid dimensions is not a
   1742  // superset of values that are valid unsigned ints.  In particular "+100" is
   1743  // not a valid dimension, but should parse as the unsigned int "100".  So if
   1744  // we got here and we don't have a valid dimension value, just try re-parsing
   1745  // the string we have as an integer.
   1746  nsAutoString val;
   1747  attrVal->ToString(val);
   1748  nsContentUtils::ParseHTMLIntegerResultFlags result;
   1749  int32_t parsedInt = nsContentUtils::ParseHTMLInteger(val, &result);
   1750  if ((result & nsContentUtils::eParseHTMLInteger_Error) || parsedInt < 0) {
   1751    return aDefault;
   1752  }
   1753 
   1754  return parsedInt;
   1755 }
   1756 
   1757 void nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr, nsAtom* aBaseAttr,
   1758                                      nsAString& aResult) const {
   1759  nsCOMPtr<nsIURI> uri;
   1760  const nsAttrValue* attr = GetURIAttr(aAttr, aBaseAttr, getter_AddRefs(uri));
   1761  if (!attr) {
   1762    aResult.Truncate();
   1763    return;
   1764  }
   1765  if (!uri) {
   1766    // Just return the attr value
   1767    attr->ToString(aResult);
   1768    return;
   1769  }
   1770  nsAutoCString spec;
   1771  uri->GetSpec(spec);
   1772  CopyUTF8toUTF16(spec, aResult);
   1773 }
   1774 
   1775 void nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr, nsAtom* aBaseAttr,
   1776                                      nsACString& aResult) const {
   1777  nsCOMPtr<nsIURI> uri;
   1778  const nsAttrValue* attr = GetURIAttr(aAttr, aBaseAttr, getter_AddRefs(uri));
   1779  if (!attr) {
   1780    aResult.Truncate();
   1781    return;
   1782  }
   1783  if (!uri) {
   1784    // Just return the attr value
   1785    nsAutoString value;
   1786    attr->ToString(value);
   1787    CopyUTF16toUTF8(value, aResult);
   1788    return;
   1789  }
   1790  uri->GetSpec(aResult);
   1791 }
   1792 
   1793 const nsAttrValue* nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr,
   1794                                                    nsAtom* aBaseAttr,
   1795                                                    nsIURI** aURI) const {
   1796  *aURI = nullptr;
   1797 
   1798  const nsAttrValue* attr = mAttrs.GetAttr(aAttr);
   1799  if (!attr) {
   1800    return nullptr;
   1801  }
   1802 
   1803  nsCOMPtr<nsIURI> baseURI = GetBaseURI();
   1804  if (aBaseAttr) {
   1805    nsAutoString baseAttrValue;
   1806    if (GetAttr(aBaseAttr, baseAttrValue)) {
   1807      nsCOMPtr<nsIURI> baseAttrURI;
   1808      nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
   1809          getter_AddRefs(baseAttrURI), baseAttrValue, OwnerDoc(), baseURI);
   1810      if (NS_FAILED(rv)) {
   1811        return attr;
   1812      }
   1813      baseURI.swap(baseAttrURI);
   1814    }
   1815  }
   1816 
   1817  // Don't care about return value.  If it fails, we still want to
   1818  // return true, and *aURI will be null.
   1819  nsContentUtils::NewURIWithDocumentCharset(
   1820      aURI, nsAttrValueOrString(attr).String(), OwnerDoc(), baseURI);
   1821  return attr;
   1822 }
   1823 
   1824 bool nsGenericHTMLElement::IsContentEditable() const {
   1825  for (const auto* element : InclusiveAncestorsOfType<nsGenericHTMLElement>()) {
   1826    const ContentEditableState state = element->GetContentEditableState();
   1827    if (state != ContentEditableState::Inherit) {
   1828      return IsEditableState(state);
   1829    }
   1830  }
   1831  return false;
   1832 }
   1833 
   1834 bool nsGenericHTMLElement::IsLabelable() const {
   1835  return IsAnyOfHTMLElements(nsGkAtoms::progress, nsGkAtoms::meter);
   1836 }
   1837 
   1838 /* static */
   1839 bool nsGenericHTMLElement::MatchLabelsElement(Element* aElement,
   1840                                              int32_t aNamespaceID,
   1841                                              nsAtom* aAtom, void* aData) {
   1842  HTMLLabelElement* element = HTMLLabelElement::FromNode(aElement);
   1843  return element && element->GetControl() == aData;
   1844 }
   1845 
   1846 already_AddRefed<nsINodeList> nsGenericHTMLElement::Labels() {
   1847  MOZ_ASSERT(IsLabelable(),
   1848             "Labels() only allow labelable elements to use it.");
   1849  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
   1850 
   1851  if (!slots->mLabelsList) {
   1852    slots->mLabelsList =
   1853        new nsLabelsNodeList(SubtreeRoot(), MatchLabelsElement, nullptr, this);
   1854  }
   1855 
   1856  RefPtr<nsLabelsNodeList> labels = slots->mLabelsList;
   1857  return labels.forget();
   1858 }
   1859 
   1860 // static
   1861 bool nsGenericHTMLElement::LegacyTouchAPIEnabled(JSContext* aCx,
   1862                                                 JSObject* aGlobal) {
   1863  return TouchEvent::LegacyAPIEnabled(aCx, aGlobal);
   1864 }
   1865 
   1866 bool nsGenericHTMLElement::IsFormControlDefaultFocusable(
   1867    IsFocusableFlags aFlags) const {
   1868  if (!(aFlags & IsFocusableFlags::WithMouse)) {
   1869    return true;
   1870  }
   1871  switch (StaticPrefs::accessibility_mouse_focuses_formcontrol()) {
   1872    case 0:
   1873      return false;
   1874    case 1:
   1875      return true;
   1876    default:
   1877      return !IsInChromeDocument();
   1878  }
   1879 }
   1880 
   1881 //----------------------------------------------------------------------
   1882 
   1883 nsGenericHTMLFormElement::nsGenericHTMLFormElement(
   1884    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
   1885    : nsGenericHTMLElement(std::move(aNodeInfo)) {
   1886  // We should add the ElementState::ENABLED bit here as needed, but that
   1887  // depends on our type, which is not initialized yet.  So we have to do this
   1888  // in subclasses. Same for a couple other bits.
   1889 }
   1890 
   1891 void nsGenericHTMLFormElement::ClearForm(bool aRemoveFromForm,
   1892                                         bool aUnbindOrDelete) {
   1893  MOZ_ASSERT(IsFormAssociatedElement());
   1894 
   1895  HTMLFormElement* form = GetFormInternal();
   1896  NS_ASSERTION((form != nullptr) == HasFlag(ADDED_TO_FORM),
   1897               "Form control should have had flag set correctly");
   1898 
   1899  if (!form) {
   1900    return;
   1901  }
   1902 
   1903  if (aRemoveFromForm) {
   1904    nsAutoString nameVal, idVal;
   1905    GetAttr(nsGkAtoms::name, nameVal);
   1906    GetAttr(nsGkAtoms::id, idVal);
   1907 
   1908    form->RemoveElement(this, true);
   1909 
   1910    if (!nameVal.IsEmpty()) {
   1911      form->RemoveElementFromTable(this, nameVal);
   1912    }
   1913 
   1914    if (!idVal.IsEmpty()) {
   1915      form->RemoveElementFromTable(this, idVal);
   1916    }
   1917  }
   1918 
   1919  UnsetFlags(ADDED_TO_FORM);
   1920  SetFormInternal(nullptr, false);
   1921  AfterClearForm(aUnbindOrDelete);
   1922 }
   1923 
   1924 nsresult nsGenericHTMLFormElement::BindToTree(BindContext& aContext,
   1925                                              nsINode& aParent) {
   1926  nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
   1927  NS_ENSURE_SUCCESS(rv, rv);
   1928 
   1929  if (IsFormAssociatedElement()) {
   1930    // If @form is set, the element *has* to be in a composed document,
   1931    // otherwise it wouldn't be possible to find an element with the
   1932    // corresponding id. If @form isn't set, the element *has* to have a parent,
   1933    // otherwise it wouldn't be possible to find a form ancestor. We should not
   1934    // call UpdateFormOwner if none of these conditions are fulfilled.
   1935    if (HasAttr(nsGkAtoms::form) ? IsInComposedDoc() : aParent.IsContent()) {
   1936      UpdateFormOwner(true, nullptr);
   1937    }
   1938  }
   1939 
   1940  // Set parent fieldset which should be used for the disabled state.
   1941  UpdateFieldSet(false);
   1942  return NS_OK;
   1943 }
   1944 
   1945 void nsGenericHTMLFormElement::UnbindFromTree(UnbindContext& aContext) {
   1946  // Save state before doing anything else.
   1947  SaveState();
   1948 
   1949  if (IsFormAssociatedElement()) {
   1950    if (HTMLFormElement* form = GetFormInternal()) {
   1951      // Might need to unset form
   1952      if (aContext.IsUnbindRoot(this)) {
   1953        // No more parent means no more form
   1954        ClearForm(true, true);
   1955      } else {
   1956        // Recheck whether we should still have an form.
   1957        if (HasAttr(nsGkAtoms::form) || !FindAncestorForm(form)) {
   1958          ClearForm(true, true);
   1959        } else {
   1960          UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
   1961        }
   1962      }
   1963    }
   1964 
   1965    // We have to remove the form id observer if there was one.
   1966    // We will re-add one later if needed (during bind to tree).
   1967    if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
   1968                                        nsGkAtoms::form)) {
   1969      RemoveFormIdObserver();
   1970    }
   1971  }
   1972 
   1973  nsGenericHTMLElement::UnbindFromTree(aContext);
   1974 
   1975  // The element might not have a fieldset anymore.
   1976  UpdateFieldSet(false);
   1977 }
   1978 
   1979 void nsGenericHTMLFormElement::BeforeSetAttr(int32_t aNameSpaceID,
   1980                                             nsAtom* aName,
   1981                                             const nsAttrValue* aValue,
   1982                                             bool aNotify) {
   1983  if (aNameSpaceID == kNameSpaceID_None && IsFormAssociatedElement()) {
   1984    nsAutoString tmp;
   1985    HTMLFormElement* form = GetFormInternal();
   1986 
   1987    // remove the control from the hashtable as needed
   1988 
   1989    if (form && (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
   1990      GetAttr(aName, tmp);
   1991 
   1992      if (!tmp.IsEmpty()) {
   1993        form->RemoveElementFromTable(this, tmp);
   1994      }
   1995    }
   1996 
   1997    if (form && aName == nsGkAtoms::type) {
   1998      GetAttr(nsGkAtoms::name, tmp);
   1999 
   2000      if (!tmp.IsEmpty()) {
   2001        form->RemoveElementFromTable(this, tmp);
   2002      }
   2003 
   2004      GetAttr(nsGkAtoms::id, tmp);
   2005 
   2006      if (!tmp.IsEmpty()) {
   2007        form->RemoveElementFromTable(this, tmp);
   2008      }
   2009 
   2010      form->RemoveElement(this, false);
   2011    }
   2012 
   2013    if (aName == nsGkAtoms::form) {
   2014      // If @form isn't set or set to the empty string, there were no observer
   2015      // so we don't have to remove it.
   2016      if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
   2017                                          nsGkAtoms::form)) {
   2018        // The current form id observer is no longer needed.
   2019        // A new one may be added in AfterSetAttr.
   2020        RemoveFormIdObserver();
   2021      }
   2022    }
   2023  }
   2024 
   2025  return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName, aValue,
   2026                                             aNotify);
   2027 }
   2028 
   2029 void nsGenericHTMLFormElement::AfterSetAttr(
   2030    int32_t aNameSpaceID, nsAtom* aName, const nsAttrValue* aValue,
   2031    const nsAttrValue* aOldValue, nsIPrincipal* aMaybeScriptedPrincipal,
   2032    bool aNotify) {
   2033  if (aNameSpaceID == kNameSpaceID_None && IsFormAssociatedElement()) {
   2034    HTMLFormElement* form = GetFormInternal();
   2035 
   2036    // add the control to the hashtable as needed
   2037    if (form && (aName == nsGkAtoms::name || aName == nsGkAtoms::id) &&
   2038        aValue && !aValue->IsEmptyString()) {
   2039      MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom,
   2040                 "Expected atom value for name/id");
   2041      form->AddElementToTable(this,
   2042                              nsDependentAtomString(aValue->GetAtomValue()));
   2043    }
   2044 
   2045    if (form && aName == nsGkAtoms::type) {
   2046      nsAutoString tmp;
   2047 
   2048      GetAttr(nsGkAtoms::name, tmp);
   2049 
   2050      if (!tmp.IsEmpty()) {
   2051        form->AddElementToTable(this, tmp);
   2052      }
   2053 
   2054      GetAttr(nsGkAtoms::id, tmp);
   2055 
   2056      if (!tmp.IsEmpty()) {
   2057        form->AddElementToTable(this, tmp);
   2058      }
   2059 
   2060      form->AddElement(this, false, aNotify);
   2061    }
   2062 
   2063    if (aName == nsGkAtoms::form) {
   2064      // We need a new form id observer.
   2065      DocumentOrShadowRoot* docOrShadow =
   2066          GetUncomposedDocOrConnectedShadowRoot();
   2067      if (docOrShadow) {
   2068        Element* formIdElement = nullptr;
   2069        if (aValue && !aValue->IsEmptyString()) {
   2070          formIdElement = AddFormIdObserver();
   2071        }
   2072 
   2073        // Because we have a new @form value (or no more @form), we have to
   2074        // update our form owner.
   2075        UpdateFormOwner(false, formIdElement);
   2076      }
   2077    }
   2078  }
   2079 
   2080  return nsGenericHTMLElement::AfterSetAttr(
   2081      aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
   2082 }
   2083 
   2084 void nsGenericHTMLFormElement::ForgetFieldSet(nsIContent* aFieldset) {
   2085  MOZ_DIAGNOSTIC_ASSERT(IsFormAssociatedElement());
   2086  if (GetFieldSetInternal() == aFieldset) {
   2087    SetFieldSetInternal(nullptr);
   2088  }
   2089 }
   2090 
   2091 Element* nsGenericHTMLFormElement::AddFormIdObserver() {
   2092  MOZ_ASSERT(IsFormAssociatedElement());
   2093 
   2094  nsAutoString formId;
   2095  DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
   2096  GetAttr(nsGkAtoms::form, formId);
   2097  NS_ASSERTION(!formId.IsEmpty(),
   2098               "@form value should not be the empty string!");
   2099  RefPtr<nsAtom> atom = NS_Atomize(formId);
   2100 
   2101  return docOrShadow->AddIDTargetObserver(atom, FormIdUpdated, this, false);
   2102 }
   2103 
   2104 void nsGenericHTMLFormElement::RemoveFormIdObserver() {
   2105  MOZ_ASSERT(IsFormAssociatedElement());
   2106 
   2107  DocumentOrShadowRoot* docOrShadow = GetUncomposedDocOrConnectedShadowRoot();
   2108  if (!docOrShadow) {
   2109    return;
   2110  }
   2111 
   2112  nsAutoString formId;
   2113  GetAttr(nsGkAtoms::form, formId);
   2114  NS_ASSERTION(!formId.IsEmpty(),
   2115               "@form value should not be the empty string!");
   2116  RefPtr<nsAtom> atom = NS_Atomize(formId);
   2117 
   2118  docOrShadow->RemoveIDTargetObserver(atom, FormIdUpdated, this, false);
   2119 }
   2120 
   2121 /* static */
   2122 bool nsGenericHTMLFormElement::FormIdUpdated(Element* aOldElement,
   2123                                             Element* aNewElement,
   2124                                             void* aData) {
   2125  nsGenericHTMLFormElement* element =
   2126      static_cast<nsGenericHTMLFormElement*>(aData);
   2127 
   2128  NS_ASSERTION(element->IsHTMLElement(), "aData should be an HTML element");
   2129 
   2130  element->UpdateFormOwner(false, aNewElement);
   2131 
   2132  return true;
   2133 }
   2134 
   2135 bool nsGenericHTMLFormElement::IsElementDisabledForEvents(WidgetEvent* aEvent,
   2136                                                          nsIFrame* aFrame) {
   2137  MOZ_ASSERT(aEvent);
   2138 
   2139  // Allow dispatch of CustomEvent and untrusted Events.
   2140  if (!aEvent->IsTrusted()) {
   2141    return false;
   2142  }
   2143 
   2144  switch (aEvent->mMessage) {
   2145    case eAnimationStart:
   2146    case eAnimationEnd:
   2147    case eAnimationIteration:
   2148    case eAnimationCancel:
   2149    case eFormChange:
   2150    case eMouseMove:
   2151    case eMouseOver:
   2152    case eMouseOut:
   2153    case eMouseEnter:
   2154    case eMouseLeave:
   2155    case ePointerMove:
   2156    case ePointerOver:
   2157    case ePointerOut:
   2158    case ePointerEnter:
   2159    case ePointerLeave:
   2160    case eTransitionCancel:
   2161    case eTransitionEnd:
   2162    case eTransitionRun:
   2163    case eTransitionStart:
   2164    case eWheel:
   2165    case eLegacyMouseLineOrPageScroll:
   2166    case eLegacyMousePixelScroll:
   2167      return false;
   2168    case eFocus:
   2169    case eBlur:
   2170    case eFocusIn:
   2171    case eFocusOut:
   2172    case eKeyPress:
   2173    case eKeyUp:
   2174    case eKeyDown:
   2175      if (StaticPrefs::dom_forms_always_allow_key_and_focus_events_enabled()) {
   2176        return false;
   2177      }
   2178      [[fallthrough]];
   2179    case ePointerDown:
   2180    case ePointerUp:
   2181    case ePointerCancel:
   2182    case ePointerGotCapture:
   2183    case ePointerLostCapture:
   2184      if (StaticPrefs::dom_forms_always_allow_pointer_events_enabled()) {
   2185        return false;
   2186      }
   2187      [[fallthrough]];
   2188    default:
   2189      break;
   2190  }
   2191 
   2192  if (aEvent->mSpecifiedEventType == nsGkAtoms::oninput) {
   2193    return false;
   2194  }
   2195 
   2196  return IsDisabled();
   2197 }
   2198 
   2199 void nsGenericHTMLFormElement::UpdateFormOwner(bool aBindToTree,
   2200                                               Element* aFormIdElement) {
   2201  MOZ_ASSERT(IsFormAssociatedElement());
   2202  MOZ_ASSERT(!aBindToTree || !aFormIdElement,
   2203             "aFormIdElement shouldn't be set if aBindToTree is true!");
   2204 
   2205  HTMLFormElement* form = GetFormInternal();
   2206  if (!aBindToTree) {
   2207    ClearForm(true, false);
   2208    form = nullptr;
   2209  }
   2210 
   2211  HTMLFormElement* oldForm = form;
   2212  if (!form) {
   2213    // If @form is set, we have to use that to find the form.
   2214    nsAutoString formId;
   2215    if (GetAttr(nsGkAtoms::form, formId)) {
   2216      if (!formId.IsEmpty()) {
   2217        Element* element = nullptr;
   2218 
   2219        if (aBindToTree) {
   2220          element = AddFormIdObserver();
   2221        } else {
   2222          element = aFormIdElement;
   2223        }
   2224 
   2225        NS_ASSERTION(!IsInComposedDoc() ||
   2226                         element == GetUncomposedDocOrConnectedShadowRoot()
   2227                                        ->GetElementById(formId),
   2228                     "element should be equals to the current element "
   2229                     "associated with the id in @form!");
   2230 
   2231        if (element && element->IsHTMLElement(nsGkAtoms::form) &&
   2232            nsContentUtils::IsInSameAnonymousTree(this, element)) {
   2233          form = static_cast<HTMLFormElement*>(element);
   2234          SetFormInternal(form, aBindToTree);
   2235        }
   2236      }
   2237    } else {
   2238      // We now have a parent, so we may have picked up an ancestor form. Search
   2239      // for it.  Note that if form is already set we don't want to do this,
   2240      // because that means someone (probably the content sink) has already set
   2241      // it to the right value.  Also note that even if being bound here didn't
   2242      // change our parent, we still need to search, since our parent chain
   2243      // probably changed _somewhere_.
   2244      form = FindAncestorForm();
   2245      SetFormInternal(form, aBindToTree);
   2246    }
   2247  }
   2248 
   2249  if (form && !HasFlag(ADDED_TO_FORM)) {
   2250    // Now we need to add ourselves to the form
   2251    nsAutoString nameVal, idVal;
   2252    GetAttr(nsGkAtoms::name, nameVal);
   2253    GetAttr(nsGkAtoms::id, idVal);
   2254 
   2255    SetFlags(ADDED_TO_FORM);
   2256 
   2257    // Notify only if we just found this form.
   2258    form->AddElement(this, true, oldForm == nullptr);
   2259 
   2260    if (!nameVal.IsEmpty()) {
   2261      form->AddElementToTable(this, nameVal);
   2262    }
   2263 
   2264    if (!idVal.IsEmpty()) {
   2265      form->AddElementToTable(this, idVal);
   2266    }
   2267  }
   2268 }
   2269 
   2270 void nsGenericHTMLFormElement::UpdateFieldSet(bool aNotify) {
   2271  if (IsInNativeAnonymousSubtree() || !IsFormAssociatedElement()) {
   2272    MOZ_ASSERT_IF(IsFormAssociatedElement(), !GetFieldSetInternal());
   2273    return;
   2274  }
   2275 
   2276  nsIContent* parent = nullptr;
   2277  nsIContent* prev = nullptr;
   2278  HTMLFieldSetElement* fieldset = GetFieldSetInternal();
   2279 
   2280  for (parent = GetParent(); parent;
   2281       prev = parent, parent = parent->GetParent()) {
   2282    HTMLFieldSetElement* parentFieldset = HTMLFieldSetElement::FromNode(parent);
   2283    if (parentFieldset && (!prev || parentFieldset->GetFirstLegend() != prev)) {
   2284      if (fieldset == parentFieldset) {
   2285        // We already have the right fieldset;
   2286        return;
   2287      }
   2288 
   2289      if (fieldset) {
   2290        fieldset->RemoveElement(this);
   2291      }
   2292      SetFieldSetInternal(parentFieldset);
   2293      parentFieldset->AddElement(this);
   2294 
   2295      // The disabled state may have changed
   2296      FieldSetDisabledChanged(aNotify);
   2297      return;
   2298    }
   2299  }
   2300 
   2301  // No fieldset found.
   2302  if (fieldset) {
   2303    fieldset->RemoveElement(this);
   2304    SetFieldSetInternal(nullptr);
   2305    // The disabled state may have changed
   2306    FieldSetDisabledChanged(aNotify);
   2307  }
   2308 }
   2309 
   2310 void nsGenericHTMLFormElement::UpdateDisabledState(bool aNotify) {
   2311  if (!CanBeDisabled()) {
   2312    return;
   2313  }
   2314 
   2315  HTMLFieldSetElement* fieldset = GetFieldSetInternal();
   2316  const bool isDisabled =
   2317      HasAttr(nsGkAtoms::disabled) || (fieldset && fieldset->IsDisabled());
   2318 
   2319  const ElementState disabledStates =
   2320      isDisabled ? ElementState::DISABLED : ElementState::ENABLED;
   2321 
   2322  ElementState oldDisabledStates = State() & ElementState::DISABLED_STATES;
   2323  ElementState changedStates = disabledStates ^ oldDisabledStates;
   2324 
   2325  if (!changedStates.IsEmpty()) {
   2326    ToggleStates(changedStates, aNotify);
   2327    if (DoesReadWriteApply()) {
   2328      // :disabled influences :read-only / :read-write.
   2329      UpdateReadOnlyState(aNotify);
   2330    }
   2331  }
   2332 }
   2333 
   2334 bool nsGenericHTMLFormElement::IsReadOnlyInternal() const {
   2335  if (DoesReadWriteApply()) {
   2336    return IsDisabled() || GetBoolAttr(nsGkAtoms::readonly);
   2337  }
   2338  return nsGenericHTMLElement::IsReadOnlyInternal();
   2339 }
   2340 
   2341 void nsGenericHTMLFormElement::FieldSetDisabledChanged(bool aNotify) {
   2342  UpdateDisabledState(aNotify);
   2343 }
   2344 
   2345 void nsGenericHTMLFormElement::SaveSubtreeState() {
   2346  SaveState();
   2347 
   2348  nsGenericHTMLElement::SaveSubtreeState();
   2349 }
   2350 
   2351 //----------------------------------------------------------------------
   2352 
   2353 void nsGenericHTMLElement::Click(CallerType aCallerType) {
   2354  if (HandlingClick()) {
   2355    return;
   2356  }
   2357 
   2358  // There are two notions of disabled.
   2359  // "disabled":
   2360  // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-disabled
   2361  // "actually disabled":
   2362  // https://html.spec.whatwg.org/multipage/semantics-other.html#concept-element-disabled
   2363  // click() reads the former but IsDisabled() is for the latter. <fieldset> is
   2364  // included only in the latter, so we exclude it here.
   2365  // XXX(krosylight): What about <optgroup>? And should we add a separate method
   2366  // for this?
   2367  if (IsDisabled() &&
   2368      !(mNodeInfo->Equals(nsGkAtoms::fieldset) &&
   2369        StaticPrefs::dom_forms_fieldset_disable_only_descendants_enabled())) {
   2370    return;
   2371  }
   2372 
   2373  // Strong in case the event kills it
   2374  nsCOMPtr<Document> doc = GetComposedDoc();
   2375 
   2376  RefPtr<nsPresContext> context;
   2377  if (doc) {
   2378    PresShell* presShell = doc->GetPresShell();
   2379    if (!presShell) {
   2380      // We need the nsPresContext for dispatching the click event. In some
   2381      // rare cases we need to flush notifications to force creation of the
   2382      // nsPresContext here (for example when a script calls button.click()
   2383      // from script early during page load). We only flush the notifications
   2384      // if the PresShell hasn't been created yet, to limit the performance
   2385      // impact.
   2386      doc->FlushPendingNotifications(FlushType::EnsurePresShellInitAndFrames);
   2387      presShell = doc->GetPresShell();
   2388    }
   2389    if (presShell) {
   2390      context = presShell->GetPresContext();
   2391    }
   2392  }
   2393 
   2394  SetHandlingClick();
   2395 
   2396  // Mark this event trusted if Click() is called from system code.
   2397  WidgetPointerEvent event(aCallerType == CallerType::System, ePointerClick,
   2398                           nullptr);
   2399  event.mFlags.mIsPositionless = true;
   2400  event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
   2401  // pointerId definition in Pointer Events:
   2402  // > The pointerId value of -1 MUST be reserved and used to indicate events
   2403  // > that were generated by something other than a pointing device.
   2404  event.pointerId = -1;
   2405 
   2406  EventDispatcher::Dispatch(this, context, &event);
   2407 
   2408  ClearHandlingClick();
   2409 }
   2410 
   2411 bool nsGenericHTMLElement::IsHTMLFocusable(IsFocusableFlags aFlags,
   2412                                           bool* aIsFocusable,
   2413                                           int32_t* aTabIndex) {
   2414  MOZ_ASSERT(aIsFocusable);
   2415  MOZ_ASSERT(aTabIndex);
   2416  if (ShadowRoot* root = GetShadowRoot()) {
   2417    if (root->DelegatesFocus()) {
   2418      *aIsFocusable = false;
   2419      return true;
   2420    }
   2421  }
   2422 
   2423  if (!IsInComposedDoc() || IsInDesignMode()) {
   2424    // In designMode documents we only allow focusing the document.
   2425    *aTabIndex = -1;
   2426    *aIsFocusable = false;
   2427    return true;
   2428  }
   2429 
   2430  *aTabIndex = TabIndex();
   2431  bool disabled = false;
   2432  bool disallowOverridingFocusability = true;
   2433  Maybe<int32_t> attrVal = GetTabIndexAttrValue();
   2434  if (IsEditingHost()) {
   2435    // Editable roots should always be focusable.
   2436    disallowOverridingFocusability = true;
   2437 
   2438    // Ignore the disabled attribute in editable contentEditable/designMode
   2439    // roots.
   2440    if (attrVal.isNothing()) {
   2441      // The default value for tabindex should be 0 for editable
   2442      // contentEditable roots.
   2443      *aTabIndex = 0;
   2444    }
   2445  } else {
   2446    disallowOverridingFocusability = false;
   2447 
   2448    // Just check for disabled attribute on form controls
   2449    disabled = IsDisabled();
   2450    if (disabled) {
   2451      *aTabIndex = -1;
   2452    }
   2453  }
   2454 
   2455  // If a tabindex is specified at all, or the default tabindex is 0, we're
   2456  // focusable.
   2457  *aIsFocusable = (*aTabIndex >= 0 || (!disabled && attrVal.isSome()));
   2458  return disallowOverridingFocusability;
   2459 }
   2460 
   2461 Result<bool, nsresult> nsGenericHTMLElement::PerformAccesskey(
   2462    bool aKeyCausesActivation, bool aIsTrustedEvent) {
   2463  RefPtr<nsPresContext> presContext = GetPresContext(eForComposedDoc);
   2464  if (!presContext) {
   2465    return Err(NS_ERROR_UNEXPECTED);
   2466  }
   2467 
   2468  // It's hard to say what HTML4 wants us to do in all cases.
   2469  bool focused = true;
   2470  if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
   2471    fm->SetFocus(this, nsIFocusManager::FLAG_BYKEY);
   2472 
   2473    // Return true if the element became the current focus within its window.
   2474    nsPIDOMWindowOuter* window = OwnerDoc()->GetWindow();
   2475    focused = window && window->GetFocusedElement() == this;
   2476  }
   2477 
   2478  if (aKeyCausesActivation) {
   2479    // Click on it if the users prefs indicate to do so.
   2480    AutoHandlingUserInputStatePusher userInputStatePusher(aIsTrustedEvent);
   2481    AutoPopupStatePusher popupStatePusher(
   2482        aIsTrustedEvent ? PopupBlocker::openAllowed : PopupBlocker::openAbused);
   2483    DispatchSimulatedClick(this, aIsTrustedEvent, presContext);
   2484    return focused;
   2485  }
   2486 
   2487  // If the accesskey won't cause the activation and the focus isn't changed,
   2488  // either. Return error so EventStateManager would try to find next element
   2489  // to handle the accesskey.
   2490  return focused ? Result<bool, nsresult>{focused} : Err(NS_ERROR_ABORT);
   2491 }
   2492 
   2493 void nsGenericHTMLElement::HandleKeyboardActivation(
   2494    EventChainPostVisitor& aVisitor) {
   2495  MOZ_ASSERT(aVisitor.mEvent->HasKeyEventMessage());
   2496  MOZ_ASSERT(aVisitor.mEvent->IsTrusted());
   2497 
   2498  // If focused element is different from this element, it may be editable.
   2499  // In that case, associated editor for the element should handle the keyboard
   2500  // instead.  Therefore, if this is not the focused element, we should not
   2501  // handle the event here.  Note that this element may be an editing host,
   2502  // i.e., focused and editable.  In the case, keyboard events should be
   2503  // handled by the focused element instead of associated editor because
   2504  // Chrome handles the case so.  For compatibility with Chrome, we follow them.
   2505  if (nsFocusManager::GetFocusedElementStatic() != this) {
   2506    return;
   2507  }
   2508 
   2509  const auto message = aVisitor.mEvent->mMessage;
   2510  const WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
   2511  if (nsEventStatus_eIgnore != aVisitor.mEventStatus) {
   2512    if (message == eKeyUp && keyEvent->mKeyCode == NS_VK_SPACE) {
   2513      // Unset the flag even if the event is default-prevented or something.
   2514      UnsetFlags(HTML_ELEMENT_ACTIVE_FOR_KEYBOARD);
   2515    }
   2516    return;
   2517  }
   2518 
   2519  bool shouldActivate = false;
   2520  switch (message) {
   2521    case eKeyDown:
   2522      if (keyEvent->ShouldWorkAsSpaceKey()) {
   2523        SetFlags(HTML_ELEMENT_ACTIVE_FOR_KEYBOARD);
   2524      }
   2525      return;
   2526    case eKeyPress:
   2527      shouldActivate = keyEvent->mKeyCode == NS_VK_RETURN;
   2528      if (keyEvent->ShouldWorkAsSpaceKey()) {
   2529        // Consume 'space' key to prevent scrolling the page down.
   2530        aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   2531      }
   2532      break;
   2533    case eKeyUp:
   2534      shouldActivate = keyEvent->ShouldWorkAsSpaceKey() &&
   2535                       HasFlag(HTML_ELEMENT_ACTIVE_FOR_KEYBOARD);
   2536      if (shouldActivate) {
   2537        UnsetFlags(HTML_ELEMENT_ACTIVE_FOR_KEYBOARD);
   2538      }
   2539      break;
   2540    default:
   2541      MOZ_ASSERT_UNREACHABLE("why didn't we bail out earlier?");
   2542      break;
   2543  }
   2544 
   2545  if (!shouldActivate) {
   2546    return;
   2547  }
   2548 
   2549  RefPtr<nsPresContext> presContext = aVisitor.mPresContext;
   2550  DispatchSimulatedClick(this, aVisitor.mEvent->IsTrusted(), presContext);
   2551  aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   2552 }
   2553 
   2554 nsresult nsGenericHTMLElement::DispatchSimulatedClick(
   2555    nsGenericHTMLElement* aElement, bool aIsTrusted,
   2556    nsPresContext* aPresContext) {
   2557  WidgetPointerEvent event(aIsTrusted, ePointerClick, nullptr);
   2558  event.mFlags.mIsPositionless = true;
   2559  event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
   2560  // pointerId definition in Pointer Events:
   2561  // > The pointerId value of -1 MUST be reserved and used to indicate events
   2562  // > that were generated by something other than a pointing device.
   2563  event.pointerId = -1;
   2564  return EventDispatcher::Dispatch(aElement, aPresContext, &event);
   2565 }
   2566 
   2567 already_AddRefed<EditorBase> nsGenericHTMLElement::GetAssociatedEditor() {
   2568  // If contenteditable is ever implemented, it might need to do something
   2569  // different here?
   2570 
   2571  RefPtr<TextEditor> textEditor = GetTextEditorInternal();
   2572  return textEditor.forget();
   2573 }
   2574 
   2575 // static
   2576 void nsGenericHTMLElement::SyncEditorsOnSubtree(nsIContent* content) {
   2577  /* Sync this node */
   2578  nsGenericHTMLElement* element = FromNode(content);
   2579  if (element) {
   2580    if (RefPtr<EditorBase> editorBase = element->GetAssociatedEditor()) {
   2581      editorBase->SyncRealTimeSpell();
   2582    }
   2583  }
   2584 
   2585  /* Sync all children */
   2586  for (nsIContent* child = content->GetFirstChild(); child;
   2587       child = child->GetNextSibling()) {
   2588    SyncEditorsOnSubtree(child);
   2589  }
   2590 }
   2591 
   2592 static void MakeContentDescendantsEditable(nsIContent* aContent) {
   2593  // If aContent is not an element, we just need to update its
   2594  // internal editable state and don't need to notify anyone about
   2595  // that.  For elements, we need to send a ElementStateChanged
   2596  // notification.
   2597  if (!aContent->IsElement()) {
   2598    aContent->UpdateEditableState(false);
   2599    return;
   2600  }
   2601 
   2602  Element* element = aContent->AsElement();
   2603 
   2604  element->UpdateEditableState(true);
   2605 
   2606  for (nsIContent* child = aContent->GetFirstChild(); child;
   2607       child = child->GetNextSibling()) {
   2608    if (!child->IsElement() ||
   2609        !child->AsElement()->HasAttr(nsGkAtoms::contenteditable)) {
   2610      MakeContentDescendantsEditable(child);
   2611    }
   2612  }
   2613 }
   2614 
   2615 void nsGenericHTMLElement::ChangeEditableState(int32_t aChange) {
   2616  Document* document = GetComposedDoc();
   2617  if (!document) {
   2618    return;
   2619  }
   2620 
   2621  Document::EditingState previousEditingState = Document::EditingState::eOff;
   2622  if (aChange != 0) {
   2623    document->ChangeContentEditableCount(this, aChange);
   2624    previousEditingState = document->GetEditingState();
   2625  }
   2626 
   2627  // MakeContentDescendantsEditable is going to call ElementStateChanged for
   2628  // this element and all descendants if editable state has changed.
   2629  // We might as well wrap it all in one script blocker.
   2630  nsAutoScriptBlocker scriptBlocker;
   2631  MakeContentDescendantsEditable(this);
   2632 
   2633  // If the document already had contenteditable and JS adds new
   2634  // contenteditable, that might cause changing editing host to current editing
   2635  // host's ancestor.  In such case, HTMLEditor needs to know that
   2636  // synchronously to update selection limitter.
   2637  // Additionally, elements in shadow DOM is not editable in the normal cases,
   2638  // but if its content has `contenteditable`, only in it can be ediable.
   2639  // So we don't need to notify HTMLEditor of this change only when we're not
   2640  // in shadow DOM and the composed document is in design mode.
   2641  if (IsInDesignMode() && !IsInShadowTree() && aChange > 0 &&
   2642      previousEditingState == Document::EditingState::eContentEditable) {
   2643    if (const RefPtr<HTMLEditor> htmlEditor =
   2644            nsContentUtils::GetHTMLEditor(document->GetPresContext())) {
   2645      htmlEditor->NotifyEditingHostMaybeChanged();
   2646    }
   2647  }
   2648 }
   2649 
   2650 //----------------------------------------------------------------------
   2651 
   2652 nsGenericHTMLFormControlElement::nsGenericHTMLFormControlElement(
   2653    already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo, FormControlType aType)
   2654    : nsGenericHTMLFormElement(std::move(aNodeInfo)),
   2655      nsIFormControl(aType),
   2656      mForm(nullptr),
   2657      mFieldSet(nullptr) {}
   2658 
   2659 nsGenericHTMLFormControlElement::~nsGenericHTMLFormControlElement() {
   2660  if (mFieldSet) {
   2661    mFieldSet->RemoveElement(this);
   2662  }
   2663 
   2664  // Check that this element doesn't know anything about its form at this point.
   2665  NS_ASSERTION(!mForm, "mForm should be null at this point!");
   2666 }
   2667 
   2668 NS_IMPL_ISUPPORTS_INHERITED(nsGenericHTMLFormControlElement,
   2669                            nsGenericHTMLFormElement, nsIFormControl)
   2670 
   2671 nsINode* nsGenericHTMLFormControlElement::GetScopeChainParent() const {
   2672  return mForm ? mForm : nsGenericHTMLElement::GetScopeChainParent();
   2673 }
   2674 
   2675 nsIContent::IMEState nsGenericHTMLFormControlElement::GetDesiredIMEState() {
   2676  TextEditor* textEditor = GetTextEditorInternal();
   2677  if (!textEditor) {
   2678    return nsGenericHTMLFormElement::GetDesiredIMEState();
   2679  }
   2680  Result<IMEState, nsresult> stateOrError = textEditor->GetPreferredIMEState();
   2681  if (MOZ_UNLIKELY(stateOrError.isErr())) {
   2682    return nsGenericHTMLFormElement::GetDesiredIMEState();
   2683  }
   2684  return stateOrError.unwrap();
   2685 }
   2686 
   2687 void nsGenericHTMLFormControlElement::GetAutocapitalize(
   2688    nsAString& aValue) const {
   2689  if (nsContentUtils::HasNonEmptyAttr(this, kNameSpaceID_None,
   2690                                      nsGkAtoms::autocapitalize)) {
   2691    nsGenericHTMLFormElement::GetAutocapitalize(aValue);
   2692    return;
   2693  }
   2694 
   2695  if (mForm && IsAutocapitalizeOrAutocorrectInheriting()) {
   2696    mForm->GetAutocapitalize(aValue);
   2697  }
   2698 }
   2699 
   2700 // https://html.spec.whatwg.org/#dom-autocorrect
   2701 bool nsGenericHTMLFormControlElement::Autocorrect() const {
   2702  auto controlType = ControlType();
   2703 
   2704  switch (controlType) {
   2705    case FormControlType::InputEmail:
   2706    case FormControlType::InputPassword:
   2707    case FormControlType::InputUrl:
   2708      return false;
   2709    default:
   2710      break;
   2711  }
   2712 
   2713  if (HasAttr(kNameSpaceID_None, nsGkAtoms::autocorrect)) {
   2714    return nsGenericHTMLElement::Autocorrect();
   2715  }
   2716 
   2717  if (mForm && IsAutocapitalizeOrAutocorrectInheriting()) {
   2718    return mForm->Autocorrect();
   2719  }
   2720 
   2721  return true;
   2722 }
   2723 
   2724 bool nsGenericHTMLFormControlElement::IsHTMLFocusable(IsFocusableFlags aFlags,
   2725                                                      bool* aIsFocusable,
   2726                                                      int32_t* aTabIndex) {
   2727  if (nsGenericHTMLFormElement::IsHTMLFocusable(aFlags, aIsFocusable,
   2728                                                aTabIndex)) {
   2729    return true;
   2730  }
   2731 
   2732  *aIsFocusable = *aIsFocusable && IsFormControlDefaultFocusable(aFlags);
   2733  return false;
   2734 }
   2735 
   2736 HTMLFieldSetElement* nsGenericHTMLFormControlElement::GetFieldSet() {
   2737  return GetFieldSetInternal();
   2738 }
   2739 
   2740 void nsGenericHTMLFormControlElement::SetForm(HTMLFormElement* aForm) {
   2741  MOZ_ASSERT(aForm, "Don't pass null here");
   2742  NS_ASSERTION(!mForm,
   2743               "We don't support switching from one non-null form to another.");
   2744 
   2745  SetFormInternal(aForm, false);
   2746 }
   2747 
   2748 void nsGenericHTMLFormControlElement::ClearForm(bool aRemoveFromForm,
   2749                                                bool aUnbindOrDelete) {
   2750  nsGenericHTMLFormElement::ClearForm(aRemoveFromForm, aUnbindOrDelete);
   2751 }
   2752 
   2753 bool nsGenericHTMLFormControlElement::IsLabelable() const {
   2754  auto type = ControlType();
   2755  return (IsInputElement(type) && type != FormControlType::InputHidden) ||
   2756         IsButtonElement(type) || type == FormControlType::Output ||
   2757         type == FormControlType::Select || type == FormControlType::Textarea;
   2758 }
   2759 
   2760 bool nsGenericHTMLFormControlElement::CanBeDisabled() const {
   2761  auto type = ControlType();
   2762  // It's easier to test the types that _cannot_ be disabled
   2763  return type != FormControlType::Object && type != FormControlType::Output;
   2764 }
   2765 
   2766 bool nsGenericHTMLFormControlElement::DoesReadWriteApply() const {
   2767  auto type = ControlType();
   2768  if (!IsInputElement(type) && type != FormControlType::Textarea) {
   2769    return false;
   2770  }
   2771 
   2772  switch (type) {
   2773    case FormControlType::InputHidden:
   2774    case FormControlType::InputButton:
   2775    case FormControlType::InputImage:
   2776    case FormControlType::InputReset:
   2777    case FormControlType::InputSubmit:
   2778    case FormControlType::InputRadio:
   2779    case FormControlType::InputFile:
   2780    case FormControlType::InputCheckbox:
   2781    case FormControlType::InputRange:
   2782    case FormControlType::InputColor:
   2783      return false;
   2784 #ifdef DEBUG
   2785    case FormControlType::Textarea:
   2786    case FormControlType::InputText:
   2787    case FormControlType::InputPassword:
   2788    case FormControlType::InputSearch:
   2789    case FormControlType::InputTel:
   2790    case FormControlType::InputEmail:
   2791    case FormControlType::InputUrl:
   2792    case FormControlType::InputNumber:
   2793    case FormControlType::InputDate:
   2794    case FormControlType::InputTime:
   2795    case FormControlType::InputMonth:
   2796    case FormControlType::InputWeek:
   2797    case FormControlType::InputDatetimeLocal:
   2798      return true;
   2799    default:
   2800      MOZ_ASSERT_UNREACHABLE("Unexpected input type in DoesReadWriteApply()");
   2801      return true;
   2802 #else   // DEBUG
   2803    default:
   2804      return true;
   2805 #endif  // DEBUG
   2806  }
   2807 }
   2808 
   2809 void nsGenericHTMLFormControlElement::SetFormInternal(HTMLFormElement* aForm,
   2810                                                      bool aBindToTree) {
   2811  if (aForm) {
   2812    BeforeSetForm(aForm, aBindToTree);
   2813  }
   2814 
   2815  // keep a *weak* ref to the form here
   2816  mForm = aForm;
   2817 }
   2818 
   2819 HTMLFormElement* nsGenericHTMLFormControlElement::GetFormInternal() const {
   2820  return mForm;
   2821 }
   2822 
   2823 HTMLFieldSetElement* nsGenericHTMLFormControlElement::GetFieldSetInternal()
   2824    const {
   2825  return mFieldSet;
   2826 }
   2827 
   2828 void nsGenericHTMLFormControlElement::SetFieldSetInternal(
   2829    HTMLFieldSetElement* aFieldset) {
   2830  mFieldSet = aFieldset;
   2831 }
   2832 
   2833 void nsGenericHTMLFormControlElement::UpdateRequiredState(bool aIsRequired,
   2834                                                          bool aNotify) {
   2835 #ifdef DEBUG
   2836  auto type = ControlType();
   2837 #endif
   2838  MOZ_ASSERT(IsInputElement(type) || type == FormControlType::Select ||
   2839                 type == FormControlType::Textarea,
   2840             "This should be called only on types that @required applies");
   2841 
   2842 #ifdef DEBUG
   2843  if (HTMLInputElement* input = HTMLInputElement::FromNode(this)) {
   2844    MOZ_ASSERT(
   2845        input->DoesRequiredApply(),
   2846        "This should be called only on input types that @required applies");
   2847  }
   2848 #endif
   2849 
   2850  ElementState requiredStates;
   2851  if (aIsRequired) {
   2852    requiredStates |= ElementState::REQUIRED;
   2853  } else {
   2854    requiredStates |= ElementState::OPTIONAL_;
   2855  }
   2856 
   2857  ElementState oldRequiredStates = State() & ElementState::REQUIRED_STATES;
   2858  ElementState changedStates = requiredStates ^ oldRequiredStates;
   2859 
   2860  if (!changedStates.IsEmpty()) {
   2861    ToggleStates(changedStates, aNotify);
   2862  }
   2863 }
   2864 
   2865 bool nsGenericHTMLFormControlElement::IsAutocapitalizeOrAutocorrectInheriting()
   2866    const {
   2867  auto type = ControlType();
   2868  return IsInputElement(type) || IsButtonElement(type) ||
   2869         type == FormControlType::Fieldset || type == FormControlType::Output ||
   2870         type == FormControlType::Select || type == FormControlType::Textarea;
   2871 }
   2872 
   2873 nsresult nsGenericHTMLFormControlElement::SubmitDirnameDir(
   2874    FormData* aFormData) {
   2875  // Submit dirname=dir if element has non-empty dirname attribute
   2876  if (HasAttr(nsGkAtoms::dirname)) {
   2877    nsAutoString dirname;
   2878    GetAttr(nsGkAtoms::dirname, dirname);
   2879    if (!dirname.IsEmpty()) {
   2880      const Directionality dir = GetDirectionality();
   2881      MOZ_ASSERT(dir == Directionality::Ltr || dir == Directionality::Rtl,
   2882                 "The directionality of an element is either ltr or rtl");
   2883      return aFormData->AddNameValuePair(
   2884          dirname, dir == Directionality::Ltr ? u"ltr"_ns : u"rtl"_ns);
   2885    }
   2886  }
   2887  return NS_OK;
   2888 }
   2889 
   2890 void nsGenericHTMLFormControlElement::GetFormAutofillState(
   2891    nsAString& aState) const {
   2892  if (State().HasState(ElementState::AUTOFILL_PREVIEW)) {
   2893    aState.AssignLiteral("preview");
   2894  } else if (State().HasState(ElementState::AUTOFILL)) {
   2895    aState.AssignLiteral("autofill");
   2896  } else {
   2897    aState.Truncate();
   2898  }
   2899 }
   2900 
   2901 void nsGenericHTMLFormControlElement::SetFormAutofillState(
   2902    const nsAString& aState) {
   2903  if (aState.EqualsLiteral("autofill")) {
   2904    RemoveStates(ElementState::AUTOFILL_PREVIEW);
   2905    AddStates(ElementState::AUTOFILL);
   2906  } else if (aState.EqualsLiteral("preview")) {
   2907    AddStates(ElementState::AUTOFILL | ElementState::AUTOFILL_PREVIEW);
   2908  } else {
   2909    RemoveStates(ElementState::AUTOFILL | ElementState::AUTOFILL_PREVIEW);
   2910  }
   2911 }
   2912 
   2913 //----------------------------------------------------------------------
   2914 
   2915 static constexpr nsAttrValue::EnumTableEntry kPopoverTargetActionTable[] = {
   2916    {"toggle", PopoverTargetAction::Toggle},
   2917    {"show", PopoverTargetAction::Show},
   2918    {"hide", PopoverTargetAction::Hide},
   2919 };
   2920 
   2921 static constexpr const nsAttrValue::EnumTableEntry*
   2922    kPopoverTargetActionDefault = &kPopoverTargetActionTable[0];
   2923 
   2924 nsGenericHTMLFormControlElementWithState::
   2925    nsGenericHTMLFormControlElementWithState(
   2926        already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
   2927        FromParser aFromParser, FormControlType aType)
   2928    : nsGenericHTMLFormControlElement(std::move(aNodeInfo), aType),
   2929      mControlNumber(!!(aFromParser & FROM_PARSER_NETWORK)
   2930                         ? OwnerDoc()->GetNextControlNumber()
   2931                         : -1) {
   2932  mStateKey.SetIsVoid(true);
   2933 }
   2934 
   2935 bool nsGenericHTMLFormControlElementWithState::ParseAttribute(
   2936    int32_t aNamespaceID, nsAtom* aAttribute, const nsAString& aValue,
   2937    nsIPrincipal* aMaybeScriptedPrincipal, nsAttrValue& aResult) {
   2938  if (aNamespaceID == kNameSpaceID_None) {
   2939    if (aAttribute == nsGkAtoms::popovertargetaction) {
   2940      return aResult.ParseEnumValue(aValue, kPopoverTargetActionTable, false,
   2941                                    kPopoverTargetActionDefault);
   2942    }
   2943    if (aAttribute == nsGkAtoms::popovertarget) {
   2944      aResult.ParseAtom(aValue);
   2945      return true;
   2946    }
   2947  }
   2948 
   2949  return nsGenericHTMLFormControlElement::ParseAttribute(
   2950      aNamespaceID, aAttribute, aValue, aMaybeScriptedPrincipal, aResult);
   2951 }
   2952 
   2953 mozilla::dom::Element*
   2954 nsGenericHTMLFormControlElementWithState::GetPopoverTargetElement() const {
   2955  return GetAttrAssociatedElement(nsGkAtoms::popovertarget);
   2956 }
   2957 
   2958 void nsGenericHTMLFormControlElementWithState::SetPopoverTargetElement(
   2959    mozilla::dom::Element* aElement) {
   2960  ExplicitlySetAttrElement(nsGkAtoms::popovertarget, aElement);
   2961 }
   2962 
   2963 // https://html.spec.whatwg.org/multipage/#popover-target-attribute-activation-behavior
   2964 void nsGenericHTMLFormControlElementWithState::HandlePopoverTargetAction(
   2965    mozilla::dom::Element* aEventTarget) {
   2966  // 1. Let popover be node's popover target element.
   2967  RefPtr<nsGenericHTMLElement> popover = GetEffectivePopoverTargetElement();
   2968 
   2969  // 2. If popover is null, then return.
   2970  if (!popover) {
   2971    return;
   2972  }
   2973 
   2974  // 3. If eventTarget is a shadow-including inclusive descendant of popover and
   2975  // popover is a shadow-including descendant of node, then return.
   2976  if (aEventTarget &&
   2977      aEventTarget->IsShadowIncludingInclusiveDescendantOf(popover) &&
   2978      popover->IsShadowIncludingDescendantOf(this)) {
   2979    return;
   2980  }
   2981 
   2982  // 4. If node's popovertargetaction attribute is in the show state and
   2983  // popover's popover visibility state is showing, then return.
   2984  // 5. If node's popovertargetaction attribute is in the hide state and
   2985  // popover's popover visibility state is hidden, then return.
   2986  // 6. If popover's popover visibility state is showing, then run the hide
   2987  // popover algorithm given popover, true, true, false, and node.
   2988  // 7. Otherwise, if popover's popover visibility state is hidden and the
   2989  // result of running check popover validity given popover, false, false, and
   2990  // null is true, then run show popover given popover, false, and node.
   2991  auto action = PopoverTargetAction::Toggle;
   2992  if (const nsAttrValue* value =
   2993          GetParsedAttr(nsGkAtoms::popovertargetaction)) {
   2994    MOZ_ASSERT(value->Type() == nsAttrValue::eEnum);
   2995    action = static_cast<PopoverTargetAction>(value->GetEnumValue());
   2996  }
   2997 
   2998  bool canHide = action == PopoverTargetAction::Hide ||
   2999                 action == PopoverTargetAction::Toggle;
   3000  bool shouldHide = canHide && popover->IsPopoverOpen();
   3001  bool canShow = action == PopoverTargetAction::Show ||
   3002                 action == PopoverTargetAction::Toggle;
   3003  bool shouldShow = canShow && !popover->IsPopoverOpen();
   3004 
   3005  if (shouldHide) {
   3006    popover->HidePopoverInternal(true, true, this, IgnoreErrors());
   3007  } else if (shouldShow) {
   3008    popover->ShowPopoverInternal(this, IgnoreErrors());
   3009  }
   3010 }
   3011 
   3012 bool nsGenericHTMLElement::IsValidCommandAction(Command aCommand) const {
   3013  return Element::IsValidCommandAction(aCommand) ||
   3014         aCommand == Command::ShowPopover ||
   3015         aCommand == Command::TogglePopover || aCommand == Command::HidePopover;
   3016 }
   3017 
   3018 MOZ_CAN_RUN_SCRIPT bool nsGenericHTMLElement::HandleCommandInternal(
   3019    Element* aSource, Command aCommand, ErrorResult& aRv) {
   3020  if (Element::HandleCommandInternal(aSource, aCommand, aRv)) {
   3021    return true;
   3022  }
   3023 
   3024  // If the element is a `popover` then we may want to handle the
   3025  // command...
   3026  auto popoverState = GetPopoverAttributeState();
   3027  if (popoverState == PopoverAttributeState::None) {
   3028    return false;
   3029  }
   3030 
   3031  const bool canShow =
   3032      aCommand == Command::TogglePopover || aCommand == Command::ShowPopover;
   3033  const bool canHide =
   3034      aCommand == Command::TogglePopover || aCommand == Command::HidePopover;
   3035 
   3036  if (canShow && !IsPopoverOpen()) {
   3037    ShowPopoverInternal(aSource, aRv);
   3038    return true;
   3039  }
   3040 
   3041  if (canHide && IsPopoverOpen()) {
   3042    HidePopoverInternal(/* aFocusPreviousElement = */ true,
   3043                        /* aFireEvents = */ true, aSource, IgnoreErrors());
   3044    return true;
   3045  }
   3046 
   3047  return false;
   3048 }
   3049 
   3050 void nsGenericHTMLFormControlElementWithState::GenerateStateKey() {
   3051  // Keep the key if already computed
   3052  if (!mStateKey.IsVoid()) {
   3053    return;
   3054  }
   3055 
   3056  Document* doc = GetUncomposedDoc();
   3057  if (!doc) {
   3058    mStateKey.Truncate();
   3059    return;
   3060  }
   3061 
   3062  // Generate the state key
   3063  nsContentUtils::GenerateStateKey(this, doc, mStateKey);
   3064 
   3065  // If the state key is blank, this is anonymous content or for whatever
   3066  // reason we are not supposed to save/restore state: keep it as such.
   3067  if (!mStateKey.IsEmpty()) {
   3068    // Add something unique to content so layout doesn't muck us up.
   3069    mStateKey += "-C";
   3070  }
   3071 }
   3072 
   3073 PresState* nsGenericHTMLFormControlElementWithState::GetPrimaryPresState() {
   3074  if (mStateKey.IsEmpty()) {
   3075    return nullptr;
   3076  }
   3077 
   3078  nsCOMPtr<nsILayoutHistoryState> history = GetLayoutHistory(false);
   3079 
   3080  if (!history) {
   3081    return nullptr;
   3082  }
   3083 
   3084  // Get the pres state for this key, if it doesn't exist, create one.
   3085  PresState* result = history->GetState(mStateKey);
   3086  if (!result) {
   3087    UniquePtr<PresState> newState = NewPresState();
   3088    result = newState.get();
   3089    history->AddState(mStateKey, std::move(newState));
   3090  }
   3091 
   3092  return result;
   3093 }
   3094 
   3095 already_AddRefed<nsILayoutHistoryState>
   3096 nsGenericHTMLFormElement::GetLayoutHistory(bool aRead) {
   3097  nsCOMPtr<Document> doc = GetUncomposedDoc();
   3098  if (!doc) {
   3099    return nullptr;
   3100  }
   3101 
   3102  //
   3103  // Get the history
   3104  //
   3105  nsCOMPtr<nsILayoutHistoryState> history = doc->GetLayoutHistoryState();
   3106  if (!history) {
   3107    return nullptr;
   3108  }
   3109 
   3110  if (aRead && !history->HasStates()) {
   3111    return nullptr;
   3112  }
   3113 
   3114  return history.forget();
   3115 }
   3116 
   3117 bool nsGenericHTMLFormControlElementWithState::RestoreFormControlState() {
   3118  MOZ_ASSERT(!mStateKey.IsVoid(),
   3119             "GenerateStateKey must already have been called");
   3120 
   3121  if (mStateKey.IsEmpty()) {
   3122    return false;
   3123  }
   3124 
   3125  nsCOMPtr<nsILayoutHistoryState> history = GetLayoutHistory(true);
   3126  if (!history) {
   3127    return false;
   3128  }
   3129 
   3130  // Get the pres state for this key
   3131  PresState* state = history->GetState(mStateKey);
   3132  if (state) {
   3133    bool result = RestoreState(state);
   3134    history->RemoveState(mStateKey);
   3135    return result;
   3136  }
   3137 
   3138  return false;
   3139 }
   3140 
   3141 void nsGenericHTMLFormControlElementWithState::NodeInfoChanged(
   3142    Document* aOldDoc) {
   3143  nsGenericHTMLFormControlElement::NodeInfoChanged(aOldDoc);
   3144 
   3145  // We need to regenerate the state key now we're in a new document.  Clearing
   3146  // mControlNumber means we stop considering this control to be parser
   3147  // inserted, and we'll generate a state key based on its position in the
   3148  // document rather than the order it was inserted into the document.
   3149  mControlNumber = -1;
   3150  mStateKey.SetIsVoid(true);
   3151 }
   3152 
   3153 void nsGenericHTMLFormControlElementWithState::GetFormAction(nsString& aValue) {
   3154  auto type = ControlType();
   3155  if (!IsInputElement(type) && !IsButtonElement(type)) {
   3156    return;
   3157  }
   3158 
   3159  if (!GetAttr(nsGkAtoms::formaction, aValue) || aValue.IsEmpty()) {
   3160    Document* document = OwnerDoc();
   3161    nsIURI* docURI = document->GetDocumentURI();
   3162    if (docURI) {
   3163      nsAutoCString spec;
   3164      nsresult rv = docURI->GetSpec(spec);
   3165      if (NS_FAILED(rv)) {
   3166        return;
   3167      }
   3168 
   3169      CopyUTF8toUTF16(spec, aValue);
   3170    }
   3171  } else {
   3172    GetURIAttr(nsGkAtoms::formaction, nullptr, aValue);
   3173  }
   3174 }
   3175 
   3176 bool nsGenericHTMLElement::IsEventAttributeNameInternal(nsAtom* aName) {
   3177  return nsContentUtils::IsEventAttributeName(aName, EventNameType_HTML);
   3178 }
   3179 
   3180 /**
   3181 * Construct a URI from a string, as an element.src attribute
   3182 * would be set to. Helper for the media elements.
   3183 */
   3184 nsresult nsGenericHTMLElement::NewURIFromString(const nsAString& aURISpec,
   3185                                                nsIURI** aURI) {
   3186  NS_ENSURE_ARG_POINTER(aURI);
   3187 
   3188  *aURI = nullptr;
   3189 
   3190  nsCOMPtr<Document> doc = OwnerDoc();
   3191 
   3192  nsresult rv = nsContentUtils::NewURIWithDocumentCharset(aURI, aURISpec, doc,
   3193                                                          GetBaseURI());
   3194  NS_ENSURE_SUCCESS(rv, rv);
   3195 
   3196  bool equal;
   3197  if (aURISpec.IsEmpty() && doc->GetDocumentURI() &&
   3198      NS_SUCCEEDED(doc->GetDocumentURI()->Equals(*aURI, &equal)) && equal) {
   3199    // Assume an element can't point to a fragment of its embedding
   3200    // document. Fail here instead of returning the recursive URI
   3201    // and waiting for the subsequent load to fail.
   3202    NS_RELEASE(*aURI);
   3203    return NS_ERROR_DOM_INVALID_STATE_ERR;
   3204  }
   3205 
   3206  return NS_OK;
   3207 }
   3208 
   3209 void nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue,
   3210                                        mozilla::ErrorResult& aError) {
   3211  // innerText depends on layout. For example, white space processing is
   3212  // something that happens during reflow and which must be reflected by
   3213  // innerText.  So for:
   3214  //
   3215  //   <div style="white-space:normal"> A     B C </div>
   3216  //
   3217  // innerText should give "A B C".
   3218  //
   3219  // The approach taken here to avoid the expense of reflow is to flush style
   3220  // and then see whether it's necessary to flush layout afterwards. Flushing
   3221  // layout can be skipped if we can detect that the element or its descendants
   3222  // are not dirty.
   3223 
   3224  // Obtain the composed doc to handle elements in Shadow DOM.
   3225  Document* doc = GetComposedDoc();
   3226  if (doc) {
   3227    doc->FlushPendingNotifications(FlushType::Style);
   3228  }
   3229 
   3230  // Elements with `display: content` will not have a frame. To handle Shadow
   3231  // DOM, walk the flattened tree looking for parent frame.
   3232  nsIFrame* frame = GetPrimaryFrame();
   3233  if (IsDisplayContents()) {
   3234    for (Element* parent = GetFlattenedTreeParentElement(); parent;
   3235         parent = parent->GetFlattenedTreeParentElement()) {
   3236      frame = parent->GetPrimaryFrame();
   3237      if (frame) {
   3238        break;
   3239      }
   3240    }
   3241  }
   3242 
   3243  // Check for dirty reflow roots in the subtree from targetFrame; this requires
   3244  // a reflow flush.
   3245  bool dirty = frame && frame->PresShell()->FrameIsAncestorOfDirtyRoot(frame);
   3246 
   3247  // The way we do that is by checking whether the element has either of the two
   3248  // dirty bits (NS_FRAME_IS_DIRTY or NS_FRAME_HAS_DIRTY_DESCENDANTS) or if any
   3249  // ancestor has NS_FRAME_IS_DIRTY.  We need to check for NS_FRAME_IS_DIRTY on
   3250  // ancestors since that is something that implies NS_FRAME_IS_DIRTY on all
   3251  // descendants.
   3252  dirty |= frame && frame->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
   3253  while (!dirty && frame) {
   3254    dirty |= frame->HasAnyStateBits(NS_FRAME_IS_DIRTY);
   3255    frame = frame->GetInFlowParent();
   3256  }
   3257 
   3258  // Flush layout if we determined a reflow is required.
   3259  if (dirty && doc) {
   3260    doc->FlushPendingNotifications(FlushType::Layout);
   3261  }
   3262 
   3263  if (!IsRendered()) {
   3264    GetTextContentInternal(aValue, aError);
   3265  } else {
   3266    nsRange::GetInnerTextNoFlush(aValue, aError, this);
   3267  }
   3268 }
   3269 
   3270 static already_AddRefed<nsINode> TextToNode(const nsAString& aString,
   3271                                            nsNodeInfoManager* aNim) {
   3272  nsString str;
   3273  const char16_t* s = aString.BeginReading();
   3274  const char16_t* end = aString.EndReading();
   3275  RefPtr<DocumentFragment> fragment;
   3276  while (true) {
   3277    if (s != end && *s == '\r' && s + 1 != end && s[1] == '\n') {
   3278      // a \r\n pair should only generate one <br>, so just skip the \r
   3279      ++s;
   3280    }
   3281    if (s == end || *s == '\r' || *s == '\n') {
   3282      if (!str.IsEmpty()) {
   3283        RefPtr<nsTextNode> textContent = new (aNim) nsTextNode(aNim);
   3284        textContent->SetText(str, true);
   3285        if (!fragment) {
   3286          if (s == end) {
   3287            return textContent.forget();
   3288          }
   3289          fragment = new (aNim) DocumentFragment(aNim);
   3290        }
   3291        fragment->AppendChildTo(textContent, true, IgnoreErrors());
   3292      }
   3293      if (s == end) {
   3294        break;
   3295      }
   3296      str.Truncate();
   3297      RefPtr<NodeInfo> ni = aNim->GetNodeInfo(
   3298          nsGkAtoms::br, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
   3299      auto* nim = ni->NodeInfoManager();
   3300      RefPtr<HTMLBRElement> br = new (nim) HTMLBRElement(ni.forget());
   3301      if (!fragment) {
   3302        if (s + 1 == end) {
   3303          return br.forget();
   3304        }
   3305        fragment = new (aNim) DocumentFragment(aNim);
   3306      }
   3307      fragment->AppendChildTo(br, true, IgnoreErrors());
   3308    } else {
   3309      str.Append(*s);
   3310    }
   3311    ++s;
   3312  }
   3313  return fragment.forget();
   3314 }
   3315 
   3316 void nsGenericHTMLElement::SetInnerTextInternal(
   3317    const nsAString& aValue, MutationEffectOnScript aMutationEffectOnScript) {
   3318  RefPtr<nsINode> node = TextToNode(aValue, NodeInfo()->NodeInfoManager());
   3319  ReplaceChildren(node, IgnoreErrors(), aMutationEffectOnScript);
   3320 }
   3321 
   3322 // https://html.spec.whatwg.org/#merge-with-the-next-text-node
   3323 static void MergeWithNextTextNode(Text& aText, ErrorResult& aRv) {
   3324  RefPtr<Text> nextSibling = Text::FromNodeOrNull(aText.GetNextSibling());
   3325  if (!nextSibling) {
   3326    return;
   3327  }
   3328  nsAutoString data;
   3329  nextSibling->GetData(data);
   3330  aText.AppendDataInternal(data, MutationEffectOnScript::KeepTrustWorthiness,
   3331                           aRv);
   3332  nextSibling->Remove();
   3333 }
   3334 
   3335 // https://html.spec.whatwg.org/#dom-outertext
   3336 void nsGenericHTMLElement::SetOuterText(const nsAString& aValue,
   3337                                        ErrorResult& aRv) {
   3338  nsCOMPtr<nsINode> parent = GetParentNode();
   3339  if (!parent) {
   3340    return aRv.ThrowNoModificationAllowedError("Element has no parent");
   3341  }
   3342 
   3343  RefPtr<nsINode> next = GetNextSibling();
   3344  RefPtr<nsINode> previous = GetPreviousSibling();
   3345 
   3346  nsNodeInfoManager* nim = NodeInfo()->NodeInfoManager();
   3347  RefPtr<nsINode> node = TextToNode(aValue, nim);
   3348  if (!node) {
   3349    // This doesn't match the spec, see
   3350    // https://github.com/whatwg/html/issues/7508
   3351    node = new (nim) nsTextNode(nim);
   3352  }
   3353  parent->ReplaceChildInternal(
   3354      *node, *this, MutationEffectOnScript::DropTrustWorthiness, aRv);
   3355  if (aRv.Failed()) {
   3356    return;
   3357  }
   3358 
   3359  if (next) {
   3360    if (RefPtr<Text> text = Text::FromNodeOrNull(next->GetPreviousSibling())) {
   3361      MergeWithNextTextNode(*text, aRv);
   3362      if (aRv.Failed()) {
   3363        return;
   3364      }
   3365    }
   3366  }
   3367  if (auto* text = Text::FromNodeOrNull(previous)) {
   3368    MergeWithNextTextNode(*text, aRv);
   3369  }
   3370 }
   3371 
   3372 // This should be true when `:open` should match.
   3373 bool nsGenericHTMLElement::PopoverOpen() const {
   3374  if (PopoverData* popoverData = GetPopoverData()) {
   3375    return popoverData->GetPopoverVisibilityState() ==
   3376           PopoverVisibilityState::Showing;
   3377  }
   3378  return false;
   3379 }
   3380 
   3381 // https://html.spec.whatwg.org/#check-popover-validity
   3382 bool nsGenericHTMLElement::CheckPopoverValidity(
   3383    PopoverVisibilityState aExpectedState, Document* aExpectedDocument,
   3384    ErrorResult& aRv) {
   3385  if (GetPopoverAttributeState() == PopoverAttributeState::None) {
   3386    aRv.ThrowNotSupportedError("Element is in the no popover state");
   3387    return false;
   3388  }
   3389 
   3390  if (GetPopoverData()->GetPopoverVisibilityState() != aExpectedState) {
   3391    return false;
   3392  }
   3393 
   3394  if (!IsInComposedDoc()) {
   3395    aRv.ThrowInvalidStateError("Element is not connected");
   3396    return false;
   3397  }
   3398 
   3399  if (aExpectedDocument && aExpectedDocument != OwnerDoc()) {
   3400    aRv.ThrowInvalidStateError("Element is moved to other document");
   3401    return false;
   3402  }
   3403 
   3404  if (auto* dialog = HTMLDialogElement::FromNode(this)) {
   3405    if (dialog->IsInTopLayer()) {
   3406      aRv.ThrowInvalidStateError("Element is a modal <dialog> element");
   3407      return false;
   3408    }
   3409  }
   3410 
   3411  if (State().HasState(ElementState::FULLSCREEN)) {
   3412    aRv.ThrowInvalidStateError("Element is fullscreen");
   3413    return false;
   3414  }
   3415 
   3416  return true;
   3417 }
   3418 
   3419 PopoverAttributeState nsGenericHTMLElement::GetPopoverAttributeState() const {
   3420  return GetPopoverData() ? GetPopoverData()->GetPopoverAttributeState()
   3421                          : PopoverAttributeState::None;
   3422 }
   3423 
   3424 void nsGenericHTMLElement::PopoverPseudoStateUpdate(bool aOpen, bool aNotify) {
   3425  SetStates(ElementState::POPOVER_OPEN, aOpen, aNotify);
   3426 }
   3427 
   3428 already_AddRefed<ToggleEvent> nsGenericHTMLElement::CreateToggleEvent(
   3429    const nsAString& aEventType, const nsAString& aOldState,
   3430    const nsAString& aNewState, Cancelable aCancelable, Element* aSource) {
   3431  ToggleEventInit init;
   3432  init.mBubbles = false;
   3433  init.mOldState = aOldState;
   3434  init.mNewState = aNewState;
   3435  init.mCancelable = aCancelable == Cancelable::eYes;
   3436  RefPtr<ToggleEvent> event = ToggleEvent::Constructor(this, aEventType, init);
   3437  event->SetTrusted(true);
   3438  event->SetTarget(this);
   3439  event->SetSource(aSource);
   3440  return event.forget();
   3441 }
   3442 
   3443 bool nsGenericHTMLElement::FireToggleEvent(const nsAString& aOldState,
   3444                                           const nsAString& aNewState,
   3445                                           const nsAString& aType,
   3446                                           Element* aSource) {
   3447  const auto cancelable = aType == u"beforetoggle"_ns && aNewState == u"open"_ns
   3448                              ? Cancelable::eYes
   3449                              : Cancelable::eNo;
   3450  RefPtr event =
   3451      CreateToggleEvent(aType, aOldState, aNewState, cancelable, aSource);
   3452  EventDispatcher::DispatchDOMEvent(this, nullptr, event, nullptr, nullptr);
   3453  return event->DefaultPrevented();
   3454 }
   3455 
   3456 // https://html.spec.whatwg.org/#queue-a-popover-toggle-event-task
   3457 void nsGenericHTMLElement::QueuePopoverEventTask(
   3458    PopoverVisibilityState aOldState, Element* aSource) {
   3459  auto* data = GetPopoverData();
   3460  MOZ_ASSERT(data, "Should have popover data");
   3461 
   3462  if (auto* queuedToggleEventTask = data->GetToggleEventTask()) {
   3463    aOldState = queuedToggleEventTask->GetOldState();
   3464  }
   3465 
   3466  auto task = MakeRefPtr<PopoverToggleEventTask>(
   3467      do_GetWeakReference(this), do_GetWeakReference(aSource), aOldState);
   3468  data->SetToggleEventTask(task);
   3469  OwnerDoc()->Dispatch(task.forget());
   3470 }
   3471 
   3472 void nsGenericHTMLElement::RunPopoverToggleEventTask(
   3473    PopoverToggleEventTask* aTask, PopoverVisibilityState aOldState,
   3474    Element* aSource) {
   3475  auto* data = GetPopoverData();
   3476  if (!data) {
   3477    return;
   3478  }
   3479 
   3480  auto* popoverToggleEventTask = data->GetToggleEventTask();
   3481  if (!popoverToggleEventTask || aTask != popoverToggleEventTask) {
   3482    return;
   3483  }
   3484  data->ClearToggleEventTask();
   3485  // Intentionally ignore the return value here as only on open event the
   3486  // cancelable attribute is initialized to true for beforetoggle event.
   3487  auto stringForState = [](PopoverVisibilityState state) {
   3488    return state == PopoverVisibilityState::Hidden ? u"closed"_ns : u"open"_ns;
   3489  };
   3490  FireToggleEvent(stringForState(aOldState),
   3491                  stringForState(data->GetPopoverVisibilityState()),
   3492                  u"toggle"_ns, aSource);
   3493 }
   3494 
   3495 // https://html.spec.whatwg.org/#dom-showpopover
   3496 void nsGenericHTMLElement::ShowPopover(const ShowPopoverOptions& aOptions,
   3497                                       ErrorResult& aRv) {
   3498  Element* source = nullptr;
   3499  if (aOptions.mSource.WasPassed()) {
   3500    source = &aOptions.mSource.Value();
   3501  }
   3502  return ShowPopoverInternal(MOZ_KnownLive(source), aRv);
   3503 }
   3504 
   3505 // https://html.spec.whatwg.org/#show-popover
   3506 void nsGenericHTMLElement::ShowPopoverInternal(Element* aSource,
   3507                                               ErrorResult& aRv) {
   3508  // 1. If the result of running check popover validity given element, false,
   3509  // throwExceptions, and null is false, then return.
   3510  if (!CheckPopoverValidity(PopoverVisibilityState::Hidden, nullptr, aRv)) {
   3511    return;
   3512  }
   3513 
   3514  // 2. Let document be element's node document.
   3515  RefPtr<Document> document = OwnerDoc();
   3516 
   3517  // 3. Assert: element's popover trigger is null.
   3518  MOZ_ASSERT(!GetPopoverData() || !GetPopoverData()->GetInvoker());
   3519 
   3520  // 4. Assert: element is not in document's top layer.
   3521  MOZ_ASSERT(!OwnerDoc()->TopLayerContains(*this));
   3522 
   3523  // 5. Let nestedShow be element's popover showing or hiding.
   3524  bool nestedShow = GetPopoverData()->IsShowingOrHiding();
   3525 
   3526  // 6. Let fireEvents be the boolean negation of nestedShow.
   3527  bool fireEvents = !nestedShow;
   3528 
   3529  // 7. Set element's popover showing or hiding to true.
   3530  GetPopoverData()->SetIsShowingOrHiding(true);
   3531 
   3532  // 8. Let cleanupShowingFlag be the following steps:
   3533  auto cleanupShowingFlag = MakeScopeExit([&]() {
   3534    // 8.1. If nestedShow is false, then set element's popover showing or hiding
   3535    // to false.
   3536    if (auto* popoverData = GetPopoverData()) {
   3537      popoverData->SetIsShowingOrHiding(nestedShow);
   3538    }
   3539  });
   3540 
   3541  // 9. If the result of firing an event named beforetoggle, using ToggleEvent,
   3542  // with the cancelable attribute initialized to true, the oldState attribute
   3543  // initialized to "closed", the newState attribute initialized to "open", and
   3544  // the source attribute initialized to source at element is false, then run
   3545  // cleanupShowingFlag and return.
   3546  if (FireToggleEvent(u"closed"_ns, u"open"_ns, u"beforetoggle"_ns, aSource)) {
   3547    return;
   3548  }
   3549 
   3550  // 10. If the result of running check popover validity given element, false,
   3551  // throwExceptions, and document is false, then run cleanupShowingFlag and
   3552  // return.
   3553  if (!CheckPopoverValidity(PopoverVisibilityState::Hidden, document, aRv)) {
   3554    return;
   3555  }
   3556 
   3557  // 11. Let shouldRestoreFocus be false.
   3558  bool shouldRestoreFocus = false;
   3559 
   3560  // 12. Let originalType be the current state of element's popover attribute.
   3561  auto originalType = GetPopoverAttributeState();
   3562 
   3563  // 13. Let stackToAppendTo be null.
   3564  PopoverAttributeState stackToAppendTo = PopoverAttributeState::None;
   3565 
   3566  // 14. Let autoAncestor be the result of running the topmost popover ancestor
   3567  // algorithm given element, document's showing auto popover list, source, and
   3568  // true.
   3569  // todo(keithamus) ^ popover hint
   3570 
   3571  // 15. Let hintAncestor be the result of running the topmost popover ancestor
   3572  // algorithm given element, document's showing hint popover list, source, and
   3573  // true.
   3574  // todo(keithamus): ^ popover hint
   3575 
   3576  nsWeakPtr originallyFocusedElement;
   3577 
   3578  // 16. If originalType is the Auto state, then:
   3579  if (originalType == PopoverAttributeState::Auto) {
   3580    // 16.1. Run close entire popover list given document's showing hint popover
   3581    // list, shouldRestoreFocus, and fireEvents.
   3582    // todo(keithamus): ^ popover hint
   3583 
   3584    // 16.2. Let ancestor be the result of running the topmost popover ancestor
   3585    // algorithm given element, document's showing auto popover list, source,
   3586    // and true.
   3587    RefPtr<nsINode> ancestor =
   3588        GetTopmostPopoverAncestor(PopoverAttributeState::Auto, aSource, true);
   3589 
   3590    // 16.3. If ancestor is null, then set ancestor to document.
   3591    if (!ancestor) {
   3592      ancestor = document;
   3593    }
   3594 
   3595    // 16.4. Run hide all popovers until given ancestor, shouldRestoreFocus, and
   3596    // fireEvents.
   3597    document->HideAllPopoversUntil(*ancestor, false, fireEvents);
   3598 
   3599    // 16.5. Set stackToAppendTo to "auto".
   3600    stackToAppendTo = PopoverAttributeState::Auto;
   3601  }
   3602 
   3603  // 17. If originalType is the Hint state, then:
   3604  // 17.1. If hintAncestor is not null, then:
   3605  // 17.1.1. Run hide all popovers until given hintAncestor, shouldRestoreFocus,
   3606  // and fireEvents.
   3607  // 17.1.2. Set stackToAppendTo to "hint".
   3608  // 17.2. Otherwise:
   3609  // 17.2.1. Run close entire popover list given document's showing hint popover
   3610  // list, shouldRestoreFocus, and fireEvents.
   3611  // 17.2.2. If autoAncestor is not
   3612  // null, then:
   3613  // 17.2.2.1 Run hide all popovers until given autoAncestor,
   3614  // shouldRestoreFocus, and fireEvents.
   3615  // 17.2.2.2 Set stackToAppendTo to "auto".
   3616  // 17.2.3. Otherwise, set stackToAppendTo to "hint".
   3617  // todo(keithamus): ^ popover hint
   3618 
   3619  // 18. If originalType is Auto or Hint, then:
   3620  // todo(keithamus): ^ popover hint
   3621  if (originalType == PopoverAttributeState::Auto) {
   3622    // 18.1. Assert: stackToAppendTo is not null.
   3623    MOZ_ASSERT(stackToAppendTo != PopoverAttributeState::None);
   3624 
   3625    // 18.2. If originalType is not equal to the value of element's popover
   3626    // attribute, then:
   3627    if (originalType != GetPopoverAttributeState()) {
   3628      // 18.2.1. If throwExceptions is true, then throw an
   3629      // "InvalidStateError" DOMException.
   3630      aRv.ThrowInvalidStateError(
   3631          "The value of the popover attribute was changed while hiding the "
   3632          "popover.");
   3633      // 18.2.2. Return.
   3634      return;
   3635    }
   3636 
   3637    // 18.3. If the result of running check popover validity given element,
   3638    // false, throwExceptions, and document is false, then run
   3639    // cleanupShowingFlag and return.
   3640    if (!CheckPopoverValidity(PopoverVisibilityState::Hidden, document, aRv)) {
   3641      return;
   3642    }
   3643 
   3644    // 18.4. If the result of running topmost
   3645    // auto or hint popover on document is null, then set shouldRestoreFocus to
   3646    // true.
   3647    shouldRestoreFocus =
   3648        !document->GetTopmostPopoverOf(PopoverAttributeState::Auto);
   3649 
   3650    // 18.5. If stackToAppendTo is "auto":
   3651    if (stackToAppendTo == PopoverAttributeState::Auto) {
   3652      // 18.5.1. Assert: document's showing auto popover list does not contain
   3653      // element.
   3654      MOZ_ASSERT(
   3655          !document->PopoverListOf(PopoverAttributeState::Auto).Contains(this));
   3656 
   3657      // 18.5.2. Set element's opened in popover mode to "auto".
   3658      GetPopoverData()->SetOpenedInMode(PopoverAttributeState::Auto);
   3659    } else {
   3660      // 18.5.- Otherwise:
   3661      // 18.5.-.1. Assert: stackToAppendTo is "hint".
   3662      // 18.5.-.2. Assert: document's showing hint popover list does not contain
   3663      // element.
   3664      // 18.5.-.3. Set element's opened in popover mode to "hint".
   3665      // todo(keithamus): ^ popover hint
   3666      MOZ_ASSERT_UNREACHABLE("stackToAppendTo was not Auto!");
   3667    }
   3668    // 18.6. Set element's popover close watcher to the result of establishing a
   3669    // close watcher given element's relevant global object, with:
   3670    //       - cancelAction being to return true.
   3671    //       - closeAction being to hide a popover given element, true, true,
   3672    //       false, and null.
   3673    //       - getEnabledState being to return true.
   3674    if (StaticPrefs::dom_closewatcher_enabled()) {
   3675      GetPopoverData()->EnsureCloseWatcher(this);
   3676    }
   3677  }
   3678 
   3679  // 20. Let originallyFocusedElement be document's focused area of the
   3680  // document's DOM anchor.
   3681  if (nsIContent* unretargetedFocus =
   3682          document->GetUnretargetedFocusedContent()) {
   3683    originallyFocusedElement =
   3684        do_GetWeakReference(unretargetedFocus->AsElement());
   3685  }
   3686 
   3687  // 21. Add an element to the top layer given element.
   3688  document->AddPopoverToTopLayer(*this);
   3689 
   3690  PopoverPseudoStateUpdate(true, true);
   3691 
   3692  {
   3693    auto* popoverData = GetPopoverData();
   3694    // 18.5.2. Set element's opened in popover mode to "auto".
   3695    popoverData->SetOpenedInMode(GetPopoverAttributeState());
   3696    // 22. Set element's popover visibility state to showing.
   3697    popoverData->SetPopoverVisibilityState(PopoverVisibilityState::Showing);
   3698    // 23. Set element's popover trigger to source.
   3699    popoverData->SetInvoker(aSource);
   3700    if (aSource && aSource->IsHTMLElement()) {
   3701      aSource->SetAssociatedPopover(*this);
   3702    }
   3703  }
   3704 
   3705  // 25. Run the popover focusing steps given element.
   3706  FocusPopover();
   3707 
   3708  // 26. If shouldRestoreFocus is true and element's popover attribute is not
   3709  // in the No Popover state, then set element's previously focused element to
   3710  // originallyFocusedElement.
   3711  if (shouldRestoreFocus &&
   3712      GetPopoverAttributeState() != PopoverAttributeState::None) {
   3713    GetPopoverData()->SetPreviouslyFocusedElement(originallyFocusedElement);
   3714  }
   3715 
   3716  // 27. Queue a popover toggle event task given element, "closed", "open",
   3717  // and source.
   3718  QueuePopoverEventTask(PopoverVisibilityState::Hidden, aSource);
   3719 
   3720  // 28. Run cleanupShowingFlag.
   3721  // XXX (see MakeScopeExit above).
   3722 }
   3723 
   3724 void nsGenericHTMLElement::HidePopoverWithoutRunningScript() {
   3725  HidePopoverInternal(/* aFocusPreviousElement = */ false,
   3726                      /* aFireEvents = */ false,
   3727                      /* aSource = */ nullptr, IgnoreErrors());
   3728 }
   3729 
   3730 // https://html.spec.whatwg.org/#dom-hidepopover
   3731 void nsGenericHTMLElement::HidePopover(ErrorResult& aRv) {
   3732  HidePopoverInternal(/* aFocusPreviousElement = */ true,
   3733                      /* aFireEvents = */ true,
   3734                      /* aSource = */ nullptr, aRv);
   3735 }
   3736 
   3737 void nsGenericHTMLElement::HidePopoverInternal(bool aFocusPreviousElement,
   3738                                               bool aFireEvents,
   3739                                               mozilla::dom::Element* aSource,
   3740                                               ErrorResult& aRv) {
   3741  OwnerDoc()->HidePopover(*this, aFocusPreviousElement, aFireEvents, aSource,
   3742                          aRv);
   3743 }
   3744 
   3745 void nsGenericHTMLElement::ForgetPreviouslyFocusedElementAfterHidingPopover() {
   3746  auto* data = GetPopoverData();
   3747  MOZ_ASSERT(data, "Should have popover data");
   3748  data->SetPreviouslyFocusedElement(nullptr);
   3749 }
   3750 
   3751 void nsGenericHTMLElement::FocusPreviousElementAfterHidingPopover() {
   3752  auto* data = GetPopoverData();
   3753  MOZ_ASSERT(data, "Should have popover data");
   3754 
   3755  RefPtr<Element> control =
   3756      do_QueryReferent(data->GetPreviouslyFocusedElement().get());
   3757  data->SetPreviouslyFocusedElement(nullptr);
   3758 
   3759  if (!control) {
   3760    return;
   3761  }
   3762 
   3763  // Step 14.2 at
   3764  // https://html.spec.whatwg.org/multipage/popover.html#hide-popover-algorithm
   3765  // If focusPreviousElement is true and document's focused area of the
   3766  // document's DOM anchor is a shadow-including inclusive descendant of
   3767  // element, then run the focusing steps for previouslyFocusedElement;
   3768  nsIContent* currentFocus = OwnerDoc()->GetUnretargetedFocusedContent();
   3769  if (currentFocus &&
   3770      currentFocus->IsShadowIncludingInclusiveDescendantOf(this)) {
   3771    FocusOptions options;
   3772    options.mPreventScroll = true;
   3773    control->Focus(options, CallerType::NonSystem, IgnoreErrors());
   3774  }
   3775 }
   3776 
   3777 // https://html.spec.whatwg.org/multipage/popover.html#dom-togglepopover
   3778 bool nsGenericHTMLElement::TogglePopover(
   3779    const TogglePopoverOptionsOrBoolean& aOptions, ErrorResult& aRv) {
   3780  std::optional<bool> force;
   3781  // Kept alive by TogglePopoverOptions if non-null.
   3782  Element* invoker = nullptr;
   3783 
   3784  if (aOptions.IsBoolean()) {
   3785    force = std::make_optional(aOptions.GetAsBoolean());
   3786  } else {
   3787    const auto& options = aOptions.GetAsTogglePopoverOptions();
   3788    if (options.mForce.WasPassed()) {
   3789      force = std::make_optional(options.mForce.Value());
   3790    }
   3791    if (options.mSource.WasPassed()) {
   3792      invoker = &options.mSource.Value();
   3793    }
   3794  }
   3795 
   3796  if (PopoverOpen() && !force.value_or(false)) {
   3797    HidePopover(aRv);
   3798  } else if (force.value_or(true)) {
   3799    // Kept alive by TogglePopoverOptions.
   3800    ShowPopoverInternal(MOZ_KnownLive(invoker), aRv);
   3801  } else {
   3802    CheckPopoverValidity(GetPopoverData()
   3803                             ? GetPopoverData()->GetPopoverVisibilityState()
   3804                             : PopoverVisibilityState::Showing,
   3805                         nullptr, aRv);
   3806  }
   3807 
   3808  return PopoverOpen();
   3809 }
   3810 
   3811 // https://html.spec.whatwg.org/multipage/popover.html#popover-focusing-steps
   3812 void nsGenericHTMLElement::FocusPopover() {
   3813  if (auto* dialog = HTMLDialogElement::FromNode(this)) {
   3814    return MOZ_KnownLive(dialog)->FocusDialog();
   3815  }
   3816 
   3817  if (RefPtr<Document> doc = GetComposedDoc()) {
   3818    doc->FlushPendingNotifications(FlushType::Frames);
   3819  }
   3820 
   3821  RefPtr<Element> control = GetBoolAttr(nsGkAtoms::autofocus)
   3822                                ? this
   3823                                : GetAutofocusDelegate(IsFocusableFlags(0));
   3824  if (!control) {
   3825    return;
   3826  }
   3827  FocusCandidate(control, false /* aClearUpFocus */);
   3828 }
   3829 
   3830 void nsGenericHTMLElement::FocusCandidate(Element* aControl,
   3831                                          bool aClearUpFocus) {
   3832  // 1) Run the focusing steps given control.
   3833  IgnoredErrorResult rv;
   3834  if (RefPtr<Element> elementToFocus = nsFocusManager::GetTheFocusableArea(
   3835          aControl, nsFocusManager::ProgrammaticFocusFlags(FocusOptions()))) {
   3836    elementToFocus->Focus(FocusOptions(), CallerType::NonSystem, rv);
   3837    if (rv.Failed()) {
   3838      return;
   3839    }
   3840  } else if (aClearUpFocus) {
   3841    if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
   3842      // Clear the focus which ends up making the body gets focused
   3843      nsCOMPtr<nsPIDOMWindowOuter> outerWindow = OwnerDoc()->GetWindow();
   3844      fm->ClearFocus(outerWindow);
   3845    }
   3846  }
   3847 
   3848  // 2) Let topDocument be the active document of control's node document's
   3849  // browsing context's top-level browsing context.
   3850  // 3) If control's node document's origin is not the same as the origin of
   3851  // topDocument, then return.
   3852  BrowsingContext* bc = aControl->OwnerDoc()->GetBrowsingContext();
   3853  if (bc && bc->IsInProcess() && bc->SameOriginWithTop()) {
   3854    if (nsCOMPtr<nsIDocShell> docShell = bc->Top()->GetDocShell()) {
   3855      if (Document* topDocument = docShell->GetExtantDocument()) {
   3856        // 4) Empty topDocument's autofocus candidates.
   3857        // 5) Set topDocument's autofocus processed flag to true.
   3858        topDocument->SetAutoFocusFired();
   3859      }
   3860    }
   3861  }
   3862 }
   3863 
   3864 already_AddRefed<ElementInternals> nsGenericHTMLElement::AttachInternals(
   3865    ErrorResult& aRv) {
   3866  // ElementInternals is only available on autonomous custom element, so
   3867  // throws an error by default. The spec steps are implemented in HTMLElement
   3868  // because ElementInternals needs to hold a pointer to HTMLElement in order
   3869  // to forward form operation to it.
   3870  aRv.ThrowNotSupportedError(nsPrintfCString(
   3871      "Cannot attach ElementInternals to a customized built-in or non-custom "
   3872      "element "
   3873      "'%s'",
   3874      NS_ConvertUTF16toUTF8(NodeInfo()->NameAtom()->GetUTF16String()).get()));
   3875  return nullptr;
   3876 }
   3877 
   3878 ElementInternals* nsGenericHTMLElement::GetInternals() const {
   3879  if (CustomElementData* data = GetCustomElementData()) {
   3880    return data->GetElementInternals();
   3881  }
   3882  return nullptr;
   3883 }
   3884 
   3885 bool nsGenericHTMLElement::IsFormAssociatedCustomElement() const {
   3886  CustomElementData* data = GetCustomElementData();
   3887  return data && data->IsFormAssociated();
   3888 }
   3889 
   3890 void nsGenericHTMLElement::GetAutocapitalize(nsAString& aValue) const {
   3891  GetEnumAttr(nsGkAtoms::autocapitalize, nullptr, kDefaultAutocapitalize->tag,
   3892              aValue);
   3893 }
   3894 
   3895 bool nsGenericHTMLElement::Translate() const {
   3896  if (const nsAttrValue* attr = mAttrs.GetAttr(nsGkAtoms::translate)) {
   3897    if (attr->IsEmptyString() || attr->Equals(nsGkAtoms::yes, eIgnoreCase)) {
   3898      return true;
   3899    }
   3900    if (attr->Equals(nsGkAtoms::no, eIgnoreCase)) {
   3901      return false;
   3902    }
   3903  }
   3904  return nsGenericHTMLElementBase::Translate();
   3905 }
   3906 
   3907 void nsGenericHTMLElement::GetPopover(nsString& aPopover) const {
   3908  GetHTMLEnumAttr(nsGkAtoms::popover, aPopover);
   3909  if (aPopover.IsEmpty() && !DOMStringIsNull(aPopover)) {
   3910    aPopover.Assign(NS_ConvertUTF8toUTF16(kPopoverAttributeValueAuto));
   3911  }
   3912 }
   3913 
   3914 /******************************************************************************
   3915 *  nsIFormControl
   3916 *****************************************************************************/
   3917 
   3918 // static
   3919 nsIFormControl* nsIFormControl::FromEventTarget(
   3920    mozilla::dom::EventTarget* aTarget) {
   3921  MOZ_ASSERT(aTarget);
   3922  return aTarget->IsNode() ? aTarget->AsNode()->GetAsFormControl() : nullptr;
   3923 }
   3924 
   3925 // static
   3926 nsIFormControl* nsIFormControl::FromEventTargetOrNull(
   3927    mozilla::dom::EventTarget* aTarget) {
   3928  return aTarget && aTarget->IsNode() ? aTarget->AsNode()->GetAsFormControl()
   3929                                      : nullptr;
   3930 }
   3931 
   3932 // static
   3933 const nsIFormControl* nsIFormControl::FromEventTarget(
   3934    const mozilla::dom::EventTarget* aTarget) {
   3935  MOZ_ASSERT(aTarget);
   3936  return aTarget->IsNode() ? aTarget->AsNode()->GetAsFormControl() : nullptr;
   3937 }
   3938 
   3939 // static
   3940 const nsIFormControl* nsIFormControl::FromEventTargetOrNull(
   3941    const mozilla::dom::EventTarget* aTarget) {
   3942  return aTarget && aTarget->IsNode() ? aTarget->AsNode()->GetAsFormControl()
   3943                                      : nullptr;
   3944 }
   3945 
   3946 // static
   3947 nsIFormControl* nsIFormControl::FromNode(nsINode* aNode) {
   3948  MOZ_ASSERT(aNode);
   3949  return aNode->GetAsFormControl();
   3950 }
   3951 
   3952 // static
   3953 nsIFormControl* nsIFormControl::FromNodeOrNull(nsINode* aNode) {
   3954  return aNode ? aNode->GetAsFormControl() : nullptr;
   3955 }
   3956 
   3957 // static
   3958 const nsIFormControl* nsIFormControl::FromNode(const nsINode* aNode) {
   3959  MOZ_ASSERT(aNode);
   3960  return aNode->GetAsFormControl();
   3961 }
   3962 
   3963 // static
   3964 const nsIFormControl* nsIFormControl::FromNodeOrNull(const nsINode* aNode) {
   3965  return aNode ? aNode->GetAsFormControl() : nullptr;
   3966 }