Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/FragmentOrElement.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
/*
8
 * Base class for all element classes and DocumentFragment.
9
 */
10
11
#include "mozilla/ArrayUtils.h"
12
#include "mozilla/Likely.h"
13
#include "mozilla/MemoryReporting.h"
14
#include "mozilla/StaticPtr.h"
15
16
#include "mozilla/dom/FragmentOrElement.h"
17
18
#include "mozilla/AsyncEventDispatcher.h"
19
#include "mozilla/DeclarationBlock.h"
20
#include "mozilla/EffectSet.h"
21
#include "mozilla/EventDispatcher.h"
22
#include "mozilla/EventListenerManager.h"
23
#include "mozilla/EventStates.h"
24
#include "mozilla/HTMLEditor.h"
25
#include "mozilla/RestyleManager.h"
26
#include "mozilla/TextEditor.h"
27
#include "mozilla/TouchEvents.h"
28
#include "mozilla/URLExtraData.h"
29
#include "mozilla/dom/Attr.h"
30
#include "nsDOMAttributeMap.h"
31
#include "nsAtom.h"
32
#include "mozilla/dom/NodeInfo.h"
33
#include "mozilla/dom/Event.h"
34
#include "mozilla/dom/TouchEvent.h"
35
#include "nsIDocumentInlines.h"
36
#include "nsIDocumentEncoder.h"
37
#include "nsIContentIterator.h"
38
#include "nsFocusManager.h"
39
#include "nsILinkHandler.h"
40
#include "nsIScriptGlobalObject.h"
41
#include "nsIURL.h"
42
#include "nsNetUtil.h"
43
#include "nsIFrame.h"
44
#include "nsIAnonymousContentCreator.h"
45
#include "nsIPresShell.h"
46
#include "nsPresContext.h"
47
#include "nsStyleConsts.h"
48
#include "nsString.h"
49
#include "nsUnicharUtils.h"
50
#include "nsDOMCID.h"
51
#include "nsIServiceManager.h"
52
#include "nsDOMCSSAttrDeclaration.h"
53
#include "nsNameSpaceManager.h"
54
#include "nsContentList.h"
55
#include "nsDOMTokenList.h"
56
#include "nsXBLPrototypeBinding.h"
57
#include "nsError.h"
58
#include "nsDOMString.h"
59
#include "nsIScriptSecurityManager.h"
60
#include "mozilla/InternalMutationEvent.h"
61
#include "mozilla/MouseEvents.h"
62
#include "nsNodeUtils.h"
63
#include "nsDocument.h"
64
#include "nsAttrValueOrString.h"
65
#include "nsQueryObject.h"
66
#ifdef MOZ_XUL
67
#include "nsXULElement.h"
68
#endif /* MOZ_XUL */
69
#include "nsFrameSelection.h"
70
#ifdef DEBUG
71
#include "nsRange.h"
72
#endif
73
74
#include "nsBindingManager.h"
75
#include "nsFrameLoader.h"
76
#include "nsXBLBinding.h"
77
#include "nsPIDOMWindow.h"
78
#include "nsPIBoxObject.h"
79
#include "nsSVGUtils.h"
80
#include "nsLayoutUtils.h"
81
#include "nsGkAtoms.h"
82
#include "nsContentUtils.h"
83
#include "nsTextFragment.h"
84
#include "nsContentCID.h"
85
#include "nsWindowSizes.h"
86
87
#include "nsIDOMEventListener.h"
88
#include "nsIWebNavigation.h"
89
#include "nsIBaseWindow.h"
90
#include "nsIWidget.h"
91
92
#include "nsNodeInfoManager.h"
93
#include "nsICategoryManager.h"
94
#include "nsGenericHTMLElement.h"
95
#include "nsContentCreatorFunctions.h"
96
#include "nsIControllers.h"
97
#include "nsView.h"
98
#include "nsViewManager.h"
99
#include "nsIScrollableFrame.h"
100
#include "ChildIterator.h"
101
#include "nsTextNode.h"
102
#include "mozilla/dom/NodeListBinding.h"
103
104
#include "nsCCUncollectableMarker.h"
105
106
#include "mozAutoDocUpdate.h"
107
108
#include "mozilla/Sprintf.h"
109
#include "nsDOMMutationObserver.h"
110
#include "nsWrapperCacheInlines.h"
111
#include "nsCycleCollector.h"
112
#include "xpcpublic.h"
113
#include "nsIScriptError.h"
114
#include "mozilla/Telemetry.h"
115
116
#include "mozilla/CORSMode.h"
117
118
#include "mozilla/dom/ShadowRoot.h"
119
#include "mozilla/dom/HTMLSlotElement.h"
120
#include "mozilla/dom/HTMLTemplateElement.h"
121
#include "mozilla/dom/SVGUseElement.h"
122
123
#include "nsStyledElement.h"
124
#include "nsIContentInlines.h"
125
#include "nsChildContentList.h"
126
#include "mozilla/BloomFilter.h"
127
128
#include "NodeUbiReporting.h"
129
130
using namespace mozilla;
131
using namespace mozilla::dom;
132
133
int32_t nsIContent::sTabFocusModel = eTabFocus_any;
134
bool nsIContent::sTabFocusModelAppliesToXUL = false;
135
uint64_t nsMutationGuard::sGeneration = 0;
136
137
NS_IMPL_CYCLE_COLLECTION_CLASS(nsIContent)
138
139
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsIContent)
140
0
  MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
141
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
142
143
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsIContent)
144
0
  MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
145
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
146
147
0
NS_INTERFACE_MAP_BEGIN(nsIContent)
148
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
149
0
  // Don't bother to QI to cycle collection, because our CC impl is
150
0
  // not doing anything anyway.
151
0
  NS_INTERFACE_MAP_ENTRY(nsIContent)
152
0
  NS_INTERFACE_MAP_ENTRY(nsINode)
153
0
  NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
154
0
  NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
155
0
                                 new nsNodeSupportsWeakRefTearoff(this))
156
0
  // DOM bindings depend on the identity pointer being the
157
0
  // same as nsINode (which nsIContent inherits).
158
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
159
0
NS_INTERFACE_MAP_END
160
161
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsIContent)
162
NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsIContent,
163
                                                                    nsNodeUtils::LastRelease(this))
164
165
nsIContent*
166
nsIContent::FindFirstNonChromeOnlyAccessContent() const
167
0
{
168
0
  // This handles also nested native anonymous content.
169
0
  for (const nsIContent *content = this; content;
170
0
       content = content->GetBindingParent()) {
171
0
    if (!content->ChromeOnlyAccess()) {
172
0
      // Oops, this function signature allows casting const to
173
0
      // non-const.  (Then again, so does GetChildAt_Deprecated(0)->GetParent().)
174
0
      return const_cast<nsIContent*>(content);
175
0
    }
176
0
  }
177
0
  return nullptr;
178
0
}
179
180
// https://dom.spec.whatwg.org/#dom-slotable-assignedslot
181
HTMLSlotElement*
182
nsIContent::GetAssignedSlotByMode() const
183
0
{
184
0
  /**
185
0
   * Get slotable's assigned slot for the result of
186
0
   * find a slot with open flag UNSET [1].
187
0
   *
188
0
   * [1] https://dom.spec.whatwg.org/#assign-a-slot
189
0
   */
190
0
  HTMLSlotElement* slot = GetAssignedSlot();
191
0
  if (!slot) {
192
0
    return nullptr;
193
0
  }
194
0
195
0
  MOZ_ASSERT(GetParent());
196
0
  MOZ_ASSERT(GetParent()->GetShadowRoot());
197
0
198
0
  /**
199
0
   * Additional check for open flag SET:
200
0
   *   If slotable’s parent’s shadow root's mode is not "open",
201
0
   *   then return null.
202
0
   */
203
0
  if (GetParent()->GetShadowRoot()->IsClosed()) {
204
0
    return nullptr;
205
0
  }
206
0
207
0
  return slot;
208
0
}
209
210
nsIContent::IMEState
211
nsIContent::GetDesiredIMEState()
212
0
{
213
0
  if (!IsEditable()) {
214
0
    // Check for the special case where we're dealing with elements which don't
215
0
    // have the editable flag set, but are readwrite (such as text controls).
216
0
    if (!IsElement() ||
217
0
        !AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
218
0
      return IMEState(IMEState::DISABLED);
219
0
    }
220
0
  }
221
0
  // NOTE: The content for independent editors (e.g., input[type=text],
222
0
  // textarea) must override this method, so, we don't need to worry about
223
0
  // that here.
224
0
  nsIContent *editableAncestor = GetEditingHost();
225
0
226
0
  // This is in another editable content, use the result of it.
227
0
  if (editableAncestor && editableAncestor != this) {
228
0
    return editableAncestor->GetDesiredIMEState();
229
0
  }
230
0
  nsIDocument* doc = GetComposedDoc();
231
0
  if (!doc) {
232
0
    return IMEState(IMEState::DISABLED);
233
0
  }
234
0
  nsPresContext* pc = doc->GetPresContext();
235
0
  if (!pc) {
236
0
    return IMEState(IMEState::DISABLED);
237
0
  }
238
0
  HTMLEditor* htmlEditor = nsContentUtils::GetHTMLEditor(pc);
239
0
  if (!htmlEditor) {
240
0
    return IMEState(IMEState::DISABLED);
241
0
  }
242
0
  IMEState state;
243
0
  htmlEditor->GetPreferredIMEState(&state);
244
0
  return state;
245
0
}
246
247
bool
248
nsIContent::HasIndependentSelection()
249
0
{
250
0
  nsIFrame* frame = GetPrimaryFrame();
251
0
  return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
252
0
}
253
254
dom::Element*
255
nsIContent::GetEditingHost()
256
0
{
257
0
  // If this isn't editable, return nullptr.
258
0
  if (!IsEditable()) {
259
0
    return nullptr;
260
0
  }
261
0
262
0
  nsIDocument* doc = GetComposedDoc();
263
0
  if (!doc) {
264
0
    return nullptr;
265
0
  }
266
0
267
0
  // If this is in designMode, we should return <body>
268
0
  if (doc->HasFlag(NODE_IS_EDITABLE) && !IsInShadowTree()) {
269
0
    return doc->GetBodyElement();
270
0
  }
271
0
272
0
  nsIContent* content = this;
273
0
  for (dom::Element* parent = GetParentElement();
274
0
       parent && parent->HasFlag(NODE_IS_EDITABLE);
275
0
       parent = content->GetParentElement()) {
276
0
    content = parent;
277
0
  }
278
0
  return content->AsElement();
279
0
}
280
281
nsresult
282
nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
283
                                       nsAString& aNamespaceURI) const
