forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			723 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			723 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "mozilla/Preferences.h"
 | |
| #include "mozilla/dom/ShadowRoot.h"
 | |
| #include "mozilla/dom/DocumentFragment.h"
 | |
| #include "ChildIterator.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsIStyleSheetLinkingElement.h"
 | |
| #include "nsWindowSizes.h"
 | |
| #include "nsXBLPrototypeBinding.h"
 | |
| #include "mozilla/dom/DirectionalityUtils.h"
 | |
| #include "mozilla/dom/Element.h"
 | |
| #include "mozilla/dom/HTMLSlotElement.h"
 | |
| #include "mozilla/EventDispatcher.h"
 | |
| #include "mozilla/IdentifierMapEntry.h"
 | |
| #include "mozilla/ServoStyleRuleMap.h"
 | |
| #include "mozilla/StyleSheet.h"
 | |
| #include "mozilla/StyleSheetInlines.h"
 | |
| #include "mozilla/dom/StyleSheetList.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::dom;
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, DocumentFragment)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
 | |
|   for (StyleSheet* sheet : tmp->mStyleSheets) {
 | |
|     // mServoStyles keeps another reference to it if applicable.
 | |
|     if (sheet->IsApplicable()) {
 | |
|       MOZ_ASSERT(tmp->mServoStyles);
 | |
|       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mServoStyles->sheets[i]");
 | |
|       cb.NoteXPCOMChild(sheet);
 | |
|     }
 | |
|   }
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
 | |
|   for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); iter.Next()) {
 | |
|     iter.Get()->Traverse(&cb);
 | |
|   }
 | |
|   DocumentOrShadowRoot::Traverse(tmp, cb);
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
 | |
|   if (tmp->GetHost()) {
 | |
|     tmp->GetHost()->RemoveMutationObserver(tmp);
 | |
|   }
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
 | |
|   tmp->mIdentifierMap.Clear();
 | |
|   DocumentOrShadowRoot::Unlink(tmp);
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
 | |
|   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIRadioGroupContainer)
 | |
| NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
 | |
| 
 | |
| NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
 | |
| NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
 | |
| 
 | |
| ShadowRoot::ShadowRoot(Element* aElement, ShadowRootMode aMode,
 | |
|                        already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
 | |
|     : DocumentFragment(std::move(aNodeInfo)),
 | |
|       DocumentOrShadowRoot(*this),
 | |
|       mMode(aMode),
 | |
|       mIsUAWidget(false) {
 | |
|   SetHost(aElement);
 | |
| 
 | |
|   // Nodes in a shadow tree should never store a value
 | |
|   // in the subtree root pointer, nodes in the shadow tree
 | |
|   // track the subtree root using GetContainingShadow().
 | |
|   ClearSubtreeRootPointer();
 | |
| 
 | |
|   SetFlags(NODE_IS_IN_SHADOW_TREE);
 | |
|   Bind();
 | |
| 
 | |
|   ExtendedDOMSlots()->mBindingParent = aElement;
 | |
|   ExtendedDOMSlots()->mContainingShadow = this;
 | |
| 
 | |
|   // Add the ShadowRoot as a mutation observer on the host to watch
 | |
|   // for mutations because the insertion points in this ShadowRoot
 | |
|   // may need to be updated when the host children are modified.
 | |
|   GetHost()->AddMutationObserver(this);
 | |
| }
 | |
| 
 | |
| ShadowRoot::~ShadowRoot() {
 | |
|   if (auto* host = GetHost()) {
 | |
|     // mHost may have been unlinked.
 | |
|     host->RemoveMutationObserver(this);
 | |
|   }
 | |
| 
 | |
|   if (IsInComposedDoc()) {
 | |
|     OwnerDoc()->RemoveComposedDocShadowRoot(*this);
 | |
|   }
 | |
| 
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!OwnerDoc()->IsComposedDocShadowRoot(*this));
 | |
| 
 | |
|   UnsetFlags(NODE_IS_IN_SHADOW_TREE);
 | |
| 
 | |
|   // nsINode destructor expects mSubtreeRoot == this.
 | |
|   SetSubtreeRootPointer(this);
 | |
| }
 | |
| 
 | |
| MOZ_DEFINE_MALLOC_SIZE_OF(ShadowRootAuthorStylesMallocSizeOf)
 | |
| MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ShadowRootAuthorStylesMallocEnclosingSizeOf)
 | |
| 
 | |
| void ShadowRoot::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
 | |
|                                         size_t* aNodeSize) const {
 | |
|   DocumentFragment::AddSizeOfExcludingThis(aSizes, aNodeSize);
 | |
|   DocumentOrShadowRoot::AddSizeOfExcludingThis(aSizes);
 | |
|   aSizes.mLayoutShadowDomAuthorStyles += Servo_AuthorStyles_SizeOfIncludingThis(
 | |
|       ShadowRootAuthorStylesMallocSizeOf,
 | |
|       ShadowRootAuthorStylesMallocEnclosingSizeOf, mServoStyles.get());
 | |
| }
 | |
| 
 | |
| JSObject* ShadowRoot::WrapObject(JSContext* aCx,
 | |
|                                  JS::Handle<JSObject*> aGivenProto) {
 | |
|   return mozilla::dom::ShadowRoot_Binding::Wrap(aCx, this, aGivenProto);
 | |
| }
 | |
| 
 | |
| void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) {
 | |
|   size_t sheetCount = aOther->SheetCount();
 | |
|   for (size_t i = 0; i < sheetCount; ++i) {
 | |
|     StyleSheet* sheet = aOther->SheetAt(i);
 | |
|     if (sheet->IsApplicable()) {
 | |
|       RefPtr<StyleSheet> clonedSheet =
 | |
|           sheet->Clone(nullptr, nullptr, this, nullptr);
 | |
|       if (clonedSheet) {
 | |
|         AppendStyleSheet(*clonedSheet.get());
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult ShadowRoot::Bind() {
 | |
|   MOZ_ASSERT(!IsInComposedDoc(), "Forgot to unbind?");
 | |
|   if (Host()->IsInComposedDoc()) {
 | |
|     SetIsConnected(true);
 | |
|     OwnerDoc()->AddComposedDocShadowRoot(*this);
 | |
|   }
 | |
| 
 | |
|   for (nsIContent* child = GetFirstChild(); child;
 | |
|        child = child->GetNextSibling()) {
 | |
|     nsresult rv = child->BindToTree(nullptr, this, Host());
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void ShadowRoot::Unbind() {
 | |
|   if (IsInComposedDoc()) {
 | |
|     SetIsConnected(false);
 | |
|     OwnerDoc()->RemoveComposedDocShadowRoot(*this);
 | |
|   }
 | |
| 
 | |
|   for (nsIContent* child = GetFirstChild(); child;
 | |
|        child = child->GetNextSibling()) {
 | |
|     child->UnbindFromTree(true, false);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ShadowRoot::Unattach() {
 | |
|   MOZ_ASSERT(!HasSlots(), "Won't work!");
 | |
|   if (!GetHost()) {
 | |
|     // It is possible that we've been unlinked already. In such case host
 | |
|     // should have called Unbind and ShadowRoot's own unlink
 | |
|     // RemoveMutationObserver.
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   Unbind();
 | |
|   GetHost()->RemoveMutationObserver(this);
 | |
|   SetHost(nullptr);
 | |
| }
 | |
| 
 | |
| void ShadowRoot::InvalidateStyleAndLayoutOnSubtree(Element* aElement) {
 | |
|   MOZ_ASSERT(aElement);
 | |
|   Document* doc = GetComposedDoc();
 | |
|   if (!doc) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsIPresShell* shell = doc->GetShell();
 | |
|   if (!shell) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   shell->DestroyFramesForAndRestyle(aElement);
 | |
| }
 | |
| 
 | |
| void ShadowRoot::AddSlot(HTMLSlotElement* aSlot) {
 | |
|   MOZ_ASSERT(aSlot);
 | |
| 
 | |
|   // Note that if name attribute missing, the slot is a default slot.
 | |
|   nsAutoString name;
 | |
|   aSlot->GetName(name);
 | |
| 
 | |
|   nsTArray<HTMLSlotElement*>* currentSlots = mSlotMap.LookupOrAdd(name);
 | |
|   MOZ_ASSERT(currentSlots);
 | |
| 
 | |
|   HTMLSlotElement* oldSlot = currentSlots->SafeElementAt(0);
 | |
| 
 | |
|   TreeOrderComparator comparator;
 | |
|   currentSlots->InsertElementSorted(aSlot, comparator);
 | |
| 
 | |
|   HTMLSlotElement* currentSlot = currentSlots->ElementAt(0);
 | |
|   if (currentSlot != aSlot) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (oldSlot && oldSlot != currentSlot) {
 | |
|     // Move assigned nodes from old slot to new slot.
 | |
|     InvalidateStyleAndLayoutOnSubtree(oldSlot);
 | |
|     const nsTArray<RefPtr<nsINode>>& assignedNodes = oldSlot->AssignedNodes();
 | |
|     bool doEnqueueSlotChange = false;
 | |
|     while (assignedNodes.Length() > 0) {
 | |
|       nsINode* assignedNode = assignedNodes[0];
 | |
| 
 | |
|       oldSlot->RemoveAssignedNode(assignedNode);
 | |
|       currentSlot->AppendAssignedNode(assignedNode);
 | |
|       doEnqueueSlotChange = true;
 | |
|     }
 | |
| 
 | |
|     if (doEnqueueSlotChange) {
 | |
|       oldSlot->EnqueueSlotChangeEvent();
 | |
|       currentSlot->EnqueueSlotChangeEvent();
 | |
|       SlotStateChanged(oldSlot);
 | |
|       SlotStateChanged(currentSlot);
 | |
|     }
 | |
|   } else {
 | |
|     bool doEnqueueSlotChange = false;
 | |
|     // Otherwise add appropriate nodes to this slot from the host.
 | |
|     for (nsIContent* child = GetHost()->GetFirstChild(); child;
 | |
|          child = child->GetNextSibling()) {
 | |
|       nsAutoString slotName;
 | |
|       if (child->IsElement()) {
 | |
|         child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::slot,
 | |
|                                     slotName);
 | |
|       }
 | |
|       if (!child->IsSlotable() || !slotName.Equals(name)) {
 | |
|         continue;
 | |
|       }
 | |
|       doEnqueueSlotChange = true;
 | |
|       currentSlot->AppendAssignedNode(child);
 | |
|     }
 | |
| 
 | |
|     if (doEnqueueSlotChange) {
 | |
|       currentSlot->EnqueueSlotChangeEvent();
 | |
|       SlotStateChanged(currentSlot);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) {
 | |
|   MOZ_ASSERT(aSlot);
 | |
| 
 | |
|   nsAutoString name;
 | |
|   aSlot->GetName(name);
 | |
| 
 | |
|   SlotArray* currentSlots = mSlotMap.Get(name);
 | |
|   MOZ_DIAGNOSTIC_ASSERT(currentSlots && currentSlots->Contains(aSlot),
 | |
|                         "Slot to deregister wasn't found?");
 | |
|   if (currentSlots->Length() == 1) {
 | |
|     MOZ_ASSERT(currentSlots->ElementAt(0) == aSlot);
 | |
| 
 | |
|     InvalidateStyleAndLayoutOnSubtree(aSlot);
 | |
| 
 | |
|     mSlotMap.Remove(name);
 | |
|     if (!aSlot->AssignedNodes().IsEmpty()) {
 | |
|       aSlot->ClearAssignedNodes();
 | |
|       aSlot->EnqueueSlotChangeEvent();
 | |
|     }
 | |
| 
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const bool wasFirstSlot = currentSlots->ElementAt(0) == aSlot;
 | |
|   currentSlots->RemoveElement(aSlot);
 | |
| 
 | |
|   // Move assigned nodes from removed slot to the next slot in
 | |
|   // tree order with the same name.
 | |
|   if (!wasFirstSlot) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   InvalidateStyleAndLayoutOnSubtree(aSlot);
 | |
|   HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
 | |
|   const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
 | |
|   bool slottedNodesChanged = !assignedNodes.IsEmpty();
 | |
|   while (!assignedNodes.IsEmpty()) {
 | |
|     nsINode* assignedNode = assignedNodes[0];
 | |
| 
 | |
|     aSlot->RemoveAssignedNode(assignedNode);
 | |
|     replacementSlot->AppendAssignedNode(assignedNode);
 | |
|   }
 | |
| 
 | |
|   if (slottedNodesChanged) {
 | |
|     aSlot->EnqueueSlotChangeEvent();
 | |
|     replacementSlot->EnqueueSlotChangeEvent();
 | |
|   }
 | |
| }
 | |
| 
 | |
| // FIXME(emilio): There's a bit of code duplication between this and the
 | |
| // equivalent ServoStyleSet methods, it'd be nice to not duplicate it...
 | |
| void ShadowRoot::RuleAdded(StyleSheet& aSheet, css::Rule& aRule) {
 | |
|   if (!aSheet.IsApplicable()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(mServoStyles);
 | |
|   if (mStyleRuleMap) {
 | |
|     mStyleRuleMap->RuleAdded(aSheet, aRule);
 | |
|   }
 | |
|   Servo_AuthorStyles_ForceDirty(mServoStyles.get());
 | |
|   ApplicableRulesChanged();
 | |
| }
 | |
| 
 | |
| void ShadowRoot::RuleRemoved(StyleSheet& aSheet, css::Rule& aRule) {
 | |
|   if (!aSheet.IsApplicable()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(mServoStyles);
 | |
|   if (mStyleRuleMap) {
 | |
|     mStyleRuleMap->RuleRemoved(aSheet, aRule);
 | |
|   }
 | |
|   Servo_AuthorStyles_ForceDirty(mServoStyles.get());
 | |
|   ApplicableRulesChanged();
 | |
| }
 | |
| 
 | |
| void ShadowRoot::RuleChanged(StyleSheet& aSheet, css::Rule*) {
 | |
|   if (!aSheet.IsApplicable()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(mServoStyles);
 | |
|   Servo_AuthorStyles_ForceDirty(mServoStyles.get());
 | |
|   ApplicableRulesChanged();
 | |
| }
 | |
| 
 | |
| void ShadowRoot::ApplicableRulesChanged() {
 | |
|   Document* doc = GetComposedDoc();
 | |
|   if (!doc) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (nsIPresShell* shell = doc->GetShell()) {
 | |
|     shell->RecordShadowStyleChange(*this);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ShadowRoot::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
 | |
|   DocumentOrShadowRoot::InsertSheetAt(aIndex, aSheet);
 | |
|   if (aSheet.IsApplicable()) {
 | |
|     InsertSheetIntoAuthorData(aIndex, aSheet);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ShadowRoot::InsertSheetIntoAuthorData(size_t aIndex, StyleSheet& aSheet) {
 | |
|   MOZ_ASSERT(SheetAt(aIndex) == &aSheet);
 | |
|   MOZ_ASSERT(aSheet.IsApplicable());
 | |
| 
 | |
|   if (!mServoStyles) {
 | |
|     mServoStyles.reset(Servo_AuthorStyles_Create());
 | |
|   }
 | |
| 
 | |
|   if (mStyleRuleMap) {
 | |
|     mStyleRuleMap->SheetAdded(aSheet);
 | |
|   }
 | |
| 
 | |
|   for (size_t i = aIndex + 1; i < SheetCount(); ++i) {
 | |
|     StyleSheet* beforeSheet = SheetAt(i);
 | |
|     if (!beforeSheet->IsApplicable()) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles.get(), &aSheet,
 | |
|                                               beforeSheet);
 | |
|     ApplicableRulesChanged();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
 | |
|   ApplicableRulesChanged();
 | |
| }
 | |
| 
 | |
| // FIXME(emilio): This needs to notify document observers and such,
 | |
| // presumably.
 | |
| void ShadowRoot::StyleSheetApplicableStateChanged(StyleSheet& aSheet,
 | |
|                                                   bool aApplicable) {
 | |
|   int32_t index = IndexOfSheet(aSheet);
 | |
|   if (index < 0) {
 | |
|     // NOTE(emilio): @import sheets are handled in the relevant RuleAdded
 | |
|     // notification, which only notifies after the sheet is loaded.
 | |
|     //
 | |
|     // This setup causes weirdness in other places, we may want to fix this in
 | |
|     // bug 1465031.
 | |
|     MOZ_DIAGNOSTIC_ASSERT(aSheet.GetParentSheet(),
 | |
|                           "It'd better be an @import sheet");
 | |
|     return;
 | |
|   }
 | |
|   if (aApplicable) {
 | |
|     InsertSheetIntoAuthorData(size_t(index), aSheet);
 | |
|   } else {
 | |
|     MOZ_ASSERT(mServoStyles);
 | |
|     if (mStyleRuleMap) {
 | |
|       mStyleRuleMap->SheetRemoved(aSheet);
 | |
|     }
 | |
|     Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
 | |
|     ApplicableRulesChanged();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ShadowRoot::RemoveSheet(StyleSheet* aSheet) {
 | |
|   MOZ_ASSERT(aSheet);
 | |
|   RefPtr<StyleSheet> sheet = DocumentOrShadowRoot::RemoveSheet(*aSheet);
 | |
|   MOZ_ASSERT(sheet);
 | |
|   if (sheet->IsApplicable()) {
 | |
|     MOZ_ASSERT(mServoStyles);
 | |
|     if (mStyleRuleMap) {
 | |
|       mStyleRuleMap->SheetRemoved(*sheet);
 | |
|     }
 | |
|     Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), sheet);
 | |
|     ApplicableRulesChanged();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ShadowRoot::AddToIdTable(Element* aElement, nsAtom* aId) {
 | |
|   IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
 | |
|   if (entry) {
 | |
|     entry->AddIdElement(aElement);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ShadowRoot::RemoveFromIdTable(Element* aElement, nsAtom* aId) {
 | |
|   IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
 | |
|   if (entry) {
 | |
|     entry->RemoveIdElement(aElement);
 | |
|     if (entry->IsEmpty()) {
 | |
|       mIdentifierMap.RemoveEntry(entry);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
 | |
|   aVisitor.mCanHandle = true;
 | |
|   aVisitor.mRootOfClosedTree = IsClosed();
 | |
|   // Inform that we're about to exit the current scope.
 | |
|   aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
 | |
| 
 | |
|   // https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
 | |
|   if (!aVisitor.mEvent->mFlags.mComposed) {
 | |
|     nsCOMPtr<nsIContent> originalTarget =
 | |
|         do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
 | |
|     if (originalTarget && originalTarget->GetContainingShadow() == this) {
 | |
|       // If we do stop propagation, we still want to propagate
 | |
|       // the event to chrome (nsPIDOMWindow::GetParentTarget()).
 | |
|       // The load event is special in that we don't ever propagate it
 | |
|       // to chrome.
 | |
|       nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
 | |
|       EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
 | |
|                                       ? win->GetParentTarget()
 | |
|                                       : nullptr;
 | |
| 
 | |
|       aVisitor.SetParentTarget(parentTarget, true);
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsIContent* shadowHost = GetHost();
 | |
|   aVisitor.SetParentTarget(shadowHost, false);
 | |
| 
 | |
|   nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
 | |
|   if (content && content->GetBindingParent() == shadowHost) {
 | |
|     aVisitor.mEventTargetAtParent = shadowHost;
 | |
|   }
 | |
| }
 | |
| 
 | |
| ShadowRoot::SlotAssignment ShadowRoot::SlotAssignmentFor(nsIContent* aContent) {
 | |
|   nsAutoString slotName;
 | |
|   // Note that if slot attribute is missing, assign it to the first default
 | |
|   // slot, if exists.
 | |
|   if (aContent->IsElement()) {
 | |
|     aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::slot,
 | |
|                                    slotName);
 | |
|   }
 | |
| 
 | |
|   nsTArray<HTMLSlotElement*>* slots = mSlotMap.Get(slotName);
 | |
|   if (!slots) {
 | |
|     return {};
 | |
|   }
 | |
| 
 | |
|   HTMLSlotElement* slot = slots->ElementAt(0);
 | |
|   MOZ_ASSERT(slot);
 | |
| 
 | |
|   // Find the appropriate position in the assigned node list for the
 | |
|   // newly assigned content.
 | |
|   const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
 | |
|   nsIContent* currentContent = GetHost()->GetFirstChild();
 | |
|   Maybe<uint32_t> insertionIndex;
 | |
|   for (uint32_t i = 0; i < assignedNodes.Length(); i++) {
 | |
|     // Seek through the host's explicit children until the
 | |
|     // assigned content is found.
 | |
|     while (currentContent && currentContent != assignedNodes[i]) {
 | |
|       if (currentContent == aContent) {
 | |
|         insertionIndex.emplace(i);
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       currentContent = currentContent->GetNextSibling();
 | |
|     }
 | |
| 
 | |
|     if (insertionIndex) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return {slot, insertionIndex};
 | |
| }
 | |
| 
 | |
| void ShadowRoot::MaybeReassignElement(Element* aElement) {
 | |
|   MOZ_ASSERT(aElement->GetParent() == GetHost());
 | |
|   HTMLSlotElement* oldSlot = aElement->GetAssignedSlot();
 | |
|   SlotAssignment assignment = SlotAssignmentFor(aElement);
 | |
| 
 | |
|   if (assignment.mSlot == oldSlot) {
 | |
|     // Nothing to do here.
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (Document* doc = GetComposedDoc()) {
 | |
|     if (nsIPresShell* shell = doc->GetShell()) {
 | |
|       shell->SlotAssignmentWillChange(*aElement, oldSlot, assignment.mSlot);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (oldSlot) {
 | |
|     oldSlot->RemoveAssignedNode(aElement);
 | |
|     oldSlot->EnqueueSlotChangeEvent();
 | |
|   }
 | |
| 
 | |
|   if (assignment.mSlot) {
 | |
|     if (assignment.mIndex) {
 | |
|       assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aElement);
 | |
|     } else {
 | |
|       assignment.mSlot->AppendAssignedNode(aElement);
 | |
|     }
 | |
|     assignment.mSlot->EnqueueSlotChangeEvent();
 | |
|   }
 | |
| 
 | |
|   SlotStateChanged(oldSlot);
 | |
|   SlotStateChanged(assignment.mSlot);
 | |
| }
 | |
| 
 | |
| Element* ShadowRoot::GetActiveElement() {
 | |
|   return GetRetargetedFocusedElement();
 | |
| }
 | |
| 
 | |
| void ShadowRoot::GetInnerHTML(nsAString& aInnerHTML) {
 | |
|   GetMarkup(false, aInnerHTML);
 | |
| }
 | |
| 
 | |
| void ShadowRoot::SetInnerHTML(const nsAString& aInnerHTML,
 | |
|                               ErrorResult& aError) {
 | |
|   SetInnerHTMLInternal(aInnerHTML, aError);
 | |
| }
 | |
| 
 | |
| nsINode* ShadowRoot::ImportNodeAndAppendChildAt(nsINode& aParentNode,
 | |
|                                                 nsINode& aNode, bool aDeep,
 | |
|                                                 mozilla::ErrorResult& rv) {
 | |
|   MOZ_ASSERT(mIsUAWidget);
 | |
| 
 | |
|   if (!aParentNode.IsInUAWidget()) {
 | |
|     rv.Throw(NS_ERROR_INVALID_ARG);
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<nsINode> node = OwnerDoc()->ImportNode(aNode, aDeep, rv);
 | |
|   if (rv.Failed()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return aParentNode.AppendChild(*node, rv);
 | |
| }
 | |
| 
 | |
| nsINode* ShadowRoot::CreateElementAndAppendChildAt(nsINode& aParentNode,
 | |
|                                                    const nsAString& aTagName,
 | |
|                                                    mozilla::ErrorResult& rv) {
 | |
|   MOZ_ASSERT(mIsUAWidget);
 | |
|   MOZ_ASSERT(OwnerDoc());
 | |
| 
 | |
|   if (!aParentNode.IsInUAWidget()) {
 | |
|     rv.Throw(NS_ERROR_INVALID_ARG);
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // This option is not exposed to UA Widgets
 | |
|   ElementCreationOptionsOrString options;
 | |
| 
 | |
|   RefPtr<nsINode> node = OwnerDoc()->CreateElement(aTagName, options, rv);
 | |
|   if (rv.Failed()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return aParentNode.AppendChild(*node, rv);
 | |
| }
 | |
| 
 | |
| void ShadowRoot::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
 | |
|                                   nsAtom* aAttribute, int32_t aModType,
 | |
|                                   const nsAttrValue* aOldValue) {
 | |
|   if (aNameSpaceID != kNameSpaceID_None || aAttribute != nsGkAtoms::slot) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (aElement->GetParent() != GetHost()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MaybeReassignElement(aElement);
 | |
| }
 | |
| 
 | |
| void ShadowRoot::ContentAppended(nsIContent* aFirstNewContent) {
 | |
|   for (nsIContent* content = aFirstNewContent; content;
 | |
|        content = content->GetNextSibling()) {
 | |
|     ContentInserted(content);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ShadowRoot::ContentInserted(nsIContent* aChild) {
 | |
|   // Check to ensure that the child not an anonymous subtree root because
 | |
|   // even though its parent could be the host it may not be in the host's child
 | |
|   // list.
 | |
|   if (aChild->IsRootOfAnonymousSubtree()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!aChild->IsSlotable()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (aChild->GetParent() == GetHost()) {
 | |
|     SlotAssignment assignment = SlotAssignmentFor(aChild);
 | |
|     if (!assignment.mSlot) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // Fallback content will go away, let layout know.
 | |
|     if (assignment.mSlot->AssignedNodes().IsEmpty()) {
 | |
|       InvalidateStyleAndLayoutOnSubtree(assignment.mSlot);
 | |
|     }
 | |
| 
 | |
|     if (assignment.mIndex) {
 | |
|       assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aChild);
 | |
|     } else {
 | |
|       assignment.mSlot->AppendAssignedNode(aChild);
 | |
|     }
 | |
|     assignment.mSlot->EnqueueSlotChangeEvent();
 | |
| 
 | |
|     SlotStateChanged(assignment.mSlot);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // If parent's root is a shadow root, and parent is a slot whose assigned
 | |
|   // nodes is the empty list, then run signal a slot change for parent.
 | |
|   HTMLSlotElement* slot = HTMLSlotElement::FromNodeOrNull(aChild->GetParent());
 | |
|   if (slot && slot->GetContainingShadow() == this &&
 | |
|       slot->AssignedNodes().IsEmpty()) {
 | |
|     slot->EnqueueSlotChangeEvent();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ShadowRoot::ContentRemoved(nsIContent* aChild,
 | |
|                                 nsIContent* aPreviousSibling) {
 | |
|   // Check to ensure that the child not an anonymous subtree root because
 | |
|   // even though its parent could be the host it may not be in the host's child
 | |
|   // list.
 | |
|   if (aChild->IsRootOfAnonymousSubtree()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!aChild->IsSlotable()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (aChild->GetParent() == GetHost()) {
 | |
|     if (HTMLSlotElement* slot = aChild->GetAssignedSlot()) {
 | |
|       // If the slot is going to start showing fallback content, we need to tell
 | |
|       // layout about it.
 | |
|       if (slot->AssignedNodes().Length() == 1) {
 | |
|         InvalidateStyleAndLayoutOnSubtree(slot);
 | |
|       }
 | |
|       slot->RemoveAssignedNode(aChild);
 | |
|       slot->EnqueueSlotChangeEvent();
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // If parent's root is a shadow root, and parent is a slot whose assigned
 | |
|   // nodes is the empty list, then run signal a slot change for parent.
 | |
|   HTMLSlotElement* slot = HTMLSlotElement::FromNodeOrNull(aChild->GetParent());
 | |
|   if (slot && slot->GetContainingShadow() == this &&
 | |
|       slot->AssignedNodes().IsEmpty()) {
 | |
|     slot->EnqueueSlotChangeEvent();
 | |
|   }
 | |
| }
 | |
| 
 | |
| ServoStyleRuleMap& ShadowRoot::ServoStyleRuleMap() {
 | |
|   if (!mStyleRuleMap) {
 | |
|     mStyleRuleMap = MakeUnique<mozilla::ServoStyleRuleMap>();
 | |
|   }
 | |
|   mStyleRuleMap->EnsureTable(*this);
 | |
|   return *mStyleRuleMap;
 | |
| }
 | |
| 
 | |
| nsresult ShadowRoot::Clone(dom::NodeInfo* aNodeInfo, nsINode** aResult) const {
 | |
|   *aResult = nullptr;
 | |
|   return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
 | |
| }
 | 
