|
Packit |
f0b94e |
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
Packit |
f0b94e |
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
Packit |
f0b94e |
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
Packit |
f0b94e |
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
Packit |
f0b94e |
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
#include "mozilla/Preferences.h"
|
|
Packit |
f0b94e |
#include "mozilla/dom/ShadowRoot.h"
|
|
Packit |
f0b94e |
#include "mozilla/dom/ShadowRootBinding.h"
|
|
Packit |
f0b94e |
#include "mozilla/dom/DocumentFragment.h"
|
|
Packit |
f0b94e |
#include "ChildIterator.h"
|
|
Packit |
f0b94e |
#include "nsContentUtils.h"
|
|
Packit |
f0b94e |
#include "nsDOMClassInfoID.h"
|
|
Packit |
f0b94e |
#include "nsIStyleSheetLinkingElement.h"
|
|
Packit |
f0b94e |
#include "mozilla/dom/Element.h"
|
|
Packit |
f0b94e |
#include "mozilla/dom/HTMLSlotElement.h"
|
|
Packit |
f0b94e |
#include "nsXBLPrototypeBinding.h"
|
|
Packit |
f0b94e |
#include "mozilla/EventDispatcher.h"
|
|
Packit |
f0b94e |
#include "mozilla/StyleSheet.h"
|
|
Packit |
f0b94e |
#include "mozilla/StyleSheetInlines.h"
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
using namespace mozilla;
|
|
Packit |
f0b94e |
using namespace mozilla::dom;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
NS_IMPL_CYCLE_COLLECTION_CLASS(ShadowRoot)
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ShadowRoot, DocumentFragment)
|
|
Packit |
f0b94e |
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMStyleSheets)
|
|
Packit |
f0b94e |
for (auto iter = tmp->mIdentifierMap.ConstIter(); !iter.Done(); iter.Next()) {
|
|
Packit |
f0b94e |
iter.Get()->Traverse(&cb;;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ShadowRoot)
|
|
Packit |
f0b94e |
if (tmp->GetHost()) {
|
|
Packit |
f0b94e |
tmp->GetHost()->RemoveMutationObserver(tmp);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
|
|
Packit |
f0b94e |
tmp->mIdentifierMap.Clear();
|
|
Packit |
f0b94e |
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DocumentFragment)
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ShadowRoot)
|
|
Packit |
f0b94e |
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
|
|
Packit |
f0b94e |
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
|
Packit |
f0b94e |
NS_INTERFACE_MAP_END_INHERITING(DocumentFragment)
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
|
|
Packit |
f0b94e |
NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ShadowRoot::ShadowRoot(Element* aElement, ShadowRootMode aMode,
|
|
Packit |
f0b94e |
already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
|
|
Packit |
f0b94e |
: DocumentFragment(aNodeInfo),
|
|
Packit |
f0b94e |
DocumentOrShadowRoot(*this),
|
|
Packit |
f0b94e |
mMode(aMode),
|
|
Packit |
f0b94e |
mServoStyles(Servo_AuthorStyles_Create()),
|
|
Packit |
f0b94e |
mIsComposedDocParticipant(false) {
|
|
Packit |
f0b94e |
SetHost(aElement);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Nodes in a shadow tree should never store a value
|
|
Packit |
f0b94e |
// in the subtree root pointer, nodes in the shadow tree
|
|
Packit |
f0b94e |
// track the subtree root using GetContainingShadow().
|
|
Packit |
f0b94e |
ClearSubtreeRootPointer();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
SetFlags(NODE_IS_IN_SHADOW_TREE);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ExtendedDOMSlots()->mBindingParent = aElement;
|
|
Packit |
f0b94e |
ExtendedDOMSlots()->mContainingShadow = this;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Add the ShadowRoot as a mutation observer on the host to watch
|
|
Packit |
f0b94e |
// for mutations because the insertion points in this ShadowRoot
|
|
Packit |
f0b94e |
// may need to be updated when the host children are modified.
|
|
Packit |
f0b94e |
GetHost()->AddMutationObserver(this);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ShadowRoot::~ShadowRoot() {
|
|
Packit |
f0b94e |
if (auto* host = GetHost()) {
|
|
Packit |
f0b94e |
// mHost may have been unlinked.
|
|
Packit |
f0b94e |
host->RemoveMutationObserver(this);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
UnsetFlags(NODE_IS_IN_SHADOW_TREE);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// nsINode destructor expects mSubtreeRoot == this.
|
|
Packit |
f0b94e |
SetSubtreeRootPointer(this);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
JSObject* ShadowRoot::WrapObject(JSContext* aCx,
|
|
Packit |
f0b94e |
JS::Handle<JSObject*> aGivenProto) {
|
|
Packit |
f0b94e |
return mozilla::dom::ShadowRootBinding::Wrap(aCx, this, aGivenProto);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) {
|
|
Packit |
f0b94e |
size_t sheetCount = aOther->SheetCount();
|
|
Packit |
f0b94e |
for (size_t i = 0; i < sheetCount; ++i) {
|
|
Packit |
f0b94e |
StyleSheet* sheet = aOther->SheetAt(i);
|
|
Packit |
f0b94e |
if (sheet->IsApplicable()) {
|
|
Packit |
f0b94e |
RefPtr<StyleSheet> clonedSheet =
|
|
Packit |
f0b94e |
sheet->Clone(nullptr, nullptr, nullptr, nullptr);
|
|
Packit |
f0b94e |
if (clonedSheet) {
|
|
Packit |
f0b94e |
AppendStyleSheet(*clonedSheet.get());
|
|
Packit |
f0b94e |
Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(),
|
|
Packit |
f0b94e |
clonedSheet->AsServo());
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::AddSlot(HTMLSlotElement* aSlot) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(aSlot);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Note that if name attribute missing, the slot is a default slot.
|
|
Packit |
f0b94e |
nsAutoString name;
|
|
Packit |
f0b94e |
aSlot->GetName(name);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsTArray<HTMLSlotElement*>* currentSlots = mSlotMap.LookupOrAdd(name);
|
|
Packit |
f0b94e |
MOZ_ASSERT(currentSlots);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
HTMLSlotElement* oldSlot = currentSlots->SafeElementAt(0);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
TreeOrderComparator comparator;
|
|
Packit |
f0b94e |
currentSlots->InsertElementSorted(aSlot, comparator);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
HTMLSlotElement* currentSlot = currentSlots->ElementAt(0);
|
|
Packit |
f0b94e |
if (currentSlot != aSlot) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
bool doEnqueueSlotChange = false;
|
|
Packit |
f0b94e |
if (oldSlot && oldSlot != currentSlot) {
|
|
Packit |
f0b94e |
// Move assigned nodes from old slot to new slot.
|
|
Packit |
f0b94e |
const nsTArray<RefPtr<nsINode>>& assignedNodes = oldSlot->AssignedNodes();
|
|
Packit |
f0b94e |
while (assignedNodes.Length() > 0) {
|
|
Packit |
f0b94e |
nsINode* assignedNode = assignedNodes[0];
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
oldSlot->RemoveAssignedNode(assignedNode);
|
|
Packit |
f0b94e |
currentSlot->AppendAssignedNode(assignedNode);
|
|
Packit |
f0b94e |
doEnqueueSlotChange = true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (doEnqueueSlotChange) {
|
|
Packit |
f0b94e |
oldSlot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
currentSlot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
// Otherwise add appropriate nodes to this slot from the host.
|
|
Packit |
f0b94e |
for (nsIContent* child = GetHost()->GetFirstChild(); child;
|
|
Packit |
f0b94e |
child = child->GetNextSibling()) {
|
|
Packit |
f0b94e |
nsAutoString slotName;
|
|
Packit |
f0b94e |
if (child->IsElement()) {
|
|
Packit |
f0b94e |
child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::slot,
|
|
Packit |
f0b94e |
slotName);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
if (child->IsSlotable() && slotName.Equals(name)) {
|
|
Packit |
f0b94e |
currentSlot->AppendAssignedNode(child);
|
|
Packit |
f0b94e |
doEnqueueSlotChange = true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (doEnqueueSlotChange) {
|
|
Packit |
f0b94e |
currentSlot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(aSlot);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsAutoString name;
|
|
Packit |
f0b94e |
aSlot->GetName(name);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
SlotArray* currentSlots = mSlotMap.Get(name);
|
|
Packit |
f0b94e |
MOZ_DIAGNOSTIC_ASSERT(currentSlots && currentSlots->Contains(aSlot),
|
|
Packit |
f0b94e |
"Slot to deregister wasn't found?");
|
|
Packit |
f0b94e |
if (currentSlots->Length() == 1) {
|
|
Packit |
f0b94e |
MOZ_ASSERT(currentSlots->ElementAt(0) == aSlot);
|
|
Packit |
f0b94e |
mSlotMap.Remove(name);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!aSlot->AssignedNodes().IsEmpty()) {
|
|
Packit |
f0b94e |
aSlot->ClearAssignedNodes();
|
|
Packit |
f0b94e |
aSlot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const bool wasFirstSlot = currentSlots->ElementAt(0) == aSlot;
|
|
Packit |
f0b94e |
currentSlots->RemoveElement(aSlot);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Move assigned nodes from removed slot to the next slot in
|
|
Packit |
f0b94e |
// tree order with the same name.
|
|
Packit |
f0b94e |
if (!wasFirstSlot) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
|
|
Packit |
f0b94e |
const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
|
|
Packit |
f0b94e |
bool slottedNodesChanged = !assignedNodes.IsEmpty();
|
|
Packit |
f0b94e |
while (!assignedNodes.IsEmpty()) {
|
|
Packit |
f0b94e |
nsINode* assignedNode = assignedNodes[0];
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
aSlot->RemoveAssignedNode(assignedNode);
|
|
Packit |
f0b94e |
replacementSlot->AppendAssignedNode(assignedNode);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (slottedNodesChanged) {
|
|
Packit |
f0b94e |
aSlot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
replacementSlot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::StyleSheetChanged() {
|
|
Packit |
f0b94e |
// FIXME(emilio): This is not needed to handle sheet additions / removals,
|
|
Packit |
f0b94e |
// only for CSSOM mutations, we should distinguish both.
|
|
Packit |
f0b94e |
Servo_AuthorStyles_ForceDirty(mServoStyles.get());
|
|
Packit |
f0b94e |
// FIXME(emilio): Similarly, we should notify of the particular mutation to
|
|
Packit |
f0b94e |
// the rule map, instead of this...
|
|
Packit |
f0b94e |
mStyleRuleMap.reset(nullptr);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsIDocument* doc = OwnerDoc();
|
|
Packit |
f0b94e |
if (nsIPresShell* shell = doc->GetShell()) {
|
|
Packit |
f0b94e |
doc->BeginUpdate(UPDATE_STYLE);
|
|
Packit |
f0b94e |
shell->RecordShadowStyleChange(*this);
|
|
Packit |
f0b94e |
doc->EndUpdate(UPDATE_STYLE);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::InsertSheet(StyleSheet* aSheet, nsIContent* aLinkingContent) {
|
|
Packit |
f0b94e |
nsCOMPtr<nsIStyleSheetLinkingElement> linkingElement =
|
|
Packit |
f0b94e |
do_QueryInterface(aLinkingContent);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// FIXME(emilio, bug 1410578): <link> should probably also be allowed here.
|
|
Packit |
f0b94e |
MOZ_ASSERT(linkingElement,
|
|
Packit |
f0b94e |
"The only styles in a ShadowRoot should come "
|
|
Packit |
f0b94e |
"from <style>.");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
linkingElement->SetStyleSheet(
|
|
Packit |
f0b94e |
aSheet); // This sets the ownerNode on the sheet
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Find the correct position to insert into the style sheet list (must
|
|
Packit |
f0b94e |
// be in tree order).
|
|
Packit |
f0b94e |
for (size_t i = 0; i <= SheetCount(); i++) {
|
|
Packit |
f0b94e |
if (i == SheetCount()) {
|
|
Packit |
f0b94e |
AppendStyleSheet(*aSheet);
|
|
Packit |
f0b94e |
Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(),
|
|
Packit |
f0b94e |
aSheet->AsServo());
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
StyleSheet* sheet = SheetAt(i);
|
|
Packit |
f0b94e |
nsINode* sheetOwningNode = sheet->GetOwnerNode();
|
|
Packit |
f0b94e |
if (nsContentUtils::PositionIsBefore(aLinkingContent, sheetOwningNode)) {
|
|
Packit |
f0b94e |
InsertSheetAt(i, *aSheet);
|
|
Packit |
f0b94e |
Servo_AuthorStyles_InsertStyleSheetBefore(
|
|
Packit |
f0b94e |
mServoStyles.get(), aSheet->AsServo(), sheet->AsServo());
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aSheet->IsApplicable()) {
|
|
Packit |
f0b94e |
StyleSheetChanged();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::RemoveSheet(StyleSheet* aSheet) {
|
|
Packit |
f0b94e |
DocumentOrShadowRoot::RemoveSheet(*aSheet);
|
|
Packit |
f0b94e |
Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), aSheet->AsServo());
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aSheet->IsApplicable()) {
|
|
Packit |
f0b94e |
StyleSheetChanged();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::AddToIdTable(Element* aElement, nsAtom* aId) {
|
|
Packit |
f0b94e |
nsIdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
|
|
Packit |
f0b94e |
if (entry) {
|
|
Packit |
f0b94e |
entry->AddIdElement(aElement);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::RemoveFromIdTable(Element* aElement, nsAtom* aId) {
|
|
Packit |
f0b94e |
nsIdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
|
|
Packit |
f0b94e |
if (entry) {
|
|
Packit |
f0b94e |
entry->RemoveIdElement(aElement);
|
|
Packit |
f0b94e |
if (entry->IsEmpty()) {
|
|
Packit |
f0b94e |
mIdentifierMap.RemoveEntry(entry);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsresult ShadowRoot::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
|
|
Packit |
f0b94e |
aVisitor.mCanHandle = true;
|
|
Packit |
f0b94e |
aVisitor.mRootOfClosedTree = IsClosed();
|
|
Packit |
f0b94e |
// Inform that we're about to exit the current scope.
|
|
Packit |
f0b94e |
aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// https://dom.spec.whatwg.org/#ref-for-get-the-parent%E2%91%A6
|
|
Packit |
f0b94e |
if (!aVisitor.mEvent->mFlags.mComposed) {
|
|
Packit |
f0b94e |
nsCOMPtr<nsIContent> originalTarget =
|
|
Packit |
f0b94e |
do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
|
|
Packit |
f0b94e |
if (originalTarget->GetContainingShadow() == this) {
|
|
Packit |
f0b94e |
// If we do stop propagation, we still want to propagate
|
|
Packit |
f0b94e |
// the event to chrome (nsPIDOMWindow::GetParentTarget()).
|
|
Packit |
f0b94e |
// The load event is special in that we don't ever propagate it
|
|
Packit |
f0b94e |
// to chrome.
|
|
Packit |
f0b94e |
nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
|
|
Packit |
f0b94e |
EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
|
|
Packit |
f0b94e |
? win->GetParentTarget()
|
|
Packit |
f0b94e |
: nullptr;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
aVisitor.SetParentTarget(parentTarget, true);
|
|
Packit |
f0b94e |
return NS_OK;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsIContent* shadowHost = GetHost();
|
|
Packit |
f0b94e |
aVisitor.SetParentTarget(shadowHost, false);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
|
|
Packit |
f0b94e |
if (content && content->GetBindingParent() == shadowHost) {
|
|
Packit |
f0b94e |
aVisitor.mEventTargetAtParent = shadowHost;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return NS_OK;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const HTMLSlotElement* ShadowRoot::AssignSlotFor(nsIContent* aContent) {
|
|
Packit |
f0b94e |
nsAutoString slotName;
|
|
Packit |
f0b94e |
// Note that if slot attribute is missing, assign it to the first default
|
|
Packit |
f0b94e |
// slot, if exists.
|
|
Packit |
f0b94e |
if (aContent->IsElement()) {
|
|
Packit |
f0b94e |
aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::slot,
|
|
Packit |
f0b94e |
slotName);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsTArray<HTMLSlotElement*>* slots = mSlotMap.Get(slotName);
|
|
Packit |
f0b94e |
if (!slots) {
|
|
Packit |
f0b94e |
return nullptr;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
HTMLSlotElement* slot = slots->ElementAt(0);
|
|
Packit |
f0b94e |
MOZ_ASSERT(slot);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Find the appropriate position in the assigned node list for the
|
|
Packit |
f0b94e |
// newly assigned content.
|
|
Packit |
f0b94e |
const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
|
|
Packit |
f0b94e |
nsIContent* currentContent = GetHost()->GetFirstChild();
|
|
Packit |
f0b94e |
Maybe<uint32_t> insertionIndex;
|
|
Packit |
f0b94e |
for (uint32_t i = 0; i < assignedNodes.Length(); i++) {
|
|
Packit |
f0b94e |
// Seek through the host's explicit children until the
|
|
Packit |
f0b94e |
// assigned content is found.
|
|
Packit |
f0b94e |
while (currentContent && currentContent != assignedNodes[i]) {
|
|
Packit |
f0b94e |
if (currentContent == aContent) {
|
|
Packit |
f0b94e |
insertionIndex.emplace(i);
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
currentContent = currentContent->GetNextSibling();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (insertionIndex) {
|
|
Packit |
f0b94e |
break;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (insertionIndex) {
|
|
Packit |
f0b94e |
slot->InsertAssignedNode(*insertionIndex, aContent);
|
|
Packit |
f0b94e |
} else {
|
|
Packit |
f0b94e |
slot->AppendAssignedNode(aContent);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return slot;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const HTMLSlotElement* ShadowRoot::UnassignSlotFor(nsIContent* aNode,
|
|
Packit |
f0b94e |
const nsAString& aSlotName) {
|
|
Packit |
f0b94e |
// Find the insertion point to which the content belongs. Note that if slot
|
|
Packit |
f0b94e |
// attribute is missing, unassign it from the first default slot, if exists.
|
|
Packit |
f0b94e |
nsTArray<HTMLSlotElement*>* slots = mSlotMap.Get(aSlotName);
|
|
Packit |
f0b94e |
if (!slots) {
|
|
Packit |
f0b94e |
return nullptr;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
HTMLSlotElement* slot = slots->ElementAt(0);
|
|
Packit |
f0b94e |
MOZ_ASSERT(slot);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!slot->AssignedNodes().Contains(aNode)) {
|
|
Packit |
f0b94e |
return nullptr;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
slot->RemoveAssignedNode(aNode);
|
|
Packit |
f0b94e |
return slot;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
bool ShadowRoot::MaybeReassignElement(Element* aElement,
|
|
Packit |
f0b94e |
const nsAttrValue* aOldValue) {
|
|
Packit |
f0b94e |
nsIContent* parent = aElement->GetParent();
|
|
Packit |
f0b94e |
if (parent && parent == GetHost()) {
|
|
Packit |
f0b94e |
const HTMLSlotElement* oldSlot = UnassignSlotFor(
|
|
Packit |
f0b94e |
aElement, aOldValue ? aOldValue->GetStringValue() : EmptyString());
|
|
Packit |
f0b94e |
const HTMLSlotElement* newSlot = AssignSlotFor(aElement);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (oldSlot != newSlot) {
|
|
Packit |
f0b94e |
if (oldSlot) {
|
|
Packit |
f0b94e |
oldSlot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
if (newSlot) {
|
|
Packit |
f0b94e |
newSlot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
return true;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
return false;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Element* ShadowRoot::GetActiveElement() {
|
|
Packit |
f0b94e |
return GetRetargetedFocusedElement();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::GetInnerHTML(nsAString& aInnerHTML) {
|
|
Packit |
f0b94e |
GetMarkup(false, aInnerHTML);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::SetInnerHTML(const nsAString& aInnerHTML,
|
|
Packit |
f0b94e |
ErrorResult& aError) {
|
|
Packit |
f0b94e |
SetInnerHTMLInternal(aInnerHTML, aError);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
|
|
Packit |
f0b94e |
nsAtom* aAttribute, int32_t aModType,
|
|
Packit |
f0b94e |
const nsAttrValue* aOldValue) {
|
|
Packit |
f0b94e |
if (aNameSpaceID != kNameSpaceID_None || aAttribute != nsGkAtoms::slot) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// Attributes may change insertion point matching, find its new distribution.
|
|
Packit |
f0b94e |
if (!MaybeReassignElement(aElement, aOldValue)) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!aElement->IsInComposedDoc()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
auto* shell = OwnerDoc()->GetShell();
|
|
Packit |
f0b94e |
if (!shell) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// FIXME(emilio): We could be more granular in a bunch of cases.
|
|
Packit |
f0b94e |
shell->DestroyFramesForAndRestyle(aElement);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::ContentAppended(nsIContent* aFirstNewContent) {
|
|
Packit |
f0b94e |
for (nsIContent* content = aFirstNewContent; content;
|
|
Packit |
f0b94e |
content = content->GetNextSibling()) {
|
|
Packit |
f0b94e |
ContentInserted(content);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::ContentInserted(nsIContent* aChild) {
|
|
Packit |
f0b94e |
// Check to ensure that the child not an anonymous subtree root because
|
|
Packit |
f0b94e |
// even though its parent could be the host it may not be in the host's child
|
|
Packit |
f0b94e |
// list.
|
|
Packit |
f0b94e |
if (aChild->IsRootOfAnonymousSubtree()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!aChild->IsSlotable()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aChild->GetParent() == GetHost()) {
|
|
Packit |
f0b94e |
if (const HTMLSlotElement* slot = AssignSlotFor(aChild)) {
|
|
Packit |
f0b94e |
slot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If parent's root is a shadow root, and parent is a slot whose assigned
|
|
Packit |
f0b94e |
// nodes is the empty list, then run signal a slot change for parent.
|
|
Packit |
f0b94e |
HTMLSlotElement* slot =
|
|
Packit |
f0b94e |
HTMLSlotElement::FromContentOrNull(aChild->GetParent());
|
|
Packit |
f0b94e |
if (slot && slot->GetContainingShadow() == this &&
|
|
Packit |
f0b94e |
slot->AssignedNodes().IsEmpty()) {
|
|
Packit |
f0b94e |
slot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
void ShadowRoot::ContentRemoved(nsIContent* aChild,
|
|
Packit |
f0b94e |
nsIContent* aPreviousSibling) {
|
|
Packit |
f0b94e |
// Check to ensure that the child not an anonymous subtree root because
|
|
Packit |
f0b94e |
// even though its parent could be the host it may not be in the host's child
|
|
Packit |
f0b94e |
// list.
|
|
Packit |
f0b94e |
if (aChild->IsRootOfAnonymousSubtree()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (!aChild->IsSlotable()) {
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
if (aChild->GetParent() == GetHost()) {
|
|
Packit |
f0b94e |
nsAutoString slotName;
|
|
Packit |
f0b94e |
if (aChild->IsElement()) {
|
|
Packit |
f0b94e |
aChild->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::slot,
|
|
Packit |
f0b94e |
slotName);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
if (const HTMLSlotElement* slot = UnassignSlotFor(aChild, slotName)) {
|
|
Packit |
f0b94e |
slot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
return;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// If parent's root is a shadow root, and parent is a slot whose assigned
|
|
Packit |
f0b94e |
// nodes is the empty list, then run signal a slot change for parent.
|
|
Packit |
f0b94e |
HTMLSlotElement* slot =
|
|
Packit |
f0b94e |
HTMLSlotElement::FromContentOrNull(aChild->GetParent());
|
|
Packit |
f0b94e |
if (slot && slot->GetContainingShadow() == this &&
|
|
Packit |
f0b94e |
slot->AssignedNodes().IsEmpty()) {
|
|
Packit |
f0b94e |
slot->EnqueueSlotChangeEvent();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
ServoStyleRuleMap& ShadowRoot::ServoStyleRuleMap() {
|
|
Packit |
f0b94e |
if (!mStyleRuleMap) {
|
|
Packit |
f0b94e |
mStyleRuleMap = MakeUnique<mozilla::ServoStyleRuleMap>();
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
mStyleRuleMap->EnsureTable(*this);
|
|
Packit |
f0b94e |
return *mStyleRuleMap;
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
nsresult ShadowRoot::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult,
|
|
Packit |
f0b94e |
bool aPreallocateChildren) const {
|
|
Packit |
f0b94e |
*aResult = nullptr;
|
|
Packit |
f0b94e |
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
|
|
Packit |
f0b94e |
}
|