Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/ShadowRoot.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
#include "mozilla/Preferences.h"
8
#include "mozilla/dom/ShadowRoot.h"
9
#include "mozilla/dom/DocumentFragment.h"
10
#include "ChildIterator.h"
11
#include "nsContentUtils.h"
12
#include "nsIStyleSheetLinkingElement.h"
13
#include "nsWindowSizes.h"
14
#include "nsXBLPrototypeBinding.h"
15
#include "mozilla/dom/DirectionalityUtils.h"
16
#include "mozilla/dom/Element.h"
17
#include "mozilla/dom/HTMLSlotElement.h"
18
#include "mozilla/EventDispatcher.h"
19
#include "mozilla/ServoStyleRuleMap.h"
20
#include "mozilla/StyleSheet.h"
21
#include "mozilla/StyleSheetInlines.h"
22
#include "mozilla/dom/StyleSheetList.h"
23
24
using namespace mozilla;
25
using namespace mozilla::dom;
26
27
NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
28
29
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, DocumentFragment)
30
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
31
0
  for (StyleSheet* sheet : tmp->mStyleSheets) {
32
0
    // mServoStyles keeps another reference to it if applicable.
33
0
    if (sheet->IsApplicable()) {
34
0
      MOZ_ASSERT(tmp->mServoStyles);
35
0
      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
36
0
      cb.NoteXPCOMChild(sheet);
37
0
    }
38
0
  }
39
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
40
0
  for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done();
41
0
       iter.Next()) {
42
0
    iter.Get()->Traverse(&cb);
43
0
  }
44
0
  DocumentOrShadowRoot::Traverse(tmp, cb);
45
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
46
47
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
48
0
  if (tmp->GetHost()) {
49
0
    tmp->GetHost()->RemoveMutationObserver(tmp);
50
0
  }
51
0
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
52
0
  tmp->mIdentifierMap.Clear();
53
0
  DocumentOrShadowRoot::Unlink(tmp);
54
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
55
56
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
57
0
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
58
0
  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
59
0
  NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer)
60
0
NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
61
62
NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
63
NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
64
65
ShadowRoot::ShadowRoot(Element* aElement, ShadowRootMode aMode,
66
                       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
67
  : DocumentFragment(std::move(aNodeInfo))
68
  , DocumentOrShadowRoot(*this)
69
  , mMode(aMode)
70
  , mIsUAWidget(false)
71
0
{
72
0
  SetHost(aElement);
73
0
74
0
  // Nodes in a shadow tree should never store a value
75
0
  // in the subtree root pointer, nodes in the shadow tree
76
0
  // track the subtree root using GetContainingShadow().
77
0
  ClearSubtreeRootPointer();
78
0
79
0
  SetFlags(NODE_IS_IN_SHADOW_TREE);
80
0
  Bind();
81
0
82
0
  ExtendedDOMSlots()->mBindingParent = aElement;
83
0
  ExtendedDOMSlots()->mContainingShadow = this;
84
0
85
0
  // Add the ShadowRoot as a mutation observer on the host to watch
86
0
  // for mutations because the insertion points in this ShadowRoot
87
0
  // may need to be updated when the host children are modified.
88
0
  GetHost()->AddMutationObserver(this);
89
0
}
90
91
ShadowRoot::~ShadowRoot()
92
0
{
93
0
  if (auto* host = GetHost()) {
94
0
    // mHost may have been unlinked.
95
0
    host->RemoveMutationObserver(this);
96
0
  }
97
0
98
0
  if (IsInComposedDoc()) {
99
0
    OwnerDoc()->RemoveComposedDocShadowRoot(*this);
100
0
  }
101
0
102
0
  MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->IsComposedDocShadowRoot(*this));
103
0
104
0
  UnsetFlags(NODE_IS_IN_SHADOW_TREE);
105
0
106
0
  // nsINode destructor expects mSubtreeRoot == this.
107
0
  SetSubtreeRootPointer(this);
108
0
}
109
110
MOZ_DEFINE_MALLOC_SIZE_OF(ShadowRootAuthorStylesMallocSizeOf)
111
MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ShadowRootAuthorStylesMallocEnclosingSizeOf)
112
113
void
114
ShadowRoot::AddSizeOfExcludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const
115
0
{
116
0
  DocumentFragment::AddSizeOfExcludingThis(aSizes, aNodeSize);
117
0
  DocumentOrShadowRoot::AddSizeOfExcludingThis(aSizes);
118
0
  aSizes.mLayoutShadowDomAuthorStyles +=
119
0
    Servo_AuthorStyles_SizeOfIncludingThis(
120
0
      ShadowRootAuthorStylesMallocSizeOf,
121
0
      ShadowRootAuthorStylesMallocEnclosingSizeOf,
122
0
      mServoStyles.get());
123
0
}
124
125
JSObject*
126
ShadowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
127
0
{
128
0
  return mozilla::dom::ShadowRoot_Binding::Wrap(aCx, this, aGivenProto);
129
0
}
130
131
void
132
ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther)
133
0
{
134
0
  size_t sheetCount = aOther->SheetCount();
135
0
  for (size_t i = 0; i < sheetCount; ++i) {
136
0
    StyleSheet* sheet = aOther->SheetAt(i);
137
0
    if (sheet->IsApplicable()) {
138
0
      RefPtr<StyleSheet> clonedSheet =
139
0
        sheet->Clone(nullptr, nullptr, this, nullptr);
140
0
      if (clonedSheet) {
141
0
        AppendStyleSheet(*clonedSheet.get());
142
0
      }
143
0
    }
144
0
  }
145
0
}
146
147
nsresult
148
ShadowRoot::Bind()
149
0
{
150
0
  MOZ_ASSERT(!IsInComposedDoc(), "Forgot to unbind?");
151
0
  if (Host()->IsInComposedDoc()) {
152
0
    SetIsConnected(true);
153
0
    OwnerDoc()->AddComposedDocShadowRoot(*this);
154
0
  }
155
0
156
0
  for (nsIContent* child = GetFirstChild();
157
0
       child;
158
0
       child = child->GetNextSibling()) {
159
0
    nsresult rv = child->BindToTree(nullptr, this, Host());
160
0
    NS_ENSURE_SUCCESS(rv, rv);
161
0
  }
162
0
163
0
  return NS_OK;
164
0
}
165
166
void
167
ShadowRoot::Unbind()
168
0
{
169
0
  if (IsInComposedDoc()) {
170
0
    SetIsConnected(false);
171
0
    OwnerDoc()->RemoveComposedDocShadowRoot(*this);
172
0
  }
173
0
174
0
  for (nsIContent* child = GetFirstChild();
175
0
       child;
176
0
       child = child->GetNextSibling()) {
177
0
    child->UnbindFromTree(true, false);
178
0
  }
179
0
}
180
181
void
182
ShadowRoot::InvalidateStyleAndLayoutOnSubtree(Element* aElement)
183
0
{
184
0
  MOZ_ASSERT(aElement);
185
0
  nsIDocument* doc = GetComposedDoc();
186
0
  if (!doc) {
187
0
    return;
188
0
  }
189
0
190
0
  nsIPresShell* shell = doc->GetShell();
191
0
  if (!shell) {
192
0
    return;
193
0
  }
194
0
195
0
  shell->DestroyFramesForAndRestyle(aElement);
196
0
}
197
198
void
199
ShadowRoot::AddSlot(HTMLSlotElement* aSlot)
200
0
{
201
0
  MOZ_ASSERT(aSlot);
202
0
203
0
  // Note that if name attribute missing, the slot is a default slot.
204
0
  nsAutoString name;
205
0
  aSlot->GetName(name);
206
0
207
0
  nsTArray<HTMLSlotElement*>* currentSlots = mSlotMap.LookupOrAdd(name);
208
0
  MOZ_ASSERT(currentSlots);
209
0
210
0
  HTMLSlotElement* oldSlot = currentSlots->SafeElementAt(0);
211
0
212
0
  TreeOrderComparator comparator;
213
0
  currentSlots->InsertElementSorted(aSlot, comparator);
214
0
215
0
  HTMLSlotElement* currentSlot = currentSlots->ElementAt(0);
216
0
  if (currentSlot != aSlot) {
217
0
    return;
218
0
  }
219
0
220
0
  if (oldSlot && oldSlot != currentSlot) {
221
0
    // Move assigned nodes from old slot to new slot.
222
0
    InvalidateStyleAndLayoutOnSubtree(oldSlot);
223
0
    const nsTArray<RefPtr<nsINode>>& assignedNodes = oldSlot->AssignedNodes();
224
0
    bool doEnqueueSlotChange = false;
225
0
    while (assignedNodes.Length() > 0) {
226
0
      nsINode* assignedNode = assignedNodes[0];
227
0
228
0
      oldSlot->RemoveAssignedNode(assignedNode);
229
0
      currentSlot->AppendAssignedNode(assignedNode);
230
0
      doEnqueueSlotChange = true;
231
0
    }
232
0
233
0
    if (doEnqueueSlotChange) {
234
0
      oldSlot->EnqueueSlotChangeEvent();
235
0
      currentSlot->EnqueueSlotChangeEvent();
236
0
      SlotStateChanged(oldSlot);
237
0
      SlotStateChanged(currentSlot);
238
0
    }
239
0
  } else {
240
0
    bool doEnqueueSlotChange = false;
241
0
    // Otherwise add appropriate nodes to this slot from the host.
242
0
    for (nsIContent* child = GetHost()->GetFirstChild();
243
0
         child;
244
0
         child = child->GetNextSibling()) {
245
0
      nsAutoString slotName;
246
0
      if (child->IsElement()) {
247
0
        child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName);
248
0
      }
249
0
      if (!child->IsSlotable() || !slotName.Equals(name)) {
250
0
        continue;
251
0
      }
252
0
      doEnqueueSlotChange = true;
253
0
      currentSlot->AppendAssignedNode(child);
254
0
    }
255
0
256
0
    if (doEnqueueSlotChange) {
257
0
      currentSlot->EnqueueSlotChangeEvent();
258
0
      SlotStateChanged(currentSlot);
259
0
    }
260
0
  }
261
0
}
262
263
void
264
ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot)
265
0
{
266
0
  MOZ_ASSERT(aSlot);
267
0
268
0
  nsAutoString name;
269
0
  aSlot->GetName(name);
270
0
271
0
  SlotArray* currentSlots = mSlotMap.Get(name);
272
0
  MOZ_DIAGNOSTIC_ASSERT(currentSlots && currentSlots->Contains(aSlot),
273
0
                        "Slot to deregister wasn't found?");
274
0
  if (currentSlots->Length() == 1) {
275
0
    MOZ_ASSERT(currentSlots->ElementAt(0) == aSlot);
276
0
277
0
    InvalidateStyleAndLayoutOnSubtree(aSlot);
278
0
279
0
    mSlotMap.Remove(name);
280
0
    if (!aSlot->AssignedNodes().IsEmpty()) {
281
0
      aSlot->ClearAssignedNodes();
282
0
      aSlot->EnqueueSlotChangeEvent();
283
0
    }
284
0
285
0
    return;
286
0
  }
287
0
288
0
  const bool wasFirstSlot = currentSlots->ElementAt(0) == aSlot;
289
0
  currentSlots->RemoveElement(aSlot);
290
0
291
0
  // Move assigned nodes from removed slot to the next slot in
292
0
  // tree order with the same name.
293
0
  if (!wasFirstSlot) {
294
0
    return;
295
0
  }
296
0
297
0
  InvalidateStyleAndLayoutOnSubtree(aSlot);
298
0
  HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
299
0
  const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
300
0
  bool slottedNodesChanged = !assignedNodes.IsEmpty();
301
0
  while (!assignedNodes.IsEmpty()) {
302
0
    nsINode* assignedNode = assignedNodes[0];
303
0
304
0
    aSlot->RemoveAssignedNode(assignedNode);
305
0
    replacementSlot->AppendAssignedNode(assignedNode);
306
0
  }
307
0
308
0
  if (slottedNodesChanged) {
309
0
    aSlot->EnqueueSlotChangeEvent();
310
0
    replacementSlot->EnqueueSlotChangeEvent();
311
0
  }
312
0
}
313
314
// FIXME(emilio): There's a bit of code duplication between this and the
315
// equivalent ServoStyleSet methods, it'd be nice to not duplicate it...
316
void
317
ShadowRoot::RuleAdded(StyleSheet& aSheet, css::Rule& aRule)
318
0
{
319
0
  if (!aSheet.IsApplicable()) {
320
0
    return;
321
0
  }
322
0
323
0
  MOZ_ASSERT(mServoStyles);
324
0
  if (mStyleRuleMap) {
325
0
    mStyleRuleMap->RuleAdded(aSheet, aRule);
326
0
  }
327
0
  Servo_AuthorStyles_ForceDirty(mServoStyles.get());
328
0
  ApplicableRulesChanged();
329
0
}
330
331
void
332
ShadowRoot::RuleRemoved(StyleSheet& aSheet, css::Rule& aRule)
333
0
{
334
0
  if (!aSheet.IsApplicable()) {
335
0
    return;
336
0
  }
337
0
338
0
  MOZ_ASSERT(mServoStyles);
339
0
  if (mStyleRuleMap) {
340
0
    mStyleRuleMap->RuleRemoved(aSheet, aRule);
341
0
  }
342
0
  Servo_AuthorStyles_ForceDirty(mServoStyles.get());
343
0
  ApplicableRulesChanged();
344
0
}
345
346
void
347
0
ShadowRoot::RuleChanged(StyleSheet& aSheet, css::Rule*) {
348
0
  if (!aSheet.IsApplicable()) {
349
0
    return;
350
0
  }
351
0
352
0
  MOZ_ASSERT(mServoStyles);
353
0
  Servo_AuthorStyles_ForceDirty(mServoStyles.get());
354
0
  ApplicableRulesChanged();
355
0
}
356
357
void
358
ShadowRoot::ApplicableRulesChanged()
359
0
{
360
0
  nsIDocument* doc = GetComposedDoc();
361
0
  if (!doc) {
362
0
    return;
363
0
  }
364
0
365
0
  if (nsIPresShell* shell = doc->GetShell()) {
366
0
    shell->RecordShadowStyleChange(*this);
367
0
  }
368
0
}
369
370
void
371
ShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet)
372
0
{
373
0
  DocumentOrShadowRoot::InsertSheetAt(aIndex, aSheet);
374
0
  if (aSheet.IsApplicable()) {
375
0
    InsertSheetIntoAuthorData(aIndex, aSheet);
376
0
  }
377
0
}
378
379
void
380
ShadowRoot::InsertSheetIntoAuthorData(size_t aIndex, StyleSheet& aSheet)
381
0
{
382
0
  MOZ_ASSERT(SheetAt(aIndex) == &aSheet);
383
0
  MOZ_ASSERT(aSheet.IsApplicable());
384
0
385
0
  if (!mServoStyles) {
386
0
    mServoStyles.reset(Servo_AuthorStyles_Create());
387
0
  }
388
0
389
0
  if (mStyleRuleMap) {
390
0
    mStyleRuleMap->SheetAdded(aSheet);
391
0
  }
392
0
393
0
  for (size_t i = aIndex + 1; i < SheetCount(); ++i) {
394
0
    StyleSheet* beforeSheet = SheetAt(i);
395
0
    if (!beforeSheet->IsApplicable()) {
396
0
      continue;
397
0
    }
398
0
399
0
    Servo_AuthorStyles_InsertStyleSheetBefore(
400
0
      mServoStyles.get(), &aSheet, beforeSheet);
401
0
    ApplicableRulesChanged();
402
0
    return;
403
0
  }
404
0
405
0
  Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
406
0
  ApplicableRulesChanged();
407
0
}
408
409
// FIXME(emilio): This needs to notify document observers and such,
410
// presumably.
411
void
412
ShadowRoot::StyleSheetApplicableStateChanged(StyleSheet& aSheet, bool aApplicable)
413
0
{
414
0
  int32_t index = IndexOfSheet(aSheet);
415
0
  if (index < 0) {
416
0
    // NOTE(emilio): @import sheets are handled in the relevant RuleAdded
417
0
    // notification, which only notifies after the sheet is loaded.
418
0
    //
419
0
    // This setup causes weirdness in other places, we may want to fix this in
420
0
    // bug 1465031.
421
0
    MOZ_DIAGNOSTIC_ASSERT(aSheet.GetParentSheet(),
422
0
                          "It'd better be an @import sheet");
423
0
    return;
424
0
  }
425
0
  if (aApplicable) {
426
0
    InsertSheetIntoAuthorData(size_t(index), aSheet);
427
0
  } else {
428
0
    MOZ_ASSERT(mServoStyles);
429
0
    if (mStyleRuleMap) {
430
0
      mStyleRuleMap->SheetRemoved(aSheet);
431
0
    }
432
0
    Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
433
0
    ApplicableRulesChanged();
434
0
  }
435
0
}
436
437
void
438
ShadowRoot::RemoveSheet(StyleSheet* aSheet)
439
0
{
440
0
  MOZ_ASSERT(aSheet);
441
0
  RefPtr<StyleSheet> sheet = DocumentOrShadowRoot::RemoveSheet(*aSheet);
442
0
  MOZ_ASSERT(sheet);
443
0
  if (sheet->IsApplicable()) {
444
0
    MOZ_ASSERT(mServoStyles);
445
0
    if (mStyleRuleMap) {
446
0
      mStyleRuleMap->SheetRemoved(*sheet);
447
0
    }
448
0
    Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), sheet);
449
0
    ApplicableRulesChanged();
450
0
  }
451
0
}
452
453
void
454
ShadowRoot::AddToIdTable(Element* aElement, nsAtom* aId)
455
0
{
456
0
  nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
457
0
  if (entry) {
458
0
    entry->AddIdElement(aElement);
459
0
  }
460
0
}
461
462
void
463
ShadowRoot::RemoveFromIdTable(Element* aElement, nsAtom* aId)
464
0
{
465
0
  nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
466
0
  if (entry) {
467
0
    entry->RemoveIdElement(aElement);
468
0
    if (entry->IsEmpty()) {
469
0
      mIdentifierMap.RemoveEntry(entry);
470
0
    }
471
0
  }
472
0
}
473
474
void
475
ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor)
476
0
{
477
0
  aVisitor.mCanHandle = true;
478
0
  aVisitor.mRootOfClosedTree = IsClosed();
479
0
  // Inform that we're about to exit the current scope.
480
0
  aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
481
0
482
0
  // https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
483
0
  if (!aVisitor.mEvent->mFlags.mComposed) {
484
0
    nsCOMPtr<nsIContent> originalTarget =
485
0
      do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
486
0
    if (originalTarget->GetContainingShadow() == this) {
487
0
      // If we do stop propagation, we still want to propagate
488
0
      // the event to chrome (nsPIDOMWindow::GetParentTarget()).
489
0
      // The load event is special in that we don't ever propagate it
490
0
      // to chrome.
491
0
      nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
492
0
      EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
493
0
        ? win->GetParentTarget() : nullptr;
494
0
495
0
      aVisitor.SetParentTarget(parentTarget, true);
496
0
      return;
497
0
    }
498
0
  }
499
0
500
0
  nsIContent* shadowHost = GetHost();
501
0
  aVisitor.SetParentTarget(shadowHost, false);
502
0
503
0
  nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
504
0
  if (content && content->GetBindingParent() == shadowHost) {
505
0
    aVisitor.mEventTargetAtParent = shadowHost;
506
0
  }
507
0
}
508
509
ShadowRoot::SlotAssignment
510
ShadowRoot::SlotAssignmentFor(nsIContent* aContent)
511
0
{
512
0
  nsAutoString slotName;
513
0
  // Note that if slot attribute is missing, assign it to the first default
514
0
  // slot, if exists.
515
0
  if (aContent->IsElement()) {
516
0
    aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::slot, slotName);
517
0
  }
