[csswg-drafts] [css-font-loading] Browsers disagree on what it means for a FontFace object to be "CSS-connected", and what effect does it have. (#5707)

emilio has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-font-loading] Browsers disagree on what it means for a FontFace object to be "CSS-connected", and what effect does it have. ==
Consider a test-case like this:

```html
<!doctype html>
<style>@font-face { font-family: Ahem; src: url(Ahem.ttf); }</style>
<iframe src="about:blank"></iframe>
<script>
onload = function() {
  let css = [...document.fonts][0];
  let nonCss = new FontFace('Ahem2', 'url(Ahem2.ttf)');

  let frameDoc = document.querySelector("iframe").contentDocument;

  frameDoc.fonts.add(nonCss);
  frameDoc.fonts.add(css);
  console.log([...frameDoc.fonts]);
}
</script>
```

Behavior across browsers differs:

 * Firefox is correct and throws when `add` is called with a CSS-connected rule, even if it's connected to a different document.
 * WebKit / Blink happily share the font-face with the other doc, for two different reasons:
   * WebKit doesn't check whether a font-face is CSS-connected before inserting in a font-face-set, they just use CSS-connection for ordering. This means that the final font list in `frameDoc` is `Ahem`, `Ahem2`.
   * Blink looks at the wrong document for checking CSS-connection, so it considers both font-faces not CSS-connected for `frameDoc`, and ends up with `Ahem2`, `Ahem`.

So this is clearly broken. Stuff gets more weird if you do something like `document.querySelector("style").remove()` and such.

I'd like to do a proposal, and _then_, separately, discuss what should happen with `.add()` etc.

The proposal I'd like to add is not to make CSS-connectedness dependent on the current state of the stylesheets of any given document. Basically, making it a per-instance "this came from a CSS rule" flag. This proposal would basically change whether `css` in the example above would be CSS-connected after doing `document.querySelector("style").remove()`, and would match WebKit.

The reasoning for that is that it's clearly error-prone, and causes synchronous stylesheet updates. Blink gets it wrong because it doesn't look at the right document. Gecko gets the above test-case right per spec, but has other bugs in the area, like incorrectly throwing in this test-case (for the same reason fundamentally, it updates the stylesheets of the wrong document):

```html
<!doctype html>
<style>@font-face { font-family: Ahem; src: url(Ahem.ttf); }</style>
<iframe src="about:blank"></iframe>
<script>
onload = function() {
  let css = [...document.fonts][0];
  let frameDoc = document.querySelector("iframe").contentDocument;
  document.querySelector("style").remove();
  frameDoc.fonts.add(css);
}
</script>
```

Then there's the discussion of what should happen when you call `.add()` with a CSS-connected rule. I think we have multiple options:

 * Follow Gecko (keep spec as is / throw). Potentially the nicest sanity-wise, but it doesn't match WebKit nor Blink, and starting to throw in an API that never threw before for them is a more risky move.
 * Follow Blink: Do nothing if called with a CSS-connected rule. I think this is the second-best thing and if it gets us to interop I'd like to propose this. 
 * Follow Webkit (do nothing if called on the same document, allow sharing if called on another document): This seems like a very weird behavior personally.

Thoughts on both proposals, noting that the first is independent of the second?

cc @litherum @tabatkins @lilles @xiaochengh 

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/5707 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Monday, 9 November 2020 05:05:15 UTC