Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/html/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 130 kB image not shown  

SSL nsGenericHTMLElement.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "mozilla/EditorBase.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/HTMLEditor.h"
#include "mozilla/FocusModel.h"
#include "mozilla/IMEContentObserver.h"
#include "mozilla/IMEStateManager.h"
#include "mozilla/MappedDeclarationsBuilder.h"
#include "mozilla/Maybe.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/PresShell.h"
#include "mozilla/TextEditor.h"
#include "mozilla/TextEvents.h"
#include "mozilla/StaticPrefs_accessibility.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/dom/AncestorIterator.h"
#include "mozilla/dom/FetchPriority.h"
#include "mozilla/dom/FormData.h"
#include "nsCaseTreatment.h"
#include "nscore.h"
#include "nsGenericHTMLElement.h"
#include "nsCOMPtr.h"
#include "nsAtom.h"
#include "nsQueryObject.h"
#include "mozilla/dom/BindContext.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/UnbindContext.h"
#include "nsPIDOMWindow.h"
#include "nsIFrameInlines.h"
#include "nsView.h"
#include "nsViewManager.h"
#include "nsIWidget.h"
#include "nsRange.h"
#include "nsPresContext.h"
#include "nsError.h"
#include "nsIPrincipal.h"
#include "nsContainerFrame.h"
#include "nsStyleUtil.h"
#include "ReferrerInfo.h"

#include "mozilla/PresState.h"
#include "nsILayoutHistoryState.h"

#include "nsHTMLParts.h"
#include "nsContentUtils.h"
#include "mozilla/dom/DirectionalityUtils.h"
#include "mozilla/dom/DocumentOrShadowRoot.h"
#include "nsString.h"
#include "nsGkAtoms.h"
#include "nsDOMCSSDeclaration.h"
#include "nsIFormControl.h"
#include "mozilla/dom/HTMLFormElement.h"
#include "nsFocusManager.h"

#include "nsDOMStringMap.h"
#include "nsDOMString.h"

#include "nsLayoutUtils.h"
#include "mozilla/dom/DocumentInlines.h"
#include "HTMLFieldSetElement.h"
#include "nsTextNode.h"
#include "HTMLBRElement.h"
#include "nsDOMMutationObserver.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/FromParser.h"
#include "mozilla/dom/Link.h"
#include "mozilla/dom/ScriptLoader.h"

#include "nsDOMTokenList.h"
#include "nsThreadUtils.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/ToggleEvent.h"
#include "mozilla/dom/TouchEvent.h"
#include "mozilla/dom/InputEvent.h"
#include "mozilla/dom/InvokeEvent.h"
#include "mozilla/ErrorResult.h"
#include "nsHTMLDocument.h"
#include "nsGlobalWindowInner.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "imgIContainer.h"
#include "nsComputedDOMStyle.h"
#include "mozilla/dom/HTMLDialogElement.h"
#include "mozilla/dom/HTMLLabelElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/CustomElementRegistry.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/ElementInternals.h"

using namespace mozilla;
using namespace mozilla::dom;

static const uint8_t NS_INPUTMODE_NONE = 1;
static const uint8_t NS_INPUTMODE_TEXT = 2;
static const uint8_t NS_INPUTMODE_TEL = 3;
static const uint8_t NS_INPUTMODE_URL = 4;
static const uint8_t NS_INPUTMODE_EMAIL = 5;
static const uint8_t NS_INPUTMODE_NUMERIC = 6;
static const uint8_t NS_INPUTMODE_DECIMAL = 7;
static const uint8_t NS_INPUTMODE_SEARCH = 8;

static constexpr nsAttrValue::EnumTable kInputmodeTable[] = {
    {"none", NS_INPUTMODE_NONE},
    {"text", NS_INPUTMODE_TEXT},
    {"tel", NS_INPUTMODE_TEL},
    {"url", NS_INPUTMODE_URL},
    {"email", NS_INPUTMODE_EMAIL},
    {"numeric", NS_INPUTMODE_NUMERIC},
    {"decimal", NS_INPUTMODE_DECIMAL},
    {"search", NS_INPUTMODE_SEARCH},
    {nullptr, 0}};

static const uint8_t NS_ENTERKEYHINT_ENTER = 1;
static const uint8_t NS_ENTERKEYHINT_DONE = 2;
static const uint8_t NS_ENTERKEYHINT_GO = 3;
static const uint8_t NS_ENTERKEYHINT_NEXT = 4;
static const uint8_t NS_ENTERKEYHINT_PREVIOUS = 5;
static const uint8_t NS_ENTERKEYHINT_SEARCH = 6;
static const uint8_t NS_ENTERKEYHINT_SEND = 7;

static constexpr nsAttrValue::EnumTable kEnterKeyHintTable[] = {
    {"enter", NS_ENTERKEYHINT_ENTER},
    {"done", NS_ENTERKEYHINT_DONE},
    {"go", NS_ENTERKEYHINT_GO},
    {"next", NS_ENTERKEYHINT_NEXT},
    {"previous", NS_ENTERKEYHINT_PREVIOUS},
    {"search", NS_ENTERKEYHINT_SEARCH},
    {"send", NS_ENTERKEYHINT_SEND},
    {nullptr, 0}};

static const uint8_t NS_AUTOCAPITALIZE_NONE = 1;
static const uint8_t NS_AUTOCAPITALIZE_SENTENCES = 2;
static const uint8_t NS_AUTOCAPITALIZE_WORDS = 3;
static const uint8_t NS_AUTOCAPITALIZE_CHARACTERS = 4;

static constexpr nsAttrValue::EnumTable kAutocapitalizeTable[] = {
    {"none", NS_AUTOCAPITALIZE_NONE},
    {"sentences", NS_AUTOCAPITALIZE_SENTENCES},
    {"words", NS_AUTOCAPITALIZE_WORDS},
    {"characters", NS_AUTOCAPITALIZE_CHARACTERS},
    {"off", NS_AUTOCAPITALIZE_NONE},
    {"on", NS_AUTOCAPITALIZE_SENTENCES},
    {"", 0},
    {nullptr, 0}};

static const nsAttrValue::EnumTable* kDefaultAutocapitalize =
    &kAutocapitalizeTable[1];

nsresult nsGenericHTMLElement::CopyInnerTo(Element* aDst) {
  MOZ_ASSERT(!aDst->GetUncomposedDoc(),
             "Should not CopyInnerTo an Element in a document");

  auto reparse = aDst->OwnerDoc() == OwnerDoc() ? ReparseAttributes::No
                                                : ReparseAttributes::Yes;
  nsresult rv = Element::CopyInnerTo(aDst, reparse);
  NS_ENSURE_SUCCESS(rv, rv);

  // cloning a node must retain its internal nonce slot
  nsString* nonce = static_cast<nsString*>(GetProperty(nsGkAtoms::nonce));
  if (nonce) {
    static_cast<nsGenericHTMLElement*>(aDst)->SetNonce(*nonce);
  }
  return NS_OK;
}

static constexpr nsAttrValue::EnumTable kDirTable[] = {
    {"ltr", Directionality::Ltr},
    {"rtl", Directionality::Rtl},
    {"auto", Directionality::Auto},
    {nullptr, 0},
};

namespace {
// See <https://html.spec.whatwg.org/#the-popover-attribute>.
enum class PopoverAttributeKeyword : uint8_t { Auto, EmptyString, Manual };

static constexpr const char kPopoverAttributeValueAuto[] = "auto";
static constexpr const char kPopoverAttributeValueEmptyString[] = "";
static constexpr const char kPopoverAttributeValueManual[] = "manual";

static constexpr nsAttrValue::EnumTable kPopoverTable[] = {
    {kPopoverAttributeValueAuto, PopoverAttributeKeyword::Auto},
    {kPopoverAttributeValueEmptyString, PopoverAttributeKeyword::EmptyString},
    {kPopoverAttributeValueManual, PopoverAttributeKeyword::Manual},
    {nullptr, 0}};

// See <https://html.spec.whatwg.org/#the-popover-attribute>.
static const nsAttrValue::EnumTable* kPopoverTableInvalidValueDefault =
    &kPopoverTable[2];
}  // namespace

void nsGenericHTMLElement::GetFetchPriority(nsAString& aFetchPriority) const {
  // <https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fetch-priority-attributes>.
  GetEnumAttr(nsGkAtoms::fetchpriority, kFetchPriorityAttributeValueAuto,
              aFetchPriority);
}

/* static */
FetchPriority nsGenericHTMLElement::ToFetchPriority(const nsAString& aValue) {
  nsAttrValue attrValue;
  ParseFetchPriority(aValue, attrValue);
  MOZ_ASSERT(attrValue.Type() == nsAttrValue::eEnum);
  return FetchPriority(attrValue.GetEnumValue());
}

namespace {
// <https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fetch-priority-attributes>.
static constexpr nsAttrValue::EnumTable kFetchPriorityEnumTable[] = {
    {kFetchPriorityAttributeValueHigh, FetchPriority::High},
    {kFetchPriorityAttributeValueLow, FetchPriority::Low},
    {kFetchPriorityAttributeValueAuto, FetchPriority::Auto},
    {nullptr, 0}};

// <https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fetch-priority-attributes>.
static const nsAttrValue::EnumTable*
    kFetchPriorityEnumTableInvalidValueDefault = &kFetchPriorityEnumTable[2];
}  // namespace

FetchPriority nsGenericHTMLElement::GetFetchPriority() const {
  const nsAttrValue* fetchpriorityAttribute =
      GetParsedAttr(nsGkAtoms::fetchpriority);
  if (fetchpriorityAttribute) {
    MOZ_ASSERT(fetchpriorityAttribute->Type() == nsAttrValue::eEnum);
    return FetchPriority(fetchpriorityAttribute->GetEnumValue());
  }

  return FetchPriority::Auto;
}

/* static */
void nsGenericHTMLElement::ParseFetchPriority(const nsAString& aValue,
                                              nsAttrValue& aResult) {
  aResult.ParseEnumValue(aValue, kFetchPriorityEnumTable,
                         false /* aCaseSensitive */,
                         kFetchPriorityEnumTableInvalidValueDefault);
}

void nsGenericHTMLElement::AddToNameTable(nsAtom* aName) {
  MOZ_ASSERT(HasName(), "Node doesn't have name?");
  Document* doc = GetUncomposedDoc();
  if (doc && !IsInNativeAnonymousSubtree()) {
    doc->AddToNameTable(this, aName);
  }
}

void nsGenericHTMLElement::RemoveFromNameTable() {
  if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
    if (Document* doc = GetUncomposedDoc()) {
      doc->RemoveFromNameTable(this,
                               GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
    }
  }
}

void nsGenericHTMLElement::GetAccessKeyLabel(nsString& aLabel) {
  nsAutoString suffix;
  GetAccessKey(suffix);
  if (!suffix.IsEmpty()) {
    EventStateManager::GetAccessKeyLabelPrefix(this, aLabel);
    aLabel.Append(suffix);
  }
}

static bool IsOffsetParent(nsIFrame* aFrame) {
  LayoutFrameType frameType = aFrame->Type();

  if (frameType == LayoutFrameType::TableCell ||
      frameType == LayoutFrameType::TableWrapper) {
    // Per the IDL for Element, only td, th, and table are acceptable
    // offsetParents apart from body or positioned elements; we need to check
    // the content type as well as the frame type so we ignore anonymous tables
    // created by an element with display: table-cell with no actual table
    nsIContent* content = aFrame->GetContent();

    return content->IsAnyOfHTMLElements(nsGkAtoms::table, nsGkAtoms::td,
                                        nsGkAtoms::th);
  }
  return false;
}

struct OffsetResult {
  Element* mParent = nullptr;
  nsRect mRect;
};

static OffsetResult GetUnretargetedOffsetsFor(const Element& aElement) {
  nsIFrame* frame = aElement.GetPrimaryFrame();
  if (!frame) {
    return {};
  }

  nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(frame);

  nsIFrame* parent = frame->GetParent();
  nsPoint origin(0, 0);

  nsIContent* offsetParent = nullptr;
  Element* docElement = aElement.GetComposedDoc()->GetRootElement();
  nsIContent* content = frame->GetContent();
  const auto effectiveZoom = frame->Style()->EffectiveZoom();

  if (content &&
      (content->IsHTMLElement(nsGkAtoms::body) || content == docElement)) {
    parent = frame;
  } else {
    const bool isPositioned = styleFrame->IsAbsPosContainingBlock();
    const bool isAbsolutelyPositioned = frame->IsAbsolutelyPositioned();
    origin += frame->GetPositionIgnoringScrolling();

    for (; parent; parent = parent->GetParent()) {
      content = parent->GetContent();

      // Stop at the first ancestor that is positioned.
      if (parent->IsAbsPosContainingBlock()) {
        offsetParent = content;
        break;
      }

      // WebKit-ism: offsetParent stops at zoom changes.
      // See https://github.com/w3c/csswg-drafts/issues/10252
      if (effectiveZoom != parent->Style()->EffectiveZoom()) {
        offsetParent = content;
        break;
      }

      // Add the parent's origin to our own to get to the
      // right coordinate system.
      const bool isOffsetParent = !isPositioned && IsOffsetParent(parent);
      if (!isOffsetParent) {
        origin += parent->GetPositionIgnoringScrolling();
      }

      if (content) {
        // If we've hit the document element, break here.
        if (content == docElement) {
          break;
        }

        // Break if the ancestor frame type makes it suitable as offset parent
        // and this element is *not* positioned or if we found the body element.
        if (isOffsetParent || content->IsHTMLElement(nsGkAtoms::body)) {
          offsetParent = content;
          break;
        }
      }
    }

    if (isAbsolutelyPositioned && !offsetParent) {
      // If this element is absolutely positioned, but we don't have
      // an offset parent it means this element is an absolutely
      // positioned child that's not nested inside another positioned
      // element, in this case the element's frame's parent is the
      // frame for the HTML element so we fail to find the body in the
      // parent chain. We want the offset parent in this case to be
      // the body, so we just get the body element from the document.
      //
      // We use GetBodyElement() here, not GetBody(), because we don't want to
      // end up with framesets here.
      offsetParent = aElement.GetComposedDoc()->GetBodyElement();
    }
  }

  // Make the position relative to the padding edge.
  if (parent) {
    const nsStyleBorder* border = parent->StyleBorder();
    origin.x -= border->GetComputedBorderWidth(eSideLeft);
    origin.y -= border->GetComputedBorderWidth(eSideTop);
  }

  // Get the union of all rectangles in this and continuation frames.
  // It doesn't really matter what we use as aRelativeTo here, since
  // we only care about the size. We just have to use something non-null.
  nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, frame);
  rcFrame.MoveTo(origin);
  return {Element::FromNodeOrNull(offsetParent), rcFrame};
}

static bool ShouldBeRetargeted(const Element& aReferenceElement,
                               const Element& aElementToMaybeRetarget) {
  ShadowRoot* shadow = aElementToMaybeRetarget.GetContainingShadow();
  if (!shadow) {
    return false;
  }
  for (ShadowRoot* scope = aReferenceElement.GetContainingShadow(); scope;
       scope = scope->Host()->GetContainingShadow()) {
    if (scope == shadow) {
      return false;
    }
  }

  return true;
}

Element* nsGenericHTMLElement::GetOffsetRect(CSSIntRect& aRect) {
  aRect = CSSIntRect();

  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
  if (!frame) {
    return nullptr;
  }

  OffsetResult thisResult = GetUnretargetedOffsetsFor(*this);
  nsRect rect = thisResult.mRect;
  Element* parent = thisResult.mParent;
  while (parent && ShouldBeRetargeted(*this, *parent)) {
    OffsetResult result = GetUnretargetedOffsetsFor(*parent);
    rect += result.mRect.TopLeft();
    parent = result.mParent;
  }

  aRect = CSSIntRect::FromAppUnitsRounded(
      frame->Style()->EffectiveZoom().Unzoom(rect));
  return parent;
}

bool nsGenericHTMLElement::Spellcheck() {
  // Has the state has been explicitly set?
  nsIContent* node;
  for (node = this; node; node = node->GetParent()) {
    if (node->IsHTMLElement()) {
      static Element::AttrValuesArray strings[] = {nsGkAtoms::_true,
                                                   nsGkAtoms::_false, nullptr};
      switch (node->AsElement()->FindAttrValueIn(
          kNameSpaceID_None, nsGkAtoms::spellcheck, strings, eCaseMatters)) {
        case 0:  // spellcheck = "true"
          return true;
        case 1:  // spellcheck = "false"
          return false;
      }
    }
  }

  // contenteditable/designMode are spellchecked by default
  if (IsEditable()) {
    return true;
  }

  // Is this a chrome element?
  if (nsContentUtils::IsChromeDoc(OwnerDoc())) {
    return false;  // Not spellchecked by default
  }

  // Anything else that's not a form control is not spellchecked by default
  const nsIFormControl* formControl = GetAsFormControl();
  if (!formControl) {
    return false;  // Not spellchecked by default
  }

  // Is this a multiline plaintext input?
  auto controlType = formControl->ControlType();
  if (controlType == FormControlType::Textarea) {
    return true;  // Spellchecked by default
  }

  // Is this anything other than an input text?
  // Other inputs are not spellchecked.
  if (controlType != FormControlType::InputText) {
    return false;  // Not spellchecked by default
  }

  // Does the user want input text spellchecked by default?
  // NOTE: Do not reflect a pref value of 0 back to the DOM getter.
  // The web page should not know if the user has disabled spellchecking.
  // We'll catch this in the editor itself.
  int32_t spellcheckLevel = StaticPrefs::layout_spellcheckDefault();
  return spellcheckLevel == 2;  // "Spellcheck multi- and single-line"
}