518
0
519
0
  nsTArray<HTMLSlotElement*>* slots = mSlotMap.Get(slotName);
520
0
  if (!slots) {
521
0
    return { };
522
0
  }
523
0
524
0
  HTMLSlotElement* slot = slots->ElementAt(0);
525
0
  MOZ_ASSERT(slot);
526
0
527
0
  // Find the appropriate position in the assigned node list for the
528
0
  // newly assigned content.
529
0
  const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
530
0
  nsIContent* currentContent = GetHost()->GetFirstChild();
531
0
  Maybe<uint32_t> insertionIndex;
532
0
  for (uint32_t i = 0; i < assignedNodes.Length(); i++) {
533
0
    // Seek through the host's explicit children until the
534
0
    // assigned content is found.
535
0
    while (currentContent && currentContent != assignedNodes[i]) {
536
0
      if (currentContent == aContent) {
537
0
        insertionIndex.emplace(i);
538
0
        break;
539
0
      }
540
0
541
0
      currentContent = currentContent->GetNextSibling();
542
0
    }
543
0
544
0
    if (insertionIndex) {
545
0
      break;
546
0
    }
547
0
  }
548
0
549
0
  return { slot, insertionIndex };
550
0
}
551
552
void
553
ShadowRoot::MaybeReassignElement(Element* aElement)
554
0
{
555
0
  MOZ_ASSERT(aElement->GetParent() == GetHost());
556
0
  HTMLSlotElement* oldSlot = aElement->GetAssignedSlot();
557
0
  SlotAssignment assignment = SlotAssignmentFor(aElement);
558
0
559
0
  if (assignment.mSlot == oldSlot) {
560
0
    // Nothing to do here.
561
0
    return;
562
0
  }
563
0
564
0
  if (nsIDocument* doc = GetComposedDoc()) {
565
0
    if (nsIPresShell* shell = doc->GetShell()) {
566
0
      shell->SlotAssignmentWillChange(*aElement, oldSlot, assignment.mSlot);
567
0
    }
568
0
  }
569
0
570
0
  if (oldSlot) {
571
0
    oldSlot->RemoveAssignedNode(aElement);
572
0
    oldSlot->EnqueueSlotChangeEvent();
573
0
  }
574
0
575
0
  if (assignment.mSlot) {
576
0
    if (assignment.mIndex) {
577
0
      assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aElement);
578
0
    } else {
579
0
      assignment.mSlot->AppendAssignedNode(aElement);
580
0
    }
581
0
    assignment.mSlot->EnqueueSlotChangeEvent();
582
0
  }
