/src/mozilla-central/dom/base/nsINode.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 DOM nodes. |
9 | | */ |
10 | | |
11 | | #include "nsINode.h" |
12 | | |
13 | | #include "AccessCheck.h" |
14 | | #include "jsapi.h" |
15 | | #include "js/JSON.h" |
16 | | #include "mozAutoDocUpdate.h" |
17 | | #include "mozilla/AsyncEventDispatcher.h" |
18 | | #include "mozilla/CORSMode.h" |
19 | | #include "mozilla/EventDispatcher.h" |
20 | | #include "mozilla/EventListenerManager.h" |
21 | | #include "mozilla/HTMLEditor.h" |
22 | | #include "mozilla/InternalMutationEvent.h" |
23 | | #include "mozilla/Likely.h" |
24 | | #include "mozilla/MemoryReporting.h" |
25 | | #include "mozilla/ServoBindings.h" |
26 | | #include "mozilla/Telemetry.h" |
27 | | #include "mozilla/TextEditor.h" |
28 | | #include "mozilla/TimeStamp.h" |
29 | | #include "mozilla/dom/CharacterData.h" |
30 | | #include "mozilla/dom/DocumentType.h" |
31 | | #include "mozilla/dom/Element.h" |
32 | | #include "mozilla/dom/Event.h" |
33 | | #include "mozilla/dom/L10nUtilsBinding.h" |
34 | | #include "mozilla/dom/Promise.h" |
35 | | #include "mozilla/dom/PromiseNativeHandler.h" |
36 | | #include "mozilla/dom/ShadowRoot.h" |
37 | | #include "mozilla/dom/SVGUseElement.h" |
38 | | #include "mozilla/dom/ScriptSettings.h" |
39 | | #include "nsAttrValueOrString.h" |
40 | | #include "nsBindingManager.h" |
41 | | #include "nsCCUncollectableMarker.h" |
42 | | #include "nsContentCreatorFunctions.h" |
43 | | #include "nsContentList.h" |
44 | | #include "nsContentUtils.h" |
45 | | #include "nsCycleCollectionParticipant.h" |
46 | | #include "nsDocument.h" |
47 | | #include "mozilla/dom/Attr.h" |
48 | | #include "nsDOMAttributeMap.h" |
49 | | #include "nsDOMCID.h" |
50 | | #include "nsDOMCSSAttrDeclaration.h" |
51 | | #include "nsError.h" |
52 | | #include "nsDOMMutationObserver.h" |
53 | | #include "nsDOMString.h" |
54 | | #include "nsDOMTokenList.h" |
55 | | #include "nsFocusManager.h" |
56 | | #include "nsFrameSelection.h" |
57 | | #include "nsGenericHTMLElement.h" |
58 | | #include "nsGkAtoms.h" |
59 | | #include "nsIAnonymousContentCreator.h" |
60 | | #include "nsAtom.h" |
61 | | #include "nsIBaseWindow.h" |
62 | | #include "nsICategoryManager.h" |
63 | | #include "nsIContentInlines.h" |
64 | | #include "nsIContentIterator.h" |
65 | | #include "nsIControllers.h" |
66 | | #include "nsIDocument.h" |
67 | | #include "nsIDOMEventListener.h" |
68 | | #include "nsILinkHandler.h" |
69 | | #include "mozilla/dom/NodeInfo.h" |
70 | | #include "mozilla/dom/NodeInfoInlines.h" |
71 | | #include "nsIPresShell.h" |
72 | | #include "nsIScriptError.h" |
73 | | #include "nsIScriptGlobalObject.h" |
74 | | #include "nsIScriptSecurityManager.h" |
75 | | #include "nsIScrollableFrame.h" |
76 | | #include "nsIServiceManager.h" |
77 | | #include "nsIURL.h" |
78 | | #include "nsView.h" |
79 | | #include "nsViewManager.h" |
80 | | #include "nsIWebNavigation.h" |
81 | | #include "nsIWidget.h" |
82 | | #include "nsLayoutUtils.h" |
83 | | #include "nsNameSpaceManager.h" |
84 | | #include "nsNodeInfoManager.h" |
85 | | #include "nsNodeUtils.h" |
86 | | #include "nsPIBoxObject.h" |
87 | | #include "nsPIDOMWindow.h" |
88 | | #include "nsPresContext.h" |
89 | | #include "nsString.h" |
90 | | #include "nsStyleConsts.h" |
91 | | #include "nsSVGUtils.h" |
92 | | #include "nsTextNode.h" |
93 | | #include "nsUnicharUtils.h" |
94 | | #include "nsXBLBinding.h" |
95 | | #include "nsXBLPrototypeBinding.h" |
96 | | #include "nsWindowSizes.h" |
97 | | #include "mozilla/Preferences.h" |
98 | | #include "xpcpublic.h" |
99 | | #include "HTMLLegendElement.h" |
100 | | #include "nsWrapperCacheInlines.h" |
101 | | #include "WrapperFactory.h" |
102 | | #include <algorithm> |
103 | | #include "nsGlobalWindow.h" |
104 | | #include "nsDOMMutationObserver.h" |
105 | | #include "GeometryUtils.h" |
106 | | #include "nsIAnimationObserver.h" |
107 | | #include "nsChildContentList.h" |
108 | | #include "mozilla/dom/NodeBinding.h" |
109 | | #include "mozilla/dom/BindingDeclarations.h" |
110 | | |
111 | | #include "XPathGenerator.h" |
112 | | |
113 | | #ifdef ACCESSIBILITY |
114 | | #include "mozilla/dom/AccessibleNode.h" |
115 | | #endif |
116 | | |
117 | | using namespace mozilla; |
118 | | using namespace mozilla::dom; |
119 | | |
120 | | nsINode::nsSlots::nsSlots() |
121 | | : mWeakReference(nullptr), |
122 | | mEditableDescendantCount(0) |
123 | 0 | { |
124 | 0 | } |
125 | | |
126 | | nsINode::nsSlots::~nsSlots() |
127 | 0 | { |
128 | 0 | if (mChildNodes) { |
129 | 0 | mChildNodes->DropReference(); |
130 | 0 | } |
131 | 0 |
|
132 | 0 | if (mWeakReference) { |
133 | 0 | mWeakReference->NoticeNodeDestruction(); |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | | void |
138 | | nsINode::nsSlots::Traverse(nsCycleCollectionTraversalCallback &cb) |
139 | 0 | { |
140 | 0 | NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildNodes"); |
141 | 0 | cb.NoteXPCOMChild(mChildNodes); |
142 | 0 | } |
143 | | |
144 | | void |
145 | | nsINode::nsSlots::Unlink() |
146 | 0 | { |
147 | 0 | if (mChildNodes) { |
148 | 0 | mChildNodes->DropReference(); |
149 | 0 | } |
150 | 0 | } |
151 | | |
152 | | //---------------------------------------------------------------------- |
153 | | |
154 | | #ifdef MOZILLA_INTERNAL_API |
155 | | nsINode::nsINode(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) |
156 | | : mNodeInfo(std::move(aNodeInfo)) |
157 | | , mParent(nullptr) |
158 | | #ifndef BOOL_FLAGS_ON_WRAPPER_CACHE |
159 | | , mBoolFlags(0) |
160 | | #endif |
161 | | , mChildCount(0) |
162 | | , mPreviousOrLastSibling(nullptr) |
163 | | , mSubtreeRoot(this) |
164 | | , mSlots(nullptr) |
165 | 0 | { |
166 | 0 | } |
167 | | #endif |
168 | | |
169 | | nsINode::~nsINode() |
170 | 0 | { |
171 | 0 | MOZ_ASSERT(!HasSlots(), "nsNodeUtils::LastRelease was not called?"); |
172 | 0 | MOZ_ASSERT(mSubtreeRoot == this, "Didn't restore state properly?"); |
173 | 0 | } |
174 | | |
175 | | void* |
176 | | nsINode::GetProperty(nsAtom* aPropertyName, nsresult* aStatus) const |
177 | 0 | { |
178 | 0 | if (!HasProperties()) { // a fast HasFlag() test |
179 | 0 | if (aStatus) { |
180 | 0 | *aStatus = NS_PROPTABLE_PROP_NOT_THERE; |
181 | 0 | } |
182 | 0 | return nullptr; |
183 | 0 | } |
184 | 0 | return OwnerDoc()->PropertyTable().GetProperty(this, aPropertyName, aStatus); |
185 | 0 | } |
186 | | |
187 | | nsresult |
188 | | nsINode::SetProperty(nsAtom* aPropertyName, |
189 | | void* aValue, |
190 | | NSPropertyDtorFunc aDtor, |
191 | | bool aTransfer) |
192 | 0 | { |
193 | 0 | nsresult rv = OwnerDoc()->PropertyTable().SetProperty(this, |
194 | 0 | aPropertyName, |
195 | 0 | aValue, |
196 | 0 | aDtor, |
197 | 0 | nullptr, |
198 | 0 | aTransfer); |
199 | 0 | if (NS_SUCCEEDED(rv)) { |
200 | 0 | SetFlags(NODE_HAS_PROPERTIES); |
201 | 0 | } |
202 | 0 |
|
203 | 0 | return rv; |
204 | 0 | } |
205 | | |
206 | | void |
207 | | nsINode::DeleteProperty(nsAtom* aPropertyName) |
208 | 0 | { |
209 | 0 | OwnerDoc()->PropertyTable().DeleteProperty(this, aPropertyName); |
210 | 0 | } |
211 | | |
212 | | void* |
213 | | nsINode::UnsetProperty(nsAtom* aPropertyName, nsresult* aStatus) |
214 | 0 | { |
215 | 0 | return OwnerDoc()->PropertyTable().UnsetProperty(this, aPropertyName, aStatus); |
216 | 0 | } |
217 | | |
218 | | nsINode::nsSlots* |
219 | | nsINode::CreateSlots() |
220 | 0 | { |
221 | 0 | return new nsSlots(); |
222 | 0 | } |
223 | | |
224 | | nsIContent* |
225 | | nsINode::GetTextEditorRootContent(TextEditor** aTextEditor) |
226 | 0 | { |
227 | 0 | if (aTextEditor) { |
228 | 0 | *aTextEditor = nullptr; |
229 | 0 | } |
230 | 0 | for (nsINode* node = this; node; node = node->GetParentNode()) { |
231 | 0 | if (!node->IsElement() || |
232 | 0 | !node->IsHTMLElement()) |
233 | 0 | continue; |
234 | 0 | |
235 | 0 | RefPtr<TextEditor> textEditor = |
236 | 0 | static_cast<nsGenericHTMLElement*>(node)->GetTextEditorInternal(); |
237 | 0 | if (!textEditor) { |
238 | 0 | continue; |
239 | 0 | } |
240 | 0 | |
241 | 0 | MOZ_ASSERT(!textEditor->AsHTMLEditor(), |
242 | 0 | "If it were an HTML editor, needs to use GetRootElement()"); |
243 | 0 | Element* rootElement = textEditor->GetRoot(); |
244 | 0 | if (aTextEditor) { |
245 | 0 | textEditor.forget(aTextEditor); |
246 | 0 | } |
247 | 0 | return rootElement; |
248 | 0 | } |
249 | 0 | return nullptr; |
250 | 0 | } |
251 | | |
252 | | nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions) |
253 | 0 | { |
254 | 0 | if (aOptions.mComposed) { |
255 | 0 | if (nsIDocument* doc = GetComposedDoc()) { |
256 | 0 | return doc; |
257 | 0 | } |
258 | 0 | |
259 | 0 | nsINode* node = this; |
260 | 0 | while(node) { |
261 | 0 | node = node->SubtreeRoot(); |
262 | 0 | ShadowRoot* shadow = ShadowRoot::FromNode(node); |
263 | 0 | if (!shadow) { |
264 | 0 | break; |
265 | 0 | } |
266 | 0 | node = shadow->GetHost(); |
267 | 0 | } |
268 | 0 |
|
269 | 0 | return node; |
270 | 0 | } |
271 | 0 |
|
272 | 0 | return SubtreeRoot(); |
273 | 0 | } |
274 | | |
275 | | nsINode* |
276 | | nsINode::GetParentOrHostNode() const |
277 | 0 | { |
278 | 0 | if (mParent) { |
279 | 0 | return mParent; |
280 | 0 | } |
281 | 0 | |
282 | 0 | const ShadowRoot* shadowRoot = ShadowRoot::FromNode(this); |
283 | 0 | return shadowRoot ? shadowRoot->GetHost() : nullptr; |
284 | 0 | } |
285 | | |
286 | | nsINode* |
287 | | nsINode::SubtreeRoot() const |
288 | 0 | { |
289 | 0 | auto RootOfNode = [](const nsINode* aStart) -> nsINode* { |
290 | 0 | const nsINode* node = aStart; |
291 | 0 | const nsINode* iter = node; |
292 | 0 | while ((iter = iter->GetParentNode())) { |
293 | 0 | node = iter; |
294 | 0 | } |
295 | 0 | return const_cast<nsINode*>(node); |
296 | 0 | }; |
297 | 0 |
|
298 | 0 | // There are four cases of interest here. nsINodes that are really: |
299 | 0 | // 1. nsIDocument nodes - Are always in the document. |
300 | 0 | // 2.a nsIContent nodes not in a shadow tree - Are either in the document, |
301 | 0 | // or mSubtreeRoot is updated in BindToTree/UnbindFromTree. |
302 | 0 | // 2.b nsIContent nodes in a shadow tree - Are never in the document, |
303 | 0 | // ignore mSubtreeRoot and return the containing shadow root. |
304 | 0 | // 4. Attr nodes - Are never in the document, and mSubtreeRoot |
305 | 0 | // is always 'this' (as set in nsINode's ctor). |
306 | 0 | nsINode* node; |
307 | 0 | if (IsInUncomposedDoc()) { |
308 | 0 | node = OwnerDocAsNode(); |
309 | 0 | } else if (IsContent()) { |
310 | 0 | ShadowRoot* containingShadow = AsContent()->GetContainingShadow(); |
311 | 0 | node = containingShadow ? containingShadow : mSubtreeRoot; |
312 | 0 | if (!node) { |
313 | 0 | NS_WARNING("Using SubtreeRoot() on unlinked element?"); |
314 | 0 | node = RootOfNode(this); |
315 | 0 | } |
316 | 0 | } else { |
317 | 0 | node = mSubtreeRoot; |
318 | 0 | } |
319 | 0 | MOZ_ASSERT(node, "Should always have a node here!"); |
320 | | #ifdef DEBUG |
321 | | { |
322 | | const nsINode* slowNode = RootOfNode(this); |
323 | | MOZ_ASSERT(slowNode == node, "These should always be in sync!"); |
324 | | } |
325 | | #endif |
326 | | return node; |
327 | 0 | } |
328 | | |
329 | | static nsIContent* GetRootForContentSubtree(nsIContent* aContent) |
330 | 0 | { |
331 | 0 | NS_ENSURE_TRUE(aContent, nullptr); |
332 | 0 |
|
333 | 0 | // Special case for ShadowRoot because the ShadowRoot itself is |
334 | 0 | // the root. This is necessary to prevent selection from crossing |
335 | 0 | // the ShadowRoot boundary. |
336 | 0 | ShadowRoot* containingShadow = aContent->GetContainingShadow(); |
337 | 0 | if (containingShadow) { |
338 | 0 | return containingShadow; |
339 | 0 | } |
340 | 0 | |
341 | 0 | nsIContent* stop = aContent->GetBindingParent(); |
342 | 0 | while (aContent) { |
343 | 0 | nsIContent* parent = aContent->GetParent(); |
344 | 0 | if (parent == stop) { |
345 | 0 | break; |
346 | 0 | } |
347 | 0 | aContent = parent; |
348 | 0 | } |
349 | 0 | return aContent; |
350 | 0 | } |
351 | | |
352 | | nsIContent* |
353 | | nsINode::GetSelectionRootContent(nsIPresShell* aPresShell) |
354 | 0 | { |
355 | 0 | NS_ENSURE_TRUE(aPresShell, nullptr); |
356 | 0 |
|
357 | 0 | if (IsDocument()) |
358 | 0 | return AsDocument()->GetRootElement(); |
359 | 0 | if (!IsContent()) |
360 | 0 | return nullptr; |
361 | 0 | |
362 | 0 | if (GetComposedDoc() != aPresShell->GetDocument()) { |
363 | 0 | return nullptr; |
364 | 0 | } |
365 | 0 | |
366 | 0 | if (static_cast<nsIContent*>(this)->HasIndependentSelection()) { |
367 | 0 | // This node should be a descendant of input/textarea editor. |
368 | 0 | nsIContent* content = GetTextEditorRootContent(); |
369 | 0 | if (content) |
370 | 0 | return content; |
371 | 0 | } |
372 | 0 | |
373 | 0 | nsPresContext* presContext = aPresShell->GetPresContext(); |
374 | 0 | if (presContext) { |
375 | 0 | HTMLEditor* htmlEditor = nsContentUtils::GetHTMLEditor(presContext); |
376 | 0 | if (htmlEditor) { |
377 | 0 | // This node is in HTML editor. |
378 | 0 | nsIDocument* doc = GetComposedDoc(); |
379 | 0 | if (!doc || doc->HasFlag(NODE_IS_EDITABLE) || |
380 | 0 | !HasFlag(NODE_IS_EDITABLE)) { |
381 | 0 | nsIContent* editorRoot = htmlEditor->GetRoot(); |
382 | 0 | NS_ENSURE_TRUE(editorRoot, nullptr); |
383 | 0 | return nsContentUtils::IsInSameAnonymousTree(this, editorRoot) ? |
384 | 0 | editorRoot : |
385 | 0 | GetRootForContentSubtree(static_cast<nsIContent*>(this)); |
386 | 0 | } |
387 | 0 | // If the document isn't editable but this is editable, this is in |
388 | 0 | // contenteditable. Use the editing host element for selection root. |
389 | 0 | return static_cast<nsIContent*>(this)->GetEditingHost(); |
390 | 0 | } |
391 | 0 | } |
392 | 0 |
|
393 | 0 | RefPtr<nsFrameSelection> fs = aPresShell->FrameSelection(); |
394 | 0 | nsIContent* content = fs->GetLimiter(); |
395 | 0 | if (!content) { |
396 | 0 | content = fs->GetAncestorLimiter(); |
397 | 0 | if (!content) { |
398 | 0 | nsIDocument* doc = aPresShell->GetDocument(); |
399 | 0 | NS_ENSURE_TRUE(doc, nullptr); |
400 | 0 | content = doc->GetRootElement(); |
401 | 0 | if (!content) |
402 | 0 | return nullptr; |
403 | 0 | } |
404 | 0 | } |
405 | 0 | |
406 | 0 | // This node might be in another subtree, if so, we should find this subtree's |
407 | 0 | // root. Otherwise, we can return the content simply. |
408 | 0 | NS_ENSURE_TRUE(content, nullptr); |
409 | 0 | if (!nsContentUtils::IsInSameAnonymousTree(this, content)) { |
410 | 0 | content = GetRootForContentSubtree(static_cast<nsIContent*>(this)); |
411 | 0 | // Fixup for ShadowRoot because the ShadowRoot itself does not have a frame. |
412 | 0 | // Use the host as the root. |
413 | 0 | if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(content)) { |
414 | 0 | content = shadowRoot->GetHost(); |
415 | 0 | } |
416 | 0 | } |
417 | 0 |
|
418 | 0 | return content; |
419 | 0 | } |
420 | | |
421 | | nsINodeList* |
422 | | nsINode::ChildNodes() |
423 | 0 | { |
424 | 0 | nsSlots* slots = Slots(); |
425 | 0 | if (!slots->mChildNodes) { |
426 | 0 | slots->mChildNodes = IsAttr() |
427 | 0 | ? new nsAttrChildContentList(this) |
428 | 0 | : new nsParentNodeChildContentList(this); |
429 | 0 | } |
430 | 0 |
|
431 | 0 | return slots->mChildNodes; |
432 | 0 | } |
433 | | |
434 | | nsIContent* |
435 | | nsINode::GetLastChild() const |
436 | 0 | { |
437 | 0 | return mFirstChild ? mFirstChild->mPreviousOrLastSibling : nullptr; |
438 | 0 | } |
439 | | |
440 | | void |
441 | | nsINode::InvalidateChildNodes() |
442 | 0 | { |
443 | 0 | MOZ_ASSERT(!IsAttr()); |
444 | 0 |
|
445 | 0 | nsSlots* slots = GetExistingSlots(); |
446 | 0 | if (!slots || !slots->mChildNodes) { |
447 | 0 | return; |
448 | 0 | } |
449 | 0 | |
450 | 0 | auto childNodes = |
451 | 0 | static_cast<nsParentNodeChildContentList*>(slots->mChildNodes.get()); |
452 | 0 | childNodes->InvalidateCache(); |
453 | 0 | } |
454 | | |
455 | | void |
456 | | nsINode::GetTextContentInternal(nsAString& aTextContent, OOMReporter& aError) |
457 | 0 | { |
458 | 0 | SetDOMStringToNull(aTextContent); |
459 | 0 | } |
460 | | |
461 | | DocumentOrShadowRoot* |
462 | | nsINode::GetUncomposedDocOrConnectedShadowRoot() const |
463 | 0 | { |
464 | 0 | if (IsInUncomposedDoc()) { |
465 | 0 | return OwnerDoc(); |
466 | 0 | } |
467 | 0 | |
468 | 0 | if (IsInComposedDoc() && IsInShadowTree()) { |
469 | 0 | return AsContent()->GetContainingShadow(); |
470 | 0 | } |
471 | 0 | |
472 | 0 | return nullptr; |
473 | 0 | } |
474 | | |
475 | | #ifdef DEBUG |
476 | | void |
477 | | nsINode::CheckNotNativeAnonymous() const |
478 | | { |
479 | | if (!IsContent()) |
480 | | return; |
481 | | nsIContent* content = static_cast<const nsIContent *>(this)->GetBindingParent(); |
482 | | while (content) { |
483 | | if (content->IsRootOfNativeAnonymousSubtree()) { |
484 | | NS_ERROR("Element not marked to be in native anonymous subtree!"); |
485 | | break; |
486 | | } |
487 | | content = content->GetBindingParent(); |
488 | | } |
489 | | } |
490 | | #endif |
491 | | |
492 | | bool |
493 | | nsINode::IsInAnonymousSubtree() const |
494 | 0 | { |
495 | 0 | if (!IsContent()) { |
496 | 0 | return false; |
497 | 0 | } |
498 | 0 | |
499 | 0 | return AsContent()->IsInAnonymousSubtree(); |
500 | 0 | } |
501 | | |
502 | | std::ostream& |
503 | | operator<<(std::ostream& aStream, const nsINode& aNode) |
504 | 0 | { |
505 | 0 | nsAutoString elemDesc; |
506 | 0 | const nsINode* curr = &aNode; |
507 | 0 | while (curr) { |
508 | 0 | const nsString& localName = curr->LocalName(); |
509 | 0 | nsString id; |
510 | 0 | if (curr->IsElement()) { |
511 | 0 | curr->AsElement()->GetId(id); |
512 | 0 | } |
513 | 0 |
|
514 | 0 | if (!elemDesc.IsEmpty()) { |
515 | 0 | elemDesc = elemDesc + NS_LITERAL_STRING("."); |
516 | 0 | } |
517 | 0 |
|
518 | 0 | elemDesc = elemDesc + localName; |
519 | 0 |
|
520 | 0 | if (!id.IsEmpty()) { |
521 | 0 | elemDesc = elemDesc + NS_LITERAL_STRING("['") + id + |
522 | 0 | NS_LITERAL_STRING("']"); |
523 | 0 | } |
524 | 0 |
|
525 | 0 | curr = curr->GetParentNode(); |
526 | 0 | } |
527 | 0 |
|
528 | 0 | NS_ConvertUTF16toUTF8 str(elemDesc); |
529 | 0 | return aStream << str.get(); |
530 | 0 | } |
531 | | |
532 | | SVGUseElement* |
533 | | nsINode::DoGetContainingSVGUseShadowHost() const |
534 | 0 | { |
535 | 0 | MOZ_ASSERT(IsInShadowTree()); |
536 | 0 | return SVGUseElement::FromNodeOrNull(AsContent()->GetContainingShadowHost()); |
537 | 0 | } |
538 | | |
539 | | bool |
540 | | nsINode::IsInUAWidget() const |
541 | 0 | { |
542 | 0 | if (!IsInShadowTree()) { |
543 | 0 | return false; |
544 | 0 | } |
545 | 0 | ShadowRoot* shadowRoot = AsContent()->GetContainingShadow(); |
546 | 0 | return shadowRoot && shadowRoot->IsUAWidget(); |
547 | 0 | } |
548 | | |
549 | | void |
550 | | nsINode::GetNodeValueInternal(nsAString& aNodeValue) |
551 | 0 | { |
552 | 0 | SetDOMStringToNull(aNodeValue); |
553 | 0 | } |
554 | | |
555 | | nsINode* |
556 | | nsINode::RemoveChild(nsINode& aOldChild, ErrorResult& aError) |
557 | 0 | { |
558 | 0 | if (!aOldChild.IsContent()) { |
559 | 0 | // aOldChild can't be one of our children. |
560 | 0 | aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); |
561 | 0 | return nullptr; |
562 | 0 | } |
563 | 0 | |
564 | 0 | if (aOldChild.GetParentNode() == this) { |
565 | 0 | nsContentUtils::MaybeFireNodeRemoved(&aOldChild, this); |
566 | 0 | } |
567 | 0 |
|
568 | 0 | // Check again, we may not be the child's parent anymore. |
569 | 0 | // Can be triggered by dom/base/crashtests/293388-1.html |
570 | 0 | if (aOldChild.AsContent()->IsRootOfAnonymousSubtree() || |
571 | 0 | aOldChild.GetParentNode() != this) { |
572 | 0 | // aOldChild isn't one of our children. |
573 | 0 | aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); |
574 | 0 | return nullptr; |
575 | 0 | } |
576 | 0 | |
577 | 0 | RemoveChildNode(aOldChild.AsContent(), true); |
578 | 0 | return &aOldChild; |
579 | 0 | } |
580 | | |
581 | | void |
582 | | nsINode::Normalize() |
583 | 0 | { |
584 | 0 | // First collect list of nodes to be removed |
585 | 0 | AutoTArray<nsCOMPtr<nsIContent>, 50> nodes; |
586 | 0 |
|
587 | 0 | bool canMerge = false; |
588 | 0 | for (nsIContent* node = this->GetFirstChild(); |
589 | 0 | node; |
590 | 0 | node = node->GetNextNode(this)) { |
591 | 0 | if (node->NodeType() != TEXT_NODE) { |
592 | 0 | canMerge = false; |
593 | 0 | continue; |
594 | 0 | } |
595 | 0 | |
596 | 0 | if (canMerge || node->TextLength() == 0) { |
597 | 0 | // No need to touch canMerge. That way we can merge across empty |
598 | 0 | // textnodes if and only if the node before is a textnode |
599 | 0 | nodes.AppendElement(node); |
600 | 0 | } |
601 | 0 | else { |
602 | 0 | canMerge = true; |
603 | 0 | } |
604 | 0 |
|
605 | 0 | // If there's no following sibling, then we need to ensure that we don't |
606 | 0 | // collect following siblings of our (grand)parent as to-be-removed |
607 | 0 | canMerge = canMerge && !!node->GetNextSibling(); |
608 | 0 | } |
609 | 0 |
|
610 | 0 | if (nodes.IsEmpty()) { |
611 | 0 | return; |
612 | 0 | } |
613 | 0 | |
614 | 0 | // We're relying on mozAutoSubtreeModified to keep the doc alive here. |
615 | 0 | nsIDocument* doc = OwnerDoc(); |
616 | 0 |
|
617 | 0 | // Batch possible DOMSubtreeModified events. |
618 | 0 | mozAutoSubtreeModified subtree(doc, nullptr); |
619 | 0 |
|
620 | 0 | // Fire all DOMNodeRemoved events. Optimize the common case of there being |
621 | 0 | // no listeners |
622 | 0 | bool hasRemoveListeners = nsContentUtils:: |
623 | 0 | HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED); |
624 | 0 | if (hasRemoveListeners) { |
625 | 0 | for (uint32_t i = 0; i < nodes.Length(); ++i) { |
626 | 0 | nsINode* parentNode = nodes[i]->GetParentNode(); |
627 | 0 | if (parentNode) { // Node may have already been removed. |
628 | 0 | nsContentUtils::MaybeFireNodeRemoved(nodes[i], parentNode); |
629 | 0 | } |
630 | 0 | } |
631 | 0 | } |
632 | 0 |
|
633 | 0 | mozAutoDocUpdate batch(doc, true); |
634 | 0 |
|
635 | 0 | // Merge and remove all nodes |
636 | 0 | nsAutoString tmpStr; |
637 | 0 | for (uint32_t i = 0; i < nodes.Length(); ++i) { |
638 | 0 | nsIContent* node = nodes[i]; |
639 | 0 | // Merge with previous node unless empty |
640 | 0 | const nsTextFragment* text = node->GetText(); |
641 | 0 | if (text->GetLength()) { |
642 | 0 | nsIContent* target = node->GetPreviousSibling(); |
643 | 0 | NS_ASSERTION((target && target->NodeType() == TEXT_NODE) || |
644 | 0 | hasRemoveListeners, |
645 | 0 | "Should always have a previous text sibling unless " |
646 | 0 | "mutation events messed us up"); |
647 | 0 | if (!hasRemoveListeners || |
648 | 0 | (target && target->NodeType() == TEXT_NODE)) { |
649 | 0 | nsTextNode* t = static_cast<nsTextNode*>(target); |
650 | 0 | if (text->Is2b()) { |
651 | 0 | t->AppendTextForNormalize(text->Get2b(), text->GetLength(), true, node); |
652 | 0 | } |
653 | 0 | else { |
654 | 0 | tmpStr.Truncate(); |
655 | 0 | text->AppendTo(tmpStr); |
656 | 0 | t->AppendTextForNormalize(tmpStr.get(), tmpStr.Length(), true, node); |
657 | 0 | } |
658 | 0 | } |
659 | 0 | } |
660 | 0 |
|
661 | 0 | // Remove node |
662 | 0 | nsCOMPtr<nsINode> parent = node->GetParentNode(); |
663 | 0 | NS_ASSERTION(parent || hasRemoveListeners, |
664 | 0 | "Should always have a parent unless " |
665 | 0 | "mutation events messed us up"); |
666 | 0 | if (parent) { |
667 | 0 | parent->RemoveChildNode(node, true); |
668 | 0 | } |
669 | 0 | } |
670 | 0 | } |
671 | | |
672 | | nsresult |
673 | | nsINode::GetBaseURI(nsAString &aURI) const |
674 | 0 | { |
675 | 0 | nsCOMPtr<nsIURI> baseURI = GetBaseURI(); |
676 | 0 |
|
677 | 0 | nsAutoCString spec; |
678 | 0 | if (baseURI) { |
679 | 0 | nsresult rv = baseURI->GetSpec(spec); |
680 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
681 | 0 | } |
682 | 0 |
|
683 | 0 | CopyUTF8toUTF16(spec, aURI); |
684 | 0 | return NS_OK; |
685 | 0 | } |
686 | | |
687 | | void |
688 | | nsINode::GetBaseURIFromJS(nsAString& aURI, |
689 | | CallerType aCallerType, |
690 | | ErrorResult& aRv) const |
691 | 0 | { |
692 | 0 | nsCOMPtr<nsIURI> baseURI = GetBaseURI(aCallerType == CallerType::System); |
693 | 0 | nsAutoCString spec; |
694 | 0 | if (baseURI) { |
695 | 0 | nsresult res = baseURI->GetSpec(spec); |
696 | 0 | if (NS_FAILED(res)) { |
697 | 0 | aRv.Throw(res); |
698 | 0 | return; |
699 | 0 | } |
700 | 0 | } |
701 | 0 | CopyUTF8toUTF16(spec, aURI); |
702 | 0 | } |
703 | | |
704 | | already_AddRefed<nsIURI> |
705 | | nsINode::GetBaseURIObject() const |
706 | 0 | { |
707 | 0 | return GetBaseURI(true); |
708 | 0 | } |
709 | | |
710 | | void |
711 | | nsINode::LookupPrefix(const nsAString& aNamespaceURI, nsAString& aPrefix) |
712 | 0 | { |
713 | 0 | Element *element = GetNameSpaceElement(); |
714 | 0 | if (element) { |
715 | 0 | // XXX Waiting for DOM spec to list error codes. |
716 | 0 |
|
717 | 0 | // Trace up the content parent chain looking for the namespace |
718 | 0 | // declaration that defines the aNamespaceURI namespace. Once found, |
719 | 0 | // return the prefix (i.e. the attribute localName). |
720 | 0 | for (nsIContent* content = element; content; |
721 | 0 | content = content->GetParent()) { |
722 | 0 | if (!content->IsElement()) { |
723 | 0 | continue; |
724 | 0 | } |
725 | 0 | |
726 | 0 | Element* element = content->AsElement(); |
727 | 0 | uint32_t attrCount = element->GetAttrCount(); |
728 | 0 |
|
729 | 0 | for (uint32_t i = 0; i < attrCount; ++i) { |
730 | 0 | const nsAttrName* name = element->GetAttrNameAt(i); |
731 | 0 |
|
732 | 0 | if (name->NamespaceEquals(kNameSpaceID_XMLNS) && |
733 | 0 | element->AttrValueIs(kNameSpaceID_XMLNS, name->LocalName(), |
734 | 0 | aNamespaceURI, eCaseMatters)) { |
735 | 0 | // If the localName is "xmlns", the prefix we output should be |
736 | 0 | // null. |
737 | 0 | nsAtom* localName = name->LocalName(); |
738 | 0 |
|
739 | 0 | if (localName != nsGkAtoms::xmlns) { |
740 | 0 | localName->ToString(aPrefix); |
741 | 0 | } |
742 | 0 | else { |
743 | 0 | SetDOMStringToNull(aPrefix); |
744 | 0 | } |
745 | 0 | return; |
746 | 0 | } |
747 | 0 | } |
748 | 0 | } |
749 | 0 | } |
750 | 0 |
|
751 | 0 | SetDOMStringToNull(aPrefix); |
752 | 0 | } |
753 | | |
754 | | uint16_t |
755 | | nsINode::CompareDocumentPosition(nsINode& aOtherNode, |
756 | | int32_t* aThisIndex, |
757 | | int32_t* aOtherIndex) const |
758 | 0 | { |
759 | 0 | if (this == &aOtherNode) { |
760 | 0 | return 0; |
761 | 0 | } |
762 | 0 | if (GetPreviousSibling() == &aOtherNode) { |
763 | 0 | MOZ_ASSERT(GetParentNode() == aOtherNode.GetParentNode()); |
764 | 0 | return Node_Binding::DOCUMENT_POSITION_PRECEDING; |
765 | 0 | } |
766 | 0 | if (GetNextSibling() == &aOtherNode) { |
767 | 0 | MOZ_ASSERT(GetParentNode() == aOtherNode.GetParentNode()); |
768 | 0 | return Node_Binding::DOCUMENT_POSITION_FOLLOWING; |
769 | 0 | } |
770 | 0 |
|
771 | 0 | AutoTArray<const nsINode*, 32> parents1, parents2; |
772 | 0 |
|
773 | 0 | const nsINode* node1 = &aOtherNode; |
774 | 0 | const nsINode* node2 = this; |
775 | 0 |
|
776 | 0 | // Check if either node is an attribute |
777 | 0 | const Attr* attr1 = Attr::FromNode(node1); |
778 | 0 | if (attr1) { |
779 | 0 | const Element* elem = attr1->GetElement(); |
780 | 0 | // If there is an owner element add the attribute |
781 | 0 | // to the chain and walk up to the element |
782 | 0 | if (elem) { |
783 | 0 | node1 = elem; |
784 | 0 | parents1.AppendElement(attr1); |
785 | 0 | } |
786 | 0 | } |
787 | 0 | if (auto* attr2 = Attr::FromNode(node2)) { |
788 | 0 | const Element* elem = attr2->GetElement(); |
789 | 0 | if (elem == node1 && attr1) { |
790 | 0 | // Both nodes are attributes on the same element. |
791 | 0 | // Compare position between the attributes. |
792 | 0 |
|
793 | 0 | uint32_t i; |
794 | 0 | const nsAttrName* attrName; |
795 | 0 | for (i = 0; (attrName = elem->GetAttrNameAt(i)); ++i) { |
796 | 0 | if (attrName->Equals(attr1->NodeInfo())) { |
797 | 0 | NS_ASSERTION(!attrName->Equals(attr2->NodeInfo()), |
798 | 0 | "Different attrs at same position"); |
799 | 0 | return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | |
800 | 0 | Node_Binding::DOCUMENT_POSITION_PRECEDING; |
801 | 0 | } |
802 | 0 | if (attrName->Equals(attr2->NodeInfo())) { |
803 | 0 | return Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | |
804 | 0 | Node_Binding::DOCUMENT_POSITION_FOLLOWING; |
805 | 0 | } |
806 | 0 | } |
807 | 0 | MOZ_ASSERT_UNREACHABLE("neither attribute in the element"); |
808 | 0 | return Node_Binding::DOCUMENT_POSITION_DISCONNECTED; |
809 | 0 | } |
810 | 0 | |
811 | 0 | if (elem) { |
812 | 0 | node2 = elem; |
813 | 0 | parents2.AppendElement(attr2); |
814 | 0 | } |
815 | 0 | } |
816 | 0 |
|
817 | 0 | // We now know that both nodes are either nsIContents or nsIDocuments. |
818 | 0 | // If either node started out as an attribute, that attribute will have |
819 | 0 | // the same relative position as its ownerElement, except if the |
820 | 0 | // ownerElement ends up being the container for the other node |
821 | 0 |
|
822 | 0 | // Build the chain of parents |
823 | 0 | do { |
824 | 0 | parents1.AppendElement(node1); |
825 | 0 | node1 = node1->GetParentNode(); |
826 | 0 | } while (node1); |
827 | 0 | do { |
828 | 0 | parents2.AppendElement(node2); |
829 | 0 | node2 = node2->GetParentNode(); |
830 | 0 | } while (node2); |
831 | 0 |
|
832 | 0 | // Check if the nodes are disconnected. |
833 | 0 | uint32_t pos1 = parents1.Length(); |
834 | 0 | uint32_t pos2 = parents2.Length(); |
835 | 0 | const nsINode* top1 = parents1.ElementAt(--pos1); |
836 | 0 | const nsINode* top2 = parents2.ElementAt(--pos2); |
837 | 0 | if (top1 != top2) { |
838 | 0 | return top1 < top2 ? |
839 | 0 | (Node_Binding::DOCUMENT_POSITION_PRECEDING | |
840 | 0 | Node_Binding::DOCUMENT_POSITION_DISCONNECTED | |
841 | 0 | Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC) : |
842 | 0 | (Node_Binding::DOCUMENT_POSITION_FOLLOWING | |
843 | 0 | Node_Binding::DOCUMENT_POSITION_DISCONNECTED | |
844 | 0 | Node_Binding::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC); |
845 | 0 | } |
846 | 0 |
|
847 | 0 | // Find where the parent chain differs and check indices in the parent. |
848 | 0 | const nsINode* parent = top1; |
849 | 0 | uint32_t len; |
850 | 0 | for (len = std::min(pos1, pos2); len > 0; --len) { |
851 | 0 | const nsINode* child1 = parents1.ElementAt(--pos1); |
852 | 0 | const nsINode* child2 = parents2.ElementAt(--pos2); |
853 | 0 | if (child1 != child2) { |
854 | 0 | // child1 or child2 can be an attribute here. This will work fine since |
855 | 0 | // ComputeIndexOf will return -1 for the attribute making the |
856 | 0 | // attribute be considered before any child. |
857 | 0 | int32_t child1Index; |
858 | 0 | bool cachedChild1Index = false; |
859 | 0 | if (&aOtherNode == child1 && aOtherIndex) { |
860 | 0 | cachedChild1Index = true; |
861 | 0 | child1Index = *aOtherIndex != -1 ? |
862 | 0 | *aOtherIndex : parent->ComputeIndexOf(child1); |
863 | 0 | } else { |
864 | 0 | child1Index = parent->ComputeIndexOf(child1); |
865 | 0 | } |
866 | 0 |
|
867 | 0 | int32_t child2Index; |
868 | 0 | bool cachedChild2Index = false; |
869 | 0 | if (this == child2 && aThisIndex) { |
870 | 0 | cachedChild2Index = true; |
871 | 0 | child2Index = *aThisIndex != -1 ? |
872 | 0 | *aThisIndex : parent->ComputeIndexOf(child2); |
873 | 0 | } else { |
874 | 0 | child2Index = parent->ComputeIndexOf(child2); |
875 | 0 | } |
876 | 0 |
|
877 | 0 | uint16_t retVal = child1Index < child2Index ? |
878 | 0 | Node_Binding::DOCUMENT_POSITION_PRECEDING : |
879 | 0 | Node_Binding::DOCUMENT_POSITION_FOLLOWING; |
880 | 0 |
|
881 | 0 | if (cachedChild1Index) { |
882 | 0 | *aOtherIndex = child1Index; |
883 | 0 | } |
884 | 0 | if (cachedChild2Index) { |
885 | 0 | *aThisIndex = child2Index; |
886 | 0 | } |
887 | 0 |
|
888 | 0 | return retVal; |
889 | 0 | } |
890 | 0 | parent = child1; |
891 | 0 | } |
892 | 0 |
|
893 | 0 | // We hit the end of one of the parent chains without finding a difference |
894 | 0 | // between the chains. That must mean that one node is an ancestor of the |
895 | 0 | // other. The one with the shortest chain must be the ancestor. |
896 | 0 | return pos1 < pos2 ? |
897 | 0 | (Node_Binding::DOCUMENT_POSITION_PRECEDING | |
898 | 0 | Node_Binding::DOCUMENT_POSITION_CONTAINS) : |
899 | 0 | (Node_Binding::DOCUMENT_POSITION_FOLLOWING | |
900 | 0 | Node_Binding::DOCUMENT_POSITION_CONTAINED_BY); |
901 | 0 | } |
902 | | |
903 | | bool |
904 | | nsINode::IsSameNode(nsINode *other) |
905 | 0 | { |
906 | 0 | return other == this; |
907 | 0 | } |
908 | | |
909 | | bool |
910 | | nsINode::IsEqualNode(nsINode* aOther) |
911 | 0 | { |
912 | 0 | if (!aOther) { |
913 | 0 | return false; |
914 | 0 | } |
915 | 0 | |
916 | 0 | nsAutoString string1, string2; |
917 | 0 |
|
918 | 0 | nsINode* node1 = this; |
919 | 0 | nsINode* node2 = aOther; |
920 | 0 | do { |
921 | 0 | uint16_t nodeType = node1->NodeType(); |
922 | 0 | if (nodeType != node2->NodeType()) { |
923 | 0 | return false; |
924 | 0 | } |
925 | 0 | |
926 | 0 | mozilla::dom::NodeInfo* nodeInfo1 = node1->mNodeInfo; |
927 | 0 | mozilla::dom::NodeInfo* nodeInfo2 = node2->mNodeInfo; |
928 | 0 | if (!nodeInfo1->Equals(nodeInfo2) || |
929 | 0 | nodeInfo1->GetExtraName() != nodeInfo2->GetExtraName()) { |
930 | 0 | return false; |
931 | 0 | } |
932 | 0 | |
933 | 0 | switch(nodeType) { |
934 | 0 | case ELEMENT_NODE: |
935 | 0 | { |
936 | 0 | // Both are elements (we checked that their nodeinfos are equal). Do the |
937 | 0 | // check on attributes. |
938 | 0 | Element* element1 = node1->AsElement(); |
939 | 0 | Element* element2 = node2->AsElement(); |
940 | 0 | uint32_t attrCount = element1->GetAttrCount(); |
941 | 0 | if (attrCount != element2->GetAttrCount()) { |
942 | 0 | return false; |
943 | 0 | } |
944 | 0 | |
945 | 0 | // Iterate over attributes. |
946 | 0 | for (uint32_t i = 0; i < attrCount; ++i) { |
947 | 0 | const nsAttrName* attrName = element1->GetAttrNameAt(i); |
948 | | #ifdef DEBUG |
949 | | bool hasAttr = |
950 | | #endif |
951 | | element1->GetAttr(attrName->NamespaceID(), attrName->LocalName(), |
952 | 0 | string1); |
953 | 0 | NS_ASSERTION(hasAttr, "Why don't we have an attr?"); |
954 | 0 |
|
955 | 0 | if (!element2->AttrValueIs(attrName->NamespaceID(), |
956 | 0 | attrName->LocalName(), |
957 | 0 | string1, |
958 | 0 | eCaseMatters)) { |
959 | 0 | return false; |
960 | 0 | } |
961 | 0 | } |
962 | 0 | break; |
963 | 0 | } |
964 | 0 | case TEXT_NODE: |
965 | 0 | case COMMENT_NODE: |
966 | 0 | case CDATA_SECTION_NODE: |
967 | 0 | case PROCESSING_INSTRUCTION_NODE: |
968 | 0 | { |
969 | 0 | MOZ_ASSERT(node1->IsCharacterData()); |
970 | 0 | MOZ_ASSERT(node2->IsCharacterData()); |
971 | 0 | string1.Truncate(); |
972 | 0 | static_cast<CharacterData*>(node1)->AppendTextTo(string1); |
973 | 0 | string2.Truncate(); |
974 | 0 | static_cast<CharacterData*>(node2)->AppendTextTo(string2); |
975 | 0 |
|
976 | 0 | if (!string1.Equals(string2)) { |
977 | 0 | return false; |
978 | 0 | } |
979 | 0 | |
980 | 0 | break; |
981 | 0 | } |
982 | 0 | case DOCUMENT_NODE: |
983 | 0 | case DOCUMENT_FRAGMENT_NODE: |
984 | 0 | break; |
985 | 0 | case ATTRIBUTE_NODE: |
986 | 0 | { |
987 | 0 | NS_ASSERTION(node1 == this && node2 == aOther, |
988 | 0 | "Did we come upon an attribute node while walking a " |
989 | 0 | "subtree?"); |
990 | 0 | node1->GetNodeValue(string1); |
991 | 0 | node2->GetNodeValue(string2); |
992 | 0 |
|
993 | 0 | // Returning here as to not bother walking subtree. And there is no |
994 | 0 | // risk that we're half way through walking some other subtree since |
995 | 0 | // attribute nodes doesn't appear in subtrees. |
996 | 0 | return string1.Equals(string2); |
997 | 0 | } |
998 | 0 | case DOCUMENT_TYPE_NODE: |
999 | 0 | { |
1000 | 0 | DocumentType* docType1 = static_cast<DocumentType*>(node1); |
1001 | 0 | DocumentType* docType2 = static_cast<DocumentType*>(node2); |
1002 | 0 |
|
1003 | 0 | // Public ID |
1004 | 0 | docType1->GetPublicId(string1); |
1005 | 0 | docType2->GetPublicId(string2); |
1006 | 0 | if (!string1.Equals(string2)) { |
1007 | 0 | return false; |
1008 | 0 | } |
1009 | 0 | |
1010 | 0 | // System ID |
1011 | 0 | docType1->GetSystemId(string1); |
1012 | 0 | docType2->GetSystemId(string2); |
1013 | 0 | if (!string1.Equals(string2)) { |
1014 | 0 | return false; |
1015 | 0 | } |
1016 | 0 | |
1017 | 0 | break; |
1018 | 0 | } |
1019 | 0 | default: |
1020 | 0 | MOZ_ASSERT(false, "Unknown node type"); |
1021 | 0 | } |
1022 | 0 |
|
1023 | 0 | nsINode* nextNode = node1->GetFirstChild(); |
1024 | 0 | if (nextNode) { |
1025 | 0 | node1 = nextNode; |
1026 | 0 | node2 = node2->GetFirstChild(); |
1027 | 0 | } |
1028 | 0 | else { |
1029 | 0 | if (node2->GetFirstChild()) { |
1030 | 0 | // node2 has a firstChild, but node1 doesn't |
1031 | 0 | return false; |
1032 | 0 | } |
1033 | 0 | |
1034 | 0 | // Find next sibling, possibly walking parent chain. |
1035 | 0 | while (1) { |
1036 | 0 | if (node1 == this) { |
1037 | 0 | NS_ASSERTION(node2 == aOther, "Should have reached the start node " |
1038 | 0 | "for both trees at the same time"); |
1039 | 0 | return true; |
1040 | 0 | } |
1041 | 0 |
|
1042 | 0 | nextNode = node1->GetNextSibling(); |
1043 | 0 | if (nextNode) { |
1044 | 0 | node1 = nextNode; |
1045 | 0 | node2 = node2->GetNextSibling(); |
1046 | 0 | break; |
1047 | 0 | } |
1048 | 0 | |
1049 | 0 | if (node2->GetNextSibling()) { |
1050 | 0 | // node2 has a nextSibling, but node1 doesn't |
1051 | 0 | return false; |
1052 | 0 | } |
1053 | 0 | |
1054 | 0 | node1 = node1->GetParentNode(); |
1055 | 0 | node2 = node2->GetParentNode(); |
1056 | 0 | NS_ASSERTION(node1 && node2, "no parent while walking subtree"); |
1057 | 0 | } |
1058 | 0 | } |
1059 | 0 | } while(node2); |
1060 | 0 |
|
1061 | 0 | return false; |
1062 | 0 | } |
1063 | | |
1064 | | void |
1065 | | nsINode::LookupNamespaceURI(const nsAString& aNamespacePrefix, |
1066 | | nsAString& aNamespaceURI) |
1067 | 0 | { |
1068 | 0 | Element *element = GetNameSpaceElement(); |
1069 | 0 | if (!element || |
1070 | 0 | NS_FAILED(element->LookupNamespaceURIInternal(aNamespacePrefix, |
1071 | 0 | aNamespaceURI))) { |
1072 | 0 | SetDOMStringToNull(aNamespaceURI); |
1073 | 0 | } |
1074 | 0 | } |
1075 | | |
1076 | | bool |
1077 | | nsINode::ComputeDefaultWantsUntrusted(ErrorResult& aRv) |
1078 | 0 | { |
1079 | 0 | return !nsContentUtils::IsChromeDoc(OwnerDoc()); |
1080 | 0 | } |
1081 | | |
1082 | | void |
1083 | | nsINode::GetBoxQuads(const BoxQuadOptions& aOptions, |
1084 | | nsTArray<RefPtr<DOMQuad> >& aResult, |
1085 | | CallerType aCallerType, |
1086 | | mozilla::ErrorResult& aRv) |
1087 | 0 | { |
1088 | 0 | mozilla::GetBoxQuads(this, aOptions, aResult, aCallerType, aRv); |
1089 | 0 | } |
1090 | | |
1091 | | already_AddRefed<DOMQuad> |
1092 | | nsINode::ConvertQuadFromNode(DOMQuad& aQuad, |
1093 | | const GeometryNode& aFrom, |
1094 | | const ConvertCoordinateOptions& aOptions, |
1095 | | CallerType aCallerType, |
1096 | | ErrorResult& aRv) |
1097 | 0 | { |
1098 | 0 | return mozilla::ConvertQuadFromNode(this, aQuad, aFrom, aOptions, aCallerType, |
1099 | 0 | aRv); |
1100 | 0 | } |
1101 | | |
1102 | | already_AddRefed<DOMQuad> |
1103 | | nsINode::ConvertRectFromNode(DOMRectReadOnly& aRect, |
1104 | | const GeometryNode& aFrom, |
1105 | | const ConvertCoordinateOptions& aOptions, |
1106 | | CallerType aCallerType, |
1107 | | ErrorResult& aRv) |
1108 | 0 | { |
1109 | 0 | return mozilla::ConvertRectFromNode(this, aRect, aFrom, aOptions, aCallerType, |
1110 | 0 | aRv); |
1111 | 0 | } |
1112 | | |
1113 | | already_AddRefed<DOMPoint> |
1114 | | nsINode::ConvertPointFromNode(const DOMPointInit& aPoint, |
1115 | | const GeometryNode& aFrom, |
1116 | | const ConvertCoordinateOptions& aOptions, |
1117 | | CallerType aCallerType, |
1118 | | ErrorResult& aRv) |
1119 | 0 | { |
1120 | 0 | return mozilla::ConvertPointFromNode(this, aPoint, aFrom, aOptions, |
1121 | 0 | aCallerType, aRv); |
1122 | 0 | } |
1123 | | |
1124 | | bool |
1125 | | nsINode::DispatchEvent(Event& aEvent, CallerType aCallerType, ErrorResult& aRv) |
1126 | 0 | { |
1127 | 0 | // XXX sXBL/XBL2 issue -- do we really want the owner here? What |
1128 | 0 | // if that's the XBL document? Would we want its presshell? Or what? |
1129 | 0 | nsCOMPtr<nsIDocument> document = OwnerDoc(); |
1130 | 0 |
|
1131 | 0 | // Do nothing if the element does not belong to a document |
1132 | 0 | if (!document) { |
1133 | 0 | return true; |
1134 | 0 | } |
1135 | 0 | |
1136 | 0 | // Obtain a presentation shell |
1137 | 0 | RefPtr<nsPresContext> context = document->GetPresContext(); |
1138 | 0 |
|
1139 | 0 | nsEventStatus status = nsEventStatus_eIgnore; |
1140 | 0 | nsresult rv = |
1141 | 0 | EventDispatcher::DispatchDOMEvent(this, nullptr, &aEvent, context, &status); |
1142 | 0 | bool retval = !aEvent.DefaultPrevented(aCallerType); |
1143 | 0 | if (NS_FAILED(rv)) { |
1144 | 0 | aRv.Throw(rv); |
1145 | 0 | } |
1146 | 0 | return retval; |
1147 | 0 | } |
1148 | | |
1149 | | nsresult |
1150 | | nsINode::PostHandleEvent(EventChainPostVisitor& /*aVisitor*/) |
1151 | 0 | { |
1152 | 0 | return NS_OK; |
1153 | 0 | } |
1154 | | |
1155 | | EventListenerManager* |
1156 | | nsINode::GetOrCreateListenerManager() |
1157 | 0 | { |
1158 | 0 | return nsContentUtils::GetListenerManagerForNode(this); |
1159 | 0 | } |
1160 | | |
1161 | | EventListenerManager* |
1162 | | nsINode::GetExistingListenerManager() const |
1163 | 0 | { |
1164 | 0 | return nsContentUtils::GetExistingListenerManagerForNode(this); |
1165 | 0 | } |
1166 | | |
1167 | | nsPIDOMWindowOuter* |
1168 | | nsINode::GetOwnerGlobalForBindings() |
1169 | 0 | { |
1170 | 0 | bool dummy; |
1171 | 0 | auto* window = static_cast<nsGlobalWindowInner*>(OwnerDoc()->GetScriptHandlingObject(dummy)); |
1172 | 0 | return window ? nsPIDOMWindowOuter::GetFromCurrentInner(window->AsInner()) : nullptr; |
1173 | 0 | } |
1174 | | |
1175 | | nsIGlobalObject* |
1176 | | nsINode::GetOwnerGlobal() const |
1177 | 0 | { |
1178 | 0 | bool dummy; |
1179 | 0 | return OwnerDoc()->GetScriptHandlingObject(dummy); |
1180 | 0 | } |
1181 | | |
1182 | | void |
1183 | | nsINode::ChangeEditableDescendantCount(int32_t aDelta) |
1184 | 0 | { |
1185 | 0 | if (aDelta == 0) { |
1186 | 0 | return; |
1187 | 0 | } |
1188 | 0 | |
1189 | 0 | nsSlots* s = Slots(); |
1190 | 0 | MOZ_ASSERT(aDelta > 0 || |
1191 | 0 | s->mEditableDescendantCount >= (uint32_t) (-1 * aDelta)); |
1192 | 0 | s->mEditableDescendantCount += aDelta; |
1193 | 0 | } |
1194 | | |
1195 | | void |
1196 | | nsINode::ResetEditableDescendantCount() |
1197 | 0 | { |
1198 | 0 | nsSlots* s = GetExistingSlots(); |
1199 | 0 | if (s) { |
1200 | 0 | s->mEditableDescendantCount = 0; |
1201 | 0 | } |
1202 | 0 | } |
1203 | | |
1204 | | uint32_t |
1205 | | nsINode::EditableDescendantCount() |
1206 | 0 | { |
1207 | 0 | nsSlots* s = GetExistingSlots(); |
1208 | 0 | if (s) { |
1209 | 0 | return s->mEditableDescendantCount; |
1210 | 0 | } |
1211 | 0 | return 0; |
1212 | 0 | } |
1213 | | |
1214 | | bool |
1215 | | nsINode::UnoptimizableCCNode() const |
1216 | 0 | { |
1217 | 0 | const uintptr_t problematicFlags = (NODE_IS_ANONYMOUS_ROOT | |
1218 | 0 | NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE | |
1219 | 0 | NODE_IS_NATIVE_ANONYMOUS_ROOT | |
1220 | 0 | NODE_MAY_BE_IN_BINDING_MNGR); |
1221 | 0 | return HasFlag(problematicFlags) || |
1222 | 0 | NodeType() == ATTRIBUTE_NODE || |
1223 | 0 | // For strange cases like xbl:content/xbl:children |
1224 | 0 | (IsElement() && |
1225 | 0 | AsElement()->IsInNamespace(kNameSpaceID_XBL)); |
1226 | 0 | } |
1227 | | |
1228 | | /* static */ |
1229 | | bool |
1230 | | nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb) |
1231 | 0 | { |
1232 | 0 | if (MOZ_LIKELY(!cb.WantAllTraces())) { |
1233 | 0 | nsIDocument* currentDoc = tmp->GetComposedDoc(); |
1234 | 0 | if (currentDoc && |
1235 | 0 | nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) { |
1236 | 0 | return false; |
1237 | 0 | } |
1238 | 0 | |
1239 | 0 | if (nsCCUncollectableMarker::sGeneration) { |
1240 | 0 | // If we're black no need to traverse. |
1241 | 0 | if (tmp->HasKnownLiveWrapper() || tmp->InCCBlackTree()) { |
1242 | 0 | return false; |
1243 | 0 | } |
1244 | 0 | |
1245 | 0 | if (!tmp->UnoptimizableCCNode()) { |
1246 | 0 | // If we're in a black document, return early. |
1247 | 0 | if ((currentDoc && currentDoc->HasKnownLiveWrapper())) { |
1248 | 0 | return false; |
1249 | 0 | } |
1250 | 0 | // If we're not in anonymous content and we have a black parent, |
1251 | 0 | // return early. |
1252 | 0 | nsIContent* parent = tmp->GetParent(); |
1253 | 0 | if (parent && !parent->UnoptimizableCCNode() && |
1254 | 0 | parent->HasKnownLiveWrapper()) { |
1255 | 0 | MOZ_ASSERT(parent->ComputeIndexOf(tmp) >= 0, "Parent doesn't own us?"); |
1256 | 0 | return false; |
1257 | 0 | } |
1258 | 0 | } |
1259 | 0 | } |
1260 | 0 | } |
1261 | 0 |
|
1262 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfo) |
1263 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFirstChild) |
1264 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNextSibling) |
1265 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent()) |
1266 | 0 |
|
1267 | 0 | nsSlots *slots = tmp->GetExistingSlots(); |
1268 | 0 | if (slots) { |
1269 | 0 | slots->Traverse(cb); |
1270 | 0 | } |
1271 | 0 |
|
1272 | 0 | if (tmp->HasProperties()) { |
1273 | 0 | nsCOMArray<nsISupports>* objects = |
1274 | 0 | static_cast<nsCOMArray<nsISupports>*>(tmp->GetProperty(nsGkAtoms::keepobjectsalive)); |
1275 | 0 | if (objects) { |
1276 | 0 | for (int32_t i = 0; i < objects->Count(); ++i) { |
1277 | 0 | cb.NoteXPCOMChild(objects->ObjectAt(i)); |
1278 | 0 | } |
1279 | 0 | } |
1280 | 0 |
|
1281 | 0 | #ifdef ACCESSIBILITY |
1282 | 0 | AccessibleNode* anode = |
1283 | 0 | static_cast<AccessibleNode*>(tmp->GetProperty(nsGkAtoms::accessiblenode)); |
1284 | 0 | if (anode) { |
1285 | 0 | cb.NoteXPCOMChild(anode); |
1286 | 0 | } |
1287 | 0 | #endif |
1288 | 0 | } |
1289 | 0 |
|
1290 | 0 | if (tmp->NodeType() != DOCUMENT_NODE && |
1291 | 0 | tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) { |
1292 | 0 | nsContentUtils::TraverseListenerManager(tmp, cb); |
1293 | 0 | } |
1294 | 0 |
|
1295 | 0 | return true; |
1296 | 0 | } |
1297 | | |
1298 | | /* static */ |
1299 | | void |
1300 | | nsINode::Unlink(nsINode* tmp) |
1301 | 0 | { |
1302 | 0 | tmp->ReleaseWrapper(tmp); |
1303 | 0 |
|
1304 | 0 | nsSlots *slots = tmp->GetExistingSlots(); |
1305 | 0 | if (slots) { |
1306 | 0 | slots->Unlink(); |
1307 | 0 | } |
1308 | 0 |
|
1309 | 0 | if (tmp->NodeType() != DOCUMENT_NODE && |
1310 | 0 | tmp->HasFlag(NODE_HAS_LISTENERMANAGER)) { |
1311 | 0 | nsContentUtils::RemoveListenerManager(tmp); |
1312 | 0 | tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER); |
1313 | 0 | } |
1314 | 0 |
|
1315 | 0 | if (tmp->HasProperties()) { |
1316 | 0 | tmp->DeleteProperty(nsGkAtoms::keepobjectsalive); |
1317 | 0 | tmp->DeleteProperty(nsGkAtoms::accessiblenode); |
1318 | 0 | } |
1319 | 0 | } |
1320 | | |
1321 | | static void |
1322 | | AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode, ErrorResult& aError) |
1323 | 0 | { |
1324 | 0 | NS_ASSERTION(!aNode->GetParentNode(), |
1325 | 0 | "Should have removed from parent already"); |
1326 | 0 |
|
1327 | 0 | nsIDocument *doc = aParent->OwnerDoc(); |
1328 | 0 |
|
1329 | 0 | DebugOnly<nsINode*> adoptedNode = doc->AdoptNode(*aNode, aError); |
1330 | 0 |
|
1331 | | #ifdef DEBUG |
1332 | | if (!aError.Failed()) { |
1333 | | MOZ_ASSERT(aParent->OwnerDoc() == doc, |
1334 | | "ownerDoc chainged while adopting"); |
1335 | | MOZ_ASSERT(adoptedNode == aNode, "Uh, adopt node changed nodes?"); |
1336 | | MOZ_ASSERT(aParent->OwnerDoc() == aNode->OwnerDoc(), |
1337 | | "ownerDocument changed again after adopting!"); |
1338 | | } |
1339 | | #endif // DEBUG |
1340 | | } |
1341 | | |
1342 | | static void |
1343 | | CheckForOutdatedParent(nsINode* aParent, nsINode* aNode, ErrorResult& aError) |
1344 | 0 | { |
1345 | 0 | if (JSObject* existingObjUnrooted = aNode->GetWrapper()) { |
1346 | 0 | JS::Rooted<JSObject*> existingObj(RootingCx(), existingObjUnrooted); |
1347 | 0 |
|
1348 | 0 | AutoJSContext cx; |
1349 | 0 | nsIGlobalObject* global = aParent->OwnerDoc()->GetScopeObject(); |
1350 | 0 | MOZ_ASSERT(global); |
1351 | 0 |
|
1352 | 0 | if (JS::GetNonCCWObjectGlobal(existingObj) != |
1353 | 0 | global->GetGlobalJSObject()) { |
1354 | 0 | JSAutoRealm ar(cx, existingObj); |
1355 | 0 | ReparentWrapper(cx, existingObj, aError); |
1356 | 0 | } |
1357 | 0 | } |
1358 | 0 | } |
1359 | | |
1360 | | static nsresult |
1361 | | ReparentWrappersInSubtree(nsIContent* aRoot) |
1362 | 0 | { |
1363 | 0 | MOZ_ASSERT(ShouldUseXBLScope(aRoot)); |
1364 | 0 | // Start off with no global so we don't fire any error events on failure. |
1365 | 0 | AutoJSAPI jsapi; |
1366 | 0 | jsapi.Init(); |
1367 | 0 |
|
1368 | 0 | JSContext* cx = jsapi.cx(); |
1369 | 0 |
|
1370 | 0 | ErrorResult rv; |
1371 | 0 | JS::Rooted<JSObject*> reflector(cx); |
1372 | 0 | for (nsIContent* cur = aRoot; cur; cur = cur->GetNextNode(aRoot)) { |
1373 | 0 | if ((reflector = cur->GetWrapper())) { |
1374 | 0 | JSAutoRealm ar(cx, reflector); |
1375 | 0 | ReparentWrapper(cx, reflector, rv); |
1376 | 0 | rv.WouldReportJSException(); |
1377 | 0 | if (rv.Failed()) { |
1378 | 0 | // We _could_ consider BlastSubtreeToPieces here, but it's not really |
1379 | 0 | // needed. Having some nodes in here accessible to content while others |
1380 | 0 | // are not is probably OK. We just need to fail out of the actual |
1381 | 0 | // insertion, so they're not in the DOM. Returning a failure here will |
1382 | 0 | // do that. |
1383 | 0 | return rv.StealNSResult(); |
1384 | 0 | } |
1385 | 0 | } |
1386 | 0 | } |
1387 | 0 |
|
1388 | 0 | return NS_OK; |
1389 | 0 | } |
1390 | | |
1391 | | nsresult |
1392 | | nsINode::InsertChildBefore(nsIContent* aKid, nsIContent* aChildToInsertBefore, |
1393 | | bool aNotify) |
1394 | 0 | { |
1395 | 0 | if (!IsContainerNode()) { |
1396 | 0 | return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; |
1397 | 0 | } |
1398 | 0 | |
1399 | 0 | MOZ_ASSERT(!aKid->GetParentNode(), "Inserting node that already has parent"); |
1400 | 0 | MOZ_ASSERT(!IsAttr()); |
1401 | 0 |
|
1402 | 0 | // The id-handling code, and in the future possibly other code, need to |
1403 | 0 | // react to unexpected attribute changes. |
1404 | 0 | nsMutationGuard::DidMutate(); |
1405 | 0 |
|
1406 | 0 | // Do this before checking the child-count since this could cause mutations |
1407 | 0 | nsIDocument* doc = GetUncomposedDoc(); |
1408 | 0 | mozAutoDocUpdate updateBatch(GetComposedDoc(), aNotify); |
1409 | 0 |
|
1410 | 0 | if (OwnerDoc() != aKid->OwnerDoc()) { |
1411 | 0 | ErrorResult error; |
1412 | 0 | AdoptNodeIntoOwnerDoc(this, aKid, error); |
1413 | 0 |
|
1414 | 0 | // Need to WouldReportJSException() if our callee can throw a JS |
1415 | 0 | // exception (which it can) and we're neither propagating the |
1416 | 0 | // error out nor unconditionally suppressing it. |
1417 | 0 | error.WouldReportJSException(); |
1418 | 0 | if (NS_WARN_IF(error.Failed())) { |
1419 | 0 | return error.StealNSResult(); |
1420 | 0 | } |
1421 | 0 | } else if (OwnerDoc()->DidDocumentOpen()) { |
1422 | 0 | ErrorResult error; |
1423 | 0 | CheckForOutdatedParent(this, aKid, error); |
1424 | 0 |
|
1425 | 0 | // Need to WouldReportJSException() if our callee can throw a JS |
1426 | 0 | // exception (which it can) and we're neither propagating the |
1427 | 0 | // error out nor unconditionally suppressing it. |
1428 | 0 | error.WouldReportJSException(); |
1429 | 0 | if (NS_WARN_IF(error.Failed())) { |
1430 | 0 | return error.StealNSResult(); |
1431 | 0 | } |
1432 | 0 | } |
1433 | 0 | |
1434 | 0 | if (!aChildToInsertBefore) { |
1435 | 0 | AppendChildToChildList(aKid); |
1436 | 0 | } else { |
1437 | 0 | InsertChildToChildList(aKid, aChildToInsertBefore); |
1438 | 0 | } |
1439 | 0 |
|
1440 | 0 | nsIContent* parent = IsContent() ? AsContent() : nullptr; |
1441 | 0 |
|
1442 | 0 | bool wasInXBLScope = ShouldUseXBLScope(aKid); |
1443 | 0 | nsresult rv = aKid->BindToTree(doc, parent, |
1444 | 0 | parent ? parent->GetBindingParent() : nullptr); |
1445 | 0 | if (NS_SUCCEEDED(rv) && !wasInXBLScope && ShouldUseXBLScope(aKid)) { |
1446 | 0 | MOZ_ASSERT(ShouldUseXBLScope(this), |
1447 | 0 | "Why does the kid need to use an XBL scope?"); |
1448 | 0 | rv = ReparentWrappersInSubtree(aKid); |
1449 | 0 | } |
1450 | 0 | if (NS_FAILED(rv)) { |
1451 | 0 | DisconnectChild(aKid); |
1452 | 0 | aKid->UnbindFromTree(); |
1453 | 0 | return rv; |
1454 | 0 | } |
1455 | 0 | |
1456 | 0 | // Invalidate cached array of child nodes |
1457 | 0 | InvalidateChildNodes(); |
1458 | 0 |
|
1459 | 0 | NS_ASSERTION(aKid->GetParentNode() == this, |
1460 | 0 | "Did we run script inappropriately?"); |
1461 | 0 |
|
1462 | 0 | if (aNotify) { |
1463 | 0 | // Note that we always want to call ContentInserted when things are added |
1464 | 0 | // as kids to documents |
1465 | 0 | if (parent && !aChildToInsertBefore) { |
1466 | 0 | nsNodeUtils::ContentAppended(parent, aKid); |
1467 | 0 | } else { |
1468 | 0 | nsNodeUtils::ContentInserted(this, aKid); |
1469 | 0 | } |
1470 | 0 |
|
1471 | 0 | if (nsContentUtils::HasMutationListeners(aKid, |
1472 | 0 | NS_EVENT_BITS_MUTATION_NODEINSERTED, this)) { |
1473 | 0 | InternalMutationEvent mutation(true, eLegacyNodeInserted); |
1474 | 0 | mutation.mRelatedNode = this; |
1475 | 0 |
|
1476 | 0 | mozAutoSubtreeModified subtree(OwnerDoc(), this); |
1477 | 0 | (new AsyncEventDispatcher(aKid, mutation))->RunDOMEventWhenSafe(); |
1478 | 0 | } |
1479 | 0 | } |
1480 | 0 |
|
1481 | 0 | return NS_OK; |
1482 | 0 | } |
1483 | | |
1484 | | nsIContent* |
1485 | | nsINode::GetPreviousSibling() const |
1486 | 0 | { |
1487 | 0 | // Do not expose circular linked list |
1488 | 0 | if (mPreviousOrLastSibling && !mPreviousOrLastSibling->mNextSibling) { |
1489 | 0 | return nullptr; |
1490 | 0 | } |
1491 | 0 | return mPreviousOrLastSibling; |
1492 | 0 | } |
1493 | | |
1494 | | // CACHE_POINTER_SHIFT indicates how many steps to downshift the |this| pointer. |
1495 | | // It should be small enough to not cause collisions between adjecent objects, |
1496 | | // and large enough to make sure that all indexes are used. |
1497 | 0 | #define CACHE_POINTER_SHIFT 6 |
1498 | 0 | #define CACHE_NUM_SLOTS 128 |
1499 | 0 | #define CACHE_CHILD_LIMIT 10 |
1500 | | |
1501 | | #define CACHE_GET_INDEX(_parent) \ |
1502 | 0 | ((NS_PTR_TO_INT32(_parent) >> CACHE_POINTER_SHIFT) & \ |
1503 | 0 | (CACHE_NUM_SLOTS - 1)) |
1504 | | |
1505 | | struct IndexCacheSlot |
1506 | | { |
1507 | | const nsINode* mParent; |
1508 | | const nsINode* mChild; |
1509 | | int32_t mChildIndex; |
1510 | | }; |
1511 | | |
1512 | | static IndexCacheSlot sIndexCache[CACHE_NUM_SLOTS]; |
1513 | | |
1514 | | static inline void |
1515 | | AddChildAndIndexToCache(const nsINode* aParent, const nsINode* aChild, |
1516 | | int32_t aChildIndex) |
1517 | 0 | { |
1518 | 0 | uint32_t index = CACHE_GET_INDEX(aParent); |
1519 | 0 | sIndexCache[index].mParent = aParent; |
1520 | 0 | sIndexCache[index].mChild = aChild; |
1521 | 0 | sIndexCache[index].mChildIndex = aChildIndex; |
1522 | 0 | } |
1523 | | |
1524 | | static inline void |
1525 | | GetChildAndIndexFromCache(const nsINode* aParent, |
1526 | | const nsINode** aChild, |
1527 | | int32_t* aChildIndex) |
1528 | 0 | { |
1529 | 0 | uint32_t index = CACHE_GET_INDEX(aParent); |
1530 | 0 | if (sIndexCache[index].mParent == aParent) { |
1531 | 0 | *aChild = sIndexCache[index].mChild; |
1532 | 0 | *aChildIndex = sIndexCache[index].mChildIndex; |
1533 | 0 | } else { |
1534 | 0 | *aChild = nullptr; |
1535 | 0 | *aChildIndex = -1; |
1536 | 0 | } |
1537 | 0 | } |
1538 | | |
1539 | | static inline void |
1540 | | RemoveFromCache(const nsINode* aParent) |
1541 | 0 | { |
1542 | 0 | uint32_t index = CACHE_GET_INDEX(aParent); |
1543 | 0 | if (sIndexCache[index].mParent == aParent) { |
1544 | 0 | sIndexCache[index] = { nullptr, nullptr, -1 }; |
1545 | 0 | } |
1546 | 0 | } |
1547 | | |
1548 | | void |
1549 | | nsINode::AppendChildToChildList(nsIContent* aKid) |
1550 | 0 | { |
1551 | 0 | MOZ_ASSERT(aKid); |
1552 | 0 | MOZ_ASSERT(!aKid->mNextSibling); |
1553 | 0 |
|
1554 | 0 | RemoveFromCache(this); |
1555 | 0 |
|
1556 | 0 | if (mFirstChild) { |
1557 | 0 | nsIContent* lastChild = GetLastChild(); |
1558 | 0 | lastChild->mNextSibling = aKid; |
1559 | 0 | aKid->mPreviousOrLastSibling = lastChild; |
1560 | 0 | } else { |
1561 | 0 | mFirstChild = aKid; |
1562 | 0 | } |
1563 | 0 |
|
1564 | 0 | // Maintain link to the last child |
1565 | 0 | mFirstChild->mPreviousOrLastSibling = aKid; |
1566 | 0 | ++mChildCount; |
1567 | 0 | } |
1568 | | |
1569 | | void |
1570 | | nsINode::InsertChildToChildList(nsIContent* aKid, nsIContent* aNextSibling) |
1571 | 0 | { |
1572 | 0 | MOZ_ASSERT(aKid); |
1573 | 0 | MOZ_ASSERT(aNextSibling); |
1574 | 0 |
|
1575 | 0 | RemoveFromCache(this); |
1576 | 0 |
|
1577 | 0 | nsIContent* previousSibling = aNextSibling->mPreviousOrLastSibling; |
1578 | 0 | aNextSibling->mPreviousOrLastSibling = aKid; |
1579 | 0 | aKid->mPreviousOrLastSibling = previousSibling; |
1580 | 0 | aKid->mNextSibling = aNextSibling; |
1581 | 0 |
|
1582 | 0 | if (aNextSibling == mFirstChild) { |
1583 | 0 | MOZ_ASSERT(!previousSibling->mNextSibling); |
1584 | 0 | mFirstChild = aKid; |
1585 | 0 | } else { |
1586 | 0 | previousSibling->mNextSibling = aKid; |
1587 | 0 | } |
1588 | 0 |
|
1589 | 0 | ++mChildCount; |
1590 | 0 | } |
1591 | | |
1592 | | void |
1593 | | nsINode::DisconnectChild(nsIContent* aKid) |
1594 | 0 | { |
1595 | 0 | MOZ_ASSERT(aKid); |
1596 | 0 | MOZ_ASSERT(GetChildCount() > 0); |
1597 | 0 |
|
1598 | 0 | RemoveFromCache(this); |
1599 | 0 |
|
1600 | 0 | nsIContent* previousSibling = aKid->GetPreviousSibling(); |
1601 | 0 | nsCOMPtr<nsIContent> ref = aKid; |
1602 | 0 |
|
1603 | 0 | if (aKid->mNextSibling) { |
1604 | 0 | aKid->mNextSibling->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling; |
1605 | 0 | } else { |
1606 | 0 | // aKid is the last child in the list |
1607 | 0 | mFirstChild->mPreviousOrLastSibling = aKid->mPreviousOrLastSibling; |
1608 | 0 | } |
1609 | 0 | aKid->mPreviousOrLastSibling = nullptr; |
1610 | 0 |
|
1611 | 0 | if (previousSibling) { |
1612 | 0 | previousSibling->mNextSibling = aKid->mNextSibling.forget(); |
1613 | 0 | } else { |
1614 | 0 | // aKid is the first child in the list |
1615 | 0 | mFirstChild = aKid->mNextSibling.forget(); |
1616 | 0 | } |
1617 | 0 |
|
1618 | 0 | --mChildCount; |
1619 | 0 | } |
1620 | | |
1621 | | nsIContent* |
1622 | | nsINode::GetChildAt_Deprecated(uint32_t aIndex) const |
1623 | 0 | { |
1624 | 0 | if (aIndex >= GetChildCount()) { |
1625 | 0 | return nullptr; |
1626 | 0 | } |
1627 | 0 | |
1628 | 0 | nsIContent* child = mFirstChild; |
1629 | 0 | while (aIndex--) { |
1630 | 0 | child = child->GetNextSibling(); |
1631 | 0 | } |
1632 | 0 |
|
1633 | 0 | return child; |
1634 | 0 | } |
1635 | | |
1636 | | int32_t |
1637 | | nsINode::ComputeIndexOf(const nsINode* aChild) const |
1638 | 0 | { |
1639 | 0 | if (!aChild) { |
1640 | 0 | return -1; |
1641 | 0 | } |
1642 | 0 | |
1643 | 0 | if (aChild->GetParentNode() != this) { |
1644 | 0 | return -1; |
1645 | 0 | } |
1646 | 0 | |
1647 | 0 | if (aChild == GetLastChild()) { |
1648 | 0 | return GetChildCount() - 1; |
1649 | 0 | } |
1650 | 0 | |
1651 | 0 | if (mChildCount >= CACHE_CHILD_LIMIT) { |
1652 | 0 | const nsINode* child; |
1653 | 0 | int32_t childIndex; |
1654 | 0 | GetChildAndIndexFromCache(this, &child, &childIndex); |
1655 | 0 | if (child) { |
1656 | 0 | if (child == aChild) { |
1657 | 0 | return childIndex; |
1658 | 0 | } |
1659 | 0 | |
1660 | 0 | int32_t nextIndex = childIndex; |
1661 | 0 | int32_t prevIndex = childIndex; |
1662 | 0 | nsINode* prev = child->GetPreviousSibling(); |
1663 | 0 | nsINode* next = child->GetNextSibling(); |
1664 | 0 | do { |
1665 | 0 | if (next) { |
1666 | 0 | ++nextIndex; |
1667 | 0 | if (next == aChild) { |
1668 | 0 | AddChildAndIndexToCache(this, aChild, nextIndex); |
1669 | 0 | return nextIndex; |
1670 | 0 | } |
1671 | 0 | next = next->GetNextSibling(); |
1672 | 0 | } |
1673 | 0 | if (prev) { |
1674 | 0 | --prevIndex; |
1675 | 0 | if (prev == aChild) { |
1676 | 0 | AddChildAndIndexToCache(this, aChild, prevIndex); |
1677 | 0 | return prevIndex; |
1678 | 0 | } |
1679 | 0 | prev = prev->GetPreviousSibling(); |
1680 | 0 | } |
1681 | 0 | } while (prev || next); |
1682 | 0 | } |
1683 | 0 | } |
1684 | 0 |
|
1685 | 0 | int32_t index = 0; |
1686 | 0 | nsINode* current = mFirstChild; |
1687 | 0 | while (current) { |
1688 | 0 | MOZ_ASSERT(current->GetParentNode() == this); |
1689 | 0 | if (current == aChild) { |
1690 | 0 | if (mChildCount >= CACHE_CHILD_LIMIT) { |
1691 | 0 | AddChildAndIndexToCache(this, current, index); |
1692 | 0 | } |
1693 | 0 | return index; |
1694 | 0 | } |
1695 | 0 | current = current->GetNextSibling(); |
1696 | 0 | ++index; |
1697 | 0 | } |
1698 | 0 |
|
1699 | 0 | return -1; |
1700 | 0 | } |
1701 | | |
1702 | | static already_AddRefed<nsINode> |
1703 | | GetNodeFromNodeOrString(const OwningNodeOrString& aNode, |
1704 | | nsIDocument* aDocument) |
1705 | 0 | { |
1706 | 0 | if (aNode.IsNode()) { |
1707 | 0 | nsCOMPtr<nsINode> node = aNode.GetAsNode(); |
1708 | 0 | return node.forget(); |
1709 | 0 | } |
1710 | 0 | |
1711 | 0 | if (aNode.IsString()){ |
1712 | 0 | RefPtr<nsTextNode> textNode = |
1713 | 0 | aDocument->CreateTextNode(aNode.GetAsString()); |
1714 | 0 | return textNode.forget(); |
1715 | 0 | } |
1716 | 0 | |
1717 | 0 | MOZ_CRASH("Impossible type"); |
1718 | 0 | } |
1719 | | |
1720 | | /** |
1721 | | * Implement the algorithm specified at |
1722 | | * https://dom.spec.whatwg.org/#converting-nodes-into-a-node for |prepend()|, |
1723 | | * |append()|, |before()|, |after()|, and |replaceWith()| APIs. |
1724 | | */ |
1725 | | MOZ_CAN_RUN_SCRIPT static already_AddRefed<nsINode> |
1726 | | ConvertNodesOrStringsIntoNode(const Sequence<OwningNodeOrString>& aNodes, |
1727 | | nsIDocument* aDocument, |
1728 | | ErrorResult& aRv) |
1729 | 0 | { |
1730 | 0 | if (aNodes.Length() == 1) { |
1731 | 0 | return GetNodeFromNodeOrString(aNodes[0], aDocument); |
1732 | 0 | } |
1733 | 0 | |
1734 | 0 | nsCOMPtr<nsINode> fragment = aDocument->CreateDocumentFragment(); |
1735 | 0 |
|
1736 | 0 | for (const auto& node : aNodes) { |
1737 | 0 | nsCOMPtr<nsINode> childNode = GetNodeFromNodeOrString(node, aDocument); |
1738 | 0 | fragment->AppendChild(*childNode, aRv); |
1739 | 0 | if (aRv.Failed()) { |
1740 | 0 | return nullptr; |
1741 | 0 | } |
1742 | 0 | } |
1743 | 0 |
|
1744 | 0 | return fragment.forget(); |
1745 | 0 | } |
1746 | | |
1747 | | static void |
1748 | | InsertNodesIntoHashset(const Sequence<OwningNodeOrString>& aNodes, |
1749 | | nsTHashtable<nsPtrHashKey<nsINode>>& aHashset) |
1750 | 0 | { |
1751 | 0 | for (const auto& node : aNodes) { |
1752 | 0 | if (node.IsNode()) { |
1753 | 0 | aHashset.PutEntry(node.GetAsNode()); |
1754 | 0 | } |
1755 | 0 | } |
1756 | 0 | } |
1757 | | |
1758 | | static nsINode* |
1759 | | FindViablePreviousSibling(const nsINode& aNode, |
1760 | | const Sequence<OwningNodeOrString>& aNodes) |
1761 | 0 | { |
1762 | 0 | nsTHashtable<nsPtrHashKey<nsINode>> nodeSet(16); |
1763 | 0 | InsertNodesIntoHashset(aNodes, nodeSet); |
1764 | 0 |
|
1765 | 0 | nsINode* viablePreviousSibling = nullptr; |
1766 | 0 | for (nsINode* sibling = aNode.GetPreviousSibling(); sibling; |
1767 | 0 | sibling = sibling->GetPreviousSibling()) { |
1768 | 0 | if (!nodeSet.Contains(sibling)) { |
1769 | 0 | viablePreviousSibling = sibling; |
1770 | 0 | break; |
1771 | 0 | } |
1772 | 0 | } |
1773 | 0 |
|
1774 | 0 | return viablePreviousSibling; |
1775 | 0 | } |
1776 | | |
1777 | | static nsINode* |
1778 | | FindViableNextSibling(const nsINode& aNode, |
1779 | | const Sequence<OwningNodeOrString>& aNodes) |
1780 | 0 | { |
1781 | 0 | nsTHashtable<nsPtrHashKey<nsINode>> nodeSet(16); |
1782 | 0 | InsertNodesIntoHashset(aNodes, nodeSet); |
1783 | 0 |
|
1784 | 0 | nsINode* viableNextSibling = nullptr; |
1785 | 0 | for (nsINode* sibling = aNode.GetNextSibling(); sibling; |
1786 | 0 | sibling = sibling->GetNextSibling()) { |
1787 | 0 | if (!nodeSet.Contains(sibling)) { |
1788 | 0 | viableNextSibling = sibling; |
1789 | 0 | break; |
1790 | 0 | } |
1791 | 0 | } |
1792 | 0 |
|
1793 | 0 | return viableNextSibling; |
1794 | 0 | } |
1795 | | |
1796 | | void |
1797 | | nsINode::Before(const Sequence<OwningNodeOrString>& aNodes, |
1798 | | ErrorResult& aRv) |
1799 | 0 | { |
1800 | 0 | nsCOMPtr<nsINode> parent = GetParentNode(); |
1801 | 0 | if (!parent) { |
1802 | 0 | return; |
1803 | 0 | } |
1804 | 0 | |
1805 | 0 | nsCOMPtr<nsINode> viablePreviousSibling = |
1806 | 0 | FindViablePreviousSibling(*this, aNodes); |
1807 | 0 |
|
1808 | 0 | nsCOMPtr<nsIDocument> doc = OwnerDoc(); |
1809 | 0 | nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); |
1810 | 0 | if (aRv.Failed()) { |
1811 | 0 | return; |
1812 | 0 | } |
1813 | 0 | |
1814 | 0 | viablePreviousSibling = viablePreviousSibling ? |
1815 | 0 | viablePreviousSibling->GetNextSibling() : parent->GetFirstChild(); |
1816 | 0 |
|
1817 | 0 | parent->InsertBefore(*node, viablePreviousSibling, aRv); |
1818 | 0 | } |
1819 | | |
1820 | | void |
1821 | | nsINode::After(const Sequence<OwningNodeOrString>& aNodes, |
1822 | | ErrorResult& aRv) |
1823 | 0 | { |
1824 | 0 | nsCOMPtr<nsINode> parent = GetParentNode(); |
1825 | 0 | if (!parent) { |
1826 | 0 | return; |
1827 | 0 | } |
1828 | 0 | |
1829 | 0 | nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes); |
1830 | 0 |
|
1831 | 0 | nsCOMPtr<nsIDocument> doc = OwnerDoc(); |
1832 | 0 | nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); |
1833 | 0 | if (aRv.Failed()) { |
1834 | 0 | return; |
1835 | 0 | } |
1836 | 0 | |
1837 | 0 | parent->InsertBefore(*node, viableNextSibling, aRv); |
1838 | 0 | } |
1839 | | |
1840 | | void |
1841 | | nsINode::ReplaceWith(const Sequence<OwningNodeOrString>& aNodes, |
1842 | | ErrorResult& aRv) |
1843 | 0 | { |
1844 | 0 | nsCOMPtr<nsINode> parent = GetParentNode(); |
1845 | 0 | if (!parent) { |
1846 | 0 | return; |
1847 | 0 | } |
1848 | 0 | |
1849 | 0 | nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes); |
1850 | 0 |
|
1851 | 0 | nsCOMPtr<nsIDocument> doc = OwnerDoc(); |
1852 | 0 | nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); |
1853 | 0 | if (aRv.Failed()) { |
1854 | 0 | return; |
1855 | 0 | } |
1856 | 0 | |
1857 | 0 | if (parent == GetParentNode()) { |
1858 | 0 | parent->ReplaceChild(*node, *this, aRv); |
1859 | 0 | } else { |
1860 | 0 | parent->InsertBefore(*node, viableNextSibling, aRv); |
1861 | 0 | } |
1862 | 0 | } |
1863 | | |
1864 | | void |
1865 | | nsINode::Remove() |
1866 | 0 | { |
1867 | 0 | nsCOMPtr<nsINode> parent = GetParentNode(); |
1868 | 0 | if (!parent) { |
1869 | 0 | return; |
1870 | 0 | } |
1871 | 0 | |
1872 | 0 | parent->RemoveChild(*this, IgnoreErrors()); |
1873 | 0 | } |
1874 | | |
1875 | | Element* |
1876 | | nsINode::GetFirstElementChild() const |
1877 | 0 | { |
1878 | 0 | for (nsIContent* child = GetFirstChild(); |
1879 | 0 | child; |
1880 | 0 | child = child->GetNextSibling()) { |
1881 | 0 | if (child->IsElement()) { |
1882 | 0 | return child->AsElement(); |
1883 | 0 | } |
1884 | 0 | } |
1885 | 0 |
|
1886 | 0 | return nullptr; |
1887 | 0 | } |
1888 | | |
1889 | | Element* |
1890 | | nsINode::GetLastElementChild() const |
1891 | 0 | { |
1892 | 0 | for (nsIContent* child = GetLastChild(); |
1893 | 0 | child; |
1894 | 0 | child = child->GetPreviousSibling()) { |
1895 | 0 | if (child->IsElement()) { |
1896 | 0 | return child->AsElement(); |
1897 | 0 | } |
1898 | 0 | } |
1899 | 0 |
|
1900 | 0 | return nullptr; |
1901 | 0 | } |
1902 | | |
1903 | | static |
1904 | | bool MatchAttribute(Element* aElement, |
1905 | | int32_t aNamespaceID, |
1906 | | nsAtom* aAttrName, |
1907 | | void* aData) |
1908 | 0 | { |
1909 | 0 | MOZ_ASSERT(aElement, "Must have content node to work with!"); |
1910 | 0 | nsString* attrValue = static_cast<nsString*>(aData); |
1911 | 0 | if (aNamespaceID != kNameSpaceID_Unknown && |
1912 | 0 | aNamespaceID != kNameSpaceID_Wildcard) { |
1913 | 0 | return attrValue->EqualsLiteral("*") ? |
1914 | 0 | aElement->HasAttr(aNamespaceID, aAttrName) : |
1915 | 0 | aElement->AttrValueIs(aNamespaceID, aAttrName, *attrValue, |
1916 | 0 | eCaseMatters); |
1917 | 0 | } |
1918 | 0 |
|
1919 | 0 | // Qualified name match. This takes more work. |
1920 | 0 | uint32_t count = aElement->GetAttrCount(); |
1921 | 0 | for (uint32_t i = 0; i < count; ++i) { |
1922 | 0 | const nsAttrName* name = aElement->GetAttrNameAt(i); |
1923 | 0 | bool nameMatch; |
1924 | 0 | if (name->IsAtom()) { |
1925 | 0 | nameMatch = name->Atom() == aAttrName; |
1926 | 0 | } else if (aNamespaceID == kNameSpaceID_Wildcard) { |
1927 | 0 | nameMatch = name->NodeInfo()->Equals(aAttrName); |
1928 | 0 | } else { |
1929 | 0 | nameMatch = name->NodeInfo()->QualifiedNameEquals(aAttrName); |
1930 | 0 | } |
1931 | 0 |
|
1932 | 0 | if (nameMatch) { |
1933 | 0 | return attrValue->EqualsLiteral("*") || |
1934 | 0 | aElement->AttrValueIs(name->NamespaceID(), name->LocalName(), |
1935 | 0 | *attrValue, eCaseMatters); |
1936 | 0 | } |
1937 | 0 | } |
1938 | 0 |
|
1939 | 0 | return false; |
1940 | 0 | } |
1941 | | |
1942 | | already_AddRefed<nsIHTMLCollection> |
1943 | | nsINode::GetElementsByAttribute(const nsAString& aAttribute, |
1944 | | const nsAString& aValue) |
1945 | 0 | { |
1946 | 0 | RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute)); |
1947 | 0 | nsAutoPtr<nsString> attrValue(new nsString(aValue)); |
1948 | 0 | RefPtr<nsContentList> list = new nsContentList(this, |
1949 | 0 | MatchAttribute, |
1950 | 0 | nsContentUtils::DestroyMatchString, |
1951 | 0 | attrValue.forget(), |
1952 | 0 | true, |
1953 | 0 | attrAtom, |
1954 | 0 | kNameSpaceID_Unknown); |
1955 | 0 |
|
1956 | 0 | return list.forget(); |
1957 | 0 | } |
1958 | | |
1959 | | already_AddRefed<nsIHTMLCollection> |
1960 | | nsINode::GetElementsByAttributeNS(const nsAString& aNamespaceURI, |
1961 | | const nsAString& aAttribute, |
1962 | | const nsAString& aValue, |
1963 | | ErrorResult& aRv) |
1964 | 0 | { |
1965 | 0 | RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute)); |
1966 | 0 | nsAutoPtr<nsString> attrValue(new nsString(aValue)); |
1967 | 0 |
|
1968 | 0 | int32_t nameSpaceId = kNameSpaceID_Wildcard; |
1969 | 0 | if (!aNamespaceURI.EqualsLiteral("*")) { |
1970 | 0 | nsresult rv = |
1971 | 0 | nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI, |
1972 | 0 | nameSpaceId); |
1973 | 0 | if (NS_FAILED(rv)) { |
1974 | 0 | aRv.Throw(rv); |
1975 | 0 | return nullptr; |
1976 | 0 | } |
1977 | 0 | } |
1978 | 0 | |
1979 | 0 | RefPtr<nsContentList> list = new nsContentList(this, |
1980 | 0 | MatchAttribute, |
1981 | 0 | nsContentUtils::DestroyMatchString, |
1982 | 0 | attrValue.forget(), |
1983 | 0 | true, |
1984 | 0 | attrAtom, |
1985 | 0 | nameSpaceId); |
1986 | 0 | return list.forget(); |
1987 | 0 | } |
1988 | | |
1989 | | |
1990 | | void |
1991 | | nsINode::Prepend(const Sequence<OwningNodeOrString>& aNodes, |
1992 | | ErrorResult& aRv) |
1993 | 0 | { |
1994 | 0 | nsCOMPtr<nsIDocument> doc = OwnerDoc(); |
1995 | 0 | nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); |
1996 | 0 | if (aRv.Failed()) { |
1997 | 0 | return; |
1998 | 0 | } |
1999 | 0 | |
2000 | 0 | nsCOMPtr<nsIContent> refNode = mFirstChild;; |
2001 | 0 | InsertBefore(*node, refNode, aRv); |
2002 | 0 | } |
2003 | | |
2004 | | void |
2005 | | nsINode::Append(const Sequence<OwningNodeOrString>& aNodes, |
2006 | | ErrorResult& aRv) |
2007 | 0 | { |
2008 | 0 | nsCOMPtr<nsIDocument> doc = OwnerDoc(); |
2009 | 0 | nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv); |
2010 | 0 | if (aRv.Failed()) { |
2011 | 0 | return; |
2012 | 0 | } |
2013 | 0 | |
2014 | 0 | AppendChild(*node, aRv); |
2015 | 0 | } |
2016 | | |
2017 | | void |
2018 | | nsINode::RemoveChildNode(nsIContent* aKid, bool aNotify) |
2019 | 0 | { |
2020 | 0 | // NOTE: This function must not trigger any calls to |
2021 | 0 | // nsIDocument::GetRootElement() calls until *after* it has removed aKid from |
2022 | 0 | // aChildArray. Any calls before then could potentially restore a stale |
2023 | 0 | // value for our cached root element, per note in |
2024 | 0 | // nsDocument::RemoveChildNode(). |
2025 | 0 | MOZ_ASSERT(aKid && aKid->GetParentNode() == this, "Bogus aKid"); |
2026 | 0 | MOZ_ASSERT(!IsAttr()); |
2027 | 0 |
|
2028 | 0 | nsMutationGuard::DidMutate(); |
2029 | 0 | mozAutoDocUpdate updateBatch(GetComposedDoc(), aNotify); |
2030 | 0 |
|
2031 | 0 | nsIContent* previousSibling = aKid->GetPreviousSibling(); |
2032 | 0 |
|
2033 | 0 | // Since aKid is use also after DisconnectChild, ensure it stays alive. |
2034 | 0 | nsCOMPtr<nsIContent> kungfuDeathGrip = aKid; |
2035 | 0 | DisconnectChild(aKid); |
2036 | 0 |
|
2037 | 0 | // Invalidate cached array of child nodes |
2038 | 0 | InvalidateChildNodes(); |
2039 | 0 |
|
2040 | 0 | if (aNotify) { |
2041 | 0 | nsNodeUtils::ContentRemoved(this, aKid, previousSibling); |
2042 | 0 | } |
2043 | 0 |
|
2044 | 0 | aKid->UnbindFromTree(); |
2045 | 0 | } |
2046 | | |
2047 | | // When replacing, aRefChild is the content being replaced; when |
2048 | | // inserting it's the content before which we're inserting. In the |
2049 | | // latter case it may be null. |
2050 | | static |
2051 | | bool IsAllowedAsChild(nsIContent* aNewChild, nsINode* aParent, |
2052 | | bool aIsReplace, nsINode* aRefChild) |
2053 | 0 | { |
2054 | 0 | MOZ_ASSERT(aNewChild, "Must have new child"); |
2055 | 0 | MOZ_ASSERT_IF(aIsReplace, aRefChild); |
2056 | 0 | MOZ_ASSERT(aParent); |
2057 | 0 | MOZ_ASSERT(aParent->IsDocument() || |
2058 | 0 | aParent->IsDocumentFragment() || |
2059 | 0 | aParent->IsElement(), |
2060 | 0 | "Nodes that are not documents, document fragments or elements " |
2061 | 0 | "can't be parents!"); |
2062 | 0 |
|
2063 | 0 | // A common case is that aNewChild has no kids, in which case |
2064 | 0 | // aParent can't be a descendant of aNewChild unless they're |
2065 | 0 | // actually equal to each other. Fast-path that case, since aParent |
2066 | 0 | // could be pretty deep in the DOM tree. |
2067 | 0 | if (aNewChild == aParent || |
2068 | 0 | ((aNewChild->GetFirstChild() || |
2069 | 0 | // HTML template elements and ShadowRoot hosts need |
2070 | 0 | // to be checked to ensure that they are not inserted into |
2071 | 0 | // the hosted content. |
2072 | 0 | aNewChild->NodeInfo()->NameAtom() == nsGkAtoms::_template || |
2073 | 0 | aNewChild->GetShadowRoot()) && |
2074 | 0 | nsContentUtils::ContentIsHostIncludingDescendantOf(aParent, |
2075 | 0 | aNewChild))) { |
2076 | 0 | return false; |
2077 | 0 | } |
2078 | 0 | |
2079 | 0 | // The allowed child nodes differ for documents and elements |
2080 | 0 | switch (aNewChild->NodeType()) { |
2081 | 0 | case nsINode::COMMENT_NODE : |
2082 | 0 | case nsINode::PROCESSING_INSTRUCTION_NODE : |
2083 | 0 | // OK in both cases |
2084 | 0 | return true; |
2085 | 0 | case nsINode::TEXT_NODE : |
2086 | 0 | case nsINode::CDATA_SECTION_NODE : |
2087 | 0 | case nsINode::ENTITY_REFERENCE_NODE : |
2088 | 0 | // Allowed under Elements and DocumentFragments |
2089 | 0 | return aParent->NodeType() != nsINode::DOCUMENT_NODE; |
2090 | 0 | case nsINode::ELEMENT_NODE : |
2091 | 0 | { |
2092 | 0 | if (!aParent->IsDocument()) { |
2093 | 0 | // Always ok to have elements under other elements or document fragments |
2094 | 0 | return true; |
2095 | 0 | } |
2096 | 0 | |
2097 | 0 | nsIDocument* parentDocument = aParent->AsDocument(); |
2098 | 0 | Element* rootElement = parentDocument->GetRootElement(); |
2099 | 0 | if (rootElement) { |
2100 | 0 | // Already have a documentElement, so this is only OK if we're |
2101 | 0 | // replacing it. |
2102 | 0 | return aIsReplace && rootElement == aRefChild; |
2103 | 0 | } |
2104 | 0 |
|
2105 | 0 | // We don't have a documentElement yet. Our one remaining constraint is |
2106 | 0 | // that the documentElement must come after the doctype. |
2107 | 0 | if (!aRefChild) { |
2108 | 0 | // Appending is just fine. |
2109 | 0 | return true; |
2110 | 0 | } |
2111 | 0 | |
2112 | 0 | nsIContent* docTypeContent = parentDocument->GetDoctype(); |
2113 | 0 | if (!docTypeContent) { |
2114 | 0 | // It's all good. |
2115 | 0 | return true; |
2116 | 0 | } |
2117 | 0 | |
2118 | 0 | int32_t doctypeIndex = aParent->ComputeIndexOf(docTypeContent); |
2119 | 0 | int32_t insertIndex = aParent->ComputeIndexOf(aRefChild); |
2120 | 0 |
|
2121 | 0 | // Now we're OK in the following two cases only: |
2122 | 0 | // 1) We're replacing something that's not before the doctype |
2123 | 0 | // 2) We're inserting before something that comes after the doctype |
2124 | 0 | return aIsReplace ? (insertIndex >= doctypeIndex) : |
2125 | 0 | insertIndex > doctypeIndex; |
2126 | 0 | } |
2127 | 0 | case nsINode::DOCUMENT_TYPE_NODE : |
2128 | 0 | { |
2129 | 0 | if (!aParent->IsDocument()) { |
2130 | 0 | // doctypes only allowed under documents |
2131 | 0 | return false; |
2132 | 0 | } |
2133 | 0 | |
2134 | 0 | nsIDocument* parentDocument = aParent->AsDocument(); |
2135 | 0 | nsIContent* docTypeContent = parentDocument->GetDoctype(); |
2136 | 0 | if (docTypeContent) { |
2137 | 0 | // Already have a doctype, so this is only OK if we're replacing it |
2138 | 0 | return aIsReplace && docTypeContent == aRefChild; |
2139 | 0 | } |
2140 | 0 |
|
2141 | 0 | // We don't have a doctype yet. Our one remaining constraint is |
2142 | 0 | // that the doctype must come before the documentElement. |
2143 | 0 | Element* rootElement = parentDocument->GetRootElement(); |
2144 | 0 | if (!rootElement) { |
2145 | 0 | // It's all good |
2146 | 0 | return true; |
2147 | 0 | } |
2148 | 0 | |
2149 | 0 | if (!aRefChild) { |
2150 | 0 | // Trying to append a doctype, but have a documentElement |
2151 | 0 | return false; |
2152 | 0 | } |
2153 | 0 | |
2154 | 0 | int32_t rootIndex = aParent->ComputeIndexOf(rootElement); |
2155 | 0 | int32_t insertIndex = aParent->ComputeIndexOf(aRefChild); |
2156 | 0 |
|
2157 | 0 | // Now we're OK if and only if insertIndex <= rootIndex. Indeed, either |
2158 | 0 | // we end up replacing aRefChild or we end up before it. Either one is |
2159 | 0 | // ok as long as aRefChild is not after rootElement. |
2160 | 0 | return insertIndex <= rootIndex; |
2161 | 0 | } |
2162 | 0 | case nsINode::DOCUMENT_FRAGMENT_NODE : |
2163 | 0 | { |
2164 | 0 | // Note that for now we only allow nodes inside document fragments if |
2165 | 0 | // they're allowed inside elements. If we ever change this to allow |
2166 | 0 | // doctype nodes in document fragments, we'll need to update this code. |
2167 | 0 | // Also, there's a version of this code in ReplaceOrInsertBefore. If you |
2168 | 0 | // change this code, change that too. |
2169 | 0 | if (!aParent->IsDocument()) { |
2170 | 0 | // All good here |
2171 | 0 | return true; |
2172 | 0 | } |
2173 | 0 | |
2174 | 0 | bool sawElement = false; |
2175 | 0 | for (nsIContent* child = aNewChild->GetFirstChild(); |
2176 | 0 | child; |
2177 | 0 | child = child->GetNextSibling()) { |
2178 | 0 | if (child->IsElement()) { |
2179 | 0 | if (sawElement) { |
2180 | 0 | // Can't put two elements into a document |
2181 | 0 | return false; |
2182 | 0 | } |
2183 | 0 | sawElement = true; |
2184 | 0 | } |
2185 | 0 | // If we can put this content at the the right place, we might be ok; |
2186 | 0 | // if not, we bail out. |
2187 | 0 | if (!IsAllowedAsChild(child, aParent, aIsReplace, aRefChild)) { |
2188 | 0 | return false; |
2189 | 0 | } |
2190 | 0 | } |
2191 | 0 |
|
2192 | 0 | // Everything in the fragment checked out ok, so we can stick it in here |
2193 | 0 | return true; |
2194 | 0 | } |
2195 | 0 | default: |
2196 | 0 | /* |
2197 | 0 | * aNewChild is of invalid type. |
2198 | 0 | */ |
2199 | 0 | break; |
2200 | 0 | } |
2201 | 0 | |
2202 | 0 | return false; |
2203 | 0 | } |
2204 | | |
2205 | | void |
2206 | | nsINode::EnsurePreInsertionValidity(nsINode& aNewChild, nsINode* aRefChild, |
2207 | | ErrorResult& aError) |
2208 | 0 | { |
2209 | 0 | EnsurePreInsertionValidity1(aNewChild, aRefChild, aError); |
2210 | 0 | if (aError.Failed()) { |
2211 | 0 | return; |
2212 | 0 | } |
2213 | 0 | EnsurePreInsertionValidity2(false, aNewChild, aRefChild, aError); |
2214 | 0 | } |
2215 | | |
2216 | | void |
2217 | | nsINode::EnsurePreInsertionValidity1(nsINode& aNewChild, nsINode* aRefChild, |
2218 | | ErrorResult& aError) |
2219 | 0 | { |
2220 | 0 | if ((!IsDocument() && !IsDocumentFragment() && !IsElement()) || |
2221 | 0 | !aNewChild.IsContent()) { |
2222 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2223 | 0 | return; |
2224 | 0 | } |
2225 | 0 | } |
2226 | | |
2227 | | void |
2228 | | nsINode::EnsurePreInsertionValidity2(bool aReplace, nsINode& aNewChild, |
2229 | | nsINode* aRefChild, ErrorResult& aError) |
2230 | 0 | { |
2231 | 0 | nsIContent* newContent = aNewChild.AsContent(); |
2232 | 0 | if (newContent->IsRootOfAnonymousSubtree()) { |
2233 | 0 | // This is anonymous content. Don't allow its insertion |
2234 | 0 | // anywhere, since it might have UnbindFromTree calls coming |
2235 | 0 | // its way. |
2236 | 0 | aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); |
2237 | 0 | return; |
2238 | 0 | } |
2239 | 0 | |
2240 | 0 | // Make sure that the inserted node is allowed as a child of its new parent. |
2241 | 0 | if (!IsAllowedAsChild(newContent, this, aReplace, aRefChild)) { |
2242 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2243 | 0 | return; |
2244 | 0 | } |
2245 | 0 | } |
2246 | | |
2247 | | nsINode* |
2248 | | nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild, |
2249 | | nsINode* aRefChild, ErrorResult& aError) |
2250 | 0 | { |
2251 | 0 | // XXXbz I wish I could assert that nsContentUtils::IsSafeToRunScript() so we |
2252 | 0 | // could rely on scriptblockers going out of scope to actually run XBL |
2253 | 0 | // teardown, but various crud adds nodes under scriptblockers (e.g. native |
2254 | 0 | // anonymous content). The only good news is those insertions can't trigger |
2255 | 0 | // the bad XBL cases. |
2256 | 0 | MOZ_ASSERT_IF(aReplace, aRefChild); |
2257 | 0 |
|
2258 | 0 | EnsurePreInsertionValidity1(*aNewChild, aRefChild, aError); |
2259 | 0 | if (aError.Failed()) { |
2260 | 0 | return nullptr; |
2261 | 0 | } |
2262 | 0 | |
2263 | 0 | uint16_t nodeType = aNewChild->NodeType(); |
2264 | 0 |
|
2265 | 0 | // Before we do anything else, fire all DOMNodeRemoved mutation events |
2266 | 0 | // We do this up front as to avoid having to deal with script running |
2267 | 0 | // at random places further down. |
2268 | 0 | // Scope firing mutation events so that we don't carry any state that |
2269 | 0 | // might be stale |
2270 | 0 | { |
2271 | 0 | // This check happens again further down (though then using |
2272 | 0 | // ComputeIndexOf). |
2273 | 0 | // We're only checking this here to avoid firing mutation events when |
2274 | 0 | // none should be fired. |
2275 | 0 | // It's ok that we do the check twice in the case when firing mutation |
2276 | 0 | // events as we need to recheck after running script anyway. |
2277 | 0 | if (aRefChild && aRefChild->GetParentNode() != this) { |
2278 | 0 | aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); |
2279 | 0 | return nullptr; |
2280 | 0 | } |
2281 | 0 | |
2282 | 0 | // If we're replacing, fire for node-to-be-replaced. |
2283 | 0 | // If aRefChild == aNewChild then we'll fire for it in check below |
2284 | 0 | if (aReplace && aRefChild != aNewChild) { |
2285 | 0 | nsContentUtils::MaybeFireNodeRemoved(aRefChild, this); |
2286 | 0 | } |
2287 | 0 |
|
2288 | 0 | // If the new node already has a parent, fire for removing from old |
2289 | 0 | // parent |
2290 | 0 | nsINode* oldParent = aNewChild->GetParentNode(); |
2291 | 0 | if (oldParent) { |
2292 | 0 | nsContentUtils::MaybeFireNodeRemoved(aNewChild, oldParent); |
2293 | 0 | } |
2294 | 0 |
|
2295 | 0 | // If we're inserting a fragment, fire for all the children of the |
2296 | 0 | // fragment |
2297 | 0 | if (nodeType == DOCUMENT_FRAGMENT_NODE) { |
2298 | 0 | static_cast<FragmentOrElement*>(aNewChild)->FireNodeRemovedForChildren(); |
2299 | 0 | } |
2300 | 0 | // Verify that our aRefChild is still sensible |
2301 | 0 | if (aRefChild && aRefChild->GetParentNode() != this) { |
2302 | 0 | aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); |
2303 | 0 | return nullptr; |
2304 | 0 | } |
2305 | 0 | } |
2306 | 0 | |
2307 | 0 | EnsurePreInsertionValidity2(aReplace, *aNewChild, aRefChild, aError); |
2308 | 0 | if (aError.Failed()) { |
2309 | 0 | return nullptr; |
2310 | 0 | } |
2311 | 0 | |
2312 | 0 | // Record the node to insert before, if any |
2313 | 0 | nsIContent* nodeToInsertBefore; |
2314 | 0 | if (aReplace) { |
2315 | 0 | nodeToInsertBefore = aRefChild->GetNextSibling(); |
2316 | 0 | } else { |
2317 | 0 | // Since aRefChild is our child, it must be an nsIContent object. |
2318 | 0 | nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr; |
2319 | 0 | } |
2320 | 0 | if (nodeToInsertBefore == aNewChild) { |
2321 | 0 | // We're going to remove aNewChild from its parent, so use its next sibling |
2322 | 0 | // as the node to insert before. |
2323 | 0 | nodeToInsertBefore = nodeToInsertBefore->GetNextSibling(); |
2324 | 0 | } |
2325 | 0 |
|
2326 | 0 | Maybe<AutoTArray<nsCOMPtr<nsIContent>, 50> > fragChildren; |
2327 | 0 |
|
2328 | 0 | // Remove the new child from the old parent if one exists |
2329 | 0 | nsIContent* newContent = aNewChild->AsContent(); |
2330 | 0 | nsCOMPtr<nsINode> oldParent = newContent->GetParentNode(); |
2331 | 0 | if (oldParent) { |
2332 | 0 | // Hold a strong ref to nodeToInsertBefore across the removal of newContent |
2333 | 0 | nsCOMPtr<nsINode> kungFuDeathGrip = nodeToInsertBefore; |
2334 | 0 |
|
2335 | 0 | // Removing a child can run script, via XBL destructors. |
2336 | 0 | nsMutationGuard guard; |
2337 | 0 |
|
2338 | 0 | // Scope for the mutation batch and scriptblocker, so they go away |
2339 | 0 | // while kungFuDeathGrip is still alive. |
2340 | 0 | { |
2341 | 0 | mozAutoDocUpdate batch(newContent->GetComposedDoc(), true); |
2342 | 0 | nsAutoMutationBatch mb(oldParent, true, true); |
2343 | 0 | // ScriptBlocker ensures previous and next stay alive. |
2344 | 0 | nsIContent* previous = aNewChild->GetPreviousSibling(); |
2345 | 0 | nsIContent* next = aNewChild->GetNextSibling(); |
2346 | 0 | oldParent->RemoveChildNode(aNewChild->AsContent(), true); |
2347 | 0 | if (nsAutoMutationBatch::GetCurrentBatch() == &mb) { |
2348 | 0 | mb.RemovalDone(); |
2349 | 0 | mb.SetPrevSibling(previous); |
2350 | 0 | mb.SetNextSibling(next); |
2351 | 0 | } |
2352 | 0 | } |
2353 | 0 |
|
2354 | 0 | // We expect one mutation (the removal) to have happened. |
2355 | 0 | if (guard.Mutated(1)) { |
2356 | 0 | // XBL destructors, yuck. |
2357 | 0 |
|
2358 | 0 | // Verify that nodeToInsertBefore, if non-null, is still our child. If |
2359 | 0 | // it's not, there's no way we can do this insert sanely; just bail out. |
2360 | 0 | if (nodeToInsertBefore && nodeToInsertBefore->GetParent() != this) { |
2361 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2362 | 0 | return nullptr; |
2363 | 0 | } |
2364 | 0 | |
2365 | 0 | // Verify that newContent has no parent. |
2366 | 0 | if (newContent->GetParentNode()) { |
2367 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2368 | 0 | return nullptr; |
2369 | 0 | } |
2370 | 0 | |
2371 | 0 | // And verify that newContent is still allowed as our child. |
2372 | 0 | if (aNewChild == aRefChild) { |
2373 | 0 | // We've already removed aRefChild. So even if we were doing a replace, |
2374 | 0 | // now we're doing a simple insert before nodeToInsertBefore. |
2375 | 0 | if (!IsAllowedAsChild(newContent, this, false, nodeToInsertBefore)) { |
2376 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2377 | 0 | return nullptr; |
2378 | 0 | } |
2379 | 0 | } else { |
2380 | 0 | if ((aRefChild && aRefChild->GetParent() != this) || |
2381 | 0 | !IsAllowedAsChild(newContent, this, aReplace, aRefChild)) { |
2382 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2383 | 0 | return nullptr; |
2384 | 0 | } |
2385 | 0 | // And recompute nodeToInsertBefore, just in case. |
2386 | 0 | if (aReplace) { |
2387 | 0 | nodeToInsertBefore = aRefChild->GetNextSibling(); |
2388 | 0 | } else { |
2389 | 0 | nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr; |
2390 | 0 | } |
2391 | 0 | } |
2392 | 0 | } |
2393 | 0 | } else if (nodeType == DOCUMENT_FRAGMENT_NODE) { |
2394 | 0 | // Make sure to remove all the fragment's kids. We need to do this before |
2395 | 0 | // we start inserting anything, so we will run out XBL destructors and |
2396 | 0 | // binding teardown (GOD, I HATE THESE THINGS) before we insert anything |
2397 | 0 | // into the DOM. |
2398 | 0 | uint32_t count = newContent->GetChildCount(); |
2399 | 0 |
|
2400 | 0 | fragChildren.emplace(); |
2401 | 0 |
|
2402 | 0 | // Copy the children into a separate array to avoid having to deal with |
2403 | 0 | // mutations to the fragment later on here. |
2404 | 0 | fragChildren->SetCapacity(count); |
2405 | 0 | for (nsIContent* child = newContent->GetFirstChild(); |
2406 | 0 | child; |
2407 | 0 | child = child->GetNextSibling()) { |
2408 | 0 | NS_ASSERTION(child->GetComposedDoc() == nullptr, |
2409 | 0 | "How did we get a child with a current doc?"); |
2410 | 0 | fragChildren->AppendElement(child); |
2411 | 0 | } |
2412 | 0 |
|
2413 | 0 | // Hold a strong ref to nodeToInsertBefore across the removals |
2414 | 0 | nsCOMPtr<nsINode> kungFuDeathGrip = nodeToInsertBefore; |
2415 | 0 |
|
2416 | 0 | nsMutationGuard guard; |
2417 | 0 |
|
2418 | 0 | // Scope for the mutation batch and scriptblocker, so they go away |
2419 | 0 | // while kungFuDeathGrip is still alive. |
2420 | 0 | { |
2421 | 0 | mozAutoDocUpdate batch(newContent->GetComposedDoc(), true); |
2422 | 0 | nsAutoMutationBatch mb(newContent, false, true); |
2423 | 0 |
|
2424 | 0 | while (newContent->HasChildren()) { |
2425 | 0 | newContent->RemoveChildNode(newContent->GetLastChild(), true); |
2426 | 0 | } |
2427 | 0 | } |
2428 | 0 |
|
2429 | 0 | // We expect |count| removals |
2430 | 0 | if (guard.Mutated(count)) { |
2431 | 0 | // XBL destructors, yuck. |
2432 | 0 |
|
2433 | 0 | // Verify that nodeToInsertBefore, if non-null, is still our child. If |
2434 | 0 | // it's not, there's no way we can do this insert sanely; just bail out. |
2435 | 0 | if (nodeToInsertBefore && nodeToInsertBefore->GetParent() != this) { |
2436 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2437 | 0 | return nullptr; |
2438 | 0 | } |
2439 | 0 | |
2440 | 0 | // Verify that all the things in fragChildren have no parent. |
2441 | 0 | for (uint32_t i = 0; i < count; ++i) { |
2442 | 0 | if (fragChildren->ElementAt(i)->GetParentNode()) { |
2443 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2444 | 0 | return nullptr; |
2445 | 0 | } |
2446 | 0 | } |
2447 | 0 |
|
2448 | 0 | // Note that unlike the single-element case above, none of our kids can |
2449 | 0 | // be aRefChild, so we can always pass through aReplace in the |
2450 | 0 | // IsAllowedAsChild checks below and don't have to worry about whether |
2451 | 0 | // recomputing nodeToInsertBefore is OK. |
2452 | 0 |
|
2453 | 0 | // Verify that our aRefChild is still sensible |
2454 | 0 | if (aRefChild && aRefChild->GetParent() != this) { |
2455 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2456 | 0 | return nullptr; |
2457 | 0 | } |
2458 | 0 | |
2459 | 0 | // Recompute nodeToInsertBefore, just in case. |
2460 | 0 | if (aReplace) { |
2461 | 0 | nodeToInsertBefore = aRefChild->GetNextSibling(); |
2462 | 0 | } else { |
2463 | 0 | // If aRefChild has 'this' as a parent, it must be an nsIContent. |
2464 | 0 | nodeToInsertBefore = aRefChild ? aRefChild->AsContent() : nullptr; |
2465 | 0 | } |
2466 | 0 |
|
2467 | 0 | // And verify that newContent is still allowed as our child. Sadly, we |
2468 | 0 | // need to reimplement the relevant part of IsAllowedAsChild() because |
2469 | 0 | // now our nodes are in an array and all. If you change this code, |
2470 | 0 | // change the code there. |
2471 | 0 | if (IsDocument()) { |
2472 | 0 | bool sawElement = false; |
2473 | 0 | for (uint32_t i = 0; i < count; ++i) { |
2474 | 0 | nsIContent* child = fragChildren->ElementAt(i); |
2475 | 0 | if (child->IsElement()) { |
2476 | 0 | if (sawElement) { |
2477 | 0 | // No good |
2478 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2479 | 0 | return nullptr; |
2480 | 0 | } |
2481 | 0 | sawElement = true; |
2482 | 0 | } |
2483 | 0 | if (!IsAllowedAsChild(child, this, aReplace, aRefChild)) { |
2484 | 0 | aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR); |
2485 | 0 | return nullptr; |
2486 | 0 | } |
2487 | 0 | } |
2488 | 0 | } |
2489 | 0 | } |
2490 | 0 | } |
2491 | 0 |
|
2492 | 0 | mozAutoDocUpdate batch(GetComposedDoc(), true); |
2493 | 0 | nsAutoMutationBatch mb; |
2494 | 0 |
|
2495 | 0 | // If we're replacing and we haven't removed aRefChild yet, do so now |
2496 | 0 | if (aReplace && aRefChild != aNewChild) { |
2497 | 0 | mb.Init(this, true, true); |
2498 | 0 |
|
2499 | 0 | // Since aRefChild is never null in the aReplace case, we know that at |
2500 | 0 | // this point nodeToInsertBefore is the next sibling of aRefChild. |
2501 | 0 | NS_ASSERTION(aRefChild->GetNextSibling() == nodeToInsertBefore, |
2502 | 0 | "Unexpected nodeToInsertBefore"); |
2503 | 0 |
|
2504 | 0 | nsIContent* toBeRemoved = nodeToInsertBefore ? |
2505 | 0 | nodeToInsertBefore->GetPreviousSibling() : GetLastChild(); |
2506 | 0 | MOZ_ASSERT(toBeRemoved); |
2507 | 0 |
|
2508 | 0 | RemoveChildNode(toBeRemoved, true); |
2509 | 0 | } |
2510 | 0 |
|
2511 | 0 | // Move new child over to our document if needed. Do this after removing |
2512 | 0 | // it from its parent so that AdoptNode doesn't fire DOMNodeRemoved |
2513 | 0 | // DocumentType nodes are the only nodes that can have a null |
2514 | 0 | // ownerDocument according to the DOM spec, and we need to allow |
2515 | 0 | // inserting them w/o calling AdoptNode(). |
2516 | 0 | nsIDocument* doc = OwnerDoc(); |
2517 | 0 | if (doc != newContent->OwnerDoc()) { |
2518 | 0 | AdoptNodeIntoOwnerDoc(this, aNewChild, aError); |
2519 | 0 | if (aError.Failed()) { |
2520 | 0 | return nullptr; |
2521 | 0 | } |
2522 | 0 | } else if (doc->DidDocumentOpen()) { |
2523 | 0 | CheckForOutdatedParent(this, aNewChild, aError); |
2524 | 0 | if (aError.Failed()) { |
2525 | 0 | return nullptr; |
2526 | 0 | } |
2527 | 0 | } |
2528 | 0 | |
2529 | 0 | /* |
2530 | 0 | * Check if we're inserting a document fragment. If we are, we need |
2531 | 0 | * to actually add its children individually (i.e. we don't add the |
2532 | 0 | * actual document fragment). |
2533 | 0 | */ |
2534 | 0 | nsINode* result = aReplace ? aRefChild : aNewChild; |
2535 | 0 | if (nodeType == DOCUMENT_FRAGMENT_NODE) { |
2536 | 0 | if (!aReplace) { |
2537 | 0 | mb.Init(this, true, true); |
2538 | 0 | } |
2539 | 0 | nsAutoMutationBatch* mutationBatch = nsAutoMutationBatch::GetCurrentBatch(); |
2540 | 0 | if (mutationBatch) { |
2541 | 0 | mutationBatch->RemovalDone(); |
2542 | 0 | mutationBatch->SetPrevSibling(nodeToInsertBefore ? |
2543 | 0 | nodeToInsertBefore->GetPreviousSibling() : GetLastChild()); |
2544 | 0 | mutationBatch->SetNextSibling(nodeToInsertBefore); |
2545 | 0 | } |
2546 | 0 |
|
2547 | 0 | uint32_t count = fragChildren->Length(); |
2548 | 0 | if (!count) { |
2549 | 0 | return result; |
2550 | 0 | } |
2551 | 0 | |
2552 | 0 | bool appending = !IsDocument() && !nodeToInsertBefore; |
2553 | 0 | nsIContent* firstInsertedContent = fragChildren->ElementAt(0); |
2554 | 0 |
|
2555 | 0 | // Iterate through the fragment's children, and insert them in the new |
2556 | 0 | // parent |
2557 | 0 | for (uint32_t i = 0; i < count; ++i) { |
2558 | 0 | // XXXbz how come no reparenting here? That seems odd... |
2559 | 0 | // Insert the child. |
2560 | 0 | aError = InsertChildBefore(fragChildren->ElementAt(i), nodeToInsertBefore, |
2561 | 0 | !appending); |
2562 | 0 | if (aError.Failed()) { |
2563 | 0 | // Make sure to notify on any children that we did succeed to insert |
2564 | 0 | if (appending && i != 0) { |
2565 | 0 | nsNodeUtils::ContentAppended(static_cast<nsIContent*>(this), |
2566 | 0 | firstInsertedContent); |
2567 | 0 | } |
2568 | 0 | return nullptr; |
2569 | 0 | } |
2570 | 0 | } |
2571 | 0 |
|
2572 | 0 | if (mutationBatch && !appending) { |
2573 | 0 | mutationBatch->NodesAdded(); |
2574 | 0 | } |
2575 | 0 |
|
2576 | 0 | // Notify and fire mutation events when appending |
2577 | 0 | if (appending) { |
2578 | 0 | nsNodeUtils::ContentAppended(static_cast<nsIContent*>(this), |
2579 | 0 | firstInsertedContent); |
2580 | 0 | if (mutationBatch) { |
2581 | 0 | mutationBatch->NodesAdded(); |
2582 | 0 | } |
2583 | 0 | // Optimize for the case when there are no listeners |
2584 | 0 | if (nsContentUtils:: |
2585 | 0 | HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEINSERTED)) { |
2586 | 0 | Element::FireNodeInserted(doc, this, *fragChildren); |
2587 | 0 | } |
2588 | 0 | } |
2589 | 0 | } |
2590 | 0 | else { |
2591 | 0 | // Not inserting a fragment but rather a single node. |
2592 | 0 |
|
2593 | 0 | // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=544654 |
2594 | 0 | // We need to reparent here for nodes for which the parent of their |
2595 | 0 | // wrapper is not the wrapper for their ownerDocument (XUL elements, |
2596 | 0 | // form controls, ...). Also applies in the fragment code above. |
2597 | 0 | if (nsAutoMutationBatch::GetCurrentBatch() == &mb) { |
2598 | 0 | mb.RemovalDone(); |
2599 | 0 | mb.SetPrevSibling(nodeToInsertBefore ? |
2600 | 0 | nodeToInsertBefore->GetPreviousSibling() : GetLastChild()); |
2601 | 0 | mb.SetNextSibling(nodeToInsertBefore); |
2602 | 0 | } |
2603 | 0 | aError = InsertChildBefore(newContent, nodeToInsertBefore, true); |
2604 | 0 | if (aError.Failed()) { |
2605 | 0 | return nullptr; |
2606 | 0 | } |
2607 | 0 | } |
2608 | 0 | |
2609 | 0 | return result; |
2610 | 0 | } |
2611 | | |
2612 | | void |
2613 | | nsINode::BindObject(nsISupports* aObject) |
2614 | 0 | { |
2615 | 0 | nsCOMArray<nsISupports>* objects = |
2616 | 0 | static_cast<nsCOMArray<nsISupports>*>(GetProperty(nsGkAtoms::keepobjectsalive)); |
2617 | 0 | if (!objects) { |
2618 | 0 | objects = new nsCOMArray<nsISupports>(); |
2619 | 0 | SetProperty(nsGkAtoms::keepobjectsalive, objects, |
2620 | 0 | nsINode::DeleteProperty< nsCOMArray<nsISupports> >, true); |
2621 | 0 | } |
2622 | 0 | objects->AppendObject(aObject); |
2623 | 0 | } |
2624 | | |
2625 | | void |
2626 | | nsINode::UnbindObject(nsISupports* aObject) |
2627 | 0 | { |
2628 | 0 | nsCOMArray<nsISupports>* objects = |
2629 | 0 | static_cast<nsCOMArray<nsISupports>*>(GetProperty(nsGkAtoms::keepobjectsalive)); |
2630 | 0 | if (objects) { |
2631 | 0 | objects->RemoveObject(aObject); |
2632 | 0 | } |
2633 | 0 | } |
2634 | | |
2635 | | already_AddRefed<AccessibleNode> |
2636 | | nsINode::GetAccessibleNode() |
2637 | 0 | { |
2638 | 0 | #ifdef ACCESSIBILITY |
2639 | 0 | nsresult rv = NS_OK; |
2640 | 0 |
|
2641 | 0 | RefPtr<AccessibleNode> anode = |
2642 | 0 | static_cast<AccessibleNode*>(GetProperty(nsGkAtoms::accessiblenode, &rv)); |
2643 | 0 | if (NS_FAILED(rv)) { |
2644 | 0 | anode = new AccessibleNode(this); |
2645 | 0 | RefPtr<AccessibleNode> temp = anode; |
2646 | 0 | rv = SetProperty(nsGkAtoms::accessiblenode, temp.forget().take(), |
2647 | 0 | nsPropertyTable::SupportsDtorFunc, true); |
2648 | 0 | if (NS_FAILED(rv)) { |
2649 | 0 | NS_WARNING("SetProperty failed"); |
2650 | 0 | return nullptr; |
2651 | 0 | } |
2652 | 0 | } |
2653 | 0 | return anode.forget(); |
2654 | | #else |
2655 | | return nullptr; |
2656 | | #endif |
2657 | | } |
2658 | | |
2659 | | void |
2660 | | nsINode::AddSizeOfExcludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const |
2661 | 0 | { |
2662 | 0 | EventListenerManager* elm = GetExistingListenerManager(); |
2663 | 0 | if (elm) { |
2664 | 0 | *aNodeSize += elm->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf); |
2665 | 0 | } |
2666 | 0 |
|
2667 | 0 | // Measurement of the following members may be added later if DMD finds it is |
2668 | 0 | // worthwhile: |
2669 | 0 | // - mNodeInfo |
2670 | 0 | // - mSlots |
2671 | 0 | // |
2672 | 0 | // The following members are not measured: |
2673 | 0 | // - mParent, mNextSibling, mPreviousOrLastSibling, mFirstChild: because they're |
2674 | 0 | // non-owning, from "exclusive ownership" point of view. |
2675 | 0 | } |
2676 | | |
2677 | | void |
2678 | | nsINode::AddSizeOfIncludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const |
2679 | 0 | { |
2680 | 0 | *aNodeSize += aSizes.mState.mMallocSizeOf(this); |
2681 | 0 | AddSizeOfExcludingThis(aSizes, aNodeSize); |
2682 | 0 | } |
2683 | | |
2684 | | #define EVENT(name_, id_, type_, struct_) \ |
2685 | 0 | EventHandlerNonNull* nsINode::GetOn##name_() { \ |
2686 | 0 | EventListenerManager *elm = GetExistingListenerManager(); \ |
2687 | 0 | return elm ? elm->GetEventHandler(nsGkAtoms::on##name_) : nullptr; \ |
2688 | 0 | } \ Unexecuted instantiation: nsINode::GetOnabort() Unexecuted instantiation: nsINode::GetOncanplay() Unexecuted instantiation: nsINode::GetOncanplaythrough() Unexecuted instantiation: nsINode::GetOnchange() Unexecuted instantiation: nsINode::GetOnCheckboxStateChange() Unexecuted instantiation: nsINode::GetOnRadioStateChange() Unexecuted instantiation: nsINode::GetOnauxclick() Unexecuted instantiation: nsINode::GetOnclick() Unexecuted instantiation: nsINode::GetOnclose() Unexecuted instantiation: nsINode::GetOncontextmenu() Unexecuted instantiation: nsINode::GetOndblclick() Unexecuted instantiation: nsINode::GetOndrag() Unexecuted instantiation: nsINode::GetOndragend() Unexecuted instantiation: nsINode::GetOndragenter() Unexecuted instantiation: nsINode::GetOndragexit() Unexecuted instantiation: nsINode::GetOndragleave() Unexecuted instantiation: nsINode::GetOndragover() Unexecuted instantiation: nsINode::GetOndragstart() Unexecuted instantiation: nsINode::GetOndrop() Unexecuted instantiation: nsINode::GetOndurationchange() Unexecuted instantiation: nsINode::GetOnemptied() Unexecuted instantiation: nsINode::GetOnended() Unexecuted instantiation: nsINode::GetOnfullscreenchange() Unexecuted instantiation: nsINode::GetOnfullscreenerror() Unexecuted instantiation: nsINode::GetOninput() Unexecuted instantiation: nsINode::GetOninvalid() Unexecuted instantiation: nsINode::GetOnkeydown() Unexecuted instantiation: nsINode::GetOnkeypress() Unexecuted instantiation: nsINode::GetOnkeyup() Unexecuted instantiation: nsINode::GetOnmozkeydownonplugin() Unexecuted instantiation: nsINode::GetOnmozkeyuponplugin() Unexecuted instantiation: nsINode::GetOnloadeddata() Unexecuted instantiation: nsINode::GetOnloadedmetadata() Unexecuted instantiation: nsINode::GetOnloadend() Unexecuted instantiation: nsINode::GetOnloadstart() Unexecuted instantiation: nsINode::GetOnmousedown() Unexecuted instantiation: nsINode::GetOnmouseenter() Unexecuted instantiation: nsINode::GetOnmouseleave() Unexecuted instantiation: nsINode::GetOnmousemove() Unexecuted instantiation: nsINode::GetOnmouseout() Unexecuted instantiation: nsINode::GetOnmouseover() Unexecuted instantiation: nsINode::GetOnmouseup() Unexecuted instantiation: nsINode::GetOnmozfullscreenchange() Unexecuted instantiation: nsINode::GetOnmozfullscreenerror() Unexecuted instantiation: nsINode::GetOnmozpointerlockchange() Unexecuted instantiation: nsINode::GetOnmozpointerlockerror() Unexecuted instantiation: nsINode::GetOnpointerlockchange() Unexecuted instantiation: nsINode::GetOnpointerlockerror() Unexecuted instantiation: nsINode::GetOnpointerdown() Unexecuted instantiation: nsINode::GetOnpointermove() Unexecuted instantiation: nsINode::GetOnpointerup() Unexecuted instantiation: nsINode::GetOnpointercancel() Unexecuted instantiation: nsINode::GetOnpointerover() Unexecuted instantiation: nsINode::GetOnpointerout() Unexecuted instantiation: nsINode::GetOnpointerenter() Unexecuted instantiation: nsINode::GetOnpointerleave() Unexecuted instantiation: nsINode::GetOngotpointercapture() Unexecuted instantiation: nsINode::GetOnlostpointercapture() Unexecuted instantiation: nsINode::GetOnselectstart() Unexecuted instantiation: nsINode::GetOnpause() Unexecuted instantiation: nsINode::GetOnplay() Unexecuted instantiation: nsINode::GetOnplaying() Unexecuted instantiation: nsINode::GetOnprogress() Unexecuted instantiation: nsINode::GetOnratechange() Unexecuted instantiation: nsINode::GetOnreset() Unexecuted instantiation: nsINode::GetOnseeked() Unexecuted instantiation: nsINode::GetOnseeking() Unexecuted instantiation: nsINode::GetOnselect() Unexecuted instantiation: nsINode::GetOnshow() Unexecuted instantiation: nsINode::GetOnstalled() Unexecuted instantiation: nsINode::GetOnsubmit() Unexecuted instantiation: nsINode::GetOnsuspend() Unexecuted instantiation: nsINode::GetOntimeupdate() Unexecuted instantiation: nsINode::GetOntoggle() Unexecuted instantiation: nsINode::GetOnvolumechange() Unexecuted instantiation: nsINode::GetOnwaiting() Unexecuted instantiation: nsINode::GetOnwheel() Unexecuted instantiation: nsINode::GetOncopy() Unexecuted instantiation: nsINode::GetOncut() Unexecuted instantiation: nsINode::GetOnpaste() Unexecuted instantiation: nsINode::GetOnbeforescriptexecute() Unexecuted instantiation: nsINode::GetOnafterscriptexecute() Unexecuted instantiation: nsINode::GetOnblur() Unexecuted instantiation: nsINode::GetOnerror() Unexecuted instantiation: nsINode::GetOnfocus() Unexecuted instantiation: nsINode::GetOnfocusin() Unexecuted instantiation: nsINode::GetOnfocusout() Unexecuted instantiation: nsINode::GetOnload() Unexecuted instantiation: nsINode::GetOnresize() Unexecuted instantiation: nsINode::GetOnscroll() Unexecuted instantiation: nsINode::GetOntouchstart() Unexecuted instantiation: nsINode::GetOntouchend() Unexecuted instantiation: nsINode::GetOntouchmove() Unexecuted instantiation: nsINode::GetOntouchcancel() Unexecuted instantiation: nsINode::GetOnreadystatechange() Unexecuted instantiation: nsINode::GetOnselectionchange() Unexecuted instantiation: nsINode::GetOnvisibilitychange() Unexecuted instantiation: nsINode::GetOntransitionstart() Unexecuted instantiation: nsINode::GetOntransitionrun() Unexecuted instantiation: nsINode::GetOntransitionend() Unexecuted instantiation: nsINode::GetOntransitioncancel() Unexecuted instantiation: nsINode::GetOnanimationstart() Unexecuted instantiation: nsINode::GetOnanimationend() Unexecuted instantiation: nsINode::GetOnanimationiteration() Unexecuted instantiation: nsINode::GetOnanimationcancel() Unexecuted instantiation: nsINode::GetOnwebkitAnimationEnd() Unexecuted instantiation: nsINode::GetOnwebkitAnimationIteration() Unexecuted instantiation: nsINode::GetOnwebkitAnimationStart() Unexecuted instantiation: nsINode::GetOnwebkitTransitionEnd() Unexecuted instantiation: nsINode::GetOnwebkitanimationend() Unexecuted instantiation: nsINode::GetOnwebkitanimationiteration() Unexecuted instantiation: nsINode::GetOnwebkitanimationstart() Unexecuted instantiation: nsINode::GetOnwebkittransitionend() |
2689 | | void nsINode::SetOn##name_(EventHandlerNonNull* handler) \ |
2690 | 0 | { \ |
2691 | 0 | EventListenerManager *elm = GetOrCreateListenerManager(); \ |
2692 | 0 | if (elm) { \ |
2693 | 0 | elm->SetEventHandler(nsGkAtoms::on##name_, handler); \ |
2694 | 0 | } \ |
2695 | 0 | } Unexecuted instantiation: nsINode::SetOnabort(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOncanplay(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOncanplaythrough(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnchange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnCheckboxStateChange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnRadioStateChange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnauxclick(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnclick(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnclose(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOncontextmenu(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOndblclick(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOndrag(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOndragend(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOndragenter(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOndragexit(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOndragleave(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOndragover(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOndragstart(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOndrop(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOndurationchange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnemptied(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnended(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnfullscreenchange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnfullscreenerror(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOninput(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOninvalid(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnkeydown(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnkeypress(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnkeyup(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmozkeydownonplugin(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmozkeyuponplugin(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnloadeddata(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnloadedmetadata(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnloadend(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnloadstart(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmousedown(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmouseenter(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmouseleave(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmousemove(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmouseout(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmouseover(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmouseup(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmozfullscreenchange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmozfullscreenerror(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmozpointerlockchange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnmozpointerlockerror(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpointerlockchange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpointerlockerror(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpointerdown(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpointermove(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpointerup(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpointercancel(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpointerover(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpointerout(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpointerenter(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpointerleave(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOngotpointercapture(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnlostpointercapture(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnselectstart(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpause(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnplay(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnplaying(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnprogress(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnratechange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnreset(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnseeked(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnseeking(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnselect(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnshow(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnstalled(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnsubmit(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnsuspend(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOntimeupdate(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOntoggle(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnvolumechange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnwaiting(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnwheel(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOncopy(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOncut(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnpaste(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnbeforescriptexecute(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnafterscriptexecute(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnblur(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnerror(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnfocus(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnfocusin(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnfocusout(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnload(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnresize(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnscroll(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOntouchstart(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOntouchend(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOntouchmove(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOntouchcancel(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnreadystatechange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnselectionchange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnvisibilitychange(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOntransitionstart(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOntransitionrun(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOntransitionend(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOntransitioncancel(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnanimationstart(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnanimationend(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnanimationiteration(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnanimationcancel(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnwebkitAnimationEnd(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnwebkitAnimationIteration(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnwebkitAnimationStart(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnwebkitTransitionEnd(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnwebkitanimationend(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnwebkitanimationiteration(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnwebkitanimationstart(mozilla::dom::EventHandlerNonNull*) Unexecuted instantiation: nsINode::SetOnwebkittransitionend(mozilla::dom::EventHandlerNonNull*) |
2696 | | #define TOUCH_EVENT EVENT |
2697 | | #define DOCUMENT_ONLY_EVENT EVENT |
2698 | | #include "mozilla/EventNameList.h" |
2699 | | #undef DOCUMENT_ONLY_EVENT |
2700 | | #undef TOUCH_EVENT |
2701 | | #undef EVENT |
2702 | | |
2703 | | bool |
2704 | | nsINode::Contains(const nsINode* aOther) const |
2705 | 0 | { |
2706 | 0 | if (aOther == this) { |
2707 | 0 | return true; |
2708 | 0 | } |
2709 | 0 | if (!aOther || |
2710 | 0 | OwnerDoc() != aOther->OwnerDoc() || |
2711 | 0 | IsInUncomposedDoc() != aOther->IsInUncomposedDoc() || |
2712 | 0 | !aOther->IsContent() || |
2713 | 0 | !GetFirstChild()) { |
2714 | 0 | return false; |
2715 | 0 | } |
2716 | 0 | |
2717 | 0 | const nsIContent* other = static_cast<const nsIContent*>(aOther); |
2718 | 0 | if (this == OwnerDoc()) { |
2719 | 0 | // document.contains(aOther) returns true if aOther is in the document, |
2720 | 0 | // but is not in any anonymous subtree. |
2721 | 0 | // IsInUncomposedDoc() check is done already before this. |
2722 | 0 | return !other->IsInAnonymousSubtree(); |
2723 | 0 | } |
2724 | 0 | |
2725 | 0 | if (!IsElement() && !IsDocumentFragment()) { |
2726 | 0 | return false; |
2727 | 0 | } |
2728 | 0 | |
2729 | 0 | if (AsContent()->GetBindingParent() != other->GetBindingParent()) { |
2730 | 0 | return false; |
2731 | 0 | } |
2732 | 0 | |
2733 | 0 | return nsContentUtils::ContentIsDescendantOf(other, this); |
2734 | 0 | } |
2735 | | |
2736 | | uint32_t |
2737 | | nsINode::Length() const |
2738 | 0 | { |
2739 | 0 | switch (NodeType()) { |
2740 | 0 | case DOCUMENT_TYPE_NODE: |
2741 | 0 | return 0; |
2742 | 0 |
|
2743 | 0 | case TEXT_NODE: |
2744 | 0 | case CDATA_SECTION_NODE: |
2745 | 0 | case PROCESSING_INSTRUCTION_NODE: |
2746 | 0 | case COMMENT_NODE: |
2747 | 0 | MOZ_ASSERT(IsContent()); |
2748 | 0 | return AsContent()->TextLength(); |
2749 | 0 |
|
2750 | 0 | default: |
2751 | 0 | return GetChildCount(); |
2752 | 0 | } |
2753 | 0 | } |
2754 | | |
2755 | | const RawServoSelectorList* |
2756 | | nsINode::ParseSelectorList(const nsAString& aSelectorString, |
2757 | | ErrorResult& aRv) |
2758 | 0 | { |
2759 | 0 | nsIDocument* doc = OwnerDoc(); |
2760 | 0 |
|
2761 | 0 | nsIDocument::SelectorCache& cache = doc->GetSelectorCache(); |
2762 | 0 | nsIDocument::SelectorCache::SelectorList* list = |
2763 | 0 | cache.GetList(aSelectorString); |
2764 | 0 | if (list) { |
2765 | 0 | if (!*list) { |
2766 | 0 | // Invalid selector. |
2767 | 0 | aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR, |
2768 | 0 | NS_LITERAL_CSTRING("'") + NS_ConvertUTF16toUTF8(aSelectorString) + |
2769 | 0 | NS_LITERAL_CSTRING("' is not a valid selector") |
2770 | 0 | ); |
2771 | 0 | return nullptr; |
2772 | 0 | } |
2773 | 0 |
|
2774 | 0 | return list->get(); |
2775 | 0 | } |
2776 | 0 | |
2777 | 0 | NS_ConvertUTF16toUTF8 selectorString(aSelectorString); |
2778 | 0 |
|
2779 | 0 | auto selectorList = |
2780 | 0 | UniquePtr<RawServoSelectorList>(Servo_SelectorList_Parse(&selectorString)); |
2781 | 0 | if (!selectorList) { |
2782 | 0 | aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR, |
2783 | 0 | NS_LITERAL_CSTRING("'") + selectorString + |
2784 | 0 | NS_LITERAL_CSTRING("' is not a valid selector") |
2785 | 0 | ); |
2786 | 0 | } |
2787 | 0 |
|
2788 | 0 | auto* ret = selectorList.get(); |
2789 | 0 | cache.CacheList(aSelectorString, std::move(selectorList)); |
2790 | 0 | return ret; |
2791 | 0 | } |
2792 | | |
2793 | | namespace { |
2794 | | struct SelectorMatchInfo { |
2795 | | }; |
2796 | | } // namespace |
2797 | | |
2798 | | // Given an id, find first element with that id under aRoot. |
2799 | | // If none found, return nullptr. aRoot must be in the document. |
2800 | | inline static Element* |
2801 | | FindMatchingElementWithId(const nsAString& aId, nsINode* aRoot) |
2802 | 0 | { |
2803 | 0 | MOZ_ASSERT(aRoot->IsInUncomposedDoc(), |
2804 | 0 | "Don't call me if the root is not in the document"); |
2805 | 0 | // FIXME(emilio): It'd be nice to optimize this for shadow roots too. |
2806 | 0 | MOZ_ASSERT(aRoot->IsElement() || aRoot->IsDocument(), |
2807 | 0 | "The optimization below to check ContentIsDescendantOf only for " |
2808 | 0 | "elements depends on aRoot being either an element or a " |
2809 | 0 | "document if it's in the document. Note that document fragments " |
2810 | 0 | "can't be IsInUncomposedDoc(), so should never show up here."); |
2811 | 0 |
|
2812 | 0 | const nsTArray<Element*>* elements = aRoot->OwnerDoc()->GetAllElementsForId(aId); |
2813 | 0 | if (!elements) { |
2814 | 0 | // Nothing to do; we're done |
2815 | 0 | return nullptr; |
2816 | 0 | } |
2817 | 0 | |
2818 | 0 | // XXXbz: Should we fall back to the tree walk if aRoot is not the |
2819 | 0 | // document and |elements| is long, for some value of "long"? |
2820 | 0 | for (size_t i = 0; i < elements->Length(); ++i) { |
2821 | 0 | Element* element = (*elements)[i]; |
2822 | 0 | if (!aRoot->IsElement() || |
2823 | 0 | (element != aRoot && |
2824 | 0 | nsContentUtils::ContentIsDescendantOf(element, aRoot))) { |
2825 | 0 | // We have an element with the right id and it's a strict descendant |
2826 | 0 | // of aRoot. |
2827 | 0 | return element; |
2828 | 0 | } |
2829 | 0 | } |
2830 | 0 | return nullptr; |
2831 | 0 | } |
2832 | | |
2833 | | Element* |
2834 | | nsINode::QuerySelector(const nsAString& aSelector, ErrorResult& aResult) |
2835 | 0 | { |
2836 | 0 | AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING( |
2837 | 0 | "nsINode::QuerySelector", DOM, aSelector); |
2838 | 0 |
|
2839 | 0 | const RawServoSelectorList* list = ParseSelectorList(aSelector, aResult); |
2840 | 0 | if (!list) { |
2841 | 0 | return nullptr; |
2842 | 0 | } |
2843 | 0 | const bool useInvalidation = false; |
2844 | 0 | return const_cast<Element*>( |
2845 | 0 | Servo_SelectorList_QueryFirst(this, list, useInvalidation)); |
2846 | 0 | } |
2847 | | |
2848 | | already_AddRefed<nsINodeList> |
2849 | | nsINode::QuerySelectorAll(const nsAString& aSelector, ErrorResult& aResult) |
2850 | 0 | { |
2851 | 0 | AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING( |
2852 | 0 | "nsINode::QuerySelectorAll", DOM, aSelector); |
2853 | 0 |
|
2854 | 0 | RefPtr<nsSimpleContentList> contentList = new nsSimpleContentList(this); |
2855 | 0 | const RawServoSelectorList* list = ParseSelectorList(aSelector, aResult); |
2856 | 0 | if (!list) { |
2857 | 0 | return contentList.forget(); |
2858 | 0 | } |
2859 | 0 | |
2860 | 0 | const bool useInvalidation = false; |
2861 | 0 | Servo_SelectorList_QueryAll(this, list, contentList.get(), useInvalidation); |
2862 | 0 | return contentList.forget(); |
2863 | 0 | } |
2864 | | |
2865 | | Element* |
2866 | | nsINode::GetElementById(const nsAString& aId) |
2867 | 0 | { |
2868 | 0 | MOZ_ASSERT(IsElement() || IsDocumentFragment(), |
2869 | 0 | "Bogus this object for GetElementById call"); |
2870 | 0 | if (IsInUncomposedDoc()) { |
2871 | 0 | return FindMatchingElementWithId(aId, this); |
2872 | 0 | } |
2873 | 0 | |
2874 | 0 | for (nsIContent* kid = GetFirstChild(); kid; kid = kid->GetNextNode(this)) { |
2875 | 0 | if (!kid->IsElement()) { |
2876 | 0 | continue; |
2877 | 0 | } |
2878 | 0 | nsAtom* id = kid->AsElement()->GetID(); |
2879 | 0 | if (id && id->Equals(aId)) { |
2880 | 0 | return kid->AsElement(); |
2881 | 0 | } |
2882 | 0 | } |
2883 | 0 | return nullptr; |
2884 | 0 | } |
2885 | | |
2886 | | JSObject* |
2887 | | nsINode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) |
2888 | 0 | { |
2889 | 0 | // Make sure one of these is true |
2890 | 0 | // (1) our owner document has a script handling object, |
2891 | 0 | // (2) Our owner document has had a script handling object, or has been marked |
2892 | 0 | // to have had one, |
2893 | 0 | // (3) we are running a privileged script. |
2894 | 0 | // Event handling is possible only if (1). If (2) event handling is |
2895 | 0 | // prevented. |
2896 | 0 | // If the document has never had a script handling object, untrusted |
2897 | 0 | // scripts (3) shouldn't touch it! |
2898 | 0 | bool hasHadScriptHandlingObject = false; |
2899 | 0 | if (!OwnerDoc()->GetScriptHandlingObject(hasHadScriptHandlingObject) && |
2900 | 0 | !hasHadScriptHandlingObject && |
2901 | 0 | !nsContentUtils::IsSystemCaller(aCx)) { |
2902 | 0 | Throw(aCx, NS_ERROR_UNEXPECTED); |
2903 | 0 | return nullptr; |
2904 | 0 | } |
2905 | 0 | |
2906 | 0 | JS::Rooted<JSObject*> obj(aCx, WrapNode(aCx, aGivenProto)); |
2907 | 0 | MOZ_ASSERT_IF(obj && ChromeOnlyAccess(), |
2908 | 0 | xpc::IsInContentXBLScope(obj) || |
2909 | 0 | !xpc::UseContentXBLScope(JS::GetObjectRealmOrNull(obj))); |
2910 | 0 | return obj; |
2911 | 0 | } |
2912 | | |
2913 | | already_AddRefed<nsINode> |
2914 | | nsINode::CloneNode(bool aDeep, ErrorResult& aError) |
2915 | 0 | { |
2916 | 0 | return nsNodeUtils::CloneNodeImpl(this, aDeep, aError); |
2917 | 0 | } |
2918 | | |
2919 | | nsDOMAttributeMap* |
2920 | | nsINode::GetAttributes() |
2921 | 0 | { |
2922 | 0 | if (!IsElement()) { |
2923 | 0 | return nullptr; |
2924 | 0 | } |
2925 | 0 | return AsElement()->Attributes(); |
2926 | 0 | } |
2927 | | |
2928 | | Element* |
2929 | | nsINode::GetParentElementCrossingShadowRoot() const |
2930 | 0 | { |
2931 | 0 | if (!mParent) { |
2932 | 0 | return nullptr; |
2933 | 0 | } |
2934 | 0 | |
2935 | 0 | if (mParent->IsElement()) { |
2936 | 0 | return mParent->AsElement(); |
2937 | 0 | } |
2938 | 0 | |
2939 | 0 | if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(mParent)) { |
2940 | 0 | MOZ_ASSERT(shadowRoot->GetHost(), "ShowRoots should always have a host"); |
2941 | 0 | return shadowRoot->GetHost(); |
2942 | 0 | } |
2943 | 0 |
|
2944 | 0 | return nullptr; |
2945 | 0 | } |
2946 | | |
2947 | | bool |
2948 | | nsINode::HasBoxQuadsSupport(JSContext* aCx, JSObject* /* unused */) |
2949 | 0 | { |
2950 | 0 | return xpc::AccessCheck::isChrome(js::GetContextCompartment(aCx)) || |
2951 | 0 | nsContentUtils::GetBoxQuadsEnabled(); |
2952 | 0 | } |
2953 | | |
2954 | | nsINode* |
2955 | | nsINode::GetScopeChainParent() const |
2956 | 0 | { |
2957 | 0 | return nullptr; |
2958 | 0 | } |
2959 | | |
2960 | | void |
2961 | | nsINode::AddAnimationObserver(nsIAnimationObserver* aAnimationObserver) |
2962 | 0 | { |
2963 | 0 | AddMutationObserver(aAnimationObserver); |
2964 | 0 | OwnerDoc()->SetMayHaveAnimationObservers(); |
2965 | 0 | } |
2966 | | |
2967 | | void |
2968 | | nsINode::AddAnimationObserverUnlessExists( |
2969 | | nsIAnimationObserver* aAnimationObserver) |
2970 | 0 | { |
2971 | 0 | AddMutationObserverUnlessExists(aAnimationObserver); |
2972 | 0 | OwnerDoc()->SetMayHaveAnimationObservers(); |
2973 | 0 | } |
2974 | | |
2975 | | void |
2976 | | nsINode::GenerateXPath(nsAString& aResult) |
2977 | 0 | { |
2978 | 0 | XPathGenerator::Generate(this, aResult); |
2979 | 0 | } |
2980 | | |
2981 | | bool |
2982 | | nsINode::IsApzAware() const |
2983 | 0 | { |
2984 | 0 | return IsNodeApzAware(); |
2985 | 0 | } |
2986 | | |
2987 | | bool |
2988 | | nsINode::IsNodeApzAwareInternal() const |
2989 | 0 | { |
2990 | 0 | return EventTarget::IsApzAware(); |
2991 | 0 | } |
2992 | | |
2993 | | DocGroup* |
2994 | | nsINode::GetDocGroup() const |
2995 | 0 | { |
2996 | 0 | return OwnerDoc()->GetDocGroup(); |
2997 | 0 | } |
2998 | | |
2999 | | class LocalizationHandler : public PromiseNativeHandler |
3000 | | { |
3001 | | public: |
3002 | 0 | LocalizationHandler() = default; |
3003 | | |
3004 | | NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
3005 | | NS_DECL_CYCLE_COLLECTION_CLASS(LocalizationHandler) |
3006 | | |
3007 | 0 | nsTArray<nsCOMPtr<Element>>& Elements() { return mElements; } |
3008 | | |
3009 | | void SetReturnValuePromise(Promise* aReturnValuePromise) |
3010 | 0 | { |
3011 | 0 | mReturnValuePromise = aReturnValuePromise; |
3012 | 0 | } |
3013 | | |
3014 | | virtual void |
3015 | | ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override |
3016 | 0 | { |
3017 | 0 | nsTArray<L10nValue> l10nData; |
3018 | 0 | if (aValue.isObject()) { |
3019 | 0 | JS::ForOfIterator iter(aCx); |
3020 | 0 | if (!iter.init(aValue, JS::ForOfIterator::AllowNonIterable)) { |
3021 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3022 | 0 | return; |
3023 | 0 | } |
3024 | 0 | if (!iter.valueIsIterable()) { |
3025 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3026 | 0 | return; |
3027 | 0 | } |
3028 | 0 | |
3029 | 0 | JS::Rooted<JS::Value> temp(aCx); |
3030 | 0 | while (true) { |
3031 | 0 | bool done; |
3032 | 0 | if (!iter.next(&temp, &done)) { |
3033 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3034 | 0 | return; |
3035 | 0 | } |
3036 | 0 | |
3037 | 0 | if (done) { |
3038 | 0 | break; |
3039 | 0 | } |
3040 | 0 | |
3041 | 0 | L10nValue* slotPtr = |
3042 | 0 | l10nData.AppendElement(mozilla::fallible); |
3043 | 0 | if (!slotPtr) { |
3044 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3045 | 0 | return; |
3046 | 0 | } |
3047 | 0 | |
3048 | 0 | if (!slotPtr->Init(aCx, temp)) { |
3049 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3050 | 0 | return; |
3051 | 0 | } |
3052 | 0 | } |
3053 | 0 | } |
3054 | 0 |
|
3055 | 0 | if (mElements.Length() != l10nData.Length()) { |
3056 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3057 | 0 | return; |
3058 | 0 | } |
3059 | 0 | |
3060 | 0 | JS::Rooted<JSObject*> untranslatedElements(aCx, |
3061 | 0 | JS_NewArrayObject(aCx, mElements.Length())); |
3062 | 0 | if (!untranslatedElements) { |
3063 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3064 | 0 | return; |
3065 | 0 | } |
3066 | 0 | |
3067 | 0 | ErrorResult rv; |
3068 | 0 | for (size_t i = 0; i < l10nData.Length(); ++i) { |
3069 | 0 | Element* elem = mElements[i]; |
3070 | 0 | nsString& content = l10nData[i].mValue; |
3071 | 0 | if (!content.IsVoid()) { |
3072 | 0 | elem->SetTextContent(content, rv); |
3073 | 0 | if (NS_WARN_IF(rv.Failed())) { |
3074 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3075 | 0 | return; |
3076 | 0 | } |
3077 | 0 | } |
3078 | 0 | |
3079 | 0 | Nullable<Sequence<AttributeNameValue>>& attributes = |
3080 | 0 | l10nData[i].mAttributes; |
3081 | 0 | if (!attributes.IsNull()) { |
3082 | 0 | for (size_t j = 0; j < attributes.Value().Length(); ++j) { |
3083 | 0 | // Use SetAttribute here to validate the attribute name! |
3084 | 0 | elem->SetAttribute(attributes.Value()[j].mName, |
3085 | 0 | attributes.Value()[j].mValue, |
3086 | 0 | rv); |
3087 | 0 | if (rv.Failed()) { |
3088 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3089 | 0 | return; |
3090 | 0 | } |
3091 | 0 | } |
3092 | 0 | } |
3093 | 0 |
|
3094 | 0 | if (content.IsVoid() && attributes.IsNull()) { |
3095 | 0 | JS::Rooted<JS::Value> wrappedElem(aCx); |
3096 | 0 | if (!ToJSValue(aCx, elem, &wrappedElem)) { |
3097 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3098 | 0 | return; |
3099 | 0 | } |
3100 | 0 | |
3101 | 0 | if (!JS_DefineElement(aCx, untranslatedElements, i, wrappedElem, JSPROP_ENUMERATE)) { |
3102 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3103 | 0 | return; |
3104 | 0 | } |
3105 | 0 | } |
3106 | 0 | } |
3107 | 0 | mReturnValuePromise->MaybeResolve(untranslatedElements); |
3108 | 0 | } |
3109 | | |
3110 | | virtual void |
3111 | | RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override |
3112 | 0 | { |
3113 | 0 | mReturnValuePromise->MaybeRejectWithUndefined(); |
3114 | 0 | } |
3115 | | |
3116 | | private: |
3117 | 0 | ~LocalizationHandler() = default; |
3118 | | |
3119 | | nsTArray<nsCOMPtr<Element>> mElements; |
3120 | | RefPtr<Promise> mReturnValuePromise; |
3121 | | }; |
3122 | | |
3123 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LocalizationHandler) |
3124 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
3125 | 0 | NS_INTERFACE_MAP_END |
3126 | | |
3127 | | NS_IMPL_CYCLE_COLLECTION_CLASS(LocalizationHandler) |
3128 | | |
3129 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(LocalizationHandler) |
3130 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(LocalizationHandler) |
3131 | | |
3132 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LocalizationHandler) |
3133 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements) |
3134 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValuePromise) |
3135 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
3136 | | |
3137 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LocalizationHandler) |
3138 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements) |
3139 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValuePromise) |
3140 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
3141 | | |
3142 | | |
3143 | | already_AddRefed<Promise> |
3144 | | nsINode::Localize(JSContext* aCx, |
3145 | | mozilla::dom::L10nCallback& aCallback, |
3146 | | mozilla::ErrorResult& aRv) |
3147 | 0 | { |
3148 | 0 | Sequence<L10nElement> l10nElements; |
3149 | 0 | SequenceRooter<L10nElement> rooter(aCx, &l10nElements); |
3150 | 0 | RefPtr<LocalizationHandler> nativeHandler = new LocalizationHandler(); |
3151 | 0 | nsTArray<nsCOMPtr<Element>>& domElements = nativeHandler->Elements(); |
3152 | 0 | nsIContent* node = IsContent() ? AsContent() : GetFirstChild(); |
3153 | 0 | nsAutoString l10nId; |
3154 | 0 | nsAutoString l10nArgs; |
3155 | 0 | nsAutoString l10nAttrs; |
3156 | 0 | nsAutoString type; |
3157 | 0 | for (; node; node = node->GetNextNode(this)) { |
3158 | 0 | if (!node->IsElement()) { |
3159 | 0 | continue; |
3160 | 0 | } |
3161 | 0 | |
3162 | 0 | Element* domElement = node->AsElement(); |
3163 | 0 | if (!domElement->GetAttr(kNameSpaceID_None, nsGkAtoms::datal10nid, l10nId)) { |
3164 | 0 | continue; |
3165 | 0 | } |
3166 | 0 | |
3167 | 0 | domElement->GetAttr(kNameSpaceID_None, nsGkAtoms::datal10nargs, l10nArgs); |
3168 | 0 | domElement->GetAttr(kNameSpaceID_None, nsGkAtoms::datal10nattrs, l10nAttrs); |
3169 | 0 | L10nElement* element = l10nElements.AppendElement(fallible); |
3170 | 0 | if (!element) { |
3171 | 0 | aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
3172 | 0 | return nullptr; |
3173 | 0 | } |
3174 | 0 | if (!domElements.AppendElement(domElement, fallible)) { |
3175 | 0 | aRv.Throw(NS_ERROR_OUT_OF_MEMORY); |
3176 | 0 | return nullptr; |
3177 | 0 | } |
3178 | 0 | |
3179 | 0 | domElement->GetNamespaceURI(element->mNamespaceURI); |
3180 | 0 | element->mLocalName = domElement->LocalName(); |
3181 | 0 | domElement->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type); |
3182 | 0 | if (!type.IsEmpty()) { |
3183 | 0 | element->mType = type; |
3184 | 0 | } |
3185 | 0 | element->mL10nId = l10nId; |
3186 | 0 | if (!l10nAttrs.IsEmpty()) { |
3187 | 0 | element->mL10nAttrs = l10nAttrs; |
3188 | 0 | } |
3189 | 0 | if (!l10nArgs.IsEmpty()) { |
3190 | 0 | JS::Rooted<JS::Value> json(aCx); |
3191 | 0 | if (!JS_ParseJSON(aCx, l10nArgs.get(), l10nArgs.Length(), &json) || |
3192 | 0 | !json.isObject()) { |
3193 | 0 | aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); |
3194 | 0 | return nullptr; |
3195 | 0 | } |
3196 | 0 | element->mL10nArgs = &json.toObject(); |
3197 | 0 | } |
3198 | 0 | } |
3199 | 0 |
|
3200 | 0 | RefPtr<Promise> callbackResult = aCallback.Call(l10nElements, aRv); |
3201 | 0 | if (aRv.Failed()) { |
3202 | 0 | return nullptr; |
3203 | 0 | } |
3204 | 0 | |
3205 | 0 | RefPtr<Promise> promise = Promise::Create(OwnerDoc()->GetParentObject(), aRv); |
3206 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
3207 | 0 | return nullptr; |
3208 | 0 | } |
3209 | 0 | |
3210 | 0 | nativeHandler->SetReturnValuePromise(promise); |
3211 | 0 | callbackResult->AppendNativeHandler(nativeHandler); |
3212 | 0 |
|
3213 | 0 | return promise.forget(); |
3214 | 0 | } |
3215 | | |
3216 | | nsINode* |
3217 | | nsINode::GetFlattenedTreeParentNodeNonInline() const |
3218 | 0 | { |
3219 | 0 | return GetFlattenedTreeParentNode(); |
3220 | 0 | } |
3221 | | |
3222 | | NS_IMPL_ISUPPORTS(nsNodeWeakReference, |
3223 | | nsIWeakReference) |
3224 | | |
3225 | | nsNodeWeakReference::nsNodeWeakReference(nsINode* aNode) |
3226 | | : nsIWeakReference(aNode) |
3227 | 0 | { |
3228 | 0 | } |
3229 | | |
3230 | | nsNodeWeakReference::~nsNodeWeakReference() |
3231 | 0 | { |
3232 | 0 | nsINode* node = static_cast<nsINode*>(mObject); |
3233 | 0 |
|
3234 | 0 | if (node) { |
3235 | 0 | NS_ASSERTION(node->Slots()->mWeakReference == this, |
3236 | 0 | "Weak reference has wrong value"); |
3237 | 0 | node->Slots()->mWeakReference = nullptr; |
3238 | 0 | } |
3239 | 0 | } |
3240 | | |
3241 | | NS_IMETHODIMP |
3242 | | nsNodeWeakReference::QueryReferentFromScript(const nsIID& aIID, void** aInstancePtr) |
3243 | 0 | { |
3244 | 0 | return QueryReferent(aIID, aInstancePtr); |
3245 | 0 | } |
3246 | | |
3247 | | size_t |
3248 | | nsNodeWeakReference::SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const |
3249 | 0 | { |
3250 | 0 | return aMallocSizeOf(this); |
3251 | 0 | } |