/* -*- 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/. */
// 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); if (Host()->IsInNativeAnonymousSubtree()) { // NOTE(emilio): We could consider just propagating the // IN_NATIVE_ANONYMOUS_SUBTREE flag (not making this an anonymous root), but // that breaks the invariant that if two nodes have the same // NativeAnonymousSubtreeRoot() they are in the same DOM tree, which we rely // on a couple places and would need extra fixes. // // We don't hit this case for now anyways, bug 1824886 would start hitting // it.
SetIsNativeAnonymousRoot();
}
Bind();
ExtendedDOMSlots()->mContainingShadow = this;
}
ShadowRoot::~ShadowRoot() { if (IsInComposedDoc()) {
OwnerDoc()->RemoveComposedDocShadowRoot(*this);
}
void ShadowRoot::CloneInternalDataFrom(ShadowRoot* aOther) { if (aOther->IsRootOfNativeAnonymousSubtree()) {
SetIsNativeAnonymousRoot();
}
if (aOther->IsUAWidget()) {
SetIsUAWidget();
}
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, this); if (clonedSheet) {
AppendStyleSheet(*clonedSheet.get());
}
}
}
CloneAdoptedSheetsFrom(*aOther);
}
nsresult ShadowRoot::Bind() {
MOZ_ASSERT(!IsInComposedDoc(), "Forgot to unbind?"); if (Host()->IsInComposedDoc()) {
SetIsConnected(true);
Document* doc = OwnerDoc();
doc->AddComposedDocShadowRoot(*this); // If our stylesheets somehow mutated when we were disconnected, we need to // ensure that our style data gets flushed as appropriate. if (mServoStyles && Servo_AuthorStyles_IsDirty(mServoStyles.get())) {
doc->RecordShadowStyleChange(*this);
}
}
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. return;
}
// For Named slots, slottables are inserted into the other slot // which has the same name already, however it's not the case // for manual slots if (index != 0 && SlotAssignment() == SlotAssignmentMode::Named) { return;
}
InvalidateStyleAndLayoutOnSubtree(aSlot);
HTMLSlotElement* oldSlot = currentSlots->SafeElementAt(1); if (SlotAssignment() == SlotAssignmentMode::Named) { if (oldSlot) {
MOZ_DIAGNOSTIC_ASSERT(oldSlot != aSlot);
// 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];
// Move assigned nodes from removed slot to the next slot in // tree order with the same name.
InvalidateStyleAndLayoutOnSubtree(aSlot);
HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0); const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes(); if (assignedNodes.IsEmpty()) { return;
}
InvalidateStyleAndLayoutOnSubtree(replacementSlot); while (!assignedNodes.IsEmpty()) {
nsINode* assignedNode = assignedNodes[0];
// 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);
}
void ShadowRoot::ImportRuleLoaded(StyleSheet& aSheet) { if (mStyleRuleMap) {
mStyleRuleMap->SheetAdded(aSheet);
}
if (!aSheet.IsApplicable()) { return;
}
// TODO(emilio): Could handle it like a regular sheet insertion, I guess, to // avoid throwing away the whole style data.
Servo_AuthorStyles_ForceDirty(mServoStyles.get());
ApplicableRulesChanged();
}
// We don't need to do anything else than forwarding to the document if // necessary. void ShadowRoot::SheetCloned(StyleSheet& aSheet) { if (Document* doc = GetComposedDoc()) { if (PresShell* shell = doc->GetPresShell()) {
shell->StyleSet()->SheetCloned(aSheet);
}
}
}
if (!mServoStyles) {
mServoStyles.reset(Servo_AuthorStyles_Create());
}
if (mStyleRuleMap) {
mStyleRuleMap->SheetAdded(aSheet);
}
auto changedOnExit =
mozilla::MakeScopeExit([&] { ApplicableRulesChanged(); });
for (size_t i = aIndex + 1; i < aList.Length(); ++i) {
StyleSheet* beforeSheet = aList.ElementAt(i); if (!beforeSheet->IsApplicable()) { continue;
}
// If this is a duplicate adopted stylesheet that is not in the right // position (the last one) then we skip over it. Otherwise we're done. if (&aList == &mAdoptedStyleSheets &&
MOZ_UNLIKELY(aList.LastIndexOf(beforeSheet) != i)) { continue;
}
if (auto* before = FirstApplicableAdoptedStyleSheet(mAdoptedStyleSheets)) {
Servo_AuthorStyles_InsertStyleSheetBefore(mServoStyles.get(), &aSheet,
before);
} else {
Servo_AuthorStyles_AppendStyleSheet(mServoStyles.get(), &aSheet);
}
}
// FIXME(emilio): This needs to notify document observers and such, // presumably. void ShadowRoot::StyleSheetApplicableStateChanged(StyleSheet& aSheet) { auto& sheetList = aSheet.IsConstructed() ? mAdoptedStyleSheets : mStyleSheets;
int32_t index = sheetList.LastIndexOf(&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 (aSheet.IsApplicable()) {
InsertSheetIntoAuthorData(size_t(index), aSheet, sheetList);
} else {
MOZ_ASSERT(mServoStyles); if (mStyleRuleMap) {
mStyleRuleMap->SheetRemoved(aSheet);
}
Servo_AuthorStyles_RemoveStyleSheet(mServoStyles.get(), &aSheet);
ApplicableRulesChanged();
}
}
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) {
nsIContent* originalTarget =
nsIContent::FromEventTargetOrNull(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.
nsPIDOMWindowOuter* win = OwnerDoc()->GetWindow();
EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
? win->GetParentTarget()
: nullptr;
void ShadowRoot::GetSlotNameFor(const nsIContent& aContent,
nsAString& aName) const { if (mIsDetailsShadowTree) { constauto* summary = HTMLSummaryElement::FromNode(aContent); if (summary && summary->IsMainSummary()) {
aName.AssignLiteral("internal-main-summary");
} // Otherwise use the default slot. return;
}
// Note that if slot attribute is missing, assign it to the first default // slot, if exists. if (const Element* element = Element::FromNode(aContent)) {
element->GetAttr(nsGkAtoms::slot, aName);
}
}
if (SlotAssignment() == SlotAssignmentMode::Named) { if (!aContent.GetNextSibling()) { // aContent is the last child, no need to loop through the assigned nodes, // we're necessarily the last one. // // This prevents multiple appends into the host from getting quadratic. return {slot, Nothing()};
}
} else { // For manual slots, if aContent is the last element, we return Nothing // because we just need to append the element to the assigned nodes. No need // to return an index. if (slot->ManuallyAssignedNodes().SafeLastElement(nullptr) == &aContent) { return {slot, Nothing()};
}
}
// Find the appropriate position in the assigned node list for the newly // assigned content. if (SlotAssignment() == SlotAssignmentMode::Manual) { const nsTArray<nsINode*>& manuallyAssignedNodes =
slot->ManuallyAssignedNodes(); auto index = manuallyAssignedNodes.IndexOf(&aContent); if (index != manuallyAssignedNodes.NoIndex) { return {slot, Some(index)};
}
} else { const nsTArray<RefPtr<nsINode>>& assignedNodes = slot->AssignedNodes();
nsIContent* currentContent = GetHost()->GetFirstChild(); 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) { return {slot, Some(i)};
}
currentContent = currentContent->GetNextSibling();
}
}
}
if (assignment.mSlot == oldSlot) { // Nothing to do here. return;
}
// The layout invalidation piece for Manual slots is handled in // HTMLSlotElement::Assign if (aElementOrText.IsElement() &&
SlotAssignment() == SlotAssignmentMode::Named) { if (Document* doc = GetComposedDoc()) { if (RefPtr<PresShell> presShell = doc->GetPresShell()) {
presShell->SlotAssignmentWillChange(*aElementOrText.AsElement(),
oldSlot, assignment.mSlot);
}
}
}
if (oldSlot) { if (SlotAssignment() == SlotAssignmentMode::Named) {
oldSlot->RemoveAssignedNode(aElementOrText); // Don't need to EnqueueSlotChangeEvent for Manual slots because it // needs to be done in tree order, so // HTMLSlotElement::Assign will handle it explicitly.
oldSlot->EnqueueSlotChangeEvent();
} else {
oldSlot->RemoveManuallyAssignedNode(aElementOrText);
}
}
if (assignment.mSlot) { if (assignment.mIndex) {
assignment.mSlot->InsertAssignedNode(*assignment.mIndex, aElementOrText);
} else {
assignment.mSlot->AppendAssignedNode(aElementOrText);
} // Similar as above, HTMLSlotElement::Assign handles enqueuing // slotchange event. if (SlotAssignment() == SlotAssignmentMode::Named) {
assignment.mSlot->EnqueueSlotChangeEvent();
}
}
}
void ShadowRoot::MaybeReassignMainSummary(SummaryChangeReason aReason) {
MOZ_ASSERT(mIsDetailsShadowTree); if (aReason == SummaryChangeReason::Insertion) { // We've inserted a summary element, may need to remove the existing one.
SlotArray* array = mSlotMap.Get(u"internal-main-summary"_ns);
MOZ_RELEASE_ASSERT(array && (*array)->Length() == 1);
HTMLSlotElement* slot = (*array)->ElementAt(0); auto* summary = HTMLSummaryElement::FromNodeOrNull(
slot->AssignedNodes().SafeElementAt(0)); if (summary) {
MaybeReassignContent(*summary);
}
} elseif (MOZ_LIKELY(GetHost())) { // We need to null-check GetHost() in case we're unlinking already. auto* details = HTMLDetailsElement::FromNode(Host());
MOZ_DIAGNOSTIC_ASSERT(details); // We've removed a summary element, we may need to assign the new one. if (HTMLSummaryElement* newMainSummary = details->GetFirstSummary()) {
MaybeReassignContent(*newMainSummary);
}
}
}
void ShadowRoot::MaybeUnslotHostChild(nsIContent& aChild) { // Need to null-check the host because we may be unlinked already.
MOZ_ASSERT(!GetHost() || aChild.GetParent() == GetHost());
HTMLSlotElement* slot = aChild.GetAssignedSlot(); if (!slot) { return;
}
MOZ_DIAGNOSTIC_ASSERT(!aChild.IsRootOfNativeAnonymousSubtree(), "How did aChild end up assigned to a slot?"); // If the slot is going to start showing fallback content, we need to tell // layout about it. if (slot->AssignedNodes().Length() == 1 && slot->HasChildren()) {
InvalidateStyleAndLayoutOnSubtree(slot);
}
if (mIsDetailsShadowTree && aChild.IsHTMLElement(nsGkAtoms::summary)) {
MaybeReassignMainSummary(SummaryChangeReason::Deletion);
}
}
void ShadowRoot::MaybeSlotHostChild(nsIContent& aChild) {
MOZ_ASSERT(aChild.GetParent() == GetHost()); // 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.IsRootOfNativeAnonymousSubtree()) { return;
}
if (!aChild.IsSlotable()) { return;
}
if (mIsDetailsShadowTree && aChild.IsHTMLElement(nsGkAtoms::summary)) {
MaybeReassignMainSummary(SummaryChangeReason::Insertion);
}
SlotInsertionPoint assignment = SlotInsertionPointFor(aChild); if (!assignment.mSlot) { return;
}
// Fallback content will go away, let layout know. if (assignment.mSlot->AssignedNodes().IsEmpty() &&
assignment.mSlot->HasChildren()) {
InvalidateStyleAndLayoutOnSubtree(assignment.mSlot);
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.26Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.