583
0
584
0
  SlotStateChanged(oldSlot);
585
0
  SlotStateChanged(assignment.mSlot);
586
0
}
587
588
Element*
589
ShadowRoot::GetActiveElement()
590
0
{
591
0
  return GetRetargetedFocusedElement();
592
0
}
593
594
void
595
ShadowRoot::GetInnerHTML(nsAString& aInnerHTML)
596
0
{
597
0
  GetMarkup(false, aInnerHTML);
598
0
}
599
600
void
601
ShadowRoot::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
602
0
{
603
0
  SetInnerHTMLInternal(aInnerHTML, aError);
604
0
}
605
606
nsINode*
607
ShadowRoot::ImportNodeAndAppendChildAt(nsINode& aParentNode,
608
                                       nsINode& aNode,
609
                                       bool aDeep,
610
                                       mozilla::ErrorResult& rv)
611
0
{
612
0
  MOZ_ASSERT(mIsUAWidget);
613
0
614
0
  if (!aParentNode.IsInUAWidget()) {
615
0
    rv.Throw(NS_ERROR_INVALID_ARG);
616
0
    return nullptr;
617
0
  }
618
0
619
0
  RefPtr<nsINode> node = OwnerDoc()->ImportNode(aNode, aDeep, rv);
620
0
  if (rv.Failed()) {
621
0
    return nullptr;
622
0
  }
623
0
624
0
  return aParentNode.AppendChild(*node, rv);
625
0
}
626
627
nsINode*
628
ShadowRoot::CreateElementAndAppendChildAt(nsINode& aParentNode,
629
                                          const nsAString& aTagName,
630
0
                                          mozilla::ErrorResult& rv) {
631
0
  MOZ_ASSERT(mIsUAWidget);
632
0
  MOZ_ASSERT(OwnerDoc());
633
0
634
0
  if (!aParentNode.IsInUAWidget()) {
635
0
    rv.Throw(NS_ERROR_INVALID_ARG);
636
0
    return nullptr;
637
0
  }
638
0
639
0
  // This option is not exposed to UA Widgets
640
0
  ElementCreationOptionsOrString options;
641
0
642
0
  RefPtr<nsINode> node = OwnerDoc()->CreateElement(aTagName, options, rv);
643
0
  if (rv.Failed()) {
644
0
    return nullptr;
645
0
  }
646
0
647
0
  return aParentNode.AppendChild(*node, rv);
648
0
}
649
650
void
651
ShadowRoot::AttributeChanged(Element* aElement,
652
                             int32_t aNameSpaceID,
653
                             nsAtom* aAttribute,
654
                             int32_t aModType,
655
                             const nsAttrValue* aOldValue)
