Files
tubestation/dom/html/HTMLDetailsElement.cpp
Emilio Cobos Álvarez e2ac38c2a8 Bug 1968202 - Generalize GlobalStyleSheetCache to support author stylesheets. r=smaug
In bug 1967507 I changed the timing of AnonymousContent stylesheet
loading in a way that it perturbed a css cache test because of
accessiblecaret.css

  https://hg.mozilla.org/mozilla-central/rev/a6a294ae1d18

However that made me realize that accessiblecaret.css is loaded
virtually in all processes, and it should be using the same mechanism we
use for UA sheets, rather than using all the CSS loader machinery
in-content. Same goes for details.css.

Expand GlobalStyleSheetCache to allow UA and Author sheets, and allow
ShadowRoot to get built-in stylesheets appended.

This allows accessiblecaret.css and details.css not to be marked as
content-accessible.

We could do the same at the document level for plaintext.css and co, but
that seems a bit less common, so maybe fine.

Differential Revision: https://phabricator.services.mozilla.com/D250909
2025-05-23 13:30:13 +00:00

262 lines
8.0 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/dom/HTMLDetailsElement.h"
#include "mozilla/dom/HTMLDetailsElementBinding.h"
#include "mozilla/dom/HTMLSummaryElement.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_dom.h"
#include "nsContentUtils.h"
#include "nsTextNode.h"
NS_IMPL_NS_NEW_HTML_ELEMENT(Details)
namespace mozilla::dom {
HTMLDetailsElement::~HTMLDetailsElement() = default;
NS_IMPL_ELEMENT_CLONE(HTMLDetailsElement)
HTMLDetailsElement::HTMLDetailsElement(already_AddRefed<NodeInfo>&& aNodeInfo)
: nsGenericHTMLElement(std::move(aNodeInfo)) {
SetupShadowTree();
}
HTMLSummaryElement* HTMLDetailsElement::GetFirstSummary() const {
// XXX: Bug 1245032: Might want to cache the first summary element.
for (nsIContent* child = nsINode::GetFirstChild(); child;
child = child->GetNextSibling()) {
if (auto* summary = HTMLSummaryElement::FromNode(child)) {
return summary;
}
}
return nullptr;
}
void HTMLDetailsElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
nsIPrincipal* aMaybeScriptedPrincipal,
bool aNotify) {
if (aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::open) {
bool wasOpen = !!aOldValue;
bool isOpen = !!aValue;
if (wasOpen != isOpen) {
auto stringForState = [](bool aOpen) {
return aOpen ? u"open"_ns : u"closed"_ns;
};
nsAutoString oldState;
if (mToggleEventDispatcher) {
oldState.Truncate();
static_cast<ToggleEvent*>(mToggleEventDispatcher->mEvent.get())
->GetOldState(oldState);
mToggleEventDispatcher->Cancel();
} else {
oldState.Assign(stringForState(wasOpen));
}
RefPtr<ToggleEvent> toggleEvent = CreateToggleEvent(
u"toggle"_ns, oldState, stringForState(isOpen), Cancelable::eNo);
mToggleEventDispatcher =
new AsyncEventDispatcher(this, toggleEvent.forget());
mToggleEventDispatcher->PostDOMEvent();
if (isOpen) {
CloseOtherElementsIfNeeded();
}
SetStates(ElementState::OPEN, isOpen);
}
} else if (aName == nsGkAtoms::name) {
CloseElementIfNeeded();
}
}
return nsGenericHTMLElement::AfterSetAttr(
aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
}
nsresult HTMLDetailsElement::BindToTree(BindContext& aContext,
nsINode& aParent) {
nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent);
NS_ENSURE_SUCCESS(rv, rv);
CloseElementIfNeeded();
return NS_OK;
}
void HTMLDetailsElement::SetupShadowTree() {
const bool kNotify = false;
AttachAndSetUAShadowRoot(NotifyUAWidgetSetup::No);
RefPtr<ShadowRoot> sr = GetShadowRoot();
if (NS_WARN_IF(!sr)) {
return;
}
nsNodeInfoManager* nim = OwnerDoc()->NodeInfoManager();
RefPtr<NodeInfo> slotNodeInfo = nim->GetNodeInfo(
nsGkAtoms::slot, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
sr->AppendBuiltInStyleSheet(BuiltInStyleSheet::Details);
{
RefPtr<nsGenericHTMLElement> slot =
NS_NewHTMLSlotElement(do_AddRef(slotNodeInfo));
if (NS_WARN_IF(!slot)) {
return;
}
slot->SetAttr(kNameSpaceID_None, nsGkAtoms::name,
u"internal-main-summary"_ns, kNotify);
sr->AppendChildTo(slot, kNotify, IgnoreErrors());
RefPtr<NodeInfo> summaryNodeInfo = nim->GetNodeInfo(
nsGkAtoms::summary, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE);
RefPtr<nsGenericHTMLElement> summary =
NS_NewHTMLSummaryElement(summaryNodeInfo.forget());
if (NS_WARN_IF(!summary)) {
return;
}
nsAutoString defaultSummaryText;
nsContentUtils::GetMaybeLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
"DefaultSummary", OwnerDoc(),
defaultSummaryText);
RefPtr<nsTextNode> description = new (nim) nsTextNode(nim);
description->SetText(defaultSummaryText, kNotify);
summary->AppendChildTo(description, kNotify, IgnoreErrors());
slot->AppendChildTo(summary, kNotify, IgnoreErrors());
}
{
RefPtr<nsGenericHTMLElement> slot =
NS_NewHTMLSlotElement(slotNodeInfo.forget());
if (NS_WARN_IF(!slot)) {
return;
}
if (StaticPrefs::layout_css_details_content_enabled()) {
slot->SetPseudoElementType(PseudoStyleType::detailsContent);
}
sr->AppendChildTo(slot, kNotify, IgnoreErrors());
}
}
void HTMLDetailsElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) {
if (mToggleEventDispatcher == aEvent) {
mToggleEventDispatcher = nullptr;
}
}
JSObject* HTMLDetailsElement::WrapNode(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return HTMLDetailsElement_Binding::Wrap(aCx, this, aGivenProto);
}
bool HTMLDetailsElement::IsValidInvokeAction(InvokeAction aAction) const {
return nsGenericHTMLElement::IsValidInvokeAction(aAction) ||
aAction == InvokeAction::Toggle || aAction == InvokeAction::Close ||
aAction == InvokeAction::Open;
}
bool HTMLDetailsElement::HandleInvokeInternal(Element* aInvoker,
InvokeAction aAction,
ErrorResult& aRv) {
if (nsGenericHTMLElement::HandleInvokeInternal(aInvoker, aAction, aRv)) {
return true;
}
if (aAction == InvokeAction::Auto || aAction == InvokeAction::Toggle) {
ToggleOpen();
return true;
} else if (aAction == InvokeAction::Close) {
if (Open()) {
SetOpen(false, IgnoreErrors());
}
return true;
} else if (aAction == InvokeAction::Open) {
if (!Open()) {
SetOpen(true, IgnoreErrors());
}
return true;
}
return false;
}
void HTMLDetailsElement::CloseElementIfNeeded() {
if (!StaticPrefs::dom_details_group_enabled()) {
return;
}
if (!Open()) {
return;
}
if (!HasName()) {
return;
}
RefPtr<nsAtom> name = GetParsedAttr(nsGkAtoms::name)->GetAsAtom();
RefPtr<Document> doc = OwnerDoc();
bool oldFlag = doc->FireMutationEvents();
doc->SetFireMutationEvents(false);
nsINode* root = SubtreeRoot();
for (nsINode* cur = root; cur; cur = cur->GetNextNode(root)) {
if (!cur->HasName()) {
continue;
}
if (auto* other = HTMLDetailsElement::FromNode(cur)) {
if (other != this && other->Open() &&
other->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, name,
eCaseMatters)) {
SetOpen(false, IgnoreErrors());
break;
}
}
}
doc->SetFireMutationEvents(oldFlag);
}
void HTMLDetailsElement::CloseOtherElementsIfNeeded() {
if (!StaticPrefs::dom_details_group_enabled()) {
return;
}
MOZ_ASSERT(Open());
if (!HasName()) {
return;
}
RefPtr<nsAtom> name = GetParsedAttr(nsGkAtoms::name)->GetAsAtom();
RefPtr<Document> doc = OwnerDoc();
bool oldFlag = doc->FireMutationEvents();
doc->SetFireMutationEvents(false);
nsINode* root = SubtreeRoot();
for (nsINode* cur = root; cur; cur = cur->GetNextNode(root)) {
if (!cur->HasName()) {
continue;
}
if (auto* other = HTMLDetailsElement::FromNode(cur)) {
if (other != this && other->Open() &&
other->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, name,
eCaseMatters)) {
RefPtr<HTMLDetailsElement> otherDetails = other;
otherDetails->SetOpen(false, IgnoreErrors());
break;
}
}
}
doc->SetFireMutationEvents(oldFlag);
}
} // namespace mozilla::dom