Skip to content
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

Declarative <script> behavior #7986

Open
jeremyroman opened this issue Jun 3, 2022 · 7 comments
Open

Declarative <script> behavior #7986

jeremyroman opened this issue Jun 3, 2022 · 7 comments

Comments

@jeremyroman
Copy link
Collaborator

tl;dr: We should decide how non-JavaScript "declarative script types" should handle removal, insertion after previously being prepared/executed, and in the case of inline scripts, dynamic modification of text content. Establishing a framework for this will help make sure we consciously choose good author, vendor and spec author ergonomics that are as consistent as possible.


Recently a few efforts have been reusing the <script> tag to insert JSON-based declarative syntax into HTML documents:

In addition to these the HTML spec already mentions that <script> elements can be used as a data block for author-specific purposes.

This allows these features to have an extensible syntax in a format familiar to web developers, with existing tooling, while benefiting from the existing special HTML parser behavior for <script>, notably treating its contents as character data and not constructing child elements. (Not without hazards, notably around when HTML comment syntax occurs midway through script text.)

Some options include:

  • one-shot (removal ends the effect of the declaration, and a new <script> element must be used to reinstate it)
  • frozen (contents frozen at prepare/execute time, can be removed and reintroduced with removal and reinsertion of the same element)
  • dynamic (broadly try to reflect changes including to the text content analogously to <style>)

Obviously the scripting section is both complex and security sensitive, and the observable behavior of JavaScript types must not be modified.

Longer document with some prior discussion and comparison.

@jeremyroman
Copy link
Collaborator Author

cc @domenic @hiroshige-g

@annevk
Copy link
Member

annevk commented Jun 4, 2022

We still have whatwg/dom#808 as well.

I was initially thinking that it would be nice to just react to insertion and removal, but the parser complicates that as the moment it's inserted it won't have all of its contents quite yet. That makes me lean toward "dynamic". We should make it clear that only "child text content" is operated on here.

We might also need to clarify https://html.spec.whatwg.org/#restrictions-for-contents-of-script-elements further in that script elements can only contain Text nodes. It's not clear we currently require that and I think we should.

@domenic
Copy link
Member

domenic commented Jun 6, 2022

+1 to leaning toward "dynamic", and to the clarifications @annevk suggests.

My tentative plan would be:

  • Land import maps in the HTML spec with their current form as implemented in Chromium and Gecko per their own draft spec. That is roughly "reset".
  • Work on a PR + tests to change import maps to "dynamic".
  • We can use that infrastructure for other features going forward.

Note that for import maps in particular, they currently have a separate element of non-dynamism where their impact on the behavior of JS imports gets locked down the first time a JS import happens. But this can be thought of as a separate mechanism layered on top of the general script processing model. For example:

  • With the current import maps spec, changes to child text content before any imports are not respected. With "dynamic", they would be.
  • With the current import maps spec, changes to child text content after any imports are ignored. With "dynamic", they would be re-processed and result in firing an error event at the script. (Like would happen if you inserted a new import map after an import happened.)

Note that there is also interest in removing this lock down. So that would be a second stage, after moving import maps to "dynamic" in the sense mentioned here.

@hiroshige-g
Copy link
Contributor

dynamic

Just to clarify, is this "Dynamic text content" in the longer doc, i.e. to reflect changes to the text content but not to reflect changes to (most of) the other attributes, namely the type attribute? (I assume so)

Land import maps in the HTML spec with their current form as implemented in Chromium and Gecko per their own draft spec. That is roughly "reset".

IIUC the current import maps spec is not like "reset" (re-insertion of <script> into DOM is no-op in the import maps spec while re-insertion runs #prepare-a-script with #already-started is false in "reset").

@hiroshige-g
Copy link
Contributor

Also, what do you think of:

  • Should <script> be effective after removal and reinsertion into DOM? (i.e. "Dynamic text content" vs. "Dynamic text content without re-insertion support" in the longer document)
  • When reacting to text content updates or reinsertion (if we allow), should <script> go through second successful #prepare-a-script, or go through a separate algorithm?

(I expect these would largely affect the implementation, and thus my personal preference is "No" and "the latter", respectively, but anyway I'd like to hear first what other people want)

@domenic
Copy link
Member

domenic commented Jun 6, 2022

Just to clarify, is this "Dynamic text content" in the longer doc, i.e. to reflect changes to the text content but not to reflect changes to (most of) the other attributes, namely the type attribute? (I assume so)

Yes, by "dynamic" I mean "dynamic text content".

In general of the state in the script section I think we would only update the script's result for these types. (I haven't checked every one.)

Should <script> be effective after removal and reinsertion into DOM? (i.e. "Dynamic text content" vs. "Dynamic text content without re-insertion support" in the longer document)

I prefer "Dynamic text content", not "Dynamic text content without re-insertion support". It's most like <style>.

  • When reacting to text content updates or reinsertion (if we allow), should <script> go through second successful #prepare-a-script, or go through a separate algorithm?

I think the following factoring might be reasonable, at least for inline:

  • Each of these new script types gets a "parse the result" algorithm, which takes as input a string and gives back a script result.

  • Each of these new script types gets an "update the result" algorithm which is given a result or null newResult, and a result or null oldResult. This removes any previous document-wide effects of oldResult (if not null), and then establishes new document-wide effects using newResult (if not null).

  • Step 30 of #prepare-a-script calls "parse the result" and passes that to "update the result" and then returns. This handles initial insertion.

  • Child text content changed steps are added which, for these types, calls "parse the result" and then passes that to "update the result" along with the current result.

  • Removal steps are added which call "update the result" with null and the current result, for these types.

  • Handling insertion after the first one is probably done by modifying step 1 of #prepare-a-script for already-started scripts of these types, to call "parse the result" and then pass that to "update the result".

@jeremyroman
Copy link
Collaborator Author

Yeah, with the caveat that some additional steps will be needed, at least for "child text content" -- CSP check comes to mind, not sure if it's the only one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

4 participants