656
0
{
657
0
  if (aNameSpaceID != kNameSpaceID_None || aAttribute != nsGkAtoms::slot) {
658
0
    return;
659
0
  }
660
0
661
0
  if (aElement->GetParent() != GetHost()) {
662
0
    return;
663
0
  }
664
0
665
0
  MaybeReassignElement(aElement);
666
0
}
667
668
void
669
ShadowRoot::ContentAppended(nsIContent* aFirstNewContent)
670
0
{
671
0
  for (nsIContent* content = aFirstNewContent;
672
0
       content;
673
0
       content = content->GetNextSibling()) {
674
0
    ContentInserted(content);
675
0
  }
676
0
}
677
678
void
679
ShadowRoot::ContentInserted(nsIContent* aChild)
680
0
{
681
0
  // Check to ensure that the child not an anonymous subtree root because
682
0
  // even though its parent could be the host it may not be in the host's child
683
0
  // list.
684
0
  if (aChild->IsRootOfAnonymousSubtree()) {
685
0
    return;
686
0
  }
687
0
688
0
  if (!aChild->IsSlotable()) {
689
0
    return;
690
0
  }
691
0
692
0
  if (aChild->GetParent() == GetHost()) {
693
0
    SlotAssignment assignment = SlotAssignmentFor(aChild);
694
0
    if (!assignment.mSlot) {
695
0
      return;
696
0
    }
697
0
698
0
    // Fallback content will go away, let layout know.
699
0
    if (assignment.mSlot->AssignedNodes().IsEmpty()) {
700
0
      InvalidateStyleAndLayoutOnSubtree(assignment.mSlot);
701
0
    }
702
0
703
0
    if (assignment.mIndex) {
704
0
      assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aChild);
705
0
    } else {
706
0
      assignment.mSlot->AppendAssignedNode(aChild);
707
0
    }
708
0
    assignment.mSlot->EnqueueSlotChangeEvent();
709
0
710
0
    SlotStateChanged(assignment.mSlot);
711
0
    return;
712
0
  }
713
0
714
0
  // If parent's root is a shadow root, and parent is a slot whose assigned
715
0
  // nodes is the empty list, then run signal a slot change for parent.
716
0
  HTMLSlotElement* slot = HTMLSlotElement::FromNodeOrNull(aChild->GetParent());
717
0
  if (slot && slot->GetContainingShadow() == this &&
718
0
      slot->AssignedNodes().IsEmpty()) {
719
0
    slot->EnqueueSlotChangeEvent();
720
0
  }
721
0
}
722
723
void
724
ShadowRoot::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling)
725
0
{
726
0
  // Check to ensure that the child not an anonymous subtree root because
727
0
  // even though its parent could be the host it may not be in the host's child
728
0
  // list.
729
0
 if (aChild->IsRootOfAnonymousSubtree()) {
730
0
    return;
731
0
  }
732
0
733
0
  if (!aChild->IsSlotable()) {
734
0
    return;
735
0
  }
736
0
737
0
  if (aChild->GetParent() == GetHost()) {
738
0
    if (HTMLSlotElement* slot = aChild->GetAssignedSlot()) {
739
0
      // If the slot is going to start showing fallback content, we need to tell
740
0
      // layout about it.
741
0
      if (slot->AssignedNodes().Length() == 1) {
742
0
        InvalidateStyleAndLayoutOnSubtree(slot);
743
0
      }
744
0
      slot->RemoveAssignedNode(aChild);
745
0
      slot->EnqueueSlotChangeEvent();
746
0
    }
747
0
    return;
748
0
  }
749
0
750
0
  // If parent's root is a shadow root, and parent is a slot whose assigned
751
0
  // nodes is the empty list, then run signal a slot change for parent.
752
0
  HTMLSlotElement* slot = HTMLSlotElement::FromNodeOrNull(aChild->GetParent());
753
0
  if (slot && slot->GetContainingShadow() == this &&
754
0
      slot->AssignedNodes().IsEmpty()) {
755
0
    slot->EnqueueSlotChangeEvent();
756
0
  }
757
0
}
758
759
ServoStyleRuleMap&
760
ShadowRoot::ServoStyleRuleMap()
761
0
{
762
0
  if (!mStyleRuleMap) {
763
0
    mStyleRuleMap = MakeUnique<mozilla::ServoStyleRuleMap>();
764
0
  }
765
0
  mStyleRuleMap->EnsureTable(*this);
766
0
  return *mStyleRuleMap;
767
0
}
768
769
nsresult
770
ShadowRoot::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const
771
0
{
772
0
  *aResult = nullptr;
773
0
  return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
774
0
}