bool nsGenericHTMLElement::Autocorrect() const {
  return !AttrValueIs(kNameSpaceID_None, nsGkAtoms::autocorrect, nsGkAtoms::OFF,
                      eIgnoreCase);
}

bool nsGenericHTMLElement::InNavQuirksMode(Document* aDoc) {
  return aDoc && aDoc->GetCompatibilityMode() == eCompatibility_NavQuirks;
}

void nsGenericHTMLElement::UpdateEditableState(bool aNotify) {
  // XXX Should we do this only when in a document?
  ContentEditableState state = GetContentEditableState();
  if (state != ContentEditableState::Inherit) {
    SetEditableFlag(IsEditableState(state));
    UpdateReadOnlyState(aNotify);
    return;
  }
  nsStyledElement::UpdateEditableState(aNotify);
}

nsresult nsGenericHTMLElement::BindToTree(BindContext& aContext,
                                          nsINode& aParent) {
  nsresult rv = nsGenericHTMLElementBase::BindToTree(aContext, aParent);
  NS_ENSURE_SUCCESS(rv, rv);

  if (IsInComposedDoc()) {
    RegUnRegAccessKey(true);
  }

  if (IsInUncomposedDoc()) {
    if (HasName() && CanHaveName(NodeInfo()->NameAtom())) {
      aContext.OwnerDoc().AddToNameTable(
          this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
    }
  }

  if (HasFlag(NODE_IS_EDITABLE) &&
      HasContentEditableAttrTrueOrPlainTextOnly() && IsInComposedDoc()) {
    aContext.OwnerDoc().ChangeContentEditableCount(this, +1);
  }

  // Hide any nonce from the DOM, but keep the internal value of the
  // nonce by copying and resetting the internal nonce value.
  if (HasFlag(NODE_HAS_NONCE_AND_HEADER_CSP) && IsInComposedDoc() &&
      OwnerDoc()->GetBrowsingContext()) {
    nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
        "nsGenericHTMLElement::ResetNonce::Runnable",
        [self = RefPtr<nsGenericHTMLElement>(this)]() {
          nsAutoString nonce;
          self->GetNonce(nonce);
          self->SetAttr(kNameSpaceID_None, nsGkAtoms::nonce, u""_ns, true);
          self->SetNonce(nonce);
        }));
  }

  // We need to consider a labels element is moved to another subtree
  // with different root, it needs to update labels list and its root
  // as well.
  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
  if (slots && slots->mLabelsList) {
    slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
  }

  return rv;
}

void nsGenericHTMLElement::UnbindFromTree(UnbindContext& aContext) {
  if (IsInComposedDoc()) {
    // https://html.spec.whatwg.org/#dom-trees:hide-popover-algorithm
    // If removedNode's popover attribute is not in the no popover state, then
    // run the hide popover algorithm given removedNode, false, false, and
    // false.
    if (GetPopoverData()) {
      HidePopoverWithoutRunningScript();
    }
    RegUnRegAccessKey(false);
  }

  RemoveFromNameTable();

  if (HasContentEditableAttrTrueOrPlainTextOnly()) {
    if (Document* doc = GetComposedDoc()) {
      doc->ChangeContentEditableCount(this, -1);
    }
  }

  nsStyledElement::UnbindFromTree(aContext);

  // Invalidate .labels list. It will be repopulated when used the next time.
  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
  if (slots && slots->mLabelsList) {
    slots->mLabelsList->MaybeResetRoot(SubtreeRoot());
  }
}

HTMLFormElement* nsGenericHTMLElement::FindAncestorForm(
    HTMLFormElement* aCurrentForm) {
  NS_ASSERTION(!HasAttr(nsGkAtoms::form) || IsHTMLElement(nsGkAtoms::img),
               "FindAncestorForm should not be called if @form is set!");
  if (IsInNativeAnonymousSubtree()) {
    return nullptr;
  }

  nsIContent* content = this;
  while (content) {
    // If the current ancestor is a form, return it as our form
    if (content->IsHTMLElement(nsGkAtoms::form)) {
#ifdef DEBUG
      if (!nsContentUtils::IsInSameAnonymousTree(this, content)) {
        // It's possible that we started unbinding at |content| or
        // some ancestor of it, and |content| and |this| used to all be
        // anonymous.  Check for this the hard way.
        for (nsIContent* child = this; child != content;
             child = child->GetParent()) {
          NS_ASSERTION(child->ComputeIndexInParentContent().isSome(),
                       "Walked too far?");
        }
      }
#endif
      return static_cast<HTMLFormElement*>(content);
    }

    nsIContent* prevContent = content;
    content = prevContent->GetParent();

    if (!content && aCurrentForm) {
      // We got to the root of the subtree we're in, and we're being removed
      // from the DOM (the only time we get into this method with a non-null
      // aCurrentForm).  Check whether aCurrentForm is in the same subtree.  If
      // it is, we want to return aCurrentForm, since this case means that
      // we're one of those inputs-in-a-table that have a hacked mForm pointer
      // and a subtree containing both us and the form got removed from the
      // DOM.
      if (aCurrentForm->IsInclusiveDescendantOf(prevContent)) {
        return aCurrentForm;
      }
    }
  }

  return nullptr;
}

bool nsGenericHTMLElement::CheckHandleEventForAnchorsPreconditions(
    EventChainVisitor& aVisitor) {
  MOZ_ASSERT(nsCOMPtr<Link>(do_QueryObject(this)),
             "should be called only when |this| implements |Link|");
  // When disconnected, only <a> should navigate away per
  // https://html.spec.whatwg.org/#cannot-navigate
  return IsInComposedDoc() || IsHTMLElement(nsGkAtoms::a);
}

void nsGenericHTMLElement::GetEventTargetParentForAnchors(
    EventChainPreVisitor& aVisitor) {
  nsGenericHTMLElementBase::GetEventTargetParent(aVisitor);

  if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) {
    return;
  }

  GetEventTargetParentForLinks(aVisitor);
}

nsresult nsGenericHTMLElement::PostHandleEventForAnchors(
    EventChainPostVisitor& aVisitor) {
  if (!CheckHandleEventForAnchorsPreconditions(aVisitor)) {
    return NS_OK;
  }

  return PostHandleEventForLinks(aVisitor);
}

bool nsGenericHTMLElement::IsHTMLLink(nsIURI** aURI) const {
  MOZ_ASSERT(aURI, "Must provide aURI out param");

  *aURI = GetHrefURIForAnchors().take();
  // We promise out param is non-null if we return true, so base rv on it
  return *aURI != nullptr;
}

already_AddRefed<nsIURI> nsGenericHTMLElement::GetHrefURIForAnchors() const {
  // This is used by the three Link implementations and
  // nsHTMLStyleElement.

  // Get href= attribute (relative URI).

  // We use the nsAttrValue's copy of the URI string to avoid copying.
  nsCOMPtr<nsIURI> uri;
  GetURIAttr(nsGkAtoms::href, nullptr, getter_AddRefs(uri));
  return uri.forget();
}

void nsGenericHTMLElement::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
                                         const nsAttrValue* aValue,
                                         bool aNotify) {
  if (aNamespaceID == kNameSpaceID_None) {
    if (aName == nsGkAtoms::accesskey) {
      // Have to unregister before clearing flag. See UnregAccessKey
      RegUnRegAccessKey(false);
      if (!aValue) {
        UnsetFlags(NODE_HAS_ACCESSKEY);
      }
    } else if (aName == nsGkAtoms::name) {
      // Have to do this before clearing flag. See RemoveFromNameTable
      RemoveFromNameTable();
      if (!aValue || aValue->IsEmptyString()) {
        ClearHasName();
      }
    } else if (aName == nsGkAtoms::contenteditable) {
      if (aValue) {
        // Set this before the attribute is set so that any subclass code that
        // runs before the attribute is set won't think we're missing a
        // contenteditable attr when we actually have one.
        SetMayHaveContentEditableAttr();
      }
    }
    if (!aValue && IsEventAttributeName(aName)) {
      if (EventListenerManager* manager = GetExistingListenerManager()) {
        manager->RemoveEventHandler(GetEventNameForAttr(aName));
      }
    }
  }

  return nsGenericHTMLElementBase::BeforeSetAttr(aNamespaceID, aName, aValue,
                                                 aNotify);
}

namespace {
constexpr PopoverAttributeState ToPopoverAttributeState(
    PopoverAttributeKeyword aPopoverAttributeKeyword) {
  // See <https://html.spec.whatwg.org/#the-popover-attribute>.
  switch (aPopoverAttributeKeyword) {
    case PopoverAttributeKeyword::Auto:
      return PopoverAttributeState::Auto;
    case PopoverAttributeKeyword::EmptyString:
      return PopoverAttributeState::Auto;
    case PopoverAttributeKeyword::Manual:
      return PopoverAttributeState::Manual;
    default: {
      MOZ_ASSERT_UNREACHABLE();
      return PopoverAttributeState::None;
    }
  }
}
}  // namespace

void nsGenericHTMLElement::AfterSetPopoverAttr() {
  auto mapPopoverState = [](const nsAttrValue* value) -> PopoverAttributeState {
    if (value) {
      MOZ_ASSERT(value->Type() == nsAttrValue::eEnum);
      const auto popoverAttributeKeyword =
          static_cast<PopoverAttributeKeyword>(value->GetEnumValue());
      return ToPopoverAttributeState(popoverAttributeKeyword);
    }

    // The missing value default is the no popover state, see
    // <https://html.spec.whatwg.org/multipage/popover.html#attr-popover>.
    return PopoverAttributeState::None;
  };

  PopoverAttributeState newState =
      mapPopoverState(GetParsedAttr(nsGkAtoms::popover));

  const PopoverAttributeState oldState = GetPopoverAttributeState();

  if (newState != oldState) {
    PopoverPseudoStateUpdate(falsetrue);

    if (IsPopoverOpen()) {
      HidePopoverInternal(/* aFocusPreviousElement = */ true,
                          /* aFireEvents = */ true, IgnoreErrors());
      // Event handlers could have removed the popover attribute, or changed
      // its value.
      // https://github.com/whatwg/html/issues/9034
      newState = mapPopoverState(GetParsedAttr(nsGkAtoms::popover));
    }

    if (newState == PopoverAttributeState::None) {
      ClearPopoverData();
      RemoveStates(ElementState::POPOVER_OPEN);
    } else {
      // TODO: what if `HidePopoverInternal` called `ShowPopup()`?
      EnsurePopoverData().SetPopoverAttributeState(newState);
    }
  }
}

void nsGenericHTMLElement::OnAttrSetButNotChanged(
    int32_t aNamespaceID, nsAtom* aName, const nsAttrValueOrString& aValue,
    bool aNotify) {
  if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::popovertarget) {
    ClearExplicitlySetAttrElement(aName);
  }
  return nsGenericHTMLElementBase::OnAttrSetButNotChanged(aNamespaceID, aName,
                                                          aValue, aNotify);
}

void nsGenericHTMLElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
                                        const nsAttrValue* aValue,
                                        const nsAttrValue* aOldValue,
                                        nsIPrincipal* aMaybeScriptedPrincipal,
                                        bool aNotify) {
  if (aNamespaceID == kNameSpaceID_None) {
    if (IsEventAttributeName(aName) && aValue) {
      MOZ_ASSERT(aValue->Type() == nsAttrValue::eString,
                 "Expected string value for script body");
      SetEventHandler(GetEventNameForAttr(aName), aValue->GetStringValue());
    } else if (aNotify && aName == nsGkAtoms::spellcheck) {
      SyncEditorsOnSubtree(this);
    } else if (aName == nsGkAtoms::popover) {
      nsContentUtils::AddScriptRunner(
          NewRunnableMethod("nsGenericHTMLElement::AfterSetPopoverAttr"this,
                            &nsGenericHTMLElement::AfterSetPopoverAttr));
    } else if (aName == nsGkAtoms::popovertarget) {
      ClearExplicitlySetAttrElement(aName);
    } else if (aName == nsGkAtoms::dir) {
      auto dir = Directionality::Ltr;
      // A boolean tracking whether we need to recompute our directionality.
      // This needs to happen after we update our internal "dir" attribute
      // state but before we call SetDirectionalityOnDescendants.
      bool recomputeDirectionality = false;
      ElementState dirStates;
      if (aValue && aValue->Type() == nsAttrValue::eEnum) {
        SetHasValidDir();
        dirStates |= ElementState::HAS_DIR_ATTR;
        auto dirValue = Directionality(aValue->GetEnumValue());
        if (dirValue == Directionality::Auto) {
          dirStates |= ElementState::HAS_DIR_ATTR_LIKE_AUTO;
        } else {
          dir = dirValue;
          SetDirectionality(dir, aNotify);
          if (dirValue == Directionality::Ltr) {
            dirStates |= ElementState::HAS_DIR_ATTR_LTR;
          } else {
            MOZ_ASSERT(dirValue == Directionality::Rtl);
            dirStates |= ElementState::HAS_DIR_ATTR_RTL;
          }
        }
      } else {
        if (aValue) {
          // We have a value, just not a valid one.
          dirStates |= ElementState::HAS_DIR_ATTR;
        }
        ClearHasValidDir();
        if (NodeInfo()->Equals(nsGkAtoms::bdi)) {
          dirStates |= ElementState::HAS_DIR_ATTR_LIKE_AUTO;
        } else {
          recomputeDirectionality = true;
        }
      }
      // Now figure out what's changed about our dir states.
      ElementState oldDirStates = State() & ElementState::DIR_ATTR_STATES;
      ElementState changedStates = dirStates ^ oldDirStates;
      if (!changedStates.IsEmpty()) {
        ToggleStates(changedStates, aNotify);
      }
      if (recomputeDirectionality) {
        dir = RecomputeDirectionality(this, aNotify);
      }
      SetDirectionalityOnDescendants(this, dir, aNotify);
    } else if (aName == nsGkAtoms::contenteditable) {
      const auto IsEditableExceptInherit = [](const nsAttrValue& aValue) {
        return aValue.Equals(EmptyString(), eCaseMatters) ||
               aValue.Equals(u"true"_ns, eIgnoreCase) ||
               (StaticPrefs::
                    dom_element_contenteditable_plaintext_only_enabled() &&
                aValue.Equals(u"plaintext-only"_ns, eIgnoreCase));
      };
      // FYI: Now, both HasContentEditableAttrTrueOrPlainTextOnly() and
      // HasContentEditableAttrFalse() return true.  Therefore, we need to clear
      // one of them or both of them.
      int32_t editableCountDelta = 0;
      if (aOldValue && IsEditableExceptInherit(*aOldValue)) {
        editableCountDelta = -1;
        ClearHasContentEditableAttrTrueOrPlainTextOnly();
      }
      if (!aValue) {
        ClearMayHaveContentEditableAttr();
      } else if (IsEditableExceptInherit(*aValue)) {
        ++editableCountDelta;
        SetHasContentEditableAttrTrueOrPlainTextOnly();
      }
      ChangeEditableState(editableCountDelta);
    } else if (aName == nsGkAtoms::accesskey) {
      if (aValue && !aValue->Equals(u""_ns, eIgnoreCase)) {
        SetFlags(NODE_HAS_ACCESSKEY);
        RegUnRegAccessKey(true);
      }
    } else if (aName == nsGkAtoms::inert) {
      if (aValue) {
        AddStates(ElementState::INERT);
      } else {
        RemoveStates(ElementState::INERT);
      }
    } else if (aName == nsGkAtoms::name) {
      if (aValue && !aValue->Equals(u""_ns, eIgnoreCase)) {
        // This may not be quite right because we can have subclass code run
        // before here. But in practice subclasses don't care about this flag,
        // and in particular selector matching does not care.  Otherwise we'd
        // want to handle it like we handle id attributes (in PreIdMaybeChange
        // and PostIdMaybeChange).
        SetHasName();
        if (CanHaveName(NodeInfo()->NameAtom())) {
          AddToNameTable(aValue->GetAtomValue());
        }
      }
    } else if (aName == nsGkAtoms::inputmode ||
               aName == nsGkAtoms::enterkeyhint) {
      if (nsFocusManager::GetFocusedElementStatic() == this) {
        if (const nsPresContext* presContext =
                GetPresContext(eForComposedDoc)) {
          IMEContentObserver* observer =
              IMEStateManager::GetActiveContentObserver();
          if (observer && observer->IsObserving(*presContext, this)) {
            if (const RefPtr<EditorBase> editorBase = GetExtantEditor()) {
              IMEState newState;
              editorBase->GetPreferredIMEState(&newState);
              OwningNonNull<nsGenericHTMLElement> kungFuDeathGrip(*this);
              IMEStateManager::UpdateIMEState(
                  newState, kungFuDeathGrip, *editorBase,
                  {IMEStateManager::UpdateIMEStateOption::ForceUpdate,
                   IMEStateManager::UpdateIMEStateOption::
                       DontCommitComposition});
            }
          }
        }
      }
    }

    // The nonce will be copied over to an internal slot and cleared from the
    // Element within BindToTree to avoid CSS Selector nonce exfiltration if
    // the CSP list contains a header-delivered CSP.
    if (nsGkAtoms::nonce == aName) {
      if (aValue) {
        SetNonce(aValue->GetStringValue());
        if (OwnerDoc()->GetHasCSPDeliveredThroughHeader()) {
          SetFlags(NODE_HAS_NONCE_AND_HEADER_CSP);
        }
      } else {
        RemoveNonce();
      }
    }
  }

  return nsGenericHTMLElementBase::AfterSetAttr(
      aNamespaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
}

EventListenerManager* nsGenericHTMLElement::GetEventListenerManagerForAttr(
    nsAtom* aAttrName, bool* aDefer) {
  // Attributes on the body and frameset tags get set on the global object
  if ((mNodeInfo->Equals(nsGkAtoms::body) ||
       mNodeInfo->Equals(nsGkAtoms::frameset)) &&
      // We only forward some event attributes from body/frameset to window
      (0
#define EVENT(name_, id_, type_, struct_) /* nothing */
#define FORWARDED_EVENT(name_, id_, type_, struct_) \
  || nsGkAtoms::on##name_ == aAttrName
#define WINDOW_EVENT FORWARDED_EVENT
#include "mozilla/EventNameList.h"  // IWYU pragma: keep
#undef WINDOW_EVENT
#undef FORWARDED_EVENT
#undef EVENT
       )) {
    nsPIDOMWindowInner* win;

    // If we have a document, and it has a window, add the event
    // listener on the window (the inner window). If not, proceed as
    // normal.
    // XXXbz sXBL/XBL2 issue: should we instead use GetComposedDoc() here,
    // override BindToTree for those classes and munge event listeners there?
    Document* document = OwnerDoc();

    *aDefer = false;
    if ((win = document->GetInnerWindow())) {
      nsCOMPtr<EventTarget> piTarget(do_QueryInterface(win));

      return piTarget->GetOrCreateListenerManager();
    }

    return nullptr;
  }

  return nsGenericHTMLElementBase::GetEventListenerManagerForAttr(aAttrName,
                                                                  aDefer);
}

#define EVENT(name_, id_, type_, struct_) /* nothing; handled by nsINode */
#define FORWARDED_EVENT(name_, id_, type_, struct_)                       \
  EventHandlerNonNull* nsGenericHTMLElement::GetOn##name_() {             \
    if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {      \
      /* XXXbz note to self: add tests for this! */                       \
      if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) {       \
        nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);  \
        return globalWin->GetOn##name_();                                 \
      }                                                                   \
      return nullptr;                                                     \
    }                                                                     \
                                                                          \
    return nsINode::GetOn##name_();                                       \
  }                                                                       \
  void nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler) { \
    if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {      \
      nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();             \
      if (!win) {                                                         \
        return;                                                           \
      }                                                                   \
                                                                          \
      nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);    \
      return globalWin->SetOn##name_(handler);                            \
    }                                                                     \
                                                                          \
    return nsINode::SetOn##name_(handler);                                \
  }
