Coverage Report

Created: 2018-09-25 14:53

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