-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch back to Real Constructors™ #57
Comments
I like point 1. Point 2 I am less fond of, because it adds this new "inert style sheet" concept and a mini state machine, and causes what I would assume is the common case to be longer. (And it seems easy to forget, leading to confusion.) Another way of stating it, is that it breaks the invariant that at any given time a CSSStyleSheet is "usable". E.g. you have to answer questions about what happens if inert style sheets are put in adoptedStyleSheets or given to customElements.define or similar. I'll open a separate issue as to whether we need the document at all. Here's a third alternative (in addition to the factory-function model and the OP's model): a text-accepting constructor, and static factory functions for import-containing strings or responses. However I'm not sold on this because the constructor hasn't really de-magicked anything; the factory functions still use some magic construction ability. The OP's model is quite nice from a de-magicking perspective though. Which is good. Maybe we should do it? |
My preference is that it would just be invalid, throwing or whatever. You're right that what's really being constructed is an |
We need a document for In addition to @domenic's questions about where you can insert an inert stylesheet, can one use CSSOM (or parts of it, which parts?) on inert sheets? If there's a separate // cc @emilio |
This is a really interesting framing. It raises the question: should InertStyleSheet be a first-class object? Does it add any value over a string? My tentative answer is negative; it seems like it does nothing more than a string, except that it gives us a place to hang methods. In which case inverting it and using factory functions is probably simpler. This exploration has helped me understand the "de-magicking" aspect a lot better though:
There's a conservation of magic here, and I (and others) have been very focused on avoiding the "creating objects without constructors" magic. Showing that you can transform that into "changing internal state" magic in an isomorphic way makes me reconsider my stance on how bad non-constructible classes are... |
My suggestion wasn't to create an InertStyleSheet, but rather to simply create a proper CSSStyleSheet via a constructor. Possibly passing the document to the constructor (if really needed). The text would be passed to the Why shouldn't you be able to construct a stylesheet, then construct style rules and append them to the sheet, never calling |
I think the static factory taking a |
So to be more specific, Then
This structure sounds a lot better to me, and makes me think the constructor approach is a reasonable way to go. |
As a user I kind of like the idea of inert style sheet, I’ve even brought up the idea before. Ultimately I don’t think there’s really a difference between a constructed style sheet and an inert one other than a style sheet marked as inert isn’t immediate added to |
So if |
I think we can just check whether the stylesheet has been used in a document different than the associated one, and then just throw if |
I was actually talking about the It would involve tracking this state on the sheet explicitly, which is a little annoying but should be doable. |
Argh, I didn't even realize you could add So yeah, tracking a bit on constructed stylesheets that just says "no inserting It's not like @import is particularly necessary; if you're doing programmatic stuff, you can just |
Note that your test-case works because |
I thought that allowing that was the main upshot of the thing @plinnss asked for in the meeting...
They do, actually. |
Ahahaha, kk. Cool then, that at least makes more sense.
No, afaik (@plinss can correct) this was irrelevant to him. He just wanted Real Constructors™ if possible, and that involves making Real Stylesheets™, and a side-consequence of that is that you can insert rules into them immediately. |
Correct, my primary motivation is getting a real constructor and having stylesheets behave like regular JS objects as much as possible. The constructor should create a fully usable stylesheet that can be loaded or simply used as-is and manipulated by it's normal API. Of course I'd like the stylesheet to behave rationally for all rule insertions, but that's another discussion. |
So do we want to disallow inserting @import rules in constructed stylesheets in all cases, or do we want to disallow it if it's already used in a different document? The first one is stronger but easier to track because we just have one bit on every constructed stylesheet like @tabatkins said. |
Oh, nevermind. We should disallow inserting @import in general according to #56, but we'd disallow @import on One thing I'm not really clear on is a detail on |
One thing I'm not clear on is what is the proposal for the contents of the stylesheet between the time when .load() is called and when it fulfills. Does it keep the old rules? Can you modify/insert rules before loading time? Maybe it's immediately replaced with the rules derived from the new text, and the promise just signals when imports complete? But then I'm unclear why .parse() exists... it seems strictly less powerful. (Side note: we'll probably need some new names. .parse() does not communicate "replace all my rules with ones derived from this text", and .load() does not communicate "replace all my rules with ones derived from this text, allowing imports and with a promise for when those finish". But, we can figure out names later, after semantics.) |
Yes, that way you have convenient immediate access to the stylesheet in the resolution function, without having to stash it elsewhere.
Intention is that you can modify/insert rules before loading time, but they're wiped out when
It's sync; as soon as it returns, the stylesheet already contains all the rules you wanted to insert. That's the sole advantage of it.
Good point. I don't want to make them too long, but maybe |
That seems really weird and frustrating. It puts you in this intermediate state where anything you do is about to get wiped out. Why can't it immediately insert the rules? |
UAs currently do not expose the rule lists of sheets that are being loaded, which enables streaming parsing, parallelized parsing, and the like. Forcing parsing into some temporary followed by a synchronization point doesn't seem like a good idea to me... I would strongly prefer that calling load() puts the sheet into a state where you can't inspect or modify its rule list until the load completes. |
That forces the parse to block the main thread, right? For large sheets this can be quite noticeable. We saw significant responsiveness improvements on some pages when we moved parsing for sheets from the network off the main thread.... |
@domenic I'm a little confused - you were present in the earlier discussions about sync vs async parsing, so I don't understand what points you're trying to make now. Are you trying to say that we should add a sync fetch to the platform? |
I think @domenic wants to parse sync, insert the rules sync, but load the imported sheets async. Again, I would much rather this were all an async process. |
@tabatkins no, I am proposing that we add the rules immediately, and then resolve the promise after all the import fetches complete. The same as if you use addRule today: the fetch happens async, the rule gets added sync. @bzbarsky see #38 for why we have a sync method of adding rules, even if that can be costly. ( |
I understand why we have a sync thing. I'd just prefer that we minimize the amount of sync work the async thing (which we also have) does. |
Got it. OK, so abandon my direction. We can re-focus on the question of what is the state of the stylesheet while the |
The intention of "let people insert rules, but if they call load()/parse(), blank out the sheet when they complete" was to avoid the "inert stylesheet" state. The constructor should, if at all possible, return a workable, usable version of the object, and an empty stylesheet is totally usable. There's no particularly good reason to make the sheet unusable at this point. Doing a full replace in the async case makes things simpler and more predictable; it answers the question of what to do if you call .load() twice, for example. In the sync case, eh, whatever, I was just trying to be consistent.
This seems reasonable to me. I wanted to avoid the sheet starting in an unusable state, but having the .load() call force it into an unusable state until it finishes seems reasonable and useful; it avoids footguns. @rakina, what do you think about that? Upon calling |
I think this is OK, though this means if we've already used that stylesheet somewhere, they will lose all those rules, and there'll be a time period where nothing is applied. Since we're gonna overwrite them anyways that's fine, we just have to make it clear to those who want to use this. |
We switched away from constructors to instead use factory functions for two reasons:
Some of the loading details get easier if it's clear what document the stylesheet is associated with. While you can find an appropriate document to associate with a normal constructor (I forget what the right term is, but it's used elsewhere), it's a little weird. A factory function on
document
makes this obvious.We want at least some of the constructors to be async, and async constructors aren't going to be a thing.
@plinss suggested that we can get around both of those issues:
If we really need to associate it with a document, we can just require passing that as an argument to the constructor. In
new CSSStyleSheet(document)
versusdocument.createCSSStyleSheet()
, the former is even slightly shorter!The constructor can take some text (or whatever, there's a few forms), and create an inert stylesheet. You can then activate it with either
.parse()
(which is sync, and which throws if there's an@import
) or.load()
(which is async and allows@import
). If we add aResponse
-taking constructor like in Add a constructor that takes a Response #49, that particular stylesheet can only be.load()
ed;.parse()
would throw.Thoughts? @bzbarsky @annevk @dbaron @domenic TAG people?
The text was updated successfully, but these errors were encountered: