tor-browser

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

Element.cpp (205365B)


      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 /*
      8 * Base class for all element classes; this provides an implementation
      9 * of DOM Core's Element, implements nsIContent, provides
     10 * utility methods for subclasses, and so forth.
     11 */
     12 
     13 #include "mozilla/dom/Element.h"
     14 
     15 #include <inttypes.h>
     16 
     17 #include <cstddef>
     18 #include <utility>
     19 
     20 #include "DOMMatrix.h"
     21 #include "ExpandedPrincipal.h"
     22 #include "PresShellInlines.h"
     23 #include "jsapi.h"
     24 #include "mozAutoDocUpdate.h"
     25 #include "mozilla/AnimationComparator.h"
     26 #include "mozilla/AnimationTarget.h"
     27 #include "mozilla/AsyncEventDispatcher.h"
     28 #include "mozilla/CORSMode.h"
     29 #include "mozilla/Components.h"
     30 #include "mozilla/ComputedStyle.h"
     31 #include "mozilla/ContentEvents.h"
     32 #include "mozilla/DebugOnly.h"
     33 #include "mozilla/DeclarationBlock.h"
     34 #include "mozilla/EditorBase.h"
     35 #include "mozilla/EffectCompositor.h"
     36 #include "mozilla/EffectSet.h"
     37 #include "mozilla/ElementAnimationData.h"
     38 #include "mozilla/ErrorResult.h"
     39 #include "mozilla/EventDispatcher.h"
     40 #include "mozilla/EventListenerManager.h"
     41 #include "mozilla/EventStateManager.h"
     42 #include "mozilla/FloatingPoint.h"
     43 #include "mozilla/FullscreenChange.h"
     44 #include "mozilla/HTMLEditor.h"
     45 #include "mozilla/Likely.h"
     46 #include "mozilla/LinkedList.h"
     47 #include "mozilla/LookAndFeel.h"
     48 #include "mozilla/MappedDeclarationsBuilder.h"
     49 #include "mozilla/Maybe.h"
     50 #include "mozilla/MouseEvents.h"
     51 #include "mozilla/PointerLockManager.h"
     52 #include "mozilla/PresShell.h"
     53 #include "mozilla/PresShellForwards.h"
     54 #include "mozilla/RefPtr.h"
     55 #include "mozilla/ReflowOutput.h"
     56 #include "mozilla/RelativeTo.h"
     57 #include "mozilla/ScrollContainerFrame.h"
     58 #include "mozilla/ScrollTypes.h"
     59 #include "mozilla/ServoStyleConsts.h"
     60 #include "mozilla/ServoStyleConstsInlines.h"
     61 #include "mozilla/SizeOfState.h"
     62 #include "mozilla/SourceLocation.h"
     63 #include "mozilla/StaticAnalysisFunctions.h"
     64 #include "mozilla/StaticPrefs_dom.h"
     65 #include "mozilla/StaticPrefs_full_screen_api.h"
     66 #include "mozilla/StaticString.h"
     67 #include "mozilla/TextControlElement.h"
     68 #include "mozilla/TextEditor.h"
     69 #include "mozilla/TextEvents.h"
     70 #include "mozilla/Try.h"
     71 #include "mozilla/dom/AnimatableBinding.h"
     72 #include "mozilla/dom/Animation.h"
     73 #include "mozilla/dom/Attr.h"
     74 #include "mozilla/dom/BindContext.h"
     75 #include "mozilla/dom/BindingDeclarations.h"
     76 #include "mozilla/dom/CSPViolationData.h"
     77 #include "mozilla/dom/ChildIterator.h"
     78 #include "mozilla/dom/CloseWatcher.h"
     79 #include "mozilla/dom/CustomElementRegistry.h"
     80 #include "mozilla/dom/DOMRect.h"
     81 #include "mozilla/dom/DirectionalityUtils.h"
     82 #include "mozilla/dom/Document.h"
     83 #include "mozilla/dom/DocumentFragment.h"
     84 #include "mozilla/dom/DocumentInlines.h"
     85 #include "mozilla/dom/DocumentTimeline.h"
     86 #include "mozilla/dom/ElementBinding.h"
     87 #include "mozilla/dom/ElementInlines.h"
     88 #include "mozilla/dom/Flex.h"
     89 #include "mozilla/dom/FragmentOrElement.h"
     90 #include "mozilla/dom/FromParser.h"
     91 #include "mozilla/dom/Grid.h"
     92 #include "mozilla/dom/HTMLDivElement.h"
     93 #include "mozilla/dom/HTMLElement.h"
     94 #include "mozilla/dom/HTMLParagraphElement.h"
     95 #include "mozilla/dom/HTMLPreElement.h"
     96 #include "mozilla/dom/HTMLSpanElement.h"
     97 #include "mozilla/dom/HTMLTableCellElement.h"
     98 #include "mozilla/dom/HTMLTemplateElement.h"
     99 #include "mozilla/dom/KeyframeAnimationOptionsBinding.h"
    100 #include "mozilla/dom/KeyframeEffect.h"
    101 #include "mozilla/dom/MouseEvent.h"
    102 #include "mozilla/dom/MouseEventBinding.h"
    103 #include "mozilla/dom/MutationObservers.h"
    104 #include "mozilla/dom/NodeInfo.h"
    105 #include "mozilla/dom/PointerEventHandler.h"
    106 #include "mozilla/dom/PolicyContainer.h"
    107 #include "mozilla/dom/Promise.h"
    108 #include "mozilla/dom/SVGElement.h"
    109 #include "mozilla/dom/Sanitizer.h"
    110 #include "mozilla/dom/ScriptLoader.h"
    111 #include "mozilla/dom/ShadowRoot.h"
    112 #include "mozilla/dom/StylePropertyMapReadOnly.h"
    113 #include "mozilla/dom/Text.h"
    114 #include "mozilla/dom/TreeIterator.h"
    115 #include "mozilla/dom/TrustedHTML.h"
    116 #include "mozilla/dom/TrustedTypeUtils.h"
    117 #include "mozilla/dom/TrustedTypesConstants.h"
    118 #include "mozilla/dom/UnbindContext.h"
    119 #include "mozilla/dom/ViewTransition.h"
    120 #include "mozilla/dom/WindowBinding.h"
    121 #include "mozilla/dom/XULCommandEvent.h"
    122 #include "mozilla/dom/nsCSPContext.h"
    123 #include "mozilla/dom/nsCSPUtils.h"
    124 #include "mozilla/gfx/BasePoint.h"
    125 #include "mozilla/gfx/BaseRect.h"
    126 #include "mozilla/gfx/BaseSize.h"
    127 #include "mozilla/gfx/Matrix.h"
    128 #include "mozilla/widget/Screen.h"
    129 #include "nsAtom.h"
    130 #include "nsAttrName.h"
    131 #include "nsAttrValueInlines.h"
    132 #include "nsAttrValueOrString.h"
    133 #include "nsBaseHashtable.h"
    134 #include "nsBlockFrame.h"
    135 #include "nsCOMPtr.h"
    136 #include "nsCSSPseudoElements.h"
    137 #include "nsCompatibility.h"
    138 #include "nsComputedDOMStyle.h"
    139 #include "nsContainerFrame.h"
    140 #include "nsContentList.h"
    141 #include "nsContentListDeclarations.h"
    142 #include "nsContentUtils.h"
    143 #include "nsCoord.h"
    144 #include "nsDOMAttributeMap.h"
    145 #include "nsDOMCSSAttrDeclaration.h"
    146 #include "nsDOMMutationObserver.h"
    147 #include "nsDOMString.h"
    148 #include "nsDOMStringMap.h"
    149 #include "nsDOMTokenList.h"
    150 #include "nsDocShell.h"
    151 #include "nsError.h"
    152 #include "nsFlexContainerFrame.h"
    153 #include "nsFocusManager.h"
    154 #include "nsFrameState.h"
    155 #include "nsGenericHTMLElement.h"
    156 #include "nsGkAtoms.h"
    157 #include "nsGridContainerFrame.h"
    158 #include "nsIAutoCompletePopup.h"
    159 #include "nsIBrowser.h"
    160 #include "nsIContentInlines.h"
    161 #include "nsIContentSecurityPolicy.h"
    162 #include "nsIDOMXULButtonElement.h"
    163 #include "nsIDOMXULContainerElement.h"
    164 #include "nsIDOMXULControlElement.h"
    165 #include "nsIDOMXULMenuListElement.h"
    166 #include "nsIDOMXULMultSelectCntrlEl.h"
    167 #include "nsIDOMXULRadioGroupElement.h"
    168 #include "nsIDOMXULRelatedElement.h"
    169 #include "nsIDOMXULSelectCntrlEl.h"
    170 #include "nsIDOMXULSelectCntrlItemEl.h"
    171 #include "nsIDocShell.h"
    172 #include "nsIFocusManager.h"
    173 #include "nsIFrame.h"
    174 #include "nsIFrameInlines.h"
    175 #include "nsIGlobalObject.h"
    176 #include "nsIIOService.h"
    177 #include "nsIInterfaceRequestor.h"
    178 #include "nsIMemoryReporter.h"
    179 #include "nsIMutationObserver.h"
    180 #include "nsIPrincipal.h"
    181 #include "nsIScriptError.h"
    182 #include "nsISpeculativeConnect.h"
    183 #include "nsISupports.h"
    184 #include "nsISupportsUtils.h"
    185 #include "nsIURI.h"
    186 #include "nsLayoutUtils.h"
    187 #include "nsLineBox.h"
    188 #include "nsLiteralString.h"
    189 #include "nsNameSpaceManager.h"
    190 #include "nsNodeInfoManager.h"
    191 #include "nsPIDOMWindow.h"
    192 #include "nsPoint.h"
    193 #include "nsPresContext.h"
    194 #include "nsQueryFrame.h"
    195 #include "nsRefPtrHashtable.h"
    196 #include "nsSize.h"
    197 #include "nsString.h"
    198 #include "nsStyleConsts.h"
    199 #include "nsStyleStruct.h"
    200 #include "nsStyledElement.h"
    201 #include "nsTArray.h"
    202 #include "nsTextNode.h"
    203 #include "nsThreadUtils.h"
    204 #include "nsWindowSizes.h"
    205 #include "nsXULElement.h"
    206 
    207 #ifdef DEBUG
    208 #  include "nsRange.h"
    209 #endif
    210 
    211 #ifdef ACCESSIBILITY
    212 #  include "nsAccessibilityService.h"
    213 #endif
    214 
    215 using mozilla::gfx::Matrix4x4;
    216 
    217 namespace mozilla::dom {
    218 
    219 // Verify sizes of nodes. We use a template rather than a direct static
    220 // assert so that the error message actually displays the sizes.
    221 // On 32 bit systems the actual allocated size varies a bit between
    222 // OSes/compilers.
    223 //
    224 // We need different numbers on certain build types to deal with the owning
    225 // thread pointer that comes with the non-threadsafe refcount on
    226 // nsIContent.
    227 #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
    228 #  define EXTRA_DOM_NODE_BYTES 8
    229 #else
    230 #  define EXTRA_DOM_NODE_BYTES 0
    231 #endif
    232 
    233 #define ASSERT_NODE_SIZE(type, opt_size_64, opt_size_32)              \
    234  template <int a, int sizeOn64, int sizeOn32>                        \
    235  struct Check##type##Size {                                          \
    236    static_assert((sizeof(void*) == 8 && a == sizeOn64) ||            \
    237                      (sizeof(void*) == 4 && a <= sizeOn32),          \
    238                  "DOM size changed");                                \
    239  };                                                                  \
    240  Check##type##Size<sizeof(type), opt_size_64 + EXTRA_DOM_NODE_BYTES, \
    241                    opt_size_32 + EXTRA_DOM_NODE_BYTES>               \
    242      g##type##CES;
    243 
    244 // Note that mozjemalloc uses a 16 byte quantum, so 64, 80 and 128 are
    245 // bucket sizes.
    246 ASSERT_NODE_SIZE(Element, 128, 80);
    247 ASSERT_NODE_SIZE(HTMLDivElement, 128, 80);
    248 ASSERT_NODE_SIZE(HTMLElement, 128, 80);
    249 ASSERT_NODE_SIZE(HTMLParagraphElement, 128, 80);
    250 ASSERT_NODE_SIZE(HTMLPreElement, 128, 80);
    251 ASSERT_NODE_SIZE(HTMLSpanElement, 128, 80);
    252 ASSERT_NODE_SIZE(HTMLTableCellElement, 128, 80);
    253 ASSERT_NODE_SIZE(Text, 120, 80);
    254 
    255 #undef ASSERT_NODE_SIZE
    256 #undef EXTRA_DOM_NODE_BYTES
    257 
    258 }  // namespace mozilla::dom
    259 
    260 nsAtom* nsIContent::DoGetID() const {
    261  MOZ_ASSERT(HasID(), "Unexpected call");
    262  MOZ_ASSERT(IsElement(), "Only elements can have IDs");
    263 
    264  return AsElement()->GetParsedAttr(nsGkAtoms::id)->GetAtomValue();
    265 }
    266 
    267 nsIFrame* nsIContent::GetPrimaryFrame(mozilla::FlushType aType) {
    268  Document* doc = GetComposedDoc();
    269  if (!doc) {
    270    return nullptr;
    271  }
    272 
    273  // Cause a flush, so we get up-to-date frame information.
    274  if (aType != mozilla::FlushType::None) {
    275    doc->FlushPendingNotifications(aType);
    276  }
    277 
    278  auto* frame = GetPrimaryFrame();
    279  if (!frame) {
    280    return nullptr;
    281  }
    282 
    283  if (aType == mozilla::FlushType::Layout) {
    284    frame->PresShell()->EnsureReflowIfFrameHasHiddenContent(frame);
    285    frame = GetPrimaryFrame();
    286  }
    287 
    288  return frame;
    289 }
    290 
    291 bool nsIContent::IsSelectable() const {
    292  if (!IsInComposedDoc() ||
    293      // Generated content is not selectable.
    294      IsGeneratedContentContainerForBefore() ||
    295      IsGeneratedContentContainerForAfter() ||
    296      // Fully invisible nodes like `Comment` should not be selectable.
    297      (!IsElement() && !IsText() && !IsShadowRoot())) {
    298    return false;
    299  }
    300  // If this is editable, this should be selectable even if `user-select` is set
    301  // to `none`.
    302  if (IsEditable()) {
    303    return true;
    304  }
    305  // ...and same if this is a text control.
    306  if (const auto* const textControlElement =
    307          mozilla::TextControlElement::FromNode(this)) {
    308    if (textControlElement->IsSingleLineTextControlOrTextArea()) {
    309      return true;
    310    }
    311  }
    312  // Otherwise, check `user-select` style with the layout if there is.
    313  for (const nsIContent* content = this; content;
    314       content = content->GetFlattenedTreeParent()) {
    315    // First, ask the primary frame.
    316    if (nsIFrame* const frame = content->GetPrimaryFrame()) {
    317      // FYI: This does the same checks which were done before this loop so that
    318      // return true for editable content or text control.
    319      return frame->IsSelectable();
    320    }
    321    if (!content->IsElement()) {
    322      // Okay, we're a `Text` or `ShadowRoot` in a `display:none` element. Let's
    323      // check the frame or style of the ancestors in the flattened tree.
    324      continue;
    325    }
    326    // Okay, we're an element whose `display` is `contents` or `none` or which
    327    // is in a `display:none` ancestors, we should check whether this element is
    328    // directly specified the `user-select` style and if it's not `auto`,
    329    // consider whether this is selectable not unselectable.
    330    const RefPtr<const mozilla::ComputedStyle> elementStyle =
    331        nsComputedDOMStyle::GetComputedStyleNoFlush(content->AsElement());
    332    if (elementStyle &&
    333        elementStyle->UserSelect() != mozilla::StyleUserSelect::Auto) {
    334      return elementStyle->UserSelect() != mozilla::StyleUserSelect::None;
    335    }
    336    // Finally, if `user-select:auto`, let's check the parent.
    337  }
    338  return false;
    339 }
    340 
    341 namespace mozilla::dom {
    342 
    343 const DOMTokenListSupportedToken Element::sSupportedBlockingValues[] = {
    344    "render", nullptr};
    345 
    346 nsDOMAttributeMap* Element::Attributes() {
    347  nsDOMSlots* slots = DOMSlots();
    348  if (!slots->mAttributeMap) {
    349    slots->mAttributeMap = new nsDOMAttributeMap(this);
    350  }
    351 
    352  return slots->mAttributeMap;
    353 }
    354 
    355 void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) {
    356  const PointerInfo* pointerInfo =
    357      PointerEventHandler::GetPointerInfo(aPointerId);
    358  if (!pointerInfo) {
    359    aError.ThrowNotFoundError("Invalid pointer id");
    360    return;
    361  }
    362  if (!IsInComposedDoc()) {
    363    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    364    return;
    365  }
    366  if (OwnerDoc()->GetPointerLockElement()) {
    367    // Throw an exception 'InvalidStateError' while the page has a locked
    368    // element.
    369    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
    370    return;
    371  }
    372  // XXX If pointerInfo->mIsSynthesizedForTests does not match the last
    373  // WidgetPointerEvent's mFlags.mIsSynthesizedForTests, should we treat it
    374  // as unknown pointerId?
    375  if (!pointerInfo->mIsActive || pointerInfo->mActiveDocument != OwnerDoc()) {
    376    return;
    377  }
    378  PointerEventHandler::RequestPointerCaptureById(aPointerId, this);
    379 }
    380 
    381 void Element::ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) {
    382  if (!PointerEventHandler::GetPointerInfo(aPointerId)) {
    383    aError.ThrowNotFoundError("Invalid pointer id");
    384    return;
    385  }
    386  if (HasPointerCapture(aPointerId)) {
    387    PointerEventHandler::ReleasePointerCaptureById(aPointerId);
    388  }
    389 }
    390 
    391 bool Element::HasPointerCapture(long aPointerId) {
    392  PointerCaptureInfo* pointerCaptureInfo =
    393      PointerEventHandler::GetPointerCaptureInfo(aPointerId);
    394  if (pointerCaptureInfo && pointerCaptureInfo->mPendingElement == this) {
    395    return true;
    396  }
    397  return false;
    398 }
    399 
    400 const nsAttrValue* Element::GetSVGAnimatedClass() const {
    401  MOZ_ASSERT(MayHaveClass() && IsSVGElement(), "Unexpected call");
    402  return static_cast<const SVGElement*>(this)->GetAnimatedClassName();
    403 }
    404 
    405 NS_IMETHODIMP
    406 Element::QueryInterface(REFNSIID aIID, void** aInstancePtr) {
    407  if (aIID.Equals(NS_GET_IID(Element))) {
    408    NS_ADDREF_THIS();
    409    *aInstancePtr = this;
    410    return NS_OK;
    411  }
    412 
    413  NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!");
    414  nsresult rv = FragmentOrElement::QueryInterface(aIID, aInstancePtr);
    415  if (NS_SUCCEEDED(rv)) {
    416    return NS_OK;
    417  }
    418 
    419  return NS_NOINTERFACE;
    420 }
    421 
    422 void Element::NotifyStateChange(ElementState aStates) {
    423  MOZ_ASSERT(!aStates.IsEmpty());
    424  if (Document* doc = GetComposedDoc()) {
    425    nsAutoScriptBlocker scriptBlocker;
    426    doc->ElementStateChanged(this, aStates);
    427  }
    428 }
    429 
    430 }  // namespace mozilla::dom
    431 
    432 void nsIContent::UpdateEditableState(bool aNotify) {
    433  if (IsInNativeAnonymousSubtree()) {
    434    // Don't propagate the editable flag into native anonymous subtrees.
    435    if (IsRootOfNativeAnonymousSubtree()) {
    436      return;
    437    }
    438 
    439    // We allow setting the flag on NAC (explicitly, see
    440    // nsTextControlFrame::CreateAnonymousContent for example), but not
    441    // unsetting it.
    442    //
    443    // Otherwise, just the act of binding the NAC subtree into our non-anonymous
    444    // parent would clear the flag, which is not good. As we shouldn't move NAC
    445    // around, this is fine.
    446    if (HasFlag(NODE_IS_EDITABLE)) {
    447      return;
    448    }
    449  }
    450 
    451  nsIContent* parent = GetParent();
    452  SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
    453 }
    454 
    455 namespace mozilla::dom {
    456 
    457 void Element::UpdateEditableState(bool aNotify) {
    458  nsIContent::UpdateEditableState(aNotify);
    459  UpdateReadOnlyState(aNotify);
    460 }
    461 
    462 bool Element::IsReadOnlyInternal() const { return !IsEditable(); }
    463 
    464 void Element::UpdateReadOnlyState(bool aNotify) {
    465  auto oldState = State();
    466  if (IsReadOnlyInternal()) {
    467    RemoveStatesSilently(ElementState::READWRITE);
    468    AddStatesSilently(ElementState::READONLY);
    469  } else {
    470    RemoveStatesSilently(ElementState::READONLY);
    471    AddStatesSilently(ElementState::READWRITE);
    472  }
    473  if (!aNotify) {
    474    return;
    475  }
    476  const auto newState = State();
    477  if (newState != oldState) {
    478    NotifyStateChange(newState ^ oldState);
    479  }
    480 }
    481 
    482 Maybe<int32_t> Element::GetTabIndexAttrValue() {
    483  const nsAttrValue* attrVal = GetParsedAttr(nsGkAtoms::tabindex);
    484  if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
    485    return Some(attrVal->GetIntegerValue());
    486  }
    487 
    488  return Nothing();
    489 }
    490 
    491 int32_t Element::TabIndex() {
    492  Maybe<int32_t> attrVal = GetTabIndexAttrValue();
    493  if (attrVal.isSome()) {
    494    return attrVal.value();
    495  }
    496 
    497  return TabIndexDefault();
    498 }
    499 
    500 void Element::Focus(const FocusOptions& aOptions, CallerType aCallerType,
    501                    ErrorResult& aError) {
    502  const RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
    503  if (MOZ_UNLIKELY(!fm)) {
    504    return;
    505  }
    506  const OwningNonNull<Element> kungFuDeathGrip(*this);
    507  // Also other browsers seem to have the hack to not re-focus (and flush) when
    508  // the element is already focused.
    509  // Until https://github.com/whatwg/html/issues/4512 is clarified, we'll
    510  // maintain interoperatibility by not re-focusing, independent of aOptions.
    511  // I.e., `focus({ preventScroll: true})` followed by `focus( { preventScroll:
    512  // false })` won't re-focus.
    513  if (fm->CanSkipFocus(this)) {
    514    fm->NotifyOfReFocus(kungFuDeathGrip);
    515    fm->NeedsFlushBeforeEventHandling(this);
    516    return;
    517  }
    518  uint32_t fmFlags = nsFocusManager::ProgrammaticFocusFlags(aOptions);
    519  if (aCallerType == CallerType::NonSystem) {
    520    fmFlags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
    521  }
    522  aError = fm->SetFocus(kungFuDeathGrip, fmFlags);
    523 }
    524 
    525 void Element::SetTabIndex(int32_t aTabIndex, mozilla::ErrorResult& aError) {
    526  nsAutoString value;
    527  value.AppendInt(aTabIndex);
    528 
    529  SetAttr(nsGkAtoms::tabindex, value, aError);
    530 }
    531 
    532 void Element::SetShadowRoot(ShadowRoot* aShadowRoot) {
    533  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
    534  MOZ_ASSERT(!aShadowRoot || !slots->mShadowRoot,
    535             "We shouldn't clear the shadow root without unbind first");
    536  slots->mShadowRoot = aShadowRoot;
    537 }
    538 
    539 void Element::SetLastRememberedBSize(float aBSize) {
    540  ExtendedDOMSlots()->mLastRememberedBSize = Some(aBSize);
    541 }
    542 
    543 void Element::SetLastRememberedISize(float aISize) {
    544  ExtendedDOMSlots()->mLastRememberedISize = Some(aISize);
    545 }
    546 
    547 void Element::RemoveLastRememberedBSize() {
    548  if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
    549    slots->mLastRememberedBSize.reset();
    550  }
    551 }
    552 
    553 void Element::RemoveLastRememberedISize() {
    554  if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
    555    slots->mLastRememberedISize.reset();
    556  }
    557 }
    558 
    559 void Element::Blur(mozilla::ErrorResult& aError) {
    560  if (!ShouldBlur(this)) {
    561    return;
    562  }
    563 
    564  Document* doc = GetComposedDoc();
    565  if (!doc) {
    566    return;
    567  }
    568 
    569  if (nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow()) {
    570    if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
    571      aError = fm->ClearFocus(win);
    572    }
    573  }
    574 }
    575 
    576 ElementState Element::StyleStateFromLocks() const {
    577  StyleStateLocks locksAndValues = LockedStyleStates();
    578  ElementState locks = locksAndValues.mLocks;
    579  ElementState values = locksAndValues.mValues;
    580  ElementState state = (mState & ~locks) | (locks & values);
    581 
    582  if (state.HasState(ElementState::VISITED)) {
    583    return state & ~ElementState::UNVISITED;
    584  }
    585  if (state.HasState(ElementState::UNVISITED)) {
    586    return state & ~ElementState::VISITED;
    587  }
    588 
    589  return state;
    590 }
    591 
    592 Element::StyleStateLocks Element::LockedStyleStates() const {
    593  StyleStateLocks* locks =
    594      static_cast<StyleStateLocks*>(GetProperty(nsGkAtoms::lockedStyleStates));
    595  if (locks) {
    596    return *locks;
    597  }
    598  return StyleStateLocks();
    599 }
    600 
    601 void Element::NotifyStyleStateChange(ElementState aStates) {
    602  if (RefPtr<Document> doc = GetComposedDoc()) {
    603    if (RefPtr<PresShell> presShell = doc->GetPresShell()) {
    604      nsAutoScriptBlocker scriptBlocker;
    605      presShell->ElementStateChanged(doc, this, aStates);
    606    }
    607  }
    608 }
    609 
    610 void Element::LockStyleStates(ElementState aStates, bool aEnabled) {
    611  StyleStateLocks* locks = new StyleStateLocks(LockedStyleStates());
    612 
    613  locks->mLocks |= aStates;
    614  if (aEnabled) {
    615    locks->mValues |= aStates;
    616  } else {
    617    locks->mValues &= ~aStates;
    618  }
    619 
    620  if (aStates.HasState(ElementState::VISITED)) {
    621    locks->mLocks &= ~ElementState::UNVISITED;
    622  }
    623  if (aStates.HasState(ElementState::UNVISITED)) {
    624    locks->mLocks &= ~ElementState::VISITED;
    625  }
    626 
    627  SetProperty(nsGkAtoms::lockedStyleStates, locks,
    628              nsINode::DeleteProperty<StyleStateLocks>);
    629  SetHasLockedStyleStates();
    630 
    631  NotifyStyleStateChange(aStates);
    632 }
    633 
    634 void Element::UnlockStyleStates(ElementState aStates) {
    635  StyleStateLocks* locks = new StyleStateLocks(LockedStyleStates());
    636 
    637  locks->mLocks &= ~aStates;
    638 
    639  if (locks->mLocks.IsEmpty()) {
    640    RemoveProperty(nsGkAtoms::lockedStyleStates);
    641    ClearHasLockedStyleStates();
    642    delete locks;
    643  } else {
    644    SetProperty(nsGkAtoms::lockedStyleStates, locks,
    645                nsINode::DeleteProperty<StyleStateLocks>);
    646  }
    647 
    648  NotifyStyleStateChange(aStates);
    649 }
    650 
    651 void Element::ClearStyleStateLocks() {
    652  StyleStateLocks locks = LockedStyleStates();
    653 
    654  RemoveProperty(nsGkAtoms::lockedStyleStates);
    655  ClearHasLockedStyleStates();
    656 
    657  NotifyStyleStateChange(locks.mLocks);
    658 }
    659 
    660 /* virtual */
    661 nsINode* Element::GetScopeChainParent() const { return OwnerDoc(); }
    662 
    663 JSObject* Element::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
    664  return Element_Binding::Wrap(aCx, this, aGivenProto);
    665 }
    666 
    667 nsDOMTokenList* Element::ClassList() {
    668  nsDOMSlots* slots = DOMSlots();
    669  if (!slots->mClassList) {
    670    slots->mClassList = new nsDOMTokenList(this, nsGkAtoms::_class);
    671  }
    672  return slots->mClassList;
    673 }
    674 
    675 nsDOMTokenList* Element::Part() {
    676  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
    677  if (!slots->mPart) {
    678    slots->mPart = new nsDOMTokenList(this, nsGkAtoms::part);
    679  }
    680  return slots->mPart;
    681 }
    682 
    683 void Element::RecompileScriptEventListeners() {
    684  for (uint32_t i = 0, count = mAttrs.AttrCount(); i < count; ++i) {
    685    BorrowedAttrInfo attrInfo = mAttrs.AttrInfoAt(i);
    686 
    687    // Eventlistenener-attributes are always in the null namespace
    688    if (!attrInfo.mName->IsAtom()) {
    689      continue;
    690    }
    691 
    692    nsAtom* attr = attrInfo.mName->Atom();
    693    if (!IsEventAttributeName(attr)) {
    694      continue;
    695    }
    696 
    697    nsAutoString value;
    698    attrInfo.mValue->ToString(value);
    699    SetEventHandler(GetEventNameForAttr(attr), value, true);
    700  }
    701 }
    702 
    703 void Element::GetAttributeNames(nsTArray<nsString>& aResult) {
    704  uint32_t count = mAttrs.AttrCount();
    705  for (uint32_t i = 0; i < count; ++i) {
    706    const nsAttrName* name = mAttrs.AttrNameAt(i);
    707    name->GetQualifiedName(*aResult.AppendElement());
    708  }
    709 }
    710 
    711 already_AddRefed<nsIHTMLCollection> Element::GetElementsByTagName(
    712    const nsAString& aLocalName) {
    713  return NS_GetContentList(this, kNameSpaceID_Unknown, aLocalName);
    714 }
    715 
    716 ScrollContainerFrame* Element::GetScrollContainerFrame(nsIFrame** aFrame,
    717                                                       FlushType aFlushType) {
    718  nsIFrame* frame = GetPrimaryFrame(aFlushType);
    719  if (aFrame) {
    720    *aFrame = frame;
    721  }
    722  if (frame) {
    723    if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
    724      // It's unclear what to return for SVG frames, so just return null.
    725      return nullptr;
    726    }
    727 
    728    if (ScrollContainerFrame* scrollContainerFrame =
    729            frame->GetScrollTargetFrame()) {
    730      MOZ_ASSERT(!OwnerDoc()->IsScrollingElement(this),
    731                 "How can we have a scroll container frame if we're the "
    732                 "scrollingElement for our document?");
    733      return scrollContainerFrame;
    734    }
    735  }
    736 
    737  Document* doc = OwnerDoc();
    738  // Note: This IsScrollingElement() call can flush frames, if we're the body of
    739  // a quirks mode document.
    740  const bool isScrollingElement = doc->IsScrollingElement(this);
    741  if (isScrollingElement) {
    742    // Our scroll info should map to the root scroll container frame if there is
    743    // one.
    744    if (PresShell* presShell = doc->GetPresShell()) {
    745      if (ScrollContainerFrame* rootScrollContainerFrame =
    746              presShell->GetRootScrollContainerFrame()) {
    747        if (aFrame) {
    748          *aFrame = rootScrollContainerFrame;
    749        }
    750        return rootScrollContainerFrame;
    751      }
    752    }
    753  }
    754  if (aFrame) {
    755    // Re-get *aFrame if the caller asked for it, because that frame flush can
    756    // kill it.
    757    *aFrame = GetPrimaryFrame(FlushType::None);
    758  }
    759  return nullptr;
    760 }
    761 
    762 bool Element::CheckVisibility(const CheckVisibilityOptions& aOptions) {
    763  nsIFrame* f =
    764      GetPrimaryFrame(aOptions.mFlush ? FlushType::Frames : FlushType::None);
    765  if (!f) {
    766    // 1. If this does not have an associated box, return false.
    767    return false;
    768  }
    769 
    770  EnumSet includeContentVisibility = {
    771      nsIFrame::IncludeContentVisibility::Hidden};
    772  if (aOptions.mContentVisibilityAuto) {
    773    includeContentVisibility += nsIFrame::IncludeContentVisibility::Auto;
    774  }
    775  // Steps 2 and 5
    776  if (f->IsHiddenByContentVisibilityOnAnyAncestor(includeContentVisibility)) {
    777    // 2. If a shadow-including ancestor of this has content-visibility: hidden,
    778    // return false.
    779    // 5. If a shadow-including ancestor of this skips its content due to
    780    // has content-visibility: auto, return false.
    781    return false;
    782  }
    783 
    784  if ((aOptions.mOpacityProperty || aOptions.mCheckOpacity) &&
    785      f->Style()->IsInOpacityZeroSubtree()) {
    786    // 3. If the checkOpacity dictionary member of options is true, and this, or
    787    // a shadow-including ancestor of this, has a computed opacity value of 0,
    788    // return false.
    789    return false;
    790  }
    791 
    792  if ((aOptions.mVisibilityProperty || aOptions.mCheckVisibilityCSS) &&
    793      !f->StyleVisibility()->IsVisible()) {
    794    // 4. If the checkVisibilityCSS dictionary member of options is true, and
    795    // this is invisible, return false.
    796    return false;
    797  }
    798 
    799  // 6. Return true
    800  return true;
    801 }
    802 
    803 void Element::ScrollIntoView(const BooleanOrScrollIntoViewOptions& aObject) {
    804  if (aObject.IsScrollIntoViewOptions()) {
    805    return ScrollIntoView(aObject.GetAsScrollIntoViewOptions());
    806  }
    807 
    808  MOZ_DIAGNOSTIC_ASSERT(aObject.IsBoolean());
    809 
    810  ScrollIntoViewOptions options;
    811  if (aObject.GetAsBoolean()) {
    812    options.mBlock = ScrollLogicalPosition::Start;
    813    options.mInline = ScrollLogicalPosition::Nearest;
    814  } else {
    815    options.mBlock = ScrollLogicalPosition::End;
    816    options.mInline = ScrollLogicalPosition::Nearest;
    817  }
    818  return ScrollIntoView(options);
    819 }
    820 
    821 void Element::ScrollIntoView(const ScrollIntoViewOptions& aOptions) {
    822  Document* document = GetComposedDoc();
    823  if (!document) {
    824    return;
    825  }
    826 
    827  // Get the presentation shell
    828  RefPtr<PresShell> presShell = document->GetPresShell();
    829  if (!presShell) {
    830    return;
    831  }
    832 
    833  const auto ToWhereToScroll =
    834      [](ScrollLogicalPosition aPosition) -> WhereToScroll {
    835    switch (aPosition) {
    836      case ScrollLogicalPosition::Start:
    837        return WhereToScroll::Start;
    838      case ScrollLogicalPosition::Center:
    839        return WhereToScroll::Center;
    840      case ScrollLogicalPosition::End:
    841        return WhereToScroll::End;
    842      case ScrollLogicalPosition::Nearest:
    843        break;
    844    }
    845    return WhereToScroll::Nearest;
    846  };
    847 
    848  const auto block = ToWhereToScroll(aOptions.mBlock);
    849  const auto inline_ = ToWhereToScroll(aOptions.mInline);
    850 
    851  ScrollFlags scrollFlags = ScrollFlags::ScrollOverflowHidden |
    852                            ScrollFlags::TriggeredByScript |
    853                            ScrollFlags::AxesAreLogical;
    854  if (aOptions.mBehavior == ScrollBehavior::Smooth) {
    855    scrollFlags |= ScrollFlags::ScrollSmooth;
    856  } else if (aOptions.mBehavior == ScrollBehavior::Auto) {
    857    scrollFlags |= ScrollFlags::ScrollSmoothAuto;
    858  }
    859 
    860  presShell->ScrollContentIntoView(
    861      this, ScrollAxis(block, WhenToScroll::Always),
    862      ScrollAxis(inline_, WhenToScroll::Always), scrollFlags);
    863 }
    864 
    865 void Element::ScrollTo(double aXScroll, double aYScroll) {
    866  ScrollToOptions options;
    867  options.mLeft.Construct(aXScroll);
    868  options.mTop.Construct(aYScroll);
    869  ScrollTo(options);
    870 }
    871 
    872 void Element::ScrollTo(const ScrollToOptions& aOptions) {
    873  // When the scroll top is 0, we don't need to flush layout to scroll to that
    874  // point; we know 0 is always in range.  At least we think so...  But we do
    875  // need to flush frames so we ensure we find the right scrollable frame if
    876  // there is one. If it's nonzero, we need to flush layout because we need to
    877  // figure out what our real scrollTopMax is.
    878  //
    879  // If we have a left value, we can't assume things based on it's value,
    880  // depending on our direction and layout 0 may or may not be in our scroll
    881  // range.  So we need to flush layout no matter what then.
    882  const bool needsLayoutFlush =
    883      aOptions.mLeft.WasPassed() ||
    884      (aOptions.mTop.WasPassed() && aOptions.mTop.Value() != 0.0);
    885 
    886  nsIFrame* frame;
    887  ScrollContainerFrame* sf = GetScrollContainerFrame(
    888      &frame, needsLayoutFlush ? FlushType::Layout : FlushType::Frames);
    889  if (!sf) {
    890    return;
    891  }
    892 
    893  CSSPoint scrollPos = sf->GetScrollPositionCSSPixels();
    894  if (aOptions.mLeft.WasPassed()) {
    895    scrollPos.x = ToZeroIfNonfinite(
    896        frame->Style()->EffectiveZoom().Zoom(aOptions.mLeft.Value()));
    897  }
    898  if (aOptions.mTop.WasPassed()) {
    899    scrollPos.y = ToZeroIfNonfinite(
    900        frame->Style()->EffectiveZoom().Zoom(aOptions.mTop.Value()));
    901  }
    902  ScrollMode scrollMode = sf->ScrollModeForScrollBehavior(aOptions.mBehavior);
    903  sf->ScrollToCSSPixels(scrollPos, scrollMode);
    904 }
    905 
    906 void Element::ScrollBy(double aXScrollDif, double aYScrollDif) {
    907  ScrollToOptions options;
    908  options.mLeft.Construct(aXScrollDif);
    909  options.mTop.Construct(aYScrollDif);
    910  ScrollBy(options);
    911 }
    912 
    913 void Element::ScrollBy(const ScrollToOptions& aOptions) {
    914  nsIFrame* frame;
    915  ScrollContainerFrame* sf = GetScrollContainerFrame(&frame);
    916  if (!sf) {
    917    return;
    918  }
    919 
    920  CSSPoint scrollDelta;
    921  if (aOptions.mLeft.WasPassed()) {
    922    scrollDelta.x = ToZeroIfNonfinite(
    923        frame->Style()->EffectiveZoom().Zoom(aOptions.mLeft.Value()));
    924  }
    925 
    926  if (aOptions.mTop.WasPassed()) {
    927    scrollDelta.y = ToZeroIfNonfinite(
    928        frame->Style()->EffectiveZoom().Zoom(aOptions.mTop.Value()));
    929  }
    930 
    931  auto scrollMode = sf->ScrollModeForScrollBehavior(aOptions.mBehavior);
    932  sf->ScrollByCSSPixels(scrollDelta, scrollMode);
    933 }
    934 
    935 double Element::ScrollTop() {
    936  return CSSPixel::FromAppUnits(GetScrollOrigin().y);
    937 }
    938 
    939 void Element::SetScrollTop(double aScrollTop) {
    940  ScrollToOptions options;
    941  options.mTop.Construct(aScrollTop);
    942  ScrollTo(options);
    943 }
    944 
    945 double Element::ScrollLeft() {
    946  return CSSPixel::FromAppUnits(GetScrollOrigin().x);
    947 }
    948 
    949 void Element::SetScrollLeft(double aScrollLeft) {
    950  ScrollToOptions options;
    951  options.mLeft.Construct(aScrollLeft);
    952  ScrollTo(options);
    953 }
    954 
    955 void Element::MozScrollSnap() {
    956  if (ScrollContainerFrame* sf =
    957          GetScrollContainerFrame(nullptr, FlushType::None)) {
    958    sf->ScrollSnap();
    959  }
    960 }
    961 
    962 nsRect Element::GetScrollRange() {
    963  nsIFrame* frame;
    964  ScrollContainerFrame* sf = GetScrollContainerFrame(&frame);
    965  if (!sf) {
    966    return nsRect();
    967  }
    968  return frame->Style()->EffectiveZoom().Unzoom(sf->GetScrollRange());
    969 }
    970 
    971 double Element::ScrollTopMin() {
    972  return CSSPixel::FromAppUnits(GetScrollRange().Y());
    973 }
    974 
    975 double Element::ScrollTopMax() {
    976  return CSSPixel::FromAppUnits(GetScrollRange().YMost());
    977 }
    978 
    979 double Element::ScrollLeftMin() {
    980  return CSSPixel::FromAppUnits(GetScrollRange().X());
    981 }
    982 
    983 double Element::ScrollLeftMax() {
    984  return CSSPixel::FromAppUnits(GetScrollRange().XMost());
    985 }
    986 
    987 static nsSize GetScrollRectSizeForOverflowVisibleFrame(nsIFrame* aFrame) {
    988  if (!aFrame || aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
    989    return nsSize();
    990  }
    991 
    992  // This matches WebKit and Blink, which in turn (apparently, according to
    993  // their source) matched old IE.
    994  const nsRect paddingRect = aFrame->GetPaddingRectRelativeToSelf();
    995  const nsRect overflowRect = [&] {
    996    OverflowAreas overflowAreas(paddingRect, paddingRect);
    997    // Add the scrollable overflow areas of children (if any) to the
    998    // paddingRect, as if aFrame was a scrolled frame. It's important to start
    999    // with the paddingRect, otherwise if there are no children the overflow
   1000    // rect will be 0,0,0,0 which will force the point 0,0 to be included in the
   1001    // final rect.
   1002    aFrame->UnionChildOverflow(overflowAreas, /* aAsIfScrolled = */ true);
   1003    // Make sure that an empty padding-rect's edges are included, by adding
   1004    // the padding-rect in again with UnionEdges.
   1005    return overflowAreas.ScrollableOverflow().UnionEdges(paddingRect);
   1006  }();
   1007 
   1008  auto directions =
   1009      ScrollContainerFrame::ComputePerAxisScrollDirections(aFrame);
   1010  const nscoord height = directions.mToBottom
   1011                             ? overflowRect.YMost() - paddingRect.Y()
   1012                             : paddingRect.YMost() - overflowRect.Y();
   1013  const nscoord width = directions.mToRight
   1014                            ? overflowRect.XMost() - paddingRect.X()
   1015                            : paddingRect.XMost() - overflowRect.X();
   1016  return nsSize(width, height);
   1017 }
   1018 
   1019 nsSize Element::GetScrollSize() {
   1020  nsIFrame* frame;
   1021  nsSize size;
   1022  if (ScrollContainerFrame* sf = GetScrollContainerFrame(&frame)) {
   1023    size = sf->GetScrollRange().Size() + sf->GetScrollPortRect().Size();
   1024  } else {
   1025    size = GetScrollRectSizeForOverflowVisibleFrame(frame);
   1026  }
   1027  if (!frame) {
   1028    return size;
   1029  }
   1030  return frame->Style()->EffectiveZoom().Unzoom(size);
   1031 }
   1032 
   1033 nsPoint Element::GetScrollOrigin() {
   1034  nsIFrame* frame;
   1035  ScrollContainerFrame* sf = GetScrollContainerFrame(&frame);
   1036  if (!sf) {
   1037    return nsPoint();
   1038  }
   1039  return frame->Style()->EffectiveZoom().Unzoom(sf->GetScrollPosition());
   1040 }
   1041 
   1042 int32_t Element::ScrollHeight() {
   1043  return nsPresContext::AppUnitsToIntCSSPixels(GetScrollSize().height);
   1044 }
   1045 
   1046 int32_t Element::ScrollWidth() {
   1047  return nsPresContext::AppUnitsToIntCSSPixels(GetScrollSize().width);
   1048 }
   1049 
   1050 nsRect Element::GetClientAreaRect() {
   1051  Document* doc = OwnerDoc();
   1052  nsPresContext* presContext = doc->GetPresContext();
   1053 
   1054  // We can avoid a layout flush if this is the scrolling element of the
   1055  // document, we have overlay scrollbars, and we aren't embedded in another
   1056  // document
   1057  if (presContext && presContext->UseOverlayScrollbars() &&
   1058      !doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout() &&
   1059      doc->IsScrollingElement(this)) {
   1060    if (RefPtr ps = doc->GetPresShell()) {
   1061      return nsRect(nsPoint(), ps->MaybePendingLayoutViewportSize());
   1062    }
   1063  }
   1064 
   1065  nsIFrame* frame;
   1066  if (ScrollContainerFrame* sf = GetScrollContainerFrame(&frame)) {
   1067    nsRect scrollPort = sf->GetScrollPortRect();
   1068 
   1069    if (!sf->IsRootScrollFrameOfDocument()) {
   1070      MOZ_ASSERT(frame);
   1071      // We want the offset to be relative to `frame`, not `sf`... Except for
   1072      // the root scroll frame, which is an ancestor of frame rather than a
   1073      // descendant and thus this wouldn't particularly make sense.
   1074      if (frame != sf) {
   1075        scrollPort.MoveBy(sf->GetOffsetTo(frame));
   1076      }
   1077    }
   1078 
   1079    // The scroll port value might be expanded to the minimum scale size, we
   1080    // should limit the size to the ICB in such cases.
   1081    scrollPort.SizeTo(sf->GetLayoutSize());
   1082    return frame->Style()->EffectiveZoom().Unzoom(scrollPort);
   1083  }
   1084 
   1085  if (frame &&
   1086      // The display check is OK even though we're not looking at the style
   1087      // frame, because the style frame only differs from "frame" for tables,
   1088      // and table wrappers have the same display as the table itself.
   1089      (!frame->StyleDisplay()->IsInlineFlow() || frame->IsReplaced())) {
   1090    // Special case code to make client area work even when there isn't
   1091    // a scroll view, see bug 180552, bug 227567.
   1092    return frame->Style()->EffectiveZoom().Unzoom(
   1093        frame->GetPaddingRect() - frame->GetPositionIgnoringScrolling());
   1094  }
   1095 
   1096  // SVG nodes reach here and just return 0
   1097  return nsRect();
   1098 }
   1099 
   1100 int32_t Element::ScreenX() {
   1101  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
   1102  return frame ? frame->GetScreenRect().x : 0;
   1103 }
   1104 
   1105 int32_t Element::ScreenY() {
   1106  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
   1107  return frame ? frame->GetScreenRect().y : 0;
   1108 }
   1109 
   1110 already_AddRefed<nsIScreen> Element::GetScreen() {
   1111  // Flush layout to guarantee that frames are created if needed, and preserve
   1112  // behavior.
   1113  (void)GetPrimaryFrame(FlushType::Frames);
   1114  if (nsIWidget* widget = nsContentUtils::WidgetForContent(this)) {
   1115    return widget->GetWidgetScreen();
   1116  }
   1117  return nullptr;
   1118 }
   1119 
   1120 double Element::CurrentCSSZoom() {
   1121  nsIFrame* f = GetPrimaryFrame(FlushType::Frames);
   1122  if (!f) {
   1123    return 1.0;
   1124  }
   1125  return f->Style()->EffectiveZoom().ToFloat();
   1126 }
   1127 
   1128 already_AddRefed<DOMRect> Element::GetBoundingClientRect() {
   1129  RefPtr<DOMRect> rect = new DOMRect(ToSupports(OwnerDoc()));
   1130 
   1131  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
   1132  if (!frame) {
   1133    // display:none, perhaps? Return the empty rect
   1134    return rect.forget();
   1135  }
   1136 
   1137  rect->SetLayoutRect(frame->GetBoundingClientRect());
   1138  return rect.forget();
   1139 }
   1140 
   1141 already_AddRefed<DOMRectList> Element::GetClientRects() {
   1142  RefPtr<DOMRectList> rectList = new DOMRectList(this);
   1143 
   1144  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
   1145  if (!frame) {
   1146    // display:none, perhaps? Return an empty list
   1147    return rectList.forget();
   1148  }
   1149 
   1150  nsLayoutUtils::RectListBuilder builder(rectList);
   1151  nsLayoutUtils::GetAllInFlowRects(
   1152      frame, nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder,
   1153      nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms);
   1154  return rectList.forget();
   1155 }
   1156 
   1157 const DOMTokenListSupportedToken Element::sAnchorAndFormRelValues[] = {
   1158    "noreferrer", "noopener", "opener", nullptr};
   1159 
   1160 // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#lazy-loading-attribute
   1161 static constexpr nsAttrValue::EnumTableEntry kLoadingTable[] = {
   1162    {"eager", Element::Loading::Eager},
   1163    {"lazy", Element::Loading::Lazy},
   1164 };
   1165 
   1166 void Element::GetLoading(nsAString& aValue) const {
   1167  GetEnumAttr(nsGkAtoms::loading, kLoadingTable[0].tag, aValue);
   1168 }
   1169 
   1170 bool Element::ParseLoadingAttribute(const nsAString& aValue,
   1171                                    nsAttrValue& aResult) {
   1172  return aResult.ParseEnumValue(aValue, kLoadingTable,
   1173                                /* aCaseSensitive = */ false,
   1174                                &kLoadingTable[0]);
   1175 }
   1176 
   1177 Element::Loading Element::LoadingState() const {
   1178  const nsAttrValue* val = mAttrs.GetAttr(nsGkAtoms::loading);
   1179  if (!val) {
   1180    return Loading::Eager;
   1181  }
   1182  return static_cast<Loading>(val->GetEnumValue());
   1183 }
   1184 
   1185 namespace {
   1186 // <https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fetch-priority-attributes>.
   1187 static constexpr nsAttrValue::EnumTableEntry kFetchPriorityEnumTable[] = {
   1188    {kFetchPriorityAttributeValueHigh, FetchPriority::High},
   1189    {kFetchPriorityAttributeValueLow, FetchPriority::Low},
   1190    {kFetchPriorityAttributeValueAuto, FetchPriority::Auto}};
   1191 
   1192 // <https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fetch-priority-attributes>.
   1193 static constexpr const nsAttrValue::EnumTableEntry*
   1194    kFetchPriorityEnumTableInvalidValueDefault = &kFetchPriorityEnumTable[2];
   1195 }  // namespace
   1196 
   1197 void Element::ParseFetchPriority(const nsAString& aValue,
   1198                                 nsAttrValue& aResult) {
   1199  aResult.ParseEnumValue(aValue, kFetchPriorityEnumTable,
   1200                         false /* aCaseSensitive */,
   1201                         kFetchPriorityEnumTableInvalidValueDefault);
   1202 }
   1203 
   1204 FetchPriority Element::GetFetchPriority() const {
   1205  const nsAttrValue* fetchpriorityAttribute =
   1206      GetParsedAttr(nsGkAtoms::fetchpriority);
   1207  if (fetchpriorityAttribute) {
   1208    MOZ_ASSERT(fetchpriorityAttribute->Type() == nsAttrValue::eEnum);
   1209    return FetchPriority(fetchpriorityAttribute->GetEnumValue());
   1210  }
   1211 
   1212  return FetchPriority::Auto;
   1213 }
   1214 
   1215 //----------------------------------------------------------------------
   1216 
   1217 void Element::AddToIdTable(nsAtom* aId) {
   1218  NS_ASSERTION(HasID(), "Node doesn't have an ID?");
   1219  if (IsInShadowTree()) {
   1220    ShadowRoot* containingShadow = GetContainingShadow();
   1221    containingShadow->AddToIdTable(this, aId);
   1222  } else {
   1223    Document* doc = GetUncomposedDoc();
   1224    if (doc && !IsInNativeAnonymousSubtree()) {
   1225      doc->AddToIdTable(this, aId);
   1226    }
   1227  }
   1228 }
   1229 
   1230 void Element::RemoveFromIdTable() {
   1231  if (!HasID()) {
   1232    return;
   1233  }
   1234 
   1235  nsAtom* id = DoGetID();
   1236  if (IsInShadowTree()) {
   1237    ShadowRoot* containingShadow = GetContainingShadow();
   1238    // Check for containingShadow because it may have
   1239    // been deleted during unlinking.
   1240    if (containingShadow) {
   1241      containingShadow->RemoveFromIdTable(this, id);
   1242    }
   1243  } else {
   1244    Document* doc = GetUncomposedDoc();
   1245    if (doc && !IsInNativeAnonymousSubtree()) {
   1246      doc->RemoveFromIdTable(this, id);
   1247    }
   1248  }
   1249 }
   1250 
   1251 void Element::SetSlot(const nsAString& aName, ErrorResult& aError) {
   1252  aError = SetAttr(kNameSpaceID_None, nsGkAtoms::slot, aName, true);
   1253 }
   1254 
   1255 void Element::GetSlot(nsAString& aName) { GetAttr(nsGkAtoms::slot, aName); }
   1256 
   1257 // https://dom.spec.whatwg.org/#dom-element-shadowroot
   1258 ShadowRoot* Element::GetShadowRootByMode() const {
   1259  /**
   1260   * 1. Let shadow be context object's shadow root.
   1261   * 2. If shadow is null or its mode is "closed", then return null.
   1262   */
   1263  ShadowRoot* shadowRoot = GetShadowRoot();
   1264  if (!shadowRoot || shadowRoot->IsClosed()) {
   1265    return nullptr;
   1266  }
   1267 
   1268  /**
   1269   * 3. Return shadow.
   1270   */
   1271  return shadowRoot;
   1272 }
   1273 
   1274 bool Element::CanAttachShadowDOM() const {
   1275  /**
   1276   * If context object's namespace is not the HTML namespace,
   1277   * return false.
   1278   *
   1279   * Deviate from the spec here to allow shadow dom attachement to
   1280   * XUL elements.
   1281   */
   1282  if (!IsHTMLElement() &&
   1283      !(IsXULElement() &&
   1284        nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal()))) {
   1285    return false;
   1286  }
   1287 
   1288  /**
   1289   * If context object's local name is not
   1290   *    a valid custom element name, "article", "aside", "blockquote",
   1291   *    "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6",
   1292   *    "header", "main" "nav", "p", "section", "search", or "span",
   1293   *  return false.
   1294   */
   1295  nsAtom* nameAtom = NodeInfo()->NameAtom();
   1296  uint32_t namespaceID = NodeInfo()->NamespaceID();
   1297  if (!(nsContentUtils::IsCustomElementName(nameAtom, namespaceID) ||
   1298        nameAtom == nsGkAtoms::article || nameAtom == nsGkAtoms::aside ||
   1299        nameAtom == nsGkAtoms::blockquote || nameAtom == nsGkAtoms::body ||
   1300        nameAtom == nsGkAtoms::div || nameAtom == nsGkAtoms::footer ||
   1301        nameAtom == nsGkAtoms::h1 || nameAtom == nsGkAtoms::h2 ||
   1302        nameAtom == nsGkAtoms::h3 || nameAtom == nsGkAtoms::h4 ||
   1303        nameAtom == nsGkAtoms::h5 || nameAtom == nsGkAtoms::h6 ||
   1304        nameAtom == nsGkAtoms::header || nameAtom == nsGkAtoms::main ||
   1305        nameAtom == nsGkAtoms::nav || nameAtom == nsGkAtoms::p ||
   1306        nameAtom == nsGkAtoms::section || nameAtom == nsGkAtoms::search ||
   1307        nameAtom == nsGkAtoms::span)) {
   1308    return false;
   1309  }
   1310 
   1311  /**
   1312   * 3. If context object’s local name is a valid custom element name, or
   1313   *    context object’s is value is not null, then:
   1314   *    If definition is not null and definition’s disable shadow is true, then
   1315   *    return false.
   1316   */
   1317  // It will always have CustomElementData when the element is a valid custom
   1318  // element or has is value.
   1319  if (CustomElementData* ceData = GetCustomElementData()) {
   1320    CustomElementDefinition* definition = ceData->GetCustomElementDefinition();
   1321    // If the definition is null, the element possible hasn't yet upgraded.
   1322    // Fallback to use LookupCustomElementDefinition to find its definition.
   1323    if (!definition) {
   1324      definition = nsContentUtils::LookupCustomElementDefinition(
   1325          NodeInfo()->GetDocument(), nameAtom, namespaceID,
   1326          ceData->GetCustomElementType());
   1327    }
   1328 
   1329    if (definition && definition->mDisableShadow) {
   1330      return false;
   1331    }
   1332  }
   1333 
   1334  return true;
   1335 }
   1336 
   1337 // https://dom.spec.whatwg.org/#dom-element-attachshadow
   1338 already_AddRefed<ShadowRoot> Element::AttachShadow(const ShadowRootInit& aInit,
   1339                                                   ErrorResult& aError) {
   1340  /**
   1341   * Step 1, 2, and 3.
   1342   */
   1343  if (!CanAttachShadowDOM()) {
   1344    aError.ThrowNotSupportedError("Unable to attach ShadowDOM");
   1345    return nullptr;
   1346  }
   1347 
   1348  /**
   1349   * 4. If element is a shadow host, then:
   1350   */
   1351  if (RefPtr<ShadowRoot> root = GetShadowRoot()) {
   1352    /**
   1353     *  1. Let currentShadowRoot be element’s shadow root.
   1354     *
   1355     *  2. If any of the following are true:
   1356     *      currentShadowRoot’s declarative is false; or
   1357     *      currentShadowRoot’s mode is not mode,
   1358     *  then throw a "NotSupportedError" DOMException.
   1359     */
   1360    if (!root->IsDeclarative() || root->Mode() != aInit.mMode) {
   1361      aError.ThrowNotSupportedError(
   1362          "Unable to re-attach to existing ShadowDOM");
   1363      return nullptr;
   1364    }
   1365    /**
   1366     * 3. Otherwise:
   1367     *      1. Remove all of currentShadowRoot’s children, in tree order.
   1368     *      2. Set currentShadowRoot’s declarative to false.
   1369     *      3. Return.
   1370     */
   1371    root->ReplaceChildren(nullptr, aError);
   1372    root->SetIsDeclarative(ShadowRootDeclarative::No);
   1373    return root.forget();
   1374  }
   1375 
   1376  if (StaticPrefs::dom_webcomponents_shadowdom_report_usage()) {
   1377    OwnerDoc()->ReportShadowDOMUsage();
   1378  }
   1379 
   1380  const nsString& referenceTarget = aInit.mReferenceTarget.WasPassed()
   1381                                        ? aInit.mReferenceTarget.Value()
   1382                                        : VoidString();
   1383  return AttachShadowWithoutNameChecks(
   1384      aInit.mMode, DelegatesFocus(aInit.mDelegatesFocus), aInit.mSlotAssignment,
   1385      ShadowRootClonable(aInit.mClonable),
   1386      ShadowRootSerializable(aInit.mSerializable), referenceTarget);
   1387 }
   1388 
   1389 already_AddRefed<ShadowRoot> Element::AttachShadowWithoutNameChecks(
   1390    ShadowRootMode aMode, DelegatesFocus aDelegatesFocus,
   1391    SlotAssignmentMode aSlotAssignment, ShadowRootClonable aClonable,
   1392    ShadowRootSerializable aSerializable, const nsAString& aReferenceTarget) {
   1393  nsAutoScriptBlocker scriptBlocker;
   1394 
   1395  auto* nim = mNodeInfo->NodeInfoManager();
   1396  RefPtr<mozilla::dom::NodeInfo> nodeInfo =
   1397      nim->GetNodeInfo(nsGkAtoms::documentFragmentNodeName, nullptr,
   1398                       kNameSpaceID_None, DOCUMENT_FRAGMENT_NODE);
   1399 
   1400  // If there are no children, the flat tree is not changing due to the presence
   1401  // of the shadow root, so we don't need to invalidate style / layout.
   1402  //
   1403  // This is a minor optimization, but also works around nasty stuff like
   1404  // bug 1397876.
   1405  if (Document* doc = GetComposedDoc()) {
   1406    if (PresShell* presShell = doc->GetPresShell()) {
   1407      presShell->ShadowRootWillBeAttached(*this);
   1408    }
   1409  }
   1410 
   1411  /**
   1412   * 5. Let shadow be a new shadow root whose node document is
   1413   *    context object's node document, host is context object,
   1414   *    and mode is init's mode.
   1415   */
   1416  RefPtr<ShadowRoot> shadowRoot = new (nim)
   1417      ShadowRoot(this, aMode, aDelegatesFocus, aSlotAssignment, aClonable,
   1418                 aSerializable, ShadowRootDeclarative::No, nodeInfo.forget());
   1419  shadowRoot->SetReferenceTarget(aReferenceTarget);
   1420 
   1421  if (NodeOrAncestorHasDirAuto()) {
   1422    shadowRoot->SetAncestorHasDirAuto();
   1423  }
   1424 
   1425  /**
   1426   * 7. If this’s custom element state is "precustomized" or "custom", then set
   1427   *    shadow’s available to element internals to true.
   1428   */
   1429  CustomElementData* ceData = GetCustomElementData();
   1430  if (ceData && (ceData->mState == CustomElementData::State::ePrecustomized ||
   1431                 ceData->mState == CustomElementData::State::eCustom)) {
   1432    shadowRoot->SetAvailableToElementInternals();
   1433  }
   1434 
   1435  /**
   1436   * 9. Set context object's shadow root to shadow.
   1437   */
   1438  SetShadowRoot(shadowRoot);
   1439 
   1440  // Dispatch a "shadowrootattached" event for devtools if needed.
   1441  if (MOZ_UNLIKELY(
   1442          nim->GetDocument()->DevToolsAnonymousAndShadowEventsEnabled())) {
   1443    AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
   1444        this, u"shadowrootattached"_ns, CanBubble::eYes,
   1445        ChromeOnlyDispatch::eYes, Composed::eYes);
   1446    dispatcher->PostDOMEvent();
   1447  }
   1448 
   1449  const LinkedList<AbstractRange>* ranges =
   1450      GetExistingClosestCommonInclusiveAncestorRanges();
   1451  if (ranges) {
   1452    for (const AbstractRange* range : *ranges) {
   1453      if (range->MayCrossShadowBoundary()) {
   1454        MOZ_ASSERT(range->IsDynamicRange());
   1455        CrossShadowBoundaryRange* crossBoundaryRange =
   1456            range->AsDynamicRange()->GetCrossShadowBoundaryRange();
   1457        MOZ_ASSERT(crossBoundaryRange);
   1458        // We may have previously selected this node before it
   1459        // becomes a shadow host, so we need to reset the values
   1460        // in RangeBoundaries to accommodate the change.
   1461        crossBoundaryRange->NotifyNodeBecomesShadowHost(this);
   1462      }
   1463    }
   1464  }
   1465  /**
   1466   * 10. Return shadow.
   1467   */
   1468  return shadowRoot.forget();
   1469 }
   1470 
   1471 void Element::AttachAndSetUAShadowRoot(NotifyUAWidgetSetup aNotify,
   1472                                       DelegatesFocus aDelegatesFocus) {
   1473  MOZ_DIAGNOSTIC_ASSERT(!CanAttachShadowDOM(),
   1474                        "Cannot be used to attach UI shadow DOM");
   1475  if (OwnerDoc()->IsStaticDocument()) {
   1476    return;
   1477  }
   1478 
   1479  if (!GetShadowRoot()) {
   1480    RefPtr<ShadowRoot> shadowRoot =
   1481        AttachShadowWithoutNameChecks(ShadowRootMode::Closed, aDelegatesFocus);
   1482    shadowRoot->SetIsUAWidget();
   1483  }
   1484 
   1485  MOZ_ASSERT(GetShadowRoot()->IsUAWidget());
   1486  if (aNotify == NotifyUAWidgetSetup::Yes) {
   1487    NotifyUAWidgetSetupOrChange();
   1488  }
   1489 }
   1490 
   1491 void Element::NotifyUAWidgetSetupOrChange() {
   1492  MOZ_ASSERT(IsInComposedDoc());
   1493  Document* doc = OwnerDoc();
   1494  if (doc->IsStaticDocument()) {
   1495    return;
   1496  }
   1497 
   1498  // Schedule a runnable, ensure the event dispatches before
   1499  // returning to content script.
   1500  // This event cause UA Widget to construct or cause onchange callback
   1501  // of existing UA Widget to run; dispatching this event twice should not cause
   1502  // UA Widget to re-init.
   1503  nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
   1504      "Element::NotifyUAWidgetSetupOrChange::UAWidgetSetupOrChange",
   1505      [self = RefPtr<Element>(this), doc = RefPtr<Document>(doc)]() {
   1506        nsContentUtils::DispatchChromeEvent(doc, self,
   1507                                            u"UAWidgetSetupOrChange"_ns,
   1508                                            CanBubble::eYes, Cancelable::eNo);
   1509      }));
   1510 }
   1511 
   1512 void Element::NotifyUAWidgetTeardown(UnattachShadowRoot aUnattachShadowRoot) {
   1513  MOZ_ASSERT(IsInComposedDoc());
   1514  if (!GetShadowRoot()) {
   1515    return;
   1516  }
   1517  MOZ_ASSERT(GetShadowRoot()->IsUAWidget());
   1518  if (aUnattachShadowRoot == UnattachShadowRoot::Yes) {
   1519    UnattachShadow();
   1520  }
   1521 
   1522  Document* doc = OwnerDoc();
   1523  if (doc->IsStaticDocument()) {
   1524    return;
   1525  }
   1526 
   1527  // The runnable will dispatch an event to tear down UA Widget.
   1528  nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
   1529      "Element::NotifyUAWidgetTeardownAndUnattachShadow::UAWidgetTeardown",
   1530      [self = RefPtr<Element>(this), doc = RefPtr<Document>(doc)]() {
   1531        // Bail out if the element is being collected by CC
   1532        bool hasHadScriptObject = true;
   1533        nsIScriptGlobalObject* scriptObject =
   1534            doc->GetScriptHandlingObject(hasHadScriptObject);
   1535        if (!scriptObject && hasHadScriptObject) {
   1536          return;
   1537        }
   1538 
   1539        (void)nsContentUtils::DispatchChromeEvent(
   1540            doc, self, u"UAWidgetTeardown"_ns, CanBubble::eYes,
   1541            Cancelable::eNo);
   1542      }));
   1543 }
   1544 
   1545 void Element::UnattachShadow() {
   1546  RefPtr<ShadowRoot> shadowRoot = GetShadowRoot();
   1547  if (!shadowRoot) {
   1548    return;
   1549  }
   1550 
   1551  nsAutoScriptBlocker scriptBlocker;
   1552 
   1553  if (RefPtr<Document> doc = GetComposedDoc()) {
   1554    if (PresShell* presShell = doc->GetPresShell()) {
   1555      presShell->DestroyFramesForAndRestyle(this);
   1556 #ifdef ACCESSIBILITY
   1557      // We need to notify the accessibility service here explicitly because,
   1558      // even though we're going to reconstruct the _host_, the shadow root and
   1559      // its children are never really going to come back. We could plumb that
   1560      // further down to DestroyFramesForAndRestyle and add a new flag to
   1561      // nsCSSFrameConstructor::ContentRemoved or such, but this seems simpler
   1562      // instead.
   1563      if (nsAccessibilityService* accService = GetAccService()) {
   1564        accService->ContentRemoved(presShell, shadowRoot);
   1565      }
   1566 #endif
   1567    }
   1568    // ContentRemoved doesn't really run script in the cases we care about (it
   1569    // can only call ClearFocus when removing iframes and so on...)
   1570    [&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
   1571      if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
   1572        fm->ContentRemoved(doc, shadowRoot, {});
   1573      }
   1574    }();
   1575  }
   1576  MOZ_ASSERT(!GetPrimaryFrame());
   1577 
   1578  shadowRoot->Unattach();
   1579  SetShadowRoot(nullptr);
   1580 }
   1581 
   1582 void Element::GetAttribute(const nsAString& aName, DOMString& aReturn) {
   1583  const nsAttrValue* val = mAttrs.GetAttr(
   1584      aName,
   1585      IsHTMLElement() && IsInHTMLDocument() ? eIgnoreCase : eCaseMatters);
   1586  if (val) {
   1587    val->ToString(aReturn);
   1588  } else {
   1589    aReturn.SetNull();
   1590  }
   1591 }
   1592 
   1593 bool Element::ToggleAttribute(const nsAString& aName,
   1594                              const Optional<bool>& aForce,
   1595                              nsIPrincipal* aTriggeringPrincipal,
   1596                              ErrorResult& aError) {
   1597  aError = nsContentUtils::CheckQName(aName, false);
   1598  if (aError.Failed()) {
   1599    return false;
   1600  }
   1601 
   1602  nsAutoString nameToUse;
   1603  const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
   1604  if (!name) {
   1605    if (aForce.WasPassed() && !aForce.Value()) {
   1606      return false;
   1607    }
   1608    RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
   1609    if (!nameAtom) {
   1610      aError.Throw(NS_ERROR_OUT_OF_MEMORY);
   1611      return false;
   1612    }
   1613    aError = SetAttr(kNameSpaceID_None, nameAtom, u""_ns, aTriggeringPrincipal,
   1614                     true);
   1615    return true;
   1616  }
   1617  if (aForce.WasPassed() && aForce.Value()) {
   1618    return true;
   1619  }
   1620  // Hold a strong reference here so that the atom or nodeinfo doesn't go
   1621  // away during UnsetAttr. If it did UnsetAttr would be left with a
   1622  // dangling pointer as argument without knowing it.
   1623  nsAttrName tmp(*name);
   1624 
   1625  aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true);
   1626  return false;
   1627 }
   1628 
   1629 void Element::SetAttribute(const nsAString& aName, const nsAString& aValue,
   1630                           nsIPrincipal* aTriggeringPrincipal,
   1631                           ErrorResult& aError) {
   1632  aError = nsContentUtils::CheckQName(aName, false);
   1633  if (aError.Failed()) {
   1634    return;
   1635  }
   1636 
   1637  nsAutoString nameToUse;
   1638  const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
   1639  if (!name) {
   1640    RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
   1641    if (!nameAtom) {
   1642      aError.Throw(NS_ERROR_OUT_OF_MEMORY);
   1643      return;
   1644    }
   1645    aError = SetAttr(kNameSpaceID_None, nameAtom, aValue, aTriggeringPrincipal,
   1646                     true);
   1647    return;
   1648  }
   1649 
   1650  aError = SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
   1651                   aValue, aTriggeringPrincipal, true);
   1652 }
   1653 
   1654 void Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError) {
   1655  const nsAttrName* name = InternalGetAttrNameFromQName(aName);
   1656 
   1657  if (!name) {
   1658    // If there is no canonical nsAttrName for this attribute name, then the
   1659    // attribute does not exist and we can't get its namespace ID and
   1660    // local name below, so we return early.
   1661    return;
   1662  }
   1663 
   1664  // Hold a strong reference here so that the atom or nodeinfo doesn't go
   1665  // away during UnsetAttr. If it did UnsetAttr would be left with a
   1666  // dangling pointer as argument without knowing it.
   1667  nsAttrName tmp(*name);
   1668 
   1669  aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true);
   1670 }
   1671 
   1672 Attr* Element::GetAttributeNode(const nsAString& aName) {
   1673  return Attributes()->GetNamedItem(aName);
   1674 }
   1675 
   1676 already_AddRefed<Attr> Element::SetAttributeNode(
   1677    Attr& aNewAttr, nsIPrincipal* aSubjectPrincipal, ErrorResult& aError) {
   1678  RefPtr<nsDOMAttributeMap> attrMap = Attributes();
   1679  return attrMap->SetNamedItemNS(aNewAttr, aSubjectPrincipal, aError);
   1680 }
   1681 
   1682 already_AddRefed<Attr> Element::RemoveAttributeNode(Attr& aAttribute,
   1683                                                    ErrorResult& aError) {
   1684  Element* elem = aAttribute.GetElement();
   1685  if (elem != this) {
   1686    aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
   1687    return nullptr;
   1688  }
   1689 
   1690  nsAutoString nameSpaceURI;
   1691  aAttribute.NodeInfo()->GetNamespaceURI(nameSpaceURI);
   1692  return Attributes()->RemoveNamedItemNS(
   1693      nameSpaceURI, aAttribute.NodeInfo()->LocalName(), aError);
   1694 }
   1695 
   1696 void Element::GetAttributeNS(const nsAString& aNamespaceURI,
   1697                             const nsAString& aLocalName, nsAString& aReturn) {
   1698  int32_t nsid = nsNameSpaceManager::GetInstance()->GetNameSpaceID(
   1699      aNamespaceURI, nsContentUtils::IsChromeDoc(OwnerDoc()));
   1700 
   1701  if (nsid == kNameSpaceID_Unknown) {
   1702    // Unknown namespace means no attribute.
   1703    SetDOMStringToNull(aReturn);
   1704    return;
   1705  }
   1706 
   1707  RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName);
   1708  bool hasAttr = GetAttr(nsid, name, aReturn);
   1709  if (!hasAttr) {
   1710    SetDOMStringToNull(aReturn);
   1711  }
   1712 }
   1713 
   1714 void Element::SetAttributeNS(const nsAString& aNamespaceURI,
   1715                             const nsAString& aQualifiedName,
   1716                             const nsAString& aValue,
   1717                             nsIPrincipal* aTriggeringPrincipal,
   1718                             ErrorResult& aError) {
   1719  RefPtr<mozilla::dom::NodeInfo> ni;
   1720  aError = nsContentUtils::GetNodeInfoFromQName(
   1721      aNamespaceURI, aQualifiedName, mNodeInfo->NodeInfoManager(),
   1722      ATTRIBUTE_NODE, getter_AddRefs(ni));
   1723  if (aError.Failed()) {
   1724    return;
   1725  }
   1726 
   1727  aError = SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(),
   1728                   aValue, aTriggeringPrincipal, true);
   1729 }
   1730 
   1731 already_AddRefed<nsIPrincipal> Element::CreateDevtoolsPrincipal() {
   1732  // Return an ExpandedPrincipal that subsumes this Element's Principal,
   1733  // and expands this Element's CSP to allow the actions that devtools
   1734  // needs to perform.
   1735  AutoTArray<nsCOMPtr<nsIPrincipal>, 1> allowList = {NodePrincipal()};
   1736  RefPtr<ExpandedPrincipal> dtPrincipal = ExpandedPrincipal::Create(
   1737      allowList, NodePrincipal()->OriginAttributesRef());
   1738 
   1739  if (nsIPolicyContainer* policyContainer = GetPolicyContainer()) {
   1740    if (nsIContentSecurityPolicy* csp =
   1741            PolicyContainer::Cast(policyContainer)->GetCSP()) {
   1742      RefPtr<nsCSPContext> dtCsp = new nsCSPContext();
   1743      dtCsp->InitFromOther(static_cast<nsCSPContext*>(csp));
   1744      dtCsp->SetSkipAllowInlineStyleCheck(true);
   1745 
   1746      dtPrincipal->SetCsp(dtCsp);
   1747    }
   1748  }
   1749 
   1750  return dtPrincipal.forget();
   1751 }
   1752 
   1753 void Element::SetAttribute(
   1754    const nsAString& aName,
   1755    const TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString& aValue,
   1756    nsIPrincipal* aTriggeringPrincipal, ErrorResult& aError) {
   1757  aError = nsContentUtils::CheckQName(aName, false);
   1758  if (aError.Failed()) {
   1759    return;
   1760  }
   1761 
   1762  nsAutoString nameToUse;
   1763  const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
   1764  if (!name) {
   1765    RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
   1766    Maybe<nsAutoString> compliantStringHolder;
   1767    const nsAString* compliantString =
   1768        TrustedTypeUtils::GetTrustedTypesCompliantAttributeValue(
   1769            *this, nameAtom, kNameSpaceID_None, aValue, aTriggeringPrincipal,
   1770            compliantStringHolder, aError);
   1771    if (aError.Failed()) {
   1772      return;
   1773    }
   1774    aError = SetAttr(kNameSpaceID_None, nameAtom, *compliantString,
   1775                     aTriggeringPrincipal, true);
   1776    return;
   1777  }
   1778 
   1779  Maybe<nsAutoString> compliantStringHolder;
   1780  RefPtr<nsAtom> attributeName = name->LocalName();
   1781  nsMutationGuard guard;
   1782  const nsAString* compliantString =
   1783      TrustedTypeUtils::GetTrustedTypesCompliantAttributeValue(
   1784          *this, attributeName, name->NamespaceID(), aValue,
   1785          aTriggeringPrincipal, compliantStringHolder, aError);
   1786  if (aError.Failed()) {
   1787    return;
   1788  }
   1789  if (!guard.Mutated(0)) {
   1790    aError = SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
   1791                     *compliantString, aTriggeringPrincipal, true);
   1792    return;
   1793  }
   1794 
   1795  // GetTrustedTypesCompliantAttributeValue may have modified mAttrs and made
   1796  // the result of InternalGetAttrNameFromQName above invalid. It may now return
   1797  // a different value, perhaps a nullptr. To be safe, just call the version of
   1798  // Element::SetAttribute accepting a string value.
   1799  SetAttribute(aName, *compliantString, aTriggeringPrincipal, aError);
   1800 }
   1801 
   1802 void Element::SetAttributeNS(
   1803    const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
   1804    const TrustedHTMLOrTrustedScriptOrTrustedScriptURLOrString& aValue,
   1805    nsIPrincipal* aTriggeringPrincipal, ErrorResult& aError) {
   1806  RefPtr<mozilla::dom::NodeInfo> ni;
   1807  aError = nsContentUtils::GetNodeInfoFromQName(
   1808      aNamespaceURI, aQualifiedName, mNodeInfo->NodeInfoManager(),
   1809      ATTRIBUTE_NODE, getter_AddRefs(ni));
   1810  if (aError.Failed()) {
   1811    return;
   1812  }
   1813 
   1814  Maybe<nsAutoString> compliantStringHolder;
   1815  RefPtr<nsAtom> attributeName = ni->NameAtom();
   1816  const nsAString* compliantString =
   1817      TrustedTypeUtils::GetTrustedTypesCompliantAttributeValue(
   1818          *this, attributeName, ni->NamespaceID(), aValue, aTriggeringPrincipal,
   1819          compliantStringHolder, aError);
   1820  if (aError.Failed()) {
   1821    return;
   1822  }
   1823  aError = SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(),
   1824                   *compliantString, aTriggeringPrincipal, true);
   1825 }
   1826 
   1827 void Element::SetAttributeDevtools(const nsAString& aName,
   1828                                   const nsAString& aValue,
   1829                                   ErrorResult& aError) {
   1830  // Run this through SetAttribute with a devtools-ready principal.
   1831  RefPtr<nsIPrincipal> dtPrincipal = CreateDevtoolsPrincipal();
   1832  SetAttribute(aName, aValue, dtPrincipal, aError);
   1833 }
   1834 
   1835 void Element::SetAttributeDevtoolsNS(const nsAString& aNamespaceURI,
   1836                                     const nsAString& aLocalName,
   1837                                     const nsAString& aValue,
   1838                                     ErrorResult& aError) {
   1839  // Run this through SetAttributeNS with a devtools-ready principal.
   1840  RefPtr<nsIPrincipal> dtPrincipal = CreateDevtoolsPrincipal();
   1841  SetAttributeNS(aNamespaceURI, aLocalName, aValue, dtPrincipal, aError);
   1842 }
   1843 
   1844 void Element::RemoveAttributeNS(const nsAString& aNamespaceURI,
   1845                                const nsAString& aLocalName,
   1846                                ErrorResult& aError) {
   1847  RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName);
   1848  int32_t nsid = nsNameSpaceManager::GetInstance()->GetNameSpaceID(
   1849      aNamespaceURI, nsContentUtils::IsChromeDoc(OwnerDoc()));
   1850 
   1851  if (nsid == kNameSpaceID_Unknown) {
   1852    // If the namespace ID is unknown, it means there can't possibly be an
   1853    // existing attribute. We would need a known namespace ID to pass into
   1854    // UnsetAttr, so we return early if we don't have one.
   1855    return;
   1856  }
   1857 
   1858  aError = UnsetAttr(nsid, name, true);
   1859 }
   1860 
   1861 Attr* Element::GetAttributeNodeNS(const nsAString& aNamespaceURI,
   1862                                  const nsAString& aLocalName) {
   1863  return GetAttributeNodeNSInternal(aNamespaceURI, aLocalName);
   1864 }
   1865 
   1866 Attr* Element::GetAttributeNodeNSInternal(const nsAString& aNamespaceURI,
   1867                                          const nsAString& aLocalName) {
   1868  return Attributes()->GetNamedItemNS(aNamespaceURI, aLocalName);
   1869 }
   1870 
   1871 already_AddRefed<Attr> Element::SetAttributeNodeNS(
   1872    Attr& aNewAttr, nsIPrincipal* aSubjectPrincipal, ErrorResult& aError) {
   1873  RefPtr<nsDOMAttributeMap> attrMap = Attributes();
   1874  return attrMap->SetNamedItemNS(aNewAttr, aSubjectPrincipal, aError);
   1875 }
   1876 
   1877 already_AddRefed<nsIHTMLCollection> Element::GetElementsByTagNameNS(
   1878    const nsAString& aNamespaceURI, const nsAString& aLocalName,
   1879    ErrorResult& aError) {
   1880  int32_t nameSpaceId = kNameSpaceID_Wildcard;
   1881 
   1882  if (!aNamespaceURI.EqualsLiteral("*")) {
   1883    aError = nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI,
   1884                                                                  nameSpaceId);
   1885    if (aError.Failed()) {
   1886      return nullptr;
   1887    }
   1888  }
   1889 
   1890  NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
   1891 
   1892  return NS_GetContentList(this, nameSpaceId, aLocalName);
   1893 }
   1894 
   1895 bool Element::HasAttributeNS(const nsAString& aNamespaceURI,
   1896                             const nsAString& aLocalName) const {
   1897  int32_t nsid = nsNameSpaceManager::GetInstance()->GetNameSpaceID(
   1898      aNamespaceURI, nsContentUtils::IsChromeDoc(OwnerDoc()));
   1899 
   1900  if (nsid == kNameSpaceID_Unknown) {
   1901    // Unknown namespace means no attr...
   1902    return false;
   1903  }
   1904 
   1905  RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName);
   1906  return HasAttr(nsid, name);
   1907 }
   1908 
   1909 already_AddRefed<nsIHTMLCollection> Element::GetElementsByClassName(
   1910    const nsAString& aClassNames) {
   1911  return nsContentUtils::GetElementsByClassName(this, aClassNames);
   1912 }
   1913 
   1914 bool Element::HasSharedRoot(const Element* aElement) const {
   1915  nsINode* root = SubtreeRoot();
   1916  nsINode* attrSubtreeRoot = aElement->SubtreeRoot();
   1917  do {
   1918    if (root == attrSubtreeRoot) {
   1919      return true;
   1920    }
   1921    auto* shadow = ShadowRoot::FromNode(root);
   1922    if (!shadow || !shadow->GetHost()) {
   1923      break;
   1924    }
   1925    root = shadow->GetHost()->SubtreeRoot();
   1926  } while (true);
   1927  return false;
   1928 }
   1929 
   1930 Element* Element::GetElementByIdInDocOrSubtree(nsAtom* aID) const {
   1931  if (auto* docOrShadowRoot = GetContainingDocumentOrShadowRoot()) {
   1932    return docOrShadowRoot->GetElementById(aID);
   1933  }
   1934 
   1935  return nsContentUtils::MatchElementId(SubtreeRoot()->AsContent(), aID);
   1936 }
   1937 
   1938 Element* Element::GetAttrAssociatedElement(nsAtom* aAttr) const {
   1939  if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
   1940    nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElementMap.Get(aAttr);
   1941    if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl)) {
   1942      // If reflectedTarget's explicitly set attr-element |attrEl| is
   1943      // a descendant of any of element's shadow-including ancestors, then
   1944      // return |atrEl|.
   1945      if (HasSharedRoot(attrEl)) {
   1946        return attrEl;
   1947      }
   1948      return nullptr;
   1949    }
   1950  }
   1951 
   1952  const nsAttrValue* value = GetParsedAttr(aAttr);
   1953  if (!value) {
   1954    return nullptr;
   1955  }
   1956 
   1957  MOZ_ASSERT(value->Type() == nsAttrValue::eAtom,
   1958             "Attribute used for attr associated element must be parsed");
   1959 
   1960  return GetElementByIdInDocOrSubtree(value->GetAtomValue());
   1961 }
   1962 
   1963 void Element::GetAttrAssociatedElements(
   1964    nsAtom* aAttr, bool* aUseCachedValue,
   1965    Nullable<nsTArray<RefPtr<Element>>>& aElements) {
   1966  MOZ_ASSERT(aElements.IsNull());
   1967 
   1968  auto& [explicitlySetAttrElements, cachedAttrElements] =
   1969      ExtendedDOMSlots()->mAttrElementsMap.LookupOrInsert(aAttr);
   1970 
   1971  // https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#attr-associated-elements
   1972  auto getAttrAssociatedElements =
   1973      [&, &explicitlySetAttrElements =
   1974              explicitlySetAttrElements]() -> Maybe<nsTArray<RefPtr<Element>>> {
   1975    nsTArray<RefPtr<Element>> elements;
   1976 
   1977    if (explicitlySetAttrElements) {
   1978      // 3. If reflectedTarget's explicitly set attr-elements is not null
   1979      for (const nsWeakPtr& weakEl : *explicitlySetAttrElements) {
   1980        // For each attrElement in reflectedTarget's explicitly set
   1981        // attr-elements:
   1982        if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakEl)) {
   1983          // If attrElement is not a descendant of any of element's
   1984          // shadow-including ancestors, then continue.
   1985          if (!HasSharedRoot(attrEl)) {
   1986            continue;
   1987          }
   1988          // Append attrElement to elements.
   1989          elements.AppendElement(attrEl);
   1990        }
   1991      }
   1992    } else {
   1993      // 4. Otherwise
   1994      //   1. Let contentAttributeValue be the result of running
   1995      //   reflectedTarget's get the content attribute.
   1996      const nsAttrValue* value = GetParsedAttr(aAttr);
   1997      //   2. If contentAttributeValue is null, then return null.
   1998      if (!value) {
   1999        return Nothing();
   2000      }
   2001 
   2002      //   3. Let tokens be contentAttributeValue, split on ASCII whitespace.
   2003      MOZ_ASSERT(value->Type() == nsAttrValue::eAtomArray ||
   2004                     value->Type() == nsAttrValue::eAtom,
   2005                 "Attribute used for attr associated elements must be parsed");
   2006      for (uint32_t i = 0; i < value->GetAtomCount(); i++) {
   2007        // For each id of tokens:
   2008        if (auto* candidate = GetElementByIdInDocOrSubtree(
   2009                value->AtomAt(static_cast<int32_t>(i)))) {
   2010          // Append candidate to elements.
   2011          elements.AppendElement(candidate);
   2012        }
   2013      }
   2014    }
   2015 
   2016    return Some(std::move(elements));
   2017  };
   2018 
   2019  // getter steps:
   2020  // 1. Let elements be the result of running this's get the attr-associated
   2021  // elements.
   2022  auto elements = getAttrAssociatedElements();
   2023 
   2024  if (elements && elements == cachedAttrElements) {
   2025    // 2. If the contents of elements is equal to the contents of this's cached
   2026    // attr-associated elements, then return this's cached attr-associated
   2027    // elements object.
   2028    MOZ_ASSERT(!*aUseCachedValue);
   2029    *aUseCachedValue = true;
   2030    return;
   2031  }
   2032 
   2033  // 3. Let elementsAsFrozenArray be elements, converted to a FrozenArray<T>?.
   2034  //    (the binding code takes aElements and returns it as a FrozenArray)
   2035  // 5. Set this's cached attr-associated elements object to
   2036  //    elementsAsFrozenArray.
   2037  //    (the binding code stores the attr-associated elements object in a slot)
   2038  // 6. Return elementsAsFrozenArray.
   2039  if (elements) {
   2040    aElements.SetValue(elements->Clone());
   2041  }
   2042 
   2043  // 4. Set this's cached attr-associated elements to elements.
   2044  cachedAttrElements = std::move(elements);
   2045 }
   2046 
   2047 void Element::ClearExplicitlySetAttrElement(nsAtom* aAttr) {
   2048  if (auto* slots = GetExistingExtendedDOMSlots()) {
   2049    slots->mExplicitlySetAttrElementMap.Remove(aAttr);
   2050  }
   2051 }
   2052 
   2053 void Element::ClearExplicitlySetAttrElements(nsAtom* aAttr) {
   2054  if (auto* slots = GetExistingExtendedDOMSlots()) {
   2055    slots->mAttrElementsMap.Remove(aAttr);
   2056  }
   2057 }
   2058 
   2059 void Element::ExplicitlySetAttrElement(nsAtom* aAttr, Element* aElement) {
   2060 #ifdef ACCESSIBILITY
   2061  nsAccessibilityService* accService = GetAccService();
   2062 #endif
   2063  // Accessibility requires that no other attribute changes occur between
   2064  // AttrElementWillChange and AttrElementChanged. Scripts could cause
   2065  // this, so don't let them run here. We do this even if accessibility isn't
   2066  // running so that the JS behavior is consistent regardless of accessibility.
   2067  // Otherwise, JS might be able to use this difference to determine whether
   2068  // accessibility is running, which would be a privacy concern.
   2069  nsAutoScriptBlocker scriptBlocker;
   2070  if (aElement) {
   2071 #ifdef ACCESSIBILITY
   2072    if (accService) {
   2073      accService->NotifyAttrElementWillChange(this, aAttr);
   2074    }
   2075 #endif
   2076    SetAttr(aAttr, EmptyString(), IgnoreErrors());
   2077    nsExtendedDOMSlots* slots = ExtendedDOMSlots();
   2078    slots->mExplicitlySetAttrElementMap.InsertOrUpdate(
   2079        aAttr, do_GetWeakReference(aElement));
   2080 #ifdef ACCESSIBILITY
   2081    if (accService) {
   2082      accService->NotifyAttrElementChanged(this, aAttr);
   2083    }
   2084 #endif
   2085    return;
   2086  }
   2087 
   2088 #ifdef ACCESSIBILITY
   2089  if (accService) {
   2090    accService->NotifyAttrElementWillChange(this, aAttr);
   2091  }
   2092 #endif
   2093  ClearExplicitlySetAttrElement(aAttr);
   2094  UnsetAttr(aAttr, IgnoreErrors());
   2095 #ifdef ACCESSIBILITY
   2096  if (accService) {
   2097    accService->NotifyAttrElementChanged(this, aAttr);
   2098  }
   2099 #endif
   2100 }
   2101 
   2102 void Element::ExplicitlySetAttrElements(
   2103    nsAtom* aAttr,
   2104    const Nullable<Sequence<OwningNonNull<Element>>>& aElements) {
   2105 #ifdef ACCESSIBILITY
   2106  nsAccessibilityService* accService = GetAccService();
   2107 #endif
   2108  // Accessibility requires that no other attribute changes occur between
   2109  // AttrElementWillChange and AttrElementChanged. Scripts could cause
   2110  // this, so don't let them run here. We do this even if accessibility isn't
   2111  // running so that the JS behavior is consistent regardless of accessibility.
   2112  // Otherwise, JS might be able to use this difference to determine whether
   2113  // accessibility is running, which would be a privacy concern.
   2114  nsAutoScriptBlocker scriptBlocker;
   2115 
   2116 #ifdef ACCESSIBILITY
   2117  if (accService) {
   2118    accService->NotifyAttrElementWillChange(this, aAttr);
   2119  }
   2120 #endif
   2121 
   2122  if (aElements.IsNull()) {
   2123    ClearExplicitlySetAttrElements(aAttr);
   2124    UnsetAttr(aAttr, IgnoreErrors());
   2125  } else {
   2126    SetAttr(aAttr, EmptyString(), IgnoreErrors());
   2127    auto& entry = ExtendedDOMSlots()->mAttrElementsMap.LookupOrInsert(aAttr);
   2128    entry.first.emplace(nsTArray<nsWeakPtr>());
   2129    for (Element* el : aElements.Value()) {
   2130      entry.first->AppendElement(do_GetWeakReference(el));
   2131    }
   2132  }
   2133 
   2134 #ifdef ACCESSIBILITY
   2135  if (accService) {
   2136    accService->NotifyAttrElementChanged(this, aAttr);
   2137  }
   2138 #endif
   2139 }
   2140 
   2141 Element* Element::GetExplicitlySetAttrElement(nsAtom* aAttr) const {
   2142  if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
   2143    nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElementMap.Get(aAttr);
   2144    if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl)) {
   2145      return attrEl;
   2146    }
   2147  }
   2148  return nullptr;
   2149 }
   2150 
   2151 void Element::GetExplicitlySetAttrElements(
   2152    nsAtom* aAttr, nsTArray<Element*>& aElements) const {
   2153  if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
   2154    if (auto attrElementsMaybeEntry = slots->mAttrElementsMap.Lookup(aAttr)) {
   2155      auto& [attrElements, cachedAttrElements] = attrElementsMaybeEntry.Data();
   2156      if (attrElements) {
   2157        for (const nsWeakPtr& weakEl : *attrElements) {
   2158          if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakEl)) {
   2159            aElements.AppendElement(attrEl);
   2160          }
   2161        }
   2162      }
   2163    }
   2164  }
   2165 }
   2166 
   2167 void Element::GetElementsWithGrid(nsTArray<RefPtr<Element>>& aElements) {
   2168  dom::TreeIterator<dom::StyleChildrenIterator> iter(*this);
   2169  while (nsIContent* cur = iter.GetCurrent()) {
   2170    if (cur->IsElement()) {
   2171      Element* elem = cur->AsElement();
   2172      if (elem->GetPrimaryFrame()) {
   2173        // See if this has a GridContainerFrame. Use the same method that
   2174        // nsGridContainerFrame uses, which deals with some edge cases.
   2175        if (nsGridContainerFrame::GetGridContainerFrame(
   2176                elem->GetPrimaryFrame())) {
   2177          aElements.AppendElement(elem);
   2178        }
   2179      }
   2180 
   2181      // Only allow the traversal to go through the children if the element
   2182      // does have a display.
   2183      if (elem->HasServoData()) {
   2184        iter.GetNext();
   2185        continue;
   2186      }
   2187    }
   2188 
   2189    // Either this isn't an element, or it has `display: none`.
   2190    // Continue with the traversal but ignore all the children.
   2191    iter.GetNextSkippingChildren();
   2192  }
   2193 }
   2194 
   2195 bool Element::HasVisibleScrollbars() {
   2196  ScrollContainerFrame* scrollFrame = GetScrollContainerFrame();
   2197  return scrollFrame && !scrollFrame->GetScrollbarVisibility().isEmpty();
   2198 }
   2199 
   2200 // Hash function for bloom filter (k=2)
   2201 // Returns 64-bit value with bit 0 set to 1 and 2 bits set in available range.
   2202 static uint64_t HashForBloomFilter(const nsAtom* aAtom) {
   2203  if (!aAtom) {
   2204    return 1ULL;  // Just the tag bit
   2205  }
   2206  // On 32-bit platforms, we have 31 bits for bloom + 1 tag bit
   2207  // On 64-bit platforms, we have 63 bits for bloom + 1 tag bit
   2208  constexpr int kAttrBloomBits = sizeof(uintptr_t) == 4 ? 31 : 63;
   2209 
   2210  uint32_t hash = aAtom->hash();
   2211  uint64_t filter = 1ULL;
   2212  // Set 2 bits in the available range (bits 1-31 on 32-bit, 1-63 on 64-bit)
   2213  uint32_t bit1 = hash % kAttrBloomBits;
   2214  uint32_t bit2 = (hash >> 6) % kAttrBloomBits;
   2215  filter |= 1ULL << (1 + bit1);
   2216  filter |= 1ULL << (1 + bit2);
   2217  return filter;
   2218 }
   2219 
   2220 // Propagates this element's bloom filter up the tree by OR-ing it with
   2221 // all ancestor element bloom filters, stopping early if no new bits are added.
   2222 void Element::PropagateBloomFilterToParents() {
   2223  Element* toUpdate = this;
   2224  Element* parent = GetParentElement();
   2225 
   2226  while (parent) {
   2227    uint64_t childBloom = toUpdate->mAttrs.GetSubtreeBloomFilter();
   2228    uint64_t parentBloom = parent->mAttrs.GetSubtreeBloomFilter();
   2229 
   2230    // Check if parent already contains all child bits
   2231    if ((parentBloom & childBloom) == childBloom) {
   2232      break;
   2233    }
   2234    parent->mAttrs.SetSubtreeBloomFilter(parentBloom | childBloom);
   2235    toUpdate = parent;
   2236    parent = toUpdate->GetParentElement();
   2237  }
   2238 }
   2239 
   2240 // Hashes all class names in a class attribute value for the bloom filter.
   2241 // Handles both single class (eAtom) and multiple classes (eAtomArray).
   2242 static uint64_t HashClassesForBloom(const nsAttrValue* aValue) {
   2243  uint64_t filter = 1ULL;  // Start with tag bit
   2244  if (!aValue) {
   2245    return filter;
   2246  }
   2247 
   2248  if (aValue->Type() == nsAttrValue::eAtomArray) {
   2249    const mozilla::AttrAtomArray* array = aValue->GetAtomArrayValue();
   2250    if (array) {
   2251      for (const RefPtr<nsAtom>& className : array->mArray) {
   2252        filter |= HashForBloomFilter(className);
   2253      }
   2254    }
   2255  } else if (aValue->Type() == nsAttrValue::eAtom) {
   2256    filter |= HashForBloomFilter(aValue->GetAtomValue());
   2257  }
   2258 #ifdef DEBUG
   2259  else {
   2260    // Assert that only empty strings make it here.
   2261    nsAutoString value;
   2262    aValue->ToString(value);
   2263    bool isOnlyWhitespace = true;
   2264    for (uint32_t i = 0; i < value.Length(); i++) {
   2265      if (!nsContentUtils::IsHTMLWhitespace(value[i])) {
   2266        isOnlyWhitespace = false;
   2267        break;
   2268      }
   2269    }
   2270    MOZ_ASSERT(isOnlyWhitespace, "Expecting only empty strings here.");
   2271  }
   2272 #endif
   2273 
   2274  return filter;
   2275 }
   2276 
   2277 #ifdef DEBUG
   2278 // Asserts that the bloom filter contains all expected bits from
   2279 // current attributes, classes, and descendant bloom filters.
   2280 void Element::VerifySubtreeBloomFilter() const {
   2281  uint64_t expectedBloom = 1ULL;
   2282 
   2283  // Hash all attribute names in kNameSpaceID_None namespace
   2284  uint32_t attrCount = GetAttrCount();
   2285  for (uint32_t i = 0; i < attrCount; i++) {
   2286    const nsAttrName* attrName = GetAttrNameAt(i);
   2287    MOZ_ASSERT(attrName, "Attribute name should not be null");
   2288    if (attrName->NamespaceEquals(kNameSpaceID_None)) {
   2289      nsAtom* localName = attrName->LocalName();
   2290      expectedBloom |= HashForBloomFilter(localName);
   2291 
   2292      if (!localName->IsAsciiLowercase()) {
   2293        Document* doc = OwnerDoc();
   2294        if (!IsHTMLElement() && doc->IsHTMLDocument()) {
   2295          RefPtr<nsAtom> lowercaseAttr(localName);
   2296          ToLowerCaseASCII(lowercaseAttr);
   2297          expectedBloom |= HashForBloomFilter(lowercaseAttr);
   2298        }
   2299      }
   2300    }
   2301  }
   2302 
   2303  // Hash class names
   2304  expectedBloom |= HashClassesForBloom(GetClasses());
   2305 
   2306  // Include children's bloom filters
   2307  for (Element* child = GetFirstElementChild(); child;
   2308       child = child->GetNextElementSibling()) {
   2309    expectedBloom |= child->mAttrs.GetSubtreeBloomFilter();
   2310  }
   2311 
   2312  uint64_t actualBloom = mAttrs.GetSubtreeBloomFilter();
   2313  // Bloom filters are append-only: bits can be set but never cleared.
   2314  // So actualBloom may contain extra bits from removed attributes.
   2315  // We only check that all expected bits are present.
   2316  MOZ_ASSERT((actualBloom & expectedBloom) == expectedBloom,
   2317             "Bloom filter missing required bits");
   2318 }
   2319 #endif
   2320 
   2321 void Element::UpdateSubtreeBloomFilterForClass(const nsAttrValue* aClassValue) {
   2322  if (!aClassValue) {
   2323    return;
   2324  }
   2325  mAttrs.UpdateSubtreeBloomFilter(HashClassesForBloom(aClassValue));
   2326 }
   2327 
   2328 void Element::UpdateSubtreeBloomFilterForAttribute(nsAtom* aAttribute) {
   2329  MOZ_ASSERT(aAttribute, "Attribute should not be null");
   2330  mAttrs.UpdateSubtreeBloomFilter(HashForBloomFilter(aAttribute));
   2331 
   2332  // For non-HTML elements, also add the lowercase hash.
   2333  // This ensures querySelector can find these attributes with case-insensitive
   2334  // matching in HTML documents, even if the element is moved to an HTML
   2335  // document after attributes are set.
   2336  if (!aAttribute->IsAsciiLowercase() && !IsHTMLElement()) {
   2337    RefPtr<nsAtom> lowercaseAttr(aAttribute);
   2338    ToLowerCaseASCII(lowercaseAttr);
   2339    mAttrs.UpdateSubtreeBloomFilter(HashForBloomFilter(lowercaseAttr));
   2340  }
   2341 }
   2342 
   2343 nsresult Element::BindToTree(BindContext& aContext, nsINode& aParent) {
   2344  MOZ_ASSERT(aParent.IsContent() || aParent.IsDocument(),
   2345             "Must have content or document parent!");
   2346  MOZ_ASSERT(aParent.OwnerDoc() == OwnerDoc(),
   2347             "Must have the same owner document");
   2348  MOZ_ASSERT(OwnerDoc() == &aContext.OwnerDoc(), "These should match too");
   2349  MOZ_ASSERT(!IsInUncomposedDoc(), "Already have a document.  Unbind first!");
   2350  MOZ_ASSERT(!IsInComposedDoc(), "Already have a document.  Unbind first!");
   2351  // Note that as we recurse into the kids, they'll have a non-null parent.  So
   2352  // only assert if our parent is _changing_ while we have a parent.
   2353  MOZ_ASSERT(!GetParentNode() || &aParent == GetParentNode(),
   2354             "Already have a parent.  Unbind first!");
   2355 
   2356  const bool hadParent = !!GetParentNode();
   2357 
   2358  if (aParent.IsInNativeAnonymousSubtree()) {
   2359    SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
   2360  }
   2361  if (IsRootOfNativeAnonymousSubtree()) {
   2362    aParent.SetMayHaveAnonymousChildren();
   2363  } else if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
   2364    SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
   2365  }
   2366  if (aParent.HasFlag(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR)) {
   2367    SetFlags(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR);
   2368  }
   2369  aParent.SetFlags(NODE_MAY_HAVE_ELEMENT_CHILDREN);
   2370 
   2371  // Now set the parent.
   2372  mParent = &aParent;
   2373  if (!hadParent && aParent.IsContent()) {
   2374    SetParentIsContent(true);
   2375    NS_ADDREF(mParent);
   2376  }
   2377  MOZ_ASSERT(!!GetParent() == aParent.IsContent());
   2378 
   2379  MOZ_ASSERT_IF(!aContext.IsMove(),
   2380                !HasAnyOfFlags(Element::kAllServoDescendantBits));
   2381 
   2382  // Finally, set the document
   2383  if (aParent.IsInUncomposedDoc() || aParent.IsInShadowTree()) {
   2384    // We no longer need to track the subtree pointer (and in fact we'll assert
   2385    // if we do this any later).
   2386    ClearSubtreeRootPointer();
   2387    SetIsConnected(aParent.IsInComposedDoc());
   2388 
   2389    if (aParent.IsInUncomposedDoc()) {
   2390      SetIsInDocument();
   2391    } else {
   2392      SetFlags(NODE_IS_IN_SHADOW_TREE);
   2393      MOZ_ASSERT(aParent.IsContent() &&
   2394                 aParent.AsContent()->GetContainingShadow());
   2395      ExtendedDOMSlots()->mContainingShadow =
   2396          aParent.AsContent()->GetContainingShadow();
   2397    }
   2398    // Clear the lazy frame construction bits.
   2399    UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
   2400  } else {
   2401    // If we're not in the doc and not in a shadow tree,
   2402    // update our subtree pointer.
   2403    SetSubtreeRootPointer(aParent.SubtreeRoot());
   2404  }
   2405 
   2406  if (IsInComposedDoc()) {
   2407    if (IsPendingMappedAttributeEvaluation()) {
   2408      aContext.OwnerDoc().ScheduleForPresAttrEvaluation(this);
   2409    }
   2410    // Connected callback must be enqueued whenever a custom element becomes
   2411    // connected.
   2412    if (CustomElementData* data = GetCustomElementData()) {
   2413      if (data->mState == CustomElementData::State::eCustom) {
   2414        nsContentUtils::EnqueueLifecycleCallback(
   2415            aContext.IsMove() ? ElementCallbackType::eConnectedMove
   2416                              : ElementCallbackType::eConnected,
   2417            this, {});
   2418      } else {
   2419        // Step 7.7.2.2 https://dom.spec.whatwg.org/#concept-node-insert
   2420        nsContentUtils::TryToUpgradeElement(this);
   2421      }
   2422    }
   2423  }
   2424 
   2425  // This has to be here, rather than in nsGenericHTMLElement::BindToTree,
   2426  //  because it has to happen after updating the parent pointer, but before
   2427  //  recursively binding the kids.
   2428  SetDirOnBind(this, nsIContent::FromNode(aParent));
   2429 
   2430  UpdateEditableState(false);
   2431 
   2432  // Call BindToTree on shadow root children.
   2433  nsresult rv;
   2434  if (ShadowRoot* shadowRoot = GetShadowRoot()) {
   2435    rv = shadowRoot->Bind();
   2436    NS_ENSURE_SUCCESS(rv, rv);
   2437  }
   2438 
   2439  // Now recurse into our kids. Ensure this happens after binding the shadow
   2440  // root so that directionality of slots is updated.
   2441  {
   2442    for (nsIContent* child = GetFirstChild(); child;
   2443         child = child->GetNextSibling()) {
   2444      rv = child->BindToTree(aContext, *this);
   2445      NS_ENSURE_SUCCESS(rv, rv);
   2446    }
   2447  }
   2448 
   2449  MutationObservers::NotifyParentChainChanged(this);
   2450 
   2451  // Ensure we only run this once, in the case we move the ShadowRoot around.
   2452  if (aContext.SubtreeRootChanges()) {
   2453    if (HasPartAttribute()) {
   2454      if (ShadowRoot* shadow = GetContainingShadow()) {
   2455        shadow->PartAdded(*this);
   2456      }
   2457    }
   2458    if (HasID()) {
   2459      AddToIdTable(DoGetID());
   2460    }
   2461    HandleShadowDOMRelatedInsertionSteps(hadParent);
   2462  }
   2463 
   2464  if (MayHaveStyle()) {
   2465    // If MayHaveStyle() is true, we must be an nsStyledElement.
   2466    static_cast<nsStyledElement*>(this)->ReparseStyleAttribute(
   2467        /* aForceInDataDoc = */ false);
   2468  }
   2469 
   2470  // XXXbz script execution during binding can trigger some of these
   2471  // postcondition asserts....  But we do want that, since things will
   2472  // generally be quite broken when that happens.
   2473  MOZ_ASSERT(OwnerDoc() == aParent.OwnerDoc(), "Bound to wrong document");
   2474  MOZ_ASSERT(IsInComposedDoc() == aContext.InComposedDoc());
   2475  MOZ_ASSERT(IsInUncomposedDoc() == aContext.InUncomposedDoc());
   2476  MOZ_ASSERT(&aParent == GetParentNode(), "Bound to wrong parent node");
   2477  MOZ_ASSERT(aParent.IsInUncomposedDoc() == IsInUncomposedDoc());
   2478  MOZ_ASSERT(aParent.IsInComposedDoc() == IsInComposedDoc());
   2479  MOZ_ASSERT(aParent.IsInShadowTree() == IsInShadowTree());
   2480  MOZ_ASSERT(aParent.SubtreeRoot() == SubtreeRoot());
   2481 
   2482 #ifdef DEBUG
   2483  VerifySubtreeBloomFilter();
   2484 #endif
   2485 
   2486  // When binding to tree, propagate this element's bloom to parents.
   2487  PropagateBloomFilterToParents();
   2488  return NS_OK;
   2489 }
   2490 
   2491 static bool WillDetachFromShadowOnUnbind(const Element& aElement,
   2492                                         bool aNullParent) {
   2493  // If our parent still is in a shadow tree by now, and we're not removing
   2494  // ourselves from it, then we're still going to be in a shadow tree after
   2495  // this.
   2496  return aElement.IsInShadowTree() &&
   2497         (aNullParent || !aElement.GetParent()->IsInShadowTree());
   2498 }
   2499 
   2500 void Element::UnbindFromTree(UnbindContext& aContext) {
   2501  const bool nullParent = aContext.IsUnbindRoot(this);
   2502 
   2503  HandleShadowDOMRelatedRemovalSteps(nullParent);
   2504 
   2505  if (HasFlag(ELEMENT_IN_CONTENT_IDENTIFIER_FOR_LCP)) {
   2506    OwnerDoc()->ContentIdentifiersForLCP().Remove(this);
   2507    UnsetFlags(ELEMENT_IN_CONTENT_IDENTIFIER_FOR_LCP);
   2508  }
   2509 
   2510  if (HasFlag(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR) &&
   2511      !IsHTMLElement(nsGkAtoms::datalist)) {
   2512    if (nullParent) {
   2513      UnsetFlags(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR);
   2514    } else {
   2515      nsIContent* parent = GetParent();
   2516      MOZ_ASSERT(parent);
   2517      if (!parent->HasFlag(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR)) {
   2518        UnsetFlags(ELEMENT_IS_DATALIST_OR_HAS_DATALIST_ANCESTOR);
   2519      }
   2520    }
   2521  }
   2522 
   2523  const bool detachingFromShadow =
   2524      WillDetachFromShadowOnUnbind(*this, nullParent);
   2525  // Make sure to only remove from the ID table if our subtree root is actually
   2526  // changing.
   2527  if (IsInUncomposedDoc() || detachingFromShadow) {
   2528    RemoveFromIdTable();
   2529  }
   2530 
   2531  if (detachingFromShadow && HasPartAttribute()) {
   2532    if (ShadowRoot* shadow = GetContainingShadow()) {
   2533      shadow->PartRemoved(*this);
   2534    }
   2535  }
   2536 
   2537  // Make sure to unbind this node before doing the kids
   2538  Document* document = GetComposedDoc();
   2539 
   2540  if (HasPointerLock()) {
   2541    PointerLockManager::Unlock("Element::UnbindFromTree");
   2542  }
   2543  if (!aContext.IsMove() && mState.HasState(ElementState::FULLSCREEN)) {
   2544    // The element being removed is an ancestor of the fullscreen element,
   2545    // exit fullscreen state.
   2546    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
   2547                                    OwnerDoc(), nsContentUtils::eDOM_PROPERTIES,
   2548                                    "RemovedFullscreenElement");
   2549    // Fully exit fullscreen.
   2550    Document::ExitFullscreenInDocTree(OwnerDoc());
   2551  }
   2552 
   2553  MOZ_ASSERT_IF(HasServoData(), document);
   2554  MOZ_ASSERT_IF(HasServoData() && !aContext.IsMove(),
   2555                IsInNativeAnonymousSubtree());
   2556  if (document && !aContext.IsMove()) {
   2557    ClearServoData(document);
   2558  }
   2559 
   2560  // Ensure that CSS transitions don't continue on an element at a
   2561  // different place in the tree (even if reinserted before next
   2562  // animation refresh).
   2563  //
   2564  // We need to delete the properties while we're still in document
   2565  // (if we were in document) so that they can look up the
   2566  // PendingAnimationTracker on the document and remove their animations,
   2567  // and so they can find their pres context for dispatching cancel events.
   2568  //
   2569  // FIXME(bug 522599): Need a test for this.
   2570  // FIXME(emilio): Why not clearing the effect set as well?
   2571  if (!aContext.IsMove()) {
   2572    if (auto* data = GetAnimationData()) {
   2573      data->ClearAllAnimationCollections();
   2574    }
   2575  }
   2576 
   2577  if (nullParent) {
   2578    if (GetParent()) {
   2579      RefPtr<nsINode> p;
   2580      p.swap(mParent);
   2581    } else {
   2582      mParent = nullptr;
   2583    }
   2584    SetParentIsContent(false);
   2585  }
   2586 
   2587 #ifdef DEBUG
   2588  // If we can get access to the PresContext, then we sanity-check that
   2589  // we're not leaving behind a pointer to ourselves as the PresContext's
   2590  // cached provider of the viewport's scrollbar styles.
   2591  if (document) {
   2592    nsPresContext* presContext = document->GetPresContext();
   2593    if (presContext) {
   2594      MOZ_ASSERT(this != presContext->GetViewportScrollStylesOverrideElement(),
   2595                 "Leaving behind a raw pointer to this element (as having "
   2596                 "propagated scrollbar styles) - that's dangerous...");
   2597    }
   2598  }
   2599 
   2600 #  ifdef ACCESSIBILITY
   2601  MOZ_ASSERT(!GetAccService() || !GetAccService()->HasAccessible(this),
   2602             "An accessible for this element still exists!");
   2603 #  endif
   2604 #endif
   2605 
   2606  ClearInDocument();
   2607  SetIsConnected(false);
   2608  if (HasElementCreatedFromPrototypeAndHasUnmodifiedL10n()) {
   2609    if (document) {
   2610      document->mL10nProtoElements.Remove(this);
   2611    }
   2612    ClearElementCreatedFromPrototypeAndHasUnmodifiedL10n();
   2613  }
   2614 
   2615  if (nullParent || !mParent->IsInShadowTree()) {
   2616    UnsetFlags(NODE_IS_IN_SHADOW_TREE);
   2617 
   2618    // Begin keeping track of our subtree root.
   2619    SetSubtreeRootPointer(nullParent ? this : mParent->SubtreeRoot());
   2620 
   2621    if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
   2622      slots->mContainingShadow = nullptr;
   2623    }
   2624  }
   2625 
   2626  if (document) {
   2627    // Disconnected must be enqueued whenever a connected custom element becomes
   2628    // disconnected.
   2629    if (CustomElementData* data = GetCustomElementData()) {
   2630      if (data->mState == CustomElementData::State::eCustom) {
   2631        if (!aContext.IsMove()) {
   2632          nsContentUtils::EnqueueLifecycleCallback(
   2633              ElementCallbackType::eDisconnected, this, {});
   2634        }
   2635      } else {
   2636        // Remove an unresolved custom element that is a candidate for upgrade
   2637        // when a custom element is disconnected.
   2638        nsContentUtils::UnregisterUnresolvedElement(this);
   2639      }
   2640    }
   2641 
   2642    if (IsPendingMappedAttributeEvaluation()) {
   2643      document->UnscheduleForPresAttrEvaluation(this);
   2644    }
   2645 
   2646    if (HasLastRememberedBSize() || HasLastRememberedISize()) {
   2647      // Make sure the element is observed so that remembered sizes are kept
   2648      // until the next time "ResizeObserver events are determined and
   2649      // delivered". See "Disconnected element" tests from
   2650      // css/css-sizing/contain-intrinsic-size/auto-006.html
   2651      document->ObserveForLastRememberedSize(*this);
   2652    }
   2653  }
   2654 
   2655  // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree,
   2656  //  because it has to happen after unsetting the parent pointer, but before
   2657  //  recursively unbinding the kids.
   2658  ResetDir(this);
   2659 
   2660  for (nsIContent* child = GetFirstChild(); child;
   2661       child = child->GetNextSibling()) {
   2662    child->UnbindFromTree(aContext);
   2663  }
   2664 
   2665  MutationObservers::NotifyParentChainChanged(this);
   2666 
   2667  // Unbind children of shadow root.
   2668  if (ShadowRoot* shadowRoot = GetShadowRoot()) {
   2669    shadowRoot->Unbind();
   2670  }
   2671 
   2672  MOZ_ASSERT_IF(!aContext.IsMove(), !HasAnyOfFlags(kAllServoDescendantBits));
   2673  MOZ_ASSERT_IF(!aContext.IsMove(),
   2674                !document || document->GetServoRestyleRoot() != this);
   2675 }
   2676 
   2677 UniquePtr<SMILAttr> Element::GetAnimatedAttr(int32_t aNamespaceID,
   2678                                             nsAtom* aName) {
   2679  return nullptr;
   2680 }
   2681 
   2682 nsDOMCSSAttributeDeclaration* Element::SMILOverrideStyle() {
   2683  Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
   2684 
   2685  if (!slots->mSMILOverrideStyle) {
   2686    slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, true);
   2687  }
   2688 
   2689  return slots->mSMILOverrideStyle;
   2690 }
   2691 
   2692 DeclarationBlock* Element::GetSMILOverrideStyleDeclaration() {
   2693  Element::nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
   2694  return slots ? slots->mSMILOverrideStyleDeclaration.get() : nullptr;
   2695 }
   2696 
   2697 void Element::SetSMILOverrideStyleDeclaration(DeclarationBlock& aDeclaration) {
   2698  ExtendedDOMSlots()->mSMILOverrideStyleDeclaration = &aDeclaration;
   2699 
   2700  // Only need to request a restyle if we're in a document.  (We might not
   2701  // be in a document, if we're clearing animation effects on a target node
   2702  // that's been detached since the previous animation sample.)
   2703  if (Document* doc = GetComposedDoc()) {
   2704    if (PresShell* presShell = doc->GetPresShell()) {
   2705      presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SMIL);
   2706    }
   2707  }
   2708 }
   2709 
   2710 bool Element::IsLabelable() const { return false; }
   2711 
   2712 bool Element::IsInteractiveHTMLContent() const { return false; }
   2713 
   2714 DeclarationBlock* Element::GetInlineStyleDeclaration() const {
   2715  if (!MayHaveStyle()) {
   2716    return nullptr;
   2717  }
   2718  const nsAttrValue* attrVal = mAttrs.GetAttr(nsGkAtoms::style);
   2719  if (!attrVal || attrVal->Type() != nsAttrValue::eCSSDeclaration) {
   2720    return nullptr;
   2721  }
   2722  return attrVal->GetCSSDeclarationValue();
   2723 }
   2724 
   2725 void Element::InlineStyleDeclarationWillChange(MutationClosureData& aData) {
   2726  MOZ_ASSERT_UNREACHABLE("Element::InlineStyleDeclarationWillChange");
   2727 }
   2728 
   2729 nsresult Element::SetInlineStyleDeclaration(DeclarationBlock& aDeclaration,
   2730                                            MutationClosureData& aData) {
   2731  MOZ_ASSERT_UNREACHABLE("Element::SetInlineStyleDeclaration");
   2732  return NS_ERROR_NOT_IMPLEMENTED;
   2733 }
   2734 
   2735 NS_IMETHODIMP_(bool)
   2736 Element::IsAttributeMapped(const nsAtom* aAttribute) const { return false; }
   2737 
   2738 nsMapRuleToAttributesFunc Element::GetAttributeMappingFunction() const {
   2739  return &MapNoAttributesInto;
   2740 }
   2741 
   2742 void Element::MapNoAttributesInto(mozilla::MappedDeclarationsBuilder&) {}
   2743 
   2744 nsChangeHint Element::GetAttributeChangeHint(const nsAtom* aAttribute,
   2745                                             AttrModType) const {
   2746  return nsChangeHint(0);
   2747 }
   2748 
   2749 void Element::SetMappedDeclarationBlock(
   2750    already_AddRefed<StyleLockedDeclarationBlock> aDeclarations) {
   2751  MOZ_ASSERT(IsPendingMappedAttributeEvaluation());
   2752  mAttrs.SetMappedDeclarationBlock(std::move(aDeclarations));
   2753  MOZ_ASSERT(!IsPendingMappedAttributeEvaluation());
   2754 }
   2755 
   2756 bool Element::FindAttributeDependence(const nsAtom* aAttribute,
   2757                                      const MappedAttributeEntry* const aMaps[],
   2758                                      uint32_t aMapCount) {
   2759  for (uint32_t mapindex = 0; mapindex < aMapCount; ++mapindex) {
   2760    for (const MappedAttributeEntry* map = aMaps[mapindex]; map->attribute;
   2761         ++map) {
   2762      if (aAttribute == map->attribute) {
   2763        return true;
   2764      }
   2765    }
   2766  }
   2767 
   2768  return false;
   2769 }
   2770 
   2771 already_AddRefed<mozilla::dom::NodeInfo> Element::GetExistingAttrNameFromQName(
   2772    const nsAString& aStr) const {
   2773  const nsAttrName* name = InternalGetAttrNameFromQName(aStr);
   2774  if (!name) {
   2775    return nullptr;
   2776  }
   2777 
   2778  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
   2779  if (name->IsAtom()) {
   2780    nodeInfo = mNodeInfo->NodeInfoManager()->GetNodeInfo(
   2781        name->Atom(), nullptr, kNameSpaceID_None, ATTRIBUTE_NODE);
   2782  } else {
   2783    nodeInfo = name->NodeInfo();
   2784  }
   2785 
   2786  return nodeInfo.forget();
   2787 }
   2788 
   2789 // static
   2790 bool Element::ShouldBlur(nsIContent* aContent) {
   2791  // Determine if the current element is focused, if it is not focused
   2792  // then we should not try to blur
   2793  Document* document = aContent->GetComposedDoc();
   2794  if (!document) return false;
   2795 
   2796  nsCOMPtr<nsPIDOMWindowOuter> window = document->GetWindow();
   2797  if (!window) return false;
   2798 
   2799  nsCOMPtr<nsPIDOMWindowOuter> focusedFrame;
   2800  nsIContent* contentToBlur = nsFocusManager::GetFocusedDescendant(
   2801      window, nsFocusManager::eOnlyCurrentWindow, getter_AddRefs(focusedFrame));
   2802 
   2803  if (!contentToBlur) {
   2804    return false;
   2805  }
   2806 
   2807  if (contentToBlur == aContent) {
   2808    return true;
   2809  }
   2810 
   2811  ShadowRoot* root = aContent->GetShadowRoot();
   2812  if (root && root->DelegatesFocus() &&
   2813      contentToBlur->IsShadowIncludingInclusiveDescendantOf(root)) {
   2814    return true;
   2815  }
   2816  return false;
   2817 }
   2818 
   2819 /* static */
   2820 nsresult Element::DispatchEvent(nsPresContext* aPresContext,
   2821                                WidgetEvent* aEvent, nsIContent* aTarget,
   2822                                bool aFullDispatch, nsEventStatus* aStatus) {
   2823  MOZ_ASSERT(aTarget, "Must have target");
   2824  MOZ_ASSERT(aEvent, "Must have source event");
   2825  MOZ_ASSERT(aStatus, "Null out param?");
   2826 
   2827  if (!aPresContext) {
   2828    return NS_OK;
   2829  }
   2830 
   2831  RefPtr<PresShell> presShell = aPresContext->GetPresShell();
   2832  if (!presShell) {
   2833    return NS_OK;
   2834  }
   2835 
   2836  if (aFullDispatch) {
   2837    return presShell->HandleEventWithTarget(aEvent, nullptr, aTarget, aStatus);
   2838  }
   2839 
   2840  return presShell->HandleDOMEventWithTarget(aTarget, aEvent, aStatus);
   2841 }
   2842 
   2843 /* static */
   2844 nsresult Element::DispatchClickEvent(nsPresContext* aPresContext,
   2845                                     WidgetInputEvent* aSourceEvent,
   2846                                     nsIContent* aTarget, bool aFullDispatch,
   2847                                     const EventFlags* aExtraEventFlags,
   2848                                     nsEventStatus* aStatus) {
   2849  MOZ_ASSERT(aTarget, "Must have target");
   2850  MOZ_ASSERT(aSourceEvent, "Must have source event");
   2851  MOZ_ASSERT(aStatus, "Null out param?");
   2852 
   2853  WidgetPointerEvent event(aSourceEvent->IsTrusted(), ePointerClick,
   2854                           aSourceEvent->mWidget);
   2855  event.mRefPoint = aSourceEvent->mRefPoint;
   2856  uint32_t clickCount = 1;
   2857  float pressure = 0;
   2858  uint32_t pointerId = 0;  // Use the default value here.
   2859  uint16_t inputSource = 0;
   2860  WidgetMouseEvent* sourceMouseEvent = aSourceEvent->AsMouseEvent();
   2861  if (sourceMouseEvent) {
   2862    clickCount = sourceMouseEvent->mClickCount;
   2863    pressure = sourceMouseEvent->mPressure;
   2864    pointerId = sourceMouseEvent->pointerId;
   2865    inputSource = sourceMouseEvent->mInputSource;
   2866  } else if (aSourceEvent->mClass == eKeyboardEventClass) {
   2867    event.mFlags.mIsPositionless = true;
   2868    inputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
   2869    // pointerId definition in Pointer Events:
   2870    // > The pointerId value of -1 MUST be reserved and used to indicate events
   2871    // > that were generated by something other than a pointing device.
   2872    pointerId = -1;
   2873  }
   2874  event.mPressure = pressure;
   2875  event.mClickCount = clickCount;
   2876  event.pointerId = pointerId;
   2877  event.mInputSource = inputSource;
   2878  event.mModifiers = aSourceEvent->mModifiers;
   2879  if (aExtraEventFlags) {
   2880    // Be careful not to overwrite existing flags!
   2881    event.mFlags.Union(*aExtraEventFlags);
   2882  }
   2883 
   2884  return DispatchEvent(aPresContext, &event, aTarget, aFullDispatch, aStatus);
   2885 }
   2886 
   2887 //----------------------------------------------------------------------
   2888 nsresult Element::LeaveLink(nsPresContext* aPresContext) {
   2889  if (!aPresContext || !aPresContext->Document()->LinkHandlingEnabled()) {
   2890    return NS_OK;
   2891  }
   2892  nsIDocShell* shell = aPresContext->Document()->GetDocShell();
   2893  if (!shell) {
   2894    return NS_OK;
   2895  }
   2896  return nsDocShell::Cast(shell)->OnLeaveLink();
   2897 }
   2898 
   2899 void Element::SetEventHandler(nsAtom* aEventName, const nsAString& aValue,
   2900                              bool aDefer) {
   2901  Document* ownerDoc = OwnerDoc();
   2902  if (ownerDoc->IsLoadedAsData()) {
   2903    // Make this a no-op rather than throwing an error to avoid
   2904    // the error causing problems setting the attribute.
   2905    return;
   2906  }
   2907 
   2908  MOZ_ASSERT(aEventName, "Must have event name!");
   2909  bool defer = true;
   2910  EventListenerManager* manager =
   2911      GetEventListenerManagerForAttr(aEventName, &defer);
   2912  if (!manager) {
   2913    return;
   2914  }
   2915 
   2916  defer = defer && aDefer;  // only defer if everyone agrees...
   2917  manager->SetEventHandler(aEventName, aValue, defer,
   2918                           !nsContentUtils::IsChromeDoc(ownerDoc), this);
   2919 }
   2920 
   2921 //----------------------------------------------------------------------
   2922 
   2923 const nsAttrName* Element::InternalGetAttrNameFromQName(
   2924    const nsAString& aStr, nsAutoString* aNameToUse) const {
   2925  MOZ_ASSERT(!aNameToUse || aNameToUse->IsEmpty());
   2926  const nsAttrName* val = nullptr;
   2927  if (IsHTMLElement() && IsInHTMLDocument()) {
   2928    nsAutoString lower;
   2929    nsAutoString& outStr = aNameToUse ? *aNameToUse : lower;
   2930    nsContentUtils::ASCIIToLower(aStr, outStr);
   2931    val = mAttrs.GetExistingAttrNameFromQName(outStr);
   2932    if (val) {
   2933      outStr.Truncate();
   2934    }
   2935  } else {
   2936    val = mAttrs.GetExistingAttrNameFromQName(aStr);
   2937    if (!val && aNameToUse) {
   2938      *aNameToUse = aStr;
   2939    }
   2940  }
   2941 
   2942  return val;
   2943 }
   2944 
   2945 bool Element::MaybeCheckSameAttrVal(int32_t aNamespaceID, const nsAtom* aName,
   2946                                    const nsAtom* aPrefix,
   2947                                    const nsAttrValueOrString& aValue,
   2948                                    bool aNotify, nsAttrValue& aOldValue,
   2949                                    AttrModType* aModType, bool* aOldValueSet) {
   2950  bool modification = false;
   2951  *aOldValueSet = false;
   2952 
   2953  // If we have no listeners and aNotify is false, we are almost certainly
   2954  // coming from the content sink and will almost certainly have no previous
   2955  // value.  Even if we do, setting the value is cheap when we don't plan to
   2956  // notify.  The check for aNotify here is an optimization.
   2957  if (aNotify) {
   2958    BorrowedAttrInfo info(GetAttrInfo(aNamespaceID, aName));
   2959    if (info.mValue) {
   2960      // Check whether the old value is the same as the new one.  Note that we
   2961      // only need to actually _get_ the old value if the element is a custom
   2962      // element (because it may have an attribute changed callback).
   2963      if (GetCustomElementData()) {
   2964        // Need to store the old value.
   2965        //
   2966        // If the current attribute value contains a pointer to some other data
   2967        // structure that gets updated in the process of setting the attribute
   2968        // we'll no longer have the old value of the attribute. Therefore, we
   2969        // should serialize the attribute value now to keep a snapshot.
   2970        aOldValue.SetToSerialized(*info.mValue);
   2971        *aOldValueSet = true;
   2972      }
   2973      bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
   2974      if (valueMatches && aPrefix == info.mName->GetPrefix()) {
   2975        return true;
   2976      }
   2977      modification = true;
   2978    }
   2979  }
   2980  *aModType = modification ? AttrModType::Modification : AttrModType::Addition;
   2981  return false;
   2982 }
   2983 
   2984 bool Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsAtom* aName,
   2985                                     nsAtom* aPrefix,
   2986                                     const nsAttrValueOrString& aValue,
   2987                                     bool aNotify, nsAttrValue& aOldValue,
   2988                                     AttrModType* aModType,
   2989                                     bool* aOldValueSet) {
   2990  if (!MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify,
   2991                             aOldValue, aModType, aOldValueSet)) {
   2992    return false;
   2993  }
   2994 
   2995  nsAutoScriptBlocker scriptBlocker;
   2996  MutationObservers::NotifyAttributeSetToCurrentValue(this, aNamespaceID,
   2997                                                      aName);
   2998  return true;
   2999 }
   3000 
   3001 nsresult Element::SetClassAttrFromParser(nsAtom* aValue) {
   3002  // Keep this in sync with SetAttr and SetParsedAttr below.
   3003 
   3004  nsAttrValue value;
   3005  value.ParseAtomArray(aValue);
   3006 
   3007  Document* document = GetComposedDoc();
   3008  mozAutoDocUpdate updateBatch(document, false);
   3009 
   3010  // In principle, BeforeSetAttr should be called here if a node type
   3011  // existed that wanted to do something special for class, but there
   3012  // is no such node type, so calling SetMayHaveClass() directly.
   3013  SetMayHaveClass();
   3014 
   3015  return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::_class,
   3016                          nullptr,  // prefix
   3017                          nullptr,  // old value
   3018                          value, nullptr, AttrModType::Addition,
   3019                          false,  // notify
   3020                          kCallAfterSetAttr, document, updateBatch);
   3021 }
   3022 
   3023 nsresult Element::SetAttr(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix,
   3024                          const nsAString& aValue,
   3025                          nsIPrincipal* aSubjectPrincipal, bool aNotify) {
   3026  // Keep this in sync with SetParsedAttr below and SetSingleClassFromParser
   3027  // above.
   3028  const nsAttrValueOrString valueForComparison(aValue);
   3029  return SetAttrInternal(aNamespaceID, aName, aPrefix, valueForComparison,
   3030                         aSubjectPrincipal, aNotify,
   3031                         [&](nsAttrValue& attrValue) {
   3032                           if (!ParseAttribute(aNamespaceID, aName, aValue,
   3033                                               aSubjectPrincipal, attrValue)) {
   3034                             attrValue.SetTo(aValue);
   3035                           }
   3036                         });
   3037 }
   3038 
   3039 nsresult Element::SetAndSwapAttr(nsAtom* aLocalName, nsAttrValue& aValue,
   3040                                 bool* aHadValue) {
   3041  MOZ_TRY(mAttrs.SetAndSwapAttr(aLocalName, aValue, aHadValue));
   3042 
   3043  if (aLocalName == nsGkAtoms::_class) {
   3044    UpdateSubtreeBloomFilterForClass(GetClasses());
   3045  }
   3046  UpdateSubtreeBloomFilterForAttribute(aLocalName);
   3047  PropagateBloomFilterToParents();
   3048 
   3049  return NS_OK;
   3050 }
   3051 
   3052 nsresult Element::SetAndSwapAttr(mozilla::dom::NodeInfo* aName,
   3053                                 nsAttrValue& aValue, bool* aHadValue) {
   3054  MOZ_TRY(mAttrs.SetAndSwapAttr(aName, aValue, aHadValue));
   3055 
   3056  // Only update bloom filter for null-namespace attributes, since the
   3057  // querySelector bloom filter optimization only applies to those.
   3058  if (aName->NamespaceEquals(kNameSpaceID_None)) {
   3059    nsAtom* localName = aName->NameAtom();
   3060    if (localName == nsGkAtoms::_class) {
   3061      UpdateSubtreeBloomFilterForClass(GetClasses());
   3062    }
   3063    UpdateSubtreeBloomFilterForAttribute(localName);
   3064    PropagateBloomFilterToParents();
   3065  }
   3066 
   3067  return NS_OK;
   3068 }
   3069 
   3070 nsresult Element::SetAttr(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix,
   3071                          nsAtom* aValue, nsIPrincipal* aSubjectPrincipal,
   3072                          bool aNotify) {
   3073  // Keep this in sync with SetParsedAttr below and SetSingleClassFromParser
   3074  // above.
   3075  const nsDependentAtomString valueString(aValue);
   3076  const nsAttrValueOrString valueForComparison(valueString);
   3077  return SetAttrInternal(aNamespaceID, aName, aPrefix, valueForComparison,
   3078                         aSubjectPrincipal, aNotify,
   3079                         [&](nsAttrValue& attrValue) {
   3080                           if (!ParseAttribute(aNamespaceID, aName, valueString,
   3081                                               aSubjectPrincipal, attrValue)) {
   3082                             attrValue.SetTo(aValue);
   3083                           }
   3084                         });
   3085 }
   3086 
   3087 template <typename ParseFunc>
   3088 nsresult Element::SetAttrInternal(int32_t aNamespaceID, nsAtom* aName,
   3089                                  nsAtom* aPrefix,
   3090                                  const nsAttrValueOrString& aValue,
   3091                                  nsIPrincipal* aSubjectPrincipal, bool aNotify,
   3092                                  ParseFunc&& aParseFn) {
   3093  NS_ENSURE_ARG_POINTER(aName);
   3094  NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
   3095               "Don't call SetAttr with unknown namespace");
   3096 
   3097  AttrModType modType{0};  // NOTE: Initialized with invalid value.
   3098  nsAttrValue oldValue;
   3099  bool oldValueSet;
   3100 
   3101  if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, aValue, aNotify,
   3102                             oldValue, &modType, &oldValueSet)) {
   3103    OnAttrSetButNotChanged(aNamespaceID, aName, aValue, aNotify);
   3104    return NS_OK;
   3105  }
   3106 
   3107  // Hold a script blocker while calling ParseAttribute since that can call
   3108  // out to id-observers
   3109  Document* document = GetComposedDoc();
   3110  mozAutoDocUpdate updateBatch(document, aNotify);
   3111 
   3112  if (aNotify) {
   3113    MutationObservers::NotifyAttributeWillChange(this, aNamespaceID, aName,
   3114                                                 modType);
   3115  }
   3116 
   3117  nsAttrValue attrValue;
   3118  aParseFn(attrValue);
   3119 
   3120  BeforeSetAttr(aNamespaceID, aName, &attrValue, aNotify);
   3121 
   3122  PreIdMaybeChange(aNamespaceID, aName, &attrValue);
   3123 
   3124  return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
   3125                          oldValueSet ? &oldValue : nullptr, attrValue,
   3126                          aSubjectPrincipal, modType, aNotify,
   3127                          kCallAfterSetAttr, document, updateBatch);
   3128 }
   3129 
   3130 nsresult Element::SetParsedAttr(int32_t aNamespaceID, nsAtom* aName,
   3131                                nsAtom* aPrefix, nsAttrValue& aParsedValue,
   3132                                bool aNotify) {
   3133  // Keep this in sync with SetAttr and SetSingleClassFromParser above
   3134 
   3135  NS_ENSURE_ARG_POINTER(aName);
   3136  NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
   3137               "Don't call SetAttr with unknown namespace");
   3138 
   3139  AttrModType modType{0};  // NOTE: Initialized with invalid value.
   3140  nsAttrValue oldValue;
   3141  bool oldValueSet;
   3142 
   3143  {
   3144    const nsAttrValueOrString value(aParsedValue);
   3145    if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
   3146                               oldValue, &modType, &oldValueSet)) {
   3147      OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify);
   3148      return NS_OK;
   3149    }
   3150  }
   3151 
   3152  Document* document = GetComposedDoc();
   3153  mozAutoDocUpdate updateBatch(document, aNotify);
   3154 
   3155  if (aNotify) {
   3156    MutationObservers::NotifyAttributeWillChange(this, aNamespaceID, aName,
   3157                                                 modType);
   3158  }
   3159 
   3160  BeforeSetAttr(aNamespaceID, aName, &aParsedValue, aNotify);
   3161 
   3162  PreIdMaybeChange(aNamespaceID, aName, &aParsedValue);
   3163 
   3164  return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
   3165                          oldValueSet ? &oldValue : nullptr, aParsedValue,
   3166                          nullptr, modType, aNotify, kCallAfterSetAttr,
   3167                          document, updateBatch);
   3168 }
   3169 
   3170 nsresult Element::SetAttrAndNotify(
   3171    int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix,
   3172    const nsAttrValue* aOldValue, nsAttrValue& aParsedValue,
   3173    nsIPrincipal* aSubjectPrincipal, AttrModType aModType, bool aNotify,
   3174    bool aCallAfterSetAttr, Document* aComposedDocument,
   3175    const mozAutoDocUpdate& aGuard) {
   3176  nsMutationGuard::DidMutate();
   3177 
   3178  // Copy aParsedValue for later use since it will be lost when we call
   3179  // SetAndSwapAttr below
   3180  nsAttrValue valueForAfterSetAttr;
   3181  if (aCallAfterSetAttr || GetCustomElementData()) {
   3182    valueForAfterSetAttr.SetTo(aParsedValue);
   3183  }
   3184 
   3185  bool hadValidDir = false;
   3186  bool hadDirAuto = false;
   3187  bool oldValueSet;
   3188 
   3189  if (aNamespaceID == kNameSpaceID_None) {
   3190    if (aName == nsGkAtoms::dir) {
   3191      hadValidDir = HasValidDir() || IsHTMLElement(nsGkAtoms::bdi);
   3192      hadDirAuto = HasDirAuto();  // already takes bdi into account
   3193    }
   3194 
   3195    MOZ_TRY(SetAndSwapAttr(aName, aParsedValue, &oldValueSet));
   3196    if (IsAttributeMapped(aName) && !IsPendingMappedAttributeEvaluation()) {
   3197      mAttrs.InfallibleMarkAsPendingPresAttributeEvaluation();
   3198      if (Document* doc = GetComposedDoc()) {
   3199        doc->ScheduleForPresAttrEvaluation(this);
   3200      }
   3201    }
   3202  } else {
   3203    RefPtr<mozilla::dom::NodeInfo> ni =
   3204        mNodeInfo->NodeInfoManager()->GetNodeInfo(aName, aPrefix, aNamespaceID,
   3205                                                  ATTRIBUTE_NODE);
   3206    MOZ_TRY(SetAndSwapAttr(ni, aParsedValue, &oldValueSet));
   3207  }
   3208 
   3209  PostIdMaybeChange(aNamespaceID, aName, &valueForAfterSetAttr);
   3210 
   3211  // If the old value owns its own data, we know it is OK to keep using it.
   3212  // oldValue will be null if there was no previously set value
   3213  const nsAttrValue* oldValue;
   3214  if (aParsedValue.StoresOwnData()) {
   3215    if (oldValueSet) {
   3216      oldValue = &aParsedValue;
   3217    } else {
   3218      oldValue = nullptr;
   3219    }
   3220  } else {
   3221    // No need to conditionally assign null here. If there was no previously
   3222    // set value for the attribute, aOldValue will already be null.
   3223    oldValue = aOldValue;
   3224  }
   3225 
   3226  if (HasElementCreatedFromPrototypeAndHasUnmodifiedL10n() &&
   3227      aNamespaceID == kNameSpaceID_None &&
   3228      (aName == nsGkAtoms::datal10nid || aName == nsGkAtoms::datal10nargs)) {
   3229    ClearElementCreatedFromPrototypeAndHasUnmodifiedL10n();
   3230    if (aComposedDocument) {
   3231      aComposedDocument->mL10nProtoElements.Remove(this);
   3232    }
   3233  }
   3234 
   3235  const CustomElementData* data = GetCustomElementData();
   3236  if (data && data->mState == CustomElementData::State::eCustom) {
   3237    CustomElementDefinition* definition = data->GetCustomElementDefinition();
   3238    MOZ_ASSERT(definition, "Should have a valid CustomElementDefinition");
   3239 
   3240    if (definition->IsInObservedAttributeList(aName)) {
   3241      nsAutoString ns;
   3242      nsNameSpaceManager::GetInstance()->GetNameSpaceURI(aNamespaceID, ns);
   3243 
   3244      LifecycleCallbackArgs args;
   3245      args.mName = aName;
   3246      if (aModType == AttrModType::Addition) {
   3247        args.mOldValue = VoidString();
   3248      } else {
   3249        if (oldValue) {
   3250          oldValue->ToString(args.mOldValue);
   3251        } else {
   3252          // If there is no old value, get the value of the uninitialized
   3253          // attribute that was swapped with aParsedValue.
   3254          aParsedValue.ToString(args.mOldValue);
   3255        }
   3256      }
   3257      valueForAfterSetAttr.ToString(args.mNewValue);
   3258      args.mNamespaceURI = ns.IsEmpty() ? VoidString() : ns;
   3259 
   3260      nsContentUtils::EnqueueLifecycleCallback(
   3261          ElementCallbackType::eAttributeChanged, this, args, definition);
   3262    }
   3263  }
   3264 
   3265  if (aCallAfterSetAttr) {
   3266    AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, oldValue,
   3267                 aSubjectPrincipal, aNotify);
   3268 
   3269    if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
   3270      OnSetDirAttr(this, &valueForAfterSetAttr, hadValidDir, hadDirAuto,
   3271                   aNotify);
   3272    }
   3273  }
   3274 
   3275  if (aNotify) {
   3276    // Don't pass aOldValue to AttributeChanged since it may not be reliable.
   3277    // Callers only compute aOldValue under certain conditions which may not
   3278    // be triggered by all nsIMutationObservers.
   3279    MutationObservers::NotifyAttributeChanged(
   3280        this, aNamespaceID, aName, aModType,
   3281        aParsedValue.StoresOwnData() ? &aParsedValue : nullptr);
   3282  }
   3283 
   3284  return NS_OK;
   3285 }
   3286 
   3287 void Element::TryReserveAttributeCount(uint32_t aAttributeCount) {
   3288  (void)mAttrs.GrowTo(aAttributeCount);
   3289 }
   3290 
   3291 bool Element::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
   3292                             const nsAString& aValue,
   3293                             nsIPrincipal* aMaybeScriptedPrincipal,
   3294                             nsAttrValue& aResult) {
   3295  if (aAttribute == nsGkAtoms::lang) {
   3296    aResult.ParseAtom(aValue);
   3297    return true;
   3298  }
   3299 
   3300  if (aNamespaceID == kNameSpaceID_None) {
   3301    if (aAttribute == nsGkAtoms::_class || aAttribute == nsGkAtoms::part ||
   3302        aAttribute == nsGkAtoms::aria_controls ||
   3303        aAttribute == nsGkAtoms::aria_describedby ||
   3304        aAttribute == nsGkAtoms::aria_details ||
   3305        aAttribute == nsGkAtoms::aria_errormessage ||
   3306        aAttribute == nsGkAtoms::aria_flowto ||
   3307        aAttribute == nsGkAtoms::aria_labelledby ||
   3308        aAttribute == nsGkAtoms::aria_owns) {
   3309      aResult.ParseAtomArray(aValue);
   3310      return true;
   3311    }
   3312 
   3313    if (aAttribute == nsGkAtoms::exportparts) {
   3314      aResult.ParsePartMapping(aValue);
   3315      return true;
   3316    }
   3317 
   3318    if (aAttribute == nsGkAtoms::aria_activedescendant) {
   3319      // String in aria-activedescendant is an id, so store as an atom.
   3320      aResult.ParseAtom(aValue);
   3321      return true;
   3322    }
   3323 
   3324    if (aAttribute == nsGkAtoms::id) {
   3325      // Store id as an atom.  id="" means that the element has no id,
   3326      // not that it has an emptystring as the id.
   3327      if (aValue.IsEmpty()) {
   3328        return false;
   3329      }
   3330      aResult.ParseAtom(aValue);
   3331      return true;
   3332    }
   3333  }
   3334 
   3335  return false;
   3336 }
   3337 
   3338 void Element::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
   3339                            const nsAttrValue* aValue, bool aNotify) {
   3340  if (aNamespaceID == kNameSpaceID_None) {
   3341    if (aName == nsGkAtoms::_class && aValue) {
   3342      // Note: This flag is asymmetrical. It is never unset and isn't exact.
   3343      // If it is ever made to be exact, we probably need to handle this
   3344      // similarly to how ids are handled in PreIdMaybeChange and
   3345      // PostIdMaybeChange.
   3346      // Note that SetSingleClassFromParser inlines BeforeSetAttr and
   3347      // calls SetMayHaveClass directly. Making a subclass take action
   3348      // on the class attribute in a BeforeSetAttr override would
   3349      // require revising SetSingleClassFromParser.
   3350      SetMayHaveClass();
   3351    }
   3352  }
   3353 }
   3354 
   3355 void Element::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
   3356                           const nsAttrValue* aValue,
   3357                           const nsAttrValue* aOldValue,
   3358                           nsIPrincipal* aMaybeScriptedPrincipal,
   3359                           bool aNotify) {
   3360  if (aNamespaceID == kNameSpaceID_None) {
   3361    if (aName == nsGkAtoms::part) {
   3362      bool isPart = !!aValue;
   3363      if (HasPartAttribute() != isPart) {
   3364        SetHasPartAttribute(isPart);
   3365        if (ShadowRoot* shadow = GetContainingShadow()) {
   3366          if (isPart) {
   3367            shadow->PartAdded(*this);
   3368          } else {
   3369            shadow->PartRemoved(*this);
   3370          }
   3371        }
   3372      }
   3373      MOZ_ASSERT(HasPartAttribute() == isPart);
   3374    } else if (aName == nsGkAtoms::slot && GetParent()) {
   3375      if (ShadowRoot* shadow = GetParent()->GetShadowRoot()) {
   3376        shadow->MaybeReassignContent(*this);
   3377      }
   3378    } else if (aName == nsGkAtoms::aria_activedescendant) {
   3379      ClearExplicitlySetAttrElement(aName);
   3380    } else if (aName == nsGkAtoms::aria_controls ||
   3381               aName == nsGkAtoms::aria_describedby ||
   3382               aName == nsGkAtoms::aria_details ||
   3383               aName == nsGkAtoms::aria_errormessage ||
   3384               aName == nsGkAtoms::aria_flowto ||
   3385               aName == nsGkAtoms::aria_labelledby ||
   3386               aName == nsGkAtoms::aria_owns) {
   3387      ClearExplicitlySetAttrElements(aName);
   3388    }
   3389  }
   3390 }
   3391 
   3392 void Element::PreIdMaybeChange(int32_t aNamespaceID, nsAtom* aName,
   3393                               const nsAttrValue* aValue) {
   3394  if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) {
   3395    return;
   3396  }
   3397  RemoveFromIdTable();
   3398 }
   3399 
   3400 void Element::PostIdMaybeChange(int32_t aNamespaceID, nsAtom* aName,
   3401                                const nsAttrValue* aValue) {
   3402  if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) {
   3403    return;
   3404  }
   3405 
   3406  // id="" means that the element has no id, not that it has an empty
   3407  // string as the id.
   3408  if (aValue && !aValue->IsEmptyString()) {
   3409    SetHasID();
   3410    AddToIdTable(aValue->GetAtomValue());
   3411  } else {
   3412    ClearHasID();
   3413  }
   3414 }
   3415 
   3416 void Element::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
   3417                                     const nsAttrValueOrString& aValue,
   3418                                     bool aNotify) {
   3419  const CustomElementData* data = GetCustomElementData();
   3420  if (data && data->mState == CustomElementData::State::eCustom) {
   3421    CustomElementDefinition* definition = data->GetCustomElementDefinition();
   3422    MOZ_ASSERT(definition, "Should have a valid CustomElementDefinition");
   3423 
   3424    if (definition->IsInObservedAttributeList(aName)) {
   3425      nsAutoString ns;
   3426      nsNameSpaceManager::GetInstance()->GetNameSpaceURI(aNamespaceID, ns);
   3427 
   3428      nsAutoString value(aValue.String());
   3429      LifecycleCallbackArgs args;
   3430      args.mName = aName;
   3431      args.mOldValue = value;
   3432      args.mNewValue = value;
   3433      args.mNamespaceURI = ns.IsEmpty() ? VoidString() : ns;
   3434 
   3435      nsContentUtils::EnqueueLifecycleCallback(
   3436          ElementCallbackType::eAttributeChanged, this, args, definition);
   3437    }
   3438  }
   3439 
   3440  if (aNamespaceID == kNameSpaceID_None &&
   3441      aName == nsGkAtoms::aria_activedescendant) {
   3442    ClearExplicitlySetAttrElement(aName);
   3443  }
   3444 
   3445  if (aNamespaceID == kNameSpaceID_None &&
   3446      (aName == nsGkAtoms::aria_controls ||
   3447       aName == nsGkAtoms::aria_describedby ||
   3448       aName == nsGkAtoms::aria_details ||
   3449       aName == nsGkAtoms::aria_errormessage ||
   3450       aName == nsGkAtoms::aria_flowto || aName == nsGkAtoms::aria_labelledby ||
   3451       aName == nsGkAtoms::aria_owns)) {
   3452    ClearExplicitlySetAttrElements(aName);
   3453  }
   3454 }
   3455 
   3456 EventListenerManager* Element::GetEventListenerManagerForAttr(nsAtom* aAttrName,
   3457                                                              bool* aDefer) {
   3458  *aDefer = true;
   3459  return GetOrCreateListenerManager();
   3460 }
   3461 
   3462 bool Element::GetAttr(const nsAtom* aName, nsAString& aResult) const {
   3463  const nsAttrValue* val = mAttrs.GetAttr(aName);
   3464  if (!val) {
   3465    aResult.Truncate();
   3466    return false;
   3467  }
   3468  val->ToString(aResult);
   3469  return true;
   3470 }
   3471 
   3472 bool Element::GetAttr(int32_t aNameSpaceID, const nsAtom* aName,
   3473                      nsAString& aResult) const {
   3474  const nsAttrValue* val = mAttrs.GetAttr(aName, aNameSpaceID);
   3475  if (!val) {
   3476    aResult.Truncate();
   3477    return false;
   3478  }
   3479  val->ToString(aResult);
   3480  return true;
   3481 }
   3482 
   3483 int32_t Element::FindAttrValueIn(int32_t aNameSpaceID, const nsAtom* aName,
   3484                                 AttrArray::AttrValuesArray* aValues,
   3485                                 nsCaseTreatment aCaseSensitive) const {
   3486  return mAttrs.FindAttrValueIn(aNameSpaceID, aName, aValues, aCaseSensitive);
   3487 }
   3488 
   3489 nsresult Element::UnsetAttr(int32_t aNameSpaceID, nsAtom* aName, bool aNotify) {
   3490  NS_ASSERTION(nullptr != aName, "must have attribute name");
   3491 
   3492  int32_t index = mAttrs.IndexOfAttr(aName, aNameSpaceID);
   3493  if (index < 0) {
   3494    return NS_OK;
   3495  }
   3496 
   3497  Document* document = GetComposedDoc();
   3498  mozAutoDocUpdate updateBatch(document, aNotify);
   3499 
   3500  if (aNotify) {
   3501    MutationObservers::NotifyAttributeWillChange(this, aNameSpaceID, aName,
   3502                                                 AttrModType::Removal);
   3503  }
   3504 
   3505  BeforeSetAttr(aNameSpaceID, aName, nullptr, aNotify);
   3506 
   3507  PreIdMaybeChange(aNameSpaceID, aName, nullptr);
   3508 
   3509  // Clear the attribute out from attribute map.
   3510  nsDOMSlots* slots = GetExistingDOMSlots();
   3511  if (slots && slots->mAttributeMap) {
   3512    slots->mAttributeMap->DropAttribute(aNameSpaceID, aName);
   3513  }
   3514 
   3515  // The id-handling code, and in the future possibly other code, need to
   3516  // react to unexpected attribute changes.
   3517  nsMutationGuard::DidMutate();
   3518 
   3519  bool hadValidDir = false;
   3520  bool hadDirAuto = false;
   3521 
   3522  if (aNameSpaceID == kNameSpaceID_None) {
   3523    if (aName == nsGkAtoms::dir) {
   3524      hadValidDir = HasValidDir() || IsHTMLElement(nsGkAtoms::bdi);
   3525      hadDirAuto = HasDirAuto();  // already takes bdi into account
   3526    }
   3527    if (IsAttributeMapped(aName) && !IsPendingMappedAttributeEvaluation()) {
   3528      mAttrs.InfallibleMarkAsPendingPresAttributeEvaluation();
   3529      if (Document* doc = GetComposedDoc()) {
   3530        doc->ScheduleForPresAttrEvaluation(this);
   3531      }
   3532    }
   3533  }
   3534 
   3535  nsAttrValue oldValue;
   3536  MOZ_TRY(mAttrs.RemoveAttrAt(index, oldValue));
   3537 
   3538  PostIdMaybeChange(aNameSpaceID, aName, nullptr);
   3539 
   3540  const CustomElementData* data = GetCustomElementData();
   3541  if (data && data->mState == CustomElementData::State::eCustom) {
   3542    CustomElementDefinition* definition = data->GetCustomElementDefinition();
   3543    MOZ_ASSERT(definition, "Should have a valid CustomElementDefinition");
   3544    if (definition->IsInObservedAttributeList(aName)) {
   3545      nsAutoString ns;
   3546      nsNameSpaceManager::GetInstance()->GetNameSpaceURI(aNameSpaceID, ns);
   3547      LifecycleCallbackArgs args;
   3548      args.mName = aName;
   3549      oldValue.ToString(args.mOldValue);
   3550      args.mNewValue = VoidString();
   3551      args.mNamespaceURI = ns.IsEmpty() ? VoidString() : ns;
   3552      nsContentUtils::EnqueueLifecycleCallback(
   3553          ElementCallbackType::eAttributeChanged, this, args, definition);
   3554    }
   3555  }
   3556 
   3557  AfterSetAttr(aNameSpaceID, aName, nullptr, &oldValue, nullptr, aNotify);
   3558 
   3559  if (aNotify) {
   3560    // We can always pass oldValue here since there is no new value which could
   3561    // have corrupted it.
   3562    MutationObservers::NotifyAttributeChanged(this, aNameSpaceID, aName,
   3563                                              AttrModType::Removal, &oldValue);
   3564  }
   3565 
   3566  if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
   3567    OnSetDirAttr(this, nullptr, hadValidDir, hadDirAuto, aNotify);
   3568  }
   3569 
   3570  return NS_OK;
   3571 }
   3572 
   3573 void Element::DescribeAttribute(uint32_t index,
   3574                                nsAString& aOutDescription) const {
   3575  // name
   3576  mAttrs.AttrNameAt(index)->GetQualifiedName(aOutDescription);
   3577 
   3578  // value
   3579  aOutDescription.AppendLiteral("=\"");
   3580  nsAutoString value;
   3581  mAttrs.AttrAt(index)->ToString(value);
   3582  for (uint32_t i = value.Length(); i > 0; --i) {
   3583    if (value[i - 1] == char16_t('"')) value.Insert(char16_t('\\'), i - 1);
   3584  }
   3585  aOutDescription.Append(value);
   3586  aOutDescription.Append('"');
   3587 }
   3588 
   3589 #ifdef MOZ_DOM_LIST
   3590 void Element::ListAttributes(FILE* out) const {
   3591  uint32_t index, count = mAttrs.AttrCount();
   3592  for (index = 0; index < count; index++) {
   3593    nsAutoString attributeDescription;
   3594    DescribeAttribute(index, attributeDescription);
   3595 
   3596    fputs(" ", out);
   3597    fputs(NS_LossyConvertUTF16toASCII(attributeDescription).get(), out);
   3598  }
   3599 }
   3600 
   3601 void Element::List(FILE* out, int32_t aIndent, const nsCString& aPrefix) const {
   3602  int32_t indent;
   3603  for (indent = aIndent; --indent >= 0;) fputs("  ", out);
   3604 
   3605  fputs(aPrefix.get(), out);
   3606 
   3607  fputs(NS_LossyConvertUTF16toASCII(mNodeInfo->QualifiedName()).get(), out);
   3608 
   3609  fprintf(out, "@%p", (void*)this);
   3610 
   3611  ListAttributes(out);
   3612 
   3613  fprintf(out, " state=[%llx]",
   3614          static_cast<unsigned long long>(State().GetInternalValue()));
   3615  fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
   3616  fprintf(out, " selectorflags=[%08x]",
   3617          static_cast<unsigned int>(GetSelectorFlags()));
   3618  if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
   3619    const LinkedList<AbstractRange>* ranges =
   3620        GetExistingClosestCommonInclusiveAncestorRanges();
   3621    int32_t count = 0;
   3622    if (ranges) {
   3623      // Can't use range-based iteration on a const LinkedList, unfortunately.
   3624      for (const AbstractRange* r = ranges->getFirst(); r; r = r->getNext()) {
   3625        ++count;
   3626      }
   3627    }
   3628    fprintf(out, " ranges:%d", count);
   3629  }
   3630  fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
   3631  fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get());
   3632 
   3633  nsIContent* child = GetFirstChild();
   3634  if (child) {
   3635    fputs("\n", out);
   3636 
   3637    for (; child; child = child->GetNextSibling()) {
   3638      child->List(out, aIndent + 1);
   3639    }
   3640 
   3641    for (indent = aIndent; --indent >= 0;) fputs("  ", out);
   3642  }
   3643 
   3644  fputs(">\n", out);
   3645 }
   3646 
   3647 void Element::DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const {
   3648  int32_t indent;
   3649  for (indent = aIndent; --indent >= 0;) fputs("  ", out);
   3650 
   3651  const nsString& buf = mNodeInfo->QualifiedName();
   3652  fputs("<", out);
   3653  fputs(NS_LossyConvertUTF16toASCII(buf).get(), out);
   3654 
   3655  if (aDumpAll) ListAttributes(out);
   3656 
   3657  fputs(">", out);
   3658 
   3659  if (aIndent) fputs("\n", out);
   3660 
   3661  for (nsIContent* child = GetFirstChild(); child;
   3662       child = child->GetNextSibling()) {
   3663    int32_t indent = aIndent ? aIndent + 1 : 0;
   3664    child->DumpContent(out, indent, aDumpAll);
   3665  }
   3666  for (indent = aIndent; --indent >= 0;) fputs("  ", out);
   3667  fputs("</", out);
   3668  fputs(NS_LossyConvertUTF16toASCII(buf).get(), out);
   3669  fputs(">", out);
   3670 
   3671  if (aIndent) fputs("\n", out);
   3672 }
   3673 #endif
   3674 
   3675 void Element::Describe(nsAString& aOutDescription, bool aShort) const {
   3676  aOutDescription.Append(mNodeInfo->QualifiedName());
   3677  aOutDescription.AppendPrintf("@%p", (void*)this);
   3678 
   3679  uint32_t index, count = mAttrs.AttrCount();
   3680  for (index = 0; index < count; index++) {
   3681    if (aShort) {
   3682      const nsAttrName* name = mAttrs.AttrNameAt(index);
   3683      if (!name->Equals(nsGkAtoms::id) && !name->Equals(nsGkAtoms::_class)) {
   3684        continue;
   3685      }
   3686    }
   3687    aOutDescription.Append(' ');
   3688    nsAutoString attributeDescription;
   3689    DescribeAttribute(index, attributeDescription);
   3690    aOutDescription.Append(attributeDescription);
   3691  }
   3692 }
   3693 
   3694 bool Element::CheckHandleEventForLinksPrecondition(
   3695    EventChainVisitor& aVisitor) const {
   3696  // Make sure we actually are a link
   3697  if (!IsLink()) {
   3698    return false;
   3699  }
   3700  if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
   3701      (!aVisitor.mEvent->IsTrusted() &&
   3702       (aVisitor.mEvent->mMessage != ePointerClick) &&
   3703       (aVisitor.mEvent->mMessage != eKeyPress) &&
   3704       (aVisitor.mEvent->mMessage != eLegacyDOMActivate)) ||
   3705      aVisitor.mEvent->mFlags.mMultipleActionsPrevented) {
   3706    return false;
   3707  }
   3708  return true;
   3709 }
   3710 
   3711 void Element::GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor) {
   3712  // Optimisation: return early if this event doesn't interest us.
   3713  // IMPORTANT: this switch and the switch below it must be kept in sync!
   3714  switch (aVisitor.mEvent->mMessage) {
   3715    case eMouseOver:
   3716    case eFocus:
   3717    case eMouseOut:
   3718    case eBlur:
   3719      break;
   3720    default:
   3721      return;
   3722  }
   3723 
   3724  // Make sure we meet the preconditions before continuing
   3725  if (!CheckHandleEventForLinksPrecondition(aVisitor)) {
   3726    return;
   3727  }
   3728 
   3729  // We try to handle everything we can even when the URI is invalid. Though of
   3730  // course we can't do stuff like updating the status bar, so return early here
   3731  // instead.
   3732  nsCOMPtr<nsIURI> absURI = GetHrefURI();
   3733  if (!absURI) {
   3734    return;
   3735  }
   3736 
   3737  // We do the status bar updates in GetEventTargetParent so that the status bar
   3738  // gets updated even if the event is consumed before we have a chance to set
   3739  // it.
   3740  switch (aVisitor.mEvent->mMessage) {
   3741    // Set the status bar similarly for mouseover and focus
   3742    case eMouseOver:
   3743      aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   3744      [[fallthrough]];
   3745    case eFocus: {
   3746      InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent();
   3747      if (!focusEvent || !focusEvent->mIsRefocus) {
   3748        nsAutoString target;
   3749        GetLinkTarget(target);
   3750        nsContentUtils::TriggerLinkMouseOver(this, absURI, target);
   3751        // Make sure any ancestor links don't also TriggerLink
   3752        aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
   3753      }
   3754      break;
   3755    }
   3756    case eMouseOut:
   3757      aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   3758      [[fallthrough]];
   3759    case eBlur: {
   3760      nsresult rv = LeaveLink(aVisitor.mPresContext);
   3761      if (NS_SUCCEEDED(rv)) {
   3762        aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
   3763      }
   3764      break;
   3765    }
   3766 
   3767    default:
   3768      // switch not in sync with the optimization switch earlier in this
   3769      // function
   3770      MOZ_ASSERT_UNREACHABLE("switch statements not in sync");
   3771  }
   3772 }
   3773 
   3774 // This dispatches a 'chromelinkclick' CustomEvent to chrome-only listeners,
   3775 // so that frontend can handle middle-clicks and ctrl/cmd/shift/etc.-clicks
   3776 // on links, without getting a call for every single click the user makes.
   3777 // Only supported for click or auxclick events.
   3778 void Element::DispatchChromeOnlyLinkClickEvent(
   3779    EventChainPostVisitor& aVisitor) {
   3780  MOZ_ASSERT(aVisitor.mEvent->mMessage == ePointerAuxClick ||
   3781                 aVisitor.mEvent->mMessage == ePointerClick,
   3782             "DispatchChromeOnlyLinkClickEvent supports only click and "
   3783             "auxclick source events");
   3784  Document* doc = OwnerDoc();
   3785  RefPtr<XULCommandEvent> event =
   3786      new XULCommandEvent(doc, aVisitor.mPresContext, nullptr);
   3787  RefPtr<dom::Event> mouseDOMEvent = aVisitor.mDOMEvent;
   3788  if (!mouseDOMEvent) {
   3789    mouseDOMEvent = EventDispatcher::CreateEvent(
   3790        aVisitor.mEvent->mOriginalTarget, aVisitor.mPresContext,
   3791        aVisitor.mEvent, u""_ns);
   3792    NS_ADDREF(aVisitor.mDOMEvent = mouseDOMEvent);
   3793  }
   3794 
   3795  MouseEvent* mouseEvent = mouseDOMEvent->AsMouseEvent();
   3796  event->InitCommandEvent(
   3797      u"chromelinkclick"_ns, /* CanBubble */ true,
   3798      /* Cancelable */ true, nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
   3799      0, mouseEvent->CtrlKey(), mouseEvent->AltKey(), mouseEvent->ShiftKey(),
   3800      mouseEvent->MetaKey(), mouseEvent->Button(), mouseDOMEvent,
   3801      mouseEvent->InputSource(CallerType::System), IgnoreErrors());
   3802  // Note: we're always trusted, but the event we pass as the `sourceEvent`
   3803  // might not be. Frontend code will check that event's trusted property to
   3804  // make that determination; doing it this way means we don't also start
   3805  // acting on web-generated custom 'chromelinkclick' events which would
   3806  // provide additional attack surface for a malicious actor.
   3807  event->SetTrusted(true);
   3808  event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
   3809  DispatchEvent(*event);
   3810 }
   3811 
   3812 nsresult Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) {
   3813  // Optimisation: return early if this event doesn't interest us.
   3814  // IMPORTANT: this switch and the switch below it must be kept in sync!
   3815  switch (aVisitor.mEvent->mMessage) {
   3816    case eMouseDown:
   3817    case ePointerClick:
   3818    case ePointerAuxClick:
   3819    case eLegacyDOMActivate:
   3820    case eKeyPress:
   3821      break;
   3822    default:
   3823      return NS_OK;
   3824  }
   3825 
   3826  // Make sure we meet the preconditions before continuing
   3827  if (!CheckHandleEventForLinksPrecondition(aVisitor)) {
   3828    return NS_OK;
   3829  }
   3830 
   3831  // We try to handle ~everything consistently even if the href is invalid
   3832  // (GetHrefURI() returns null).
   3833  nsresult rv = NS_OK;
   3834 
   3835  switch (aVisitor.mEvent->mMessage) {
   3836    case eMouseDown: {
   3837      if (!OwnerDoc()->LinkHandlingEnabled()) {
   3838        break;
   3839      }
   3840 
   3841      WidgetMouseEvent* const mouseEvent = aVisitor.mEvent->AsMouseEvent();
   3842      mouseEvent->mFlags.mMultipleActionsPrevented |=
   3843          mouseEvent->mButton == MouseButton::ePrimary ||
   3844          mouseEvent->mButton == MouseButton::eMiddle;
   3845 
   3846      if (mouseEvent->mButton == MouseButton::ePrimary) {
   3847        // For avoiding focus popup opened by clicking this link to get blurred,
   3848        // we need this to get focused now.  However, if the mousedown occurs
   3849        // in editable element in this link, we should not do this because its
   3850        // editing host will get focus.
   3851        if (IsInComposedDoc()) {
   3852          Element* targetElement = Element::FromEventTargetOrNull(
   3853              aVisitor.mEvent->GetDOMEventTarget());
   3854          if (targetElement && targetElement->IsInclusiveDescendantOf(this) &&
   3855              (!targetElement->IsEditable() ||
   3856               targetElement->GetEditingHost() == this)) {
   3857            if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
   3858              RefPtr<Element> kungFuDeathGrip(this);
   3859              fm->SetFocus(kungFuDeathGrip, nsIFocusManager::FLAG_BYMOUSE |
   3860                                                nsIFocusManager::FLAG_NOSCROLL);
   3861            }
   3862          }
   3863        }
   3864 
   3865        if (aVisitor.mPresContext) {
   3866          EventStateManager::SetActiveManager(
   3867              aVisitor.mPresContext->EventStateManager(), this);
   3868        }
   3869 
   3870        // OK, we're pretty sure we're going to load, so warm up a speculative
   3871        // connection to be sure we have one ready when we open the channel.
   3872        if (nsIDocShell* shell = OwnerDoc()->GetDocShell()) {
   3873          if (nsCOMPtr<nsIURI> absURI = GetHrefURI()) {
   3874            if (nsCOMPtr<nsISpeculativeConnect> sc =
   3875                    mozilla::components::IO::Service()) {
   3876              nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(shell);
   3877              sc->SpeculativeConnect(absURI, NodePrincipal(), ir, false);
   3878            }
   3879          }
   3880        }
   3881      }
   3882    } break;
   3883 
   3884    case ePointerClick: {
   3885      WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
   3886      if (mouseEvent->IsLeftClickEvent()) {
   3887        if (!mouseEvent->IsControl() && !mouseEvent->IsMeta() &&
   3888            !mouseEvent->IsAlt() && !mouseEvent->IsShift()) {
   3889          if (OwnerDoc()->MayHaveDOMActivateListeners()) {
   3890            // The default action is simply to dispatch DOMActivate.
   3891            // But dispatch that only if needed.
   3892            nsEventStatus status = nsEventStatus_eIgnore;
   3893            // DOMActivate event should be trusted since the activation is
   3894            // actually occurred even if the cause is an untrusted click event.
   3895            InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent);
   3896            actEvent.mDetail = 1;
   3897            rv = EventDispatcher::Dispatch(this, aVisitor.mPresContext,
   3898                                           &actEvent, nullptr, &status);
   3899            if (NS_SUCCEEDED(rv)) {
   3900              aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   3901            }
   3902          } else {
   3903            if (nsCOMPtr<nsIURI> absURI = GetHrefURI()) {
   3904              // If you modify this code, tweak also the code handling
   3905              // eLegacyDOMActivate.
   3906              nsAutoString target;
   3907              GetLinkTarget(target);
   3908              UserNavigationInvolvement userInvolvement =
   3909                  mouseEvent->IsTrusted()
   3910                      ? UserNavigationInvolvement::Activation
   3911                      : UserNavigationInvolvement::None;
   3912              nsContentUtils::TriggerLinkClick(this, absURI, target,
   3913                                               userInvolvement);
   3914            }
   3915            // Since we didn't dispatch DOMActivate because there were no
   3916            // listeners, do still set mEventStatus as if it was dispatched
   3917            // successfully.
   3918            aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   3919          }
   3920        }
   3921 
   3922        DispatchChromeOnlyLinkClickEvent(aVisitor);
   3923      }
   3924      break;
   3925    }
   3926    case ePointerAuxClick: {
   3927      DispatchChromeOnlyLinkClickEvent(aVisitor);
   3928      break;
   3929    }
   3930    case eLegacyDOMActivate: {
   3931      // If you modify this code, tweak also the code handling
   3932      // ePointerClick.
   3933      if (aVisitor.mEvent->mOriginalTarget == this) {
   3934        if (nsCOMPtr<nsIURI> absURI = GetHrefURI()) {
   3935          nsAutoString target;
   3936          GetLinkTarget(target);
   3937          UserNavigationInvolvement userInvolvement =
   3938              aVisitor.mEvent->IsTrusted()
   3939                  ? UserNavigationInvolvement::Activation
   3940                  : UserNavigationInvolvement::None;
   3941          nsContentUtils::TriggerLinkClick(this, absURI, target,
   3942                                           userInvolvement);
   3943          aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   3944        }
   3945      }
   3946    } break;
   3947 
   3948    case eKeyPress: {
   3949      WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
   3950      if (keyEvent && keyEvent->mKeyCode == NS_VK_RETURN) {
   3951        nsEventStatus status = nsEventStatus_eIgnore;
   3952        rv = DispatchClickEvent(aVisitor.mPresContext, keyEvent, this, false,
   3953                                nullptr, &status);
   3954        if (NS_SUCCEEDED(rv)) {
   3955          aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
   3956        }
   3957      }
   3958    } break;
   3959 
   3960    default:
   3961      // switch not in sync with the optimization switch earlier in this
   3962      // function
   3963      MOZ_ASSERT_UNREACHABLE("switch statements not in sync");
   3964      return NS_ERROR_UNEXPECTED;
   3965  }
   3966 
   3967  return rv;
   3968 }
   3969 
   3970 // static
   3971 void Element::SanitizeLinkOrFormTarget(nsAString& aTarget) {
   3972  // <https://html.spec.whatwg.org/multipage/semantics.html#get-an-element's-target>
   3973  // 2. If target is not null, and contains an ASCII tab or newline and a U+003C
   3974  // (<), then set target to "_blank".
   3975  if (!aTarget.IsEmpty() && aTarget.FindCharInSet(u"\t\n\r") != kNotFound &&
   3976      aTarget.Contains('<')) {
   3977    aTarget.AssignLiteral("_blank");
   3978  }
   3979 }
   3980 
   3981 void Element::GetLinkTarget(nsAString& aTarget) {
   3982  GetLinkTargetImpl(aTarget);
   3983  SanitizeLinkOrFormTarget(aTarget);
   3984 }
   3985 
   3986 void Element::GetLinkTargetImpl(nsAString& aTarget) { aTarget.Truncate(); }
   3987 
   3988 nsresult Element::CopyInnerTo(Element* aDst, ReparseAttributes aReparse) {
   3989  nsresult rv = aDst->mAttrs.EnsureCapacityToClone(mAttrs);
   3990  NS_ENSURE_SUCCESS(rv, rv);
   3991 
   3992  const bool reparse = aReparse == ReparseAttributes::Yes;
   3993 
   3994  uint32_t count = mAttrs.AttrCount();
   3995  for (uint32_t i = 0; i < count; ++i) {
   3996    BorrowedAttrInfo info = mAttrs.AttrInfoAt(i);
   3997    const nsAttrName* name = info.mName;
   3998    const nsAttrValue* value = info.mValue;
   3999    if (value->Type() == nsAttrValue::eCSSDeclaration) {
   4000      MOZ_ASSERT(name->Equals(nsGkAtoms::style, kNameSpaceID_None));
   4001      // We still clone CSS attributes, even in the `reparse` (cross-document)
   4002      // case.  https://github.com/w3c/webappsec-csp/issues/212
   4003      nsAttrValue valueCopy(*value);
   4004      rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(),
   4005                               name->GetPrefix(), valueCopy, false);
   4006      NS_ENSURE_SUCCESS(rv, rv);
   4007 
   4008      value->GetCSSDeclarationValue()->SetImmutable();
   4009    } else if (reparse) {
   4010      nsAutoString valStr;
   4011      value->ToString(valStr);
   4012      rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
   4013                         name->GetPrefix(), valStr, false);
   4014      NS_ENSURE_SUCCESS(rv, rv);
   4015    } else {
   4016      nsAttrValue valueCopy(*value);
   4017      rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(),
   4018                               name->GetPrefix(), valueCopy, false);
   4019      NS_ENSURE_SUCCESS(rv, rv);
   4020    }
   4021  }
   4022 
   4023  dom::NodeInfo* dstNodeInfo = aDst->NodeInfo();
   4024  if (CustomElementData* data = GetCustomElementData()) {
   4025    // The cloned node may be a custom element that may require
   4026    // enqueing upgrade reaction.
   4027    if (nsAtom* typeAtom = data->GetCustomElementType()) {
   4028      aDst->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
   4029      MOZ_ASSERT(dstNodeInfo->NameAtom()->Equals(dstNodeInfo->LocalName()));
   4030      CustomElementDefinition* definition =
   4031          nsContentUtils::LookupCustomElementDefinition(
   4032              dstNodeInfo->GetDocument(), dstNodeInfo->NameAtom(),
   4033              dstNodeInfo->NamespaceID(), typeAtom);
   4034      if (definition) {
   4035        nsContentUtils::EnqueueUpgradeReaction(aDst, definition);
   4036      }
   4037    }
   4038  }
   4039 
   4040  if (dstNodeInfo->GetDocument()->IsStaticDocument()) {
   4041    // Propagate :defined state to the static clone.
   4042    if (State().HasState(ElementState::DEFINED)) {
   4043      aDst->SetDefined(true);
   4044    }
   4045  }
   4046 
   4047  return NS_OK;
   4048 }
   4049 
   4050 Element* Element::Closest(const nsACString& aSelector, ErrorResult& aResult) {
   4051  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("Element::Closest",
   4052                                        LAYOUT_SelectorQuery, aSelector);
   4053  const StyleSelectorList* list = ParseSelectorList(aSelector, aResult);
   4054  if (!list) {
   4055    return nullptr;
   4056  }
   4057 
   4058  return const_cast<Element*>(Servo_SelectorList_Closest(this, list));
   4059 }
   4060 
   4061 bool Element::Matches(const nsACString& aSelector, ErrorResult& aResult) {
   4062  AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("Element::Matches",
   4063                                        LAYOUT_SelectorQuery, aSelector);
   4064  const StyleSelectorList* list = ParseSelectorList(aSelector, aResult);
   4065  if (!list) {
   4066    return false;
   4067  }
   4068 
   4069  return Servo_SelectorList_Matches(this, list);
   4070 }
   4071 
   4072 static constexpr nsAttrValue::EnumTableEntry kCORSAttributeTable[] = {
   4073    // Order matters here
   4074    // See ParseCORSValue
   4075    {"anonymous", CORS_ANONYMOUS},
   4076    {"use-credentials", CORS_USE_CREDENTIALS}};
   4077 
   4078 /* static */
   4079 void Element::ParseCORSValue(const nsAString& aValue, nsAttrValue& aResult) {
   4080  DebugOnly<bool> success =
   4081      aResult.ParseEnumValue(aValue, kCORSAttributeTable, false,
   4082                             // default value is anonymous if aValue is
   4083                             // not a value we understand
   4084                             &kCORSAttributeTable[0]);
   4085  MOZ_ASSERT(success);
   4086 }
   4087 
   4088 /* static */
   4089 CORSMode Element::StringToCORSMode(const nsAString& aValue) {
   4090  if (aValue.IsVoid()) {
   4091    return CORS_NONE;
   4092  }
   4093 
   4094  nsAttrValue val;
   4095  Element::ParseCORSValue(aValue, val);
   4096  return CORSMode(val.GetEnumValue());
   4097 }
   4098 
   4099 /* static */
   4100 CORSMode Element::AttrValueToCORSMode(const nsAttrValue* aValue) {
   4101  if (!aValue) {
   4102    return CORS_NONE;
   4103  }
   4104 
   4105  return CORSMode(aValue->GetEnumValue());
   4106 }
   4107 
   4108 /**
   4109 * Returns nullptr if requests for fullscreen are allowed in the current
   4110 * context. Requests are only allowed if the user initiated them (like with
   4111 * a mouse-click or key press), unless this check has been disabled by
   4112 * setting the pref "full-screen-api.allow-trusted-requests-only" to false
   4113 * or if the caller is privileged. Feature policy may also deny requests.
   4114 * If fullscreen is not allowed, a key for the error message is returned.
   4115 */
   4116 static const char* GetFullscreenError(CallerType aCallerType,
   4117                                      Document* aDocument) {
   4118  MOZ_ASSERT(aDocument);
   4119 
   4120  // Privileged callers can always request fullscreen
   4121  if (aCallerType == CallerType::System) {
   4122    return nullptr;
   4123  }
   4124 
   4125  if (nsContentUtils::IsPDFJS(aDocument->GetPrincipal())) {
   4126    // The built-in pdf viewer can always request fullscreen
   4127    return nullptr;
   4128  }
   4129 
   4130  if (const char* error = aDocument->GetFullscreenError(aCallerType)) {
   4131    return error;
   4132  }
   4133 
   4134  // Bypass user interaction checks if preference is set
   4135  if (!StaticPrefs::full_screen_api_allow_trusted_requests_only()) {
   4136    return nullptr;
   4137  }
   4138 
   4139  if (!aDocument->ConsumeTransientUserGestureActivation()) {
   4140    return "FullscreenDeniedNotInputDriven";
   4141  }
   4142 
   4143  // Entering full-screen on mouse mouse event is only allowed with left mouse
   4144  // button
   4145  if (StaticPrefs::full_screen_api_mouse_event_allow_left_button_only() &&
   4146      (EventStateManager::sCurrentMouseBtn == MouseButton::eMiddle ||
   4147       EventStateManager::sCurrentMouseBtn == MouseButton::eSecondary)) {
   4148    return "FullscreenDeniedMouseEventOnlyLeftBtn";
   4149  }
   4150 
   4151  return nullptr;
   4152 }
   4153 
   4154 void Element::SetCapture(bool aRetargetToElement) {
   4155  // If there is already an active capture, ignore this request. This would
   4156  // occur if a splitter, frame resizer, etc had already captured and we don't
   4157  // want to override those.
   4158  if (!PresShell::GetCapturingContent()) {
   4159    PresShell::SetCapturingContent(
   4160        this, CaptureFlags::PreventDragStart |
   4161                  (aRetargetToElement ? CaptureFlags::RetargetToElement
   4162                                      : CaptureFlags::None));
   4163  }
   4164 }
   4165 
   4166 void Element::SetCaptureAlways(bool aRetargetToElement) {
   4167  PresShell::SetCapturingContent(
   4168      this, CaptureFlags::PreventDragStart | CaptureFlags::IgnoreAllowedState |
   4169                (aRetargetToElement ? CaptureFlags::RetargetToElement
   4170                                    : CaptureFlags::None));
   4171 }
   4172 
   4173 void Element::ReleaseCapture() {
   4174  if (PresShell::GetCapturingContent() == this) {
   4175    PresShell::ReleaseCapturingContent();
   4176  }
   4177 }
   4178 
   4179 already_AddRefed<Promise> Element::RequestFullscreen(CallerType aCallerType,
   4180                                                     ErrorResult& aRv) {
   4181  auto request = FullscreenRequest::Create(this, aCallerType, aRv);
   4182  RefPtr<Promise> promise = request->GetPromise();
   4183 
   4184  // Only grant fullscreen requests if this is called from inside a trusted
   4185  // event handler (i.e. inside an event handler for a user initiated event).
   4186  // This stops the fullscreen from being abused similar to the popups of old,
   4187  // and it also makes it harder for bad guys' script to go fullscreen and
   4188  // spoof the browser chrome/window and phish logins etc.
   4189  // Note that requests for fullscreen inside a web app's origin are exempt
   4190  // from this restriction.
   4191  if (const char* error = GetFullscreenError(aCallerType, OwnerDoc())) {
   4192    request->Reject(error);
   4193  } else {
   4194    OwnerDoc()->RequestFullscreen(std::move(request));
   4195  }
   4196  return promise.forget();
   4197 }
   4198 
   4199 void Element::RequestPointerLock(CallerType aCallerType) {
   4200  PointerLockManager::RequestLock(this, aCallerType);
   4201 }
   4202 
   4203 already_AddRefed<Flex> Element::GetAsFlexContainer() {
   4204  // We need the flex frame to compute additional info, and use
   4205  // that annotated version of the frame.
   4206  nsFlexContainerFrame* flexFrame =
   4207      nsFlexContainerFrame::GetFlexFrameWithComputedInfo(
   4208          GetPrimaryFrame(FlushType::Layout));
   4209 
   4210  if (flexFrame) {
   4211    RefPtr<Flex> flex = new Flex(this, flexFrame);
   4212    return flex.forget();
   4213  }
   4214  return nullptr;
   4215 }
   4216 
   4217 void Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult) {
   4218  nsGridContainerFrame* frame =
   4219      nsGridContainerFrame::GetGridFrameWithComputedInfo(
   4220          GetPrimaryFrame(FlushType::Layout));
   4221 
   4222  // If we get a nsGridContainerFrame from the prior call,
   4223  // all the next-in-flow frames will also be nsGridContainerFrames.
   4224  while (frame) {
   4225    // Get the existing Grid object, if it exists. This object is
   4226    // guaranteed to be up-to-date because GetGridFrameWithComputedInfo
   4227    // will delete an existing one when regenerating grid info.
   4228    Grid* gridFragment = frame->GetGridFragmentInfo();
   4229    if (!gridFragment) {
   4230      // Grid constructor will add itself as a property to frame, and
   4231      // its unlink method will remove itself if the frame still exists.
   4232      gridFragment = new Grid(this, frame);
   4233    }
   4234    aResult.AppendElement(gridFragment);
   4235    frame = static_cast<nsGridContainerFrame*>(frame->GetNextInFlow());
   4236  }
   4237 }
   4238 
   4239 bool Element::HasGridFragments() {
   4240  return !!nsGridContainerFrame::GetGridFrameWithComputedInfo(
   4241      GetPrimaryFrame(FlushType::Layout));
   4242 }
   4243 
   4244 already_AddRefed<DOMMatrixReadOnly> Element::GetTransformToAncestor(
   4245    Element& aAncestor) {
   4246  nsIFrame* primaryFrame = GetPrimaryFrame();
   4247  nsIFrame* ancestorFrame = aAncestor.GetPrimaryFrame();
   4248 
   4249  Matrix4x4 transform;
   4250  if (primaryFrame) {
   4251    // If aAncestor is not actually an ancestor of this (including nullptr),
   4252    // then the call to GetTransformToAncestor will return the transform
   4253    // all the way up through the parent chain.
   4254    transform = nsLayoutUtils::GetTransformToAncestor(RelativeTo{primaryFrame},
   4255                                                      RelativeTo{ancestorFrame},
   4256                                                      nsIFrame::IN_CSS_UNITS)
   4257                    .GetMatrix();
   4258  }
   4259 
   4260  DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
   4261  RefPtr<DOMMatrixReadOnly> result(matrix);
   4262  return result.forget();
   4263 }
   4264 
   4265 already_AddRefed<DOMMatrixReadOnly> Element::GetTransformToParent() {
   4266  nsIFrame* primaryFrame = GetPrimaryFrame();
   4267 
   4268  Matrix4x4 transform;
   4269  if (primaryFrame) {
   4270    nsIFrame* parentFrame = primaryFrame->GetParent();
   4271    transform = nsLayoutUtils::GetTransformToAncestor(RelativeTo{primaryFrame},
   4272                                                      RelativeTo{parentFrame},
   4273                                                      nsIFrame::IN_CSS_UNITS)
   4274                    .GetMatrix();
   4275  }
   4276 
   4277  DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
   4278  RefPtr<DOMMatrixReadOnly> result(matrix);
   4279  return result.forget();
   4280 }
   4281 
   4282 already_AddRefed<DOMMatrixReadOnly> Element::GetTransformToViewport() {
   4283  nsIFrame* primaryFrame = GetPrimaryFrame();
   4284  Matrix4x4 transform;
   4285  if (primaryFrame) {
   4286    transform =
   4287        nsLayoutUtils::GetTransformToAncestor(
   4288            RelativeTo{primaryFrame},
   4289            RelativeTo{nsLayoutUtils::GetDisplayRootFrame(primaryFrame)},
   4290            nsIFrame::IN_CSS_UNITS)
   4291            .GetMatrix();
   4292  }
   4293 
   4294  DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
   4295  RefPtr<DOMMatrixReadOnly> result(matrix);
   4296  return result.forget();
   4297 }
   4298 
   4299 already_AddRefed<Animation> Element::Animate(
   4300    JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
   4301    const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
   4302    ErrorResult& aError) {
   4303  nsCOMPtr<nsIGlobalObject> ownerGlobal = GetOwnerGlobal();
   4304  if (!ownerGlobal) {
   4305    aError.Throw(NS_ERROR_FAILURE);
   4306    return nullptr;
   4307  }
   4308  GlobalObject global(aContext, ownerGlobal->GetGlobalJSObject());
   4309  MOZ_ASSERT(!global.Failed());
   4310 
   4311  // KeyframeEffect constructor doesn't follow the standard Xray calling
   4312  // convention and needs to be called in caller's compartment.
   4313  // This should match to RunConstructorInCallerCompartment attribute in
   4314  // KeyframeEffect.webidl.
   4315  RefPtr<KeyframeEffect> effect =
   4316      KeyframeEffect::Constructor(global, this, aKeyframes, aOptions, aError);
   4317  if (aError.Failed()) {
   4318    return nullptr;
   4319  }
   4320 
   4321  // Animation constructor follows the standard Xray calling convention and
   4322  // needs to be called in the target element's realm.
   4323  JSAutoRealm ar(aContext, global.Get());
   4324 
   4325  AnimationTimeline* timeline = OwnerDoc()->Timeline();
   4326  RefPtr<Animation> animation = Animation::Constructor(
   4327      global, effect, Optional<AnimationTimeline*>(timeline), aError);
   4328  if (aError.Failed()) {
   4329    return nullptr;
   4330  }
   4331 
   4332  if (aOptions.IsKeyframeAnimationOptions()) {
   4333    animation->SetId(aOptions.GetAsKeyframeAnimationOptions().mId);
   4334  }
   4335 
   4336  animation->Play(aError, Animation::LimitBehavior::AutoRewind);
   4337  if (aError.Failed()) {
   4338    return nullptr;
   4339  }
   4340 
   4341  return animation.forget();
   4342 }
   4343 
   4344 void Element::GetAnimations(const GetAnimationsOptions& aOptions,
   4345                            nsTArray<RefPtr<Animation>>& aAnimations) {
   4346  if (Document* doc = GetComposedDoc()) {
   4347    // We don't need to explicitly flush throttled animations here, since
   4348    // updating the animation style of elements will never affect the set of
   4349    // running animations and it's only the set of running animations that is
   4350    // important here.
   4351    //
   4352    // NOTE: Any changes to the flags passed to the following call should
   4353    // be reflected in the flags passed in DocumentOrShadowRoot::GetAnimations
   4354    // too.
   4355    doc->FlushPendingNotifications(
   4356        ChangesToFlush(FlushType::Style, /* aFlushAnimations = */ false,
   4357                       /* aUpdateRelevancy = */ false));
   4358  }
   4359 
   4360  GetAnimationsWithoutFlush(aOptions, aAnimations);
   4361 }
   4362 
   4363 static void GetAnimationsUnsorted(const Element* aElement,
   4364                                  const PseudoStyleRequest& aPseudoRequest,
   4365                                  nsTArray<RefPtr<Animation>>& aAnimations) {
   4366  MOZ_ASSERT(aPseudoRequest.IsNotPseudo() ||
   4367                 AnimationUtils::IsSupportedPseudoForAnimations(aPseudoRequest),
   4368             "Unsupported pseudo type");
   4369  MOZ_ASSERT(aElement, "Null element");
   4370 
   4371  EffectSet* effects = EffectSet::Get(aElement, aPseudoRequest);
   4372  if (!effects) {
   4373    return;
   4374  }
   4375 
   4376  for (KeyframeEffect* effect : *effects) {
   4377    MOZ_ASSERT(effect && effect->GetAnimation(),
   4378               "Only effects associated with an animation should be "
   4379               "added to an element's effect set");
   4380    Animation* animation = effect->GetAnimation();
   4381 
   4382    MOZ_ASSERT(animation->IsRelevant(),
   4383               "Only relevant animations should be added to an element's "
   4384               "effect set");
   4385    aAnimations.AppendElement(animation);
   4386  }
   4387 }
   4388 
   4389 static inline bool IsSupportedForGetAnimationsSubtree(PseudoStyleType aType) {
   4390  return aType == PseudoStyleType::NotPseudo ||
   4391         aType == PseudoStyleType::mozSnapshotContainingBlock ||
   4392         PseudoStyle::IsViewTransitionPseudoElement(aType);
   4393 }
   4394 
   4395 // This traverses the subtree from the root, |aRootElement|, to get the
   4396 // animations.
   4397 static void GetAnimationsUnsortedForSubtree(
   4398    const Element* aRootElement, nsTArray<RefPtr<Animation>>& aAnimations) {
   4399  const PseudoStyleType type = aRootElement->GetPseudoElementType();
   4400  // Only elements and view transition pseudo-elements get handled in this
   4401  // function.
   4402  if (MOZ_UNLIKELY(!IsSupportedForGetAnimationsSubtree(type))) {
   4403    return;
   4404  }
   4405 
   4406  // For non pseudo-elements, we have to get the animations on the element
   4407  // itself, ::before, ::after, and ::marker.
   4408  if (type == PseudoStyleType::NotPseudo) {
   4409    for (const nsIContent* node = aRootElement; node;
   4410         node = node->GetNextNode(aRootElement)) {
   4411      if (!node->IsElement()) {
   4412        continue;
   4413      }
   4414      const Element* element = node->AsElement();
   4415      GetAnimationsUnsorted(element, PseudoStyleRequest::NotPseudo(),
   4416                            aAnimations);
   4417      GetAnimationsUnsorted(element, PseudoStyleRequest::Before(), aAnimations);
   4418      GetAnimationsUnsorted(element, PseudoStyleRequest::After(), aAnimations);
   4419      GetAnimationsUnsorted(element, PseudoStyleRequest::Marker(), aAnimations);
   4420      GetAnimationsUnsorted(element, PseudoStyleRequest::Backdrop(),
   4421                            aAnimations);
   4422    }
   4423  }
   4424 
   4425  // If |aRootElement| is the document element, or it is a view transition
   4426  // pseudo-element (including the snapshot containing block), we have to
   4427  // traverse the view transition subtree. Otherwise, we can skip the traversal.
   4428  if (!aRootElement->IsRootElement() && type == PseudoStyleType::NotPseudo) {
   4429    return;
   4430  }
   4431 
   4432  const Document* doc = aRootElement->OwnerDoc();
   4433  const Element* originatingElement = doc->GetRootElement();
   4434  if (!originatingElement) {
   4435    return;
   4436  }
   4437 
   4438  const Element* rootForTraversal = [&]() -> const Element* {
   4439    if (!aRootElement->IsRootElement()) {
   4440      // It is in the view transition pseudo-element tree already, so we use it
   4441      // directly.
   4442      return aRootElement;
   4443    }
   4444    // View transition pseudo-elements cannot be accessed directly from the
   4445    // document element, so we have to retrieve its tree root from the active
   4446    // view transition object.
   4447    const ViewTransition* vt = doc->GetActiveViewTransition();
   4448    return vt ? vt->GetViewTransitionTreeRoot() : nullptr;
   4449  }();
   4450 
   4451  for (const nsIContent* node = rootForTraversal; node;
   4452       node = node->GetNextNode(rootForTraversal)) {
   4453    if (!node->IsElement()) {
   4454      continue;
   4455    }
   4456    const Element* pseudo = node->AsElement();
   4457    const PseudoStyleRequest request(
   4458        pseudo->GetPseudoElementType(),
   4459        pseudo->HasName()
   4460            ? pseudo->GetParsedAttr(nsGkAtoms::name)->GetAtomValue()
   4461            : nullptr);
   4462    GetAnimationsUnsorted(originatingElement, request, aAnimations);
   4463  }
   4464 }
   4465 
   4466 void Element::GetAnimationsWithoutFlush(
   4467    const GetAnimationsOptions& aOptions,
   4468    nsTArray<RefPtr<Animation>>& aAnimations) {
   4469  Element* elem = this;
   4470  PseudoStyleRequest pseudoRequest;
   4471  // For animations on generated-content elements, the animations are stored
   4472  // on the parent element.
   4473  if (IsGeneratedContentContainerForBefore()) {
   4474    elem = GetParentElement();
   4475    pseudoRequest.mType = PseudoStyleType::before;
   4476  } else if (IsGeneratedContentContainerForAfter()) {
   4477    elem = GetParentElement();
   4478    pseudoRequest.mType = PseudoStyleType::after;
   4479  } else if (IsGeneratedContentContainerForMarker()) {
   4480    elem = GetParentElement();
   4481    pseudoRequest.mType = PseudoStyleType::marker;
   4482  } else if (IsGeneratedContentContainerForBackdrop()) {
   4483    elem = GetParentElement();
   4484    pseudoRequest.mType = PseudoStyleType::backdrop;
   4485  }
   4486 
   4487  if (!elem) {
   4488    return;
   4489  }
   4490 
   4491  // FIXME: Bug 1935557. Rewrite this to support pseudoElement option.
   4492  if (!aOptions.mSubtree || (pseudoRequest.mType == PseudoStyleType::before ||
   4493                             pseudoRequest.mType == PseudoStyleType::after ||
   4494                             pseudoRequest.mType == PseudoStyleType::backdrop ||
   4495                             pseudoRequest.mType == PseudoStyleType::marker)) {
   4496    // Case 1: Non-subtree, or |this| is ::before, ::after, or ::marker.
   4497    //
   4498    // ::before, ::after, and ::marker doesn't have subtree on themself, so we
   4499    // just simply get the animations of the element itself even if mSubtree is
   4500    // true.
   4501    GetAnimationsUnsorted(elem, pseudoRequest, aAnimations);
   4502  } else {
   4503    // Case 2: Subtree. |this| is an element or a view transition
   4504    // pseudo-element.
   4505    GetAnimationsUnsortedForSubtree(elem, aAnimations);
   4506  }
   4507  aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
   4508 }
   4509 
   4510 void Element::CloneAnimationsFrom(const Element& aOther) {
   4511  AnimationTimeline* const timeline = OwnerDoc()->Timeline();
   4512  MOZ_ASSERT(timeline, "Timeline has not been set on the document yet");
   4513  // Iterate through all pseudo types and copy the effects from each of the
   4514  // other element's effect sets into this element's effect set.
   4515  // FIXME: Bug 1929470. This function is for printing, and it may be tricky to
   4516  // support view transitions. We have to revisit here after we support view
   4517  // transitions to make sure we clone the animations properly.
   4518  for (PseudoStyleType pseudoType :
   4519       {PseudoStyleType::NotPseudo, PseudoStyleType::before,
   4520        PseudoStyleType::after, PseudoStyleType::marker,
   4521        PseudoStyleType::backdrop}) {
   4522    // If the element has an effect set for this pseudo type (or not pseudo)
   4523    // then copy the effects and animation properties.
   4524    const PseudoStyleRequest request(pseudoType);
   4525    if (auto* const effects = EffectSet::Get(&aOther, request)) {
   4526      auto* const clonedEffects = EffectSet::GetOrCreate(this, request);
   4527      for (KeyframeEffect* const effect : *effects) {
   4528        auto* animation = effect->GetAnimation();
   4529        if (animation->AsCSSTransition()) {
   4530          // Don't clone transitions, for compat with other browsers.
   4531          continue;
   4532        }
   4533        // Clone the effect.
   4534        RefPtr<KeyframeEffect> clonedEffect = new KeyframeEffect(
   4535            OwnerDoc(), OwningAnimationTarget{this, request}, *effect);
   4536 
   4537        // Clone the animation
   4538        RefPtr<Animation> clonedAnimation = Animation::ClonePausedAnimation(
   4539            OwnerDoc()->GetParentObject(), *animation, *clonedEffect,
   4540            *timeline);
   4541        if (!clonedAnimation) {
   4542          continue;
   4543        }
   4544        clonedEffects->AddEffect(*clonedEffect);
   4545      }
   4546    }
   4547  }
   4548 }
   4549 
   4550 void Element::GetInnerHTML(nsAString& aInnerHTML, OOMReporter& aError) {
   4551  GetMarkup(false, aInnerHTML);
   4552 }
   4553 
   4554 void Element::GetInnerHTML(OwningTrustedHTMLOrNullIsEmptyString& aInnerHTML,
   4555                           OOMReporter& aError) {
   4556  GetInnerHTML(aInnerHTML.SetAsNullIsEmptyString(), aError);
   4557 }
   4558 
   4559 void Element::SetInnerHTML(const TrustedHTMLOrNullIsEmptyString& aInnerHTML,
   4560                           nsIPrincipal* aSubjectPrincipal,
   4561                           ErrorResult& aError) {
   4562  constexpr nsLiteralString sink = u"Element innerHTML"_ns;
   4563 
   4564  Maybe<nsAutoString> compliantStringHolder;
   4565  const nsAString* compliantString =
   4566      TrustedTypeUtils::GetTrustedTypesCompliantString(
   4567          aInnerHTML, sink, kTrustedTypesOnlySinkGroup, *this,
   4568          aSubjectPrincipal, compliantStringHolder, aError);
   4569 
   4570  if (aError.Failed()) {
   4571    return;
   4572  }
   4573 
   4574  SetInnerHTMLTrusted(*compliantString, aSubjectPrincipal, aError);
   4575 }
   4576 
   4577 void Element::SetInnerHTMLTrusted(const nsAString& aInnerHTML,
   4578                                  nsIPrincipal* aSubjectPrincipal,
   4579                                  ErrorResult& aError) {
   4580  SetInnerHTMLInternal(aInnerHTML, aError);
   4581 }
   4582 
   4583 void Element::GetOuterHTML(OwningTrustedHTMLOrNullIsEmptyString& aOuterHTML) {
   4584  GetMarkup(true, aOuterHTML.SetAsNullIsEmptyString());
   4585 }
   4586 
   4587 void Element::SetOuterHTML(const TrustedHTMLOrNullIsEmptyString& aOuterHTML,
   4588                           nsIPrincipal* aSubjectPrincipal,
   4589                           ErrorResult& aError) {
   4590  constexpr nsLiteralString sink = u"Element outerHTML"_ns;
   4591 
   4592  Maybe<nsAutoString> compliantStringHolder;
   4593  const nsAString* compliantString =
   4594      TrustedTypeUtils::GetTrustedTypesCompliantString(
   4595          aOuterHTML, sink, kTrustedTypesOnlySinkGroup, *this,
   4596          aSubjectPrincipal, compliantStringHolder, aError);
   4597  if (aError.Failed()) {
   4598    return;
   4599  }
   4600 
   4601  nsCOMPtr<nsINode> parent = GetParentNode();
   4602  if (!parent) {
   4603    return;
   4604  }
   4605 
   4606  if (parent->NodeType() == DOCUMENT_NODE) {
   4607    aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
   4608    return;
   4609  }
   4610 
   4611  if (OwnerDoc()->IsHTMLDocument()) {
   4612    nsAtom* localName;
   4613    int32_t namespaceID;
   4614    if (parent->IsElement()) {
   4615      localName = parent->NodeInfo()->NameAtom();
   4616      namespaceID = parent->NodeInfo()->NamespaceID();
   4617    } else {
   4618      NS_ASSERTION(
   4619          parent->NodeType() == DOCUMENT_FRAGMENT_NODE,
   4620          "How come the parent isn't a document, a fragment or an element?");
   4621      localName = nsGkAtoms::body;
   4622      namespaceID = kNameSpaceID_XHTML;
   4623    }
   4624    RefPtr<DocumentFragment> fragment = new (OwnerDoc()->NodeInfoManager())
   4625        DocumentFragment(OwnerDoc()->NodeInfoManager());
   4626    nsContentUtils::ParseFragmentHTML(
   4627        *compliantString, fragment, localName, namespaceID,
   4628        OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
   4629    parent->ReplaceChild(*fragment, *this, aError);
   4630    return;
   4631  }
   4632 
   4633  nsCOMPtr<nsINode> context;
   4634  if (parent->IsElement()) {
   4635    context = parent;
   4636  } else {
   4637    NS_ASSERTION(
   4638        parent->NodeType() == DOCUMENT_FRAGMENT_NODE,
   4639        "How come the parent isn't a document, a fragment or an element?");
   4640    RefPtr<mozilla::dom::NodeInfo> info =
   4641        OwnerDoc()->NodeInfoManager()->GetNodeInfo(
   4642            nsGkAtoms::body, nullptr, kNameSpaceID_XHTML, ELEMENT_NODE);
   4643    context = NS_NewHTMLBodyElement(info.forget(), FROM_PARSER_FRAGMENT);
   4644  }
   4645 
   4646  RefPtr<DocumentFragment> fragment = nsContentUtils::CreateContextualFragment(
   4647      context, *compliantString, true, aError);
   4648  if (aError.Failed()) {
   4649    return;
   4650  }
   4651  parent->ReplaceChild(*fragment, *this, aError);
   4652 }
   4653 
   4654 enum nsAdjacentPosition { eBeforeBegin, eAfterBegin, eBeforeEnd, eAfterEnd };
   4655 
   4656 void Element::InsertAdjacentHTML(
   4657    const nsAString& aPosition, const TrustedHTMLOrString& aTrustedHTMLOrString,
   4658    nsIPrincipal* aSubjectPrincipal, ErrorResult& aError) {
   4659  constexpr nsLiteralString kSink = u"Element insertAdjacentHTML"_ns;
   4660 
   4661  Maybe<nsAutoString> compliantStringHolder;
   4662  const nsAString* compliantString =
   4663      TrustedTypeUtils::GetTrustedTypesCompliantString(
   4664          aTrustedHTMLOrString, kSink, kTrustedTypesOnlySinkGroup, *this,
   4665          aSubjectPrincipal, compliantStringHolder, aError);
   4666 
   4667  if (aError.Failed()) {
   4668    return;
   4669  }
   4670 
   4671  nsAdjacentPosition position;
   4672  if (aPosition.LowerCaseEqualsLiteral("beforebegin")) {
   4673    position = eBeforeBegin;
   4674  } else if (aPosition.LowerCaseEqualsLiteral("afterbegin")) {
   4675    position = eAfterBegin;
   4676  } else if (aPosition.LowerCaseEqualsLiteral("beforeend")) {
   4677    position = eBeforeEnd;
   4678  } else if (aPosition.LowerCaseEqualsLiteral("afterend")) {
   4679    position = eAfterEnd;
   4680  } else {
   4681    aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   4682    return;
   4683  }
   4684 
   4685  nsCOMPtr<nsIContent> destination;
   4686  if (position == eBeforeBegin || position == eAfterEnd) {
   4687    destination = GetParent();
   4688    if (!destination) {
   4689      aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
   4690      return;
   4691    }
   4692  } else {
   4693    destination = this;
   4694  }
   4695 
   4696  // mozAutoDocUpdate keeps the owner document alive.  Therefore, using a raw
   4697  // pointer here is safe.
   4698  Document* const doc = OwnerDoc();
   4699 
   4700  // Needed when insertAdjacentHTML is used in combination with contenteditable
   4701  mozAutoDocUpdate updateBatch(doc, true);
   4702  nsAutoScriptLoaderDisabler sld(doc);
   4703 
   4704  // Parse directly into destination if possible
   4705  nsIContent* oldLastChild = destination->GetLastChild();
   4706  bool oldLastChildIsText = oldLastChild && oldLastChild->IsText();
   4707  if (doc->IsHTMLDocument() && !OwnerDoc()->MayHaveDOMMutationObservers() &&
   4708      ((position == eBeforeEnd && !oldLastChildIsText) ||
   4709       (position == eAfterEnd && !GetNextSibling()) ||
   4710       (position == eAfterBegin && !GetFirstChild()))) {
   4711    doc->SuspendDOMNotifications();
   4712    int32_t contextNs = destination->GetNameSpaceID();
   4713    nsAtom* contextLocal = destination->NodeInfo()->NameAtom();
   4714    if (contextLocal == nsGkAtoms::html && contextNs == kNameSpaceID_XHTML) {
   4715      // For compat with IE6 through IE9. Willful violation of HTML5 as of
   4716      // 2011-04-06. CreateContextualFragment does the same already.
   4717      // Spec bug: http://www.w3.org/Bugs/Public/show_bug.cgi?id=12434
   4718      contextLocal = nsGkAtoms::body;
   4719    }
   4720    aError = nsContentUtils::ParseFragmentHTML(
   4721        *compliantString, destination, contextLocal, contextNs,
   4722        doc->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
   4723    doc->ResumeDOMNotifications();
   4724    nsIContent* firstNewChild = oldLastChild ? oldLastChild->GetNextSibling()
   4725                                             : destination->GetFirstChild();
   4726    if (firstNewChild) {
   4727      MutationObservers::NotifyContentAppended(destination, firstNewChild, {});
   4728    }
   4729    return;
   4730  }
   4731 
   4732  // couldn't parse directly
   4733  RefPtr<DocumentFragment> fragment = nsContentUtils::CreateContextualFragment(
   4734      destination, *compliantString, true, aError);
   4735  if (aError.Failed()) {
   4736    return;
   4737  }
   4738 
   4739  // Suppress assertion about node removal mutation events that can't have
   4740  // listeners anyway, because no one has had the chance to register mutation
   4741  // listeners on the fragment that comes from the parser.
   4742  nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
   4743 
   4744  switch (position) {
   4745    case eBeforeBegin:
   4746      destination->InsertBefore(*fragment, this, aError);
   4747      break;
   4748    case eAfterBegin:
   4749      static_cast<nsINode*>(this)->InsertBefore(*fragment, GetFirstChild(),
   4750                                                aError);
   4751      break;
   4752    case eBeforeEnd:
   4753      static_cast<nsINode*>(this)->AppendChild(*fragment, aError);
   4754      break;
   4755    case eAfterEnd:
   4756      destination->InsertBefore(*fragment, GetNextSibling(), aError);
   4757      break;
   4758  }
   4759 }
   4760 
   4761 nsINode* Element::InsertAdjacent(const nsAString& aWhere, nsINode* aNode,
   4762                                 ErrorResult& aError) {
   4763  if (aWhere.LowerCaseEqualsLiteral("beforebegin")) {
   4764    nsCOMPtr<nsINode> parent = GetParentNode();
   4765    if (!parent) {
   4766      return nullptr;
   4767    }
   4768    parent->InsertBefore(*aNode, this, aError);
   4769  } else if (aWhere.LowerCaseEqualsLiteral("afterbegin")) {
   4770    nsCOMPtr<nsINode> refNode = GetFirstChild();
   4771    static_cast<nsINode*>(this)->InsertBefore(*aNode, refNode, aError);
   4772  } else if (aWhere.LowerCaseEqualsLiteral("beforeend")) {
   4773    static_cast<nsINode*>(this)->AppendChild(*aNode, aError);
   4774  } else if (aWhere.LowerCaseEqualsLiteral("afterend")) {
   4775    nsCOMPtr<nsINode> parent = GetParentNode();
   4776    if (!parent) {
   4777      return nullptr;
   4778    }
   4779    nsCOMPtr<nsINode> refNode = GetNextSibling();
   4780    parent->InsertBefore(*aNode, refNode, aError);
   4781  } else {
   4782    aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   4783    return nullptr;
   4784  }
   4785 
   4786  return aError.Failed() ? nullptr : aNode;
   4787 }
   4788 
   4789 Element* Element::InsertAdjacentElement(const nsAString& aWhere,
   4790                                        Element& aElement,
   4791                                        ErrorResult& aError) {
   4792  nsINode* newNode = InsertAdjacent(aWhere, &aElement, aError);
   4793  MOZ_ASSERT(!newNode || newNode->IsElement());
   4794 
   4795  return newNode ? newNode->AsElement() : nullptr;
   4796 }
   4797 
   4798 void Element::InsertAdjacentText(const nsAString& aWhere,
   4799                                 const nsAString& aData, ErrorResult& aError) {
   4800  RefPtr<nsTextNode> textNode = OwnerDoc()->CreateTextNode(aData);
   4801  InsertAdjacent(aWhere, textNode, aError);
   4802 }
   4803 
   4804 TextEditor* Element::GetTextEditorInternal() {
   4805  TextControlElement* textControlElement = TextControlElement::FromNode(this);
   4806  return textControlElement ? MOZ_KnownLive(textControlElement)->GetTextEditor()
   4807                            : nullptr;
   4808 }
   4809 
   4810 nsresult Element::SetBoolAttr(nsAtom* aAttr, bool aValue) {
   4811  if (aValue) {
   4812    return SetAttr(kNameSpaceID_None, aAttr, u""_ns, true);
   4813  }
   4814 
   4815  return UnsetAttr(kNameSpaceID_None, aAttr, true);
   4816 }
   4817 
   4818 void Element::GetEnumAttr(nsAtom* aAttr, const char* aDefault,
   4819                          nsAString& aResult) const {
   4820  GetEnumAttr(aAttr, aDefault, aDefault, aResult);
   4821 }
   4822 
   4823 void Element::GetEnumAttr(nsAtom* aAttr, const char* aDefaultMissing,
   4824                          const char* aDefaultInvalid,
   4825                          nsAString& aResult) const {
   4826  const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
   4827 
   4828  aResult.Truncate();
   4829 
   4830  if (!attrVal) {
   4831    if (aDefaultMissing) {
   4832      AppendASCIItoUTF16(nsDependentCString(aDefaultMissing), aResult);
   4833    } else {
   4834      SetDOMStringToNull(aResult);
   4835    }
   4836  } else {
   4837    if (attrVal->Type() == nsAttrValue::eEnum) {
   4838      attrVal->GetEnumString(aResult, true);
   4839    } else if (aDefaultInvalid) {
   4840      AppendASCIItoUTF16(nsDependentCString(aDefaultInvalid), aResult);
   4841    }
   4842  }
   4843 }
   4844 
   4845 void Element::SetOrRemoveNullableStringAttr(nsAtom* aName,
   4846                                            const nsAString& aValue,
   4847                                            ErrorResult& aError) {
   4848  if (DOMStringIsNull(aValue)) {
   4849    UnsetAttr(aName, aError);
   4850  } else {
   4851    SetAttr(aName, aValue, aError);
   4852  }
   4853 }
   4854 
   4855 Directionality Element::GetComputedDirectionality() const {
   4856  if (nsIFrame* frame = GetPrimaryFrame()) {
   4857    return frame->StyleVisibility()->mDirection == StyleDirection::Ltr
   4858               ? Directionality::Ltr
   4859               : Directionality::Rtl;
   4860  }
   4861 
   4862  return GetDirectionality();
   4863 }
   4864 
   4865 float Element::FontSizeInflation() {
   4866  nsIFrame* frame = GetPrimaryFrame();
   4867  if (!frame) {
   4868    return -1.0;
   4869  }
   4870 
   4871  if (nsLayoutUtils::FontSizeInflationEnabled(frame->PresContext())) {
   4872    return nsLayoutUtils::FontSizeInflationFor(frame);
   4873  }
   4874 
   4875  return 1.0;
   4876 }
   4877 
   4878 void Element::GetImplementedPseudoElement(nsAString& aPseudo) const {
   4879  PseudoStyleType pseudoType = GetPseudoElementType();
   4880  if (pseudoType == PseudoStyleType::NotPseudo) {
   4881    return SetDOMStringToNull(aPseudo);
   4882  }
   4883  nsDependentAtomString pseudo(nsCSSPseudoElements::GetPseudoAtom(pseudoType));
   4884 
   4885  // We want to use the modern syntax (::placeholder, etc), but the atoms only
   4886  // contain one semi-colon.
   4887  MOZ_ASSERT(pseudo.Length() > 2 && pseudo[0] == ':' && pseudo[1] != ':');
   4888 
   4889  aPseudo.Truncate();
   4890  aPseudo.SetCapacity(pseudo.Length() + 1);
   4891  aPseudo.Append(':');
   4892  aPseudo.Append(pseudo);
   4893 }
   4894 
   4895 // This function traverses the view transition pseudo-elements tree and finds
   4896 // the pseudo-element matched with |aRequest|.
   4897 static Element* SearchViewTransitionPseudo(const Element* aElement,
   4898                                           const PseudoStyleRequest& aRequest) {
   4899  // If |aElement| is not the root.
   4900  if (!aElement->IsRootElement()) {
   4901    return nullptr;
   4902  }
   4903 
   4904  const Document* doc = aElement->OwnerDoc();
   4905  const ViewTransition* vt = doc->GetActiveViewTransition();
   4906  if (!vt) {
   4907    return nullptr;
   4908  }
   4909 
   4910  return vt->FindPseudo(aRequest);
   4911 }
   4912 
   4913 Element* Element::GetPseudoElement(const PseudoStyleRequest& aRequest) const {
   4914  switch (aRequest.mType) {
   4915    case PseudoStyleType::NotPseudo:
   4916      // It's unfortunate we have to do const cast, so we don't have to write
   4917      // the almost duplicate function for the non-const function.
   4918      return const_cast<Element*>(this);
   4919    case PseudoStyleType::before:
   4920      return nsLayoutUtils::GetBeforePseudo(this);
   4921    case PseudoStyleType::after:
   4922      return nsLayoutUtils::GetAfterPseudo(this);
   4923    case PseudoStyleType::marker:
   4924      return nsLayoutUtils::GetMarkerPseudo(this);
   4925    case PseudoStyleType::backdrop:
   4926      return nsLayoutUtils::GetBackdropPseudo(this);
   4927    case PseudoStyleType::viewTransition:
   4928    case PseudoStyleType::viewTransitionGroup:
   4929    case PseudoStyleType::viewTransitionImagePair:
   4930    case PseudoStyleType::viewTransitionOld:
   4931    case PseudoStyleType::viewTransitionNew: {
   4932      Element* result = SearchViewTransitionPseudo(this, aRequest);
   4933      MOZ_ASSERT(!result || result->GetPseudoElementType() == aRequest.mType,
   4934                 "The type should match");
   4935      MOZ_ASSERT(!result || !result->HasName() ||
   4936                     result->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() ==
   4937                         aRequest.mIdentifier,
   4938                 "The identifier should match");
   4939      return result;
   4940    }
   4941    default:
   4942      return nullptr;
   4943  }
   4944 }
   4945 
   4946 ReferrerPolicy Element::GetReferrerPolicyAsEnum() const {
   4947  if (IsHTMLElement()) {
   4948    return ReferrerPolicyFromAttr(GetParsedAttr(nsGkAtoms::referrerpolicy));
   4949  }
   4950  return ReferrerPolicy::_empty;
   4951 }
   4952 
   4953 ReferrerPolicy Element::ReferrerPolicyFromAttr(
   4954    const nsAttrValue* aValue) const {
   4955  if (aValue && aValue->Type() == nsAttrValue::eEnum) {
   4956    return ReferrerPolicy(aValue->GetEnumValue());
   4957  }
   4958  return ReferrerPolicy::_empty;
   4959 }
   4960 
   4961 already_AddRefed<nsDOMStringMap> Element::Dataset() {
   4962  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
   4963  if (!slots->mDataset) {
   4964    // mDataset is a weak reference so assignment will not AddRef.
   4965    // AddRef is called before returning the pointer.
   4966    slots->mDataset = new nsDOMStringMap(this);
   4967  }
   4968  return do_AddRef(slots->mDataset);
   4969 }
   4970 
   4971 void Element::ClearDataset() {
   4972  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
   4973  MOZ_ASSERT(slots && slots->mDataset,
   4974             "Slots should exist and dataset should not be null.");
   4975  slots->mDataset = nullptr;
   4976 }
   4977 
   4978 template <class T>
   4979 void Element::GetCustomInterface(nsGetterAddRefs<T> aResult) {
   4980  nsCOMPtr<nsISupports> iface =
   4981      CustomElementRegistry::CallGetCustomInterface(this, NS_GET_IID(T));
   4982  if (iface) {
   4983    if (NS_SUCCEEDED(CallQueryInterface(iface, static_cast<T**>(aResult)))) {
   4984      return;
   4985    }
   4986  }
   4987 }
   4988 
   4989 void Element::ClearServoData(Document* aDoc) {
   4990  MOZ_ASSERT(aDoc);
   4991  if (HasServoData()) {
   4992    Servo_Element_ClearData(this);
   4993  } else {
   4994    UnsetFlags(kAllServoDescendantBits | NODE_NEEDS_FRAME);
   4995  }
   4996  // Since this element is losing its servo data, nothing under it may have
   4997  // servo data either, so we can forget restyles rooted at this element. This
   4998  // is necessary for correctness, since we invoke ClearServoData in various
   4999  // places where an element's flattened tree parent changes, and such a change
   5000  // may also make an element invalid to be used as a restyle root.
   5001  if (aDoc->GetServoRestyleRoot() == this) {
   5002    aDoc->ClearServoRestyleRoot();
   5003  }
   5004 }
   5005 
   5006 bool Element::IsPopoverOpenedInMode(PopoverAttributeState aMode) const {
   5007  const auto* htmlElement = nsGenericHTMLElement::FromNode(this);
   5008  return htmlElement && htmlElement->PopoverOpen() &&
   5009         htmlElement->GetPopoverData()->GetOpenedInMode() == aMode;
   5010 }
   5011 
   5012 bool Element::IsPopoverOpen() const {
   5013  const auto* htmlElement = nsGenericHTMLElement::FromNode(this);
   5014  return htmlElement && htmlElement->PopoverOpen();
   5015 }
   5016 
   5017 void Element::SetAssociatedPopover(nsGenericHTMLElement& aPopover) {
   5018  MOZ_ASSERT(IsHTMLElement());
   5019  MOZ_ASSERT(aPopover.IsHTMLElement());
   5020  auto* slots = ExtendedDOMSlots();
   5021  slots->mAssociatedPopover = do_GetWeakReference(&aPopover);
   5022 }
   5023 
   5024 nsGenericHTMLElement* Element::GetAssociatedPopover() const {
   5025  if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
   5026    if (nsCOMPtr<nsGenericHTMLElement> popover =
   5027            do_QueryReferent(slots->mAssociatedPopover)) {
   5028      if (popover->GetPopoverData() &&
   5029          popover->GetPopoverData()->GetInvoker() == this) {
   5030        return popover;
   5031      }
   5032    }
   5033  }
   5034  return nullptr;
   5035 }
   5036 
   5037 Element* Element::GetTopmostPopoverAncestor(PopoverAttributeState aMode,
   5038                                            const Element* aInvoker,
   5039                                            bool isPopover) const {
   5040  const Element* newPopover = this;
   5041 
   5042  nsTHashMap<nsPtrHashKey<const Element>, size_t> popoverPositions;
   5043  size_t index = 0;
   5044  for (Element* popover : OwnerDoc()->PopoverListOf(aMode)) {
   5045    popoverPositions.LookupOrInsert(popover, index++);
   5046  }
   5047 
   5048  if (isPopover) {
   5049    popoverPositions.LookupOrInsert(newPopover, index);
   5050  }
   5051 
   5052  Element* topmostPopoverAncestor = nullptr;
   5053 
   5054  auto checkAncestor = [&](const Element* candidate) {
   5055    if (!candidate) {
   5056      return;
   5057    }
   5058    Element* candidateAncestor = candidate->GetNearestInclusiveOpenPopover();
   5059    if (!candidateAncestor) {
   5060      return;
   5061    }
   5062    size_t candidatePosition;
   5063    if (popoverPositions.Get(candidateAncestor, &candidatePosition)) {
   5064      size_t topmostPosition;
   5065      if (!topmostPopoverAncestor ||
   5066          (popoverPositions.Get(topmostPopoverAncestor, &topmostPosition) &&
   5067           topmostPosition < candidatePosition)) {
   5068        topmostPopoverAncestor = candidateAncestor;
   5069      }
   5070    }
   5071  };
   5072 
   5073  checkAncestor(newPopover->GetFlattenedTreeParentElement());
   5074  checkAncestor(aInvoker);
   5075 
   5076  return topmostPopoverAncestor;
   5077 }
   5078 
   5079 ElementAnimationData& Element::CreateAnimationData() {
   5080  MOZ_ASSERT(!GetAnimationData());
   5081  SetMayHaveAnimations();
   5082  auto* slots = ExtendedDOMSlots();
   5083  slots->mAnimations = MakeUnique<ElementAnimationData>();
   5084  return *slots->mAnimations;
   5085 }
   5086 
   5087 PopoverData& Element::CreatePopoverData() {
   5088  MOZ_ASSERT(!GetPopoverData());
   5089  auto* slots = ExtendedDOMSlots();
   5090  slots->mPopoverData = MakeUnique<PopoverData>();
   5091  return *slots->mPopoverData;
   5092 }
   5093 
   5094 void Element::ClearPopoverData() {
   5095  nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
   5096  if (slots) {
   5097    slots->mPopoverData = nullptr;
   5098  }
   5099 }
   5100 
   5101 void Element::SetCustomElementData(UniquePtr<CustomElementData> aData) {
   5102  SetHasCustomElementData();
   5103 
   5104  if (aData->mState != CustomElementData::State::eCustom) {
   5105    SetDefined(false);
   5106  }
   5107 
   5108  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
   5109  MOZ_ASSERT(!slots->mCustomElementData,
   5110             "Custom element data may not be changed once set.");
   5111 #if DEBUG
   5112  // We assert only XUL usage, since web may pass whatever as 'is' value
   5113  if (NodeInfo()->NamespaceID() == kNameSpaceID_XUL) {
   5114    nsAtom* name = NodeInfo()->NameAtom();
   5115    nsAtom* type = aData->GetCustomElementType();
   5116    // Check to see if the tag name is a dashed name.
   5117    if (nsContentUtils::IsNameWithDash(name)) {
   5118      // Assert that a tag name with dashes is always an autonomous custom
   5119      // element.
   5120      MOZ_ASSERT(type == name);
   5121    } else {
   5122      // Could still be an autonomous custom element with a non-dashed tag name.
   5123      // Need the check below for sure.
   5124      if (type != name) {
   5125        // Assert that the name of the built-in custom element type is always
   5126        // a dashed name.
   5127        MOZ_ASSERT(nsContentUtils::IsNameWithDash(type));
   5128      }
   5129    }
   5130  }
   5131 #endif
   5132  slots->mCustomElementData = std::move(aData);
   5133 }
   5134 
   5135 nsTArray<RefPtr<nsAtom>>& Element::EnsureCustomStates() {
   5136  MOZ_ASSERT(IsHTMLElement());
   5137  nsExtendedDOMSlots* slots = ExtendedDOMSlots();
   5138  return slots->mCustomStates;
   5139 }
   5140 
   5141 CustomElementDefinition* Element::GetCustomElementDefinition() const {
   5142  CustomElementData* data = GetCustomElementData();
   5143  if (!data) {
   5144    return nullptr;
   5145  }
   5146 
   5147  return data->GetCustomElementDefinition();
   5148 }
   5149 
   5150 void Element::SetCustomElementDefinition(CustomElementDefinition* aDefinition) {
   5151  CustomElementData* data = GetCustomElementData();
   5152  MOZ_ASSERT(data);
   5153 
   5154  data->SetCustomElementDefinition(aDefinition);
   5155 }
   5156 
   5157 already_AddRefed<nsIDOMXULButtonElement> Element::AsXULButton() {
   5158  nsCOMPtr<nsIDOMXULButtonElement> value;
   5159  GetCustomInterface(getter_AddRefs(value));
   5160  return value.forget();
   5161 }
   5162 
   5163 already_AddRefed<nsIDOMXULContainerElement> Element::AsXULContainer() {
   5164  nsCOMPtr<nsIDOMXULContainerElement> value;
   5165  GetCustomInterface(getter_AddRefs(value));
   5166  return value.forget();
   5167 }
   5168 
   5169 already_AddRefed<nsIDOMXULContainerItemElement> Element::AsXULContainerItem() {
   5170  nsCOMPtr<nsIDOMXULContainerItemElement> value;
   5171  GetCustomInterface(getter_AddRefs(value));
   5172  return value.forget();
   5173 }
   5174 
   5175 already_AddRefed<nsIDOMXULControlElement> Element::AsXULControl() {
   5176  nsCOMPtr<nsIDOMXULControlElement> value;
   5177  GetCustomInterface(getter_AddRefs(value));
   5178  return value.forget();
   5179 }
   5180 
   5181 already_AddRefed<nsIDOMXULMenuListElement> Element::AsXULMenuList() {
   5182  nsCOMPtr<nsIDOMXULMenuListElement> value;
   5183  GetCustomInterface(getter_AddRefs(value));
   5184  return value.forget();
   5185 }
   5186 
   5187 already_AddRefed<nsIDOMXULMultiSelectControlElement>
   5188 Element::AsXULMultiSelectControl() {
   5189  nsCOMPtr<nsIDOMXULMultiSelectControlElement> value;
   5190  GetCustomInterface(getter_AddRefs(value));
   5191  return value.forget();
   5192 }
   5193 
   5194 already_AddRefed<nsIDOMXULRadioGroupElement> Element::AsXULRadioGroup() {
   5195  nsCOMPtr<nsIDOMXULRadioGroupElement> value;
   5196  GetCustomInterface(getter_AddRefs(value));
   5197  return value.forget();
   5198 }
   5199 
   5200 already_AddRefed<nsIDOMXULRelatedElement> Element::AsXULRelated() {
   5201  nsCOMPtr<nsIDOMXULRelatedElement> value;
   5202  GetCustomInterface(getter_AddRefs(value));
   5203  return value.forget();
   5204 }
   5205 
   5206 already_AddRefed<nsIDOMXULSelectControlElement> Element::AsXULSelectControl() {
   5207  nsCOMPtr<nsIDOMXULSelectControlElement> value;
   5208  GetCustomInterface(getter_AddRefs(value));
   5209  return value.forget();
   5210 }
   5211 
   5212 already_AddRefed<nsIDOMXULSelectControlItemElement>
   5213 Element::AsXULSelectControlItem() {
   5214  nsCOMPtr<nsIDOMXULSelectControlItemElement> value;
   5215  GetCustomInterface(getter_AddRefs(value));
   5216  return value.forget();
   5217 }
   5218 
   5219 already_AddRefed<nsIBrowser> Element::AsBrowser() {
   5220  nsCOMPtr<nsIBrowser> value;
   5221  GetCustomInterface(getter_AddRefs(value));
   5222  return value.forget();
   5223 }
   5224 
   5225 already_AddRefed<nsIAutoCompletePopup> Element::AsAutoCompletePopup() {
   5226  nsCOMPtr<nsIAutoCompletePopup> value;
   5227  GetCustomInterface(getter_AddRefs(value));
   5228  return value.forget();
   5229 }
   5230 
   5231 nsPresContext* Element::GetPresContext(PresContextFor aFor) const {
   5232  // Get the document
   5233  Document* doc =
   5234      (aFor == eForComposedDoc) ? GetComposedDoc() : GetUncomposedDoc();
   5235  if (doc) {
   5236    return doc->GetPresContext();
   5237  }
   5238 
   5239  return nullptr;
   5240 }
   5241 
   5242 MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf)
   5243 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf)
   5244 
   5245 void Element::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
   5246                                     size_t* aNodeSize) const {
   5247  FragmentOrElement::AddSizeOfExcludingThis(aSizes, aNodeSize);
   5248  *aNodeSize += mAttrs.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
   5249 
   5250  if (HasServoData()) {
   5251    // Measure the ElementData object itself.
   5252    aSizes.mLayoutElementDataObjects +=
   5253        aSizes.mState.mMallocSizeOf(mServoData.Get());
   5254 
   5255    // Measure mServoData, excluding the ComputedValues. This measurement
   5256    // counts towards the element's size. We use ServoElementMallocSizeOf and
   5257    // ServoElementMallocEnclosingSizeOf rather than |aState.mMallocSizeOf| to
   5258    // better distinguish in DMD's output the memory measured within Servo
   5259    // code.
   5260    *aNodeSize += Servo_Element_SizeOfExcludingThisAndCVs(
   5261        ServoElementMallocSizeOf, ServoElementMallocEnclosingSizeOf,
   5262        &aSizes.mState.mSeenPtrs, this);
   5263 
   5264    // Now measure just the ComputedValues (and style structs) under
   5265    // mServoData. This counts towards the relevant fields in |aSizes|.
   5266    if (auto* style = Servo_Element_GetMaybeOutOfDateStyle(this)) {
   5267      if (!aSizes.mState.HaveSeenPtr(style)) {
   5268        style->AddSizeOfIncludingThis(aSizes, &aSizes.mLayoutComputedValuesDom);
   5269      }
   5270 
   5271      for (size_t i = 0; i < PseudoStyle::kEagerPseudoCount; i++) {
   5272        if (auto* style = Servo_Element_GetMaybeOutOfDatePseudoStyle(this, i)) {
   5273          if (!aSizes.mState.HaveSeenPtr(style)) {
   5274            style->AddSizeOfIncludingThis(aSizes,
   5275                                          &aSizes.mLayoutComputedValuesDom);
   5276          }
   5277        }
   5278      }
   5279    }
   5280  }
   5281 }
   5282 
   5283 #ifdef DEBUG
   5284 static bool BitsArePropagated(const Element* aElement, uint32_t aBits,
   5285                              nsINode* aRestyleRoot) {
   5286  const Element* curr = aElement;
   5287  while (curr) {
   5288    if (curr == aRestyleRoot) {
   5289      return true;
   5290    }
   5291    if (!curr->HasAllFlags(aBits)) {
   5292      return false;
   5293    }
   5294    nsINode* parentNode = curr->GetParentNode();
   5295    curr = curr->GetFlattenedTreeParentElementForStyle();
   5296    MOZ_ASSERT_IF(!curr,
   5297                  parentNode == aElement->OwnerDoc() ||
   5298                      parentNode == parentNode->OwnerDoc()->GetRootElement());
   5299  }
   5300  return true;
   5301 }
   5302 #endif
   5303 
   5304 static inline void AssertNoBitsPropagatedFrom(nsINode* aRoot) {
   5305 #ifdef DEBUG
   5306  if (!aRoot || !aRoot->IsElement()) {
   5307    return;
   5308  }
   5309 
   5310  auto* element = aRoot->GetFlattenedTreeParentElementForStyle();
   5311  while (element) {
   5312    MOZ_ASSERT(!element->HasAnyOfFlags(Element::kAllServoDescendantBits));
   5313    element = element->GetFlattenedTreeParentElementForStyle();
   5314  }
   5315 #endif
   5316 }
   5317 
   5318 // Sets `aBits` on `aElement` and all of its flattened-tree ancestors up to and
   5319 // including aStopAt or the root element (whichever is encountered first), and
   5320 // as long as `aBitsToStopAt` isn't found anywhere in the chain.
   5321 static inline Element* PropagateBits(Element* aElement, uint32_t aBits,
   5322                                     nsINode* aStopAt, uint32_t aBitsToStopAt) {
   5323  Element* curr = aElement;
   5324  while (curr && !curr->HasAllFlags(aBitsToStopAt)) {
   5325    curr->SetFlags(aBits);
   5326    if (curr == aStopAt) {
   5327      break;
   5328    }
   5329    curr = curr->GetFlattenedTreeParentElementForStyle();
   5330  }
   5331 
   5332  if (aBitsToStopAt != aBits && curr) {
   5333    curr->SetFlags(aBits);
   5334  }
   5335 
   5336  return curr;
   5337 }
   5338 
   5339 // Notes that a given element is "dirty" with respect to the given descendants
   5340 // bit (which may be one of dirty descendants, dirty animation descendants, or
   5341 // need frame construction for descendants).
   5342 //
   5343 // This function operates on the dirty element itself, despite the fact that the
   5344 // bits are generally used to describe descendants. This allows restyle roots
   5345 // to be scoped as tightly as possible. On the first call to NoteDirtyElement
   5346 // since the last restyle, we don't set any descendant bits at all, and just set
   5347 // the element as the restyle root.
   5348 //
   5349 // Because the style traversal handles multiple tasks (styling,
   5350 // animation-ticking, and lazy frame construction), there are potentially three
   5351 // separate kinds of dirtiness to track. Rather than maintaining three separate
   5352 // restyle roots, we use a single root, and always bubble it up to be the
   5353 // nearest common ancestor of all the dirty content in the tree. This means that
   5354 // we need to track the types of dirtiness that the restyle root corresponds to,
   5355 // so SetServoRestyleRoot accepts a bitfield along with an element.
   5356 //
   5357 // The overall algorithm is as follows:
   5358 // * When the first dirty element is noted, we just set as the restyle root.
   5359 // * When additional dirty elements are noted, we propagate the given bit up
   5360 //   the tree, until we either reach the restyle root or the document root.
   5361 // * If we reach the document root, we then propagate the bits associated with
   5362 //   the restyle root up the tree until we cross the path of the new root. Once
   5363 //   we find this common ancestor, we record it as the restyle root, and then
   5364 //   clear the bits between the new restyle root and the document root.
   5365 // * If we have dirty content beneath multiple "document style traversal roots"
   5366 //   (which are the main DOM + each piece of document-level native-anoymous
   5367 //   content), we set the restyle root to the nsINode of the document itself.
   5368 //   This is the bail-out case where we traverse everything.
   5369 //
   5370 // Note that, since we track a root, we try to optimize the case where an
   5371 // element under the current root is dirtied, that's why we don't trivially use
   5372 // `nsContentUtils::GetCommonFlattenedTreeAncestorForStyle`.
   5373 static void NoteDirtyElement(Element* aElement, uint32_t aBits) {
   5374  MOZ_ASSERT(aElement->IsInComposedDoc());
   5375 
   5376  // Check the existing root early on, since it may allow us to short-circuit
   5377  // before examining the parent chain.
   5378  Document* doc = aElement->GetComposedDoc();
   5379  nsINode* existingRoot = doc->GetServoRestyleRoot();
   5380  if (existingRoot == aElement) {
   5381    doc->SetServoRestyleRootDirtyBits(doc->GetServoRestyleRootDirtyBits() |
   5382                                      aBits);
   5383    return;
   5384  }
   5385 
   5386  nsINode* parent = aElement->GetFlattenedTreeParentNodeForStyle();
   5387  if (!parent) {
   5388    // The element is not in the flattened tree, bail.
   5389    return;
   5390  }
   5391 
   5392  if (MOZ_LIKELY(parent->IsElement())) {
   5393    // If our parent is unstyled, we can inductively assume that it will be
   5394    // traversed when the time is right, and that the traversal will reach us
   5395    // when it happens. Nothing left to do.
   5396    if (!parent->AsElement()->HasServoData()) {
   5397      return;
   5398    }
   5399 
   5400    // Similarly, if our parent already has the bit we're propagating, we can
   5401    // assume everything is already set up.
   5402    if (parent->HasAllFlags(aBits)) {
   5403      return;
   5404    }
   5405 
   5406    // If the parent is styled but is display:none, we're done.
   5407    //
   5408    // We can't check for a frame here, since <frame> elements inside <frameset>
   5409    // still need to generate a frame, even if they're display: none. :(
   5410    //
   5411    // The servo traversal doesn't keep style data under display: none subtrees,
   5412    // so in order for it to not need to cleanup each time anything happens in a
   5413    // display: none subtree, we keep it clean.
   5414    //
   5415    // Also, we can't be much more smarter about using the parent's frame in
   5416    // order to avoid work here, because since the style system keeps style data
   5417    // in, e.g., subtrees under a leaf frame, missing restyles and such in there
   5418    // has observable behavior via getComputedStyle, for example.
   5419    if (Servo_Element_IsDisplayNone(parent->AsElement())) {
   5420      return;
   5421    }
   5422  }
   5423 
   5424  if (PresShell* presShell = doc->GetPresShell()) {
   5425    presShell->EnsureStyleFlush();
   5426  }
   5427 
   5428  MOZ_ASSERT(parent->IsElement() || parent == doc);
   5429 
   5430  // The bit checks below rely on this to arrive to useful conclusions about the
   5431  // shape of the tree.
   5432  AssertNoBitsPropagatedFrom(existingRoot);
   5433 
   5434  // If there's no existing restyle root, or if the root is already aElement,
   5435  // just note the root+bits and return.
   5436  if (!existingRoot) {
   5437    doc->SetServoRestyleRoot(aElement, aBits);
   5438    return;
   5439  }
   5440 
   5441  // There is an existing restyle root - walk up the tree from our element,
   5442  // propagating bits as we go.
   5443  const bool reachedDocRoot =
   5444      !parent->IsElement() ||
   5445      !PropagateBits(parent->AsElement(), aBits, existingRoot, aBits);
   5446 
   5447  uint32_t existingBits = doc->GetServoRestyleRootDirtyBits();
   5448  if (!reachedDocRoot || existingRoot == doc) {
   5449    // We're a descendant of the existing root. All that's left to do is to
   5450    // make sure the bit we propagated is also registered on the root.
   5451    doc->SetServoRestyleRoot(existingRoot, existingBits | aBits);
   5452  } else {
   5453    // We reached the root without crossing the pre-existing restyle root. We
   5454    // now need to find the nearest common ancestor, so climb up from the
   5455    // existing root, extending bits along the way.
   5456    Element* rootParent = existingRoot->GetFlattenedTreeParentElementForStyle();
   5457    // We can stop at the first occurrence of `aBits` in order to find the
   5458    // common ancestor.
   5459    if (Element* commonAncestor =
   5460            PropagateBits(rootParent, existingBits, aElement, aBits)) {
   5461      MOZ_ASSERT(commonAncestor == aElement ||
   5462                 commonAncestor ==
   5463                     nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
   5464                         aElement, rootParent));
   5465 
   5466      // We found a common ancestor. Make that the new style root, and clear the
   5467      // bits between the new style root and the document root.
   5468      doc->SetServoRestyleRoot(commonAncestor, existingBits | aBits);
   5469      Element* curr = commonAncestor;
   5470      while ((curr = curr->GetFlattenedTreeParentElementForStyle())) {
   5471        MOZ_ASSERT(curr->HasAllFlags(aBits));
   5472        curr->UnsetFlags(aBits);
   5473      }
   5474      AssertNoBitsPropagatedFrom(commonAncestor);
   5475    } else {
   5476      // We didn't find a common ancestor element. That means we're descended
   5477      // from two different document style roots, so the common ancestor is the
   5478      // document.
   5479      doc->SetServoRestyleRoot(doc, existingBits | aBits);
   5480    }
   5481  }
   5482 
   5483  // See the comment in Document::SetServoRestyleRoot about the !IsElement()
   5484  // check there. Same justification here.
   5485  MOZ_ASSERT(aElement == doc->GetServoRestyleRoot() ||
   5486             !doc->GetServoRestyleRoot()->IsElement() ||
   5487             nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
   5488                 aElement, doc->GetServoRestyleRoot()));
   5489  MOZ_ASSERT(aElement == doc->GetServoRestyleRoot() ||
   5490             !doc->GetServoRestyleRoot()->IsElement() || !parent->IsElement() ||
   5491             BitsArePropagated(parent->AsElement(), aBits,
   5492                               doc->GetServoRestyleRoot()));
   5493  MOZ_ASSERT(doc->GetServoRestyleRootDirtyBits() & aBits);
   5494 }
   5495 
   5496 void Element::NoteDirtySubtreeForServo() {
   5497  MOZ_ASSERT(IsInComposedDoc());
   5498  MOZ_ASSERT(HasServoData());
   5499 
   5500  Document* doc = GetComposedDoc();
   5501  nsINode* existingRoot = doc->GetServoRestyleRoot();
   5502  uint32_t existingBits =
   5503      existingRoot ? doc->GetServoRestyleRootDirtyBits() : 0;
   5504 
   5505  if (existingRoot && existingRoot->IsElement() && existingRoot != this &&
   5506      nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
   5507          existingRoot->AsElement(), this)) {
   5508    PropagateBits(
   5509        existingRoot->AsElement()->GetFlattenedTreeParentElementForStyle(),
   5510        existingBits, this, existingBits);
   5511 
   5512    doc->ClearServoRestyleRoot();
   5513  }
   5514 
   5515  NoteDirtyElement(this,
   5516                   existingBits | ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
   5517 }
   5518 
   5519 void Element::NoteDirtyForServo() {
   5520  NoteDirtyElement(this, ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
   5521 }
   5522 
   5523 void Element::NoteAnimationOnlyDirtyForServo() {
   5524  NoteDirtyElement(this,
   5525                   ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO);
   5526 }
   5527 
   5528 void Element::NoteDescendantsNeedFramesForServo() {
   5529  // Since lazy frame construction can be required for non-element nodes, this
   5530  // Note() method operates on the parent of the frame-requiring content, unlike
   5531  // the other Note() methods above (which operate directly on the element that
   5532  // needs processing).
   5533  NoteDirtyElement(this, NODE_DESCENDANTS_NEED_FRAMES);
   5534  SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
   5535 }
   5536 
   5537 double Element::FirstLineBoxBSize() const {
   5538  const nsBlockFrame* frame = do_QueryFrame(GetPrimaryFrame());
   5539  if (!frame) {
   5540    return 0.0;
   5541  }
   5542  nsBlockFrame::ConstLineIterator line = frame->LinesBegin();
   5543  nsBlockFrame::ConstLineIterator lineEnd = frame->LinesEnd();
   5544  return line != lineEnd
   5545             ? nsPresContext::AppUnitsToDoubleCSSPixels(line->BSize())
   5546             : 0.0;
   5547 }
   5548 
   5549 // static
   5550 nsAtom* Element::GetEventNameForAttr(nsAtom* aAttr) {
   5551  if (aAttr == nsGkAtoms::onwebkitanimationend) {
   5552    return nsGkAtoms::onwebkitAnimationEnd;
   5553  }
   5554  if (aAttr == nsGkAtoms::onwebkitanimationiteration) {
   5555    return nsGkAtoms::onwebkitAnimationIteration;
   5556  }
   5557  if (aAttr == nsGkAtoms::onwebkitanimationstart) {
   5558    return nsGkAtoms::onwebkitAnimationStart;
   5559  }
   5560  if (aAttr == nsGkAtoms::onwebkittransitionend) {
   5561    return nsGkAtoms::onwebkitTransitionEnd;
   5562  }
   5563  return aAttr;
   5564 }
   5565 
   5566 void Element::RegUnRegAccessKey(bool aDoReg) {
   5567  // first check to see if we have an access key
   5568  nsAutoString accessKey;
   5569  GetAttr(nsGkAtoms::accesskey, accessKey);
   5570  if (accessKey.IsEmpty()) {
   5571    return;
   5572  }
   5573 
   5574  // We have an access key, so get the ESM from the pres context.
   5575  if (nsPresContext* presContext = GetPresContext(eForComposedDoc)) {
   5576    EventStateManager* esm = presContext->EventStateManager();
   5577 
   5578    // Register or unregister as appropriate.
   5579    if (aDoReg) {
   5580      esm->RegisterAccessKey(this, (uint32_t)accessKey.First());
   5581    } else {
   5582      esm->UnregisterAccessKey(this, (uint32_t)accessKey.First());
   5583    }
   5584  }
   5585 }
   5586 
   5587 void Element::SetHTML(const nsAString& aHTML, const SetHTMLOptions& aOptions,
   5588                      ErrorResult& aError) {
   5589  nsContentUtils::SetHTML(this, this, aHTML, aOptions, aError);
   5590 }
   5591 
   5592 void Element::GetHTML(const GetHTMLOptions& aOptions, nsAString& aResult) {
   5593  if (aOptions.mSerializableShadowRoots || !aOptions.mShadowRoots.IsEmpty()) {
   5594    nsContentUtils::SerializeNodeToMarkup<SerializeShadowRoots::Yes>(
   5595        this, true, aResult, aOptions.mSerializableShadowRoots,
   5596        aOptions.mShadowRoots);
   5597  } else {
   5598    nsContentUtils::SerializeNodeToMarkup<SerializeShadowRoots::No>(
   5599        this, true, aResult, aOptions.mSerializableShadowRoots,
   5600        aOptions.mShadowRoots);
   5601  }
   5602 }
   5603 
   5604 StylePropertyMapReadOnly* Element::ComputedStyleMap() {
   5605  nsDOMSlots* slots = DOMSlots();
   5606 
   5607  if (!slots->mComputedStyleMap) {
   5608    slots->mComputedStyleMap =
   5609        MakeRefPtr<StylePropertyMapReadOnly>(this, /* aComputed */ true);
   5610  }
   5611 
   5612  return slots->mComputedStyleMap;
   5613 }
   5614 
   5615 bool Element::Translate() const {
   5616  if (const auto* parent = Element::FromNodeOrNull(mParent)) {
   5617    return parent->Translate();
   5618  }
   5619  return true;
   5620 }
   5621 
   5622 EditorBase* Element::GetExtantEditor() const {
   5623  if (!IsInComposedDoc()) {
   5624    return nullptr;
   5625  }
   5626  const bool isInDesignMode = IsInDesignMode();
   5627  // Even if a text control element is an editing host, TextEditor handles
   5628  // user input.  Therefore, we should return TextEditor (or nullptr) in this
   5629  // case.  Note that text control element in the design mode does not work as
   5630  // a text control.  Therefore, in that case, we should return HTMLEditor.
   5631  if (!isInDesignMode) {
   5632    if (const auto* textControlElement = TextControlElement::FromNode(this)) {
   5633      if (textControlElement->IsSingleLineTextControlOrTextArea()) {
   5634        return textControlElement->GetExtantTextEditor();
   5635      }
   5636    }
   5637  }
   5638 
   5639  if (!isInDesignMode && !IsEditable()) {
   5640    return nullptr;
   5641  }
   5642  // FYI: This never creates HTMLEditor immediately.
   5643  nsDocShell* const docShell = nsDocShell::Cast(OwnerDoc()->GetDocShell());
   5644  return docShell ? docShell->GetHTMLEditorInternal() : nullptr;
   5645 }
   5646 
   5647 void Element::SetHTMLUnsafe(const TrustedHTMLOrString& aHTML,
   5648                            const SetHTMLUnsafeOptions& aOptions,
   5649                            nsIPrincipal* aSubjectPrincipal,
   5650                            ErrorResult& aError) {
   5651  nsContentUtils::SetHTMLUnsafe(this, this, aHTML, aOptions,
   5652                                false /*aIsShadowRoot*/, aSubjectPrincipal,
   5653                                aError);
   5654 }
   5655 
   5656 // https://html.spec.whatwg.org/#event-beforematch
   5657 void Element::FireBeforematchEvent(ErrorResult& aRv) {
   5658  RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
   5659  event->InitEvent(u"beforematch"_ns,
   5660                   /*aCanBubble=*/true,
   5661                   /*aCancelable=*/false);
   5662 
   5663  event->SetTrusted(true);
   5664  DispatchEvent(*event, aRv);
   5665 }
   5666 
   5667 bool Element::BlockingContainsRender() const {
   5668  const nsAttrValue* attrValue = GetParsedAttr(nsGkAtoms::blocking);
   5669  if (!attrValue || !StaticPrefs::dom_element_blocking_enabled()) {
   5670    return false;
   5671  }
   5672  MOZ_ASSERT(attrValue->Type() == nsAttrValue::eAtomArray,
   5673             "Checking blocking attribute on element that doesn't parse it?");
   5674  return attrValue->Contains(nsGkAtoms::render, eIgnoreCase);
   5675 }
   5676 
   5677 static bool IsOffsetParent(nsIFrame* aFrame) {
   5678  LayoutFrameType frameType = aFrame->Type();
   5679 
   5680  if (frameType == LayoutFrameType::TableCell ||
   5681      frameType == LayoutFrameType::TableWrapper) {
   5682    // Per the IDL for Element, only td, th, and table are acceptable
   5683    // offsetParents apart from body or positioned elements; we need to check
   5684    // the content type as well as the frame type so we ignore anonymous tables
   5685    // created by an element with display: table-cell with no actual table
   5686    nsIContent* content = aFrame->GetContent();
   5687 
   5688    return content->IsAnyOfHTMLElements(nsGkAtoms::table, nsGkAtoms::td,
   5689                                        nsGkAtoms::th);
   5690  }
   5691  return false;
   5692 }
   5693 
   5694 struct OffsetResult {
   5695  Element* mParent = nullptr;
   5696  nsRect mRect;
   5697 };
   5698 
   5699 static OffsetResult GetUnretargetedOffsetsFor(const Element& aElement) {
   5700  nsIFrame* frame = aElement.GetPrimaryFrame();
   5701  if (!frame) {
   5702    return {};
   5703  }
   5704 
   5705  nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(frame);
   5706 
   5707  nsIFrame* parent = frame->GetParent();
   5708  nsPoint origin(0, 0);
   5709 
   5710  nsIContent* offsetParent = nullptr;
   5711  Element* docElement = aElement.GetComposedDoc()->GetRootElement();
   5712  nsIContent* content = frame->GetContent();
   5713  const auto effectiveZoom = frame->Style()->EffectiveZoom();
   5714 
   5715  if (content &&
   5716      (content->IsHTMLElement(nsGkAtoms::body) || content == docElement)) {
   5717    parent = frame;
   5718  } else {
   5719    const bool isPositioned = styleFrame->IsAbsPosContainingBlock();
   5720    const bool isAbsolutelyPositioned = frame->IsAbsolutelyPositioned();
   5721    origin += frame->GetPositionIgnoringScrolling();
   5722 
   5723    for (; parent; parent = parent->GetParent()) {
   5724      content = parent->GetContent();
   5725 
   5726      // Stop at the first ancestor that is positioned.
   5727      if (parent->IsAbsPosContainingBlock()) {
   5728        offsetParent = content;
   5729        break;
   5730      }
   5731 
   5732      // WebKit-ism: offsetParent stops at zoom changes.
   5733      // See https://github.com/w3c/csswg-drafts/issues/10252
   5734      if (effectiveZoom != parent->Style()->EffectiveZoom()) {
   5735        offsetParent = content;
   5736        break;
   5737      }
   5738 
   5739      // Add the parent's origin to our own to get to the
   5740      // right coordinate system.
   5741      const bool isOffsetParent = !isPositioned && IsOffsetParent(parent);
   5742      if (!isOffsetParent) {
   5743        origin += parent->GetPositionIgnoringScrolling();
   5744      }
   5745 
   5746      if (content) {
   5747        // If we've hit the document element, break here.
   5748        if (content == docElement) {
   5749          break;
   5750        }
   5751 
   5752        // Break if the ancestor frame type makes it suitable as offset parent
   5753        // and this element is *not* positioned or if we found the body element.
   5754        if (isOffsetParent || content->IsHTMLElement(nsGkAtoms::body)) {
   5755          offsetParent = content;
   5756          break;
   5757        }
   5758      }
   5759    }
   5760 
   5761    if (isAbsolutelyPositioned && !offsetParent &&
   5762        !frame->GetParent()->IsViewportFrame()) {
   5763      // If this element is absolutely positioned, but we don't have
   5764      // an offset parent it means this element is an absolutely
   5765      // positioned child that's not nested inside another positioned
   5766      // element, in this case the element's frame's parent is the
   5767      // frame for the HTML element so we fail to find the body in the
   5768      // parent chain. We want the offset parent in this case to be
   5769      // the body, so we just get the body element from the document.
   5770      //
   5771      // We use GetBodyElement() here, not GetBody(), because we don't want to
   5772      // end up with framesets here.
   5773      offsetParent = aElement.GetComposedDoc()->GetBodyElement();
   5774    }
   5775  }
   5776 
   5777  // Make the position relative to the padding edge.
   5778  if (parent) {
   5779    const nsStyleBorder* border = parent->StyleBorder();
   5780    origin.x -= border->GetComputedBorderWidth(eSideLeft);
   5781    origin.y -= border->GetComputedBorderWidth(eSideTop);
   5782  }
   5783 
   5784  // Get the union of all rectangles in this and continuation frames.
   5785  // It doesn't really matter what we use as aRelativeTo here, since
   5786  // we only care about the size. We just have to use something non-null.
   5787  nsRect rcFrame = nsLayoutUtils::GetAllInFlowRectsUnion(frame, frame);
   5788  rcFrame.MoveTo(origin);
   5789  return {Element::FromNodeOrNull(offsetParent), rcFrame};
   5790 }
   5791 
   5792 static bool ShouldBeRetargeted(const Element& aReferenceElement,
   5793                               const Element& aElementToMaybeRetarget) {
   5794  ShadowRoot* shadow = aElementToMaybeRetarget.GetContainingShadow();
   5795  if (!shadow) {
   5796    return false;
   5797  }
   5798  for (ShadowRoot* scope = aReferenceElement.GetContainingShadow(); scope;
   5799       scope = scope->Host()->GetContainingShadow()) {
   5800    if (scope == shadow) {
   5801      return false;
   5802    }
   5803  }
   5804 
   5805  return true;
   5806 }
   5807 
   5808 Element* Element::GetOffsetRect(CSSIntRect& aRect) {
   5809  aRect = CSSIntRect();
   5810 
   5811  nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
   5812  if (!frame) {
   5813    return nullptr;
   5814  }
   5815 
   5816  OffsetResult thisResult = GetUnretargetedOffsetsFor(*this);
   5817  nsRect rect = thisResult.mRect;
   5818  Element* parent = thisResult.mParent;
   5819  while (parent && ShouldBeRetargeted(*this, *parent)) {
   5820    OffsetResult result = GetUnretargetedOffsetsFor(*parent);
   5821    rect += result.mRect.TopLeft();
   5822    parent = result.mParent;
   5823  }
   5824 
   5825  aRect = CSSIntRect::FromAppUnitsRounded(
   5826      frame->Style()->EffectiveZoom().Unzoom(rect));
   5827  return parent;
   5828 }
   5829 
   5830 }  // namespace mozilla::dom