284
0
{
285
0
  if (aNamespacePrefix.EqualsLiteral("xml")) {
286
0
    // Special-case for xml prefix
287
0
    aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace");
288
0
    return NS_OK;
289
0
  }
290
0
291
0
  if (aNamespacePrefix.EqualsLiteral("xmlns")) {
292
0
    // Special-case for xmlns prefix
293
0
    aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
294
0
    return NS_OK;
295
0
  }
296
0
297
0
  RefPtr<nsAtom> name;
298
0
  if (!aNamespacePrefix.IsEmpty()) {
299
0
    name = NS_Atomize(aNamespacePrefix);
300
0
    NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
301
0
  }
302
0
  else {
303
0
    name = nsGkAtoms::xmlns;
304
0
  }
305
0
  // Trace up the content parent chain looking for the namespace
306
0
  // declaration that declares aNamespacePrefix.
307
0
  const nsIContent* content = this;
308
0
  do {
309
0
    if (content->IsElement() &&
310
0
        content->AsElement()->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
311
0
      return NS_OK;
312
0
  } while ((content = content->GetParent()));
313
0
  return NS_ERROR_FAILURE;
314
0
}
315
316
nsAtom*
317
nsIContent::GetLang() const
318
0
{
319
0
  for (const auto* content = this; content; content = content->GetParent()) {
320
0
    if (!content->IsElement()) {
321
0
      continue;
322
0
    }
323
0
324
0
    auto* element = content->AsElement();
325
0
    if (!element->GetAttrCount()) {
326
0
      continue;
327
0
    }
328
0
329
0
    // xml:lang has precedence over lang on HTML elements (see
330
0
    // XHTML1 section C.7).
331
0
    const nsAttrValue* attr =
332
0
      element->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
333
0
    if (!attr && element->SupportsLangAttr()) {
334
0
      attr = element->GetParsedAttr(nsGkAtoms::lang);
335
0
    }
336
0
    if (attr) {
337
0
      MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
338
0
      MOZ_ASSERT(attr->GetAtomValue());
339
0
      return attr->GetAtomValue();
340
0
    }
341
0
  }
342
0
343
0
  return nullptr;
344
0
}
345
346
already_AddRefed<nsIURI>
347
nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const
348
0
{
349
0
  if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
350
0
    // XXX Ignore xml:base as we are removing it.
351
0
    if (URLExtraData* data = use->GetContentURLData()) {
352
0
      return do_AddRef(data->BaseURI());
353
0
    }
354
0
  }
355
0
356
0
  nsIDocument* doc = OwnerDoc();
357
0
  // Start with document base
358
0
  nsCOMPtr<nsIURI> base = doc->GetBaseURI(aTryUseXHRDocBaseURI);
359
0
360
0
  // Collect array of xml:base attribute values up the parent chain. This
361
0
  // is slightly slower for the case when there are xml:base attributes, but
362
0
  // faster for the far more common case of there not being any such
363
0
  // attributes.
364
0
  // Also check for SVG elements which require special handling
365
0
  AutoTArray<nsString, 5> baseAttrs;
366
0
  nsString attr;
367
0
  const nsIContent *elem = this;
368
0
  do {
369
0
    // First check for SVG specialness (why is this SVG specific?)
370
0
    if (elem->IsSVGElement()) {
371
0
      nsIContent* bindingParent = elem->GetBindingParent();
372
0
      if (bindingParent) {
373
0
        nsXBLBinding* binding = bindingParent->GetXBLBinding();
374
0
        if (binding) {
375
0
          // XXX sXBL/XBL2 issue
376
0
          // If this is an anonymous XBL element use the binding
377
0
          // document for the base URI.
378
0
          // XXX Will fail with xml:base
379
0
          base = binding->PrototypeBinding()->DocURI();
380
0
          break;
381
0
        }
382
0
      }
383
0
    }
384
0
385
0
    // Otherwise check for xml:base attribute
386
0
    if (elem->IsElement()) {
387
0
      elem->AsElement()->GetAttr(kNameSpaceID_XML, nsGkAtoms::base, attr);
388
0
      if (!attr.IsEmpty()) {
389
0
        baseAttrs.AppendElement(attr);
390
0
      }
391
0
    }
392
0
    elem = elem->GetParent();
393
0
  } while(elem);
394
0
395
0
  if (!baseAttrs.IsEmpty()) {
396
0
    doc->WarnOnceAbout(nsIDocument::eXMLBaseAttribute);
397
0
    // Now resolve against all xml:base attrs
398
0
    for (uint32_t i = baseAttrs.Length() - 1; i != uint32_t(-1); --i) {
399
0
      nsCOMPtr<nsIURI> newBase;
400
0
      nsresult rv = NS_NewURI(getter_AddRefs(newBase), baseAttrs[i],
401
0
                              doc->GetDocumentCharacterSet(), base);
402
0
      // Do a security check, almost the same as nsDocument::SetBaseURL()
403
0
      // Only need to do this on the final uri
404
0
      if (NS_SUCCEEDED(rv) && i == 0) {
405
0
        rv = nsContentUtils::GetSecurityManager()->
406
0
          CheckLoadURIWithPrincipal(NodePrincipal(), newBase,
407
0
                                    nsIScriptSecurityManager::STANDARD);
408
0
      }
409
0
      if (NS_SUCCEEDED(rv)) {
410
0
        base.swap(newBase);
411
0
      }
412
0
    }
413
0
  }
414
0
415
0
  return base.forget();
416
0
}
417
418
nsIURI*
419
nsIContent::GetBaseURIForStyleAttr() const
420
0
{
421
0
  if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
422
0
    if (URLExtraData* data = use->GetContentURLData()) {
423
0
      return data->BaseURI();
424
0
    }
425
0
  }
426
0
  // This also ignores the case that SVG inside XBL binding.
427
0
  // But it is probably fine.
428
0
  return OwnerDoc()->GetDocBaseURI();
429
0
}
430
431
already_AddRefed<URLExtraData>
432
nsIContent::GetURLDataForStyleAttr(nsIPrincipal* aSubjectPrincipal) const
433
0
{
434
0
  if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
435
0
    if (URLExtraData* data = use->GetContentURLData()) {
436
0
      return do_AddRef(data);
437
0
    }
438
0
  }
439
0
  if (aSubjectPrincipal && aSubjectPrincipal != NodePrincipal()) {
440
0
    // TODO: Cache this?
441
0
    return MakeAndAddRef<URLExtraData>(OwnerDoc()->GetDocBaseURI(),
442
0
                                       OwnerDoc()->GetDocumentURI(),
443
0
                                       aSubjectPrincipal,
444
0
                                       OwnerDoc()->GetReferrerPolicy());
445
0
  }
446
0
  // This also ignores the case that SVG inside XBL binding.
447
0
  // But it is probably fine.
448
0
  return do_AddRef(OwnerDoc()->DefaultStyleAttrURLData());
449
0
}
450
451
void
452
nsIContent::ConstructUbiNode(void* storage)
453
0
{
454
0
  JS::ubi::Concrete<nsIContent>::construct(storage, this);
455
0
}
456
457
//----------------------------------------------------------------------
458
459
static inline JSObject*
460
GetJSObjectChild(nsWrapperCache* aCache)
461
0
{
462
0
  return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor() : nullptr;
463
0
}
464
465
static bool
466
NeedsScriptTraverse(nsINode* aNode)
467
0
{
468
0
  return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() &&
469
0
         !aNode->HasKnownLiveWrapperAndDoesNotNeedTracing(aNode);
470
0
}
471
472
//----------------------------------------------------------------------
473
474
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAttrChildContentList)
475
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAttrChildContentList)
476
477
// If nsAttrChildContentList is changed so that any additional fields are
478
// traversed by the cycle collector, then CAN_SKIP must be updated to
479
// check that the additional fields are null.
480
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsAttrChildContentList)
481
482
// nsAttrChildContentList only ever has a single child, its wrapper, so if
483
// the wrapper is known-live, the list can't be part of a garbage cycle.
484
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsAttrChildContentList)
485
0
  return tmp->HasKnownLiveWrapper();
486
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
487
488
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsAttrChildContentList)
489
0
  return tmp->HasKnownLiveWrapperAndDoesNotNeedTracing(tmp);
490
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
491
492
// CanSkipThis returns false to avoid problems with incomplete unlinking.
493
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsAttrChildContentList)
494
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
495
496
0
NS_INTERFACE_TABLE_HEAD(nsAttrChildContentList)
497
0
  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
498
0
  NS_INTERFACE_TABLE(nsAttrChildContentList, nsINodeList)
499
0
  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAttrChildContentList)
500
0
NS_INTERFACE_MAP_END
501
502
JSObject*
503
nsAttrChildContentList::WrapObject(JSContext *cx,
504
                                   JS::Handle<JSObject*> aGivenProto)
505
0
{
506
0
  return NodeList_Binding::Wrap(cx, this, aGivenProto);
507
0
}
508
509
uint32_t
510
nsAttrChildContentList::Length()
511
0
{
512
0
  return mNode ? mNode->GetChildCount() : 0;
513
0
}
514
515
nsIContent*
516
nsAttrChildContentList::Item(uint32_t aIndex)
517
0
{
518
0
  if (mNode) {
519
0
    return mNode->GetChildAt_Deprecated(aIndex);
520
0
  }
521
0
522
0
  return nullptr;
523
0
}
524
525
int32_t
526
nsAttrChildContentList::IndexOf(nsIContent* aContent)
527
0
{
528
0
  if (mNode) {
529
0
    return mNode->ComputeIndexOf(aContent);
530
0
  }
531
0
532
0
  return -1;
533
0
}
534
535
//----------------------------------------------------------------------
536
uint32_t
537
nsParentNodeChildContentList::Length()
538
0
{
539
0
  if (!mIsCacheValid && !ValidateCache()) {
540
0
    return 0;
541
0
  }
542
0
543
0
  MOZ_ASSERT(mIsCacheValid);
544
0
545
0
  return mCachedChildArray.Length();
546
0
}
547
548
nsIContent*
549
nsParentNodeChildContentList::Item(uint32_t aIndex)
550
0
{
551
0
  if (!mIsCacheValid && !ValidateCache()) {
552
0
    return nullptr;
553
0
  }
554
0
555
0
  MOZ_ASSERT(mIsCacheValid);
556
0
557
0
  return mCachedChildArray.SafeElementAt(aIndex, nullptr);
558
0
}
559
560
int32_t
561
nsParentNodeChildContentList::IndexOf(nsIContent* aContent)
562
0
{
563
0
  if (!mIsCacheValid && !ValidateCache()) {
564
0
    return -1;
565
0
  }
566
0
567
0
  MOZ_ASSERT(mIsCacheValid);
568
0
569
0
  return mCachedChildArray.IndexOf(aContent);
570
0
}
571
572
bool
573
nsParentNodeChildContentList::ValidateCache()
574
0
{
575
0
  MOZ_ASSERT(!mIsCacheValid);
576
0
  MOZ_ASSERT(mCachedChildArray.IsEmpty());
577
0
578
0
  nsINode* parent = GetParentObject();
579
0
  if (!parent) {
580
0
    return false;
581
0
  }
582
0
583
0
  for (nsIContent* node = parent->GetFirstChild(); node;
584
0
       node = node->GetNextSibling()) {
585
0
    mCachedChildArray.AppendElement(node);
586
0
  }
587
0
  mIsCacheValid = true;
588
0
589
0
  return true;
590
0
}
591
592
//----------------------------------------------------------------------
593
594
nsIHTMLCollection*
595
FragmentOrElement::Children()
596
0
{
597
0
  nsDOMSlots* slots = DOMSlots();
598
0
599
0
  if (!slots->mChildrenList) {
600
0
    slots->mChildrenList = new nsContentList(this, kNameSpaceID_Wildcard,
601
0
                                             nsGkAtoms::_asterisk, nsGkAtoms::_asterisk,
602
0
                                             false);
603
0
  }
604
0
605
0
  return slots->mChildrenList;
606
0
}
607
608
609
//----------------------------------------------------------------------
610
611
NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode)
612
613
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff)
614
0
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
615
0
NS_INTERFACE_MAP_END_AGGREGATED(mNode)
616
617
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff)
618
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff)
619
620
NS_IMETHODIMP
621
nsNodeSupportsWeakRefTearoff::GetWeakReference(nsIWeakReference** aInstancePtr)
622
0
{
623
0
  nsINode::nsSlots* slots = mNode->Slots();
624
0
  if (!slots->mWeakReference) {
625
0
    slots->mWeakReference = new nsNodeWeakReference(mNode);
626
0
  }
627
0
628
0
  NS_ADDREF(*aInstancePtr = slots->mWeakReference);
629
0
630
0
  return NS_OK;
631
0
}
632
633
//----------------------------------------------------------------------
634
635
static const size_t MaxDOMSlotSizeAllowed =
636
#ifdef HAVE_64BIT_BUILD
637
128;
638
#else
639
64;
640
#endif
641
642
static_assert(sizeof(nsINode::nsSlots) <= MaxDOMSlotSizeAllowed,
643
              "DOM slots cannot be grown without consideration");
644
static_assert(sizeof(FragmentOrElement::nsDOMSlots) <= MaxDOMSlotSizeAllowed,
645
              "DOM slots cannot be grown without consideration");
646
647
void
648
nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots()
649
0
{
650
0
  mBindingParent = nullptr;
651
0
  mXBLInsertionPoint = nullptr;
652
0
  mContainingShadow = nullptr;
653
0
  mAssignedSlot = nullptr;
654
0
}
655
656
void
657
nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(nsCycleCollectionTraversalCallback& aCb)
658
0
{
659
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mBindingParent");
660
0
  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mBindingParent));
661
0
662
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mContainingShadow");
663
0
  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
664
0
665
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mAssignedSlot");
666
0
  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mAssignedSlot.get()));
667
0
668
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mXBLInsertionPoint");
669
0
  aCb.NoteXPCOMChild(mXBLInsertionPoint.get());
670
0
}
671
672
nsIContent::nsExtendedContentSlots::nsExtendedContentSlots()
673
0
{
674
0
}
675
676
0
nsIContent::nsExtendedContentSlots::~nsExtendedContentSlots() = default;
677
678
size_t
679
nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
680
0
{
681
0
  // For now, nothing to measure here.  We don't actually own any of our
682
0
  // members.
683
0
  return 0;
684
0
}
685
686
FragmentOrElement::nsDOMSlots::nsDOMSlots()
687
  : nsIContent::nsContentSlots(),
688
    mDataset(nullptr)
689
0
{
690
0
  MOZ_COUNT_CTOR(nsDOMSlots);
691
0
}
692
693
FragmentOrElement::nsDOMSlots::~nsDOMSlots()
694
0
{
695
0
  MOZ_COUNT_DTOR(nsDOMSlots);
696
0
697
0
  if (mAttributeMap) {
698
0
    mAttributeMap->DropReference();
699
0
  }
700
0
}
701
702
void
703
FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback& aCb)
704
0
{
705
0
  nsIContent::nsContentSlots::Traverse(aCb);
706
0
707
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mStyle");
708
0
  aCb.NoteXPCOMChild(mStyle.get());
709
0
710
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mAttributeMap");
711
0
  aCb.NoteXPCOMChild(mAttributeMap.get());
712
0
713
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mChildrenList");
714
0
  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mChildrenList));
715
0
716
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mClassList");
717
0
  aCb.NoteXPCOMChild(mClassList.get());
718
0
}
719
720
void
721
FragmentOrElement::nsDOMSlots::Unlink()
722
0
{
723
0
  nsIContent::nsContentSlots::Unlink();
724
0
  mStyle = nullptr;
725
0
  if (mAttributeMap) {
726
0
    mAttributeMap->DropReference();
727
0
    mAttributeMap = nullptr;
728
0
  }
729
0
  mChildrenList = nullptr;
730
0
  mClassList = nullptr;
731
0
}
732
733
size_t
734
FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
735
0
{
736
0
  size_t n = aMallocSizeOf(this);
737
0
738
0
  nsExtendedContentSlots* extendedSlots = GetExtendedContentSlots();
739
0
  if (extendedSlots) {
740
0
    if (OwnsExtendedSlots()) {
741
0
      n += aMallocSizeOf(extendedSlots);
742
0
    }
743
0
744
0
    n += extendedSlots->SizeOfExcludingThis(aMallocSizeOf);
745
0
  }
746
0
747
0
  if (mAttributeMap) {
748
0
    n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
749
0
  }
750
0
751
0
  if (mChildrenList) {
752
0
    n += mChildrenList->SizeOfIncludingThis(aMallocSizeOf);
753
0
  }
754
0
755
0
  // Measurement of the following members may be added later if DMD finds it is
756
0
  // worthwhile:
757
0
  // - Superclass members (nsINode::nsSlots)
758
0
  // - mStyle
759
0
  // - mDataSet
760
0
  // - mClassList
761
0
762
0
  // The following member are not measured:
763
0
  // - mControllers: because it is non-owning
764
0
  // - mBindingParent: because it is some ancestor element.
765
0
  return n;
766
0
}
767
768
0
FragmentOrElement::nsExtendedDOMSlots::nsExtendedDOMSlots() = default;
769
770
FragmentOrElement::nsExtendedDOMSlots::~nsExtendedDOMSlots()
771
0
{
772
0
}
773
774
void
775
FragmentOrElement::nsExtendedDOMSlots::UnlinkExtendedSlots()
776
0
{
777
0
  nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots();
778
0
779
0
  // Don't clear mXBLBinding, it'll be done in
780
0
  // BindingManager::RemovedFromDocument from FragmentOrElement::Unlink.
781
0
  //
782
0
  // mShadowRoot will similarly be cleared explicitly from
783
0
  // FragmentOrElement::Unlink.
784
0
  mSMILOverrideStyle = nullptr;
785
0
  mControllers = nullptr;
786
0
  mLabelsList = nullptr;
787
0
  if (mCustomElementData) {
788
0
    mCustomElementData->Unlink();
789
0
    mCustomElementData = nullptr;
790
0
  }
791
0
}
792
793
void
794
FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots(nsCycleCollectionTraversalCallback& aCb)
795
0
{
796
0
  nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(aCb);
797
0
798
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mSMILOverrideStyle");
799
0
  aCb.NoteXPCOMChild(mSMILOverrideStyle.get());
800
0
801
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mControllers");
802
0
  aCb.NoteXPCOMChild(mControllers);
803
0
804
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mLabelsList");
805
0
  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mLabelsList));
806
0
807
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mShadowRoot");
808
0
  aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
809
0
810
0
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mXBLBinding");
811
0
  aCb.NoteNativeChild(mXBLBinding,
812
0
                     NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
813
0
814
0
  if (mCustomElementData) {
815
0
    mCustomElementData->Traverse(aCb);
816
0
  }
817
0
}
818
819
size_t
820
FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
821
0
{
822
0
  size_t n = nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(aMallocSizeOf);
823
0
824
0
  // We own mSMILOverrideStyle but there seems to be no memory reporting on CSS
825
0
  // declarations?  At least report the memory the declaration takes up
826
0
  // directly.
827
0
  if (mSMILOverrideStyle) {
828
0
    n += aMallocSizeOf(mSMILOverrideStyle);
829
0
  }
830
0
831
0
  // We don't really own mSMILOverrideStyleDeclaration.  mSMILOverrideStyle owns
832
0
  // it.
833
0
834
0
  // We don't seem to have memory reporting for nsXULControllers.  At least
835
0
  // report the memory it's using directly.
836
0
  if (mControllers) {
837
0
    n += aMallocSizeOf(mControllers);
838
0
  }
839
0
840
0
  if (mLabelsList) {
841
0
    n += mLabelsList->SizeOfIncludingThis(aMallocSizeOf);
842
0
  }
843
0
844
0
  // mShadowRoot should be handled during normal DOM tree memory reporting, just
845
0
  // like kids, siblings, etc.
846
0
847
0
  // We don't seem to have memory reporting for nsXBLBinding.  At least
848
0
  // report the memory it's using directly.
849
0
  if (mXBLBinding) {
850
0
    n += aMallocSizeOf(mXBLBinding);
851
0
  }
852
0
853
0
  if (mCustomElementData) {
854
0
    n += mCustomElementData->SizeOfIncludingThis(aMallocSizeOf);
855
0
  }
856
0
857
0
  return n;
858
0
}
859
860
FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
861
  : nsIContent(std::move(aNodeInfo))
862
0
{
863
0
}
864
865
FragmentOrElement::~FragmentOrElement()
866
0
{
867
0
  MOZ_ASSERT(!IsInUncomposedDoc(),
868
0
             "Please remove this from the document properly");
869
0
  if (GetParent()) {
870
0
    NS_RELEASE(mParent);
871
0
  }
872
0
}
873
874
already_AddRefed<nsINodeList>
875
FragmentOrElement::GetChildren(uint32_t aFilter)
876
0
{
877
0
  RefPtr<nsSimpleContentList> list = new nsSimpleContentList(this);
878
0
  AllChildrenIterator iter(this, aFilter);
879
0
  while (nsIContent* kid = iter.GetNextChild()) {
880
0
    list->AppendElement(kid);
881
0
  }
882
0
883
0
  return list.forget();
884
0
}
885
886
static nsIContent*
887
FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
888
0
{
889
0
  if (aContent->ChromeOnlyAccess()) {
890
0
    bool chromeAccessOnly = false;
891
0
    while (aContent && !chromeAccessOnly) {
892
0
      chromeAccessOnly = aContent->IsRootOfChromeAccessOnlySubtree();
893
0
      aContent = aContent->GetParent();
894
0
    }
895
0
  }
896
0
  return aContent;
897
0
}
898
899
already_AddRefed<nsINode>
900
FindChromeAccessOnlySubtreeOwner(EventTarget* aTarget)
901
0
{
902
0
  nsCOMPtr<nsINode> node = do_QueryInterface(aTarget);
903
0
  if (!node || !node->ChromeOnlyAccess()) {
904
0
    return node.forget();
905
0
  }
906
0
907
0
  if (!node->IsContent()) {
908
0
    return nullptr;
909
0
  }
910
0
911
0
  node = FindChromeAccessOnlySubtreeOwner(node->AsContent());
912
0
  return node.forget();
913
0
}
914
915
void
916
nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor)
917
0
{
918
0
  //FIXME! Document how this event retargeting works, Bug 329124.
919
0
  aVisitor.mCanHandle = true;
920
0
  aVisitor.mMayHaveListenerManager = HasListenerManager();
921
0
922
0
  if (IsInShadowTree()) {
923
0
    aVisitor.mItemInShadowTree = true;
924
0
  }
925
0
926
0
  // Don't propagate mouseover and mouseout events when mouse is moving
927
0
  // inside chrome access only content.
928
0
  bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
929
0
  aVisitor.mRootOfClosedTree = isAnonForEvents;
930
0
  if ((aVisitor.mEvent->mMessage == eMouseOver ||
931
0
       aVisitor.mEvent->mMessage == eMouseOut ||
932
0
       aVisitor.mEvent->mMessage == ePointerOver ||
933
0
       aVisitor.mEvent->mMessage == ePointerOut) &&
934
0
      // Check if we should stop event propagation when event has just been
935
0
      // dispatched or when we're about to propagate from
936
0
      // chrome access only subtree or if we are about to propagate out of
937
0
      // a shadow root to a shadow root host.
938
0
      ((this == aVisitor.mEvent->mOriginalTarget &&
939
0
        !ChromeOnlyAccess()) || isAnonForEvents)) {
940
0
     nsCOMPtr<nsIContent> relatedTarget =
941
0
       do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->mRelatedTarget);
942
0
    if (relatedTarget &&
943
0
        relatedTarget->OwnerDoc() == OwnerDoc()) {
944
0
945
0
      // If current target is anonymous for events or we know that related
946
0
      // target is descendant of an element which is anonymous for events,
947
0
      // we may want to stop event propagation.
948
0
      // If this is the original target, aVisitor.mRelatedTargetIsInAnon
949
0
      // must be updated.
950
0
      if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
951
0
          (aVisitor.mEvent->mOriginalTarget == this &&
952
0
           (aVisitor.mRelatedTargetIsInAnon =
953
0
            relatedTarget->ChromeOnlyAccess()))) {
954
0
        nsIContent* anonOwner = FindChromeAccessOnlySubtreeOwner(this);
955
0
        if (anonOwner) {
956
0
          nsIContent* anonOwnerRelated =
957
0
            FindChromeAccessOnlySubtreeOwner(relatedTarget);
958
0
          if (anonOwnerRelated) {
959
0
            // Note, anonOwnerRelated may still be inside some other
960
0
            // native anonymous subtree. The case where anonOwner is still
961
0
            // inside native anonymous subtree will be handled when event
962
0
            // propagates up in the DOM tree.
963
0
            while (anonOwner != anonOwnerRelated &&
964
0
                   anonOwnerRelated->ChromeOnlyAccess()) {
965
0
              anonOwnerRelated = FindChromeAccessOnlySubtreeOwner(anonOwnerRelated);
966
0
            }
967
0
            if (anonOwner == anonOwnerRelated) {
968
#ifdef DEBUG_smaug
969
              nsCOMPtr<nsIContent> originalTarget =
970
                do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
971
              nsAutoString ot, ct, rt;
972
              if (originalTarget) {
973
                originalTarget->NodeInfo()->NameAtom()->ToString(ot);
974
              }
975
              NodeInfo()->NameAtom()->ToString(ct);
976
              relatedTarget->NodeInfo()->NameAtom()->ToString(rt);
977
              printf("Stopping %s propagation:"
978
                     "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
979
                     "\n\trelatedTarget=%s %s \n%s",
980
                     (aVisitor.mEvent->mMessage == eMouseOver)
981
                       ? "mouseover" : "mouseout",
982
                     NS_ConvertUTF16toUTF8(ot).get(),
983
                     NS_ConvertUTF16toUTF8(ct).get(),
984
                     isAnonForEvents
985
                       ? "(is native anonymous)"
986
                       : (ChromeOnlyAccess()
987
                           ? "(is in native anonymous subtree)" : ""),
988
                     NS_ConvertUTF16toUTF8(rt).get(),
989
                     relatedTarget->ChromeOnlyAccess()
990
                       ? "(is in native anonymous subtree)" : "",
991
                     (originalTarget &&
992
                      relatedTarget->FindFirstNonChromeOnlyAccessContent() ==
993
                        originalTarget->FindFirstNonChromeOnlyAccessContent())
994
                       ? "" : "Wrong event propagation!?!\n");
995
#endif
996
              aVisitor.SetParentTarget(nullptr, false);
997
0
              // Event should not propagate to non-anon content.
998
0
              aVisitor.mCanHandle = isAnonForEvents;
999
0
              return;
1000
0
            }
1001
0
          }
1002
0
        }
1003
0
      }
1004
0
    }