#define ERROR_EVENT(name_, id_, type_, struct_)                                \
  already_AddRefed<EventHandlerNonNull> nsGenericHTMLElement::GetOn##name_() { \
    if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {           \
      /* XXXbz note to self: add tests for this! */                            \
      if (nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow()) {            \
        nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);       \
        OnErrorEventHandlerNonNull* errorHandler = globalWin->GetOn##name_();  \
        if (errorHandler) {                                                    \
          RefPtr<EventHandlerNonNull> handler =                                \
              new EventHandlerNonNull(errorHandler);                           \
          return handler.forget();                                             \
        }                                                                      \
      }                                                                        \
      return nullptr;                                                          \
    }                                                                          \
                                                                               \
    RefPtr<EventHandlerNonNull> handler = nsINode::GetOn##name_();             \
    return handler.forget();                                                   \
  }                                                                            \
  void nsGenericHTMLElement::SetOn##name_(EventHandlerNonNull* handler) {      \
    if (IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {           \
      nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();                  \
      if (!win) {                                                              \
        return;                                                                \
      }                                                                        \
                                                                               \
      nsGlobalWindowInner* globalWin = nsGlobalWindowInner::Cast(win);         \
      RefPtr<OnErrorEventHandlerNonNull> errorHandler;                         \
      if (handler) {                                                           \
        errorHandler = new OnErrorEventHandlerNonNull(handler);                \
      }                                                                        \
      return globalWin->SetOn##name_(errorHandler);                            \
    }                                                                          \
                                                                               \
    return nsINode::SetOn##name_(handler);                                     \
  }
#include "mozilla/EventNameList.h"  // IWYU pragma: keep
#undef ERROR_EVENT
#undef FORWARDED_EVENT
#undef EVENT

void nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const {
  OwnerDoc()->GetBaseTarget(aBaseTarget);
}

//----------------------------------------------------------------------

bool nsGenericHTMLElement::ParseAttribute(int32_t aNamespaceID,
                                          nsAtom* aAttribute,
                                          const nsAString& aValue,
                                          nsIPrincipal* aMaybeScriptedPrincipal,
                                          nsAttrValue& aResult) {
  if (aNamespaceID == kNameSpaceID_None) {
    if (aAttribute == nsGkAtoms::dir) {
      return aResult.ParseEnumValue(aValue, kDirTable, false);
    }

    if (aAttribute == nsGkAtoms::popover) {
      return aResult.ParseEnumValue(aValue, kPopoverTable, false,
                                    kPopoverTableInvalidValueDefault);
    }

    if (aAttribute == nsGkAtoms::tabindex) {
      return aResult.ParseIntValue(aValue);
    }

    if (aAttribute == nsGkAtoms::referrerpolicy) {
      return ParseReferrerAttribute(aValue, aResult);
    }

    if (aAttribute == nsGkAtoms::name) {
      // Store name as an atom.  name="" means that the element has no name,
      // not that it has an empty string as the name.
      if (aValue.IsEmpty()) {
        return false;
      }
      aResult.ParseAtom(aValue);
      return true;
    }

    if (aAttribute == nsGkAtoms::contenteditable ||
        aAttribute == nsGkAtoms::translate) {
      aResult.ParseAtom(aValue);
      return true;
    }

    if (aAttribute == nsGkAtoms::rel) {
      aResult.ParseAtomArray(aValue);
      return true;
    }

    if (aAttribute == nsGkAtoms::inputmode) {
      return aResult.ParseEnumValue(aValue, kInputmodeTable, false);
    }

    if (aAttribute == nsGkAtoms::enterkeyhint) {
      return aResult.ParseEnumValue(aValue, kEnterKeyHintTable, false);
    }

    if (aAttribute == nsGkAtoms::autocapitalize) {
      return aResult.ParseEnumValue(aValue, kAutocapitalizeTable, false);
    }
  }

  return nsGenericHTMLElementBase::ParseAttribute(
      aNamespaceID, aAttribute, aValue, aMaybeScriptedPrincipal, aResult);
}

bool nsGenericHTMLElement::ParseBackgroundAttribute(int32_t aNamespaceID,
                                                    nsAtom* aAttribute,
                                                    const nsAString& aValue,
                                                    nsAttrValue& aResult) {
  if (aNamespaceID == kNameSpaceID_None &&
      aAttribute == nsGkAtoms::background && !aValue.IsEmpty()) {
    // Resolve url to an absolute url
    Document* doc = OwnerDoc();
    nsCOMPtr<nsIURI> uri;
    nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
        getter_AddRefs(uri), aValue, doc, GetBaseURI());
    if (NS_FAILED(rv)) {
      return false;
    }
    aResult.SetTo(uri, &aValue);
    return true;
  }

  return false;
}

bool nsGenericHTMLElement::IsAttributeMapped(const nsAtom* aAttribute) const {
  static const MappedAttributeEntry* const map[] = {sCommonAttributeMap};

  return FindAttributeDependence(aAttribute, map);
}

nsMapRuleToAttributesFunc nsGenericHTMLElement::GetAttributeMappingFunction()
    const {
  return &MapCommonAttributesInto;
}

static constexpr nsAttrValue::EnumTable kDivAlignTable[] = {
    {"left", StyleTextAlign::MozLeft},
    {"right", StyleTextAlign::MozRight},
    {"center", StyleTextAlign::MozCenter},
    {"middle", StyleTextAlign::MozCenter},
    {"justify", StyleTextAlign::Justify},
    {nullptr, 0}};

static constexpr nsAttrValue::EnumTable kFrameborderTable[] = {
    {"yes", FrameBorderProperty::Yes},
    {"no", FrameBorderProperty::No},
    {"1", FrameBorderProperty::One},
    {"0", FrameBorderProperty::Zero},
    {nullptr, 0}};

// TODO(emilio): Nobody uses the parsed attribute here.
static constexpr nsAttrValue::EnumTable kScrollingTable[] = {
    {"yes", ScrollingAttribute::Yes},
    {"no", ScrollingAttribute::No},
    {"on", ScrollingAttribute::On},
    {"off", ScrollingAttribute::Off},
    {"scroll", ScrollingAttribute::Scroll},
    {"noscroll", ScrollingAttribute::Noscroll},
    {"auto", ScrollingAttribute::Auto},
    {nullptr, 0}};

static constexpr nsAttrValue::EnumTable kTableVAlignTable[] = {
    {"top", StyleVerticalAlignKeyword::Top},
    {"middle", StyleVerticalAlignKeyword::Middle},
    {"bottom", StyleVerticalAlignKeyword::Bottom},
    {"baseline", StyleVerticalAlignKeyword::Baseline},
    {nullptr, 0}};

bool nsGenericHTMLElement::ParseAlignValue(const nsAString& aString,
                                           nsAttrValue& aResult) {
  static constexpr nsAttrValue::EnumTable kAlignTable[] = {
      {"left", StyleTextAlign::Left},
      {"right", StyleTextAlign::Right},

      {"top", StyleVerticalAlignKeyword::Top},
      {"middle", StyleVerticalAlignKeyword::MozMiddleWithBaseline},

      // Intentionally not bottom.
      {"bottom", StyleVerticalAlignKeyword::Baseline},

      {"center", StyleVerticalAlignKeyword::MozMiddleWithBaseline},
      {"baseline", StyleVerticalAlignKeyword::Baseline},

      {"texttop", StyleVerticalAlignKeyword::TextTop},
      {"absmiddle", StyleVerticalAlignKeyword::Middle},
      {"abscenter", StyleVerticalAlignKeyword::Middle},
      {"absbottom", StyleVerticalAlignKeyword::Bottom},
      {nullptr, 0}};

  static_assert(uint8_t(StyleTextAlign::Left) !=
                    uint8_t(StyleVerticalAlignKeyword::Top) &&
                uint8_t(StyleTextAlign::Left) !=
                    uint8_t(StyleVerticalAlignKeyword::MozMiddleWithBaseline) &&
                uint8_t(StyleTextAlign::Left) !=
                    uint8_t(StyleVerticalAlignKeyword::Baseline) &&
                uint8_t(StyleTextAlign::Left) !=
                    uint8_t(StyleVerticalAlignKeyword::TextTop) &&
                uint8_t(StyleTextAlign::Left) !=
                    uint8_t(StyleVerticalAlignKeyword::Middle) &&
                uint8_t(StyleTextAlign::Left) !=
                    uint8_t(StyleVerticalAlignKeyword::Bottom));

  static_assert(uint8_t(StyleTextAlign::Right) !=
                    uint8_t(StyleVerticalAlignKeyword::Top) &&
                uint8_t(StyleTextAlign::Right) !=
                    uint8_t(StyleVerticalAlignKeyword::MozMiddleWithBaseline) &&
                uint8_t(StyleTextAlign::Right) !=
                    uint8_t(StyleVerticalAlignKeyword::Baseline) &&
                uint8_t(StyleTextAlign::Right) !=
                    uint8_t(StyleVerticalAlignKeyword::TextTop) &&
                uint8_t(StyleTextAlign::Right) !=
                    uint8_t(StyleVerticalAlignKeyword::Middle) &&
                uint8_t(StyleTextAlign::Right) !=
                    uint8_t(StyleVerticalAlignKeyword::Bottom));

  return aResult.ParseEnumValue(aString, kAlignTable, false);
}

//----------------------------------------

static constexpr nsAttrValue::EnumTable kTableHAlignTable[] = {
    {"left", StyleTextAlign::Left},
    {"right", StyleTextAlign::Right},
    {"center", StyleTextAlign::Center},
    {"justify", StyleTextAlign::Justify},
    {nullptr, 0}};

bool nsGenericHTMLElement::ParseTableHAlignValue(const nsAString& aString,
                                                 nsAttrValue& aResult) {
  return aResult.ParseEnumValue(aString, kTableHAlignTable, false);
}

//----------------------------------------

// This table is used for td, th, tr, col, thead, tbody and tfoot.
static constexpr nsAttrValue::EnumTable kTableCellHAlignTable[] = {
    {"left", StyleTextAlign::MozLeft},
    {"right", StyleTextAlign::MozRight},
    {"center", StyleTextAlign::MozCenter},
    {"justify", StyleTextAlign::Justify},
    {"middle", StyleTextAlign::MozCenter},
    {"absmiddle", StyleTextAlign::Center},
    {nullptr, 0}};

bool nsGenericHTMLElement::ParseTableCellHAlignValue(const nsAString& aString,
                                                     nsAttrValue& aResult) {
  return aResult.ParseEnumValue(aString, kTableCellHAlignTable, false);
}

//----------------------------------------

bool nsGenericHTMLElement::ParseTableVAlignValue(const nsAString& aString,
                                                 nsAttrValue& aResult) {
  return aResult.ParseEnumValue(aString, kTableVAlignTable, false);
}

bool nsGenericHTMLElement::ParseDivAlignValue(const nsAString& aString,
                                              nsAttrValue& aResult) {
  return aResult.ParseEnumValue(aString, kDivAlignTable, false);
}

bool nsGenericHTMLElement::ParseImageAttribute(nsAtom* aAttribute,
                                               const nsAString& aString,
                                               nsAttrValue& aResult) {
  if (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
      aAttribute == nsGkAtoms::hspace || aAttribute == nsGkAtoms::vspace) {
    return aResult.ParseHTMLDimension(aString);
  }
  if (aAttribute == nsGkAtoms::border) {
    return aResult.ParseNonNegativeIntValue(aString);
  }
  return false;
}

bool nsGenericHTMLElement::ParseReferrerAttribute(const nsAString& aString,
                                                  nsAttrValue& aResult) {
  using mozilla::dom::ReferrerInfo;
  static constexpr nsAttrValue::EnumTable kReferrerPolicyTable[] = {
      {GetEnumString(ReferrerPolicy::No_referrer).get(),
       static_cast<int16_t>(ReferrerPolicy::No_referrer)},
      {GetEnumString(ReferrerPolicy::Origin).get(),
       static_cast<int16_t>(ReferrerPolicy::Origin)},
      {GetEnumString(ReferrerPolicy::Origin_when_cross_origin).get(),
       static_cast<int16_t>(ReferrerPolicy::Origin_when_cross_origin)},
      {GetEnumString(ReferrerPolicy::No_referrer_when_downgrade).get(),
       static_cast<int16_t>(ReferrerPolicy::No_referrer_when_downgrade)},
      {GetEnumString(ReferrerPolicy::Unsafe_url).get(),
       static_cast<int16_t>(ReferrerPolicy::Unsafe_url)},
      {GetEnumString(ReferrerPolicy::Strict_origin).get(),
       static_cast<int16_t>(ReferrerPolicy::Strict_origin)},
      {GetEnumString(ReferrerPolicy::Same_origin).get(),
       static_cast<int16_t>(ReferrerPolicy::Same_origin)},
      {GetEnumString(ReferrerPolicy::Strict_origin_when_cross_origin).get(),
       static_cast<int16_t>(ReferrerPolicy::Strict_origin_when_cross_origin)},
      {nullptr, ReferrerPolicy::_empty}};
  return aResult.ParseEnumValue(aString, kReferrerPolicyTable, false);
}

