Search code examples
javascripttypescriptcorsvscode-extensionsvscode-webview

VS Code showing 401 trying to load script and CSS assets in a Webview


Summary

I'm trying to load an HTML page referencing local JS and CSS files into a Webview. Details below on the code I'm using to construct the URIs (the results don't look correct, but the code I believe matches other examples online). The Webview Developer Tools show 401 errors for the assets. How can I get these loaded?

Details

I'm producing HTML with this in the <head>. (I have both logged this HTML as well as viewed the source elements through the Webview Developer tools to verify.)

<meta http-equiv="Content-Security-Policy"
      content="default-src 'none'; style-src 'self' https://*.vscode-cdn.net; script-src 'nonce-1CAM0Lr33whIXdLU4Rz5KKxJaq0hduY3'">
<link href="https://file%2B.vscode-resource.vscode-cdn.net/Users/phrogz/Code/visual-scxml-editor/resources/baseeditorstyles.css"
      rel="stylesheet">
<script nonce="1CAM0Lr33whIXdLU4Rz5KKxJaq0hduY3" type='module'
        src="https://file%2B.vscode-resource.vscode-cdn.net/Users/phrogz/Code/visual-scxml-editor/resources/editorglue.js"></script>

The URIs are being produced by this code:

private resourceURI(file: string) {
    return vscode.Uri.joinPath(this.extensionURI, 'resources', file);
}

private webviewURI(file: string) {
    return this.panel.webview.asWebviewUri(this.resourceURI(file)).toString();
}

// …

const editorCSSURI = this.webviewURI('baseeditorstyles.css');

… where extensionURI is from context.extensionUri with contents: {scheme: 'file', authority: '', path: '/Users/phrogz/Code/visual-scxml-editor', query: '', fragment: '', …}. That path is correct.

As best I can tell, that's the same effective code path as shown in this VS Code example for generating the webview URI.

The result, however, is that every CSS and JS asset shows net::ERR_ABORTED 401 in the Webview Developer Tools.

Screenshot of Webview Developer tools showing the text shown immediately following this image

GET https://file+.vscode-resource.vscode-cdn.net/Users/phrogz/Code/visual-scxml-editor/resources/baseeditorstyles.css net::ERR_ABORTED 401
(anonymous) @ index.html?id=084ede1f-0484-44cf-8cd7-1b60312bd463&origin=cf31cd3f-41e9-41ea-861e-c867d65713ed&swVersion=4&extensionId=undefined_publisher.visual-scxml-editor&platform=electron&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&parentOrigin=vscode-file%3A%2F%2Fvscode-app:973
setTimeout (async)
onFrameLoaded @ index.html?id=084ede1f-0484-44cf-8cd7-1b60312bd463&origin=cf31cd3f-41e9-41ea-861e-c867d65713ed&swVersion=4&extensionId=undefined_publisher.visual-scxml-editor&platform=electron&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&parentOrigin=vscode-file%3A%2F%2Fvscode-app:971
(anonymous) @ index.html?id=084ede1f-0484-44cf-8cd7-1b60312bd463&origin=cf31cd3f-41e9-41ea-861e-c867d65713ed&swVersion=4&extensionId=undefined_publisher.visual-scxml-editor&platform=electron&vscode-resource-base-authority=vscode-resource.vscode-cdn.net&parentOrigin=vscode-file%3A%2F%2Fvscode-app:1002

Any thoughts as to what I'm doing wrong?


Investigation

@Andrew below suggests that asWebviewUri() should be returning a URI with a scheme of vscode-resource. Modifying my webviewURI() function to output debug information:

console.log({
   file,
   fileURI:this.resourceURI(file), 
   webURI:this.panel.webview.asWebviewUri(this.resourceURI(file))
})

…shows that it is in fact generating URIs with an https schema and an authority of file+.vscode-resource.vscode-cdn.net:

{file: 'neatxml.js', fileURI: f, webURI: f}
    file: 'neatxml.js'
    fileURI: f {scheme: 'file', authority: '', path: '/Users/phrogz/Code/visual-scxml-editor/resources/neatxml.js', query: '', fragment: '', …}
        _formatted: null
        _fsPath: null
        authority: ''
        fragment: ''
        fsPath: ƒ fsPath(){return this._fsPath||(this._fsPath=b(this,!1)),this._fsPath}
        path: '/Users/phrogz/Code/visual-scxml-editor/resources/neatxml.js'
        query: ''
        scheme: 'file'
    webURI: f {scheme: 'https', authority: 'file+.vscode-resource.vscode-cdn.net', path: '/Users/phrogz/Code/visual-scxml-editor/resources/neatxml.js', query: '', fragment: '', …}
        _formatted: null
        _fsPath: null
        authority: 'file+.vscode-resource.vscode-cdn.net'
        fragment: ''
        fsPath: ƒ fsPath(){return this._fsPath||(this._fsPath=b(this,!1)),this._fsPath}
        path: '/Users/phrogz/Code/visual-scxml-editor/resources/neatxml.js'
        query: ''
        scheme: 'https'


Solution

  • While in the past it may have been that asWebviewUri() generated URIs with a vscode-resource: scheme, that is no longer true. The CORS policy generated by this.panel.webview.cspSource is properly aligned with the URIs it generates to work.

    What is missing is that when vscode.window.createWebviewPanel() is called one must pass along localResourceRoots listing the location(s) where files may be loaded from. For example:

    const resourcesDir = vscode.Uri.joinPath(context.extensionUri, 'resources')const panel = vscode.window.createWebviewPanel(
        'scxml', `SCXML ${path.basename(doc.fileName)}`,
        vscode.ViewColumn.Beside,
        {
            enableScripts: true,
            localResourceRoots: [resourcesDir]
        }
    );
    

    I actually had both enableScripts and localResourceRoots set, but due to a copy/paste error I was setting the directory to 'media' instead of my actual 'resources' directory.