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 #include "mozilla/dom/ElementInlines.h"
15 
16 #include <inttypes.h>
17 #include <initializer_list>
18 #include <new>
19 #include "DOMIntersectionObserver.h"
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/ComputedStyle.h"
30 #include "mozilla/ContentEvents.h"
31 #include "mozilla/DebugOnly.h"
32 #include "mozilla/DeclarationBlock.h"
33 #include "mozilla/EffectCompositor.h"
34 #include "mozilla/EffectSet.h"
35 #include "mozilla/ErrorResult.h"
36 #include "mozilla/EventDispatcher.h"
37 #include "mozilla/EventListenerManager.h"
38 #include "mozilla/EventStateManager.h"
39 #include "mozilla/EventStates.h"
40 #include "mozilla/FloatingPoint.h"
41 #include "mozilla/FullscreenChange.h"
42 #include "mozilla/InternalMutationEvent.h"
43 #include "mozilla/Likely.h"
44 #include "mozilla/LinkedList.h"
45 #include "mozilla/LookAndFeel.h"
46 #include "mozilla/MouseEvents.h"
47 #include "mozilla/NotNull.h"
48 #include "mozilla/PointerLockManager.h"
49 #include "mozilla/PresShell.h"
50 #include "mozilla/PresShellForwards.h"
51 #include "mozilla/ReflowOutput.h"
52 #include "mozilla/RelativeTo.h"
53 #include "mozilla/ScrollOrigin.h"
54 #include "mozilla/ScrollTypes.h"
55 #include "mozilla/ServoStyleConsts.h"
56 #include "mozilla/ServoStyleConstsInlines.h"
57 #include "mozilla/SizeOfState.h"
58 #include "mozilla/StaticAnalysisFunctions.h"
59 #include "mozilla/StaticPrefs_dom.h"
60 #include "mozilla/StaticPrefs_full_screen_api.h"
61 #include "mozilla/TextControlElement.h"
62 #include "mozilla/TextEvents.h"
63 #include "mozilla/TypedEnumBits.h"
64 #include "mozilla/Unused.h"
65 #include "mozilla/dom/AnimatableBinding.h"
66 #include "mozilla/dom/Animation.h"
67 #include "mozilla/dom/Attr.h"
68 #include "mozilla/dom/BindContext.h"
69 #include "mozilla/dom/BindingDeclarations.h"
70 #include "mozilla/dom/CustomElementRegistry.h"
71 #include "mozilla/dom/DOMRect.h"
72 #include "mozilla/dom/DirectionalityUtils.h"
73 #include "mozilla/dom/Document.h"
74 #include "mozilla/dom/DocumentFragment.h"
75 #include "mozilla/dom/DocumentInlines.h"
76 #include "mozilla/dom/DocumentTimeline.h"
77 #include "mozilla/dom/ElementBinding.h"
78 #include "mozilla/dom/ElementInlines.h"
79 #include "mozilla/dom/Flex.h"
80 #include "mozilla/dom/FromParser.h"
81 #include "mozilla/dom/Grid.h"
82 #include "mozilla/dom/HTMLDivElement.h"
83 #include "mozilla/dom/HTMLParagraphElement.h"
84 #include "mozilla/dom/HTMLPreElement.h"
85 #include "mozilla/dom/HTMLSpanElement.h"
86 #include "mozilla/dom/HTMLTableCellElement.h"
87 #include "mozilla/dom/KeyframeAnimationOptionsBinding.h"
88 #include "mozilla/dom/KeyframeEffect.h"
89 #include "mozilla/dom/MouseEventBinding.h"
90 #include "mozilla/dom/MutationEventBinding.h"
91 #include "mozilla/dom/MutationObservers.h"
92 #include "mozilla/dom/NodeInfo.h"
93 #include "mozilla/dom/PointerEventHandler.h"
94 #include "mozilla/dom/Promise.h"
95 #include "mozilla/dom/SVGElement.h"
96 #include "mozilla/dom/ScriptLoader.h"
97 #include "mozilla/dom/ShadowRoot.h"
98 #include "mozilla/dom/Text.h"
99 #include "mozilla/dom/WindowBinding.h"
100 #include "mozilla/dom/nsCSPContext.h"
101 #include "mozilla/gfx/BasePoint.h"
102 #include "mozilla/gfx/BaseRect.h"
103 #include "mozilla/gfx/BaseSize.h"
104 #include "mozilla/gfx/Matrix.h"
105 #include "nsAtom.h"
106 #include "nsAttrName.h"
107 #include "nsAttrValueInlines.h"
108 #include "nsAttrValueOrString.h"
109 #include "nsBaseHashtable.h"
110 #include "nsBlockFrame.h"
111 #include "nsCOMPtr.h"
112 #include "nsContentUtils.h"
113 #include "nsCSSPseudoElements.h"
114 #include "nsCompatibility.h"
115 #include "nsContainerFrame.h"
116 #include "nsContentList.h"
117 #include "nsContentListDeclarations.h"
118 #include "nsCoord.h"
119 #include "nsDOMAttributeMap.h"
120 #include "nsDOMCSSAttrDeclaration.h"
121 #include "nsDOMMutationObserver.h"
122 #include "nsDOMString.h"
123 #include "nsDOMStringMap.h"
124 #include "nsDOMTokenList.h"
125 #include "nsDocShell.h"
126 #include "nsError.h"
127 #include "nsFlexContainerFrame.h"
128 #include "nsFocusManager.h"
129 #include "nsFrameState.h"
130 #include "nsGenericHTMLElement.h"
131 #include "nsGkAtoms.h"
132 #include "nsGridContainerFrame.h"
133 #include "nsIAutoCompletePopup.h"
134 #include "nsIBrowser.h"
135 #include "nsIContentInlines.h"
136 #include "nsIDOMXULButtonElement.h"
137 #include "nsIDOMXULContainerElement.h"
138 #include "nsIDOMXULControlElement.h"
139 #include "nsIDOMXULMenuListElement.h"
140 #include "nsIDOMXULMultSelectCntrlEl.h"
141 #include "nsIDOMXULRadioGroupElement.h"
142 #include "nsIDOMXULRelatedElement.h"
143 #include "nsIDOMXULSelectCntrlEl.h"
144 #include "nsIDOMXULSelectCntrlItemEl.h"
145 #include "nsIDocShell.h"
146 #include "nsIFocusManager.h"
147 #include "nsIFrame.h"
148 #include "nsIGlobalObject.h"
149 #include "nsIIOService.h"
150 #include "nsIInterfaceRequestor.h"
151 #include "nsIMemoryReporter.h"
152 #include "nsIPrincipal.h"
153 #include "nsIScriptError.h"
154 #include "nsIScrollableFrame.h"
155 #include "nsISpeculativeConnect.h"
156 #include "nsISupports.h"
157 #include "nsISupportsUtils.h"
158 #include "nsIURI.h"
159 #include "nsLayoutUtils.h"
160 #include "nsLineBox.h"
161 #include "nsNameSpaceManager.h"
162 #include "nsNodeInfoManager.h"
163 #include "nsPIDOMWindow.h"
164 #include "nsPoint.h"
165 #include "nsPresContext.h"
166 #include "nsQueryFrame.h"
167 #include "nsRefPtrHashtable.h"
168 #include "nsSize.h"
169 #include "nsString.h"
170 #include "nsStyleConsts.h"
171 #include "nsStyleStruct.h"
172 #include "nsStyledElement.h"
173 #include "nsTArray.h"
174 #include "nsTextNode.h"
175 #include "nsThreadUtils.h"
176 #include "nsViewManager.h"
177 #include "nsWindowSizes.h"
178 
179 #ifdef MOZ_XUL
180 #  include "nsXULElement.h"
181 #endif /* MOZ_XUL */
182 
183 #ifdef DEBUG
184 #  include "nsRange.h"
185 #endif
186 
187 #ifdef ACCESSIBILITY
188 #  include "nsAccessibilityService.h"
189 #endif
190 
191 using mozilla::gfx::Matrix4x4;
192 
193 namespace mozilla::dom {
194 
195 // Verify sizes of nodes. We use a template rather than a direct static
196 // assert so that the error message actually displays the sizes.
197 // On 32 bit systems the actual allocated size varies a bit between
198 // OSes/compilers.
199 //
200 // We need different numbers on certain build types to deal with the owning
201 // thread pointer that comes with the non-threadsafe refcount on
202 // nsIContent.
203 #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED
204 #  define EXTRA_DOM_NODE_BYTES 8
205 #else
206 #  define EXTRA_DOM_NODE_BYTES 0
207 #endif
208 
209 #define ASSERT_NODE_SIZE(type, opt_size_64, opt_size_32)              \
210   template <int a, int sizeOn64, int sizeOn32>                        \
211   struct Check##type##Size {                                          \
212     static_assert((sizeof(void*) == 8 && a == sizeOn64) ||            \
213                       (sizeof(void*) == 4 && a <= sizeOn32),          \
214                   "DOM size changed");                                \
215   };                                                                  \
216   Check##type##Size<sizeof(type), opt_size_64 + EXTRA_DOM_NODE_BYTES, \
217                     opt_size_32 + EXTRA_DOM_NODE_BYTES>               \
218       g##type##CES;
219 
220 // Note that mozjemalloc uses a 16 byte quantum, so 64, 80 and 128 are
221 // bucket sizes.
222 ASSERT_NODE_SIZE(Element, 128, 80);
223 ASSERT_NODE_SIZE(HTMLDivElement, 128, 80);
224 ASSERT_NODE_SIZE(HTMLParagraphElement, 128, 80);
225 ASSERT_NODE_SIZE(HTMLPreElement, 128, 80);
226 ASSERT_NODE_SIZE(HTMLSpanElement, 128, 80);
227 ASSERT_NODE_SIZE(HTMLTableCellElement, 128, 80);
228 ASSERT_NODE_SIZE(Text, 120, 64);
229 
230 #undef ASSERT_NODE_SIZE
231 #undef EXTRA_DOM_NODE_BYTES
232 
233 }  // namespace mozilla::dom
234 
DoGetID() const235 nsAtom* nsIContent::DoGetID() const {
236   MOZ_ASSERT(HasID(), "Unexpected call");
237   MOZ_ASSERT(IsElement(), "Only elements can have IDs");
238 
239   return AsElement()->GetParsedAttr(nsGkAtoms::id)->GetAtomValue();
240 }
241 
GetPrimaryFrame(mozilla::FlushType aType)242 nsIFrame* nsIContent::GetPrimaryFrame(mozilla::FlushType aType) {
243   Document* doc = GetComposedDoc();
244   if (!doc) {
245     return nullptr;
246   }
247 
248   // Cause a flush, so we get up-to-date frame information.
249   if (aType != mozilla::FlushType::None) {
250     doc->FlushPendingNotifications(aType);
251   }
252 
253   return GetPrimaryFrame();
254 }
255 
256 namespace mozilla::dom {
257 
Attributes()258 nsDOMAttributeMap* Element::Attributes() {
259   nsDOMSlots* slots = DOMSlots();
260   if (!slots->mAttributeMap) {
261     slots->mAttributeMap = new nsDOMAttributeMap(this);
262   }
263 
264   return slots->mAttributeMap;
265 }
266 
SetPointerCapture(int32_t aPointerId,ErrorResult & aError)267 void Element::SetPointerCapture(int32_t aPointerId, ErrorResult& aError) {
268   if (nsContentUtils::ShouldResistFingerprinting(GetComposedDoc()) &&
269       aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) {
270     aError.ThrowNotFoundError("Invalid pointer id");
271     return;
272   }
273   const PointerInfo* pointerInfo =
274       PointerEventHandler::GetPointerInfo(aPointerId);
275   if (!pointerInfo) {
276     aError.ThrowNotFoundError("Invalid pointer id");
277     return;
278   }
279   if (!IsInComposedDoc()) {
280     aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
281     return;
282   }
283   if (OwnerDoc()->GetPointerLockElement()) {
284     // Throw an exception 'InvalidStateError' while the page has a locked
285     // element.
286     aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
287     return;
288   }
289   if (!pointerInfo->mActiveState ||
290       pointerInfo->mActiveDocument != OwnerDoc()) {
291     return;
292   }
293   PointerEventHandler::RequestPointerCaptureById(aPointerId, this);
294 }
295 
ReleasePointerCapture(int32_t aPointerId,ErrorResult & aError)296 void Element::ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) {
297   if (nsContentUtils::ShouldResistFingerprinting(GetComposedDoc()) &&
298       aPointerId != PointerEventHandler::GetSpoofedPointerIdForRFP()) {
299     aError.ThrowNotFoundError("Invalid pointer id");
300     return;
301   }
302   if (!PointerEventHandler::GetPointerInfo(aPointerId)) {
303     aError.ThrowNotFoundError("Invalid pointer id");
304     return;
305   }
306   if (HasPointerCapture(aPointerId)) {
307     PointerEventHandler::ReleasePointerCaptureById(aPointerId);
308   }
309 }
310 
HasPointerCapture(long aPointerId)311 bool Element::HasPointerCapture(long aPointerId) {
312   PointerCaptureInfo* pointerCaptureInfo =
313       PointerEventHandler::GetPointerCaptureInfo(aPointerId);
314   if (pointerCaptureInfo && pointerCaptureInfo->mPendingElement == this) {
315     return true;
316   }
317   return false;
318 }
319 
GetSVGAnimatedClass() const320 const nsAttrValue* Element::GetSVGAnimatedClass() const {
321   MOZ_ASSERT(MayHaveClass() && IsSVGElement(), "Unexpected call");
322   return static_cast<const SVGElement*>(this)->GetAnimatedClassName();
323 }
324 
325 NS_IMETHODIMP
QueryInterface(REFNSIID aIID,void ** aInstancePtr)326 Element::QueryInterface(REFNSIID aIID, void** aInstancePtr) {
327   if (aIID.Equals(NS_GET_IID(Element))) {
328     NS_ADDREF_THIS();
329     *aInstancePtr = this;
330     return NS_OK;
331   }
332 
333   NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!");
334   nsresult rv = FragmentOrElement::QueryInterface(aIID, aInstancePtr);
335   if (NS_SUCCEEDED(rv)) {
336     return NS_OK;
337   }
338 
339   return NS_NOINTERFACE;
340 }
341 
IntrinsicState() const342 EventStates Element::IntrinsicState() const {
343   return IsEditable() ? NS_EVENT_STATE_READWRITE : NS_EVENT_STATE_READONLY;
344 }
345 
NotifyStateChange(EventStates aStates)346 void Element::NotifyStateChange(EventStates aStates) {
347   if (aStates.IsEmpty()) {
348     return;
349   }
350 
351   if (Document* doc = GetComposedDoc()) {
352     nsAutoScriptBlocker scriptBlocker;
353     doc->ContentStateChanged(this, aStates);
354   }
355 }
356 
UpdateLinkState(EventStates aState)357 void Element::UpdateLinkState(EventStates aState) {
358   MOZ_ASSERT(!aState.HasAtLeastOneOfStates(
359                  ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)),
360              "Unexpected link state bits");
361   mState =
362       (mState & ~(NS_EVENT_STATE_VISITED | NS_EVENT_STATE_UNVISITED)) | aState;
363 }
364 
UpdateState(bool aNotify)365 void Element::UpdateState(bool aNotify) {
366   EventStates oldState = mState;
367   mState = IntrinsicState() | (oldState & EXTERNALLY_MANAGED_STATES);
368   if (aNotify) {
369     EventStates changedStates = oldState ^ mState;
370     if (!changedStates.IsEmpty()) {
371       Document* doc = GetComposedDoc();
372       if (doc) {
373         nsAutoScriptBlocker scriptBlocker;
374         doc->ContentStateChanged(this, changedStates);
375       }
376     }
377   }
378 }
379 
380 }  // namespace mozilla::dom
381 
UpdateEditableState(bool aNotify)382 void nsIContent::UpdateEditableState(bool aNotify) {
383   if (IsInNativeAnonymousSubtree()) {
384     // Don't propagate the editable flag into native anonymous subtrees.
385     if (IsRootOfNativeAnonymousSubtree()) {
386       return;
387     }
388 
389     // We allow setting the flag on NAC (explicitly, see
390     // nsTextControlFrame::CreateAnonymousContent for example), but not
391     // unsetting it.
392     //
393     // Otherwise, just the act of binding the NAC subtree into our non-anonymous
394     // parent would clear the flag, which is not good. As we shouldn't move NAC
395     // around, this is fine.
396     if (HasFlag(NODE_IS_EDITABLE)) {
397       return;
398     }
399   }
400 
401   nsIContent* parent = GetParent();
402   SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
403 }
404 
405 namespace mozilla::dom {
406 
UpdateEditableState(bool aNotify)407 void Element::UpdateEditableState(bool aNotify) {
408   nsIContent::UpdateEditableState(aNotify);
409   if (aNotify) {
410     UpdateState(aNotify);
411   } else {
412     // Avoid calling UpdateState in this very common case, because
413     // this gets called for pretty much every single element on
414     // insertion into the document and UpdateState can be slow for
415     // some kinds of elements even when not notifying.
416     if (IsEditable()) {
417       RemoveStatesSilently(NS_EVENT_STATE_READONLY);
418       AddStatesSilently(NS_EVENT_STATE_READWRITE);
419     } else {
420       RemoveStatesSilently(NS_EVENT_STATE_READWRITE);
421       AddStatesSilently(NS_EVENT_STATE_READONLY);
422     }
423   }
424 }
425 
GetTabIndexAttrValue()426 Maybe<int32_t> Element::GetTabIndexAttrValue() {
427   const nsAttrValue* attrVal = GetParsedAttr(nsGkAtoms::tabindex);
428   if (attrVal && attrVal->Type() == nsAttrValue::eInteger) {
429     return Some(attrVal->GetIntegerValue());
430   }
431 
432   return Nothing();
433 }
434 
TabIndex()435 int32_t Element::TabIndex() {
436   Maybe<int32_t> attrVal = GetTabIndexAttrValue();
437   if (attrVal.isSome()) {
438     return attrVal.value();
439   }
440 
441   return TabIndexDefault();
442 }
443 
Focus(const FocusOptions & aOptions,CallerType aCallerType,ErrorResult & aError)444 void Element::Focus(const FocusOptions& aOptions, CallerType aCallerType,
445                     ErrorResult& aError) {
446   RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
447   if (!fm) {
448     return;
449   }
450   // Also other browsers seem to have the hack to not re-focus (and flush) when
451   // the element is already focused.
452   // Until https://github.com/whatwg/html/issues/4512 is clarified, we'll
453   // maintain interoperatibility by not re-focusing, independent of aOptions.
454   // I.e., `focus({ preventScroll: true})` followed by `focus( { preventScroll:
455   // false })` won't re-focus.
456   if (fm->CanSkipFocus(this)) {
457     fm->NotifyOfReFocus(*this);
458     fm->NeedsFlushBeforeEventHandling(this);
459     return;
460   }
461   uint32_t fmFlags = nsFocusManager::ProgrammaticFocusFlags(aOptions);
462   if (aCallerType == CallerType::NonSystem) {
463     fmFlags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
464   }
465   aError = fm->SetFocus(this, fmFlags);
466 }
467 
SetTabIndex(int32_t aTabIndex,mozilla::ErrorResult & aError)468 void Element::SetTabIndex(int32_t aTabIndex, mozilla::ErrorResult& aError) {
469   nsAutoString value;
470   value.AppendInt(aTabIndex);
471 
472   SetAttr(nsGkAtoms::tabindex, value, aError);
473 }
474 
SetShadowRoot(ShadowRoot * aShadowRoot)475 void Element::SetShadowRoot(ShadowRoot* aShadowRoot) {
476   nsExtendedDOMSlots* slots = ExtendedDOMSlots();
477   MOZ_ASSERT(!aShadowRoot || !slots->mShadowRoot,
478              "We shouldn't clear the shadow root without unbind first");
479   slots->mShadowRoot = aShadowRoot;
480 }
481 
Blur(mozilla::ErrorResult & aError)482 void Element::Blur(mozilla::ErrorResult& aError) {
483   if (!ShouldBlur(this)) {
484     return;
485   }
486 
487   Document* doc = GetComposedDoc();
488   if (!doc) {
489     return;
490   }
491 
492   nsPIDOMWindowOuter* win = doc->GetWindow();
493   nsFocusManager* fm = nsFocusManager::GetFocusManager();
494   if (win && fm) {
495     aError = fm->ClearFocus(win);
496   }
497 }
498 
StyleStateFromLocks() const499 EventStates Element::StyleStateFromLocks() const {
500   StyleStateLocks locksAndValues = LockedStyleStates();
501   EventStates locks = locksAndValues.mLocks;
502   EventStates values = locksAndValues.mValues;
503   EventStates state = (mState & ~locks) | (locks & values);
504 
505   if (state.HasState(NS_EVENT_STATE_VISITED)) {
506     return state & ~NS_EVENT_STATE_UNVISITED;
507   }
508   if (state.HasState(NS_EVENT_STATE_UNVISITED)) {
509     return state & ~NS_EVENT_STATE_VISITED;
510   }
511 
512   return state;
513 }
514 
LockedStyleStates() const515 Element::StyleStateLocks Element::LockedStyleStates() const {
516   StyleStateLocks* locks =
517       static_cast<StyleStateLocks*>(GetProperty(nsGkAtoms::lockedStyleStates));
518   if (locks) {
519     return *locks;
520   }
521   return StyleStateLocks();
522 }
523 
NotifyStyleStateChange(EventStates aStates)524 void Element::NotifyStyleStateChange(EventStates aStates) {
525   Document* doc = GetComposedDoc();
526   if (doc) {
527     RefPtr<PresShell> presShell = doc->GetPresShell();
528     if (presShell) {
529       nsAutoScriptBlocker scriptBlocker;
530       presShell->ContentStateChanged(doc, this, aStates);
531     }
532   }
533 }
534 
LockStyleStates(EventStates aStates,bool aEnabled)535 void Element::LockStyleStates(EventStates aStates, bool aEnabled) {
536   StyleStateLocks* locks = new StyleStateLocks(LockedStyleStates());
537 
538   locks->mLocks |= aStates;
539   if (aEnabled) {
540     locks->mValues |= aStates;
541   } else {
542     locks->mValues &= ~aStates;
543   }
544 
545   if (aStates.HasState(NS_EVENT_STATE_VISITED)) {
546     locks->mLocks &= ~NS_EVENT_STATE_UNVISITED;
547   }
548   if (aStates.HasState(NS_EVENT_STATE_UNVISITED)) {
549     locks->mLocks &= ~NS_EVENT_STATE_VISITED;
550   }
551 
552   SetProperty(nsGkAtoms::lockedStyleStates, locks,
553               nsINode::DeleteProperty<StyleStateLocks>);
554   SetHasLockedStyleStates();
555 
556   NotifyStyleStateChange(aStates);
557 }
558 
UnlockStyleStates(EventStates aStates)559 void Element::UnlockStyleStates(EventStates aStates) {
560   StyleStateLocks* locks = new StyleStateLocks(LockedStyleStates());
561 
562   locks->mLocks &= ~aStates;
563 
564   if (locks->mLocks.IsEmpty()) {
565     RemoveProperty(nsGkAtoms::lockedStyleStates);
566     ClearHasLockedStyleStates();
567     delete locks;
568   } else {
569     SetProperty(nsGkAtoms::lockedStyleStates, locks,
570                 nsINode::DeleteProperty<StyleStateLocks>);
571   }
572 
573   NotifyStyleStateChange(aStates);
574 }
575 
ClearStyleStateLocks()576 void Element::ClearStyleStateLocks() {
577   StyleStateLocks locks = LockedStyleStates();
578 
579   RemoveProperty(nsGkAtoms::lockedStyleStates);
580   ClearHasLockedStyleStates();
581 
582   NotifyStyleStateChange(locks.mLocks);
583 }
584 
585 /* virtual */
GetScopeChainParent() const586 nsINode* Element::GetScopeChainParent() const { return OwnerDoc(); }
587 
ClassList()588 nsDOMTokenList* Element::ClassList() {
589   Element::nsDOMSlots* slots = DOMSlots();
590 
591   if (!slots->mClassList) {
592     slots->mClassList = new nsDOMTokenList(this, nsGkAtoms::_class);
593   }
594 
595   return slots->mClassList;
596 }
597 
Part()598 nsDOMTokenList* Element::Part() {
599   Element::nsDOMSlots* slots = DOMSlots();
600 
601   if (!slots->mPart) {
602     slots->mPart = new nsDOMTokenList(this, nsGkAtoms::part);
603   }
604 
605   return slots->mPart;
606 }
607 
GetAttributeNames(nsTArray<nsString> & aResult)608 void Element::GetAttributeNames(nsTArray<nsString>& aResult) {
609   uint32_t count = mAttrs.AttrCount();
610   for (uint32_t i = 0; i < count; ++i) {
611     const nsAttrName* name = mAttrs.AttrNameAt(i);
612     name->GetQualifiedName(*aResult.AppendElement());
613   }
614 }
615 
GetElementsByTagName(const nsAString & aLocalName)616 already_AddRefed<nsIHTMLCollection> Element::GetElementsByTagName(
617     const nsAString& aLocalName) {
618   return NS_GetContentList(this, kNameSpaceID_Unknown, aLocalName);
619 }
620 
GetScrollFrame(nsIFrame ** aFrame,FlushType aFlushType)621 nsIScrollableFrame* Element::GetScrollFrame(nsIFrame** aFrame,
622                                             FlushType aFlushType) {
623   nsIFrame* frame = GetPrimaryFrame(aFlushType);
624   if (aFrame) {
625     *aFrame = frame;
626   }
627   if (frame) {
628     if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
629       // It's unclear what to return for SVG frames, so just return null.
630       return nullptr;
631     }
632 
633     // menu frames implement GetScrollTargetFrame but we don't want
634     // to use it here.  Similar for comboboxes.
635     LayoutFrameType type = frame->Type();
636     if (type != LayoutFrameType::Menu &&
637         type != LayoutFrameType::ComboboxControl) {
638       nsIScrollableFrame* scrollFrame = frame->GetScrollTargetFrame();
639       if (scrollFrame) {
640         MOZ_ASSERT(!OwnerDoc()->IsScrollingElement(this),
641                    "How can we have a scrollframe if we're the "
642                    "scrollingElement for our document?");
643         return scrollFrame;
644       }
645     }
646   }
647 
648   Document* doc = OwnerDoc();
649   // Note: This IsScrollingElement() call can flush frames, if we're the body of
650   // a quirks mode document.
651   bool isScrollingElement = OwnerDoc()->IsScrollingElement(this);
652   // Now reget *aStyledFrame if the caller asked for it, because that frame
653   // flush can kill it.
654   if (aFrame) {
655     *aFrame = GetPrimaryFrame(FlushType::None);
656   }
657 
658   if (isScrollingElement) {
659     // Our scroll info should map to the root scrollable frame if there is one.
660     if (PresShell* presShell = doc->GetPresShell()) {
661       return presShell->GetRootScrollFrameAsScrollable();
662     }
663   }
664 
665   return nullptr;
666 }
667 
ScrollIntoView(const BooleanOrScrollIntoViewOptions & aObject)668 void Element::ScrollIntoView(const BooleanOrScrollIntoViewOptions& aObject) {
669   if (aObject.IsScrollIntoViewOptions()) {
670     return ScrollIntoView(aObject.GetAsScrollIntoViewOptions());
671   }
672 
673   MOZ_DIAGNOSTIC_ASSERT(aObject.IsBoolean());
674 
675   ScrollIntoViewOptions options;
676   if (aObject.GetAsBoolean()) {
677     options.mBlock = ScrollLogicalPosition::Start;
678     options.mInline = ScrollLogicalPosition::Nearest;
679   } else {
680     options.mBlock = ScrollLogicalPosition::End;
681     options.mInline = ScrollLogicalPosition::Nearest;
682   }
683   return ScrollIntoView(options);
684 }
685 
ScrollIntoView(const ScrollIntoViewOptions & aOptions)686 void Element::ScrollIntoView(const ScrollIntoViewOptions& aOptions) {
687   Document* document = GetComposedDoc();
688   if (!document) {
689     return;
690   }
691 
692   // Get the presentation shell
693   RefPtr<PresShell> presShell = document->GetPresShell();
694   if (!presShell) {
695     return;
696   }
697 
698   WhereToScroll whereToScrollVertically = kScrollToCenter;
699   switch (aOptions.mBlock) {
700     case ScrollLogicalPosition::Start:
701       whereToScrollVertically = kScrollToTop;
702       break;
703     case ScrollLogicalPosition::Center:
704       whereToScrollVertically = kScrollToCenter;
705       break;
706     case ScrollLogicalPosition::End:
707       whereToScrollVertically = kScrollToBottom;
708       break;
709     case ScrollLogicalPosition::Nearest:
710       whereToScrollVertically = kScrollMinimum;
711       break;
712     default:
713       MOZ_ASSERT_UNREACHABLE("Unexpected ScrollLogicalPosition value");
714   }
715 
716   WhereToScroll whereToScrollHorizontally = kScrollToCenter;
717   switch (aOptions.mInline) {
718     case ScrollLogicalPosition::Start:
719       whereToScrollHorizontally = kScrollToLeft;
720       break;
721     case ScrollLogicalPosition::Center:
722       whereToScrollHorizontally = kScrollToCenter;
723       break;
724     case ScrollLogicalPosition::End:
725       whereToScrollHorizontally = kScrollToRight;
726       break;
727     case ScrollLogicalPosition::Nearest:
728       whereToScrollHorizontally = kScrollMinimum;
729       break;
730     default:
731       MOZ_ASSERT_UNREACHABLE("Unexpected ScrollLogicalPosition value");
732   }
733 
734   ScrollFlags scrollFlags =
735       ScrollFlags::ScrollOverflowHidden | ScrollFlags::ScrollSnap;
736   if (aOptions.mBehavior == ScrollBehavior::Smooth) {
737     scrollFlags |= ScrollFlags::ScrollSmooth;
738   } else if (aOptions.mBehavior == ScrollBehavior::Auto) {
739     scrollFlags |= ScrollFlags::ScrollSmoothAuto;
740   }
741 
742   presShell->ScrollContentIntoView(
743       this, ScrollAxis(whereToScrollVertically, WhenToScroll::Always),
744       ScrollAxis(whereToScrollHorizontally, WhenToScroll::Always), scrollFlags);
745 }
746 
Scroll(const CSSIntPoint & aScroll,const ScrollOptions & aOptions)747 void Element::Scroll(const CSSIntPoint& aScroll,
748                      const ScrollOptions& aOptions) {
749   nsIScrollableFrame* sf = GetScrollFrame();
750   if (sf) {
751     ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
752                                 ? ScrollMode::SmoothMsd
753                                 : ScrollMode::Instant;
754 
755     sf->ScrollToCSSPixels(aScroll, scrollMode);
756   }
757 }
758 
Scroll(double aXScroll,double aYScroll)759 void Element::Scroll(double aXScroll, double aYScroll) {
760   // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
761   auto scrollPos = CSSIntPoint::Truncate(mozilla::ToZeroIfNonfinite(aXScroll),
762                                          mozilla::ToZeroIfNonfinite(aYScroll));
763 
764   Scroll(scrollPos, ScrollOptions());
765 }
766 
Scroll(const ScrollToOptions & aOptions)767 void Element::Scroll(const ScrollToOptions& aOptions) {
768   nsIScrollableFrame* sf = GetScrollFrame();
769   if (sf) {
770     CSSIntPoint scrollPos = sf->GetScrollPositionCSSPixels();
771     if (aOptions.mLeft.WasPassed()) {
772       scrollPos.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
773     }
774     if (aOptions.mTop.WasPassed()) {
775       scrollPos.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
776     }
777     Scroll(scrollPos, aOptions);
778   }
779 }
780 
ScrollTo(double aXScroll,double aYScroll)781 void Element::ScrollTo(double aXScroll, double aYScroll) {
782   Scroll(aXScroll, aYScroll);
783 }
784 
ScrollTo(const ScrollToOptions & aOptions)785 void Element::ScrollTo(const ScrollToOptions& aOptions) { Scroll(aOptions); }
786 
ScrollBy(double aXScrollDif,double aYScrollDif)787 void Element::ScrollBy(double aXScrollDif, double aYScrollDif) {
788   nsIScrollableFrame* sf = GetScrollFrame();
789   if (sf) {
790     ScrollToOptions options;
791     options.mLeft.Construct(aXScrollDif);
792     options.mTop.Construct(aYScrollDif);
793     ScrollBy(options);
794   }
795 }
796 
ScrollBy(const ScrollToOptions & aOptions)797 void Element::ScrollBy(const ScrollToOptions& aOptions) {
798   nsIScrollableFrame* sf = GetScrollFrame();
799   if (sf) {
800     CSSIntPoint scrollDelta;
801     if (aOptions.mLeft.WasPassed()) {
802       scrollDelta.x = mozilla::ToZeroIfNonfinite(aOptions.mLeft.Value());
803     }
804     if (aOptions.mTop.WasPassed()) {
805       scrollDelta.y = mozilla::ToZeroIfNonfinite(aOptions.mTop.Value());
806     }
807 
808     ScrollMode scrollMode = sf->IsSmoothScroll(aOptions.mBehavior)
809                                 ? ScrollMode::SmoothMsd
810                                 : ScrollMode::Instant;
811 
812     sf->ScrollByCSSPixels(scrollDelta, scrollMode,
813                           mozilla::ScrollOrigin::Relative);
814   }
815 }
816 
ScrollTop()817 int32_t Element::ScrollTop() {
818   nsIScrollableFrame* sf = GetScrollFrame();
819   return sf ? sf->GetScrollPositionCSSPixels().y : 0;
820 }
821 
SetScrollTop(int32_t aScrollTop)822 void Element::SetScrollTop(int32_t aScrollTop) {
823   // When aScrollTop is 0, we don't need to flush layout to scroll to that
824   // point; we know 0 is always in range.  At least we think so...  But we do
825   // need to flush frames so we ensure we find the right scrollable frame if
826   // there is one.
827   //
828   // If aScrollTop is nonzero, we need to flush layout because we need to figure
829   // out what our real scrollTopMax is.
830   FlushType flushType = aScrollTop == 0 ? FlushType::Frames : FlushType::Layout;
831   nsIScrollableFrame* sf = GetScrollFrame(nullptr, flushType);
832   if (sf) {
833     ScrollMode scrollMode =
834         sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
835 
836     sf->ScrollToCSSPixels(
837         CSSIntPoint(sf->GetScrollPositionCSSPixels().x, aScrollTop),
838         scrollMode);
839   }
840 }
841 
ScrollLeft()842 int32_t Element::ScrollLeft() {
843   nsIScrollableFrame* sf = GetScrollFrame();
844   return sf ? sf->GetScrollPositionCSSPixels().x : 0;
845 }
846 
SetScrollLeft(int32_t aScrollLeft)847 void Element::SetScrollLeft(int32_t aScrollLeft) {
848   // We can't assume things here based on the value of aScrollLeft, because
849   // depending on our direction and layout 0 may or may not be in our scroll
850   // range.  So we need to flush layout no matter what.
851   nsIScrollableFrame* sf = GetScrollFrame();
852   if (sf) {
853     ScrollMode scrollMode =
854         sf->IsSmoothScroll() ? ScrollMode::SmoothMsd : ScrollMode::Instant;
855 
856     sf->ScrollToCSSPixels(
857         CSSIntPoint(aScrollLeft, sf->GetScrollPositionCSSPixels().y),
858         scrollMode);
859   }
860 }
861 
MozScrollSnap()862 void Element::MozScrollSnap() {
863   nsIScrollableFrame* sf = GetScrollFrame(nullptr, FlushType::None);
864   if (sf) {
865     sf->ScrollSnap();
866   }
867 }
868 
ScrollTopMin()869 int32_t Element::ScrollTopMin() {
870   nsIScrollableFrame* sf = GetScrollFrame();
871   if (!sf) {
872     return 0;
873   }
874   return CSSPixel::FromAppUnits(sf->GetScrollRange().y).Rounded();
875 }
876 
ScrollTopMax()877 int32_t Element::ScrollTopMax() {
878   nsIScrollableFrame* sf = GetScrollFrame();
879   if (!sf) {
880     return 0;
881   }
882   return CSSPixel::FromAppUnits(sf->GetScrollRange().YMost()).Rounded();
883 }
884 
ScrollLeftMin()885 int32_t Element::ScrollLeftMin() {
886   nsIScrollableFrame* sf = GetScrollFrame();
887   if (!sf) {
888     return 0;
889   }
890   return CSSPixel::FromAppUnits(sf->GetScrollRange().x).Rounded();
891 }
892 
ScrollLeftMax()893 int32_t Element::ScrollLeftMax() {
894   nsIScrollableFrame* sf = GetScrollFrame();
895   if (!sf) {
896     return 0;
897   }
898   return CSSPixel::FromAppUnits(sf->GetScrollRange().XMost()).Rounded();
899 }
900 
GetScrollRectSizeForOverflowVisibleFrame(nsIFrame * aFrame)901 static nsSize GetScrollRectSizeForOverflowVisibleFrame(nsIFrame* aFrame) {
902   if (!aFrame || aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
903     return nsSize(0, 0);
904   }
905 
906   nsRect paddingRect = aFrame->GetPaddingRectRelativeToSelf();
907   OverflowAreas overflowAreas(paddingRect, paddingRect);
908   // Add the scrollable overflow areas of children (if any) to the paddingRect.
909   // It's important to start with the paddingRect, otherwise if there are no
910   // children the overflow rect will be 0,0,0,0 which will force the point 0,0
911   // to be included in the final rect.
912   nsLayoutUtils::UnionChildOverflow(aFrame, overflowAreas);
913   // Make sure that an empty padding-rect's edges are included, by adding
914   // the padding-rect in again with UnionEdges.
915   nsRect overflowRect =
916       overflowAreas.ScrollableOverflow().UnionEdges(paddingRect);
917   return nsLayoutUtils::GetScrolledRect(aFrame, overflowRect,
918                                         paddingRect.Size(),
919                                         aFrame->StyleVisibility()->mDirection)
920       .Size();
921 }
922 
ScrollHeight()923 int32_t Element::ScrollHeight() {
924   nsIFrame* frame;
925   nsIScrollableFrame* sf = GetScrollFrame(&frame);
926   nscoord height;
927   if (sf) {
928     height = sf->GetScrollRange().Height() + sf->GetScrollPortRect().Height();
929   } else {
930     height = GetScrollRectSizeForOverflowVisibleFrame(frame).height;
931   }
932 
933   return nsPresContext::AppUnitsToIntCSSPixels(height);
934 }
935 
ScrollWidth()936 int32_t Element::ScrollWidth() {
937   nsIFrame* frame;
938   nsIScrollableFrame* sf = GetScrollFrame(&frame);
939   nscoord width;
940   if (sf) {
941     width = sf->GetScrollRange().Width() + sf->GetScrollPortRect().Width();
942   } else {
943     width = GetScrollRectSizeForOverflowVisibleFrame(frame).width;
944   }
945 
946   return nsPresContext::AppUnitsToIntCSSPixels(width);
947 }
948 
GetClientAreaRect()949 nsRect Element::GetClientAreaRect() {
950   Document* doc = OwnerDoc();
951   nsPresContext* presContext = doc->GetPresContext();
952 
953   // We can avoid a layout flush if this is the scrolling element of the
954   // document, we have overlay scrollbars, and we aren't embedded in another
955   // document
956   bool overlayScrollbars =
957       LookAndFeel::GetInt(LookAndFeel::IntID::UseOverlayScrollbars) != 0;
958   bool rootContentDocument =
959       presContext && presContext->IsRootContentDocument();
960   if (overlayScrollbars && rootContentDocument &&
961       doc->IsScrollingElement(this)) {
962     // We will always have a pres shell if we have a pres context, and we will
963     // only get here if we have a pres context from the root content document
964     // check
965     PresShell* presShell = doc->GetPresShell();
966 
967     // Ensure up to date dimensions, but don't reflow
968     RefPtr<nsViewManager> viewManager = presShell->GetViewManager();
969     if (viewManager) {
970       viewManager->FlushDelayedResize(false);
971     }
972     return nsRect(nsPoint(), presContext->GetVisibleArea().Size());
973   }
974 
975   nsIFrame* frame;
976   if (nsIScrollableFrame* sf = GetScrollFrame(&frame)) {
977     nsRect scrollPort = sf->GetScrollPortRect();
978 
979     if (!sf->IsRootScrollFrameOfDocument()) {
980       MOZ_ASSERT(frame);
981       nsIFrame* scrollableAsFrame = do_QueryFrame(sf);
982       // We want the offset to be relative to `frame`, not `sf`... Except for
983       // the root scroll frame, which is an ancestor of frame rather than a
984       // descendant and thus this wouldn't particularly make sense.
985       if (frame != scrollableAsFrame) {
986         scrollPort.MoveBy(scrollableAsFrame->GetOffsetTo(frame));
987       }
988     }
989 
990     // The scroll port value might be expanded to the minimum scale size, we
991     // should limit the size to the ICB in such cases.
992     scrollPort.SizeTo(sf->GetLayoutSize());
993     return scrollPort;
994   }
995 
996   if (frame &&
997       // The display check is OK even though we're not looking at the style
998       // frame, because the style frame only differs from "frame" for tables,
999       // and table wrappers have the same display as the table itself.
1000       (!frame->StyleDisplay()->IsInlineFlow() ||
1001        frame->IsFrameOfType(nsIFrame::eReplaced))) {
1002     // Special case code to make client area work even when there isn't
1003     // a scroll view, see bug 180552, bug 227567.
1004     return frame->GetPaddingRect() - frame->GetPositionIgnoringScrolling();
1005   }
1006 
1007   // SVG nodes reach here and just return 0
1008   return nsRect(0, 0, 0, 0);
1009 }
1010 
GetBoundingClientRect()1011 already_AddRefed<DOMRect> Element::GetBoundingClientRect() {
1012   RefPtr<DOMRect> rect = new DOMRect(this);
1013 
1014   nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
1015   if (!frame) {
1016     // display:none, perhaps? Return the empty rect
1017     return rect.forget();
1018   }
1019 
1020   rect->SetLayoutRect(frame->GetBoundingClientRect());
1021   return rect.forget();
1022 }
1023 
GetClientRects()1024 already_AddRefed<DOMRectList> Element::GetClientRects() {
1025   RefPtr<DOMRectList> rectList = new DOMRectList(this);
1026 
1027   nsIFrame* frame = GetPrimaryFrame(FlushType::Layout);
1028   if (!frame) {
1029     // display:none, perhaps? Return an empty list
1030     return rectList.forget();
1031   }
1032 
1033   nsLayoutUtils::RectListBuilder builder(rectList);
1034   nsLayoutUtils::GetAllInFlowRects(
1035       frame, nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder,
1036       nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
1037   return rectList.forget();
1038 }
1039 
1040 //----------------------------------------------------------------------
1041 
AddToIdTable(nsAtom * aId)1042 void Element::AddToIdTable(nsAtom* aId) {
1043   NS_ASSERTION(HasID(), "Node doesn't have an ID?");
1044   if (IsInShadowTree()) {
1045     ShadowRoot* containingShadow = GetContainingShadow();
1046     containingShadow->AddToIdTable(this, aId);
1047   } else {
1048     Document* doc = GetUncomposedDoc();
1049     if (doc && !IsInNativeAnonymousSubtree()) {
1050       doc->AddToIdTable(this, aId);
1051     }
1052   }
1053 }
1054 
RemoveFromIdTable()1055 void Element::RemoveFromIdTable() {
1056   if (!HasID()) {
1057     return;
1058   }
1059 
1060   nsAtom* id = DoGetID();
1061   if (IsInShadowTree()) {
1062     ShadowRoot* containingShadow = GetContainingShadow();
1063     // Check for containingShadow because it may have
1064     // been deleted during unlinking.
1065     if (containingShadow) {
1066       containingShadow->RemoveFromIdTable(this, id);
1067     }
1068   } else {
1069     Document* doc = GetUncomposedDoc();
1070     if (doc && !IsInNativeAnonymousSubtree()) {
1071       doc->RemoveFromIdTable(this, id);
1072     }
1073   }
1074 }
1075 
SetSlot(const nsAString & aName,ErrorResult & aError)1076 void Element::SetSlot(const nsAString& aName, ErrorResult& aError) {
1077   aError = SetAttr(kNameSpaceID_None, nsGkAtoms::slot, aName, true);
1078 }
1079 
GetSlot(nsAString & aName)1080 void Element::GetSlot(nsAString& aName) {
1081   GetAttr(kNameSpaceID_None, nsGkAtoms::slot, aName);
1082 }
1083 
1084 // https://dom.spec.whatwg.org/#dom-element-shadowroot
GetShadowRootByMode() const1085 ShadowRoot* Element::GetShadowRootByMode() const {
1086   /**
1087    * 1. Let shadow be context object's shadow root.
1088    * 2. If shadow is null or its mode is "closed", then return null.
1089    */
1090   ShadowRoot* shadowRoot = GetShadowRoot();
1091   if (!shadowRoot || shadowRoot->IsClosed()) {
1092     return nullptr;
1093   }
1094 
1095   /**
1096    * 3. Return shadow.
1097    */
1098   return shadowRoot;
1099 }
1100 
CanAttachShadowDOM() const1101 bool Element::CanAttachShadowDOM() const {
1102   /**
1103    * If context object's namespace is not the HTML namespace,
1104    * return false.
1105    *
1106    * Deviate from the spec here to allow shadow dom attachement to
1107    * XUL elements.
1108    */
1109   if (!IsHTMLElement() &&
1110       !(IsXULElement() &&
1111         nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal()))) {
1112     return false;
1113   }
1114 
1115   /**
1116    * If context object's local name is not
1117    *    a valid custom element name, "article", "aside", "blockquote",
1118    *    "body", "div", "footer", "h1", "h2", "h3", "h4", "h5", "h6",
1119    *    "header", "main" "nav", "p", "section", or "span",
1120    *  return false.
1121    */
1122   nsAtom* nameAtom = NodeInfo()->NameAtom();
1123   uint32_t namespaceID = NodeInfo()->NamespaceID();
1124   if (!(nsContentUtils::IsCustomElementName(nameAtom, namespaceID) ||
1125         nameAtom == nsGkAtoms::article || nameAtom == nsGkAtoms::aside ||
1126         nameAtom == nsGkAtoms::blockquote || nameAtom == nsGkAtoms::body ||
1127         nameAtom == nsGkAtoms::div || nameAtom == nsGkAtoms::footer ||
1128         nameAtom == nsGkAtoms::h1 || nameAtom == nsGkAtoms::h2 ||
1129         nameAtom == nsGkAtoms::h3 || nameAtom == nsGkAtoms::h4 ||
1130         nameAtom == nsGkAtoms::h5 || nameAtom == nsGkAtoms::h6 ||
1131         nameAtom == nsGkAtoms::header || nameAtom == nsGkAtoms::main ||
1132         nameAtom == nsGkAtoms::nav || nameAtom == nsGkAtoms::p ||
1133         nameAtom == nsGkAtoms::section || nameAtom == nsGkAtoms::span)) {
1134     return false;
1135   }
1136 
1137   /**
1138    * 3. If context object’s local name is a valid custom element name, or
1139    *    context object’s is value is not null, then:
1140    *    If definition is not null and definition’s disable shadow is true, then
1141    *    return false.
1142    */
1143   // It will always have CustomElementData when the element is a valid custom
1144   // element or has is value.
1145   CustomElementData* ceData = GetCustomElementData();
1146   if (StaticPrefs::dom_webcomponents_formAssociatedCustomElement_enabled() &&
1147       ceData) {
1148     CustomElementDefinition* definition = ceData->GetCustomElementDefinition();
1149     // If the definition is null, the element possible hasn't yet upgraded.
1150     // Fallback to use LookupCustomElementDefinition to find its definition.
1151     if (!definition) {
1152       definition = nsContentUtils::LookupCustomElementDefinition(
1153           NodeInfo()->GetDocument(), nameAtom, namespaceID,
1154           ceData->GetCustomElementType());
1155     }
1156 
1157     if (definition && definition->mDisableShadow) {
1158       return false;
1159     }
1160   }
1161 
1162   return true;
1163 }
1164 
1165 // https://dom.spec.whatwg.org/#dom-element-attachshadow
AttachShadow(const ShadowRootInit & aInit,ErrorResult & aError)1166 already_AddRefed<ShadowRoot> Element::AttachShadow(const ShadowRootInit& aInit,
1167                                                    ErrorResult& aError) {
1168   /**
1169    * 1. If context object's namespace is not the HTML namespace,
1170    *    then throw a "NotSupportedError" DOMException.
1171    * 2. If context object's local name is not valid to attach shadow DOM to,
1172    *    then throw a "NotSupportedError" DOMException.
1173    */
1174   if (!CanAttachShadowDOM()) {
1175     aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
1176     return nullptr;
1177   }
1178 
1179   /**
1180    * 4. If context object is a shadow host, then throw
1181    *    an "NotSupportedError" DOMException.
1182    */
1183   if (GetShadowRoot()) {
1184     aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
1185     return nullptr;
1186   }
1187 
1188   if (StaticPrefs::dom_webcomponents_shadowdom_report_usage()) {
1189     OwnerDoc()->ReportShadowDOMUsage();
1190   }
1191 
1192   return AttachShadowWithoutNameChecks(aInit.mMode);
1193 }
1194 
AttachShadowWithoutNameChecks(ShadowRootMode aMode)1195 already_AddRefed<ShadowRoot> Element::AttachShadowWithoutNameChecks(
1196     ShadowRootMode aMode) {
1197   nsAutoScriptBlocker scriptBlocker;
1198 
1199   RefPtr<mozilla::dom::NodeInfo> nodeInfo =
1200       mNodeInfo->NodeInfoManager()->GetNodeInfo(
1201           nsGkAtoms::documentFragmentNodeName, nullptr, kNameSpaceID_None,
1202           DOCUMENT_FRAGMENT_NODE);
1203 
1204   // If there are no children, the flat tree is not changing due to the presence
1205   // of the shadow root, so we don't need to invalidate style / layout.
1206   //
1207   // This is a minor optimization, but also works around nasty stuff like
1208   // bug 1397876.
1209   if (HasChildren()) {
1210     if (Document* doc = GetComposedDoc()) {
1211       if (PresShell* presShell = doc->GetPresShell()) {
1212         presShell->DestroyFramesForAndRestyle(this);
1213       }
1214     }
1215     MOZ_ASSERT(!GetPrimaryFrame());
1216   }
1217 
1218   /**
1219    * 4. Let shadow be a new shadow root whose node document is
1220    *    context object's node document, host is context object,
1221    *    and mode is init's mode.
1222    */
1223   auto* nim = nodeInfo->NodeInfoManager();
1224   RefPtr<ShadowRoot> shadowRoot =
1225       new (nim) ShadowRoot(this, aMode, nodeInfo.forget());
1226 
1227   if (NodeOrAncestorHasDirAuto()) {
1228     shadowRoot->SetAncestorHasDirAuto();
1229   }
1230 
1231   /**
1232    * 5. Set context object's shadow root to shadow.
1233    */
1234   SetShadowRoot(shadowRoot);
1235 
1236   // Dispatch a "shadowrootattached" event for devtools if needed.
1237   if (MOZ_UNLIKELY(nim->GetDocument()->ShadowRootAttachedEventEnabled())) {
1238     AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(
1239         this, u"shadowrootattached"_ns, CanBubble::eYes,
1240         ChromeOnlyDispatch::eYes, Composed::eYes);
1241     dispatcher->PostDOMEvent();
1242   }
1243 
1244   /**
1245    * 6. Return shadow.
1246    */
1247   return shadowRoot.forget();
1248 }
1249 
AttachAndSetUAShadowRoot(NotifyUAWidgetSetup aNotify)1250 void Element::AttachAndSetUAShadowRoot(NotifyUAWidgetSetup aNotify) {
1251   MOZ_DIAGNOSTIC_ASSERT(!CanAttachShadowDOM(),
1252                         "Cannot be used to attach UI shadow DOM");
1253   if (OwnerDoc()->IsStaticDocument()) {
1254     return;
1255   }
1256 
1257   if (!GetShadowRoot()) {
1258     RefPtr<ShadowRoot> shadowRoot =
1259         AttachShadowWithoutNameChecks(ShadowRootMode::Closed);
1260     shadowRoot->SetIsUAWidget();
1261   }
1262 
1263   MOZ_ASSERT(GetShadowRoot()->IsUAWidget());
1264   if (aNotify == NotifyUAWidgetSetup::Yes) {
1265     NotifyUAWidgetSetupOrChange();
1266   }
1267 }
1268 
NotifyUAWidgetSetupOrChange()1269 void Element::NotifyUAWidgetSetupOrChange() {
1270   MOZ_ASSERT(IsInComposedDoc());
1271   Document* doc = OwnerDoc();
1272   if (doc->IsStaticDocument()) {
1273     return;
1274   }
1275 
1276   // Schedule a runnable, ensure the event dispatches before
1277   // returning to content script.
1278   // This event cause UA Widget to construct or cause onchange callback
1279   // of existing UA Widget to run; dispatching this event twice should not cause
1280   // UA Widget to re-init.
1281   nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
1282       "Element::NotifyUAWidgetSetupOrChange::UAWidgetSetupOrChange",
1283       [self = RefPtr<Element>(this), doc = RefPtr<Document>(doc)]() {
1284         nsContentUtils::DispatchChromeEvent(doc, self,
1285                                             u"UAWidgetSetupOrChange"_ns,
1286                                             CanBubble::eYes, Cancelable::eNo);
1287       }));
1288 }
1289 
NotifyUAWidgetTeardown(UnattachShadowRoot aUnattachShadowRoot)1290 void Element::NotifyUAWidgetTeardown(UnattachShadowRoot aUnattachShadowRoot) {
1291   MOZ_ASSERT(IsInComposedDoc());
1292   if (!GetShadowRoot()) {
1293     return;
1294   }
1295   MOZ_ASSERT(GetShadowRoot()->IsUAWidget());
1296   if (aUnattachShadowRoot == UnattachShadowRoot::Yes) {
1297     UnattachShadow();
1298   }
1299 
1300   Document* doc = OwnerDoc();
1301   if (doc->IsStaticDocument()) {
1302     return;
1303   }
1304 
1305   // The runnable will dispatch an event to tear down UA Widget.
1306   nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
1307       "Element::NotifyUAWidgetTeardownAndUnattachShadow::UAWidgetTeardown",
1308       [self = RefPtr<Element>(this), doc = RefPtr<Document>(doc)]() {
1309         // Bail out if the element is being collected by CC
1310         bool hasHadScriptObject = true;
1311         nsIScriptGlobalObject* scriptObject =
1312             doc->GetScriptHandlingObject(hasHadScriptObject);
1313         if (!scriptObject && hasHadScriptObject) {
1314           return;
1315         }
1316 
1317         Unused << nsContentUtils::DispatchChromeEvent(
1318             doc, self, u"UAWidgetTeardown"_ns, CanBubble::eYes,
1319             Cancelable::eNo);
1320       }));
1321 }
1322 
UnattachShadow()1323 void Element::UnattachShadow() {
1324   ShadowRoot* shadowRoot = GetShadowRoot();
1325   if (!shadowRoot) {
1326     return;
1327   }
1328 
1329   nsAutoScriptBlocker scriptBlocker;
1330 
1331   if (Document* doc = GetComposedDoc()) {
1332     if (PresShell* presShell = doc->GetPresShell()) {
1333       presShell->DestroyFramesForAndRestyle(this);
1334 #ifdef ACCESSIBILITY
1335       // We need to notify the accessibility service here explicitly because,
1336       // even though we're going to reconstruct the _host_, the shadow root and
1337       // its children are never really going to come back. We could plumb that
1338       // further down to DestroyFramesForAndRestyle and add a new flag to
1339       // nsCSSFrameConstructor::ContentRemoved or such, but this seems simpler
1340       // instead.
1341       if (nsAccessibilityService* accService = GetAccService()) {
1342         accService->ContentRemoved(presShell, shadowRoot);
1343       }
1344 #endif
1345     }
1346   }
1347   MOZ_ASSERT(!GetPrimaryFrame());
1348 
1349   shadowRoot->Unattach();
1350   SetShadowRoot(nullptr);
1351 
1352   // Beware shadowRoot could be dead after this call.
1353 }
1354 
GetAttribute(const nsAString & aName,DOMString & aReturn)1355 void Element::GetAttribute(const nsAString& aName, DOMString& aReturn) {
1356   const nsAttrValue* val = mAttrs.GetAttr(
1357       aName,
1358       IsHTMLElement() && IsInHTMLDocument() ? eIgnoreCase : eCaseMatters);
1359   if (val) {
1360     val->ToString(aReturn);
1361   } else {
1362     if (IsXULElement()) {
1363       // XXX should be SetDOMStringToNull(aReturn);
1364       // See bug 232598
1365       // aReturn is already empty
1366     } else {
1367       aReturn.SetNull();
1368     }
1369   }
1370 }
1371 
ToggleAttribute(const nsAString & aName,const Optional<bool> & aForce,nsIPrincipal * aTriggeringPrincipal,ErrorResult & aError)1372 bool Element::ToggleAttribute(const nsAString& aName,
1373                               const Optional<bool>& aForce,
1374                               nsIPrincipal* aTriggeringPrincipal,
1375                               ErrorResult& aError) {
1376   aError = nsContentUtils::CheckQName(aName, false);
1377   if (aError.Failed()) {
1378     return false;
1379   }
1380 
1381   nsAutoString nameToUse;
1382   const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
1383   if (!name) {
1384     if (aForce.WasPassed() && !aForce.Value()) {
1385       return false;
1386     }
1387     RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
1388     if (!nameAtom) {
1389       aError.Throw(NS_ERROR_OUT_OF_MEMORY);
1390       return false;
1391     }
1392     aError = SetAttr(kNameSpaceID_None, nameAtom, u""_ns, aTriggeringPrincipal,
1393                      true);
1394     return true;
1395   }
1396   if (aForce.WasPassed() && aForce.Value()) {
1397     return true;
1398   }
1399   // Hold a strong reference here so that the atom or nodeinfo doesn't go
1400   // away during UnsetAttr. If it did UnsetAttr would be left with a
1401   // dangling pointer as argument without knowing it.
1402   nsAttrName tmp(*name);
1403 
1404   aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true);
1405   return false;
1406 }
1407 
SetAttribute(const nsAString & aName,const nsAString & aValue,nsIPrincipal * aTriggeringPrincipal,ErrorResult & aError)1408 void Element::SetAttribute(const nsAString& aName, const nsAString& aValue,
1409                            nsIPrincipal* aTriggeringPrincipal,
1410                            ErrorResult& aError) {
1411   aError = nsContentUtils::CheckQName(aName, false);
1412   if (aError.Failed()) {
1413     return;
1414   }
1415 
1416   nsAutoString nameToUse;
1417   const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse);
1418   if (!name) {
1419     RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse);
1420     if (!nameAtom) {
1421       aError.Throw(NS_ERROR_OUT_OF_MEMORY);
1422       return;
1423     }
1424     aError = SetAttr(kNameSpaceID_None, nameAtom, aValue, aTriggeringPrincipal,
1425                      true);
1426     return;
1427   }
1428 
1429   aError = SetAttr(name->NamespaceID(), name->LocalName(), name->GetPrefix(),
1430                    aValue, aTriggeringPrincipal, true);
1431 }
1432 
RemoveAttribute(const nsAString & aName,ErrorResult & aError)1433 void Element::RemoveAttribute(const nsAString& aName, ErrorResult& aError) {
1434   const nsAttrName* name = InternalGetAttrNameFromQName(aName);
1435 
1436   if (!name) {
1437     // If there is no canonical nsAttrName for this attribute name, then the
1438     // attribute does not exist and we can't get its namespace ID and
1439     // local name below, so we return early.
1440     return;
1441   }
1442 
1443   // Hold a strong reference here so that the atom or nodeinfo doesn't go
1444   // away during UnsetAttr. If it did UnsetAttr would be left with a
1445   // dangling pointer as argument without knowing it.
1446   nsAttrName tmp(*name);
1447 
1448   aError = UnsetAttr(name->NamespaceID(), name->LocalName(), true);
1449 }
1450 
GetAttributeNode(const nsAString & aName)1451 Attr* Element::GetAttributeNode(const nsAString& aName) {
1452   return Attributes()->GetNamedItem(aName);
1453 }
1454 
SetAttributeNode(Attr & aNewAttr,ErrorResult & aError)1455 already_AddRefed<Attr> Element::SetAttributeNode(Attr& aNewAttr,
1456                                                  ErrorResult& aError) {
1457   return Attributes()->SetNamedItemNS(aNewAttr, aError);
1458 }
1459 
RemoveAttributeNode(Attr & aAttribute,ErrorResult & aError)1460 already_AddRefed<Attr> Element::RemoveAttributeNode(Attr& aAttribute,
1461                                                     ErrorResult& aError) {
1462   Element* elem = aAttribute.GetElement();
1463   if (elem != this) {
1464     aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
1465     return nullptr;
1466   }
1467 
1468   nsAutoString nameSpaceURI;
1469   aAttribute.NodeInfo()->GetNamespaceURI(nameSpaceURI);
1470   return Attributes()->RemoveNamedItemNS(
1471       nameSpaceURI, aAttribute.NodeInfo()->LocalName(), aError);
1472 }
1473 
GetAttributeNS(const nsAString & aNamespaceURI,const nsAString & aLocalName,nsAString & aReturn)1474 void Element::GetAttributeNS(const nsAString& aNamespaceURI,
1475                              const nsAString& aLocalName, nsAString& aReturn) {
1476   int32_t nsid = nsContentUtils::NameSpaceManager()->GetNameSpaceID(
1477       aNamespaceURI, nsContentUtils::IsChromeDoc(OwnerDoc()));
1478 
1479   if (nsid == kNameSpaceID_Unknown) {
1480     // Unknown namespace means no attribute.
1481     SetDOMStringToNull(aReturn);
1482     return;
1483   }
1484 
1485   RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName);
1486   bool hasAttr = GetAttr(nsid, name, aReturn);
1487   if (!hasAttr) {
1488     SetDOMStringToNull(aReturn);
1489   }
1490 }
1491 
SetAttributeNS(const nsAString & aNamespaceURI,const nsAString & aQualifiedName,const nsAString & aValue,nsIPrincipal * aTriggeringPrincipal,ErrorResult & aError)1492 void Element::SetAttributeNS(const nsAString& aNamespaceURI,
1493                              const nsAString& aQualifiedName,
1494                              const nsAString& aValue,
1495                              nsIPrincipal* aTriggeringPrincipal,
1496                              ErrorResult& aError) {
1497   RefPtr<mozilla::dom::NodeInfo> ni;
1498   aError = nsContentUtils::GetNodeInfoFromQName(
1499       aNamespaceURI, aQualifiedName, mNodeInfo->NodeInfoManager(),
1500       ATTRIBUTE_NODE, getter_AddRefs(ni));
1501   if (aError.Failed()) {
1502     return;
1503   }
1504 
1505   aError = SetAttr(ni->NamespaceID(), ni->NameAtom(), ni->GetPrefixAtom(),
1506                    aValue, aTriggeringPrincipal, true);
1507 }
1508 
CreateDevtoolsPrincipal()1509 already_AddRefed<nsIPrincipal> Element::CreateDevtoolsPrincipal() {
1510   // Return an ExpandedPrincipal that subsumes this Element's Principal,
1511   // and expands this Element's CSP to allow the actions that devtools
1512   // needs to perform.
1513   AutoTArray<nsCOMPtr<nsIPrincipal>, 1> allowList = {NodePrincipal()};
1514   RefPtr<ExpandedPrincipal> dtPrincipal = ExpandedPrincipal::Create(
1515       allowList, NodePrincipal()->OriginAttributesRef());
1516 
1517   if (nsIContentSecurityPolicy* csp = GetCsp()) {
1518     RefPtr<nsCSPContext> dtCsp = new nsCSPContext();
1519     dtCsp->InitFromOther(static_cast<nsCSPContext*>(csp));
1520     dtCsp->SetSkipAllowInlineStyleCheck(true);
1521 
1522     dtPrincipal->SetCsp(dtCsp);
1523   }
1524 
1525   return dtPrincipal.forget();
1526 }
1527 
SetAttributeDevtools(const nsAString & aName,const nsAString & aValue,ErrorResult & aError)1528 void Element::SetAttributeDevtools(const nsAString& aName,
1529                                    const nsAString& aValue,
1530                                    ErrorResult& aError) {
1531   // Run this through SetAttribute with a devtools-ready principal.
1532   RefPtr<nsIPrincipal> dtPrincipal = CreateDevtoolsPrincipal();
1533   SetAttribute(aName, aValue, dtPrincipal, aError);
1534 }
1535 
SetAttributeDevtoolsNS(const nsAString & aNamespaceURI,const nsAString & aLocalName,const nsAString & aValue,ErrorResult & aError)1536 void Element::SetAttributeDevtoolsNS(const nsAString& aNamespaceURI,
1537                                      const nsAString& aLocalName,
1538                                      const nsAString& aValue,
1539                                      ErrorResult& aError) {
1540   // Run this through SetAttributeNS with a devtools-ready principal.
1541   RefPtr<nsIPrincipal> dtPrincipal = CreateDevtoolsPrincipal();
1542   SetAttributeNS(aNamespaceURI, aLocalName, aValue, dtPrincipal, aError);
1543 }
1544 
RemoveAttributeNS(const nsAString & aNamespaceURI,const nsAString & aLocalName,ErrorResult & aError)1545 void Element::RemoveAttributeNS(const nsAString& aNamespaceURI,
1546                                 const nsAString& aLocalName,
1547                                 ErrorResult& aError) {
1548   RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName);
1549   int32_t nsid = nsContentUtils::NameSpaceManager()->GetNameSpaceID(
1550       aNamespaceURI, nsContentUtils::IsChromeDoc(OwnerDoc()));
1551 
1552   if (nsid == kNameSpaceID_Unknown) {
1553     // If the namespace ID is unknown, it means there can't possibly be an
1554     // existing attribute. We would need a known namespace ID to pass into
1555     // UnsetAttr, so we return early if we don't have one.
1556     return;
1557   }
1558 
1559   aError = UnsetAttr(nsid, name, true);
1560 }
1561 
GetAttributeNodeNS(const nsAString & aNamespaceURI,const nsAString & aLocalName)1562 Attr* Element::GetAttributeNodeNS(const nsAString& aNamespaceURI,
1563                                   const nsAString& aLocalName) {
1564   return GetAttributeNodeNSInternal(aNamespaceURI, aLocalName);
1565 }
1566 
GetAttributeNodeNSInternal(const nsAString & aNamespaceURI,const nsAString & aLocalName)1567 Attr* Element::GetAttributeNodeNSInternal(const nsAString& aNamespaceURI,
1568                                           const nsAString& aLocalName) {
1569   return Attributes()->GetNamedItemNS(aNamespaceURI, aLocalName);
1570 }
1571 
SetAttributeNodeNS(Attr & aNewAttr,ErrorResult & aError)1572 already_AddRefed<Attr> Element::SetAttributeNodeNS(Attr& aNewAttr,
1573                                                    ErrorResult& aError) {
1574   return Attributes()->SetNamedItemNS(aNewAttr, aError);
1575 }
1576 
GetElementsByTagNameNS(const nsAString & aNamespaceURI,const nsAString & aLocalName,ErrorResult & aError)1577 already_AddRefed<nsIHTMLCollection> Element::GetElementsByTagNameNS(
1578     const nsAString& aNamespaceURI, const nsAString& aLocalName,
1579     ErrorResult& aError) {
1580   int32_t nameSpaceId = kNameSpaceID_Wildcard;
1581 
1582   if (!aNamespaceURI.EqualsLiteral("*")) {
1583     aError = nsContentUtils::NameSpaceManager()->RegisterNameSpace(
1584         aNamespaceURI, nameSpaceId);
1585     if (aError.Failed()) {
1586       return nullptr;
1587     }
1588   }
1589 
1590   NS_ASSERTION(nameSpaceId != kNameSpaceID_Unknown, "Unexpected namespace ID!");
1591 
1592   return NS_GetContentList(this, nameSpaceId, aLocalName);
1593 }
1594 
HasAttributeNS(const nsAString & aNamespaceURI,const nsAString & aLocalName) const1595 bool Element::HasAttributeNS(const nsAString& aNamespaceURI,
1596                              const nsAString& aLocalName) const {
1597   int32_t nsid = nsContentUtils::NameSpaceManager()->GetNameSpaceID(
1598       aNamespaceURI, nsContentUtils::IsChromeDoc(OwnerDoc()));
1599 
1600   if (nsid == kNameSpaceID_Unknown) {
1601     // Unknown namespace means no attr...
1602     return false;
1603   }
1604 
1605   RefPtr<nsAtom> name = NS_AtomizeMainThread(aLocalName);
1606   return HasAttr(nsid, name);
1607 }
1608 
GetElementsByClassName(const nsAString & aClassNames)1609 already_AddRefed<nsIHTMLCollection> Element::GetElementsByClassName(
1610     const nsAString& aClassNames) {
1611   return nsContentUtils::GetElementsByClassName(this, aClassNames);
1612 }
1613 
GetElementsWithGrid(nsTArray<RefPtr<Element>> & aElements)1614 void Element::GetElementsWithGrid(nsTArray<RefPtr<Element>>& aElements) {
1615   nsINode* cur = this;
1616   while (cur) {
1617     if (cur->IsElement()) {
1618       Element* elem = cur->AsElement();
1619 
1620       if (elem->GetPrimaryFrame()) {
1621         // See if this has a GridContainerFrame. Use the same method that
1622         // nsGridContainerFrame uses, which deals with some edge cases.
1623         if (nsGridContainerFrame::GetGridContainerFrame(
1624                 elem->GetPrimaryFrame())) {
1625           aElements.AppendElement(elem);
1626         }
1627 
1628         // This element has a frame, so allow the traversal to go through
1629         // the children.
1630         cur = cur->GetNextNode(this);
1631         continue;
1632       }
1633     }
1634 
1635     // Either this isn't an element, or it has no frame. Continue with the
1636     // traversal but ignore all the children.
1637     cur = cur->GetNextNonChildNode(this);
1638   }
1639 }
1640 
HasVisibleScrollbars()1641 bool Element::HasVisibleScrollbars() {
1642   nsIScrollableFrame* scrollFrame = GetScrollFrame();
1643   return scrollFrame && (!scrollFrame->GetScrollbarVisibility().isEmpty());
1644 }
1645 
BindToTree(BindContext & aContext,nsINode & aParent)1646 nsresult Element::BindToTree(BindContext& aContext, nsINode& aParent) {
1647   MOZ_ASSERT(aParent.IsContent() || aParent.IsDocument(),
1648              "Must have content or document parent!");
1649   MOZ_ASSERT(aParent.OwnerDoc() == OwnerDoc(),
1650              "Must have the same owner document");
1651   MOZ_ASSERT(OwnerDoc() == &aContext.OwnerDoc(), "These should match too");
1652   MOZ_ASSERT(!IsInUncomposedDoc(), "Already have a document.  Unbind first!");
1653   MOZ_ASSERT(!IsInComposedDoc(), "Already have a document.  Unbind first!");
1654   // Note that as we recurse into the kids, they'll have a non-null parent.  So
1655   // only assert if our parent is _changing_ while we have a parent.
1656   MOZ_ASSERT(!GetParentNode() || &aParent == GetParentNode(),
1657              "Already have a parent.  Unbind first!");
1658 
1659   const bool hadParent = !!GetParentNode();
1660 
1661   if (aParent.IsInNativeAnonymousSubtree()) {
1662     SetFlags(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE);
1663   }
1664   if (aParent.HasFlag(NODE_HAS_BEEN_IN_UA_WIDGET)) {
1665     SetFlags(NODE_HAS_BEEN_IN_UA_WIDGET);
1666   }
1667   if (IsRootOfNativeAnonymousSubtree()) {
1668     aParent.SetMayHaveAnonymousChildren();
1669   }
1670 
1671   // Now set the parent.
1672   mParent = &aParent;
1673   if (!hadParent && aParent.IsContent()) {
1674     SetParentIsContent(true);
1675     NS_ADDREF(mParent);
1676   }
1677   MOZ_ASSERT(!!GetParent() == aParent.IsContent());
1678 
1679   MOZ_ASSERT(!HasAnyOfFlags(Element::kAllServoDescendantBits));
1680 
1681   // Finally, set the document
1682   if (aParent.IsInUncomposedDoc() || aParent.IsInShadowTree()) {
1683     // We no longer need to track the subtree pointer (and in fact we'll assert
1684     // if we do this any later).
1685     ClearSubtreeRootPointer();
1686     SetIsConnected(aParent.IsInComposedDoc());
1687 
1688     if (aParent.IsInUncomposedDoc()) {
1689       SetIsInDocument();
1690     } else {
1691       SetFlags(NODE_IS_IN_SHADOW_TREE);
1692       MOZ_ASSERT(aParent.IsContent() &&
1693                  aParent.AsContent()->GetContainingShadow());
1694       ExtendedDOMSlots()->mContainingShadow =
1695           aParent.AsContent()->GetContainingShadow();
1696     }
1697     // Clear the lazy frame construction bits.
1698     UnsetFlags(NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES);
1699   } else {
1700     // If we're not in the doc and not in a shadow tree,
1701     // update our subtree pointer.
1702     SetSubtreeRootPointer(aParent.SubtreeRoot());
1703   }
1704 
1705   if (IsInComposedDoc()) {
1706     // Connected callback must be enqueued whenever a custom element becomes
1707     // connected.
1708     if (CustomElementData* data = GetCustomElementData()) {
1709       if (data->mState == CustomElementData::State::eCustom) {
1710         nsContentUtils::EnqueueLifecycleCallback(
1711             ElementCallbackType::eConnected, this);
1712       } else {
1713         // Step 7.7.2.2 https://dom.spec.whatwg.org/#concept-node-insert
1714         nsContentUtils::TryToUpgradeElement(this);
1715       }
1716     }
1717   }
1718 
1719   // This has to be here, rather than in nsGenericHTMLElement::BindToTree,
1720   //  because it has to happen after updating the parent pointer, but before
1721   //  recursively binding the kids.
1722   if (IsHTMLElement()) {
1723     SetDirOnBind(this, nsIContent::FromNode(aParent));
1724   }
1725 
1726   UpdateEditableState(false);
1727 
1728   // Call BindToTree on shadow root children.
1729   nsresult rv;
1730   if (ShadowRoot* shadowRoot = GetShadowRoot()) {
1731     rv = shadowRoot->Bind();
1732     NS_ENSURE_SUCCESS(rv, rv);
1733   }
1734 
1735   // Now recurse into our kids. Ensure this happens after binding the shadow
1736   // root so that directionality of slots is updated.
1737   {
1738     for (nsIContent* child = GetFirstChild(); child;
1739          child = child->GetNextSibling()) {
1740       rv = child->BindToTree(aContext, *this);
1741       NS_ENSURE_SUCCESS(rv, rv);
1742     }
1743   }
1744 
1745   MutationObservers::NotifyParentChainChanged(this);
1746   if (!hadParent && IsRootOfNativeAnonymousSubtree()) {
1747     MutationObservers::NotifyNativeAnonymousChildListChange(this, false);
1748   }
1749 
1750   // Ensure we only run this once, in the case we move the ShadowRoot around.
1751   if (aContext.SubtreeRootChanges()) {
1752     if (HasPartAttribute()) {
1753       if (ShadowRoot* shadow = GetContainingShadow()) {
1754         shadow->PartAdded(*this);
1755       }
1756     }
1757     if (HasID()) {
1758       AddToIdTable(DoGetID());
1759     }
1760     HandleShadowDOMRelatedInsertionSteps(hadParent);
1761   }
1762 
1763   if (MayHaveStyle()) {
1764     // If MayHaveStyle() is true, we must be an nsStyledElement.
1765     static_cast<nsStyledElement*>(this)->ReparseStyleAttribute(
1766         /* aForceInDataDoc = */ false);
1767   }
1768 
1769   // FIXME(emilio): Why is this needed? The element shouldn't even be styled in
1770   // the first place, we should style it properly eventually.
1771   //
1772   // Also, if this _is_ needed, then it's wrong and should use GetComposedDoc()
1773   // to account for Shadow DOM.
1774   if (aParent.IsInUncomposedDoc() && MayHaveAnimations()) {
1775     PseudoStyleType pseudoType = GetPseudoElementType();
1776     if ((pseudoType == PseudoStyleType::NotPseudo ||
1777          pseudoType == PseudoStyleType::before ||
1778          pseudoType == PseudoStyleType::after ||
1779          pseudoType == PseudoStyleType::marker) &&
1780         EffectSet::GetEffectSet(this, pseudoType)) {
1781       if (nsPresContext* presContext = aContext.OwnerDoc().GetPresContext()) {
1782         presContext->EffectCompositor()->RequestRestyle(
1783             this, pseudoType, EffectCompositor::RestyleType::Standard,
1784             EffectCompositor::CascadeLevel::Animations);
1785       }
1786     }
1787   }
1788 
1789   // XXXbz script execution during binding can trigger some of these
1790   // postcondition asserts....  But we do want that, since things will
1791   // generally be quite broken when that happens.
1792   MOZ_ASSERT(OwnerDoc() == aParent.OwnerDoc(), "Bound to wrong document");
1793   MOZ_ASSERT(IsInComposedDoc() == aContext.InComposedDoc());
1794   MOZ_ASSERT(IsInUncomposedDoc() == aContext.InUncomposedDoc());
1795   MOZ_ASSERT(&aParent == GetParentNode(), "Bound to wrong parent node");
1796   MOZ_ASSERT(aParent.IsInUncomposedDoc() == IsInUncomposedDoc());
1797   MOZ_ASSERT(aParent.IsInComposedDoc() == IsInComposedDoc());
1798   MOZ_ASSERT(aParent.IsInShadowTree() == IsInShadowTree());
1799   MOZ_ASSERT(aParent.SubtreeRoot() == SubtreeRoot());
1800   return NS_OK;
1801 }
1802 
WillDetachFromShadowOnUnbind(const Element & aElement,bool aNullParent)1803 bool WillDetachFromShadowOnUnbind(const Element& aElement, bool aNullParent) {
1804   // If our parent still is in a shadow tree by now, and we're not removing
1805   // ourselves from it, then we're still going to be in a shadow tree after
1806   // this.
1807   return aElement.IsInShadowTree() &&
1808          (aNullParent || !aElement.GetParent()->IsInShadowTree());
1809 }
1810 
UnbindFromTree(bool aNullParent)1811 void Element::UnbindFromTree(bool aNullParent) {
1812   HandleShadowDOMRelatedRemovalSteps(aNullParent);
1813 
1814   const bool detachingFromShadow =
1815       WillDetachFromShadowOnUnbind(*this, aNullParent);
1816   // Make sure to only remove from the ID table if our subtree root is actually
1817   // changing.
1818   if (IsInUncomposedDoc() || detachingFromShadow) {
1819     RemoveFromIdTable();
1820   }
1821 
1822   if (detachingFromShadow && HasPartAttribute()) {
1823     if (ShadowRoot* shadow = GetContainingShadow()) {
1824       shadow->PartRemoved(*this);
1825     }
1826   }
1827 
1828   // Make sure to unbind this node before doing the kids
1829   Document* document = GetComposedDoc();
1830 
1831   if (HasPointerLock()) {
1832     PointerLockManager::Unlock();
1833   }
1834   if (mState.HasState(NS_EVENT_STATE_FULLSCREEN)) {
1835     // The element being removed is an ancestor of the fullscreen element,
1836     // exit fullscreen state.
1837     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
1838                                     OwnerDoc(), nsContentUtils::eDOM_PROPERTIES,
1839                                     "RemovedFullscreenElement");
1840     // Fully exit fullscreen.
1841     Document::ExitFullscreenInDocTree(OwnerDoc());
1842   }
1843 
1844   if (HasServoData()) {
1845     MOZ_ASSERT(document);
1846     MOZ_ASSERT(IsInNativeAnonymousSubtree());
1847   }
1848 
1849   if (document) {
1850     ClearServoData(document);
1851   }
1852 
1853   if (aNullParent) {
1854     if (IsRootOfNativeAnonymousSubtree()) {
1855       MutationObservers::NotifyNativeAnonymousChildListChange(this, true);
1856     }
1857 
1858     if (GetParent()) {
1859       RefPtr<nsINode> p;
1860       p.swap(mParent);
1861     } else {
1862       mParent = nullptr;
1863     }
1864     SetParentIsContent(false);
1865   }
1866 
1867 #ifdef DEBUG
1868   // If we can get access to the PresContext, then we sanity-check that
1869   // we're not leaving behind a pointer to ourselves as the PresContext's
1870   // cached provider of the viewport's scrollbar styles.
1871   if (document) {
1872     nsPresContext* presContext = document->GetPresContext();
1873     if (presContext) {
1874       MOZ_ASSERT(this != presContext->GetViewportScrollStylesOverrideElement(),
1875                  "Leaving behind a raw pointer to this element (as having "
1876                  "propagated scrollbar styles) - that's dangerous...");
1877     }
1878   }
1879 
1880 #  ifdef ACCESSIBILITY
1881   MOZ_ASSERT(!GetAccService() || !GetAccService()->HasAccessible(this),
1882              "An accessible for this element still exists!");
1883 #  endif
1884 #endif
1885 
1886   // Ensure that CSS transitions don't continue on an element at a
1887   // different place in the tree (even if reinserted before next
1888   // animation refresh).
1889   //
1890   // We need to delete the properties while we're still in document
1891   // (if we were in document) so that they can look up the
1892   // PendingAnimationTracker on the document and remove their animations,
1893   // and so they can find their pres context for dispatching cancel events.
1894   //
1895   // FIXME (Bug 522599): Need a test for this.
1896   if (MayHaveAnimations()) {
1897     RemoveProperty(nsGkAtoms::transitionsOfBeforeProperty);
1898     RemoveProperty(nsGkAtoms::transitionsOfAfterProperty);
1899     RemoveProperty(nsGkAtoms::transitionsOfMarkerProperty);
1900     RemoveProperty(nsGkAtoms::transitionsProperty);
1901     RemoveProperty(nsGkAtoms::animationsOfBeforeProperty);
1902     RemoveProperty(nsGkAtoms::animationsOfAfterProperty);
1903     RemoveProperty(nsGkAtoms::animationsOfMarkerProperty);
1904     RemoveProperty(nsGkAtoms::animationsProperty);
1905     if (document) {
1906       if (nsPresContext* presContext = document->GetPresContext()) {
1907         // We have to clear all pending restyle requests for the animations on
1908         // this element to avoid unnecessary restyles when we re-attached this
1909         // element.
1910         presContext->EffectCompositor()->ClearRestyleRequestsFor(this);
1911       }
1912     }
1913   }
1914 
1915   ClearInDocument();
1916   SetIsConnected(false);
1917   if (HasElementCreatedFromPrototypeAndHasUnmodifiedL10n()) {
1918     if (document) {
1919       document->mL10nProtoElements.Remove(this);
1920     }
1921     ClearElementCreatedFromPrototypeAndHasUnmodifiedL10n();
1922   }
1923 
1924   if (aNullParent || !mParent->IsInShadowTree()) {
1925     UnsetFlags(NODE_IS_IN_SHADOW_TREE);
1926 
1927     // Begin keeping track of our subtree root.
1928     SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
1929   }
1930 
1931   if (nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
1932     if (aNullParent || !mParent->IsInShadowTree()) {
1933       slots->mContainingShadow = nullptr;
1934     }
1935   }
1936 
1937   if (document) {
1938     // Disconnected must be enqueued whenever a connected custom element becomes
1939     // disconnected.
1940     CustomElementData* data = GetCustomElementData();
1941     if (data) {
1942       if (data->mState == CustomElementData::State::eCustom) {
1943         nsContentUtils::EnqueueLifecycleCallback(
1944             ElementCallbackType::eDisconnected, this);
1945       } else {
1946         // Remove an unresolved custom element that is a candidate for upgrade
1947         // when a custom element is disconnected.
1948         nsContentUtils::UnregisterUnresolvedElement(this);
1949       }
1950     }
1951   }
1952 
1953   // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree,
1954   //  because it has to happen after unsetting the parent pointer, but before
1955   //  recursively unbinding the kids.
1956   if (IsHTMLElement()) {
1957     ResetDir(this);
1958   }
1959 
1960   for (nsIContent* child = GetFirstChild(); child;
1961        child = child->GetNextSibling()) {
1962     // Note that we pass false for aNullParent here, since we don't want
1963     // the kids to forget us.
1964     child->UnbindFromTree(false);
1965   }
1966 
1967   MutationObservers::NotifyParentChainChanged(this);
1968 
1969   // Unbind children of shadow root.
1970   if (ShadowRoot* shadowRoot = GetShadowRoot()) {
1971     shadowRoot->Unbind();
1972   }
1973 
1974   MOZ_ASSERT(!HasAnyOfFlags(kAllServoDescendantBits));
1975   MOZ_ASSERT(!document || document->GetServoRestyleRoot() != this);
1976 }
1977 
GetAnimatedAttr(int32_t aNamespaceID,nsAtom * aName)1978 UniquePtr<SMILAttr> Element::GetAnimatedAttr(int32_t aNamespaceID,
1979                                              nsAtom* aName) {
1980   return nullptr;
1981 }
1982 
SMILOverrideStyle()1983 nsDOMCSSAttributeDeclaration* Element::SMILOverrideStyle() {
1984   Element::nsExtendedDOMSlots* slots = ExtendedDOMSlots();
1985 
1986   if (!slots->mSMILOverrideStyle) {
1987     slots->mSMILOverrideStyle = new nsDOMCSSAttributeDeclaration(this, true);
1988   }
1989 
1990   return slots->mSMILOverrideStyle;
1991 }
1992 
GetSMILOverrideStyleDeclaration()1993 DeclarationBlock* Element::GetSMILOverrideStyleDeclaration() {
1994   Element::nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
1995   return slots ? slots->mSMILOverrideStyleDeclaration.get() : nullptr;
1996 }
1997 
SetSMILOverrideStyleDeclaration(DeclarationBlock & aDeclaration)1998 void Element::SetSMILOverrideStyleDeclaration(DeclarationBlock& aDeclaration) {
1999   ExtendedDOMSlots()->mSMILOverrideStyleDeclaration = &aDeclaration;
2000 
2001   // Only need to request a restyle if we're in a document.  (We might not
2002   // be in a document, if we're clearing animation effects on a target node
2003   // that's been detached since the previous animation sample.)
2004   if (Document* doc = GetComposedDoc()) {
2005     if (PresShell* presShell = doc->GetPresShell()) {
2006       presShell->RestyleForAnimation(this, RestyleHint::RESTYLE_SMIL);
2007     }
2008   }
2009 }
2010 
IsLabelable() const2011 bool Element::IsLabelable() const { return false; }
2012 
IsInteractiveHTMLContent() const2013 bool Element::IsInteractiveHTMLContent() const { return false; }
2014 
GetInlineStyleDeclaration() const2015 DeclarationBlock* Element::GetInlineStyleDeclaration() const {
2016   if (!MayHaveStyle()) {
2017     return nullptr;
2018   }
2019   const nsAttrValue* attrVal = mAttrs.GetAttr(nsGkAtoms::style);
2020 
2021   if (attrVal && attrVal->Type() == nsAttrValue::eCSSDeclaration) {
2022     return attrVal->GetCSSDeclarationValue();
2023   }
2024 
2025   return nullptr;
2026 }
2027 
GetMappedAttributes() const2028 const nsMappedAttributes* Element::GetMappedAttributes() const {
2029   return mAttrs.GetMapped();
2030 }
2031 
InlineStyleDeclarationWillChange(MutationClosureData & aData)2032 void Element::InlineStyleDeclarationWillChange(MutationClosureData& aData) {
2033   MOZ_ASSERT_UNREACHABLE("Element::InlineStyleDeclarationWillChange");
2034 }
2035 
SetInlineStyleDeclaration(DeclarationBlock & aDeclaration,MutationClosureData & aData)2036 nsresult Element::SetInlineStyleDeclaration(DeclarationBlock& aDeclaration,
2037                                             MutationClosureData& aData) {
2038   MOZ_ASSERT_UNREACHABLE("Element::SetInlineStyleDeclaration");
2039   return NS_ERROR_NOT_IMPLEMENTED;
2040 }
2041 
NS_IMETHODIMP_(bool)2042 NS_IMETHODIMP_(bool)
2043 Element::IsAttributeMapped(const nsAtom* aAttribute) const { return false; }
2044 
GetAttributeChangeHint(const nsAtom * aAttribute,int32_t aModType) const2045 nsChangeHint Element::GetAttributeChangeHint(const nsAtom* aAttribute,
2046                                              int32_t aModType) const {
2047   return nsChangeHint(0);
2048 }
2049 
FindAttributeDependence(const nsAtom * aAttribute,const MappedAttributeEntry * const aMaps[],uint32_t aMapCount)2050 bool Element::FindAttributeDependence(const nsAtom* aAttribute,
2051                                       const MappedAttributeEntry* const aMaps[],
2052                                       uint32_t aMapCount) {
2053   for (uint32_t mapindex = 0; mapindex < aMapCount; ++mapindex) {
2054     for (const MappedAttributeEntry* map = aMaps[mapindex]; map->attribute;
2055          ++map) {
2056       if (aAttribute == map->attribute) {
2057         return true;
2058       }
2059     }
2060   }
2061 
2062   return false;
2063 }
2064 
GetExistingAttrNameFromQName(const nsAString & aStr) const2065 already_AddRefed<mozilla::dom::NodeInfo> Element::GetExistingAttrNameFromQName(
2066     const nsAString& aStr) const {
2067   const nsAttrName* name = InternalGetAttrNameFromQName(aStr);
2068   if (!name) {
2069     return nullptr;
2070   }
2071 
2072   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
2073   if (name->IsAtom()) {
2074     nodeInfo = mNodeInfo->NodeInfoManager()->GetNodeInfo(
2075         name->Atom(), nullptr, kNameSpaceID_None, ATTRIBUTE_NODE);
2076   } else {
2077     nodeInfo = name->NodeInfo();
2078   }
2079 
2080   return nodeInfo.forget();
2081 }
2082 
2083 // static
ShouldBlur(nsIContent * aContent)2084 bool Element::ShouldBlur(nsIContent* aContent) {
2085   // Determine if the current element is focused, if it is not focused
2086   // then we should not try to blur
2087   Document* document = aContent->GetComposedDoc();
2088   if (!document) return false;
2089 
2090   nsCOMPtr<nsPIDOMWindowOuter> window = document->GetWindow();
2091   if (!window) return false;
2092 
2093   nsCOMPtr<nsPIDOMWindowOuter> focusedFrame;
2094   nsIContent* contentToBlur = nsFocusManager::GetFocusedDescendant(
2095       window, nsFocusManager::eOnlyCurrentWindow, getter_AddRefs(focusedFrame));
2096   if (contentToBlur == aContent) return true;
2097 
2098   // if focus on this element would get redirected, then check the redirected
2099   // content as well when blurring.
2100   return (contentToBlur &&
2101           nsFocusManager::GetRedirectedFocus(aContent) == contentToBlur);
2102 }
2103 
IsNodeOfType(uint32_t aFlags) const2104 bool Element::IsNodeOfType(uint32_t aFlags) const { return false; }
2105 
2106 /* static */
DispatchEvent(nsPresContext * aPresContext,WidgetEvent * aEvent,nsIContent * aTarget,bool aFullDispatch,nsEventStatus * aStatus)2107 nsresult Element::DispatchEvent(nsPresContext* aPresContext,
2108                                 WidgetEvent* aEvent, nsIContent* aTarget,
2109                                 bool aFullDispatch, nsEventStatus* aStatus) {
2110   MOZ_ASSERT(aTarget, "Must have target");
2111   MOZ_ASSERT(aEvent, "Must have source event");
2112   MOZ_ASSERT(aStatus, "Null out param?");
2113 
2114   if (!aPresContext) {
2115     return NS_OK;
2116   }
2117 
2118   RefPtr<PresShell> presShell = aPresContext->GetPresShell();
2119   if (!presShell) {
2120     return NS_OK;
2121   }
2122 
2123   if (aFullDispatch) {
2124     return presShell->HandleEventWithTarget(aEvent, nullptr, aTarget, aStatus);
2125   }
2126 
2127   return presShell->HandleDOMEventWithTarget(aTarget, aEvent, aStatus);
2128 }
2129 
2130 /* static */
DispatchClickEvent(nsPresContext * aPresContext,WidgetInputEvent * aSourceEvent,nsIContent * aTarget,bool aFullDispatch,const EventFlags * aExtraEventFlags,nsEventStatus * aStatus)2131 nsresult Element::DispatchClickEvent(nsPresContext* aPresContext,
2132                                      WidgetInputEvent* aSourceEvent,
2133                                      nsIContent* aTarget, bool aFullDispatch,
2134                                      const EventFlags* aExtraEventFlags,
2135                                      nsEventStatus* aStatus) {
2136   MOZ_ASSERT(aTarget, "Must have target");
2137   MOZ_ASSERT(aSourceEvent, "Must have source event");
2138   MOZ_ASSERT(aStatus, "Null out param?");
2139 
2140   WidgetMouseEvent event(aSourceEvent->IsTrusted(), eMouseClick,
2141                          aSourceEvent->mWidget, WidgetMouseEvent::eReal);
2142   event.mRefPoint = aSourceEvent->mRefPoint;
2143   uint32_t clickCount = 1;
2144   float pressure = 0;
2145   uint32_t pointerId = 0;  // Use the default value here.
2146   uint16_t inputSource = 0;
2147   WidgetMouseEvent* sourceMouseEvent = aSourceEvent->AsMouseEvent();
2148   if (sourceMouseEvent) {
2149     clickCount = sourceMouseEvent->mClickCount;
2150     pressure = sourceMouseEvent->mPressure;
2151     pointerId = sourceMouseEvent->pointerId;
2152     inputSource = sourceMouseEvent->mInputSource;
2153   } else if (aSourceEvent->mClass == eKeyboardEventClass) {
2154     event.mFlags.mIsPositionless = true;
2155     inputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
2156   }
2157   event.mPressure = pressure;
2158   event.mClickCount = clickCount;
2159   event.pointerId = pointerId;
2160   event.mInputSource = inputSource;
2161   event.mModifiers = aSourceEvent->mModifiers;
2162   if (aExtraEventFlags) {
2163     // Be careful not to overwrite existing flags!
2164     event.mFlags.Union(*aExtraEventFlags);
2165   }
2166 
2167   return DispatchEvent(aPresContext, &event, aTarget, aFullDispatch, aStatus);
2168 }
2169 
2170 //----------------------------------------------------------------------
LeaveLink(nsPresContext * aPresContext)2171 nsresult Element::LeaveLink(nsPresContext* aPresContext) {
2172   if (!aPresContext || !aPresContext->Document()->LinkHandlingEnabled()) {
2173     return NS_OK;
2174   }
2175   nsIDocShell* shell = aPresContext->Document()->GetDocShell();
2176   if (!shell) {
2177     return NS_OK;
2178   }
2179   return nsDocShell::Cast(shell)->OnLeaveLink();
2180 }
2181 
SetEventHandler(nsAtom * aEventName,const nsAString & aValue,bool aDefer)2182 void Element::SetEventHandler(nsAtom* aEventName, const nsAString& aValue,
2183                               bool aDefer) {
2184   Document* ownerDoc = OwnerDoc();
2185   if (ownerDoc->IsLoadedAsData()) {
2186     // Make this a no-op rather than throwing an error to avoid
2187     // the error causing problems setting the attribute.
2188     return;
2189   }
2190 
2191   MOZ_ASSERT(aEventName, "Must have event name!");
2192   bool defer = true;
2193   EventListenerManager* manager =
2194       GetEventListenerManagerForAttr(aEventName, &defer);
2195   if (!manager) {
2196     return;
2197   }
2198 
2199   defer = defer && aDefer;  // only defer if everyone agrees...
2200   manager->SetEventHandler(aEventName, aValue, defer,
2201                            !nsContentUtils::IsChromeDoc(ownerDoc), this);
2202 }
2203 
2204 //----------------------------------------------------------------------
2205 
InternalGetAttrNameFromQName(const nsAString & aStr,nsAutoString * aNameToUse) const2206 const nsAttrName* Element::InternalGetAttrNameFromQName(
2207     const nsAString& aStr, nsAutoString* aNameToUse) const {
2208   MOZ_ASSERT(!aNameToUse || aNameToUse->IsEmpty());
2209   const nsAttrName* val = nullptr;
2210   if (IsHTMLElement() && IsInHTMLDocument()) {
2211     nsAutoString lower;
2212     nsAutoString& outStr = aNameToUse ? *aNameToUse : lower;
2213     nsContentUtils::ASCIIToLower(aStr, outStr);
2214     val = mAttrs.GetExistingAttrNameFromQName(outStr);
2215     if (val) {
2216       outStr.Truncate();
2217     }
2218   } else {
2219     val = mAttrs.GetExistingAttrNameFromQName(aStr);
2220     if (!val && aNameToUse) {
2221       *aNameToUse = aStr;
2222     }
2223   }
2224 
2225   return val;
2226 }
2227 
MaybeCheckSameAttrVal(int32_t aNamespaceID,const nsAtom * aName,const nsAtom * aPrefix,const nsAttrValueOrString & aValue,bool aNotify,nsAttrValue & aOldValue,uint8_t * aModType,bool * aHasListeners,bool * aOldValueSet)2228 bool Element::MaybeCheckSameAttrVal(int32_t aNamespaceID, const nsAtom* aName,
2229                                     const nsAtom* aPrefix,
2230                                     const nsAttrValueOrString& aValue,
2231                                     bool aNotify, nsAttrValue& aOldValue,
2232                                     uint8_t* aModType, bool* aHasListeners,
2233                                     bool* aOldValueSet) {
2234   bool modification = false;
2235   *aHasListeners =
2236       aNotify && nsContentUtils::HasMutationListeners(
2237                      this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
2238   *aOldValueSet = false;
2239 
2240   // If we have no listeners and aNotify is false, we are almost certainly
2241   // coming from the content sink and will almost certainly have no previous
2242   // value.  Even if we do, setting the value is cheap when we have no
2243   // listeners and don't plan to notify.  The check for aNotify here is an
2244   // optimization, the check for *aHasListeners is a correctness issue.
2245   if (*aHasListeners || aNotify) {
2246     BorrowedAttrInfo info(GetAttrInfo(aNamespaceID, aName));
2247     if (info.mValue) {
2248       // Check whether the old value is the same as the new one.  Note that we
2249       // only need to actually _get_ the old value if we have listeners or
2250       // if the element is a custom element (because it may have an
2251       // attribute changed callback).
2252       if (*aHasListeners || GetCustomElementData()) {
2253         // Need to store the old value.
2254         //
2255         // If the current attribute value contains a pointer to some other data
2256         // structure that gets updated in the process of setting the attribute
2257         // we'll no longer have the old value of the attribute. Therefore, we
2258         // should serialize the attribute value now to keep a snapshot.
2259         //
2260         // We have to serialize the value anyway in order to create the
2261         // mutation event so there's no cost in doing it now.
2262         aOldValue.SetToSerialized(*info.mValue);
2263         *aOldValueSet = true;
2264       }
2265       bool valueMatches = aValue.EqualsAsStrings(*info.mValue);
2266       if (valueMatches && aPrefix == info.mName->GetPrefix()) {
2267         return true;
2268       }
2269       modification = true;
2270     }
2271   }
2272   *aModType = modification
2273                   ? static_cast<uint8_t>(MutationEvent_Binding::MODIFICATION)
2274                   : static_cast<uint8_t>(MutationEvent_Binding::ADDITION);
2275   return false;
2276 }
2277 
OnlyNotifySameValueSet(int32_t aNamespaceID,nsAtom * aName,nsAtom * aPrefix,const nsAttrValueOrString & aValue,bool aNotify,nsAttrValue & aOldValue,uint8_t * aModType,bool * aHasListeners,bool * aOldValueSet)2278 bool Element::OnlyNotifySameValueSet(int32_t aNamespaceID, nsAtom* aName,
2279                                      nsAtom* aPrefix,
2280                                      const nsAttrValueOrString& aValue,
2281                                      bool aNotify, nsAttrValue& aOldValue,
2282                                      uint8_t* aModType, bool* aHasListeners,
2283                                      bool* aOldValueSet) {
2284   if (!MaybeCheckSameAttrVal(aNamespaceID, aName, aPrefix, aValue, aNotify,
2285                              aOldValue, aModType, aHasListeners,
2286                              aOldValueSet)) {
2287     return false;
2288   }
2289 
2290   nsAutoScriptBlocker scriptBlocker;
2291   MutationObservers::NotifyAttributeSetToCurrentValue(this, aNamespaceID,
2292                                                       aName);
2293   return true;
2294 }
2295 
SetSingleClassFromParser(nsAtom * aSingleClassName)2296 nsresult Element::SetSingleClassFromParser(nsAtom* aSingleClassName) {
2297   // Keep this in sync with SetAttr and SetParsedAttr below.
2298 
2299   nsAttrValue value(aSingleClassName);
2300 
2301   Document* document = GetComposedDoc();
2302   mozAutoDocUpdate updateBatch(document, false);
2303 
2304   // In principle, BeforeSetAttr should be called here if a node type
2305   // existed that wanted to do something special for class, but there
2306   // is no such node type, so calling SetMayHaveClass() directly.
2307   SetMayHaveClass();
2308 
2309   return SetAttrAndNotify(kNameSpaceID_None, nsGkAtoms::_class,
2310                           nullptr,  // prefix
2311                           nullptr,  // old value
2312                           value, nullptr,
2313                           static_cast<uint8_t>(MutationEvent_Binding::ADDITION),
2314                           false,  // hasListeners
2315                           false,  // notify
2316                           kCallAfterSetAttr, document, updateBatch);
2317 }
2318 
SetAttr(int32_t aNamespaceID,nsAtom * aName,nsAtom * aPrefix,const nsAString & aValue,nsIPrincipal * aSubjectPrincipal,bool aNotify)2319 nsresult Element::SetAttr(int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix,
2320                           const nsAString& aValue,
2321                           nsIPrincipal* aSubjectPrincipal, bool aNotify) {
2322   // Keep this in sync with SetParsedAttr below and SetSingleClassFromParser
2323   // above.
2324 
2325   NS_ENSURE_ARG_POINTER(aName);
2326   NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
2327                "Don't call SetAttr with unknown namespace");
2328 
2329   uint8_t modType;
2330   bool hasListeners;
2331   nsAttrValueOrString value(aValue);
2332   nsAttrValue oldValue;
2333   bool oldValueSet;
2334 
2335   if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
2336                              oldValue, &modType, &hasListeners, &oldValueSet)) {
2337     return OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify);
2338   }
2339 
2340   // Hold a script blocker while calling ParseAttribute since that can call
2341   // out to id-observers
2342   Document* document = GetComposedDoc();
2343   mozAutoDocUpdate updateBatch(document, aNotify);
2344 
2345   if (aNotify) {
2346     MutationObservers::NotifyAttributeWillChange(this, aNamespaceID, aName,
2347                                                  modType);
2348   }
2349 
2350   nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
2351   NS_ENSURE_SUCCESS(rv, rv);
2352 
2353   nsAttrValue attrValue;
2354   if (!ParseAttribute(aNamespaceID, aName, aValue, aSubjectPrincipal,
2355                       attrValue)) {
2356     attrValue.SetTo(aValue);
2357   }
2358 
2359   PreIdMaybeChange(aNamespaceID, aName, &value);
2360 
2361   return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
2362                           oldValueSet ? &oldValue : nullptr, attrValue,
2363                           aSubjectPrincipal, modType, hasListeners, aNotify,
2364                           kCallAfterSetAttr, document, updateBatch);
2365 }
2366 
SetParsedAttr(int32_t aNamespaceID,nsAtom * aName,nsAtom * aPrefix,nsAttrValue & aParsedValue,bool aNotify)2367 nsresult Element::SetParsedAttr(int32_t aNamespaceID, nsAtom* aName,
2368                                 nsAtom* aPrefix, nsAttrValue& aParsedValue,
2369                                 bool aNotify) {
2370   // Keep this in sync with SetAttr and SetSingleClassFromParser above
2371 
2372   NS_ENSURE_ARG_POINTER(aName);
2373   NS_ASSERTION(aNamespaceID != kNameSpaceID_Unknown,
2374                "Don't call SetAttr with unknown namespace");
2375 
2376   uint8_t modType;
2377   bool hasListeners;
2378   nsAttrValueOrString value(aParsedValue);
2379   nsAttrValue oldValue;
2380   bool oldValueSet;
2381 
2382   if (OnlyNotifySameValueSet(aNamespaceID, aName, aPrefix, value, aNotify,
2383                              oldValue, &modType, &hasListeners, &oldValueSet)) {
2384     return OnAttrSetButNotChanged(aNamespaceID, aName, value, aNotify);
2385   }
2386 
2387   Document* document = GetComposedDoc();
2388   mozAutoDocUpdate updateBatch(document, aNotify);
2389 
2390   if (aNotify) {
2391     MutationObservers::NotifyAttributeWillChange(this, aNamespaceID, aName,
2392                                                  modType);
2393   }
2394 
2395   nsresult rv = BeforeSetAttr(aNamespaceID, aName, &value, aNotify);
2396   NS_ENSURE_SUCCESS(rv, rv);
2397 
2398   PreIdMaybeChange(aNamespaceID, aName, &value);
2399 
2400   return SetAttrAndNotify(aNamespaceID, aName, aPrefix,
2401                           oldValueSet ? &oldValue : nullptr, aParsedValue,
2402                           nullptr, modType, hasListeners, aNotify,
2403                           kCallAfterSetAttr, document, updateBatch);
2404 }
2405 
SetAttrAndNotify(int32_t aNamespaceID,nsAtom * aName,nsAtom * aPrefix,const nsAttrValue * aOldValue,nsAttrValue & aParsedValue,nsIPrincipal * aSubjectPrincipal,uint8_t aModType,bool aFireMutation,bool aNotify,bool aCallAfterSetAttr,Document * aComposedDocument,const mozAutoDocUpdate &)2406 nsresult Element::SetAttrAndNotify(
2407     int32_t aNamespaceID, nsAtom* aName, nsAtom* aPrefix,
2408     const nsAttrValue* aOldValue, nsAttrValue& aParsedValue,
2409     nsIPrincipal* aSubjectPrincipal, uint8_t aModType, bool aFireMutation,
2410     bool aNotify, bool aCallAfterSetAttr, Document* aComposedDocument,
2411     const mozAutoDocUpdate&) {
2412   nsresult rv;
2413   nsMutationGuard::DidMutate();
2414 
2415   // Copy aParsedValue for later use since it will be lost when we call
2416   // SetAndSwapMappedAttr below
2417   nsAttrValue valueForAfterSetAttr;
2418   if (aCallAfterSetAttr || GetCustomElementData()) {
2419     valueForAfterSetAttr.SetTo(aParsedValue);
2420   }
2421 
2422   bool hadValidDir = false;
2423   bool hadDirAuto = false;
2424   bool oldValueSet;
2425 
2426   if (aNamespaceID == kNameSpaceID_None) {
2427     if (aName == nsGkAtoms::dir) {
2428       hadValidDir = HasValidDir() || IsHTMLElement(nsGkAtoms::bdi);
2429       hadDirAuto = HasDirAuto();  // already takes bdi into account
2430     }
2431 
2432     // XXXbz Perhaps we should push up the attribute mapping function
2433     // stuff to Element?
2434     if (!IsAttributeMapped(aName) ||
2435         !SetAndSwapMappedAttribute(aName, aParsedValue, &oldValueSet, &rv)) {
2436       rv = mAttrs.SetAndSwapAttr(aName, aParsedValue, &oldValueSet);
2437     }
2438   } else {
2439     RefPtr<mozilla::dom::NodeInfo> ni;
2440     ni = mNodeInfo->NodeInfoManager()->GetNodeInfo(aName, aPrefix, aNamespaceID,
2441                                                    ATTRIBUTE_NODE);
2442 
2443     rv = mAttrs.SetAndSwapAttr(ni, aParsedValue, &oldValueSet);
2444   }
2445   NS_ENSURE_SUCCESS(rv, rv);
2446 
2447   PostIdMaybeChange(aNamespaceID, aName, &valueForAfterSetAttr);
2448 
2449   // If the old value owns its own data, we know it is OK to keep using it.
2450   // oldValue will be null if there was no previously set value
2451   const nsAttrValue* oldValue;
2452   if (aParsedValue.StoresOwnData()) {
2453     if (oldValueSet) {
2454       oldValue = &aParsedValue;
2455     } else {
2456       oldValue = nullptr;
2457     }
2458   } else {
2459     // No need to conditionally assign null here. If there was no previously
2460     // set value for the attribute, aOldValue will already be null.
2461     oldValue = aOldValue;
2462   }
2463 
2464   if (HasElementCreatedFromPrototypeAndHasUnmodifiedL10n() &&
2465       aNamespaceID == kNameSpaceID_None &&
2466       (aName == nsGkAtoms::datal10nid || aName == nsGkAtoms::datal10nargs)) {
2467     ClearElementCreatedFromPrototypeAndHasUnmodifiedL10n();
2468     if (aComposedDocument) {
2469       aComposedDocument->mL10nProtoElements.Remove(this);
2470     }
2471   }
2472 
2473   const CustomElementData* data = GetCustomElementData();
2474   if (data && data->mState == CustomElementData::State::eCustom) {
2475     CustomElementDefinition* definition = data->GetCustomElementDefinition();
2476     MOZ_ASSERT(definition, "Should have a valid CustomElementDefinition");
2477 
2478     if (definition->IsInObservedAttributeList(aName)) {
2479       RefPtr<nsAtom> oldValueAtom;
2480       if (oldValue) {
2481         oldValueAtom = oldValue->GetAsAtom();
2482       } else {
2483         // If there is no old value, get the value of the uninitialized
2484         // attribute that was swapped with aParsedValue.
2485         oldValueAtom = aParsedValue.GetAsAtom();
2486       }
2487       RefPtr<nsAtom> newValueAtom = valueForAfterSetAttr.GetAsAtom();
2488       nsAutoString ns;
2489       nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
2490 
2491       LifecycleCallbackArgs args = {nsDependentAtomString(aName),
2492                                     aModType == MutationEvent_Binding::ADDITION
2493                                         ? VoidString()
2494                                         : nsDependentAtomString(oldValueAtom),
2495                                     nsDependentAtomString(newValueAtom),
2496                                     (ns.IsEmpty() ? VoidString() : ns)};
2497 
2498       nsContentUtils::EnqueueLifecycleCallback(
2499           ElementCallbackType::eAttributeChanged, this, &args, nullptr,
2500           definition);
2501     }
2502   }
2503 
2504   if (aCallAfterSetAttr) {
2505     rv = AfterSetAttr(aNamespaceID, aName, &valueForAfterSetAttr, oldValue,
2506                       aSubjectPrincipal, aNotify);
2507     NS_ENSURE_SUCCESS(rv, rv);
2508 
2509     if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
2510       OnSetDirAttr(this, &valueForAfterSetAttr, hadValidDir, hadDirAuto,
2511                    aNotify);
2512     }
2513   }
2514 
2515   UpdateState(aNotify);
2516 
2517   if (aNotify) {
2518     // Don't pass aOldValue to AttributeChanged since it may not be reliable.
2519     // Callers only compute aOldValue under certain conditions which may not
2520     // be triggered by all nsIMutationObservers.
2521     MutationObservers::NotifyAttributeChanged(
2522         this, aNamespaceID, aName, aModType,
2523         aParsedValue.StoresOwnData() ? &aParsedValue : nullptr);
2524   }
2525 
2526   if (aFireMutation) {
2527     InternalMutationEvent mutation(true, eLegacyAttrModified);
2528 
2529     nsAutoString ns;
2530     nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
2531     Attr* attrNode =
2532         GetAttributeNodeNSInternal(ns, nsDependentAtomString(aName));
2533     mutation.mRelatedNode = attrNode;
2534 
2535     mutation.mAttrName = aName;
2536     nsAutoString newValue;
2537     GetAttr(aNamespaceID, aName, newValue);
2538     if (!newValue.IsEmpty()) {
2539       mutation.mNewAttrValue = NS_Atomize(newValue);
2540     }
2541     if (oldValue && !oldValue->IsEmptyString()) {
2542       mutation.mPrevAttrValue = oldValue->GetAsAtom();
2543     }
2544     mutation.mAttrChange = aModType;
2545 
2546     mozAutoSubtreeModified subtree(OwnerDoc(), this);
2547     (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
2548   }
2549 
2550   return NS_OK;
2551 }
2552 
ParseAttribute(int32_t aNamespaceID,nsAtom * aAttribute,const nsAString & aValue,nsIPrincipal * aMaybeScriptedPrincipal,nsAttrValue & aResult)2553 bool Element::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
2554                              const nsAString& aValue,
2555                              nsIPrincipal* aMaybeScriptedPrincipal,
2556                              nsAttrValue& aResult) {
2557   if (aAttribute == nsGkAtoms::lang) {
2558     aResult.ParseAtom(aValue);
2559     return true;
2560   }
2561 
2562   if (aNamespaceID == kNameSpaceID_None) {
2563     if (aAttribute == nsGkAtoms::_class || aAttribute == nsGkAtoms::part) {
2564       aResult.ParseAtomArray(aValue);
2565       return true;
2566     }
2567 
2568     if (aAttribute == nsGkAtoms::exportparts) {
2569       aResult.ParsePartMapping(aValue);
2570       return true;
2571     }
2572 
2573     if (aAttribute == nsGkAtoms::id) {
2574       // Store id as an atom.  id="" means that the element has no id,
2575       // not that it has an emptystring as the id.
2576       if (aValue.IsEmpty()) {
2577         return false;
2578       }
2579       aResult.ParseAtom(aValue);
2580       return true;
2581     }
2582   }
2583 
2584   return false;
2585 }
2586 
SetAndSwapMappedAttribute(nsAtom * aName,nsAttrValue & aValue,bool * aValueWasSet,nsresult * aRetval)2587 bool Element::SetAndSwapMappedAttribute(nsAtom* aName, nsAttrValue& aValue,
2588                                         bool* aValueWasSet, nsresult* aRetval) {
2589   *aRetval = NS_OK;
2590   return false;
2591 }
2592 
BeforeSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString * aValue,bool aNotify)2593 nsresult Element::BeforeSetAttr(int32_t aNamespaceID, nsAtom* aName,
2594                                 const nsAttrValueOrString* aValue,
2595                                 bool aNotify) {
2596   if (aNamespaceID == kNameSpaceID_None) {
2597     if (aName == nsGkAtoms::_class && aValue) {
2598       // Note: This flag is asymmetrical. It is never unset and isn't exact.
2599       // If it is ever made to be exact, we probably need to handle this
2600       // similarly to how ids are handled in PreIdMaybeChange and
2601       // PostIdMaybeChange.
2602       // Note that SetSingleClassFromParser inlines BeforeSetAttr and
2603       // calls SetMayHaveClass directly. Making a subclass take action
2604       // on the class attribute in a BeforeSetAttr override would
2605       // require revising SetSingleClassFromParser.
2606       SetMayHaveClass();
2607     }
2608   }
2609 
2610   return NS_OK;
2611 }
2612 
AfterSetAttr(int32_t aNamespaceID,nsAtom * aName,const nsAttrValue * aValue,const nsAttrValue * aOldValue,nsIPrincipal * aMaybeScriptedPrincipal,bool aNotify)2613 nsresult Element::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
2614                                const nsAttrValue* aValue,
2615                                const nsAttrValue* aOldValue,
2616                                nsIPrincipal* aMaybeScriptedPrincipal,
2617                                bool aNotify) {
2618   if (aNamespaceID == kNameSpaceID_None) {
2619     if (aName == nsGkAtoms::part) {
2620       bool isPart = !!aValue;
2621       if (HasPartAttribute() != isPart) {
2622         SetHasPartAttribute(isPart);
2623         if (ShadowRoot* shadow = GetContainingShadow()) {
2624           if (isPart) {
2625             shadow->PartAdded(*this);
2626           } else {
2627             shadow->PartRemoved(*this);
2628           }
2629         }
2630       }
2631       MOZ_ASSERT(HasPartAttribute() == isPart);
2632     } else if (aName == nsGkAtoms::slot && GetParent()) {
2633       if (ShadowRoot* shadow = GetParent()->GetShadowRoot()) {
2634         shadow->MaybeReassignElement(*this);
2635       }
2636     }
2637   }
2638   return NS_OK;
2639 }
2640 
PreIdMaybeChange(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString * aValue)2641 void Element::PreIdMaybeChange(int32_t aNamespaceID, nsAtom* aName,
2642                                const nsAttrValueOrString* aValue) {
2643   if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) {
2644     return;
2645   }
2646   RemoveFromIdTable();
2647 }
2648 
PostIdMaybeChange(int32_t aNamespaceID,nsAtom * aName,const nsAttrValue * aValue)2649 void Element::PostIdMaybeChange(int32_t aNamespaceID, nsAtom* aName,
2650                                 const nsAttrValue* aValue) {
2651   if (aNamespaceID != kNameSpaceID_None || aName != nsGkAtoms::id) {
2652     return;
2653   }
2654 
2655   // id="" means that the element has no id, not that it has an empty
2656   // string as the id.
2657   if (aValue && !aValue->IsEmptyString()) {
2658     SetHasID();
2659     AddToIdTable(aValue->GetAtomValue());
2660   } else {
2661     ClearHasID();
2662   }
2663 }
2664 
OnAttrSetButNotChanged(int32_t aNamespaceID,nsAtom * aName,const nsAttrValueOrString & aValue,bool aNotify)2665 nsresult Element::OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName,
2666                                          const nsAttrValueOrString& aValue,
2667                                          bool aNotify) {
2668   const CustomElementData* data = GetCustomElementData();
2669   if (data && data->mState == CustomElementData::State::eCustom) {
2670     CustomElementDefinition* definition = data->GetCustomElementDefinition();
2671     MOZ_ASSERT(definition, "Should have a valid CustomElementDefinition");
2672 
2673     if (definition->IsInObservedAttributeList(aName)) {
2674       nsAutoString ns;
2675       nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNamespaceID, ns);
2676 
2677       nsAutoString value(aValue.String());
2678       LifecycleCallbackArgs args = {nsDependentAtomString(aName), value, value,
2679                                     (ns.IsEmpty() ? VoidString() : ns)};
2680 
2681       nsContentUtils::EnqueueLifecycleCallback(
2682           ElementCallbackType::eAttributeChanged, this, &args, nullptr,
2683           definition);
2684     }
2685   }
2686 
2687   return NS_OK;
2688 }
2689 
GetEventListenerManagerForAttr(nsAtom * aAttrName,bool * aDefer)2690 EventListenerManager* Element::GetEventListenerManagerForAttr(nsAtom* aAttrName,
2691                                                               bool* aDefer) {
2692   *aDefer = true;
2693   return GetOrCreateListenerManager();
2694 }
2695 
GetAttr(int32_t aNameSpaceID,const nsAtom * aName,nsAString & aResult) const2696 bool Element::GetAttr(int32_t aNameSpaceID, const nsAtom* aName,
2697                       nsAString& aResult) const {
2698   DOMString str;
2699   bool haveAttr = GetAttr(aNameSpaceID, aName, str);
2700   str.ToString(aResult);
2701   return haveAttr;
2702 }
2703 
FindAttrValueIn(int32_t aNameSpaceID,const nsAtom * aName,AttrValuesArray * aValues,nsCaseTreatment aCaseSensitive) const2704 int32_t Element::FindAttrValueIn(int32_t aNameSpaceID, const nsAtom* aName,
2705                                  AttrValuesArray* aValues,
2706                                  nsCaseTreatment aCaseSensitive) const {
2707   NS_ASSERTION(aName, "Must have attr name");
2708   NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace");
2709   NS_ASSERTION(aValues, "Null value array");
2710 
2711   const nsAttrValue* val = mAttrs.GetAttr(aName, aNameSpaceID);
2712   if (val) {
2713     for (int32_t i = 0; aValues[i]; ++i) {
2714       if (val->Equals(aValues[i], aCaseSensitive)) {
2715         return i;
2716       }
2717     }
2718     return ATTR_VALUE_NO_MATCH;
2719   }
2720   return ATTR_MISSING;
2721 }
2722 
UnsetAttr(int32_t aNameSpaceID,nsAtom * aName,bool aNotify)2723 nsresult Element::UnsetAttr(int32_t aNameSpaceID, nsAtom* aName, bool aNotify) {
2724   NS_ASSERTION(nullptr != aName, "must have attribute name");
2725 
2726   int32_t index = mAttrs.IndexOfAttr(aName, aNameSpaceID);
2727   if (index < 0) {
2728     return NS_OK;
2729   }
2730 
2731   Document* document = GetComposedDoc();
2732   mozAutoDocUpdate updateBatch(document, aNotify);
2733 
2734   if (aNotify) {
2735     MutationObservers::NotifyAttributeWillChange(
2736         this, aNameSpaceID, aName, MutationEvent_Binding::REMOVAL);
2737   }
2738 
2739   nsresult rv = BeforeSetAttr(aNameSpaceID, aName, nullptr, aNotify);
2740   NS_ENSURE_SUCCESS(rv, rv);
2741 
2742   bool hasMutationListeners =
2743       aNotify && nsContentUtils::HasMutationListeners(
2744                      this, NS_EVENT_BITS_MUTATION_ATTRMODIFIED, this);
2745 
2746   PreIdMaybeChange(aNameSpaceID, aName, nullptr);
2747 
2748   // Grab the attr node if needed before we remove it from the attr map
2749   RefPtr<Attr> attrNode;
2750   if (hasMutationListeners) {
2751     nsAutoString ns;
2752     nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns);
2753     attrNode = GetAttributeNodeNSInternal(ns, nsDependentAtomString(aName));
2754   }
2755 
2756   // Clear the attribute out from attribute map.
2757   nsDOMSlots* slots = GetExistingDOMSlots();
2758   if (slots && slots->mAttributeMap) {
2759     slots->mAttributeMap->DropAttribute(aNameSpaceID, aName);
2760   }
2761 
2762   // The id-handling code, and in the future possibly other code, need to
2763   // react to unexpected attribute changes.
2764   nsMutationGuard::DidMutate();
2765 
2766   bool hadValidDir = false;
2767   bool hadDirAuto = false;
2768 
2769   if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
2770     hadValidDir = HasValidDir() || IsHTMLElement(nsGkAtoms::bdi);
2771     hadDirAuto = HasDirAuto();  // already takes bdi into account
2772   }
2773 
2774   nsAttrValue oldValue;
2775   rv = mAttrs.RemoveAttrAt(index, oldValue);
2776   NS_ENSURE_SUCCESS(rv, rv);
2777 
2778   PostIdMaybeChange(aNameSpaceID, aName, nullptr);
2779 
2780   const CustomElementData* data = GetCustomElementData();
2781   if (data && data->mState == CustomElementData::State::eCustom) {
2782     CustomElementDefinition* definition = data->GetCustomElementDefinition();
2783     MOZ_ASSERT(definition, "Should have a valid CustomElementDefinition");
2784 
2785     if (definition->IsInObservedAttributeList(aName)) {
2786       nsAutoString ns;
2787       nsContentUtils::NameSpaceManager()->GetNameSpaceURI(aNameSpaceID, ns);
2788 
2789       RefPtr<nsAtom> oldValueAtom = oldValue.GetAsAtom();
2790       LifecycleCallbackArgs args = {
2791           nsDependentAtomString(aName), nsDependentAtomString(oldValueAtom),
2792           VoidString(), (ns.IsEmpty() ? VoidString() : ns)};
2793 
2794       nsContentUtils::EnqueueLifecycleCallback(
2795           ElementCallbackType::eAttributeChanged, this, &args, nullptr,
2796           definition);
2797     }
2798   }
2799 
2800   rv = AfterSetAttr(aNameSpaceID, aName, nullptr, &oldValue, nullptr, aNotify);
2801   NS_ENSURE_SUCCESS(rv, rv);
2802 
2803   UpdateState(aNotify);
2804 
2805   if (aNotify) {
2806     // We can always pass oldValue here since there is no new value which could
2807     // have corrupted it.
2808     MutationObservers::NotifyAttributeChanged(
2809         this, aNameSpaceID, aName, MutationEvent_Binding::REMOVAL, &oldValue);
2810   }
2811 
2812   if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::dir) {
2813     OnSetDirAttr(this, nullptr, hadValidDir, hadDirAuto, aNotify);
2814   }
2815 
2816   if (hasMutationListeners) {
2817     InternalMutationEvent mutation(true, eLegacyAttrModified);
2818 
2819     mutation.mRelatedNode = attrNode;
2820     mutation.mAttrName = aName;
2821 
2822     nsAutoString value;
2823     oldValue.ToString(value);
2824     if (!value.IsEmpty()) mutation.mPrevAttrValue = NS_Atomize(value);
2825     mutation.mAttrChange = MutationEvent_Binding::REMOVAL;
2826 
2827     mozAutoSubtreeModified subtree(OwnerDoc(), this);
2828     (new AsyncEventDispatcher(this, mutation))->RunDOMEventWhenSafe();
2829   }
2830 
2831   return NS_OK;
2832 }
2833 
DescribeAttribute(uint32_t index,nsAString & aOutDescription) const2834 void Element::DescribeAttribute(uint32_t index,
2835                                 nsAString& aOutDescription) const {
2836   // name
2837   mAttrs.AttrNameAt(index)->GetQualifiedName(aOutDescription);
2838 
2839   // value
2840   aOutDescription.AppendLiteral("=\"");
2841   nsAutoString value;
2842   mAttrs.AttrAt(index)->ToString(value);
2843   for (uint32_t i = value.Length(); i > 0; --i) {
2844     if (value[i - 1] == char16_t('"')) value.Insert(char16_t('\\'), i - 1);
2845   }
2846   aOutDescription.Append(value);
2847   aOutDescription.Append('"');
2848 }
2849 
2850 #ifdef MOZ_DOM_LIST
ListAttributes(FILE * out) const2851 void Element::ListAttributes(FILE* out) const {
2852   uint32_t index, count = mAttrs.AttrCount();
2853   for (index = 0; index < count; index++) {
2854     nsAutoString attributeDescription;
2855     DescribeAttribute(index, attributeDescription);
2856 
2857     fputs(" ", out);
2858     fputs(NS_LossyConvertUTF16toASCII(attributeDescription).get(), out);
2859   }
2860 }
2861 
List(FILE * out,int32_t aIndent,const nsCString & aPrefix) const2862 void Element::List(FILE* out, int32_t aIndent, const nsCString& aPrefix) const {
2863   int32_t indent;
2864   for (indent = aIndent; --indent >= 0;) fputs("  ", out);
2865 
2866   fputs(aPrefix.get(), out);
2867 
2868   fputs(NS_LossyConvertUTF16toASCII(mNodeInfo->QualifiedName()).get(), out);
2869 
2870   fprintf(out, "@%p", (void*)this);
2871 
2872   ListAttributes(out);
2873 
2874   fprintf(out, " state=[%llx]",
2875           static_cast<unsigned long long>(State().GetInternalValue()));
2876   fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
2877   if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
2878     const LinkedList<nsRange>* ranges =
2879         GetExistingClosestCommonInclusiveAncestorRanges();
2880     int32_t count = 0;
2881     if (ranges) {
2882       // Can't use range-based iteration on a const LinkedList, unfortunately.
2883       for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) {
2884         ++count;
2885       }
2886     }
2887     fprintf(out, " ranges:%d", count);
2888   }
2889   fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
2890   fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get());
2891 
2892   nsIContent* child = GetFirstChild();
2893   if (child) {
2894     fputs("\n", out);
2895 
2896     for (; child; child = child->GetNextSibling()) {
2897       child->List(out, aIndent + 1);
2898     }
2899 
2900     for (indent = aIndent; --indent >= 0;) fputs("  ", out);
2901   }
2902 
2903   fputs(">\n", out);
2904 }
2905 
DumpContent(FILE * out,int32_t aIndent,bool aDumpAll) const2906 void Element::DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const {
2907   int32_t indent;
2908   for (indent = aIndent; --indent >= 0;) fputs("  ", out);
2909 
2910   const nsString& buf = mNodeInfo->QualifiedName();
2911   fputs("<", out);
2912   fputs(NS_LossyConvertUTF16toASCII(buf).get(), out);
2913 
2914   if (aDumpAll) ListAttributes(out);
2915 
2916   fputs(">", out);
2917 
2918   if (aIndent) fputs("\n", out);
2919 
2920   for (nsIContent* child = GetFirstChild(); child;
2921        child = child->GetNextSibling()) {
2922     int32_t indent = aIndent ? aIndent + 1 : 0;
2923     child->DumpContent(out, indent, aDumpAll);
2924   }
2925   for (indent = aIndent; --indent >= 0;) fputs("  ", out);
2926   fputs("</", out);
2927   fputs(NS_LossyConvertUTF16toASCII(buf).get(), out);
2928   fputs(">", out);
2929 
2930   if (aIndent) fputs("\n", out);
2931 }
2932 #endif
2933 
Describe(nsAString & aOutDescription) const2934 void Element::Describe(nsAString& aOutDescription) const {
2935   aOutDescription.Append(mNodeInfo->QualifiedName());
2936   aOutDescription.AppendPrintf("@%p", (void*)this);
2937 
2938   uint32_t index, count = mAttrs.AttrCount();
2939   for (index = 0; index < count; index++) {
2940     aOutDescription.Append(' ');
2941     nsAutoString attributeDescription;
2942     DescribeAttribute(index, attributeDescription);
2943     aOutDescription.Append(attributeDescription);
2944   }
2945 }
2946 
CheckHandleEventForLinksPrecondition(EventChainVisitor & aVisitor,nsIURI ** aURI) const2947 bool Element::CheckHandleEventForLinksPrecondition(EventChainVisitor& aVisitor,
2948                                                    nsIURI** aURI) const {
2949   if (aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
2950       (!aVisitor.mEvent->IsTrusted() &&
2951        (aVisitor.mEvent->mMessage != eMouseClick) &&
2952        (aVisitor.mEvent->mMessage != eKeyPress) &&
2953        (aVisitor.mEvent->mMessage != eLegacyDOMActivate)) ||
2954       aVisitor.mEvent->mFlags.mMultipleActionsPrevented) {
2955     return false;
2956   }
2957 
2958   // Make sure we actually are a link
2959   return IsLink(aURI);
2960 }
2961 
GetEventTargetParentForLinks(EventChainPreVisitor & aVisitor)2962 void Element::GetEventTargetParentForLinks(EventChainPreVisitor& aVisitor) {
2963   // Optimisation: return early if this event doesn't interest us.
2964   // IMPORTANT: this switch and the switch below it must be kept in sync!
2965   switch (aVisitor.mEvent->mMessage) {
2966     case eMouseOver:
2967     case eFocus:
2968     case eMouseOut:
2969     case eBlur:
2970       break;
2971     default:
2972       return;
2973   }
2974 
2975   // Make sure we meet the preconditions before continuing
2976   nsCOMPtr<nsIURI> absURI;
2977   if (!CheckHandleEventForLinksPrecondition(aVisitor, getter_AddRefs(absURI))) {
2978     return;
2979   }
2980 
2981   // We do the status bar updates in GetEventTargetParent so that the status bar
2982   // gets updated even if the event is consumed before we have a chance to set
2983   // it.
2984   switch (aVisitor.mEvent->mMessage) {
2985     // Set the status bar similarly for mouseover and focus
2986     case eMouseOver:
2987       aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
2988       [[fallthrough]];
2989     case eFocus: {
2990       InternalFocusEvent* focusEvent = aVisitor.mEvent->AsFocusEvent();
2991       if (!focusEvent || !focusEvent->mIsRefocus) {
2992         nsAutoString target;
2993         GetLinkTarget(target);
2994         nsContentUtils::TriggerLink(this, absURI, target,
2995                                     /* click */ false, /* isTrusted */ true);
2996         // Make sure any ancestor links don't also TriggerLink
2997         aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
2998       }
2999       break;
3000     }
3001     case eMouseOut:
3002       aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
3003       [[fallthrough]];
3004     case eBlur: {
3005       nsresult rv = LeaveLink(aVisitor.mPresContext);
3006       if (NS_SUCCEEDED(rv)) {
3007         aVisitor.mEvent->mFlags.mMultipleActionsPrevented = true;
3008       }
3009       break;
3010     }
3011 
3012     default:
3013       // switch not in sync with the optimization switch earlier in this
3014       // function
3015       MOZ_ASSERT_UNREACHABLE("switch statements not in sync");
3016   }
3017 }
3018 
PostHandleEventForLinks(EventChainPostVisitor & aVisitor)3019 nsresult Element::PostHandleEventForLinks(EventChainPostVisitor& aVisitor) {
3020   // Optimisation: return early if this event doesn't interest us.
3021   // IMPORTANT: this switch and the switch below it must be kept in sync!
3022   switch (aVisitor.mEvent->mMessage) {
3023     case eMouseDown:
3024     case eMouseClick:
3025     case eLegacyDOMActivate:
3026     case eKeyPress:
3027       break;
3028     default:
3029       return NS_OK;
3030   }
3031 
3032   // Make sure we meet the preconditions before continuing
3033   nsCOMPtr<nsIURI> absURI;
3034   if (!CheckHandleEventForLinksPrecondition(aVisitor, getter_AddRefs(absURI))) {
3035     return NS_OK;
3036   }
3037 
3038   nsresult rv = NS_OK;
3039 
3040   switch (aVisitor.mEvent->mMessage) {
3041     case eMouseDown: {
3042       if (!OwnerDoc()->LinkHandlingEnabled()) {
3043         break;
3044       }
3045 
3046       WidgetMouseEvent* const mouseEvent = aVisitor.mEvent->AsMouseEvent();
3047       mouseEvent->mFlags.mMultipleActionsPrevented |=
3048           mouseEvent->mButton == MouseButton::ePrimary ||
3049           mouseEvent->mButton == MouseButton::eMiddle;
3050 
3051       if (mouseEvent->mButton == MouseButton::ePrimary) {
3052         if (IsInComposedDoc()) {
3053           if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
3054             RefPtr<Element> kungFuDeathGrip(this);
3055             fm->SetFocus(kungFuDeathGrip, nsIFocusManager::FLAG_BYMOUSE |
3056                                               nsIFocusManager::FLAG_NOSCROLL);
3057           }
3058         }
3059 
3060         if (aVisitor.mPresContext) {
3061           EventStateManager::SetActiveManager(
3062               aVisitor.mPresContext->EventStateManager(), this);
3063         }
3064 
3065         // OK, we're pretty sure we're going to load, so warm up a speculative
3066         // connection to be sure we have one ready when we open the channel.
3067         if (nsIDocShell* shell = OwnerDoc()->GetDocShell()) {
3068           nsCOMPtr<nsISpeculativeConnect> sc =
3069               do_QueryInterface(nsContentUtils::GetIOService());
3070           nsCOMPtr<nsIInterfaceRequestor> ir = do_QueryInterface(shell);
3071           sc->SpeculativeConnect(absURI, NodePrincipal(), ir);
3072         }
3073       }
3074     } break;
3075 
3076     case eMouseClick: {
3077       WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
3078       if (mouseEvent->IsLeftClickEvent()) {
3079         if (mouseEvent->IsControl() || mouseEvent->IsMeta() ||
3080             mouseEvent->IsAlt() || mouseEvent->IsShift()) {
3081           break;
3082         }
3083 
3084         // The default action is simply to dispatch DOMActivate
3085         nsEventStatus status = nsEventStatus_eIgnore;
3086         // DOMActive event should be trusted since the activation is actually
3087         // occurred even if the cause is an untrusted click event.
3088         InternalUIEvent actEvent(true, eLegacyDOMActivate, mouseEvent);
3089         actEvent.mDetail = 1;
3090 
3091         rv = EventDispatcher::Dispatch(this, aVisitor.mPresContext, &actEvent,
3092                                        nullptr, &status);
3093         if (NS_SUCCEEDED(rv)) {
3094           aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
3095         }
3096       }
3097       break;
3098     }
3099     case eLegacyDOMActivate: {
3100       if (aVisitor.mEvent->mOriginalTarget == this) {
3101         nsAutoString target;
3102         GetLinkTarget(target);
3103         const InternalUIEvent* activeEvent = aVisitor.mEvent->AsUIEvent();
3104         MOZ_ASSERT(activeEvent);
3105         nsContentUtils::TriggerLink(this, absURI, target, /* click */ true,
3106                                     activeEvent->IsTrustable());
3107         aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
3108       }
3109     } break;
3110 
3111     case eKeyPress: {
3112       WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
3113       if (keyEvent && keyEvent->mKeyCode == NS_VK_RETURN) {
3114         nsEventStatus status = nsEventStatus_eIgnore;
3115         rv = DispatchClickEvent(aVisitor.mPresContext, keyEvent, this, false,
3116                                 nullptr, &status);
3117         if (NS_SUCCEEDED(rv)) {
3118           aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
3119         }
3120       }
3121     } break;
3122 
3123     default:
3124       // switch not in sync with the optimization switch earlier in this
3125       // function
3126       MOZ_ASSERT_UNREACHABLE("switch statements not in sync");
3127       return NS_ERROR_UNEXPECTED;
3128   }
3129 
3130   return rv;
3131 }
3132 
GetLinkTarget(nsAString & aTarget)3133 void Element::GetLinkTarget(nsAString& aTarget) { aTarget.Truncate(); }
3134 
3135 static nsStaticAtom* const sPropertiesToTraverseAndUnlink[] = {
3136     nsGkAtoms::dirAutoSetBy, nullptr};
3137 
3138 // static
HTMLSVGPropertiesToTraverseAndUnlink()3139 nsStaticAtom* const* Element::HTMLSVGPropertiesToTraverseAndUnlink() {
3140   return sPropertiesToTraverseAndUnlink;
3141 }
3142 
CopyInnerTo(Element * aDst,ReparseAttributes aReparse)3143 nsresult Element::CopyInnerTo(Element* aDst, ReparseAttributes aReparse) {
3144   nsresult rv = aDst->mAttrs.EnsureCapacityToClone(mAttrs);
3145   NS_ENSURE_SUCCESS(rv, rv);
3146 
3147   const bool reparse = aReparse == ReparseAttributes::Yes;
3148 
3149   uint32_t count = mAttrs.AttrCount();
3150   for (uint32_t i = 0; i < count; ++i) {
3151     BorrowedAttrInfo info = mAttrs.AttrInfoAt(i);
3152     const nsAttrName* name = info.mName;
3153     const nsAttrValue* value = info.mValue;
3154     if (value->Type() == nsAttrValue::eCSSDeclaration) {
3155       MOZ_ASSERT(name->Equals(nsGkAtoms::style, kNameSpaceID_None));
3156       // We still clone CSS attributes, even in the `reparse` (cross-document)
3157       // case.  https://github.com/w3c/webappsec-csp/issues/212
3158       nsAttrValue valueCopy(*value);
3159       rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(),
3160                                name->GetPrefix(), valueCopy, false);
3161       NS_ENSURE_SUCCESS(rv, rv);
3162 
3163       value->GetCSSDeclarationValue()->SetImmutable();
3164     } else if (reparse) {
3165       nsAutoString valStr;
3166       value->ToString(valStr);
3167       rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
3168                          name->GetPrefix(), valStr, false);
3169       NS_ENSURE_SUCCESS(rv, rv);
3170     } else {
3171       nsAttrValue valueCopy(*value);
3172       rv = aDst->SetParsedAttr(name->NamespaceID(), name->LocalName(),
3173                                name->GetPrefix(), valueCopy, false);
3174       NS_ENSURE_SUCCESS(rv, rv);
3175     }
3176   }
3177 
3178   dom::NodeInfo* dstNodeInfo = aDst->NodeInfo();
3179   if (CustomElementData* data = GetCustomElementData()) {
3180     // The cloned node may be a custom element that may require
3181     // enqueing upgrade reaction.
3182     if (nsAtom* typeAtom = data->GetCustomElementType()) {
3183       aDst->SetCustomElementData(new CustomElementData(typeAtom));
3184       MOZ_ASSERT(dstNodeInfo->NameAtom()->Equals(dstNodeInfo->LocalName()));
3185       CustomElementDefinition* definition =
3186           nsContentUtils::LookupCustomElementDefinition(
3187               dstNodeInfo->GetDocument(), dstNodeInfo->NameAtom(),
3188               dstNodeInfo->NamespaceID(), typeAtom);
3189       if (definition) {
3190         nsContentUtils::EnqueueUpgradeReaction(aDst, definition);
3191       }
3192     }
3193   }
3194 
3195   if (dstNodeInfo->GetDocument()->IsStaticDocument()) {
3196     // Propagate :defined state to the static clone.
3197     if (State().HasState(NS_EVENT_STATE_DEFINED)) {
3198       aDst->SetDefined(true);
3199     }
3200   }
3201 
3202   return NS_OK;
3203 }
3204 
Closest(const nsACString & aSelector,ErrorResult & aResult)3205 Element* Element::Closest(const nsACString& aSelector, ErrorResult& aResult) {
3206   const RawServoSelectorList* list = ParseSelectorList(aSelector, aResult);
3207   if (!list) {
3208     return nullptr;
3209   }
3210 
3211   return const_cast<Element*>(Servo_SelectorList_Closest(this, list));
3212 }
3213 
Matches(const nsACString & aSelector,ErrorResult & aResult)3214 bool Element::Matches(const nsACString& aSelector, ErrorResult& aResult) {
3215   const RawServoSelectorList* list = ParseSelectorList(aSelector, aResult);
3216   if (!list) {
3217     return false;
3218   }
3219 
3220   return Servo_SelectorList_Matches(this, list);
3221 }
3222 
3223 static const nsAttrValue::EnumTable kCORSAttributeTable[] = {
3224     // Order matters here
3225     // See ParseCORSValue
3226     {"anonymous", CORS_ANONYMOUS},
3227     {"use-credentials", CORS_USE_CREDENTIALS},
3228     {nullptr, 0}};
3229 
3230 /* static */
ParseCORSValue(const nsAString & aValue,nsAttrValue & aResult)3231 void Element::ParseCORSValue(const nsAString& aValue, nsAttrValue& aResult) {
3232   DebugOnly<bool> success =
3233       aResult.ParseEnumValue(aValue, kCORSAttributeTable, false,
3234                              // default value is anonymous if aValue is
3235                              // not a value we understand
3236                              &kCORSAttributeTable[0]);
3237   MOZ_ASSERT(success);
3238 }
3239 
3240 /* static */
StringToCORSMode(const nsAString & aValue)3241 CORSMode Element::StringToCORSMode(const nsAString& aValue) {
3242   if (aValue.IsVoid()) {
3243     return CORS_NONE;
3244   }
3245 
3246   nsAttrValue val;
3247   Element::ParseCORSValue(aValue, val);
3248   return CORSMode(val.GetEnumValue());
3249 }
3250 
3251 /* static */
AttrValueToCORSMode(const nsAttrValue * aValue)3252 CORSMode Element::AttrValueToCORSMode(const nsAttrValue* aValue) {
3253   if (!aValue) {
3254     return CORS_NONE;
3255   }
3256 
3257   return CORSMode(aValue->GetEnumValue());
3258 }
3259 
3260 /**
3261  * Returns nullptr if requests for fullscreen are allowed in the current
3262  * context. Requests are only allowed if the user initiated them (like with
3263  * a mouse-click or key press), unless this check has been disabled by
3264  * setting the pref "full-screen-api.allow-trusted-requests-only" to false
3265  * or if the caller is privileged. Feature policy may also deny requests.
3266  * If fullscreen is not allowed, a key for the error message is returned.
3267  */
GetFullscreenError(CallerType aCallerType,Document * aDocument)3268 static const char* GetFullscreenError(CallerType aCallerType,
3269                                       Document* aDocument) {
3270   MOZ_ASSERT(aDocument);
3271 
3272   // Privileged callers can always request fullscreen
3273   if (aCallerType == CallerType::System) {
3274     return nullptr;
3275   }
3276 
3277   if (const char* error = aDocument->GetFullscreenError(aCallerType)) {
3278     return error;
3279   }
3280 
3281   // Bypass user interaction checks if preference is set
3282   if (!StaticPrefs::full_screen_api_allow_trusted_requests_only()) {
3283     return nullptr;
3284   }
3285 
3286   if (!aDocument->ConsumeTransientUserGestureActivation()) {
3287     return "FullscreenDeniedNotInputDriven";
3288   }
3289 
3290   // Entering full-screen on mouse mouse event is only allowed with left mouse
3291   // button
3292   if (StaticPrefs::full_screen_api_mouse_event_allow_left_button_only() &&
3293       (EventStateManager::sCurrentMouseBtn == MouseButton::eMiddle ||
3294        EventStateManager::sCurrentMouseBtn == MouseButton::eSecondary)) {
3295     return "FullscreenDeniedMouseEventOnlyLeftBtn";
3296   }
3297 
3298   return nullptr;
3299 }
3300 
SetCapture(bool aRetargetToElement)3301 void Element::SetCapture(bool aRetargetToElement) {
3302   // If there is already an active capture, ignore this request. This would
3303   // occur if a splitter, frame resizer, etc had already captured and we don't
3304   // want to override those.
3305   if (!PresShell::GetCapturingContent()) {
3306     PresShell::SetCapturingContent(
3307         this, CaptureFlags::PreventDragStart |
3308                   (aRetargetToElement ? CaptureFlags::RetargetToElement
3309                                       : CaptureFlags::None));
3310   }
3311 }
3312 
SetCaptureAlways(bool aRetargetToElement)3313 void Element::SetCaptureAlways(bool aRetargetToElement) {
3314   PresShell::SetCapturingContent(
3315       this, CaptureFlags::PreventDragStart | CaptureFlags::IgnoreAllowedState |
3316                 (aRetargetToElement ? CaptureFlags::RetargetToElement
3317                                     : CaptureFlags::None));
3318 }
3319 
ReleaseCapture()3320 void Element::ReleaseCapture() {
3321   if (PresShell::GetCapturingContent() == this) {
3322     PresShell::ReleaseCapturingContent();
3323   }
3324 }
3325 
RequestFullscreen(CallerType aCallerType,ErrorResult & aRv)3326 already_AddRefed<Promise> Element::RequestFullscreen(CallerType aCallerType,
3327                                                      ErrorResult& aRv) {
3328   auto request = FullscreenRequest::Create(this, aCallerType, aRv);
3329   RefPtr<Promise> promise = request->GetPromise();
3330 
3331   // Only grant fullscreen requests if this is called from inside a trusted
3332   // event handler (i.e. inside an event handler for a user initiated event).
3333   // This stops the fullscreen from being abused similar to the popups of old,
3334   // and it also makes it harder for bad guys' script to go fullscreen and
3335   // spoof the browser chrome/window and phish logins etc.
3336   // Note that requests for fullscreen inside a web app's origin are exempt
3337   // from this restriction.
3338   if (const char* error = GetFullscreenError(aCallerType, OwnerDoc())) {
3339     request->Reject(error);
3340   } else {
3341     OwnerDoc()->AsyncRequestFullscreen(std::move(request));
3342   }
3343   return promise.forget();
3344 }
3345 
RequestPointerLock(CallerType aCallerType)3346 void Element::RequestPointerLock(CallerType aCallerType) {
3347   PointerLockManager::RequestLock(this, aCallerType);
3348 }
3349 
GetAsFlexContainer()3350 already_AddRefed<Flex> Element::GetAsFlexContainer() {
3351   // We need the flex frame to compute additional info, and use
3352   // that annotated version of the frame.
3353   nsFlexContainerFrame* flexFrame =
3354       nsFlexContainerFrame::GetFlexFrameWithComputedInfo(
3355           GetPrimaryFrame(FlushType::Layout));
3356 
3357   if (flexFrame) {
3358     RefPtr<Flex> flex = new Flex(this, flexFrame);
3359     return flex.forget();
3360   }
3361   return nullptr;
3362 }
3363 
GetGridFragments(nsTArray<RefPtr<Grid>> & aResult)3364 void Element::GetGridFragments(nsTArray<RefPtr<Grid>>& aResult) {
3365   nsGridContainerFrame* frame =
3366       nsGridContainerFrame::GetGridFrameWithComputedInfo(
3367           GetPrimaryFrame(FlushType::Layout));
3368 
3369   // If we get a nsGridContainerFrame from the prior call,
3370   // all the next-in-flow frames will also be nsGridContainerFrames.
3371   while (frame) {
3372     aResult.AppendElement(new Grid(this, frame));
3373     frame = static_cast<nsGridContainerFrame*>(frame->GetNextInFlow());
3374   }
3375 }
3376 
HasGridFragments()3377 bool Element::HasGridFragments() {
3378   return !!nsGridContainerFrame::GetGridFrameWithComputedInfo(
3379       GetPrimaryFrame(FlushType::Layout));
3380 }
3381 
GetTransformToAncestor(Element & aAncestor)3382 already_AddRefed<DOMMatrixReadOnly> Element::GetTransformToAncestor(
3383     Element& aAncestor) {
3384   nsIFrame* primaryFrame = GetPrimaryFrame();
3385   nsIFrame* ancestorFrame = aAncestor.GetPrimaryFrame();
3386 
3387   Matrix4x4 transform;
3388   if (primaryFrame) {
3389     // If aAncestor is not actually an ancestor of this (including nullptr),
3390     // then the call to GetTransformToAncestor will return the transform
3391     // all the way up through the parent chain.
3392     transform = nsLayoutUtils::GetTransformToAncestor(RelativeTo{primaryFrame},
3393                                                       RelativeTo{ancestorFrame},
3394                                                       nsIFrame::IN_CSS_UNITS)
3395                     .GetMatrix();
3396   }
3397 
3398   DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
3399   RefPtr<DOMMatrixReadOnly> result(matrix);
3400   return result.forget();
3401 }
3402 
GetTransformToParent()3403 already_AddRefed<DOMMatrixReadOnly> Element::GetTransformToParent() {
3404   nsIFrame* primaryFrame = GetPrimaryFrame();
3405 
3406   Matrix4x4 transform;
3407   if (primaryFrame) {
3408     nsIFrame* parentFrame = primaryFrame->GetParent();
3409     transform = nsLayoutUtils::GetTransformToAncestor(RelativeTo{primaryFrame},
3410                                                       RelativeTo{parentFrame},
3411                                                       nsIFrame::IN_CSS_UNITS)
3412                     .GetMatrix();
3413   }
3414 
3415   DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
3416   RefPtr<DOMMatrixReadOnly> result(matrix);
3417   return result.forget();
3418 }
3419 
GetTransformToViewport()3420 already_AddRefed<DOMMatrixReadOnly> Element::GetTransformToViewport() {
3421   nsIFrame* primaryFrame = GetPrimaryFrame();
3422   Matrix4x4 transform;
3423   if (primaryFrame) {
3424     transform =
3425         nsLayoutUtils::GetTransformToAncestor(
3426             RelativeTo{primaryFrame},
3427             RelativeTo{nsLayoutUtils::GetDisplayRootFrame(primaryFrame)},
3428             nsIFrame::IN_CSS_UNITS)
3429             .GetMatrix();
3430   }
3431 
3432   DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
3433   RefPtr<DOMMatrixReadOnly> result(matrix);
3434   return result.forget();
3435 }
3436 
Animate(JSContext * aContext,JS::Handle<JSObject * > aKeyframes,const UnrestrictedDoubleOrKeyframeAnimationOptions & aOptions,ErrorResult & aError)3437 already_AddRefed<Animation> Element::Animate(
3438     JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
3439     const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
3440     ErrorResult& aError) {
3441   nsCOMPtr<nsIGlobalObject> ownerGlobal = GetOwnerGlobal();
3442   if (!ownerGlobal) {
3443     aError.Throw(NS_ERROR_FAILURE);
3444     return nullptr;
3445   }
3446   GlobalObject global(aContext, ownerGlobal->GetGlobalJSObject());
3447   MOZ_ASSERT(!global.Failed());
3448 
3449   // KeyframeEffect constructor doesn't follow the standard Xray calling
3450   // convention and needs to be called in caller's compartment.
3451   // This should match to RunConstructorInCallerCompartment attribute in
3452   // KeyframeEffect.webidl.
3453   RefPtr<KeyframeEffect> effect =
3454       KeyframeEffect::Constructor(global, this, aKeyframes, aOptions, aError);
3455   if (aError.Failed()) {
3456     return nullptr;
3457   }
3458 
3459   // Animation constructor follows the standard Xray calling convention and
3460   // needs to be called in the target element's realm.
3461   JSAutoRealm ar(aContext, global.Get());
3462 
3463   AnimationTimeline* timeline = OwnerDoc()->Timeline();
3464   RefPtr<Animation> animation = Animation::Constructor(
3465       global, effect, Optional<AnimationTimeline*>(timeline), aError);
3466   if (aError.Failed()) {
3467     return nullptr;
3468   }
3469 
3470   if (aOptions.IsKeyframeAnimationOptions()) {
3471     animation->SetId(aOptions.GetAsKeyframeAnimationOptions().mId);
3472   }
3473 
3474   animation->Play(aError, Animation::LimitBehavior::AutoRewind);
3475   if (aError.Failed()) {
3476     return nullptr;
3477   }
3478 
3479   return animation.forget();
3480 }
3481 
GetAnimations(const GetAnimationsOptions & aOptions,nsTArray<RefPtr<Animation>> & aAnimations)3482 void Element::GetAnimations(const GetAnimationsOptions& aOptions,
3483                             nsTArray<RefPtr<Animation>>& aAnimations) {
3484   if (Document* doc = GetComposedDoc()) {
3485     // We don't need to explicitly flush throttled animations here, since
3486     // updating the animation style of elements will never affect the set of
3487     // running animations and it's only the set of running animations that is
3488     // important here.
3489     //
3490     // NOTE: Any changes to the flags passed to the following call should
3491     // be reflected in the flags passed in DocumentOrShadowRoot::GetAnimations
3492     // too.
3493     doc->FlushPendingNotifications(
3494         ChangesToFlush(FlushType::Style, false /* flush animations */));
3495   }
3496 
3497   GetAnimationsWithoutFlush(aOptions, aAnimations);
3498 }
3499 
GetAnimationsWithoutFlush(const GetAnimationsOptions & aOptions,nsTArray<RefPtr<Animation>> & aAnimations)3500 void Element::GetAnimationsWithoutFlush(
3501     const GetAnimationsOptions& aOptions,
3502     nsTArray<RefPtr<Animation>>& aAnimations) {
3503   Element* elem = this;
3504   PseudoStyleType pseudoType = PseudoStyleType::NotPseudo;
3505   // For animations on generated-content elements, the animations are stored
3506   // on the parent element.
3507   if (IsGeneratedContentContainerForBefore()) {
3508     elem = GetParentElement();
3509     pseudoType = PseudoStyleType::before;
3510   } else if (IsGeneratedContentContainerForAfter()) {
3511     elem = GetParentElement();
3512     pseudoType = PseudoStyleType::after;
3513   } else if (IsGeneratedContentContainerForMarker()) {
3514     elem = GetParentElement();
3515     pseudoType = PseudoStyleType::marker;
3516   }
3517 
3518   if (!elem) {
3519     return;
3520   }
3521 
3522   if (!aOptions.mSubtree || pseudoType == PseudoStyleType::before ||
3523       pseudoType == PseudoStyleType::after ||
3524       pseudoType == PseudoStyleType::marker) {
3525     GetAnimationsUnsorted(elem, pseudoType, aAnimations);
3526   } else {
3527     for (nsIContent* node = this; node; node = node->GetNextNode(this)) {
3528       if (!node->IsElement()) {
3529         continue;
3530       }
3531       Element* element = node->AsElement();
3532       Element::GetAnimationsUnsorted(element, PseudoStyleType::NotPseudo,
3533                                      aAnimations);
3534       Element::GetAnimationsUnsorted(element, PseudoStyleType::before,
3535                                      aAnimations);
3536       Element::GetAnimationsUnsorted(element, PseudoStyleType::after,
3537                                      aAnimations);
3538       Element::GetAnimationsUnsorted(element, PseudoStyleType::marker,
3539                                      aAnimations);
3540     }
3541   }
3542   aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
3543 }
3544 
3545 /* static */
GetAnimationsUnsorted(Element * aElement,PseudoStyleType aPseudoType,nsTArray<RefPtr<Animation>> & aAnimations)3546 void Element::GetAnimationsUnsorted(Element* aElement,
3547                                     PseudoStyleType aPseudoType,
3548                                     nsTArray<RefPtr<Animation>>& aAnimations) {
3549   MOZ_ASSERT(aPseudoType == PseudoStyleType::NotPseudo ||
3550                  aPseudoType == PseudoStyleType::after ||
3551                  aPseudoType == PseudoStyleType::before ||
3552                  aPseudoType == PseudoStyleType::marker,
3553              "Unsupported pseudo type");
3554   MOZ_ASSERT(aElement, "Null element");
3555 
3556   EffectSet* effects = EffectSet::GetEffectSet(aElement, aPseudoType);
3557   if (!effects) {
3558     return;
3559   }
3560 
3561   for (KeyframeEffect* effect : *effects) {
3562     MOZ_ASSERT(effect && effect->GetAnimation(),
3563                "Only effects associated with an animation should be "
3564                "added to an element's effect set");
3565     Animation* animation = effect->GetAnimation();
3566 
3567     MOZ_ASSERT(animation->IsRelevant(),
3568                "Only relevant animations should be added to an element's "
3569                "effect set");
3570     aAnimations.AppendElement(animation);
3571   }
3572 }
3573 
CloneAnimationsFrom(const Element & aOther)3574 void Element::CloneAnimationsFrom(const Element& aOther) {
3575   AnimationTimeline* const timeline = OwnerDoc()->Timeline();
3576   MOZ_ASSERT(timeline, "Timeline has not been set on the document yet");
3577   // Iterate through all pseudo types and copy the effects from each of the
3578   // other element's effect sets into this element's effect set.
3579   for (PseudoStyleType pseudoType :
3580        {PseudoStyleType::NotPseudo, PseudoStyleType::before,
3581         PseudoStyleType::after, PseudoStyleType::marker}) {
3582     // If the element has an effect set for this pseudo type (or not pseudo)
3583     // then copy the effects and animation properties.
3584     if (EffectSet* const effects =
3585             EffectSet::GetEffectSet(&aOther, pseudoType)) {
3586       EffectSet* const clonedEffects =
3587           EffectSet::GetOrCreateEffectSet(this, pseudoType);
3588       for (KeyframeEffect* const effect : *effects) {
3589         // Clone the effect.
3590         RefPtr<KeyframeEffect> clonedEffect = new KeyframeEffect(
3591             OwnerDoc(), OwningAnimationTarget{this, pseudoType}, *effect);
3592 
3593         // Clone the animation
3594         RefPtr<Animation> clonedAnimation = Animation::ClonePausedAnimation(
3595             OwnerDoc()->GetParentObject(), *effect->GetAnimation(),
3596             *clonedEffect, *timeline);
3597         clonedEffects->AddEffect(*clonedEffect);
3598       }
3599     }
3600   }
3601 }
3602 
GetInnerHTML(nsAString & aInnerHTML,OOMReporter & aError)3603 void Element::GetInnerHTML(nsAString& aInnerHTML, OOMReporter& aError) {
3604   GetMarkup(false, aInnerHTML);
3605 }
3606 
SetInnerHTML(const nsAString & aInnerHTML,nsIPrincipal * aSubjectPrincipal,ErrorResult & aError)3607 void Element::SetInnerHTML(const nsAString& aInnerHTML,
3608                            nsIPrincipal* aSubjectPrincipal,
3609                            ErrorResult& aError) {
3610   SetInnerHTMLInternal(aInnerHTML, aError);
3611 }
3612 
GetOuterHTML(nsAString & aOuterHTML)3613 void Element::GetOuterHTML(nsAString& aOuterHTML) {
3614   GetMarkup(true, aOuterHTML);
3615 }
3616 
SetOuterHTML(const nsAString & aOuterHTML,ErrorResult & aError)3617 void Element::SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError) {
3618   nsCOMPtr<nsINode> parent = GetParentNode();
3619   if (!parent) {
3620     return;
3621   }
3622 
3623   if (parent->NodeType() == DOCUMENT_NODE) {
3624     aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
3625     return;
3626   }
3627 
3628   if (OwnerDoc()->IsHTMLDocument()) {
3629     nsAtom* localName;
3630     int32_t namespaceID;
3631     if (parent->IsElement()) {
3632       localName = parent->NodeInfo()->NameAtom();
3633       namespaceID = parent->NodeInfo()->NamespaceID();
3634     } else {
3635       NS_ASSERTION(
3636           parent->NodeType() == DOCUMENT_FRAGMENT_NODE,
3637           "How come the parent isn't a document, a fragment or an element?");
3638       localName = nsGkAtoms::body;
3639       namespaceID = kNameSpaceID_XHTML;
3640     }
3641     RefPtr<DocumentFragment> fragment = new (OwnerDoc()->NodeInfoManager())
3642         DocumentFragment(OwnerDoc()->NodeInfoManager());
3643     nsContentUtils::ParseFragmentHTML(
3644         aOuterHTML, fragment, localName, namespaceID,
3645         OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
3646     parent->ReplaceChild(*fragment, *this, aError);
3647     return;
3648   }
3649 
3650   nsCOMPtr<nsINode> context;
3651   if (parent->IsElement()) {
3652     context = parent;
3653   } else {
3654     NS_ASSERTION(
3655         parent->NodeType() == DOCUMENT_FRAGMENT_NODE,
3656         "How come the parent isn't a document, a fragment or an element?");
3657     RefPtr<mozilla::dom::NodeInfo> info =
3658         OwnerDoc()->NodeInfoManager()->GetNodeInfo(
3659             nsGkAtoms::body, nullptr, kNameSpaceID_XHTML, ELEMENT_NODE);
3660     context = NS_NewHTMLBodyElement(info.forget(), FROM_PARSER_FRAGMENT);
3661   }
3662 
3663   RefPtr<DocumentFragment> fragment = nsContentUtils::CreateContextualFragment(
3664       context, aOuterHTML, true, aError);
3665   if (aError.Failed()) {
3666     return;
3667   }
3668   parent->ReplaceChild(*fragment, *this, aError);
3669 }
3670 
3671 enum nsAdjacentPosition { eBeforeBegin, eAfterBegin, eBeforeEnd, eAfterEnd };
3672 
InsertAdjacentHTML(const nsAString & aPosition,const nsAString & aText,ErrorResult & aError)3673 void Element::InsertAdjacentHTML(const nsAString& aPosition,
3674                                  const nsAString& aText, ErrorResult& aError) {
3675   nsAdjacentPosition position;
3676   if (aPosition.LowerCaseEqualsLiteral("beforebegin")) {
3677     position = eBeforeBegin;
3678   } else if (aPosition.LowerCaseEqualsLiteral("afterbegin")) {
3679     position = eAfterBegin;
3680   } else if (aPosition.LowerCaseEqualsLiteral("beforeend")) {
3681     position = eBeforeEnd;
3682   } else if (aPosition.LowerCaseEqualsLiteral("afterend")) {
3683     position = eAfterEnd;
3684   } else {
3685     aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
3686     return;
3687   }
3688 
3689   nsCOMPtr<nsIContent> destination;
3690   if (position == eBeforeBegin || position == eAfterEnd) {
3691     destination = GetParent();
3692     if (!destination) {
3693       aError.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
3694       return;
3695     }
3696   } else {
3697     destination = this;
3698   }
3699 
3700   Document* doc = OwnerDoc();
3701 
3702   // Needed when insertAdjacentHTML is used in combination with contenteditable
3703   mozAutoDocUpdate updateBatch(doc, true);
3704   nsAutoScriptLoaderDisabler sld(doc);
3705 
3706   // Batch possible DOMSubtreeModified events.
3707   mozAutoSubtreeModified subtree(doc, nullptr);
3708 
3709   // Parse directly into destination if possible
3710   if (doc->IsHTMLDocument() && !OwnerDoc()->MayHaveDOMMutationObservers() &&
3711       (position == eBeforeEnd || (position == eAfterEnd && !GetNextSibling()) ||
3712        (position == eAfterBegin && !GetFirstChild()))) {
3713     int32_t oldChildCount = destination->GetChildCount();
3714     int32_t contextNs = destination->GetNameSpaceID();
3715     nsAtom* contextLocal = destination->NodeInfo()->NameAtom();
3716     if (contextLocal == nsGkAtoms::html && contextNs == kNameSpaceID_XHTML) {
3717       // For compat with IE6 through IE9. Willful violation of HTML5 as of
3718       // 2011-04-06. CreateContextualFragment does the same already.
3719       // Spec bug: http://www.w3.org/Bugs/Public/show_bug.cgi?id=12434
3720       contextLocal = nsGkAtoms::body;
3721     }
3722     aError = nsContentUtils::ParseFragmentHTML(
3723         aText, destination, contextLocal, contextNs,
3724         doc->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
3725     // HTML5 parser has notified, but not fired mutation events.
3726     nsContentUtils::FireMutationEventsForDirectParsing(doc, destination,
3727                                                        oldChildCount);
3728     return;
3729   }
3730 
3731   // couldn't parse directly
3732   RefPtr<DocumentFragment> fragment = nsContentUtils::CreateContextualFragment(
3733       destination, aText, true, aError);
3734   if (aError.Failed()) {
3735     return;
3736   }
3737 
3738   // Suppress assertion about node removal mutation events that can't have
3739   // listeners anyway, because no one has had the chance to register mutation
3740   // listeners on the fragment that comes from the parser.
3741   nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
3742 
3743   nsAutoMutationBatch mb(destination, true, false);
3744   switch (position) {
3745     case eBeforeBegin:
3746       destination->InsertBefore(*fragment, this, aError);
3747       break;
3748     case eAfterBegin:
3749       static_cast<nsINode*>(this)->InsertBefore(*fragment, GetFirstChild(),
3750                                                 aError);
3751       break;
3752     case eBeforeEnd:
3753       static_cast<nsINode*>(this)->AppendChild(*fragment, aError);
3754       break;
3755     case eAfterEnd:
3756       destination->InsertBefore(*fragment, GetNextSibling(), aError);
3757       break;
3758   }
3759 }
3760 
InsertAdjacent(const nsAString & aWhere,nsINode * aNode,ErrorResult & aError)3761 nsINode* Element::InsertAdjacent(const nsAString& aWhere, nsINode* aNode,
3762                                  ErrorResult& aError) {
3763   if (aWhere.LowerCaseEqualsLiteral("beforebegin")) {
3764     nsCOMPtr<nsINode> parent = GetParentNode();
3765     if (!parent) {
3766       return nullptr;
3767     }
3768     parent->InsertBefore(*aNode, this, aError);
3769   } else if (aWhere.LowerCaseEqualsLiteral("afterbegin")) {
3770     nsCOMPtr<nsINode> refNode = GetFirstChild();
3771     static_cast<nsINode*>(this)->InsertBefore(*aNode, refNode, aError);
3772   } else if (aWhere.LowerCaseEqualsLiteral("beforeend")) {
3773     static_cast<nsINode*>(this)->AppendChild(*aNode, aError);
3774   } else if (aWhere.LowerCaseEqualsLiteral("afterend")) {
3775     nsCOMPtr<nsINode> parent = GetParentNode();
3776     if (!parent) {
3777       return nullptr;
3778     }
3779     nsCOMPtr<nsINode> refNode = GetNextSibling();
3780     parent->InsertBefore(*aNode, refNode, aError);
3781   } else {
3782     aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
3783     return nullptr;
3784   }
3785 
3786   return aError.Failed() ? nullptr : aNode;
3787 }
3788 
InsertAdjacentElement(const nsAString & aWhere,Element & aElement,ErrorResult & aError)3789 Element* Element::InsertAdjacentElement(const nsAString& aWhere,
3790                                         Element& aElement,
3791                                         ErrorResult& aError) {
3792   nsINode* newNode = InsertAdjacent(aWhere, &aElement, aError);
3793   MOZ_ASSERT(!newNode || newNode->IsElement());
3794 
3795   return newNode ? newNode->AsElement() : nullptr;
3796 }
3797 
InsertAdjacentText(const nsAString & aWhere,const nsAString & aData,ErrorResult & aError)3798 void Element::InsertAdjacentText(const nsAString& aWhere,
3799                                  const nsAString& aData, ErrorResult& aError) {
3800   RefPtr<nsTextNode> textNode = OwnerDoc()->CreateTextNode(aData);
3801   InsertAdjacent(aWhere, textNode, aError);
3802 }
3803 
GetTextEditorInternal()3804 TextEditor* Element::GetTextEditorInternal() {
3805   TextControlElement* textControlElement = TextControlElement::FromNode(this);
3806   return textControlElement ? MOZ_KnownLive(textControlElement)->GetTextEditor()
3807                             : nullptr;
3808 }
3809 
SetBoolAttr(nsAtom * aAttr,bool aValue)3810 nsresult Element::SetBoolAttr(nsAtom* aAttr, bool aValue) {
3811   if (aValue) {
3812     return SetAttr(kNameSpaceID_None, aAttr, u""_ns, true);
3813   }
3814 
3815   return UnsetAttr(kNameSpaceID_None, aAttr, true);
3816 }
3817 
GetEnumAttr(nsAtom * aAttr,const char * aDefault,nsAString & aResult) const3818 void Element::GetEnumAttr(nsAtom* aAttr, const char* aDefault,
3819                           nsAString& aResult) const {
3820   GetEnumAttr(aAttr, aDefault, aDefault, aResult);
3821 }
3822 
GetEnumAttr(nsAtom * aAttr,const char * aDefaultMissing,const char * aDefaultInvalid,nsAString & aResult) const3823 void Element::GetEnumAttr(nsAtom* aAttr, const char* aDefaultMissing,
3824                           const char* aDefaultInvalid,
3825                           nsAString& aResult) const {
3826   const nsAttrValue* attrVal = mAttrs.GetAttr(aAttr);
3827 
3828   aResult.Truncate();
3829 
3830   if (!attrVal) {
3831     if (aDefaultMissing) {
3832       AppendASCIItoUTF16(nsDependentCString(aDefaultMissing), aResult);
3833     } else {
3834       SetDOMStringToNull(aResult);
3835     }
3836   } else {
3837     if (attrVal->Type() == nsAttrValue::eEnum) {
3838       attrVal->GetEnumString(aResult, true);
3839     } else if (aDefaultInvalid) {
3840       AppendASCIItoUTF16(nsDependentCString(aDefaultInvalid), aResult);
3841     }
3842   }
3843 }
3844 
SetOrRemoveNullableStringAttr(nsAtom * aName,const nsAString & aValue,ErrorResult & aError)3845 void Element::SetOrRemoveNullableStringAttr(nsAtom* aName,
3846                                             const nsAString& aValue,
3847                                             ErrorResult& aError) {
3848   if (DOMStringIsNull(aValue)) {
3849     UnsetAttr(aName, aError);
3850   } else {
3851     SetAttr(aName, aValue, aError);
3852   }
3853 }
3854 
GetComputedDirectionality() const3855 Directionality Element::GetComputedDirectionality() const {
3856   if (nsIFrame* frame = GetPrimaryFrame()) {
3857     return frame->StyleVisibility()->mDirection == StyleDirection::Ltr
3858                ? eDir_LTR
3859                : eDir_RTL;
3860   }
3861 
3862   return GetDirectionality();
3863 }
3864 
FontSizeInflation()3865 float Element::FontSizeInflation() {
3866   nsIFrame* frame = GetPrimaryFrame();
3867   if (!frame) {
3868     return -1.0;
3869   }
3870 
3871   if (nsLayoutUtils::FontSizeInflationEnabled(frame->PresContext())) {
3872     return nsLayoutUtils::FontSizeInflationFor(frame);
3873   }
3874 
3875   return 1.0;
3876 }
3877 
GetImplementedPseudoElement(nsAString & aPseudo) const3878 void Element::GetImplementedPseudoElement(nsAString& aPseudo) const {
3879   PseudoStyleType pseudoType = GetPseudoElementType();
3880   if (pseudoType == PseudoStyleType::NotPseudo) {
3881     return SetDOMStringToNull(aPseudo);
3882   }
3883   nsDependentAtomString pseudo(nsCSSPseudoElements::GetPseudoAtom(pseudoType));
3884 
3885   // We want to use the modern syntax (::placeholder, etc), but the atoms only
3886   // contain one semi-colon.
3887   MOZ_ASSERT(pseudo.Length() > 2 && pseudo[0] == ':' && pseudo[1] != ':');
3888 
3889   aPseudo.Truncate();
3890   aPseudo.SetCapacity(pseudo.Length() + 1);
3891   aPseudo.Append(':');
3892   aPseudo.Append(pseudo);
3893 }
3894 
GetReferrerPolicyAsEnum() const3895 ReferrerPolicy Element::GetReferrerPolicyAsEnum() const {
3896   if (IsHTMLElement()) {
3897     return ReferrerPolicyFromAttr(GetParsedAttr(nsGkAtoms::referrerpolicy));
3898   }
3899   return ReferrerPolicy::_empty;
3900 }
3901 
ReferrerPolicyFromAttr(const nsAttrValue * aValue) const3902 ReferrerPolicy Element::ReferrerPolicyFromAttr(
3903     const nsAttrValue* aValue) const {
3904   if (aValue && aValue->Type() == nsAttrValue::eEnum) {
3905     return ReferrerPolicy(aValue->GetEnumValue());
3906   }
3907   return ReferrerPolicy::_empty;
3908 }
3909 
Dataset()3910 already_AddRefed<nsDOMStringMap> Element::Dataset() {
3911   nsDOMSlots* slots = DOMSlots();
3912 
3913   if (!slots->mDataset) {
3914     // mDataset is a weak reference so assignment will not AddRef.
3915     // AddRef is called before returning the pointer.
3916     slots->mDataset = new nsDOMStringMap(this);
3917   }
3918 
3919   RefPtr<nsDOMStringMap> ret = slots->mDataset;
3920   return ret.forget();
3921 }
3922 
ClearDataset()3923 void Element::ClearDataset() {
3924   nsDOMSlots* slots = GetExistingDOMSlots();
3925 
3926   MOZ_ASSERT(slots && slots->mDataset,
3927              "Slots should exist and dataset should not be null.");
3928   slots->mDataset = nullptr;
3929 }
3930 
3931 enum nsPreviousIntersectionThreshold {
3932   eUninitialized = -2,
3933   eNonIntersecting = -1
3934 };
3935 
IntersectionObserverPropertyDtor(void * aObject,nsAtom * aPropertyName,void * aPropertyValue,void * aData)3936 static void IntersectionObserverPropertyDtor(void* aObject,
3937                                              nsAtom* aPropertyName,
3938                                              void* aPropertyValue,
3939                                              void* aData) {
3940   auto* element = static_cast<Element*>(aObject);
3941   auto* observers = static_cast<IntersectionObserverList*>(aPropertyValue);
3942   for (DOMIntersectionObserver* observer : observers->Keys()) {
3943     observer->UnlinkTarget(*element);
3944   }
3945   delete observers;
3946 }
3947 
RegisterIntersectionObserver(DOMIntersectionObserver * aObserver)3948 void Element::RegisterIntersectionObserver(DOMIntersectionObserver* aObserver) {
3949   IntersectionObserverList* observers = static_cast<IntersectionObserverList*>(
3950       GetProperty(nsGkAtoms::intersectionobserverlist));
3951 
3952   if (!observers) {
3953     observers = new IntersectionObserverList();
3954     observers->InsertOrUpdate(aObserver, eUninitialized);
3955     SetProperty(nsGkAtoms::intersectionobserverlist, observers,
3956                 IntersectionObserverPropertyDtor, /* aTransfer = */ true);
3957     return;
3958   }
3959 
3960   // Value can be:
3961   //   -2:   Makes sure next calculated threshold always differs, leading to a
3962   //         notification task being scheduled.
3963   //   -1:   Non-intersecting.
3964   //   >= 0: Intersecting, valid index of aObserver->mThresholds.
3965   observers->LookupOrInsert(aObserver, eUninitialized);
3966 }
3967 
UnregisterIntersectionObserver(DOMIntersectionObserver * aObserver)3968 void Element::UnregisterIntersectionObserver(
3969     DOMIntersectionObserver* aObserver) {
3970   auto* observers = static_cast<IntersectionObserverList*>(
3971       GetProperty(nsGkAtoms::intersectionobserverlist));
3972   if (observers) {
3973     observers->Remove(aObserver);
3974     if (observers->IsEmpty()) {
3975       RemoveProperty(nsGkAtoms::intersectionobserverlist);
3976     }
3977   }
3978 }
3979 
UnlinkIntersectionObservers()3980 void Element::UnlinkIntersectionObservers() {
3981   // IntersectionObserverPropertyDtor takes care of the hard work.
3982   RemoveProperty(nsGkAtoms::intersectionobserverlist);
3983 }
3984 
UpdateIntersectionObservation(DOMIntersectionObserver * aObserver,int32_t aThreshold)3985 bool Element::UpdateIntersectionObservation(DOMIntersectionObserver* aObserver,
3986                                             int32_t aThreshold) {
3987   auto* observers = static_cast<IntersectionObserverList*>(
3988       GetProperty(nsGkAtoms::intersectionobserverlist));
3989   if (!observers) {
3990     return false;
3991   }
3992   bool updated = false;
3993   if (auto entry = observers->Lookup(aObserver)) {
3994     updated = entry.Data() != aThreshold;
3995     entry.Data() = aThreshold;
3996   }
3997   return updated;
3998 }
3999 
4000 template <class T>
GetCustomInterface(nsGetterAddRefs<T> aResult)4001 void Element::GetCustomInterface(nsGetterAddRefs<T> aResult) {
4002   nsCOMPtr<nsISupports> iface = CustomElementRegistry::CallGetCustomInterface(
4003       this, NS_GET_TEMPLATE_IID(T));
4004   if (iface) {
4005     if (NS_SUCCEEDED(CallQueryInterface(iface, static_cast<T**>(aResult)))) {
4006       return;
4007     }
4008   }
4009 }
4010 
ClearServoData(Document * aDoc)4011 void Element::ClearServoData(Document* aDoc) {
4012   MOZ_ASSERT(aDoc);
4013   if (HasServoData()) {
4014     Servo_Element_ClearData(this);
4015   } else {
4016     UnsetFlags(kAllServoDescendantBits | NODE_NEEDS_FRAME);
4017   }
4018   // Since this element is losing its servo data, nothing under it may have
4019   // servo data either, so we can forget restyles rooted at this element. This
4020   // is necessary for correctness, since we invoke ClearServoData in various
4021   // places where an element's flattened tree parent changes, and such a change
4022   // may also make an element invalid to be used as a restyle root.
4023   if (aDoc->GetServoRestyleRoot() == this) {
4024     aDoc->ClearServoRestyleRoot();
4025   }
4026 }
4027 
SetCustomElementData(CustomElementData * aData)4028 void Element::SetCustomElementData(CustomElementData* aData) {
4029   SetHasCustomElementData();
4030 
4031   if (aData->mState != CustomElementData::State::eCustom) {
4032     SetDefined(false);
4033   }
4034 
4035   nsExtendedDOMSlots* slots = ExtendedDOMSlots();
4036   MOZ_ASSERT(!slots->mCustomElementData,
4037              "Custom element data may not be changed once set.");
4038 #if DEBUG
4039   // We assert only XUL usage, since web may pass whatever as 'is' value
4040   if (NodeInfo()->NamespaceID() == kNameSpaceID_XUL) {
4041     nsAtom* name = NodeInfo()->NameAtom();
4042     nsAtom* type = aData->GetCustomElementType();
4043     // Check to see if the tag name is a dashed name.
4044     if (nsContentUtils::IsNameWithDash(name)) {
4045       // Assert that a tag name with dashes is always an autonomous custom
4046       // element.
4047       MOZ_ASSERT(type == name);
4048     } else {
4049       // Could still be an autonomous custom element with a non-dashed tag name.
4050       // Need the check below for sure.
4051       if (type != name) {
4052         // Assert that the name of the built-in custom element type is always
4053         // a dashed name.
4054         MOZ_ASSERT(nsContentUtils::IsNameWithDash(type));
4055       }
4056     }
4057   }
4058 #endif
4059   slots->mCustomElementData = aData;
4060 }
4061 
GetCustomElementDefinition() const4062 CustomElementDefinition* Element::GetCustomElementDefinition() const {
4063   CustomElementData* data = GetCustomElementData();
4064   if (!data) {
4065     return nullptr;
4066   }
4067 
4068   return data->GetCustomElementDefinition();
4069 }
4070 
SetCustomElementDefinition(CustomElementDefinition * aDefinition)4071 void Element::SetCustomElementDefinition(CustomElementDefinition* aDefinition) {
4072   CustomElementData* data = GetCustomElementData();
4073   MOZ_ASSERT(data);
4074 
4075   data->SetCustomElementDefinition(aDefinition);
4076 }
4077 
AsXULButton()4078 already_AddRefed<nsIDOMXULButtonElement> Element::AsXULButton() {
4079   nsCOMPtr<nsIDOMXULButtonElement> value;
4080   GetCustomInterface(getter_AddRefs(value));
4081   return value.forget();
4082 }
4083 
AsXULContainer()4084 already_AddRefed<nsIDOMXULContainerElement> Element::AsXULContainer() {
4085   nsCOMPtr<nsIDOMXULContainerElement> value;
4086   GetCustomInterface(getter_AddRefs(value));
4087   return value.forget();
4088 }
4089 
AsXULContainerItem()4090 already_AddRefed<nsIDOMXULContainerItemElement> Element::AsXULContainerItem() {
4091   nsCOMPtr<nsIDOMXULContainerItemElement> value;
4092   GetCustomInterface(getter_AddRefs(value));
4093   return value.forget();
4094 }
4095 
AsXULControl()4096 already_AddRefed<nsIDOMXULControlElement> Element::AsXULControl() {
4097   nsCOMPtr<nsIDOMXULControlElement> value;
4098   GetCustomInterface(getter_AddRefs(value));
4099   return value.forget();
4100 }
4101 
AsXULMenuList()4102 already_AddRefed<nsIDOMXULMenuListElement> Element::AsXULMenuList() {
4103   nsCOMPtr<nsIDOMXULMenuListElement> value;
4104   GetCustomInterface(getter_AddRefs(value));
4105   return value.forget();
4106 }
4107 
4108 already_AddRefed<nsIDOMXULMultiSelectControlElement>
AsXULMultiSelectControl()4109 Element::AsXULMultiSelectControl() {
4110   nsCOMPtr<nsIDOMXULMultiSelectControlElement> value;
4111   GetCustomInterface(getter_AddRefs(value));
4112   return value.forget();
4113 }
4114 
AsXULRadioGroup()4115 already_AddRefed<nsIDOMXULRadioGroupElement> Element::AsXULRadioGroup() {
4116   nsCOMPtr<nsIDOMXULRadioGroupElement> value;
4117   GetCustomInterface(getter_AddRefs(value));
4118   return value.forget();
4119 }
4120 
AsXULRelated()4121 already_AddRefed<nsIDOMXULRelatedElement> Element::AsXULRelated() {
4122   nsCOMPtr<nsIDOMXULRelatedElement> value;
4123   GetCustomInterface(getter_AddRefs(value));
4124   return value.forget();
4125 }
4126 
AsXULSelectControl()4127 already_AddRefed<nsIDOMXULSelectControlElement> Element::AsXULSelectControl() {
4128   nsCOMPtr<nsIDOMXULSelectControlElement> value;
4129   GetCustomInterface(getter_AddRefs(value));
4130   return value.forget();
4131 }
4132 
4133 already_AddRefed<nsIDOMXULSelectControlItemElement>
AsXULSelectControlItem()4134 Element::AsXULSelectControlItem() {
4135   nsCOMPtr<nsIDOMXULSelectControlItemElement> value;
4136   GetCustomInterface(getter_AddRefs(value));
4137   return value.forget();
4138 }
4139 
AsBrowser()4140 already_AddRefed<nsIBrowser> Element::AsBrowser() {
4141   nsCOMPtr<nsIBrowser> value;
4142   GetCustomInterface(getter_AddRefs(value));
4143   return value.forget();
4144 }
4145 
AsAutoCompletePopup()4146 already_AddRefed<nsIAutoCompletePopup> Element::AsAutoCompletePopup() {
4147   nsCOMPtr<nsIAutoCompletePopup> value;
4148   GetCustomInterface(getter_AddRefs(value));
4149   return value.forget();
4150 }
4151 
GetPresContext(PresContextFor aFor)4152 nsPresContext* Element::GetPresContext(PresContextFor aFor) {
4153   // Get the document
4154   Document* doc =
4155       (aFor == eForComposedDoc) ? GetComposedDoc() : GetUncomposedDoc();
4156   if (doc) {
4157     return doc->GetPresContext();
4158   }
4159 
4160   return nullptr;
4161 }
4162 
4163 MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf)
MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf)4164 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf)
4165 
4166 void Element::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
4167                                      size_t* aNodeSize) const {
4168   FragmentOrElement::AddSizeOfExcludingThis(aSizes, aNodeSize);
4169   *aNodeSize += mAttrs.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
4170 
4171   if (HasServoData()) {
4172     // Measure the ElementData object itself.
4173     aSizes.mLayoutElementDataObjects +=
4174         aSizes.mState.mMallocSizeOf(mServoData.Get());
4175 
4176     // Measure mServoData, excluding the ComputedValues. This measurement
4177     // counts towards the element's size. We use ServoElementMallocSizeOf and
4178     // ServoElementMallocEnclosingSizeOf rather than |aState.mMallocSizeOf| to
4179     // better distinguish in DMD's output the memory measured within Servo
4180     // code.
4181     *aNodeSize += Servo_Element_SizeOfExcludingThisAndCVs(
4182         ServoElementMallocSizeOf, ServoElementMallocEnclosingSizeOf,
4183         &aSizes.mState.mSeenPtrs, this);
4184 
4185     // Now measure just the ComputedValues (and style structs) under
4186     // mServoData. This counts towards the relevant fields in |aSizes|.
4187     if (auto* style = Servo_Element_GetMaybeOutOfDateStyle(this)) {
4188       if (!aSizes.mState.HaveSeenPtr(style)) {
4189         style->AddSizeOfIncludingThis(aSizes, &aSizes.mLayoutComputedValuesDom);
4190       }
4191 
4192       for (size_t i = 0; i < PseudoStyle::kEagerPseudoCount; i++) {
4193         if (auto* style = Servo_Element_GetMaybeOutOfDatePseudoStyle(this, i)) {
4194           if (!aSizes.mState.HaveSeenPtr(style)) {
4195             style->AddSizeOfIncludingThis(aSizes,
4196                                           &aSizes.mLayoutComputedValuesDom);
4197           }
4198         }
4199       }
4200     }
4201   }
4202 }
4203 
4204 #ifdef DEBUG
BitsArePropagated(const Element * aElement,uint32_t aBits,nsINode * aRestyleRoot)4205 static bool BitsArePropagated(const Element* aElement, uint32_t aBits,
4206                               nsINode* aRestyleRoot) {
4207   const Element* curr = aElement;
4208   while (curr) {
4209     if (curr == aRestyleRoot) {
4210       return true;
4211     }
4212     if (!curr->HasAllFlags(aBits)) {
4213       return false;
4214     }
4215     nsINode* parentNode = curr->GetParentNode();
4216     curr = curr->GetFlattenedTreeParentElementForStyle();
4217     MOZ_ASSERT_IF(!curr,
4218                   parentNode == aElement->OwnerDoc() ||
4219                       parentNode == parentNode->OwnerDoc()->GetRootElement());
4220   }
4221   return true;
4222 }
4223 #endif
4224 
AssertNoBitsPropagatedFrom(nsINode * aRoot)4225 static inline void AssertNoBitsPropagatedFrom(nsINode* aRoot) {
4226 #ifdef DEBUG
4227   if (!aRoot || !aRoot->IsElement()) {
4228     return;
4229   }
4230 
4231   auto* element = aRoot->GetFlattenedTreeParentElementForStyle();
4232   while (element) {
4233     MOZ_ASSERT(!element->HasAnyOfFlags(Element::kAllServoDescendantBits));
4234     element = element->GetFlattenedTreeParentElementForStyle();
4235   }
4236 #endif
4237 }
4238 
4239 // Sets `aBits` on `aElement` and all of its flattened-tree ancestors up to and
4240 // including aStopAt or the root element (whichever is encountered first), and
4241 // as long as `aBitsToStopAt` isn't found anywhere in the chain.
PropagateBits(Element * aElement,uint32_t aBits,nsINode * aStopAt,uint32_t aBitsToStopAt)4242 static inline Element* PropagateBits(Element* aElement, uint32_t aBits,
4243                                      nsINode* aStopAt, uint32_t aBitsToStopAt) {
4244   Element* curr = aElement;
4245   while (curr && !curr->HasAllFlags(aBitsToStopAt)) {
4246     curr->SetFlags(aBits);
4247     if (curr == aStopAt) {
4248       break;
4249     }
4250     curr = curr->GetFlattenedTreeParentElementForStyle();
4251   }
4252 
4253   if (aBitsToStopAt != aBits && curr) {
4254     curr->SetFlags(aBits);
4255   }
4256 
4257   return curr;
4258 }
4259 
4260 // Notes that a given element is "dirty" with respect to the given descendants
4261 // bit (which may be one of dirty descendants, dirty animation descendants, or
4262 // need frame construction for descendants).
4263 //
4264 // This function operates on the dirty element itself, despite the fact that the
4265 // bits are generally used to describe descendants. This allows restyle roots
4266 // to be scoped as tightly as possible. On the first call to NoteDirtyElement
4267 // since the last restyle, we don't set any descendant bits at all, and just set
4268 // the element as the restyle root.
4269 //
4270 // Because the style traversal handles multiple tasks (styling,
4271 // animation-ticking, and lazy frame construction), there are potentially three
4272 // separate kinds of dirtiness to track. Rather than maintaining three separate
4273 // restyle roots, we use a single root, and always bubble it up to be the
4274 // nearest common ancestor of all the dirty content in the tree. This means that
4275 // we need to track the types of dirtiness that the restyle root corresponds to,
4276 // so SetServoRestyleRoot accepts a bitfield along with an element.
4277 //
4278 // The overall algorithm is as follows:
4279 // * When the first dirty element is noted, we just set as the restyle root.
4280 // * When additional dirty elements are noted, we propagate the given bit up
4281 //   the tree, until we either reach the restyle root or the document root.
4282 // * If we reach the document root, we then propagate the bits associated with
4283 //   the restyle root up the tree until we cross the path of the new root. Once
4284 //   we find this common ancestor, we record it as the restyle root, and then
4285 //   clear the bits between the new restyle root and the document root.
4286 // * If we have dirty content beneath multiple "document style traversal roots"
4287 //   (which are the main DOM + each piece of document-level native-anoymous
4288 //   content), we set the restyle root to the nsINode of the document itself.
4289 //   This is the bail-out case where we traverse everything.
4290 //
4291 // Note that, since we track a root, we try to optimize the case where an
4292 // element under the current root is dirtied, that's why we don't trivially use
4293 // `nsContentUtils::GetCommonFlattenedTreeAncestorForStyle`.
NoteDirtyElement(Element * aElement,uint32_t aBits)4294 static void NoteDirtyElement(Element* aElement, uint32_t aBits) {
4295   MOZ_ASSERT(aElement->IsInComposedDoc());
4296 
4297   // Check the existing root early on, since it may allow us to short-circuit
4298   // before examining the parent chain.
4299   Document* doc = aElement->GetComposedDoc();
4300   nsINode* existingRoot = doc->GetServoRestyleRoot();
4301   if (existingRoot == aElement) {
4302     doc->SetServoRestyleRootDirtyBits(doc->GetServoRestyleRootDirtyBits() |
4303                                       aBits);
4304     return;
4305   }
4306 
4307   nsINode* parent = aElement->GetFlattenedTreeParentNodeForStyle();
4308   if (!parent) {
4309     // The element is not in the flattened tree, bail.
4310     return;
4311   }
4312 
4313   if (MOZ_LIKELY(parent->IsElement())) {
4314     // If our parent is unstyled, we can inductively assume that it will be
4315     // traversed when the time is right, and that the traversal will reach us
4316     // when it happens. Nothing left to do.
4317     if (!parent->AsElement()->HasServoData()) {
4318       return;
4319     }
4320 
4321     // Similarly, if our parent already has the bit we're propagating, we can
4322     // assume everything is already set up.
4323     if (parent->HasAllFlags(aBits)) {
4324       return;
4325     }
4326 
4327     // If the parent is styled but is display:none, we're done.
4328     //
4329     // We can't check for a frame here, since <frame> elements inside <frameset>
4330     // still need to generate a frame, even if they're display: none. :(
4331     //
4332     // The servo traversal doesn't keep style data under display: none subtrees,
4333     // so in order for it to not need to cleanup each time anything happens in a
4334     // display: none subtree, we keep it clean.
4335     //
4336     // Also, we can't be much more smarter about using the parent's frame in
4337     // order to avoid work here, because since the style system keeps style data
4338     // in, e.g., subtrees under a leaf frame, missing restyles and such in there
4339     // has observable behavior via getComputedStyle, for example.
4340     if (Servo_Element_IsDisplayNone(parent->AsElement())) {
4341       return;
4342     }
4343   }
4344 
4345   if (PresShell* presShell = doc->GetPresShell()) {
4346     presShell->EnsureStyleFlush();
4347   }
4348 
4349   MOZ_ASSERT(parent->IsElement() || parent == doc);
4350 
4351   // The bit checks below rely on this to arrive to useful conclusions about the
4352   // shape of the tree.
4353   AssertNoBitsPropagatedFrom(existingRoot);
4354 
4355   // If there's no existing restyle root, or if the root is already aElement,
4356   // just note the root+bits and return.
4357   if (!existingRoot) {
4358     doc->SetServoRestyleRoot(aElement, aBits);
4359     return;
4360   }
4361 
4362   // There is an existing restyle root - walk up the tree from our element,
4363   // propagating bits as we go.
4364   const bool reachedDocRoot =
4365       !parent->IsElement() ||
4366       !PropagateBits(parent->AsElement(), aBits, existingRoot, aBits);
4367 
4368   uint32_t existingBits = doc->GetServoRestyleRootDirtyBits();
4369   if (!reachedDocRoot || existingRoot == doc) {
4370     // We're a descendant of the existing root. All that's left to do is to
4371     // make sure the bit we propagated is also registered on the root.
4372     doc->SetServoRestyleRoot(existingRoot, existingBits | aBits);
4373   } else {
4374     // We reached the root without crossing the pre-existing restyle root. We
4375     // now need to find the nearest common ancestor, so climb up from the
4376     // existing root, extending bits along the way.
4377     Element* rootParent = existingRoot->GetFlattenedTreeParentElementForStyle();
4378     // We can stop at the first occurrence of `aBits` in order to find the
4379     // common ancestor.
4380     if (Element* commonAncestor =
4381             PropagateBits(rootParent, existingBits, aElement, aBits)) {
4382       MOZ_ASSERT(commonAncestor == aElement ||
4383                  commonAncestor ==
4384                      nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
4385                          aElement, rootParent));
4386 
4387       // We found a common ancestor. Make that the new style root, and clear the
4388       // bits between the new style root and the document root.
4389       doc->SetServoRestyleRoot(commonAncestor, existingBits | aBits);
4390       Element* curr = commonAncestor;
4391       while ((curr = curr->GetFlattenedTreeParentElementForStyle())) {
4392         MOZ_ASSERT(curr->HasAllFlags(aBits));
4393         curr->UnsetFlags(aBits);
4394       }
4395       AssertNoBitsPropagatedFrom(commonAncestor);
4396     } else {
4397       // We didn't find a common ancestor element. That means we're descended
4398       // from two different document style roots, so the common ancestor is the
4399       // document.
4400       doc->SetServoRestyleRoot(doc, existingBits | aBits);
4401     }
4402   }
4403 
4404   // See the comment in Document::SetServoRestyleRoot about the !IsElement()
4405   // check there. Same justification here.
4406   MOZ_ASSERT(aElement == doc->GetServoRestyleRoot() ||
4407              !doc->GetServoRestyleRoot()->IsElement() ||
4408              nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
4409                  aElement, doc->GetServoRestyleRoot()));
4410   MOZ_ASSERT(aElement == doc->GetServoRestyleRoot() ||
4411              !doc->GetServoRestyleRoot()->IsElement() || !parent->IsElement() ||
4412              BitsArePropagated(parent->AsElement(), aBits,
4413                                doc->GetServoRestyleRoot()));
4414   MOZ_ASSERT(doc->GetServoRestyleRootDirtyBits() & aBits);
4415 }
4416 
NoteDirtySubtreeForServo()4417 void Element::NoteDirtySubtreeForServo() {
4418   MOZ_ASSERT(IsInComposedDoc());
4419   MOZ_ASSERT(HasServoData());
4420 
4421   Document* doc = GetComposedDoc();
4422   nsINode* existingRoot = doc->GetServoRestyleRoot();
4423   uint32_t existingBits =
4424       existingRoot ? doc->GetServoRestyleRootDirtyBits() : 0;
4425 
4426   if (existingRoot && existingRoot->IsElement() && existingRoot != this &&
4427       nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
4428           existingRoot->AsElement(), this)) {
4429     PropagateBits(
4430         existingRoot->AsElement()->GetFlattenedTreeParentElementForStyle(),
4431         existingBits, this, existingBits);
4432 
4433     doc->ClearServoRestyleRoot();
4434   }
4435 
4436   NoteDirtyElement(this,
4437                    existingBits | ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
4438 }
4439 
NoteDirtyForServo()4440 void Element::NoteDirtyForServo() {
4441   NoteDirtyElement(this, ELEMENT_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
4442 }
4443 
NoteAnimationOnlyDirtyForServo()4444 void Element::NoteAnimationOnlyDirtyForServo() {
4445   NoteDirtyElement(this,
4446                    ELEMENT_HAS_ANIMATION_ONLY_DIRTY_DESCENDANTS_FOR_SERVO);
4447 }
4448 
NoteDescendantsNeedFramesForServo()4449 void Element::NoteDescendantsNeedFramesForServo() {
4450   // Since lazy frame construction can be required for non-element nodes, this
4451   // Note() method operates on the parent of the frame-requiring content, unlike
4452   // the other Note() methods above (which operate directly on the element that
4453   // needs processing).
4454   NoteDirtyElement(this, NODE_DESCENDANTS_NEED_FRAMES);
4455   SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
4456 }
4457 
FirstLineBoxBSize() const4458 double Element::FirstLineBoxBSize() const {
4459   const nsBlockFrame* frame = do_QueryFrame(GetPrimaryFrame());
4460   if (!frame) {
4461     return 0.0;
4462   }
4463   nsBlockFrame::ConstLineIterator line = frame->LinesBegin();
4464   nsBlockFrame::ConstLineIterator lineEnd = frame->LinesEnd();
4465   return line != lineEnd
4466              ? nsPresContext::AppUnitsToDoubleCSSPixels(line->BSize())
4467              : 0.0;
4468 }
4469 
4470 // static
GetEventNameForAttr(nsAtom * aAttr)4471 nsAtom* Element::GetEventNameForAttr(nsAtom* aAttr) {
4472   if (aAttr == nsGkAtoms::onwebkitanimationend) {
4473     return nsGkAtoms::onwebkitAnimationEnd;
4474   }
4475   if (aAttr == nsGkAtoms::onwebkitanimationiteration) {
4476     return nsGkAtoms::onwebkitAnimationIteration;
4477   }
4478   if (aAttr == nsGkAtoms::onwebkitanimationstart) {
4479     return nsGkAtoms::onwebkitAnimationStart;
4480   }
4481   if (aAttr == nsGkAtoms::onwebkittransitionend) {
4482     return nsGkAtoms::onwebkitTransitionEnd;
4483   }
4484   return aAttr;
4485 }
4486 
RegUnRegAccessKey(bool aDoReg)4487 void Element::RegUnRegAccessKey(bool aDoReg) {
4488   // first check to see if we have an access key
4489   nsAutoString accessKey;
4490   GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
4491   if (accessKey.IsEmpty()) {
4492     return;
4493   }
4494 
4495   // We have an access key, so get the ESM from the pres context.
4496   if (nsPresContext* presContext = GetPresContext(eForComposedDoc)) {
4497     EventStateManager* esm = presContext->EventStateManager();
4498 
4499     // Register or unregister as appropriate.
4500     if (aDoReg) {
4501       esm->RegisterAccessKey(this, (uint32_t)accessKey.First());
4502     } else {
4503       esm->UnregisterAccessKey(this, (uint32_t)accessKey.First());
4504     }
4505   }
4506 }
4507 
4508 }  // namespace mozilla::dom
4509