diff --git a/browser/base/content/test/general/browser_bug839103.js b/browser/base/content/test/general/browser_bug839103.js index e565ba732802..217b486655d4 100644 --- a/browser/base/content/test/general/browser_bug839103.js +++ b/browser/base/content/test/general/browser_bug839103.js @@ -59,7 +59,7 @@ async function testBody(testRoot) { stateChanged = ContentTaskUtils.waitForEvent(this, "StyleSheetApplicableStateChanged", true); - link.disabled = true; + link.sheet.disabled = true; evt = await stateChanged; is(evt.type, "StyleSheetApplicableStateChanged", "evt.type has expected value"); diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index 7fc184aeb6b8..ce64d5a89fa2 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -758,6 +758,7 @@ nsresult nsContentSink::ProcessStyleLinkFromHeader( aMedia, aAlternate ? Loader::HasAlternateRel::Yes : Loader::HasAlternateRel::No, Loader::IsInline::No, + Loader::IsExplicitlyEnabled::No, }; auto loadResultOrErr = diff --git a/dom/base/nsIStyleSheetLinkingElement.h b/dom/base/nsIStyleSheetLinkingElement.h index c468d865cc83..291996f6045b 100644 --- a/dom/base/nsIStyleSheetLinkingElement.h +++ b/dom/base/nsIStyleSheetLinkingElement.h @@ -24,26 +24,37 @@ class nsIURI; class nsIStyleSheetLinkingElement : public nsISupports { public: - enum class ForceUpdate { - Yes, + enum class ForceUpdate : uint8_t { No, + Yes, }; - enum class Completed { - Yes, + enum class Completed : uint8_t { No, + Yes, }; - enum class HasAlternateRel { Yes, No }; - - enum class IsAlternate { - Yes, + enum class HasAlternateRel : uint8_t { No, + Yes, }; - enum class IsInline { Yes, No }; + enum class IsAlternate : uint8_t { + No, + Yes, + }; - enum class MediaMatched { + enum class IsInline : uint8_t { + No, + Yes, + }; + + enum class IsExplicitlyEnabled : uint8_t { + No, + Yes, + }; + + enum class MediaMatched : uint8_t { Yes, No, }; @@ -90,14 +101,14 @@ class nsIStyleSheetLinkingElement : public nsISupports { bool mHasAlternateRel; bool mIsInline; + IsExplicitlyEnabled mIsExplicitlyEnabled; SheetInfo(const mozilla::dom::Document&, nsIContent*, already_AddRefed aURI, already_AddRefed aTriggeringPrincipal, - mozilla::net::ReferrerPolicy aReferrerPolicy, - mozilla::CORSMode aCORSMode, const nsAString& aTitle, - const nsAString& aMedia, HasAlternateRel aHasAlternateRel, - IsInline aIsInline); + mozilla::net::ReferrerPolicy aReferrerPolicy, mozilla::CORSMode, + const nsAString& aTitle, const nsAString& aMedia, HasAlternateRel, + IsInline, IsExplicitlyEnabled); ~SheetInfo(); }; diff --git a/dom/base/nsStyleLinkElement.cpp b/dom/base/nsStyleLinkElement.cpp index 6251a75decd1..a48cbe1d69df 100644 --- a/dom/base/nsStyleLinkElement.cpp +++ b/dom/base/nsStyleLinkElement.cpp @@ -40,7 +40,8 @@ nsStyleLinkElement::SheetInfo::SheetInfo( already_AddRefed aTriggeringPrincipal, mozilla::net::ReferrerPolicy aReferrerPolicy, mozilla::CORSMode aCORSMode, const nsAString& aTitle, const nsAString& aMedia, - HasAlternateRel aHasAlternateRel, IsInline aIsInline) + HasAlternateRel aHasAlternateRel, IsInline aIsInline, + IsExplicitlyEnabled aIsExplicitlyEnabled) : mContent(aContent), mURI(aURI), mTriggeringPrincipal(aTriggeringPrincipal), @@ -49,7 +50,8 @@ nsStyleLinkElement::SheetInfo::SheetInfo( mTitle(aTitle), mMedia(aMedia), mHasAlternateRel(aHasAlternateRel == HasAlternateRel::Yes), - mIsInline(aIsInline == IsInline::Yes) { + mIsInline(aIsInline == IsInline::Yes), + mIsExplicitlyEnabled(aIsExplicitlyEnabled) { MOZ_ASSERT(!mIsInline || aContent); MOZ_ASSERT_IF(aContent, aContent->OwnerDoc() == &aDocument); diff --git a/dom/html/HTMLLinkElement.cpp b/dom/html/HTMLLinkElement.cpp index 79f24ce63a9c..e01b5e79c086 100644 --- a/dom/html/HTMLLinkElement.cpp +++ b/dom/html/HTMLLinkElement.cpp @@ -85,12 +85,18 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLLinkElement, NS_IMPL_ELEMENT_CLONE(HTMLLinkElement) -bool HTMLLinkElement::Disabled() { +bool HTMLLinkElement::Disabled() const { + if (StaticPrefs::dom_link_disabled_attribute_enabled()) { + return GetBoolAttr(nsGkAtoms::disabled); + } StyleSheet* ss = GetSheet(); return ss && ss->Disabled(); } -void HTMLLinkElement::SetDisabled(bool aDisabled) { +void HTMLLinkElement::SetDisabled(bool aDisabled, ErrorResult& aRv) { + if (StaticPrefs::dom_link_disabled_attribute_enabled()) { + return SetHTMLBoolAttr(nsGkAtoms::disabled, aDisabled, aRv); + } if (StyleSheet* ss = GetSheet()) { ss->SetDisabled(aDisabled); } @@ -314,7 +320,9 @@ nsresult HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, (aName == nsGkAtoms::href || aName == nsGkAtoms::rel || aName == nsGkAtoms::title || aName == nsGkAtoms::media || aName == nsGkAtoms::type || aName == nsGkAtoms::as || - aName == nsGkAtoms::crossorigin)) { + aName == nsGkAtoms::crossorigin || + (aName == nsGkAtoms::disabled && + StaticPrefs::dom_link_disabled_attribute_enabled()))) { bool dropSheet = false; if (aName == nsGkAtoms::rel) { nsAutoString value; @@ -338,18 +346,25 @@ nsresult HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, const bool forceUpdate = dropSheet || aName == nsGkAtoms::title || aName == nsGkAtoms::media || - aName == nsGkAtoms::type; + aName == nsGkAtoms::type || + aName == nsGkAtoms::disabled; Unused << UpdateStyleSheetInternal( nullptr, nullptr, forceUpdate ? ForceUpdate::Yes : ForceUpdate::No); } } else { - // Since removing href or rel makes us no longer link to a - // stylesheet, force updates for those too. if (aNameSpaceID == kNameSpaceID_None) { + if (aName == nsGkAtoms::disabled && + StaticPrefs::dom_link_disabled_attribute_enabled()) { + mExplicitlyEnabled = true; + } + // Since removing href or rel makes us no longer link to a stylesheet, + // force updates for those too. if (aName == nsGkAtoms::href || aName == nsGkAtoms::rel || aName == nsGkAtoms::title || aName == nsGkAtoms::media || - aName == nsGkAtoms::type) { + aName == nsGkAtoms::type || + (aName == nsGkAtoms::disabled && + StaticPrefs::dom_link_disabled_attribute_enabled())) { Unused << UpdateStyleSheetInternal(nullptr, nullptr, ForceUpdate::Yes); } if ((aName == nsGkAtoms::as || aName == nsGkAtoms::type || @@ -415,6 +430,10 @@ Maybe HTMLLinkElement::GetStyleSheetInfo() { return Nothing(); } + if (StaticPrefs::dom_link_disabled_attribute_enabled() && Disabled()) { + return Nothing(); + } + nsAutoString title; nsAutoString media; GetTitleAndMediaForElement(*this, title, media); @@ -444,6 +463,7 @@ Maybe HTMLLinkElement::GetStyleSheetInfo() { media, alternate ? HasAlternateRel::Yes : HasAlternateRel::No, IsInline::No, + mExplicitlyEnabled ? IsExplicitlyEnabled::Yes : IsExplicitlyEnabled::No, }); } diff --git a/dom/html/HTMLLinkElement.h b/dom/html/HTMLLinkElement.h index 07f6c727600f..4f15b289d0b3 100644 --- a/dom/html/HTMLLinkElement.h +++ b/dom/html/HTMLLinkElement.h @@ -78,8 +78,8 @@ class HTMLLinkElement final : public nsGenericHTMLElement, virtual bool HasDeferredDNSPrefetchRequest() override; // WebIDL - bool Disabled(); - void SetDisabled(bool aDisabled); + bool Disabled() const; + void SetDisabled(bool aDisabled, ErrorResult& aRv); void GetHref(nsAString& aValue) { GetURIAttr(nsGkAtoms::href, nullptr, aValue); @@ -170,8 +170,14 @@ class HTMLLinkElement final : public nsGenericHTMLElement, // nsStyleLinkElement Maybe GetStyleSheetInfo() final; - protected: RefPtr mRelList; + + // The "explicitly enabled" flag. This flag is set whenever the `disabled` + // attribute is explicitly unset, and makes alternate stylesheets not be + // disabled by default anymore. + // + // See https://github.com/whatwg/html/issues/3840#issuecomment-481034206. + bool mExplicitlyEnabled = false; }; } // namespace dom diff --git a/dom/html/HTMLStyleElement.cpp b/dom/html/HTMLStyleElement.cpp index bfc7d5c571d5..025d529d75f1 100644 --- a/dom/html/HTMLStyleElement.cpp +++ b/dom/html/HTMLStyleElement.cpp @@ -45,7 +45,7 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED(HTMLStyleElement, NS_IMPL_ELEMENT_CLONE(HTMLStyleElement) -bool HTMLStyleElement::Disabled() { +bool HTMLStyleElement::Disabled() const { StyleSheet* ss = GetSheet(); return ss && ss->Disabled(); } @@ -186,6 +186,7 @@ Maybe HTMLStyleElement::GetStyleSheetInfo() { media, HasAlternateRel::No, IsInline::Yes, + IsExplicitlyEnabled::No, }); } diff --git a/dom/html/HTMLStyleElement.h b/dom/html/HTMLStyleElement.h index b5066c012440..f4202000c9b6 100644 --- a/dom/html/HTMLStyleElement.h +++ b/dom/html/HTMLStyleElement.h @@ -56,7 +56,7 @@ class HTMLStyleElement final : public nsGenericHTMLElement, NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED - bool Disabled(); + bool Disabled() const; void SetDisabled(bool aDisabled); void GetMedia(nsAString& aValue) { GetHTMLAttr(nsGkAtoms::media, aValue); } void SetMedia(const nsAString& aMedia, ErrorResult& aError) { diff --git a/dom/svg/SVGStyleElement.cpp b/dom/svg/SVGStyleElement.cpp index 78e27134540b..f95aee2afce2 100644 --- a/dom/svg/SVGStyleElement.cpp +++ b/dom/svg/SVGStyleElement.cpp @@ -197,6 +197,7 @@ Maybe SVGStyleElement::GetStyleSheetInfo() { media, HasAlternateRel::No, IsInline::Yes, + IsExplicitlyEnabled::No, }); } diff --git a/dom/webidl/HTMLLinkElement.webidl b/dom/webidl/HTMLLinkElement.webidl index 0fc7890d6eaa..ab94dbad419e 100644 --- a/dom/webidl/HTMLLinkElement.webidl +++ b/dom/webidl/HTMLLinkElement.webidl @@ -14,7 +14,7 @@ // http://www.whatwg.org/specs/web-apps/current-work/#the-link-element [HTMLConstructor] interface HTMLLinkElement : HTMLElement { - [Pure] + [CEReactions, SetterThrows, Pure] attribute boolean disabled; [CEReactions, SetterNeedsSubjectPrincipal=NonSystem, SetterThrows, Pure] attribute DOMString href; diff --git a/dom/xml/XMLStylesheetProcessingInstruction.cpp b/dom/xml/XMLStylesheetProcessingInstruction.cpp index 5c9e3d6e60d9..52093f3f5bc3 100644 --- a/dom/xml/XMLStylesheetProcessingInstruction.cpp +++ b/dom/xml/XMLStylesheetProcessingInstruction.cpp @@ -137,6 +137,7 @@ XMLStylesheetProcessingInstruction::GetStyleSheetInfo() { media, alternate ? HasAlternateRel::Yes : HasAlternateRel::No, IsInline::No, + IsExplicitlyEnabled::No, }); } diff --git a/layout/reftests/bugs/360746-1.html b/layout/reftests/bugs/360746-1.html index 4bb7b021bea5..5e448429b3b9 100644 --- a/layout/reftests/bugs/360746-1.html +++ b/layout/reftests/bugs/360746-1.html @@ -4,7 +4,7 @@ - + diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 57d0923e1f46..2a76418730ae 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -1088,11 +1088,10 @@ static Loader::MediaMatched MediaListMatches(const MediaList* aMediaList, * well as setting the enabled state based on the title and whether * the sheet had "alternate" in its rel. */ -Loader::MediaMatched Loader::PrepareSheet(StyleSheet* aSheet, - const nsAString& aTitle, - const nsAString& aMediaString, - MediaList* aMediaList, - IsAlternate aIsAlternate) { +Loader::MediaMatched Loader::PrepareSheet( + StyleSheet* aSheet, const nsAString& aTitle, const nsAString& aMediaString, + MediaList* aMediaList, IsAlternate aIsAlternate, + IsExplicitlyEnabled aIsExplicitlyEnabled) { MOZ_ASSERT(aSheet, "Must have a sheet!"); RefPtr mediaList(aMediaList); @@ -1106,7 +1105,8 @@ Loader::MediaMatched Loader::PrepareSheet(StyleSheet* aSheet, aSheet->SetMedia(mediaList); aSheet->SetTitle(aTitle); - aSheet->SetEnabled(aIsAlternate == IsAlternate::No); + aSheet->SetEnabled(aIsAlternate == IsAlternate::No || + aIsExplicitlyEnabled == IsExplicitlyEnabled::Yes); return MediaListMatches(mediaList, mDocument); } @@ -1835,8 +1835,8 @@ Result Loader::LoadInlineStyle( LOG((" Sheet is alternate: %d", static_cast(isAlternate))); - auto matched = - PrepareSheet(sheet, aInfo.mTitle, aInfo.mMedia, nullptr, isAlternate); + auto matched = PrepareSheet(sheet, aInfo.mTitle, aInfo.mMedia, nullptr, + isAlternate, aInfo.mIsExplicitlyEnabled); InsertSheetInTree(*sheet, aInfo.mContent); @@ -1940,8 +1940,8 @@ Result Loader::LoadStyleLink( LOG((" Sheet is alternate: %d", static_cast(isAlternate))); - auto matched = - PrepareSheet(sheet, aInfo.mTitle, aInfo.mMedia, nullptr, isAlternate); + auto matched = PrepareSheet(sheet, aInfo.mTitle, aInfo.mMedia, nullptr, + isAlternate, aInfo.mIsExplicitlyEnabled); InsertSheetInTree(*sheet, aInfo.mContent); @@ -2107,7 +2107,8 @@ nsresult Loader::LoadChildSheet(StyleSheet* aParentSheet, &sheet); NS_ENSURE_SUCCESS(rv, rv); - PrepareSheet(sheet, empty, empty, aMedia, IsAlternate::No); + PrepareSheet(sheet, empty, empty, aMedia, IsAlternate::No, + IsExplicitlyEnabled::No); } MOZ_ASSERT(sheet); @@ -2218,7 +2219,8 @@ nsresult Loader::InternalLoadNonDocumentSheet( aReferrerPolicy, aIntegrity, syncLoad, state, &sheet); NS_ENSURE_SUCCESS(rv, rv); - PrepareSheet(sheet, empty, empty, nullptr, IsAlternate::No); + PrepareSheet(sheet, empty, empty, nullptr, IsAlternate::No, + IsExplicitlyEnabled::No); if (state == eSheetComplete) { LOG((" Sheet already complete")); diff --git a/layout/style/Loader.h b/layout/style/Loader.h index e294fdaf8170..3c1eb3ffabaf 100644 --- a/layout/style/Loader.h +++ b/layout/style/Loader.h @@ -199,6 +199,7 @@ class Loader final { typedef nsIStyleSheetLinkingElement::HasAlternateRel HasAlternateRel; typedef nsIStyleSheetLinkingElement::IsAlternate IsAlternate; typedef nsIStyleSheetLinkingElement::IsInline IsInline; + typedef nsIStyleSheetLinkingElement::IsExplicitlyEnabled IsExplicitlyEnabled; typedef nsIStyleSheetLinkingElement::MediaMatched MediaMatched; typedef nsIStyleSheetLinkingElement::Update LoadSheetResult; typedef nsIStyleSheetLinkingElement::SheetInfo SheetInfo; @@ -480,8 +481,8 @@ class Loader final { // // This method will set the sheet's enabled state based on aIsAlternate MediaMatched PrepareSheet(StyleSheet* aSheet, const nsAString& aTitle, - const nsAString& aMediaString, - dom::MediaList* aMediaList, IsAlternate); + const nsAString& aMediaString, dom::MediaList*, + IsAlternate, IsExplicitlyEnabled); // Inserts a style sheet in a document or a ShadowRoot. void InsertSheetInTree(StyleSheet& aSheet, nsIContent* aLinkingContent); diff --git a/modules/libpref/init/StaticPrefList.h b/modules/libpref/init/StaticPrefList.h index bfbbccb824d8..6943771ec35d 100644 --- a/modules/libpref/init/StaticPrefList.h +++ b/modules/libpref/init/StaticPrefList.h @@ -253,6 +253,18 @@ VARCACHE_PREF( bool, true ) +// Whether the disabled attribute in HTMLLinkElement disables the sheet loading +// altogether, or forwards to the inner stylesheet method without attribute +// reflection. +// +// Historical behavior is the second, the first is being discussed at: +// https://github.com/whatwg/html/issues/3840 +VARCACHE_PREF( + "dom.link.disabled_attribute.enabled", + dom_link_disabled_attribute_enabled, + bool, true +) + VARCACHE_PREF( "dom.performance.enable_scheduler_timing", dom_performance_enable_scheduler_timing, diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-001.tentative.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-001.tentative.html new file mode 100644 index 000000000000..877356f924de --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-001.tentative.html @@ -0,0 +1,45 @@ + +<link disabled>, HTMLLinkElement.disabled and CSSStyleSheet.disabled interactions + + + + + + + + diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-002.tentative.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-002.tentative.html new file mode 100644 index 000000000000..9c46f60a6214 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-002.tentative.html @@ -0,0 +1,38 @@ + +<link disabled>, HTMLLinkElement.disabled and CSSStyleSheet.disabled interactions (alternate) + + + + + + + + diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-003.tentative.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-003.tentative.html new file mode 100644 index 000000000000..fc86e6ba1f05 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-003.tentative.html @@ -0,0 +1,32 @@ + +<link disabled>'s "explicitly enabled" state persists after getting disconnected from the tree + + + + + + + + diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-004.tentative.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-004.tentative.html new file mode 100644 index 000000000000..7e6c34ee40d9 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-004.tentative.html @@ -0,0 +1,40 @@ + +<link disabled>'s "explicitly enabled" state doesn't persist for clones + + + + + + + + diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-005.tentative.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-005.tentative.html new file mode 100644 index 000000000000..0233f8c746ed --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-005.tentative.html @@ -0,0 +1,27 @@ + +<link disabled>'s "explicitly enabled" persists across rel changes + + + + + + + + diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-006.tentative.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-006.tentative.html new file mode 100644 index 000000000000..5828e1ce1421 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-006.tentative.html @@ -0,0 +1,26 @@ + +<link disabled>'s "explicitly enabled" state isn't magically set from the setter + + + + + + + diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-007.tentative.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-007.tentative.html new file mode 100644 index 000000000000..451fc3d591f2 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-007.tentative.html @@ -0,0 +1,27 @@ + +<link disabled>'s "explicitly enabled" state works when set explicitly back and forth + + + + + + + diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate-ref.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate-ref.html new file mode 100644 index 000000000000..5d87bfdaf5b0 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate-ref.html @@ -0,0 +1,7 @@ + +CSS Test Reference + + + diff --git a/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate.tentative.html b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate.tentative.html new file mode 100644 index 000000000000..5b020a172b17 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/HTMLLinkElement-disabled-alternate.tentative.html @@ -0,0 +1,15 @@ + + +CSS Test: alternate stylesheets can be disabled by HTMLLinkElement.disabled if they have the disabled attribute already + + + + + + +