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