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