1005
0
  }
1006
0
1007
0
  // Event parent is the assigned slot, if node is assigned, or node's parent
1008
0
  // otherwise.
1009
0
  HTMLSlotElement* slot = GetAssignedSlot();
1010
0
  nsIContent* parent = slot ? slot : GetParent();
1011
0
1012
0
  // Event may need to be retargeted if this is the root of a native
1013
0
  // anonymous content subtree or event is dispatched somewhere inside XBL.
1014
0
  if (isAnonForEvents) {
1015
#ifdef DEBUG
1016
    // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
1017
    // all the events are allowed even in the native anonymous content..
1018
    nsCOMPtr<nsIContent> t =
1019
      do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
1020
    NS_ASSERTION(!t || !t->ChromeOnlyAccess() ||
1021
                 aVisitor.mEvent->mClass != eMutationEventClass ||
1022
                 aVisitor.mDOMEvent,
1023
                 "Mutation event dispatched in native anonymous content!?!");
1024
#endif
1025
    aVisitor.mEventTargetAtParent = parent;
1026
0
  } else if (parent && aVisitor.mOriginalTargetIsInAnon) {
1027
0
    nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
1028
0
    if (content && content->GetBindingParent() == parent) {
1029
0
      aVisitor.mEventTargetAtParent = parent;
1030
0
    }
1031
0
  }
1032
0
1033
0
  // check for an anonymous parent
1034
0
  // XXX XBL2/sXBL issue
1035
0
  if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
1036
0
    nsIContent* insertionParent = GetXBLInsertionParent();
1037
0
    NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent &&
1038
0
                   aVisitor.mEventTargetAtParent != insertionParent),
1039
0
                 "Retargeting and having insertion parent!");
1040
0
    if (insertionParent) {
1041
0
      parent = insertionParent;
1042
0
    }
1043
0
  }
1044
0
1045
0
  if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent &&
1046
0
      IsRootOfNativeAnonymousSubtree() && OwnerDoc()->GetWindow()) {
1047
0
    aVisitor.SetParentTarget(OwnerDoc()->GetWindow()->GetParentTarget(), true);
1048
0
  } else if (parent) {
1049
0
    aVisitor.SetParentTarget(parent, false);
1050
0
    if (slot) {
1051
0
      ShadowRoot* root = slot->GetContainingShadow();
1052
0
      if (root && root->IsClosed()) {
1053
0
        aVisitor.mParentIsSlotInClosedTree = true;
1054
0
      }
1055
0
    }
1056
0
  } else {
1057
0
    aVisitor.SetParentTarget(GetComposedDoc(), false);
1058
0
  }
1059
0
1060
0
  if (!ChromeOnlyAccess() && !aVisitor.mRelatedTargetRetargetedInCurrentScope) {
1061
0
    // We don't support Shadow DOM in native anonymous content yet.
1062
0
    aVisitor.mRelatedTargetRetargetedInCurrentScope = true;
1063
0
    if (aVisitor.mEvent->mOriginalRelatedTarget) {
1064
0
      // https://dom.spec.whatwg.org/#concept-event-dispatch
1065
0
      // Step 3.
1066
0
      // "Let relatedTarget be the result of retargeting event's relatedTarget
1067
0
      //  against target if event's relatedTarget is non-null, and null
1068
0
      //  otherwise."
1069
0
      //
1070
0
      // This is a bit complicated because the event might be from native
1071
0
      // anonymous content, but we need to deal with non-native anonymous
1072
0
      // content there.
1073
0
      bool initialTarget = this == aVisitor.mEvent->mOriginalTarget;
1074
0
      nsCOMPtr<nsINode> originalTargetAsNode;
1075
0
      // Use of mOriginalTargetIsInAnon is an optimization here.
1076
0
      if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) {
1077
0
        originalTargetAsNode =
1078
0
          FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalTarget);
1079
0
        initialTarget = originalTargetAsNode == this;
1080
0
      }
1081
0
      if (initialTarget) {
1082
0
        nsCOMPtr<nsINode> relatedTargetAsNode =
1083
0
          FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
1084
0
        if (!originalTargetAsNode) {
1085
0
          originalTargetAsNode =
1086
0
            do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
1087
0
        }
1088
0
1089
0
        if (relatedTargetAsNode && originalTargetAsNode) {
1090
0
          nsINode* retargetedRelatedTarget =
1091
0
            nsContentUtils::Retarget(relatedTargetAsNode, originalTargetAsNode);
1092
0
          if (originalTargetAsNode == retargetedRelatedTarget &&
1093
0
              retargetedRelatedTarget != relatedTargetAsNode) {
1094
0
            // Step 4.
1095
0
            // "If target is relatedTarget and target is not event's
1096
0
            //  relatedTarget, then return true."
1097
0
            aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
1098
0
            // Old code relies on mTarget to point to the first element which
1099
0
            // was not added to the event target chain because of mCanHandle
1100
0
            // being false, but in Shadow DOM case mTarget really should
1101
0
            // point to a node in Shadow DOM.
1102
0
            aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
1103
0
            return;
1104
0
          }
1105
0
1106
0
          // Part of step 5. Retargeting target has happened already higher
1107
0
          // up in this method.
1108
0
          // "Append to an event path with event, target, targetOverride,
1109
0
          //  relatedTarget, and false."
1110
0
          aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
1111
0
        }
1112
0
      } else {
1113
0
        nsCOMPtr<nsINode> relatedTargetAsNode =
1114
0
          FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalRelatedTarget);
1115
0
        if (relatedTargetAsNode) {
1116
0
          // Step 11.3.
1117
0
          // "Let relatedTarget be the result of retargeting event's
1118
0
          // relatedTarget against parent if event's relatedTarget is non-null,
1119
0
          // and null otherwise.".
1120
0
          nsINode* retargetedRelatedTarget =
1121
0
            nsContentUtils::Retarget(relatedTargetAsNode, this);
1122
0
          nsCOMPtr<nsINode> targetInKnownToBeHandledScope =
1123
0
            FindChromeAccessOnlySubtreeOwner(aVisitor.mTargetInKnownToBeHandledScope);
1124
0
          // If aVisitor.mTargetInKnownToBeHandledScope wasn't nsINode,
1125
0
          // targetInKnownToBeHandledScope will be null. This may happen when
1126
0
          // dispatching event to Window object in a content page and
1127
0
          // propagating the event to a chrome Element.
1128
0
          if (targetInKnownToBeHandledScope &&
1129
0
              nsContentUtils::ContentIsShadowIncludingDescendantOf(
1130
0
                this, targetInKnownToBeHandledScope->SubtreeRoot())) {
1131
0
            // Part of step 11.4.
1132
0
            // "If target's root is a shadow-including inclusive ancestor of
1133
0
            //  parent, then"
1134
0
            // "...Append to an event path with event, parent, null, relatedTarget,
1135
0
            // "   and slot-in-closed-tree."
1136
0
            aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
1137
0
          } else if (this == retargetedRelatedTarget) {
1138
0
            // Step 11.5
1139
0
            // "Otherwise, if parent and relatedTarget are identical, then set
1140
0
            //  parent to null."
1141
0
            aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
1142
0
            // Old code relies on mTarget to point to the first element which
1143
0
            // was not added to the event target chain because of mCanHandle
1144
0
            // being false, but in Shadow DOM case mTarget really should
1145
0
            // point to a node in Shadow DOM.
1146
0
            aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
1147
0
            return;
1148
0
          } else if (targetInKnownToBeHandledScope) {
1149
0
            // Note, if targetInKnownToBeHandledScope is null,
1150
0
            // mTargetInKnownToBeHandledScope could be Window object in content
1151
0
            // page and we're in chrome document in the same process.
1152
0
1153
0
            // Step 11.6
1154
0
            aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
1155
0
          }
1156
0
        }
1157
0
      }
1158
0
    }
1159
0
1160
0
    if (aVisitor.mEvent->mClass == eTouchEventClass) {
1161
0
      // Retarget touch objects.
1162
0
      MOZ_ASSERT(!aVisitor.mRetargetedTouchTargets.isSome());
1163
0
      aVisitor.mRetargetedTouchTargets.emplace();
1164
0
      WidgetTouchEvent* touchEvent = aVisitor.mEvent->AsTouchEvent();
1165
0
      WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
1166
0
      for (uint32_t i = 0; i < touches.Length(); ++i) {
1167
0
        Touch* touch = touches[i];
1168
0
        EventTarget* originalTarget = touch->mOriginalTarget;
1169
0
        EventTarget* touchTarget = originalTarget;
1170
0
        nsCOMPtr<nsINode> targetAsNode = do_QueryInterface(originalTarget);
1171
0
        if (targetAsNode) {
1172
0
          EventTarget* retargeted = nsContentUtils::Retarget(targetAsNode, this);
1173
0
          if (retargeted) {
1174
0
            touchTarget = retargeted;
1175
0
          }
1176
0
        }
1177
0
        aVisitor.mRetargetedTouchTargets->AppendElement(touchTarget);
1178
0
        touch->mTarget = touchTarget;
1179
0
      }
1180
0
      MOZ_ASSERT(aVisitor.mRetargetedTouchTargets->Length() ==
1181
0
                   touches.Length());
1182
0
    }
1183
0
  }
1184
0
1185
0
  if (slot) {
1186
0
    // Inform that we're about to exit the current scope.
1187
0
    aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
1188
0
  }
1189
0
}
1190
1191
bool
1192
nsIContent::IsFocusable(int32_t* aTabIndex, bool aWithMouse)
1193
0
{
1194
0
  bool focusable = IsFocusableInternal(aTabIndex, aWithMouse);
1195
0
  // Ensure that the return value and aTabIndex are consistent in the case
1196
0
  // we're in userfocusignored context.
1197
0
  if (focusable || (aTabIndex && *aTabIndex != -1)) {
1198
0
    if (nsContentUtils::IsUserFocusIgnored(this)) {
1199
0
      if (aTabIndex) {
1200
0
        *aTabIndex = -1;
1201
0
      }
1202
0
      return false;
1203
0
    }
1204
0
    return focusable;
1205
0
  }
1206
0
  return false;
1207
0
}
1208
1209
bool
1210
nsIContent::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse)
1211
0
{
1212
0
  if (aTabIndex) {
1213
0
    *aTabIndex = -1; // Default, not tabbable
1214
0
  }
1215
0
  return false;
1216
0
}
1217
1218
bool
1219
FragmentOrElement::IsLink(nsIURI** aURI) const
1220
0
{
1221
0
  *aURI = nullptr;
1222
0
  return false;
1223
0
}
1224
1225
nsXBLBinding*
1226
FragmentOrElement::DoGetXBLBinding() const
1227
0
{
1228
0
  MOZ_ASSERT(HasFlag(NODE_MAY_BE_IN_BINDING_MNGR));
1229
0
  const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
1230
0
  return slots ? slots->mXBLBinding.get() : nullptr;
1231
0
}
1232
1233
nsIContent*
1234
nsIContent::GetContainingShadowHost() const
1235
0
{
1236
0
  if (mozilla::dom::ShadowRoot* shadow = GetContainingShadow()) {
1237
0
    return shadow->GetHost();
1238
0
  }
1239
0
  return nullptr;
1240
0
}
1241
1242
void
1243
nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot)
1244
0
{
1245
0
  MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots());
1246
0
  ExtendedContentSlots()->mAssignedSlot = aSlot;
1247
0
}
1248
1249
void
1250
nsIContent::SetXBLInsertionPoint(nsIContent* aContent)
1251
0
{
1252
0
  if (aContent) {
1253
0
    nsExtendedContentSlots* slots = ExtendedContentSlots();
1254
0
    SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
1255
0
    slots->mXBLInsertionPoint = aContent;
1256
0
  } else {
1257
0
    if (nsExtendedContentSlots* slots = GetExistingExtendedContentSlots()) {
1258
0
      slots->mXBLInsertionPoint = nullptr;
1259
0
    }
1260
0
  }
1261
0
}
1262
1263
void
1264
FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
1265
                                          OOMReporter& aError)
1266
0
{
1267
0
  if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) {
1268
0
    aError.ReportOOM();
1269
0
  }
1270
0
}
1271
1272
void
1273
FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent,
1274
                                          nsIPrincipal* aSubjectPrincipal,
1275
                                          ErrorResult& aError)
1276
0
{
1277
0
  aError = nsContentUtils::SetNodeTextContent(this, aTextContent, false);
1278
0
}
1279
1280
void
1281
FragmentOrElement::DestroyContent()
1282
0
{
1283
0
  // Drop any servo data. We do this before the RemovedFromDocument call below
1284
0
  // so that it doesn't need to try to keep the style state sane when shuffling
1285
0
  // around the flattened tree.
1286
0
  //
1287
0
  // TODO(emilio): I suspect this can be asserted against instead, with a bit of
1288
0
  // effort to avoid calling nsDocument::Destroy with a shell...
1289
0
  if (IsElement()) {
1290
0
    AsElement()->ClearServoData();
1291
0
  }
1292
0
1293
0
  nsIDocument* document = OwnerDoc();
1294
0
1295
0
  document->BindingManager()->RemovedFromDocument(this, document,
1296
0
                                                  nsBindingManager::eRunDtor);
1297
0
  document->ClearBoxObjectFor(this);
1298
0
1299
#ifdef DEBUG
1300
  uint32_t oldChildCount = GetChildCount();
1301
#endif
1302
1303
0
  for (nsIContent* child = GetFirstChild();
1304
0
       child;
1305
0
       child = child->GetNextSibling()) {
1306
0
    child->DestroyContent();
1307
0
    MOZ_ASSERT(child->GetParent() == this,
1308
0
               "Mutating the tree during XBL destructors is evil");
1309
0
  }
1310
0
1311
0
  MOZ_ASSERT(oldChildCount == GetChildCount(),
1312
0
             "Mutating the tree during XBL destructors is evil");
1313
0
1314
0
  if (ShadowRoot* shadowRoot = GetShadowRoot()) {
1315
0
    shadowRoot->DestroyContent();
1316
0
  }
1317
0
}
1318
1319
void
1320
FragmentOrElement::SaveSubtreeState()
1321
0
{
1322
0
  for (nsIContent* child = GetFirstChild();
1323
0
       child;
1324
0
       child = child->GetNextSibling()) {
1325
0
    child->SaveSubtreeState();
1326
0
  }
1327
0
1328
0
  // FIXME(bug 1469277): Pretty sure this wants to dig into shadow trees as
1329
0
  // well.
1330
0
}
1331
1332
//----------------------------------------------------------------------
1333
1334
// Generic DOMNode implementations
1335
1336
void
1337
FragmentOrElement::FireNodeInserted(nsIDocument* aDoc,
1338
                                   nsINode* aParent,
1339
                                   nsTArray<nsCOMPtr<nsIContent> >& aNodes)
1340
0
{
1341
0
  uint32_t count = aNodes.Length();
1342
0
  for (uint32_t i = 0; i < count; ++i) {
1343
0
    nsIContent* childContent = aNodes[i];
1344
0
1345
0
    if (nsContentUtils::HasMutationListeners(childContent,
1346
0
          NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
1347
0
      InternalMutationEvent mutation(true, eLegacyNodeInserted);
1348
0
      mutation.mRelatedNode = aParent;
1349
0
1350
0
      mozAutoSubtreeModified subtree(aDoc, aParent);
1351
0
      (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe();
1352
0
    }
1353
0
  }
1354
0
}
1355
1356
//----------------------------------------------------------------------
1357
1358
// nsISupports implementation
1359
1360
0
#define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
1361
1362
class ContentUnbinder : public Runnable
1363
{
1364
public:
1365
  ContentUnbinder()
1366
    : Runnable("ContentUnbinder")
1367
0
  {
1368
0
    mLast = this;
1369
0
  }
1370
1371
  ~ContentUnbinder()
1372
0
  {
1373
0
    Run();
1374
0
  }
1375
1376
  void UnbindSubtree(nsIContent* aNode)
1377
0
  {
1378
0
    if (aNode->NodeType() != nsINode::ELEMENT_NODE &&
1379
0
        aNode->NodeType() != nsINode::DOCUMENT_FRAGMENT_NODE) {
1380
0
      return;
1381
0
    }
1382
0
    FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
1383
0
    if (container->HasChildren()) {
1384
0
      // Invalidate cached array of child nodes
1385
0
      container->InvalidateChildNodes();
1386
0
1387
0
      while (container->HasChildren()) {
1388
0
        // Hold a strong ref to the node when we remove it, because we may be
1389
0
        // the last reference to it.  We need to call DisconnectChild()
1390
0
        // before calling UnbindFromTree, since this last can notify various
1391
0
        // observers and they should really see consistent
1392
0
        // tree state.
1393
0
        // If this code changes, change the corresponding code in
1394
0
        // FragmentOrElement's and nsDocument's unlink impls.
1395
0
        nsCOMPtr<nsIContent> child = container->GetLastChild();
1396
0
        container->DisconnectChild(child);
1397
0
        UnbindSubtree(child);
1398
0
        child->UnbindFromTree();
1399
0
      }
1400
0
    }
1401
0
  }
1402
1403
  NS_IMETHOD Run() override
1404
0
  {
1405
0
    nsAutoScriptBlocker scriptBlocker;
1406
0
    uint32_t len = mSubtreeRoots.Length();
1407
0
    if (len) {
1408
0
      for (uint32_t i = 0; i < len; ++i) {
1409
0
        UnbindSubtree(mSubtreeRoots[i]);
1410
0
      }
1411
0
      mSubtreeRoots.Clear();
1412
0
    }
1413
0
    nsCycleCollector_dispatchDeferredDeletion();
1414
0
    if (this == sContentUnbinder) {
1415
0
      sContentUnbinder = nullptr;
1416
0
      if (mNext) {
1417
0
        RefPtr<ContentUnbinder> next;
1418
0
        next.swap(mNext);
1419
0
        sContentUnbinder = next;
1420
0
        next->mLast = mLast;
1421
0
        mLast = nullptr;
1422
0
        NS_IdleDispatchToCurrentThread(next.forget());
1423
0
      }
1424
0
    }
1425
0
    return NS_OK;
1426
0
  }
1427
1428
  static void UnbindAll()
1429
0
  {
1430
0
    RefPtr<ContentUnbinder> ub = sContentUnbinder;
1431
0
    sContentUnbinder = nullptr;
1432
0
    while (ub) {
1433
0
      ub->Run();
1434
0
      ub = ub->mNext;
1435
0
    }
1436
0
  }
1437
1438
  static void Append(nsIContent* aSubtreeRoot)
1439
0
  {
1440
0
    if (!sContentUnbinder) {
1441
0
      sContentUnbinder = new ContentUnbinder();
1442
0
      nsCOMPtr<nsIRunnable> e = sContentUnbinder;
1443
0
      NS_IdleDispatchToCurrentThread(e.forget());
1444
0
    }
1445
0
1446
0
    if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
1447
0
        SUBTREE_UNBINDINGS_PER_RUNNABLE) {
1448
0
      sContentUnbinder->mLast->mNext = new ContentUnbinder();
1449
0
      sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
1450
0
    }
1451
0
    sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
1452
0
  }
1453
1454
private:
1455
  AutoTArray<nsCOMPtr<nsIContent>,
1456
               SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
1457
  RefPtr<ContentUnbinder>                     mNext;
1458
  ContentUnbinder*                              mLast;
1459
  static ContentUnbinder*                       sContentUnbinder;
1460
};
1461
1462
ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
1463
1464
void
1465
FragmentOrElement::ClearContentUnbinder()
1466
0
{
1467
0
  ContentUnbinder::UnbindAll();
1468
0
}
1469
1470
NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement)
1471
1472
// We purposefully don't UNLINK_BEGIN_INHERITED here.
1473
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
1474
0
  nsIContent::Unlink(tmp);
1475
0
1476
0
  // The XBL binding is removed by RemoveFromBindingManagerRunnable
1477
0
  // which is dispatched in UnbindFromTree.
1478
0
1479
0
  if (tmp->HasProperties()) {
1480
0
    if (tmp->IsElement()) {
1481
0
      Element* elem = tmp->AsElement();
1482
0
      elem->UnlinkIntersectionObservers();
1483
0
    }
1484
0
1485
0
    if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
1486
0
      nsStaticAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
1487
0
      for (uint32_t i = 0; props[i]; ++i) {
1488
0
        tmp->DeleteProperty(*props[i]);
1489
0
      }
1490
0
      if (tmp->MayHaveAnimations()) {
1491
0
        nsAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
1492
0
        for (uint32_t i = 0; effectProps[i]; ++i) {
1493
0
          tmp->DeleteProperty(effectProps[i]);
1494
0
        }
1495
0
      }
1496
0
    }
1497
0
  }
1498
0
1499
0
  // Unlink child content (and unbind our subtree).
1500
0
  if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
1501
0
    // Don't allow script to run while we're unbinding everything.
1502
0
    nsAutoScriptBlocker scriptBlocker;
1503
0
    while (tmp->HasChildren()) {
1504
0
      // Hold a strong ref to the node when we remove it, because we may be
1505
0
      // the last reference to it.
1506
0
      // If this code changes, change the corresponding code in nsDocument's
1507
0
      // unlink impl and ContentUnbinder::UnbindSubtree.
1508
0
      nsCOMPtr<nsIContent> child = tmp->GetLastChild();
1509
0
      tmp->DisconnectChild(child);
1510
0
      child->UnbindFromTree();
1511
0
    }
1512
0
  } else if (!tmp->GetParent() && tmp->HasChildren()) {
1513
0
    ContentUnbinder::Append(tmp);
1514
0
  } /* else {
1515
0
    The subtree root will end up to a ContentUnbinder, and that will
1516
0
    unbind the child nodes.
1517
0
  } */
1518
0
1519
0
  if (ShadowRoot* shadowRoot = tmp->GetShadowRoot()) {
1520
0
    shadowRoot->Unbind();
1521
0
    tmp->ExtendedDOMSlots()->mShadowRoot = nullptr;
1522
0
  }
1523
0
1524
0
  nsIDocument* doc = tmp->OwnerDoc();
1525
0
  doc->BindingManager()->RemovedFromDocument(tmp, doc,
1526
0
                                             nsBindingManager::eDoNotRunDtor);
1527
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1528
1529
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
1530
1531
void
1532
FragmentOrElement::MarkNodeChildren(nsINode* aNode)
1533
0
{
1534
0
  JSObject* o = GetJSObjectChild(aNode);
1535
0
  if (o) {
1536
0
    JS::ExposeObjectToActiveJS(o);
1537
0
  }
1538
0
1539
0
  EventListenerManager* elm = aNode->GetExistingListenerManager();
1540
0
  if (elm) {
1541
0
    elm->MarkForCC();
1542
0
  }
1543
0
}
1544
1545
nsINode*
1546
FindOptimizableSubtreeRoot(nsINode* aNode)
1547
0
{
1548
0
  nsINode* p;
1549
0
  while ((p = aNode->GetParentNode())) {
1550
0
    if (aNode->UnoptimizableCCNode()) {
1551
0
      return nullptr;
1552
0
    }
1553
0
    aNode = p;
1554
0
  }
1555
0
1556
0
  if (aNode->UnoptimizableCCNode()) {
1557
0
    return nullptr;
1558
0
  }
1559
0
  return aNode;
1560
0
}
1561
1562
StaticAutoPtr<nsTHashtable<nsPtrHashKey<nsINode>>> gCCBlackMarkedNodes;
1563
1564
static void
1565
ClearBlackMarkedNodes()
1566
0
{
1567
0
  if (!gCCBlackMarkedNodes) {
1568
0
    return;
1569
0
  }
1570
0
  for (auto iter = gCCBlackMarkedNodes->ConstIter(); !iter.Done();
1571
0
       iter.Next()) {
1572
0
    nsINode* n = iter.Get()->GetKey();
1573
0
    n->SetCCMarkedRoot(false);
1574
0
    n->SetInCCBlackTree(false);
1575
0
  }
1576
0
  gCCBlackMarkedNodes = nullptr;
1577
0
}
1578
1579
// static
1580
void
1581
FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode)
1582
0
{
1583
0
  if (!gCCBlackMarkedNodes) {
1584
0
    return;
1585
0
  }
1586
0
  gCCBlackMarkedNodes->RemoveEntry(aNode);
1587
0
}
1588
1589
static bool
1590
IsCertainlyAliveNode(nsINode* aNode, nsIDocument* aDoc)
1591
0
{
1592
0
  MOZ_ASSERT(aNode->GetComposedDoc() == aDoc);
1593
0
1594
0
  // Marked to be in-CC-generation or if the document is an svg image that's
1595
0
  // being kept alive by the image cache. (Note that an svg image's internal
1596
0
  // SVG document will receive an OnPageHide() call when it gets purged from
1597
0
  // the image cache; hence, we use IsVisible() as a hint that the document is
1598
0
  // actively being kept alive by the cache.)
1599
0
  return nsCCUncollectableMarker::InGeneration(aDoc->GetMarkedCCGeneration()) ||
1600
0
         (nsCCUncollectableMarker::sGeneration &&
1601
0
          aDoc->IsBeingUsedAsImage() &&
1602
0
          aDoc->IsVisible());
1603
0
}
1604
1605
// static
1606
bool
1607
FragmentOrElement::CanSkipInCC(nsINode* aNode)
1608
0
{
1609
0
  // Don't try to optimize anything during shutdown.
1610
0
  if (nsCCUncollectableMarker::sGeneration == 0) {
1611
0
    return false;
1612
0
  }
1613
0
1614
0
  nsIDocument* currentDoc = aNode->GetComposedDoc();
1615
0
  if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc)) {
1616
0
    return !NeedsScriptTraverse(aNode);
1617
0
  }
1618
0
1619
0
  // Bail out early if aNode is somewhere in anonymous content,
1620
0
  // or otherwise unusual.
1621
0
  if (aNode->UnoptimizableCCNode()) {
1622
0
    return false;
1623
0
  }
1624
0
1625
0
  nsINode* root =
1626
0
    currentDoc ? static_cast<nsINode*>(currentDoc) :
1627
0
                 FindOptimizableSubtreeRoot(aNode);
1628
0
  if (!root) {
1629
0
    return false;
1630
0
  }
1631
0
1632
0
  // Subtree has been traversed already.
1633
0
  if (root->CCMarkedRoot()) {
1634
0
    return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
1635
0
  }
1636
0
1637
0
  if (!gCCBlackMarkedNodes) {
1638
0
    gCCBlackMarkedNodes = new nsTHashtable<nsPtrHashKey<nsINode> >(1020);
1639
0
  }
1640
0
1641
0
  // nodesToUnpurple contains nodes which will be removed
1642
0
  // from the purple buffer if the DOM tree is known-live.
1643
0
  AutoTArray<nsIContent*, 1020> nodesToUnpurple;
1644
0
  // grayNodes need script traverse, so they aren't removed from
1645
0
  // the purple buffer, but are marked to be in known-live subtree so that
1646
0
  // traverse is faster.
1647
0
  AutoTArray<nsINode*, 1020> grayNodes;
1648
0
1649
0
  bool foundLiveWrapper = root->HasKnownLiveWrapper();
1650
0
  if (root != currentDoc) {
1651
0
    currentDoc = nullptr;
1652
0
    if (NeedsScriptTraverse(root)) {
1653
0
      grayNodes.AppendElement(root);
1654
0
    } else if (static_cast<nsIContent*>(root)->IsPurple()) {
1655
0
      nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
1656
0
    }
1657
0
  }
1658
0
1659
0
  // Traverse the subtree and check if we could know without CC
1660
0
  // that it is known-live.
1661
0
  // Note, this traverse is non-virtual and inline, so it should be a lot faster
1662
0
  // than CC's generic traverse.
1663
0
  for (nsIContent* node = root->GetFirstChild(); node;
1664
0
       node = node->GetNextNode(root)) {
1665
0
    foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper();
1666
0
    if (foundLiveWrapper && currentDoc) {
1667
0
      // If we can mark the whole document known-live, no need to optimize
1668
0
      // so much, since when the next purple node in the document will be
1669
0
      // handled, it is fast to check that currentDoc is in CCGeneration.
1670
0
      break;
1671
0
    }
1672
0
    if (NeedsScriptTraverse(node)) {
1673
0
      // Gray nodes need real CC traverse.
1674
0
      grayNodes.AppendElement(node);
1675
0
    } else if (node->IsPurple()) {
1676
0
      nodesToUnpurple.AppendElement(node);
1677
0
    }
1678
0
  }
1679
0
1680
0
  root->SetCCMarkedRoot(true);
1681
0
  root->SetInCCBlackTree(foundLiveWrapper);
1682
0
  gCCBlackMarkedNodes->PutEntry(root);
1683
0
1684
0
  if (!foundLiveWrapper) {
1685
0
    return false;
1686
0
  }
1687
0
1688
0
  if (currentDoc) {
1689
0
    // Special case documents. If we know the document is known-live,
1690
0
    // we can mark the document to be in CCGeneration.
1691
0
    currentDoc->
1692
0
      MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
1693
0
  } else {
1694
0
    for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
1695
0
      nsINode* node = grayNodes[i];
1696
0
      node->SetInCCBlackTree(true);
1697
0
      gCCBlackMarkedNodes->PutEntry(node);
1698
0
    }
1699
0
  }
1700
0
1701
0
  // Subtree is known-live, we can remove non-gray purple nodes from
1702
0
  // purple buffer.
1703
0
  for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
1704
0
    nsIContent* purple = nodesToUnpurple[i];
1705
0
    // Can't remove currently handled purple node.
1706
0
    if (purple != aNode) {
1707
0
      purple->RemovePurple();
1708
0
    }
1709
0
  }
1710
0
  return !NeedsScriptTraverse(aNode);
1711
0
}
1712
1713
AutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr;
1714
AutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr;
1715
1716
void ClearCycleCollectorCleanupData()
1717
0
{
1718
0
  if (gPurpleRoots) {
1719
0
    uint32_t len = gPurpleRoots->Length();
1720
0
    for (uint32_t i = 0; i < len; ++i) {
1721
0
      nsINode* n = gPurpleRoots->ElementAt(i);
1722
0
      n->SetIsPurpleRoot(false);
1723
0
    }
1724
0
    delete gPurpleRoots;
1725
0
    gPurpleRoots = nullptr;
1726
0
  }
1727
0
  if (gNodesToUnbind) {
1728
0
    uint32_t len = gNodesToUnbind->Length();
1729
0
    for (uint32_t i = 0; i < len; ++i) {
1730
0
      nsIContent* c = gNodesToUnbind->ElementAt(i);
1731
0
      c->SetIsPurpleRoot(false);
1732
0
      ContentUnbinder::Append(c);
1733
0
    }
1734
0
    delete gNodesToUnbind;
1735
0
    gNodesToUnbind = nullptr;
1736
0
  }
1737
0
}
1738
1739
static bool
1740
ShouldClearPurple(nsIContent* aContent)
1741
0
{
1742
0
  MOZ_ASSERT(aContent);
1743
0
  if (aContent->IsPurple()) {
1744
0
    return true;
1745
0
  }
1746
0
1747
0
  JSObject* o = GetJSObjectChild(aContent);
1748
0
  if (o && JS::ObjectIsMarkedGray(o)) {
1749
0
    return true;
1750
0
  }
1751
0
1752
0
  if (aContent->HasListenerManager()) {
1753
0
    return true;
1754
0
  }
1755
0
1756
0
  return aContent->HasProperties();
1757
0
}
1758
1759
// If aNode is not optimizable, but is an element
1760
// with a frame in a document which has currently active presshell,
1761
// we can act as if it was optimizable. When the primary frame dies, aNode
1762
// will end up to the purple buffer because of the refcount change.
1763
bool
1764
NodeHasActiveFrame(nsIDocument* aCurrentDoc, nsINode* aNode)
1765
0
{
1766
0
  return aCurrentDoc->GetShell() && aNode->IsElement() &&
1767
0
         aNode->AsElement()->GetPrimaryFrame();
1768
0
}
1769
1770
bool
1771
OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode)
1772
0
{
1773
0
  return aNode->IsElement() && aNode->AsElement()->GetXBLBinding();
1774
0
}
1775
1776
// CanSkip checks if aNode is known-live, and if it is, returns true. If aNode
1777
// is in a known-live DOM tree, CanSkip may also remove other objects from
1778
// purple buffer and unmark event listeners and user data.  If the root of the
1779
// DOM tree is a document, less optimizations are done since checking the
1780
// liveness of the current document is usually fast and we don't want slow down
1781
// such common cases.
1782
bool
1783
FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
1784
0
{
1785
0
  // Don't try to optimize anything during shutdown.
1786
0
  if (nsCCUncollectableMarker::sGeneration == 0) {
1787
0
    return false;
1788
0
  }
1789
0
1790
0
  bool unoptimizable = aNode->UnoptimizableCCNode();
1791
0
  nsIDocument* currentDoc = aNode->GetComposedDoc();
1792
0
  if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc) &&
1793
0
      (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode) ||
1794
0
       OwnedByBindingManager(currentDoc, aNode))) {
1795
0
    MarkNodeChildren(aNode);
1796
0
    return true;
1797
0
  }
1798
0
1799
0
  if (unoptimizable) {
1800
0
    return false;
1801
0
  }
1802
0
1803
0
  nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) :
1804
0
                               FindOptimizableSubtreeRoot(aNode);
1805
0
  if (!root) {
1806
0
    return false;
1807
0
  }
1808
0
1809
0
  // Subtree has been traversed already, and aNode has
1810
0
  // been handled in a way that doesn't require revisiting it.
1811
0
  if (root->IsPurpleRoot()) {
1812
0
    return false;
1813
0
  }
1814
0
1815
0
  // nodesToClear contains nodes which are either purple or
1816
0
  // gray.
1817
0
  AutoTArray<nsIContent*, 1020> nodesToClear;
1818
0
1819
0
  bool foundLiveWrapper = root->HasKnownLiveWrapper();
1820
0
  bool domOnlyCycle = false;
1821
0
  if (root != currentDoc) {
1822
0
    currentDoc = nullptr;
1823
0
    if (!foundLiveWrapper) {
1824
0
      domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
1825
0
    }
1826
0
    if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
1827
0
      nodesToClear.AppendElement(static_cast<nsIContent*>(root));
1828
0
    }
1829
0
  }
1830
0
1831
0
  // Traverse the subtree and check if we could know without CC
1832
0
  // that it is known-live.
1833
0
  // Note, this traverse is non-virtual and inline, so it should be a lot faster
1834
0
  // than CC's generic traverse.
1835
0
  for (nsIContent* node = root->GetFirstChild(); node;
1836
0
       node = node->GetNextNode(root)) {
1837
0
    foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper();
1838
0
    if (foundLiveWrapper) {
1839
0
      domOnlyCycle = false;
1840
0
      if (currentDoc) {
1841
0
        // If we can mark the whole document live, no need to optimize
1842
0
        // so much, since when the next purple node in the document will be
1843
0
        // handled, it is fast to check that the currentDoc is in CCGeneration.
1844
0
        break;
1845
0
      }
1846
0
      // No need to put stuff to the nodesToClear array, if we can clear it
1847
0
      // already here.
1848
0
      if (node->IsPurple() && (node != aNode || aRemovingAllowed)) {
1849
0
        node->RemovePurple();
1850
0
      }
1851
0
      MarkNodeChildren(node);
1852
0
    } else {
1853
0
      domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
1854
0
      if (ShouldClearPurple(node)) {
1855
0
        // Collect interesting nodes which we can clear if we find that
1856
0
        // they are kept alive in a known-live tree or are in a DOM-only cycle.
1857
0
        nodesToClear.AppendElement(node);
1858
0
      }
1859
0
    }
1860
0
  }
1861
0
1862
0
  if (!currentDoc || !foundLiveWrapper) {
1863
0
    root->SetIsPurpleRoot(true);
1864
0
    if (domOnlyCycle) {
1865
0
      if (!gNodesToUnbind) {
1866
0
        gNodesToUnbind = new AutoTArray<nsIContent*, 1020>();
1867
0
      }
1868
0
      gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
1869
0
      for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1870
0
        nsIContent* n = nodesToClear[i];
1871
0
        if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1872
0
          n->RemovePurple();
1873
0
        }
1874
0
      }
1875
0
      return true;
1876
0
    } else {
1877
0
      if (!gPurpleRoots) {
1878
0
        gPurpleRoots = new AutoTArray<nsINode*, 1020>();
1879
0
      }
1880
0
      gPurpleRoots->AppendElement(root);
1881
0
    }
1882
0
  }
1883
0
1884
0
  if (!foundLiveWrapper) {
1885
0
    return false;
1886
0
  }
1887
0
1888
0
  if (currentDoc) {
1889
0
    // Special case documents. If we know the document is known-live,
1890
0
    // we can mark the document to be in CCGeneration.
1891
0
    currentDoc->
1892
0
      MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
1893
0
    MarkNodeChildren(currentDoc);
1894
0
  }
1895
0
1896
0
  // Subtree is known-live, so we can remove purple nodes from
1897
0
  // purple buffer and mark stuff that to be certainly alive.
1898
0
  for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1899
0
    nsIContent* n = nodesToClear[i];
1900
0
    MarkNodeChildren(n);
1901
0
    // Can't remove currently handled purple node,
1902
0
    // unless aRemovingAllowed is true.
1903
0
    if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1904
0
      n->RemovePurple();
1905
0
    }
1906
0
  }
1907
0
  return true;
1908
0
}
1909
1910
bool
1911
FragmentOrElement::CanSkipThis(nsINode* aNode)
1912
0
{
1913
0
  if (nsCCUncollectableMarker::sGeneration == 0) {
1914
0
    return false;
1915
0
  }
1916
0
  if (aNode->HasKnownLiveWrapper()) {
1917
0
    return true;
1918
0
  }
1919
0
  nsIDocument* c = aNode->GetComposedDoc();
1920
0
  return
1921
0
    ((c && IsCertainlyAliveNode(aNode, c)) || aNode->InCCBlackTree()) &&
1922
0
    !NeedsScriptTraverse(aNode);
1923
0
}
1924
1925
void
1926
FragmentOrElement::InitCCCallbacks()
1927
3
{
1928
3
  nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
1929
3
  nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
1930
3
}
1931
1932
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement)
1933
0
  return FragmentOrElement::CanSkip(tmp, aRemovingAllowed);
1934
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1935
1936
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement)
1937
0
  return FragmentOrElement::CanSkipInCC(tmp);
1938
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1939
1940
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement)
1941
0
  return FragmentOrElement::CanSkipThis(tmp);
1942
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1943
1944
static const char* kNSURIs[] = {
1945
  " ([none])",
1946
  " (xmlns)",
1947
  " (xml)",
1948
  " (xhtml)",
1949
  " (XLink)",
1950
  " (XSLT)",
1951
  " (XBL)",
1952
  " (MathML)",
1953
  " (RDF)",
1954
  " (XUL)",
1955
  " (SVG)",
1956
  " (XML Events)"
1957
};
1958
1959
// We purposefully don't TRAVERSE_BEGIN_INHERITED here.  All the bits
1960
// we should traverse should be added here or in nsINode::Traverse.
1961
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
1962
0
  if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1963
0
    char name[512];
1964
0
    uint32_t nsid = tmp->GetNameSpaceID();
1965
0
    nsAtomCString localName(tmp->NodeInfo()->NameAtom());
1966
0
    nsAutoCString uri;
1967
0
    if (tmp->OwnerDoc()->GetDocumentURI()) {
1968
0
      uri = tmp->OwnerDoc()->GetDocumentURI()->GetSpecOrDefault();
1969
0
    }
1970
0
1971
0
    nsAutoString id;
1972
0
    nsAtom* idAtom = tmp->GetID();
1973
0
    if (idAtom) {
1974
0
      id.AppendLiteral(" id='");
1975
0
      id.Append(nsDependentAtomString(idAtom));
1976
0
      id.Append('\'');
1977
0
    }
1978
0
1979
0
    nsAutoString classes;
1980
0
    const nsAttrValue* classAttrValue = tmp->IsElement() ?
1981
0
      tmp->AsElement()->GetClasses() : nullptr;
1982
0
    if (classAttrValue) {
1983
0
      classes.AppendLiteral(" class='");
1984
0
      nsAutoString classString;
1985
0
      classAttrValue->ToString(classString);
1986
0
      classString.ReplaceChar(char16_t('\n'), char16_t(' '));
1987
0
      classes.Append(classString);
1988
0
      classes.Append('\'');
1989
0
    }
1990
0
1991
0
    nsAutoCString orphan;
1992
0
    if (!tmp->IsInComposedDoc() &&
1993
0
        // Ignore xbl:content, which is never in the document and hence always
1994
0
        // appears to be orphaned.
1995
0
        !tmp->NodeInfo()->Equals(nsGkAtoms::content, kNameSpaceID_XBL)) {
1996
0
      orphan.AppendLiteral(" (orphan)");
1997
0
    }
1998
0
1999
0
    const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : "";
2000
0
    SprintfLiteral(name, "FragmentOrElement%s %s%s%s%s %s",
2001
0
                   nsuri,
2002
0
                   localName.get(),
2003
0
                   NS_ConvertUTF16toUTF8(id).get(),
2004
0
                   NS_ConvertUTF16toUTF8(classes).get(),
2005
0
                   orphan.get(),
2006
0
                   uri.get());
2007
0
    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
2008
0
  }
2009
0
  else {
2010
0
    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
2011
0
  }
2012
0
2013
0
  if (!nsIContent::Traverse(tmp, cb)) {
2014
0
    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
2015
0
  }
2016
0
2017
0
  tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb);
2018
0
2019
0
  // Check that whenever we have effect properties, MayHaveAnimations is set.
2020
#ifdef DEBUG
2021
  nsAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
2022
  for (uint32_t i = 0; effectProps[i]; ++i) {
2023
    MOZ_ASSERT_IF(tmp->GetProperty(effectProps[i]), tmp->MayHaveAnimations());
2024
  }
2025
#endif
2026
2027
0
  if (tmp->HasProperties()) {
2028
0
    if (tmp->IsElement()) {
2029
0
      Element* elem = tmp->AsElement();
2030
0
      IntersectionObserverList* observers =
2031
0
        static_cast<IntersectionObserverList*>(
2032
0
          elem->GetProperty(nsGkAtoms::intersectionobserverlist)
2033
0
        );
2034
0
      if (observers) {
2035
0
        for (auto iter = observers->Iter(); !iter.Done(); iter.Next()) {
2036
0
          DOMIntersectionObserver* observer = iter.Key();
2037
0
          cb.NoteXPCOMChild(observer);
2038
0
        }
2039
0
      }
2040
0
    }
2041
0
    if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
2042
0
      nsStaticAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
2043
0
      for (uint32_t i = 0; props[i]; ++i) {
2044
0
        nsISupports* property =
2045
0
          static_cast<nsISupports*>(tmp->GetProperty(*props[i]));
2046
0
        cb.NoteXPCOMChild(property);
2047
0
      }
2048
0
      if (tmp->MayHaveAnimations()) {
2049
0
        nsAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
2050
0
        for (uint32_t i = 0; effectProps[i]; ++i) {
2051
0
          EffectSet* effectSet =
2052
0
            static_cast<EffectSet*>(tmp->GetProperty(effectProps[i]));
2053
0
          if (effectSet) {
2054
0
            effectSet->Traverse(cb);
2055
0
          }
2056
0
        }
2057
0
      }
2058
0
    }
2059
0
  }
2060
0
2061
0
  // Traverse attribute names.
2062
0
  {
2063
0
    uint32_t i;
2064
0
    uint32_t attrs = tmp->mAttrs.AttrCount();
2065
0
    for (i = 0; i < attrs; i++) {
2066
0
      const nsAttrName* name = tmp->mAttrs.AttrNameAt(i);
2067
0
      if (!name->IsAtom()) {
2068
0
        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
2069
0
                                           "mAttrs[i]->NodeInfo()");
2070
0
        cb.NoteNativeChild(name->NodeInfo(),
2071
0
                           NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
2072
0
      }
2073
0
    }
2074
0
  }
2075
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2076
2077
2078
0
NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
2079
0
  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
2080
0
NS_INTERFACE_MAP_END_INHERITING(nsIContent)
2081
2082
//----------------------------------------------------------------------
2083
2084
nsresult
2085
FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst)
2086
0
{
2087
0
  nsresult rv = aDst->mAttrs.EnsureCapacityToClone(mAttrs);
2088
0
  NS_ENSURE_SUCCESS(rv, rv);
2089
0
2090
0
  uint32_t i, count = mAttrs.AttrCount();
2091
0
  for (i = 0; i < count; ++i) {
2092
0
    const nsAttrName* name = mAttrs.AttrNameAt(i);
2093
0
    const nsAttrValue* value = mAttrs.AttrAt(i);
2094
0
    nsAutoString valStr;
2095
0
    value->ToString(valStr);
2096
0
    rv = aDst->AsElement()->SetAttr(name->NamespaceID(), name->LocalName(),
2097
0
                                    name->GetPrefix(), valStr, false);
2098
0
    NS_ENSURE_SUCCESS(rv, rv);
2099
0
  }
2100
0
2101
0
  return NS_OK;
2102
0
}
2103
2104
const nsTextFragment*
2105
FragmentOrElement::GetText()
2106
0
{
2107
0
  return nullptr;
2108
0
}
2109
2110
uint32_t
2111
FragmentOrElement::TextLength() const
2112
0
{
2113
0
  // We can remove this assertion if it turns out to be useful to be able
2114
0
  // to depend on this returning 0
2115
0
  MOZ_ASSERT_UNREACHABLE("called FragmentOrElement::TextLength");
2116
0
2117
0
  return 0;
2118
0
}
2119
2120
bool
2121
FragmentOrElement::TextIsOnlyWhitespace()
2122
0
{
2123
0
  return false;
2124
0
}
2125
2126
bool
2127
FragmentOrElement::ThreadSafeTextIsOnlyWhitespace() const
2128
0
{
2129
0
  return false;
2130
0
}
2131
2132
static inline bool
2133
IsVoidTag(nsAtom* aTag)
2134
0
{
2135
0
  static const nsAtom* voidElements[] = {
2136
0
    nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
2137
0
    nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
2138
0
    nsGkAtoms::embed, nsGkAtoms::frame,
2139
0
    nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
2140
0
    nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
2141
0
    nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
2142
0
    nsGkAtoms::wbr
2143
0
  };
2144
0
2145
0
  static mozilla::BloomFilter<12, nsAtom> sFilter;
2146
0
  static bool sInitialized = false;
2147
0
  if (!sInitialized) {
2148
0
    sInitialized = true;
2149
0
    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
2150
0
      sFilter.add(voidElements[i]);
2151
0
    }
2152
0
  }
2153
0
2154
0
  if (sFilter.mightContain(aTag)) {
2155
0
    for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
2156
0
      if (aTag == voidElements[i]) {
2157
0
        return true;
2158
0
      }
2159
0
    }
2160
0
  }
2161
0
  return false;
2162
0
}
2163
2164
/* static */
2165
bool
2166
FragmentOrElement::IsHTMLVoid(nsAtom* aLocalName)
2167
0
{
2168
0
  return aLocalName && IsVoidTag(aLocalName);
2169
0
}
2170
2171
void
2172
FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
2173
0
{
2174
0
  aMarkup.Truncate();
2175
0
2176
0
  nsIDocument* doc = OwnerDoc();
2177
0
  if (IsInHTMLDocument()) {
2178
0
    nsContentUtils::SerializeNodeToMarkup(this, !aIncludeSelf, aMarkup);
2179
0
    return;
2180
0
  }
2181
0
2182
0
  nsAutoString contentType;
2183
0
  doc->GetContentType(contentType);
2184
0
  bool tryToCacheEncoder = !aIncludeSelf;
2185
0
2186
0
  nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
2187
0
  if (!docEncoder) {
2188
0
    docEncoder =
2189
0
      do_CreateInstance(PromiseFlatCString(
2190
0
        nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
2191
0
        NS_ConvertUTF16toUTF8(contentType)
2192
0
      ).get());
2193
0
  }
2194
0
  if (!docEncoder) {
2195
0
    // This could be some type for which we create a synthetic document.  Try
2196
0
    // again as XML
2197
0
    contentType.AssignLiteral("application/xml");
2198
0
    docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
2199
0
    // Don't try to cache the encoder since it would point to a different
2200
0
    // contentType once it has been reinitialized.
2201
0
    tryToCacheEncoder = false;
2202
0
  }
2203
0
2204
0
  NS_ENSURE_TRUE_VOID(docEncoder);
2205
0
2206
0
  uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
2207
0
                   // Output DOM-standard newlines
2208
0
                   nsIDocumentEncoder::OutputLFLineBreak |
2209
0
                   // Don't do linebreaking that's not present in
2210
0
                   // the source
2211
0
                   nsIDocumentEncoder::OutputRaw |
2212
0
                   // Only check for mozdirty when necessary (bug 599983)
2213
0
                   nsIDocumentEncoder::OutputIgnoreMozDirty;
2214
0
2215
0
  if (IsEditable()) {
2216
0
    nsCOMPtr<Element> elem = do_QueryInterface(this);
2217
0
    TextEditor* textEditor = elem ? elem->GetTextEditorInternal() : nullptr;
2218
0
    if (textEditor && textEditor->OutputsMozDirty()) {
2219
0
      flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
2220
0
    }
2221
0
  }
2222
0
2223
0
  DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
2224
0
  MOZ_ASSERT(NS_SUCCEEDED(rv));
2225
0
2226
0
  if (aIncludeSelf) {
2227
0
    docEncoder->SetNode(this);
2228
0
  } else {
2229
0
    docEncoder->SetContainerNode(this);
2230
0
  }
2231
0
  rv = docEncoder->EncodeToString(aMarkup);
2232
0
  MOZ_ASSERT(NS_SUCCEEDED(rv));
2233
0
  if (tryToCacheEncoder) {
2234
0
    doc->SetCachedEncoder(docEncoder.forget());
2235
0
  }
2236
0
}
2237
2238
static bool
2239
ContainsMarkup(const nsAString& aStr)
2240
0
{
2241
0
  // Note: we can't use FindCharInSet because null is one of the characters we
2242
0
  // want to search for.
2243
0
  const char16_t* start = aStr.BeginReading();
2244
0
  const char16_t* end = aStr.EndReading();
2245
0
2246
0
  while (start != end) {
2247
0
    char16_t c = *start;
2248
0
    if (c == char16_t('<') ||
2249
0
        c == char16_t('&') ||
2250
0
        c == char16_t('\r') ||
2251
0
        c == char16_t('\0')) {
2252
0
      return true;
2253
0
    }
2254
0
    ++start;
2255
0
  }
2256
0
2257
0
  return false;
2258
0
}
2259
2260
void
2261
FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError)
2262
0
{
2263
0
  FragmentOrElement* target = this;
2264
0
  // Handle template case.
2265
0
  if (nsNodeUtils::IsTemplateElement(target)) {
2266
0
    DocumentFragment* frag =
2267
0
      static_cast<HTMLTemplateElement*>(target)->Content();
2268
0
    MOZ_ASSERT(frag);
2269
0
    target = frag;
2270
0
  }
2271
0
2272
0
  // Fast-path for strings with no markup. Limit this to short strings, to
2273
0
  // avoid ContainsMarkup taking too long. The choice for 100 is based on
2274
0
  // gut feeling.
2275
0
  //
2276
0
  // Don't do this for elements with a weird parser insertion mode, for
2277
0
  // instance setting innerHTML = "" on a <html> element should add the
2278
0
  // optional <head> and <body> elements.
2279
0
  if (!target->HasWeirdParserInsertionMode() &&
2280
0
      aInnerHTML.Length() < 100 && !ContainsMarkup(aInnerHTML)) {
2281
0
    aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false);
2282
0
    return;
2283
0
  }
2284
0
2285
0
  nsIDocument* doc = target->OwnerDoc();
2286
0
2287
0
  // Batch possible DOMSubtreeModified events.
2288
0
  mozAutoSubtreeModified subtree(doc, nullptr);
2289
0
2290
0
  target->FireNodeRemovedForChildren();
2291
0
2292
0
  // Needed when innerHTML is used in combination with contenteditable
2293
0
  mozAutoDocUpdate updateBatch(doc, true);
2294
0
2295
0
  // Remove childnodes.
2296
0
  nsAutoMutationBatch mb(target, true, false);
2297
0
  while (target->HasChildren()) {
2298
0
    target->RemoveChildNode(target->GetFirstChild(), true);
2299
0
  }
2300
0
  mb.RemovalDone();
2301
0
2302
0
  nsAutoScriptLoaderDisabler sld(doc);
2303
0
2304
0
  nsAtom* contextLocalName = NodeInfo()->NameAtom();
2305
0
  int32_t contextNameSpaceID = GetNameSpaceID();
2306
0
2307
0
  if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(this)) {
2308
0
    // Fix up the context to be the host of the ShadowRoot.
2309
0
    contextLocalName = shadowRoot->GetHost()->NodeInfo()->NameAtom();
2310
0
    contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
2311
0
  }
2312
0
2313
0
  if (doc->IsHTMLDocument()) {
2314
0
    int32_t oldChildCount = target->GetChildCount();
2315
0
    aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
2316
0
                                               target,
2317
0
                                               contextLocalName,
2318
0
                                               contextNameSpaceID,
2319
0
                                               doc->GetCompatibilityMode() ==
2320
0
                                                 eCompatibility_NavQuirks,
2321
0
                                               true);
2322
0
    mb.NodesAdded();
2323
0
    // HTML5 parser has notified, but not fired mutation events.
2324
0
    nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
2325
0
                                                       oldChildCount);
2326
0
  } else {
2327
0
    RefPtr<DocumentFragment> df =
2328
0
      nsContentUtils::CreateContextualFragment(target, aInnerHTML, true,
2329
0
                                               aError);
2330
0
    if (!aError.Failed()) {
2331
0
      // Suppress assertion about node removal mutation events that can't have
2332
0
      // listeners anyway, because no one has had the chance to register mutation
2333
0
      // listeners on the fragment that comes from the parser.
2334
0
      nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
2335
0
2336
0
      static_cast<nsINode*>(target)->AppendChild(*df, aError);
2337
0
      mb.NodesAdded();
2338
0
    }
2339
0
  }
2340
0
}
2341
2342
void
2343
FragmentOrElement::FireNodeRemovedForChildren()
2344
0
{
2345
0
  nsIDocument* doc = OwnerDoc();
2346
0
  // Optimize the common case
2347
0
  if (!nsContentUtils::
2348
0
        HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
2349
0
    return;
2350
0
  }
2351
0
2352
0
  nsCOMPtr<nsINode> child;
2353
0
  for (child = GetFirstChild();
2354
0
       child && child->GetParentNode() == this;
2355
0
       child = child->GetNextSibling()) {
2356
0
    nsContentUtils::MaybeFireNodeRemoved(child, this);
2357
0
  }
2358
0
}
2359
2360
void
2361
FragmentOrElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
2362
                                          size_t* aNodeSize) const
2363
0
{
2364
0
  nsIContent::AddSizeOfExcludingThis(aSizes, aNodeSize);
2365
0
  *aNodeSize +=
2366
0
    mAttrs.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
2367
0
2368
0
  nsDOMSlots* slots = GetExistingDOMSlots();
2369
0
  if (slots) {
2370
0
    *aNodeSize += slots->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
2371
0
  }
2372
0
}