w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.42k stars 648 forks source link

[cssom] Can we lift the restriction on constructed flag for adoptedStylesheets? #10013

Open keithamus opened 6 months ago

keithamus commented 6 months ago

Currently adoptedStyleSheets has a restriction on CSSStyleSheets that have the constructed flag set:

The set an indexed value algorithm for adoptedStyleSheets, given value and index, is the following:

  1. If value’s constructed flag is not set, or its constructor document is not equal to this DocumentOrShadowRoot's node document, throw a "NotAllowedError" DOMException.

I'm here to rattle this Chesterton's fence. It would be really useful to be able to pluck a documents stylesheets and adopt it into a shadowroots adoptedStyleSheets, co-opting the pages styles while applying the shadow's own styles. Currently this is possible with a rather unfortunate amount of JS by duplicating a <link> tag and stuffing it in your shadow-dom, but it feels like we can make this easier?

(Cue discussion about @import and media queries...)

mayank99 commented 5 months ago

Some assorted thoughts:

The primary use case cited is for adopting document.styleSheets, but this will also enable adopting stylesheets from a parent shadow-root (which is equally useful!)

const parentRoot = el.shadowRoot.host.getRootNode();
el.shadowRoot.adoptedStyleSheets.push(
  ...parentRoot.styleSheets,
  ...parentRoot.adoptedStyleSheets,
);

I think there is (or should be) a difference between "constructed" stylesheets and "adoptable" stylesheets.

Regular document .styleSheets are obviously not constructed. Similarly, there might be a way to write declaratively adopted stylesheets in the future; these wouldn't be constructed either.

The @import restriction could continue to apply to constructed stylesheets, but does not need to apply to other adopted stylesheets.


Should there be an easier way to feature detect?

Currently I can see a try/catch way of polyfilling.

function requiresConstructing() {
  try {
    const doc = document.implementation.createHTMLDocument();
    doc.body.appendChild(doc.createElement("style");
    doc.adoptedStyleSheets = doc.styleSheets;
    return false;
  } catch {
    return true;
  }
}
el.shadowRoot.adoptedStyleSheets.push(
  ...constructIfNecessary(document.styleSheets),
  ...document.adoptedStyleSheets,
);

function constructIfNecessary(styleSheets) {
  if (!requiresConstructing()) return styleSheets;

  return Array.from(styleSheets).map(styleSheet => {
    const sheet = new CSSStyleSheet();
    sheet.replace(stringifyStyleSheet(styleSheet));
    return sheet;
  });
}

(using stringifyStyleSheet helper from #7171)


Should there be a way to adopt stylesheets not just once, but "forever"?

If I understand correctly, adoptedStyleSheets.push(...document.styleSheets) is taking a snapshot of document.styleSheets at the time it's called. styleSheets could change in the future, and it's probably desirable to propagate those changes into wherever they are adopted.

Edit: maybe the changes to the original styleSheets do get reflected automatically, but using ... means you do not get any new styleSheets that were created later. For this reason, maybe adoptedStyleSheets should accept a StyleSheetList?

- el.adoptedStyleSheets.push(...document.styleSheets);
+ el.adoptedStyleSheets.push(document.styleSheets);
css-meeting-bot commented 2 months ago

The CSS Working Group just discussed [cssom] Can we lift the restriction on constructed flag for adoptedStylesheets?, and agreed to the following:

The full IRC log of that discussion <jarhar> emilio: so basically this is about adopted stylesheets being, this came up as a more general problem which is sometimes you want to apply some stylesheets across shadow boundaries, use case for global ish stylesheets
<jarhar> emilio: right now that requires making them adopted stylesheets and adoping them in shadowroots or making them insert markup in all the shadowroots with a link or style element
<jarhar> emilio: theyre not great, yhou should be able to magically - adopted by the shadowroot without changing the shadowroot markjup
<jarhar> emilio: on paper theres nothing terribly possible about it. there ssome solutions - assumptions about stylesheet being a constructed or adopted or owned by a dom element which would stop holding but my intuition is that those are effectively fixable
<futhark> q+
<jarhar> emilio: another alternative to this would be to expose a cheap clone of a stylesheet that gives you basically gives you a construcuted stylesheet from a constructued one. all browsers have this concept of cheap copyable stylesheets contents for caching
<jarhar> emilio: that would be a potential another path which would sidestep the ownership issues
<TabAtkins> q+
<jarhar> emilio: i added this to the agenda because this came up when discussing with keith and it seems like it would be a useful thing to do for web component authors especially - massive thread in web compoentns repo about how to mix web compoentns with ? global stylesheets and this gives you the author level tool to do this in a less disruptive way
<lwarlow> https://github.com/WICG/webcomponents/issues/909 - here's the issue mentioned
<jarhar> emilio: i am not looking ofr a particular solution but is there anybody who thinks this would be terrible or is there something that i missed? if not should we ?
<astearns> ack futhark
<jarhar> futhark: i think that i have basically the same take. some minor issues? what if you have a linked stylesheet and then you remove the link, but thats details we can resolve. i think that the cheap cloning is probably ? in all browsers also. thats the way in general to ?
<astearns> ack TabAtkins
<bkardell_> futhark: doesn't this happen with imported sheets today though?
<jarhar> TabAtkins: my concern was about what exaclty the reasining was behind a consturcted flag and now that rune said that its about the ? adopted stylesheet, the cheap clone would avoid those issues. you still havoid the document association
<emilio> q+
<lwarlow> q+
<astearns> ack emilio
<jarhar> TabAtkins: it would be clear as a lcone that in a separate stylesheet we did disconnec thte ? make sure you mantain the base url youre associated with if you move to a new document. ? cheap clone because it makes things less weird in those cases, you can construct a stylesheet ? i would like to address that, construct an api if possible so you can
<jarhar> only create this this way. i would like this to get fixed better
<jarhar> emilio: regarding that, i think the baseuri association is basically ? so if you basically keep a reference to a stylesheet and then keep a link theres nothing - gecko keeps the baseuri in the stylesheet so theres nothing that would prevent that from working
<jarhar> emilio: i dont think any browsers ? based mutations that would be terrible
<jarhar> TabAtkins: so as long as that is indeed stable
<jarhar> TabAtkins: if thats stable rather than dynamic then i dont have an issue except that ? i would like to pursue being able to set the baseurl in the dconstructed stylesheetg construcotr so its not only availbe for this behavior
<jarhar> emilio: you can already do that, baseuri in stylesheet options
<astearns> ack lwarlow
<jarhar> TabAtkins: then ive got no qualms
<jarhar> lwarlow: how does this work with cross origin stylesheets? you cant get access to the source
<jarhar> lwarlow: if you turn it into a css stylesheet you can read the source from it
<emilio> q+
<jarhar> TabAtkins: i imagine we just dont allow that. has to be a same origin stylesheet
<bkardell_> q+
<jarhar> emilio: my understanding is that if we do this cloned stuff then you shoul dhave the same checks that regular stylesheets do, you get a css stylesheet reference you just dont get access to .cssrules and so on
<bkardell_> q-
<astearns> ack emilio
<bkardell_> (I think Emilio just said what I was queuing for)
<jarhar> emilio: another thing to maybe check would be whether we should reuse the stylesheet presumably we also raise the media condition that you get with ? ink, these is things that can get sorted out while we work on this
<jarhar> astearns: proposed resolution that yes we would like to have adopted stylesheets be able to use non construcuted?
<jarhar> emilio: either that or exposing some sort of clone method, but presumably former is a nicer api because it allows you to clone directly from document.stylesheets which seems nicer
<jarhar> astearns: so yes we would like to fix it, ?
<jarhar> emilio: yeah that sounds good
<jarhar> emilio: no objection to this, so yeah thats not a resolution
<jarhar> astearns: this is the point where i call to objections
<jarhar> astearns: anyone have concerns?
<jarhar> emilio: we would like to do this
<bramus> Seems a useful additinon
<bramus> s/additinon/addition
<astearns> RESOLVED: We do want to remove this restriction, and will work on how to address this
jogibear9988 commented 2 months ago

My question to this, how can the styles be shared between two windows? Problem now is, you can move a webcomponent with adoptedStylesheets to a new window (via adoptNode) but then you need to copy all the styles. Also the component can not do this itself, cause the adopted callback is fired when the component is adopted and then the adoptedStylesheets list is empty.

I did a hack here so it copies all the styles https://github.com/node-projects/dock-spawn-ts/blob/e90e3e50b7022e9525bdf8e53031e053ca86f4e0/src/BrowserDialogHelper.ts#L63 But I'd like to see platform support. It is usefull if you have a dockui like vscode (sample of such a ui with webcomponents https://node-projects.github.io/web-component-designer-demo/index.html)

image