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