Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Intent to Implement and Ship: <link rel="stylesheet" disabled>, reflected by HTMLLinkElement.disabled

72 views
Skip to first unread message

Emilio Cobos Álvarez

unread,
Apr 17, 2019, 7:00:10 PM4/17/19
to dev-pl...@lists.mozilla.org
Summary: Make the disabled attribute on link elements do something
useful, and the disabled IDL attribute reflect this attribute instead of
forwarding to the style sheet object (if present). This is mostly compat
work, but should also do less work in pages that use it already. See
below for the long story.

Bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1281135

Link to standard: https://github.com/whatwg/html/pull/4519

There are two parts of this proposal. One is "implementing the disabled
attribute". That's what that HTML spec PR is about. The other is about
changing the behavior of HTMLLinkElement.disabled, which we do
implement, to reflect the content attribute. This used to be spec'd like
what Gecko does, but was removed from the spec. New thing matches WebKit
/ Blink. See the "Do other browser engines implement this?" for all
details you may want to know (and probably more) about this situation.

Platform coverage: All

Estimated or target release: 68

Preference behind which this will be implemented:
dom.link.disabled_attribute.enabled

Is this feature enabled by default in sandboxed iframes? Yes

DevTools bug: I don't think it's needed, but please file if you think
otherwise.

Do other browser engines implement this?

All other engines implement the disabled attribute in one way or
another. See below for the details of what WebKit and Blink do. Edge
looks like they implemented the `disabled` attribute as a "default value
for the stylesheet disabled flag" kind of thing. That has the downside
of still downloading the stylesheet in a case where WebKit and Blink
don't right now, and thus I don't think WebKit or Blink would be
interested in switching to such a model.

Regarding HTMLLinkElement.disabled, also all engines implement it, with
two different behaviors right now:

* Gecko (before my patch) and Edge implement the old spec for this,
where it forwards to the stylesheet's disabled flag.
* Blink / WebKit implement it as reflecting the disabled attribute (I'm
not aware of any spec reference, historical or not, for this behavior,
but I'd be interested in knowing about it).

Now the long story, which is mostly in [1]. The TL;DR: is written in
[2], and that contains the proposed behavior that my change implements.
Feel free to skip to the "web-platform-tests" section below if you're
not interested on the details.

Blink and WebKit implement the `disabled` attribute on stylesheets, and
it's the only way to enable alternate stylesheets there. There's no spec
for that of course, and the behavior of this attribute in those engines
is quite bizarre (see [1] for reference).

Separately, there's the HTMLLinkElement.disabled setter and getters,
which were removed from the spec, but that everyone still implements
(and I'm pretty sure it'd not be compatible to remove it).

Spec-wise, the spec says that alternate stylesheets are supposed to be
implemented using the disabled flag on the stylesheet [3]. Edge and
Gecko do that, and make HTMLLinkElement.disabled forward to the stylesheet.

In WebKit / Blink, this is not the case. An alternate stylesheet is not
disabled (as in `link.sheet` is non-null, `link.sheet.disabled` returns
false, but still magically does not apply to the page). The only way to
enable such an stylesheet in Blink and WebKit is removing the `disabled`
attribute on the link.

This means that the only way to have a cross-browser alternate
stylesheet is:

<link rel="alternate stylesheet" disabled ...>

And the cross-browser way to enable is using `link.disabled`:

* In Gecko and Edge, that setter will forward to the stylesheet, object
(it's the same as saying `if (link.sheet) link.sheet.disabled = false;`.
And given they implement alternate sheets according to the standard,
then it'd enable the already loaded and parsed stylesheet.

* In Blink and WebKit, this will remove the disabled attribute, which
will load the stylesheet, and set a magic "explicitly enabled" bit that
will make the stylesheet apply when it loads.

Separately, some websites are starting to use:

<link rel="stylesheet" disabled>

And it's a common cause of confusion when people find that it doesn't
work on Firefox. The disabled attribute on its own does nothing in Gecko
and thus we apply the stylesheet when other browsers don't, and thus the
website breaks.

This is all quite sad and bizarre, and other that this move I don't see
many other paths forwards to achieve interop in all this mess. Blink and
WebKit could probably fix their alternate stylesheet implementation to
properly use the disabled flag, but given their use counters [4][5]
they'll probably not remove the disabled attribute regardless. Also,
they have other incentives to keep the disabled attribute, like the fact
that for them it's a performance boost (they don't have to download and
parse potentially large stylesheets).

So my proposed model aligns with WebKit and Blink as much as possible
without implementing obvious bugs. The disabled attribute also causes
the stylesheet to not be present / loaded at all. This makes sense, and
Blink and WebKit do this when the disabled attribute is set from the
parser. They have a bug where adding the disabled attribute to an
already loaded stylesheet will not remove the sheet from
document.styleSheets or link.sheet. That makes no sense and I hope
they'll fix that (or I may have to go and do it myself and send them
some patches, but I'd rather not...).

Also, the `link.disabled = false` in an alternate sheet case needs to
keep working. The sane thing if we were designing this from scratch
would be probably "you remove the disabled attribute so the stylesheet
is loaded, and once it's loaded you set sheet.disabled = false". But
given it's the only thing people can use right now to toggle alternate
stylesheets I'm pretty sure we can't break it if we want stuff to keep
working. So in the proposed model you also need a magical "explicitly
enabled" bit of the sorts when the disabled attribute is removed.

Anyhow, this is the long explanation of me trying to improve interop in
this dark corner of the web. Over with the tests.

web-platform-tests: I've added 8 different web-platform tests, testing
the basic model, the alternate stylesheet disabling bit (there was no
test for that lol), and various edge cases regarding how the enabled bit
is preserved or not when the node is cloned, rel or href change, the
node is disconnected, etc.

Except the two tests that test for the general model and some of the
WebKit / Blink oddness here, WebKit passes all of the rest.

The seventh test catches an interop issue between WebKit and Blink
(toggling the disabled attribute on a disconnected, recently created
element doesn't set the explicit state in Blink, but does in WebKit and
my implementation).

Sorry for the huge wall of text. Hope this all albeit sad makes some
sort of sense.

Let me know if you have any concerns with this.

-- Emilio

[1]: https://github.com/whatwg/html/issues/3840
[2]: https://github.com/whatwg/html/issues/3840#issuecomment-481034206
[3]: https://drafts.csswg.org/cssom/#concept-css-style-sheet-disabled-flag
[4]: https://www.chromestatus.com/metrics/feature/timeline/popularity/809
[5]: https://www.chromestatus.com/metrics/feature/timeline/popularity/2516
0 new messages