Support for Manifest V3 in Browser Extension Development
Although not immediately pressing. I would like to discuss what is required to update shadow-cljs so that it supports V3 of the extension manifest https://developer.chrome.com/docs/extensions/mv3/intro/
V2 will eventually be phased out and V3 made mandatory so starting to work on support for building V3 extensions will be helpful for extension developers opting to use ClojureScript.
Can you be more precice in what shadow needs to do here? Can't you just set "manifest_version": 3 in the JSON file? shadow really doesn't care whats in your manifest.
Someone actually working on a Chrome Extension should probably take this on.
One big change in manifest v3 is user can't use unsafe-eval in development build and remove it in production build any more. (user can still use unsafe-eval csp in sandboxed pages)
https://developer.chrome.com/docs/extensions/mv3/intro/mv3-migration/#content-security-policy
So is it possible to still has a working repl and don't use eval in development build?
Well, it is read-eval-print-loop. So no, you can't have a REPL without eval.
That's sad. The quickest solution might be build a chromium without the extension csp check and use it in dev env.
The related code might be in this file https://github.com/chromium/chromium/blob/main/extensions/common/csp_validator.cc
I am about to start dev'ing a chrome extension and thought I'd try figuring this out.
I started with this tutorial extension:
https://developer.chrome.com/docs/extensions/mv3/getstarted
and attempted to load it with this addition to the manifest.json file:
"content_security_policy": {
"extension_pages": "default-src 'self'; script-src 'self' 'unsafe-eval' http://localhost:9630; connect-src * data: blob: filesystem:; style-src 'self' data: chrome-extension-resource: 'unsafe-inline'; img-src 'self' data: chrome-extension-resource:; frame-src 'self' data: chrome-extension-resource:; font-src 'self' data: chrome-extension-resource:; media-src * data: blob: filesystem:;"
}
I also needed to add the directive: "host_permissions": ["<all_urls>"], (separate from our issue of desiring to evaluate code - yep, their own tutorial is broken).
And indeed there is a validation error and the extension cannot be loaded.
Shout out to @yqrashawn for the idea to augment the source (and a starting point to look at).
I managed to build chromium fairly easily (took about an hour to figure out and 40 mins to compile) having never done this before.
Then another hour or two tinkering and re-building - but it's working now! The extension loads successfully.
The source changes I made in csp_validator.cc were to add this on line 47:
const char kExtensionUnsafeEvalSource[] = "'unsafe-eval'";
and make use of it in the implementation of DoesCSPDisallowRemoteCode
the block starting on line 706 becomes:
auto directive_values = mapping.directive->directive_values;
auto it = std::find_if_not(
directive_values.begin(), directive_values.end(),
[](base::StringPiece source) {
std::string source_lower = base::ToLowerASCII(source);
return source_lower == kSelfSource || source_lower == kNoneSource ||
IsLocalHostSource(source_lower) ||
source_lower == kExtensionUnsafeEvalSource || // <-- added
source_lower == kWasmUnsafeEvalSource;
});
I haven't actually attempted loading any code though - the chrome extension written in cljs I'm trying this with outputs background scripts which are not supported in v3 (uses service workers instead). I'm hoping there won't be more checks in the chromium codebase but, at least the first hurdle is passed.
I created an example v3 extension. See https://github.com/thheller/shadow-cljs/issues/1051
I made no attempt at eval but :target :chrome-extension should be considered obsolete by now and :target :esm seems to be the way forward.
@dvingo @yqrashawn Hi! I know this is a closed issue, but I wanted to say thanks for the inspiration and starting point within the Chromium source. I'm porting an MV2 Clojurescript extension to MV3 at this moment and as of yesterday I have hot reloading working within a modified Chromium build.
If anybody else is building an MV3 extension with Shadow CLJS, hopefully this might be helpful. https://gist.github.com/blake-ctrl/778db8715556d1bc1af00338a8d755b9
@dvingo @yqrashawn Hi! I know this is a closed issue, but I wanted to say thanks for the inspiration and starting point within the Chromium source. I'm porting an MV2 Clojurescript extension to MV3 at this moment and as of yesterday I have hot reloading working within a modified Chromium build.
If anybody else is building an MV3 extension with Shadow CLJS, hopefully this might be helpful. https://gist.github.com/blake-ctrl/778db8715556d1bc1af00338a8d755b9
Thanks! I am in the same boat: migrating a mv2 extension to mv3. However, I can't seem to get the REPL to work on the patched Chromium build.
I am trying to run thheller's mv3 example: https://github.com/thheller/chrome-ext-v3
After I changed the ":runtime" to "browser" i could see a bunch of websocket errors that indicate outputted JS is unable to connect to shadow-cljs
I am curious what changes you had to make to the shadow-cljs config to make it work.
My shadow-cljs.edn looks something like this:
...
:dev-http {8080 {:root "public"
:host "127.0.0.1"}}
:builds
{:extension
{:target :esm
:compiler-options {:source-map true}
:output-dir "extension/js"
:runtime :browser ;:custom
:devtools {:preloads [devtools.preload]
:use-document-host false}
:modules {:shared {:entries []}
:serviceworker
{:init-fn xyz.serviceworker/init
:depends-on #{:shared}}
:popup
{:init-fn xyz.popup/init
:depends-on #{:shared}}
...
I don't know if it makes a difference but my manifest explicitly sets the CSP policy:
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self';"
},
It doesn't include unsafe-eval, but the patch to Chromium should make it always act as if unsafe-eval is allowed.
For me, before patching, the websockets connected without issue but the js-eval from a reload or repl evaluation would throw an error visible from the console in Chromium Devtools.
thank you @blake-ctrl
Posting this comment for posterity for anyone else who might be facing similar issues:
in shadow-cljs.edn This fixed the WebSockets issues:
:devtools {
:use-document-host false}
in manifest.json This fixed the eval issue:
"content_security_policy": {
"extension_pages": "script-src 'self' 'unsafe-eval' 'wasm-unsafe-eval';"
}
So after applying your patch and building Chromium i had to make the 2 mentioned changes in my extension and voila!
Thanks you for your reply and for the Chromium fix :slightly_smiling_face: