/* -*- 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/. */
/* * Base class for all element classes; this provides an implementation * of DOM Core's Element, implements nsIContent, provides * utility methods for subclasses, and so forth.
*/
#ifdef ACCESSIBILITY # include "nsAccessibilityService.h" #endif
using mozilla::gfx::Matrix4x4;
namespace mozilla::dom {
// Verify sizes of nodes. We use a template rather than a direct static // assert so that the error message actually displays the sizes. // On 32 bit systems the actual allocated size varies a bit between // OSes/compilers. // // We need different numbers on certain build types to deal with the owning // thread pointer that comes with the non-threadsafe refcount on // nsIContent. #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED # define EXTRA_DOM_NODE_BYTES 8 #else # define EXTRA_DOM_NODE_BYTES 0 #endif
#define ASSERT_NODE_SIZE(type, opt_size_64, opt_size_32) \ template <int a, int sizeOn64, int sizeOn32> \ struct Check##type##Size { \
static_assert((sizeof(void*) == 8 && a == sizeOn64) || \
(sizeof(void*) == 4 && a <= sizeOn32), \ "DOM size changed"); \
}; \
Check##type##Size<sizeof(type), opt_size_64 + EXTRA_DOM_NODE_BYTES, \
opt_size_32 + EXTRA_DOM_NODE_BYTES> \
g##type##CES;
// Note that mozjemalloc uses a 16 byte quantum, so 64, 80 and 128 are // bucket sizes.
ASSERT_NODE_SIZE(Element, 128, 80);
ASSERT_NODE_SIZE(HTMLDivElement, 128, 80);
ASSERT_NODE_SIZE(HTMLElement, 128, 80);
ASSERT_NODE_SIZE(HTMLParagraphElement, 128, 80);
ASSERT_NODE_SIZE(HTMLPreElement, 128, 80);
ASSERT_NODE_SIZE(HTMLSpanElement, 128, 80);
ASSERT_NODE_SIZE(HTMLTableCellElement, 128, 80);
ASSERT_NODE_SIZE(Text, 120, 80);
void nsIContent::UpdateEditableState(bool aNotify) { if (IsInNativeAnonymousSubtree()) { // Don't propagate the editable flag into native anonymous subtrees. if (IsRootOfNativeAnonymousSubtree()) { return;
}
// We allow setting the flag on NAC (explicitly, see // nsTextControlFrame::CreateAnonymousContent for example), but not // unsetting it. // // Otherwise, just the act of binding the NAC subtree into our non-anonymous // parent would clear the flag, which is not good. As we shouldn't move NAC // around, this is fine. if (HasFlag(NODE_IS_EDITABLE)) { return;
}
}
void Element::Focus(const FocusOptions& aOptions, CallerType aCallerType,
ErrorResult& aError) { const RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager(); if (MOZ_UNLIKELY(!fm)) { return;
} const OwningNonNull<Element> kungFuDeathGrip(*this); // Also other browsers seem to have the hack to not re-focus (and flush) when // the element is already focused. // Until https://github.com/whatwg/html/issues/4512 is clarified, we'll // maintain interoperatibility by not re-focusing, independent of aOptions. // I.e., `focus({ preventScroll: true})` followed by `focus( { preventScroll: // false })` won't re-focus. if (fm->CanSkipFocus(this)) {
fm->NotifyOfReFocus(kungFuDeathGrip);
fm->NeedsFlushBeforeEventHandling(this); return;
}
uint32_t fmFlags = nsFocusManager::ProgrammaticFocusFlags(aOptions); if (aCallerType == CallerType::NonSystem) {
fmFlags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
}
aError = fm->SetFocus(kungFuDeathGrip, fmFlags);
}
if (state.HasState(ElementState::VISITED)) { return state & ~ElementState::UNVISITED;
} if (state.HasState(ElementState::UNVISITED)) { return state & ~ElementState::VISITED;
}
ScrollContainerFrame* Element::GetScrollContainerFrame(nsIFrame** aFrame,
FlushType aFlushType) {
nsIFrame* frame = GetPrimaryFrame(aFlushType); if (aFrame) {
*aFrame = frame;
} if (frame) { if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) { // It's unclear what to return for SVG frames, so just return null. return nullptr;
}
if (ScrollContainerFrame* scrollContainerFrame =
frame->GetScrollTargetFrame()) {
MOZ_ASSERT(!OwnerDoc()->IsScrollingElement(this), "How can we have a scroll container frame if we're the " "scrollingElement for our document?"); return scrollContainerFrame;
}
}
Document* doc = OwnerDoc(); // Note: This IsScrollingElement() call can flush frames, if we're the body of // a quirks mode document. constbool isScrollingElement = doc->IsScrollingElement(this); if (isScrollingElement) { // Our scroll info should map to the root scroll container frame if there is // one. if (PresShell* presShell = doc->GetPresShell()) { if (ScrollContainerFrame* rootScrollContainerFrame =
presShell->GetRootScrollContainerFrame()) { if (aFrame) {
*aFrame = rootScrollContainerFrame;
} return rootScrollContainerFrame;
}
}
} if (aFrame) { // Re-get *aFrame if the caller asked for it, because that frame flush can // kill it.
*aFrame = GetPrimaryFrame(FlushType::None);
} return nullptr;
}
bool Element::CheckVisibility(const CheckVisibilityOptions& aOptions) {
nsIFrame* f =
GetPrimaryFrame(aOptions.mFlush ? FlushType::Frames : FlushType::None); if (!f) { // 1. If this does not have an associated box, return false. returnfalse;
}
EnumSet includeContentVisibility = {
nsIFrame::IncludeContentVisibility::Hidden}; if (aOptions.mContentVisibilityAuto) {
includeContentVisibility += nsIFrame::IncludeContentVisibility::Auto;
} // Steps 2 and 5 if (f->IsHiddenByContentVisibilityOnAnyAncestor(includeContentVisibility)) { // 2. If a shadow-including ancestor of this has content-visibility: hidden, // return false. // 5. If a shadow-including ancestor of this skips its content due to // has content-visibility: auto, return false. returnfalse;
}
if ((aOptions.mOpacityProperty || aOptions.mCheckOpacity) &&
f->Style()->IsInOpacityZeroSubtree()) { // 3. If the checkOpacity dictionary member of options is true, and this, or // a shadow-including ancestor of this, has a computed opacity value of 0, // return false. returnfalse;
}
if ((aOptions.mVisibilityProperty || aOptions.mCheckVisibilityCSS) &&
!f->StyleVisibility()->IsVisible()) { // 4. If the checkVisibilityCSS dictionary member of options is true, and // this is invisible, return false. returnfalse;
}
// TODO: Propagate whether the axes are logical or not down (via scrollflags).
presShell->ScrollContentIntoView( this, ScrollAxis(block, WhenToScroll::Always),
ScrollAxis(inline_, WhenToScroll::Always), scrollFlags);
}
void Element::ScrollTo(const ScrollToOptions& aOptions) { // When the scroll top is 0, we don't need to flush layout to scroll to that // point; we know 0 is always in range. At least we think so... But we do // need to flush frames so we ensure we find the right scrollable frame if // there is one. If it's nonzero, we need to flush layout because we need to // figure out what our real scrollTopMax is. // // If we have a left value, we can't assume things based on it's value, // depending on our direction and layout 0 may or may not be in our scroll // range. So we need to flush layout no matter what then. constbool needsLayoutFlush =
aOptions.mLeft.WasPassed() ||
(aOptions.mTop.WasPassed() && aOptions.mTop.Value() != 0.0);
// This matches WebKit and Blink, which in turn (apparently, according to // their source) matched old IE. const nsRect paddingRect = aFrame->GetPaddingRectRelativeToSelf(); const nsRect overflowRect = [&] {
OverflowAreas overflowAreas(paddingRect, paddingRect); // Add the scrollable overflow areas of children (if any) to the // paddingRect, as if aFrame was a scrolled frame. It's important to start // with the paddingRect, otherwise if there are no children the overflow // rect will be 0,0,0,0 which will force the point 0,0 to be included in the // final rect.
aFrame->UnionChildOverflow(overflowAreas, /* aAsIfScrolled = */ true); // Make sure that an empty padding-rect's edges are included, by adding // the padding-rect in again with UnionEdges. return overflowAreas.ScrollableOverflow().UnionEdges(paddingRect);
}();
// We can avoid a layout flush if this is the scrolling element of the // document, we have overlay scrollbars, and we aren't embedded in another // document if (presContext && presContext->UseOverlayScrollbars() &&
!doc->StyleOrLayoutObservablyDependsOnParentDocumentLayout() &&
doc->IsScrollingElement(this)) { if (PresShell* presShell = doc->GetPresShell()) { // Ensure up to date dimensions, but don't reflow if (RefPtr<nsViewManager> viewManager = presShell->GetViewManager()) {
viewManager->FlushDelayedResize();
} return nsRect(nsPoint(), presContext->GetVisibleArea().Size());
}
}
nsIFrame* frame; if (ScrollContainerFrame* sf = GetScrollContainerFrame(&frame)) {
nsRect scrollPort = sf->GetScrollPortRect();
if (!sf->IsRootScrollFrameOfDocument()) {
MOZ_ASSERT(frame); // We want the offset to be relative to `frame`, not `sf`... Except for // the root scroll frame, which is an ancestor of frame rather than a // descendant and thus this wouldn't particularly make sense. if (frame != sf) {
scrollPort.MoveBy(sf->GetOffsetTo(frame));
}
}
// The scroll port value might be expanded to the minimum scale size, we // should limit the size to the ICB in such cases.
scrollPort.SizeTo(sf->GetLayoutSize()); return frame->Style()->EffectiveZoom().Unzoom(scrollPort);
}
if (frame && // The display check is OK even though we're not looking at the style // frame, because the style frame only differs from "frame" for tables, // and table wrappers have the same display as the table itself.
(!frame->StyleDisplay()->IsInlineFlow() || frame->IsReplaced())) { // Special case code to make client area work even when there isn't // a scroll view, see bug 180552, bug 227567. return frame->Style()->EffectiveZoom().Unzoom(
frame->GetPaddingRect() - frame->GetPositionIgnoringScrolling());
}
// SVG nodes reach here and just return 0 return nsRect();
}
already_AddRefed<nsIScreen> Element::GetScreen() { // Flush layout to guarantee that frames are created if needed, and preserve // behavior.
Unused << GetPrimaryFrame(FlushType::Frames); if (nsIWidget* widget = nsContentUtils::WidgetForContent(this)) { return widget->GetWidgetScreen();
} return nullptr;
}
double Element::CurrentCSSZoom() {
nsIFrame* f = GetPrimaryFrame(FlushType::Frames); if (!f) { return 1.0;
} return f->Style()->EffectiveZoom().ToFloat();
}
already_AddRefed<DOMRect> Element::GetBoundingClientRect() {
RefPtr<DOMRect> rect = new DOMRect(ToSupports(OwnerDoc()));
nsIFrame* frame = GetPrimaryFrame(FlushType::Layout); if (!frame) { // display:none, perhaps? Return the empty rect return rect.forget();
}
void Element::AddToIdTable(nsAtom* aId) {
NS_ASSERTION(HasID(), "Node doesn't have an ID?"); if (IsInShadowTree()) {
ShadowRoot* containingShadow = GetContainingShadow();
containingShadow->AddToIdTable(this, aId);
} else {
Document* doc = GetUncomposedDoc(); if (doc && !IsInNativeAnonymousSubtree()) {
doc->AddToIdTable(this, aId);
}
}
}
void Element::RemoveFromIdTable() { if (!HasID()) { return;
}
nsAtom* id = DoGetID(); if (IsInShadowTree()) {
ShadowRoot* containingShadow = GetContainingShadow(); // Check for containingShadow because it may have // been deleted during unlinking. if (containingShadow) {
containingShadow->RemoveFromIdTable(this, id);
}
} else {
Document* doc = GetUncomposedDoc(); if (doc && !IsInNativeAnonymousSubtree()) {
doc->RemoveFromIdTable(this, id);
}
}
}
// https://dom.spec.whatwg.org/#dom-element-shadowroot
ShadowRoot* Element::GetShadowRootByMode() const { /** * 1. Let shadow be context object's shadow root. * 2. If shadow is null or its mode is "closed", then return null.
*/
ShadowRoot* shadowRoot = GetShadowRoot(); if (!shadowRoot || shadowRoot->IsClosed()) { return nullptr;
}
/** * 3. Return shadow.
*/ return shadowRoot;
}
bool Element::CanAttachShadowDOM() const { /** * If context object's namespace is not the HTML namespace, * return false. * * Deviate from the spec here to allow shadow dom attachement to * XUL elements.
*/ if (!IsHTMLElement() &&
!(IsXULElement() &&
nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal()))) { returnfalse;
}
/** * 3. If context object’s local name is a valid custom element name, or * context object’s is value is not null, then: * If definition is not null and definition’s disable shadow is true, then * return false.
*/ // It will always have CustomElementData when the element is a valid custom // element or has is value. if (CustomElementData* ceData = GetCustomElementData()) {
CustomElementDefinition* definition = ceData->GetCustomElementDefinition(); // If the definition is null, the element possible hasn't yet upgraded. // Fallback to use LookupCustomElementDefinition to find its definition. if (!definition) {
definition = nsContentUtils::LookupCustomElementDefinition(
NodeInfo()->GetDocument(), nameAtom, namespaceID,
ceData->GetCustomElementType());
}
if (definition && definition->mDisableShadow) { returnfalse;
}
}
returntrue;
}
// https://dom.spec.whatwg.org/#dom-element-attachshadow
already_AddRefed<ShadowRoot> Element::AttachShadow(const ShadowRootInit& aInit,
ErrorResult& aError) { /** * Step 1, 2, and 3.
*/ if (!CanAttachShadowDOM()) {
aError.ThrowNotSupportedError("Unable to attach ShadowDOM"); return nullptr;
}
/** * 4. If element is a shadow host, then:
*/ if (RefPtr<ShadowRoot> root = GetShadowRoot()) { /** * 1. Let currentShadowRoot be element’s shadow root. * * 2. If any of the following are true: * currentShadowRoot’s declarative is false; or * currentShadowRoot’s mode is not mode, * then throw a "NotSupportedError" DOMException.
*/ if (!root->IsDeclarative() || root->Mode() != aInit.mMode) {
aError.ThrowNotSupportedError( "Unable to re-attach to existing ShadowDOM"); return nullptr;
} /** * 3. Otherwise: * 1. Remove all of currentShadowRoot’s children, in tree order. * 2. Set currentShadowRoot’s declarative to false. * 3. Return.
*/
root->ReplaceChildren(nullptr, aError);
root->SetIsDeclarative(ShadowRootDeclarative::No); return root.forget();
}
if (StaticPrefs::dom_webcomponents_shadowdom_report_usage()) {
OwnerDoc()->ReportShadowDOMUsage();
}
// If there are no children, the flat tree is not changing due to the presence // of the shadow root, so we don't need to invalidate style / layout. // // This is a minor optimization, but also works around nasty stuff like // bug 1397876. if (Document* doc = GetComposedDoc()) { if (PresShell* presShell = doc->GetPresShell()) {
presShell->ShadowRootWillBeAttached(*this);
}
}
/** * 5. Let shadow be a new shadow root whose node document is * context object's node document, host is context object, * and mode is init's mode.
*/
RefPtr<ShadowRoot> shadowRoot = new (nim)
ShadowRoot(this, aMode, aDelegatesFocus, aSlotAssignment, aClonable,
aSerializable, ShadowRootDeclarative::No, nodeInfo.forget());
if (NodeOrAncestorHasDirAuto()) {
shadowRoot->SetAncestorHasDirAuto();
}
/** * 7. If this’s custom element state is "precustomized" or "custom", then set * shadow’s available to element internals to true.
*/
CustomElementData* ceData = GetCustomElementData(); if (ceData && (ceData->mState == CustomElementData::State::ePrecustomized ||
ceData->mState == CustomElementData::State::eCustom)) {
shadowRoot->SetAvailableToElementInternals();
}
/** * 9. Set context object's shadow root to shadow.
*/
SetShadowRoot(shadowRoot);
// Dispatch a "shadowrootattached" event for devtools if needed. if (MOZ_UNLIKELY(
nim->GetDocument()->DevToolsAnonymousAndShadowEventsEnabled())) {
AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher( this, u"shadowrootattached"_ns, CanBubble::eYes,
ChromeOnlyDispatch::eYes, Composed::eYes);
dispatcher->PostDOMEvent();
}
const LinkedList<AbstractRange>* ranges =
GetExistingClosestCommonInclusiveAncestorRanges(); if (ranges) { for (const AbstractRange* range : *ranges) { if (range->MayCrossShadowBoundary()) {
MOZ_ASSERT(range->IsDynamicRange());
CrossShadowBoundaryRange* crossBoundaryRange =
range->AsDynamicRange()->GetCrossShadowBoundaryRange();
MOZ_ASSERT(crossBoundaryRange); // We may have previously selected this node before it // becomes a shadow host, so we need to reset the values // in RangeBoundaries to accommodate the change.
crossBoundaryRange->NotifyNodeBecomesShadowHost(this);
}
}
} /** * 10. Return shadow.
*/ return shadowRoot.forget();
}
void Element::AttachAndSetUAShadowRoot(NotifyUAWidgetSetup aNotify,
DelegatesFocus aDelegatesFocus) {
MOZ_DIAGNOSTIC_ASSERT(!CanAttachShadowDOM(), "Cannot be used to attach UI shadow DOM"); if (OwnerDoc()->IsStaticDocument()) { return;
}
if (!GetShadowRoot()) {
RefPtr<ShadowRoot> shadowRoot =
AttachShadowWithoutNameChecks(ShadowRootMode::Closed, aDelegatesFocus);
shadowRoot->SetIsUAWidget();
}
MOZ_ASSERT(GetShadowRoot()->IsUAWidget()); if (aNotify == NotifyUAWidgetSetup::Yes) {
NotifyUAWidgetSetupOrChange();
}
}
// Schedule a runnable, ensure the event dispatches before // returning to content script. // This event cause UA Widget to construct or cause onchange callback // of existing UA Widget to run; dispatching this event twice should not cause // UA Widget to re-init.
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( "Element::NotifyUAWidgetSetupOrChange::UAWidgetSetupOrChange",
[self = RefPtr<Element>(this), doc = RefPtr<Document>(doc)]() {
nsContentUtils::DispatchChromeEvent(doc, self,
u"UAWidgetSetupOrChange"_ns,
CanBubble::eYes, Cancelable::eNo);
}));
}
void Element::NotifyUAWidgetTeardown(UnattachShadowRoot aUnattachShadowRoot) {
MOZ_ASSERT(IsInComposedDoc()); if (!GetShadowRoot()) { return;
}
MOZ_ASSERT(GetShadowRoot()->IsUAWidget()); if (aUnattachShadowRoot == UnattachShadowRoot::Yes) {
UnattachShadow();
}
Document* doc = OwnerDoc(); if (doc->IsStaticDocument()) { return;
}
// The runnable will dispatch an event to tear down UA Widget.
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( "Element::NotifyUAWidgetTeardownAndUnattachShadow::UAWidgetTeardown",
[self = RefPtr<Element>(this), doc = RefPtr<Document>(doc)]() { // Bail out if the element is being collected by CC bool hasHadScriptObject = true;
nsIScriptGlobalObject* scriptObject =
doc->GetScriptHandlingObject(hasHadScriptObject); if (!scriptObject && hasHadScriptObject) { return;
}
if (RefPtr<Document> doc = GetComposedDoc()) { if (PresShell* presShell = doc->GetPresShell()) {
presShell->DestroyFramesForAndRestyle(this); #ifdef ACCESSIBILITY // We need to notify the accessibility service here explicitly because, // even though we're going to reconstruct the _host_, the shadow root and // its children are never really going to come back. We could plumb that // further down to DestroyFramesForAndRestyle and add a new flag to // nsCSSFrameConstructor::ContentRemoved or such, but this seems simpler // instead. if (nsAccessibilityService* accService = GetAccService()) {
accService->ContentRemoved(presShell, shadowRoot);
} #endif
} // ContentRemoved doesn't really run script in the cases we care about (it // can only call ClearFocus when removing iframes and so on...)
[&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY { if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
fm->ContentRemoved(doc, shadowRoot);
}
}();
}
MOZ_ASSERT(!GetPrimaryFrame());
nsAutoString nameToUse; const nsAttrName* name = InternalGetAttrNameFromQName(aName, &nameToUse); if (!name) { if (aForce.WasPassed() && !aForce.Value()) { returnfalse;
}
RefPtr<nsAtom> nameAtom = NS_AtomizeMainThread(nameToUse); if (!nameAtom) {
aError.Throw(NS_ERROR_OUT_OF_MEMORY); returnfalse;
}
aError = SetAttr(kNameSpaceID_None, nameAtom, u""_ns, aTriggeringPrincipal, true); returntrue;
} if (aForce.WasPassed() && aForce.Value()) { returntrue;
} // Hold a strong reference here so that the atom or nodeinfo doesn't go // away during UnsetAttr. If it did UnsetAttr would be left with a // dangling pointer as argument without knowing it.
nsAttrName tmp(*name);
if (!name) { // If there is no canonical nsAttrName for this attribute name, then the // attribute does not exist and we can't get its namespace ID and // local name below, so we return early. return;
}
// Hold a strong reference here so that the atom or nodeinfo doesn't go // away during UnsetAttr. If it did UnsetAttr would be left with a // dangling pointer as argument without knowing it.
nsAttrName tmp(*name);
already_AddRefed<nsIPrincipal> Element::CreateDevtoolsPrincipal() { // Return an ExpandedPrincipal that subsumes this Element's Principal, // and expands this Element's CSP to allow the actions that devtools // needs to perform.
AutoTArray<nsCOMPtr<nsIPrincipal>, 1> allowList = {NodePrincipal()};
RefPtr<ExpandedPrincipal> dtPrincipal = ExpandedPrincipal::Create(
allowList, NodePrincipal()->OriginAttributesRef());
if (nsIContentSecurityPolicy* csp = GetCsp()) {
RefPtr<nsCSPContext> dtCsp = new nsCSPContext();
dtCsp->InitFromOther(static_cast<nsCSPContext*>(csp));
dtCsp->SetSkipAllowInlineStyleCheck(true);
// GetTrustedTypesCompliantAttributeValue may have modified mAttrs and made // the result of InternalGetAttrNameFromQName above invalid. It may now return // a different value, perhaps a nullptr. To be safe, just call the version of // Element::SetAttribute accepting a string value.
SetAttribute(aName, *compliantString, aTriggeringPrincipal, aError);
}
if (nsid == kNameSpaceID_Unknown) { // If the namespace ID is unknown, it means there can't possibly be an // existing attribute. We would need a known namespace ID to pass into // UnsetAttr, so we return early if we don't have one. return;
}
Element* Element::GetAttrAssociatedElement(nsAtom* aAttr) const { if (const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots()) {
nsWeakPtr weakAttrEl = slots->mExplicitlySetAttrElementMap.Get(aAttr); if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakAttrEl)) { // If reflectedTarget's explicitly set attr-element |attrEl| is // a descendant of any of element's shadow-including ancestors, then // return |atrEl|. if (HasSharedRoot(attrEl)) { return attrEl;
} return nullptr;
}
}
const nsAttrValue* value = GetParsedAttr(aAttr); if (!value) { return nullptr;
}
MOZ_ASSERT(value->Type() == nsAttrValue::eAtom, "Attribute used for attr associated element must be parsed");
if (explicitlySetAttrElements) { // 3. If reflectedTarget's explicitly set attr-elements is not null for (const nsWeakPtr& weakEl : *explicitlySetAttrElements) { // For each attrElement in reflectedTarget's explicitly set // attr-elements: if (nsCOMPtr<Element> attrEl = do_QueryReferent(weakEl)) { // If attrElement is not a descendant of any of element's // shadow-including ancestors, then continue. if (!HasSharedRoot(attrEl)) { continue;
} // Append attrElement to elements.
elements.AppendElement(attrEl);
}
}
} else { // 4. Otherwise // 1. Let contentAttributeValue be the result of running // reflectedTarget's get the content attribute. const nsAttrValue* value = GetParsedAttr(aAttr); // 2. If contentAttributeValue is null, then return null. if (!value) { return Nothing();
}
// 3. Let tokens be contentAttributeValue, split on ASCII whitespace.
MOZ_ASSERT(value->Type() == nsAttrValue::eAtomArray ||
value->Type() == nsAttrValue::eAtom, "Attribute used for attr associated elements must be parsed"); for (uint32_t i = 0; i < value->GetAtomCount(); i++) { // For each id of tokens: if (auto* candidate = GetElementByIdInDocOrSubtree(
value->AtomAt(static_cast<int32_t>(i)))) { // Append candidate to elements.
elements.AppendElement(candidate);
}
}
}
return Some(std::move(elements));
};
// getter steps: // 1. Let elements be the result of running this's get the attr-associated // elements. auto elements = getAttrAssociatedElements();
if (elements && elements == cachedAttrElements) { // 2. If the contents of elements is equal to the contents of this's cached // attr-associated elements, then return this's cached attr-associated // elements object.
MOZ_ASSERT(!*aUseCachedValue);
*aUseCachedValue = true; return;
}
// 3. Let elementsAsFrozenArray be elements, converted to a FrozenArray<T>?. // (the binding code takes aElements and returns it as a FrozenArray) // 5. Set this's cached attr-associated elements object to // elementsAsFrozenArray. // (the binding code stores the attr-associated elements object in a slot) // 6. Return elementsAsFrozenArray. if (elements) {
--> --------------------
--> maximum size reached
--> --------------------
¤ 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.23Bemerkung:
(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.