bool nsGenericHTMLElement::ParseFrameborderValue(const nsAString& aString,
                                                 nsAttrValue& aResult) {
  return aResult.ParseEnumValue(aString, kFrameborderTable, false);
}

bool nsGenericHTMLElement::ParseScrollingValue(const nsAString& aString,
                                               nsAttrValue& aResult) {
  return aResult.ParseEnumValue(aString, kScrollingTable, false);
}

static inline void MapLangAttributeInto(MappedDeclarationsBuilder& aBuilder) {
  const nsAttrValue* langValue = aBuilder.GetAttr(nsGkAtoms::lang);
  if (!langValue) {
    return;
  }
  MOZ_ASSERT(langValue->Type() == nsAttrValue::eAtom);
  aBuilder.SetIdentAtomValueIfUnset(eCSSProperty__x_lang,
                                    langValue->GetAtomValue());
  if (!aBuilder.PropertyIsSet(eCSSProperty_text_emphasis_position)) {
    const nsAtom* lang = langValue->GetAtomValue();
    if (nsStyleUtil::MatchesLanguagePrefix(lang, u"zh")) {
      aBuilder.SetKeywordValue(eCSSProperty_text_emphasis_position,
                               StyleTextEmphasisPosition::UNDER._0);
    } else if (nsStyleUtil::MatchesLanguagePrefix(lang, u"ja") ||
               nsStyleUtil::MatchesLanguagePrefix(lang, u"mn")) {
      // This branch is currently no part of the spec.
      // See bug 1040668 comment 69 and comment 75.
      aBuilder.SetKeywordValue(eCSSProperty_text_emphasis_position,
                               StyleTextEmphasisPosition::OVER._0);
    }
  }
}

/**
 * Handle attributes common to all html elements
 */

void nsGenericHTMLElement::MapCommonAttributesIntoExceptHidden(
    MappedDeclarationsBuilder& aBuilder) {
  MapLangAttributeInto(aBuilder);
}

void nsGenericHTMLElement::MapCommonAttributesInto(
    MappedDeclarationsBuilder& aBuilder) {
  MapCommonAttributesIntoExceptHidden(aBuilder);
  if (!aBuilder.PropertyIsSet(eCSSProperty_display)) {
    if (aBuilder.GetAttr(nsGkAtoms::hidden)) {
      aBuilder.SetKeywordValue(eCSSProperty_display, StyleDisplay::None._0);
    }
  }
}

/* static */
const nsGenericHTMLElement::MappedAttributeEntry
    nsGenericHTMLElement::sCommonAttributeMap[] = {{nsGkAtoms::contenteditable},
                                                   {nsGkAtoms::lang},
                                                   {nsGkAtoms::hidden},
                                                   {nullptr}};

/* static */
const Element::MappedAttributeEntry
    nsGenericHTMLElement::sImageMarginSizeAttributeMap[] = {{nsGkAtoms::width},
                                                            {nsGkAtoms::height},
                                                            {nsGkAtoms::hspace},
                                                            {nsGkAtoms::vspace},
                                                            {nullptr}};

/* static */
const Element::MappedAttributeEntry
    nsGenericHTMLElement::sImageAlignAttributeMap[] = {{nsGkAtoms::align},
                                                       {nullptr}};

/* static */
const Element::MappedAttributeEntry
    nsGenericHTMLElement::sDivAlignAttributeMap[] = {{nsGkAtoms::align},
                                                     {nullptr}};

/* static */
const Element::MappedAttributeEntry
    nsGenericHTMLElement::sImageBorderAttributeMap[] = {{nsGkAtoms::border},
                                                        {nullptr}};

/* static */
const Element::MappedAttributeEntry
    nsGenericHTMLElement::sBackgroundAttributeMap[] = {
        {nsGkAtoms::background}, {nsGkAtoms::bgcolor}, {nullptr}};

/* static */
const Element::MappedAttributeEntry
    nsGenericHTMLElement::sBackgroundColorAttributeMap[] = {
        {nsGkAtoms::bgcolor}, {nullptr}};

void nsGenericHTMLElement::MapImageAlignAttributeInto(
    MappedDeclarationsBuilder& aBuilder) {
  const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::align);
  if (value && value->Type() == nsAttrValue::eEnum) {
    int32_t align = value->GetEnumValue();
    if (!aBuilder.PropertyIsSet(eCSSProperty_float)) {
      if (align == uint8_t(StyleTextAlign::Left)) {
        aBuilder.SetKeywordValue(eCSSProperty_float, StyleFloat::Left);
      } else if (align == uint8_t(StyleTextAlign::Right)) {
        aBuilder.SetKeywordValue(eCSSProperty_float, StyleFloat::Right);
      }
    }
    if (!aBuilder.PropertyIsSet(eCSSProperty_vertical_align)) {
      switch (align) {
        case uint8_t(StyleTextAlign::Left):
        case uint8_t(StyleTextAlign::Right):
          break;
        default:
          aBuilder.SetKeywordValue(eCSSProperty_vertical_align, align);
          break;
      }
    }
  }
}

void nsGenericHTMLElement::MapDivAlignAttributeInto(
    MappedDeclarationsBuilder& aBuilder) {
  if (!aBuilder.PropertyIsSet(eCSSProperty_text_align)) {
    // align: enum
    const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::align);
    if (value && value->Type() == nsAttrValue::eEnum)
      aBuilder.SetKeywordValue(eCSSProperty_text_align, value->GetEnumValue());
  }
}

void nsGenericHTMLElement::MapVAlignAttributeInto(
    MappedDeclarationsBuilder& aBuilder) {
  if (!aBuilder.PropertyIsSet(eCSSProperty_vertical_align)) {
    // align: enum
    const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::valign);
    if (value && value->Type() == nsAttrValue::eEnum)
      aBuilder.SetKeywordValue(eCSSProperty_vertical_align,
                               value->GetEnumValue());
  }
}

void nsGenericHTMLElement::MapDimensionAttributeInto(
    MappedDeclarationsBuilder& aBuilder, nsCSSPropertyID aProp,
    const nsAttrValue& aValue) {
  MOZ_ASSERT(!aBuilder.PropertyIsSet(aProp),
             "Why mapping the same property twice?");
  if (aValue.Type() == nsAttrValue::eInteger) {
    return aBuilder.SetPixelValue(aProp, aValue.GetIntegerValue());
  }
  if (aValue.Type() == nsAttrValue::ePercent) {
    return aBuilder.SetPercentValue(aProp, aValue.GetPercentValue());
  }
  if (aValue.Type() == nsAttrValue::eDoubleValue) {
    return aBuilder.SetPixelValue(aProp, aValue.GetDoubleValue());
  }
}

void nsGenericHTMLElement::MapImageMarginAttributeInto(
    MappedDeclarationsBuilder& aBuilder) {
  // hspace: value
  if (const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::hspace)) {
    MapDimensionAttributeInto(aBuilder, eCSSProperty_margin_left, *value);
    MapDimensionAttributeInto(aBuilder, eCSSProperty_margin_right, *value);
  }

  // vspace: value
  if (const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::vspace)) {
    MapDimensionAttributeInto(aBuilder, eCSSProperty_margin_top, *value);
    MapDimensionAttributeInto(aBuilder, eCSSProperty_margin_bottom, *value);
  }
}

void nsGenericHTMLElement::MapWidthAttributeInto(
    MappedDeclarationsBuilder& aBuilder) {
  if (const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::width)) {
    MapDimensionAttributeInto(aBuilder, eCSSProperty_width, *value);
  }
}

void nsGenericHTMLElement::MapHeightAttributeInto(
    MappedDeclarationsBuilder& aBuilder) {
  if (const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::height)) {
    MapDimensionAttributeInto(aBuilder, eCSSProperty_height, *value);
  }
}

void nsGenericHTMLElement::DoMapAspectRatio(
    const nsAttrValue& aWidth, const nsAttrValue& aHeight,
    MappedDeclarationsBuilder& aBuilder) {
  Maybe<double> w;
  if (aWidth.Type() == nsAttrValue::eInteger) {
    w.emplace(aWidth.GetIntegerValue());
  } else if (aWidth.Type() == nsAttrValue::eDoubleValue) {
    w.emplace(aWidth.GetDoubleValue());
  }

  Maybe<double> h;
  if (aHeight.Type() == nsAttrValue::eInteger) {
    h.emplace(aHeight.GetIntegerValue());
  } else if (aHeight.Type() == nsAttrValue::eDoubleValue) {
    h.emplace(aHeight.GetDoubleValue());
  }

  if (w && h) {
    aBuilder.SetAspectRatio(*w, *h);
  }
}

void nsGenericHTMLElement::MapImageSizeAttributesInto(
    MappedDeclarationsBuilder& aBuilder, MapAspectRatio aMapAspectRatio) {
  auto* width = aBuilder.GetAttr(nsGkAtoms::width);
  auto* height = aBuilder.GetAttr(nsGkAtoms::height);
  if (width) {
    MapDimensionAttributeInto(aBuilder, eCSSProperty_width, *width);
  }
  if (height) {
    MapDimensionAttributeInto(aBuilder, eCSSProperty_height, *height);
  }
  if (aMapAspectRatio == MapAspectRatio::Yes && width && height) {
    DoMapAspectRatio(*width, *height, aBuilder);
  }
}

void nsGenericHTMLElement::MapAspectRatioInto(
    MappedDeclarationsBuilder& aBuilder) {
  auto* width = aBuilder.GetAttr(nsGkAtoms::width);
  auto* height = aBuilder.GetAttr(nsGkAtoms::height);
  if (width && height) {
    DoMapAspectRatio(*width, *height, aBuilder);
  }
}

void nsGenericHTMLElement::MapImageBorderAttributeInto(
    MappedDeclarationsBuilder& aBuilder) {
  // border: pixels
  const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::border);
  if (!value) return;

  nscoord val = 0;
  if (value->Type() == nsAttrValue::eInteger) val = value->GetIntegerValue();

  aBuilder.SetPixelValueIfUnset(eCSSProperty_border_top_width, (float)val);
  aBuilder.SetPixelValueIfUnset(eCSSProperty_border_right_width, (float)val);
  aBuilder.SetPixelValueIfUnset(eCSSProperty_border_bottom_width, (float)val);
  aBuilder.SetPixelValueIfUnset(eCSSProperty_border_left_width, (float)val);

  aBuilder.SetKeywordValueIfUnset(eCSSProperty_border_top_style,
                                  StyleBorderStyle::Solid);
  aBuilder.SetKeywordValueIfUnset(eCSSProperty_border_right_style,
                                  StyleBorderStyle::Solid);
  aBuilder.SetKeywordValueIfUnset(eCSSProperty_border_bottom_style,
                                  StyleBorderStyle::Solid);
  aBuilder.SetKeywordValueIfUnset(eCSSProperty_border_left_style,
                                  StyleBorderStyle::Solid);

  aBuilder.SetCurrentColorIfUnset(eCSSProperty_border_top_color);
  aBuilder.SetCurrentColorIfUnset(eCSSProperty_border_right_color);
  aBuilder.SetCurrentColorIfUnset(eCSSProperty_border_bottom_color);
  aBuilder.SetCurrentColorIfUnset(eCSSProperty_border_left_color);
}

void nsGenericHTMLElement::MapBackgroundInto(
    MappedDeclarationsBuilder& aBuilder) {
  if (!aBuilder.PropertyIsSet(eCSSProperty_background_image)) {
    // background
    if (const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::background)) {
      aBuilder.SetBackgroundImage(*value);
    }
  }
}

void nsGenericHTMLElement::MapBGColorInto(MappedDeclarationsBuilder& aBuilder) {
  if (aBuilder.PropertyIsSet(eCSSProperty_background_color)) {
    return;
  }
  const nsAttrValue* value = aBuilder.GetAttr(nsGkAtoms::bgcolor);
  nscolor color;
  if (value && value->GetColorValue(color)) {
    aBuilder.SetColorValue(eCSSProperty_background_color, color);
  }
}

void nsGenericHTMLElement::MapBackgroundAttributesInto(
    MappedDeclarationsBuilder& aBuilder) {
  MapBackgroundInto(aBuilder);
  MapBGColorInto(aBuilder);
}

//----------------------------------------------------------------------

int32_t nsGenericHTMLElement::GetIntAttr(nsAtom* aAttr,
                                         int32_t aDefault) const {
  const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
  if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
    return attrVal->GetIntegerValue();
  }
  return aDefault;
}

nsresult nsGenericHTMLElement::SetIntAttr(nsAtom* aAttr, int32_t aValue) {
  nsAutoString value;
  value.AppendInt(aValue);

  return SetAttr(kNameSpaceID_None, aAttr, value, true);
}

uint32_t nsGenericHTMLElement::GetUnsignedIntAttr(nsAtom* aAttr,
                                                  uint32_t aDefault) const {
  const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
  if (!attrVal || attrVal->Type() != nsAttrValue::eInteger) {
    return aDefault;
  }

  return attrVal->GetIntegerValue();
}

uint32_t nsGenericHTMLElement::GetDimensionAttrAsUnsignedInt(
    nsAtom* aAttr, uint32_t aDefault) const {
  const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
  if (!attrVal) {
    return aDefault;
  }

  if (attrVal->Type() == nsAttrValue::eInteger) {
    return attrVal->GetIntegerValue();
  }

  if (attrVal->Type() == nsAttrValue::ePercent) {
    // This is a nasty hack.  When we parsed the value, we stored it as an
    // ePercent, not eInteger, because there was a '%' after it in the string.
    // But the spec says to basically re-parse the string as an integer.
    // Luckily, we can just return the value we have stored.  But
    // GetPercentValue() divides it by 100, so we need to multiply it back.
    return uint32_t(attrVal->GetPercentValue() * 100.0f);
  }

  if (attrVal->Type() == nsAttrValue::eDoubleValue) {
    return uint32_t(attrVal->GetDoubleValue());
  }

  // Unfortunately, the set of values that are valid dimensions is not a
  // superset of values that are valid unsigned ints.  In particular "+100" is
  // not a valid dimension, but should parse as the unsigned int "100".  So if
  // we got here and we don't have a valid dimension value, just try re-parsing
  // the string we have as an integer.
  nsAutoString val;
  attrVal->ToString(val);
  nsContentUtils::ParseHTMLIntegerResultFlags result;
  int32_t parsedInt = nsContentUtils::ParseHTMLInteger(val, &result);
  if ((result & nsContentUtils::eParseHTMLInteger_Error) || parsedInt < 0) {
    return aDefault;
  }

  return parsedInt;
}

void nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr, nsAtom* aBaseAttr,
                                      nsAString& aResult) const {
  nsCOMPtr<nsIURI> uri;
  const nsAttrValue* attr = GetURIAttr(aAttr, aBaseAttr, getter_AddRefs(uri));
  if (!attr) {
    aResult.Truncate();
    return;
  }
  if (!uri) {
    // Just return the attr value
    attr->ToString(aResult);
    return;
  }
  nsAutoCString spec;
  uri->GetSpec(spec);
  CopyUTF8toUTF16(spec, aResult);
}

void nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr, nsAtom* aBaseAttr,
                                      nsACString& aResult) const {
  nsCOMPtr<nsIURI> uri;
  const nsAttrValue* attr = GetURIAttr(aAttr, aBaseAttr, getter_AddRefs(uri));
  if (!attr) {
    aResult.Truncate();
    return;
  }
  if (!uri) {
    // Just return the attr value
    nsAutoString value;
    attr->ToString(value);
    CopyUTF16toUTF8(value, aResult);
    return;
  }
  uri->GetSpec(aResult);
}

const nsAttrValue* nsGenericHTMLElement::GetURIAttr(nsAtom* aAttr,
                                                    nsAtom* aBaseAttr,
                                                    nsIURI** aURI) const {
  *aURI = nullptr;

  const nsAttrValue* attr = mAttrs.GetAttr(aAttr);
  if (!attr) {
    return nullptr;
  }

  nsCOMPtr<nsIURI> baseURI = GetBaseURI();
  if (aBaseAttr) {
    nsAutoString baseAttrValue;
    if (GetAttr(aBaseAttr, baseAttrValue)) {
      nsCOMPtr<nsIURI> baseAttrURI;
      nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
          getter_AddRefs(baseAttrURI), baseAttrValue, OwnerDoc(), baseURI);
      if (NS_FAILED(rv)) {
        return attr;
      }
      baseURI.swap(baseAttrURI);
    }
  }

  // Don't care about return value.  If it fails, we still want to
  // return true, and *aURI will be null.
  nsContentUtils::NewURIWithDocumentCharset(aURI, attr->GetStringValue(),
                                            OwnerDoc(), baseURI);
  return attr;
}

bool nsGenericHTMLElement::IsContentEditable() const {
  for (const auto* element : InclusiveAncestorsOfType<nsGenericHTMLElement>()) {
--> --------------------

--> maximum size reached

--> --------------------

98%


¤ Dauer der Verarbeitung: 0.65 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.