Skip to content

Replace document.write('<script ...>') with document.createElement('script')Β #9797

Closed

Description

@jiakuan

GWT version: 2.10.0
Browser (with version): Chrome
Operating System: Version 109.0.5414.119 (Official Build) (x86_64)


Description

I'm using SingleScriptLinker to produce a single Javascript file, and the generated JS code contains:

    $doc_0.write('<script id="' + markerId + '"><\/script>');

Which causes errors when the GWT output JS is used in a Chrome Extension:

Failed to execute 'write' on 'Document': It isn't possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.

According to the latest documentation about document.write:
https://developer.mozilla.org/en-US/docs/Web/API/Document/write

Use of the document.write() method is strongly discouraged.

I'm wondering if it's a good idea to replace the following two lines:

    $doc.write('<script id="' + markerId + '"></script>');
    markerScript = $doc.getElementById(markerId);

https://github.com/gwtproject/gwt/blob/main/dev/core/src/com/google/gwt/core/linker/SingleScriptTemplate.js#L96-L97

with:

    markerScript = $doc.createElement('script');
    markerScript.id = markerId;
    $doc.body.appendChild(markerScript);

If this makes sense, there are a few other script templates needing similar changes.
https://github.com/gwtproject/gwt/search?q=%24doc.write%28%27%3Cscript&type=code

Steps to reproduce

Inspect the generated GWT Javascript code, you will see doc.write in it.

Known workarounds

I've tested the new template in a custom linker, which avoided document.write in the generated Javascript, hence eliminated such errors in Chrome Extension.

Links to further discussions

Activity

  1. tbroyer commented on Jan 30, 2023

    @tbroyer
    Member

    If the gwt.xml has <script> elements, they'll be injected using document.write too (IIRC, this is/was to guarantee execution order):

    private static String generateScriptInjector(String scriptUrl) {
    if (isRelativeURL(scriptUrl)) {
    return " if (!__gwt_scriptsLoaded['"
    + scriptUrl
    + "']) {\n"
    + " __gwt_scriptsLoaded['"
    + scriptUrl
    + "'] = true;\n"
    + " document.write('<script language=\\\"javascript\\\" src=\\\"'+base+'"
    + scriptUrl + "\\\"></script>');\n" + " }\n";
    } else {
    return " if (!__gwt_scriptsLoaded['" + scriptUrl + "']) {\n"
    + " __gwt_scriptsLoaded['" + scriptUrl + "'] = true;\n"
    + " document.write('<script language=\\\"javascript\\\" src=\\\""
    + scriptUrl + "\\\"></script>');\n" + " }\n";
    }
    }

    AFAICT, your proposed change won't work:

    • if the script is in the head of the page (document.body doesn't exist yet)
    • might then break the following lines that walk up the DOM trying to find the currently executing script.

    There's definitely room from improvement, and these templates are quite old, but if they don't work for you I'd suggest creating your own linker. But IIRC the xsiframe linker works for browser extensions and shouldn't use document.write.

  2. jiakuan commented on Jan 30, 2023

    @jiakuan
    Author

    I was guessing probably document.write was used to guarantee execution order. If we could figure the correct place to inject the script tag, then perhaps we could use the DOM API instead of the document.write to create the script element.

  3. added a commit that references this issue on Mar 19, 2024
    84fa2a1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions