feat: support registering MV3 extension service workers (#27562)
* feat: support registering MV3 extension service workers * feat: load chrome extension APIs in worker context * feat: add more ContentRendererClient service worker overrides * fix: lint error * refactor: emit object for 'registration-completed' * docs: clarify when registration-completed emits
This commit is contained in:
parent
6681f8f507
commit
3250ef551c
10 changed files with 152 additions and 6 deletions
|
@ -45,6 +45,16 @@ Returns:
|
||||||
|
|
||||||
Emitted when a service worker logs something to the console.
|
Emitted when a service worker logs something to the console.
|
||||||
|
|
||||||
|
#### Event: 'registration-completed'
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
* `event` Event
|
||||||
|
* `details` Object - Information about the registered service worker
|
||||||
|
* `scope` String - The base URL that a service worker is registered for
|
||||||
|
|
||||||
|
Emitted when a service worker has been registered. Can occur after a call to [`navigator.serviceWorker.register('/sw.js')`](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register) successfully resolves or when a Chrome extension is loaded.
|
||||||
|
|
||||||
### Instance Methods
|
### Instance Methods
|
||||||
|
|
||||||
The following methods are available on instances of `ServiceWorkers`:
|
The following methods are available on instances of `ServiceWorkers`:
|
||||||
|
|
|
@ -207,7 +207,15 @@ void ElectronContentClient::AddAdditionalSchemes(Schemes* schemes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
schemes->service_worker_schemes.emplace_back(url::kFileScheme);
|
schemes->service_worker_schemes.emplace_back(url::kFileScheme);
|
||||||
schemes->standard_schemes.emplace_back(extensions::kExtensionScheme);
|
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
schemes->standard_schemes.push_back(extensions::kExtensionScheme);
|
||||||
|
schemes->savable_schemes.push_back(extensions::kExtensionScheme);
|
||||||
|
schemes->secure_schemes.push_back(extensions::kExtensionScheme);
|
||||||
|
schemes->service_worker_schemes.push_back(extensions::kExtensionScheme);
|
||||||
|
schemes->cors_enabled_schemes.push_back(extensions::kExtensionScheme);
|
||||||
|
schemes->csp_bypassing_schemes.push_back(extensions::kExtensionScheme);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElectronContentClient::AddPepperPlugins(
|
void ElectronContentClient::AddPepperPlugins(
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "gin/object_template_builder.h"
|
#include "gin/object_template_builder.h"
|
||||||
#include "shell/browser/electron_browser_context.h"
|
#include "shell/browser/electron_browser_context.h"
|
||||||
#include "shell/browser/javascript_environment.h"
|
#include "shell/browser/javascript_environment.h"
|
||||||
|
#include "shell/common/gin_converters/gurl_converter.h"
|
||||||
#include "shell/common/gin_converters/value_converter.h"
|
#include "shell/common/gin_converters/value_converter.h"
|
||||||
#include "shell/common/gin_helper/dictionary.h"
|
#include "shell/common/gin_helper/dictionary.h"
|
||||||
#include "shell/common/gin_helper/function_template_extensions.h"
|
#include "shell/common/gin_helper/function_template_extensions.h"
|
||||||
|
@ -102,6 +103,13 @@ void ServiceWorkerContext::OnReportConsoleMessage(
|
||||||
.Build());
|
.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ServiceWorkerContext::OnRegistrationCompleted(const GURL& scope) {
|
||||||
|
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
Emit("registration-completed",
|
||||||
|
gin::DataObjectBuilder(isolate).Set("scope", scope).Build());
|
||||||
|
}
|
||||||
|
|
||||||
void ServiceWorkerContext::OnDestruct(content::ServiceWorkerContext* context) {
|
void ServiceWorkerContext::OnDestruct(content::ServiceWorkerContext* context) {
|
||||||
if (context == service_worker_context_) {
|
if (context == service_worker_context_) {
|
||||||
delete this;
|
delete this;
|
||||||
|
|
|
@ -34,6 +34,7 @@ class ServiceWorkerContext
|
||||||
void OnReportConsoleMessage(int64_t version_id,
|
void OnReportConsoleMessage(int64_t version_id,
|
||||||
const GURL& scope,
|
const GURL& scope,
|
||||||
const content::ConsoleMessage& message) override;
|
const content::ConsoleMessage& message) override;
|
||||||
|
void OnRegistrationCompleted(const GURL& scope) override;
|
||||||
void OnDestruct(content::ServiceWorkerContext* context) override;
|
void OnDestruct(content::ServiceWorkerContext* context) override;
|
||||||
|
|
||||||
// gin::Wrappable
|
// gin::Wrappable
|
||||||
|
|
|
@ -4,7 +4,11 @@
|
||||||
|
|
||||||
#include "shell/renderer/extensions/electron_extensions_renderer_client.h"
|
#include "shell/renderer/extensions/electron_extensions_renderer_client.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "content/public/renderer/render_thread.h"
|
#include "content/public/renderer/render_thread.h"
|
||||||
|
#include "extensions/common/constants.h"
|
||||||
|
#include "extensions/common/manifest_handlers/background_info.h"
|
||||||
#include "extensions/renderer/dispatcher.h"
|
#include "extensions/renderer/dispatcher.h"
|
||||||
#include "shell/common/world_ids.h"
|
#include "shell/common/world_ids.h"
|
||||||
#include "shell/renderer/extensions/electron_extensions_dispatcher_delegate.h"
|
#include "shell/renderer/extensions/electron_extensions_dispatcher_delegate.h"
|
||||||
|
@ -35,8 +39,24 @@ extensions::Dispatcher* ElectronExtensionsRendererClient::GetDispatcher() {
|
||||||
bool ElectronExtensionsRendererClient::
|
bool ElectronExtensionsRendererClient::
|
||||||
ExtensionAPIEnabledForServiceWorkerScript(const GURL& scope,
|
ExtensionAPIEnabledForServiceWorkerScript(const GURL& scope,
|
||||||
const GURL& script_url) const {
|
const GURL& script_url) const {
|
||||||
// TODO(nornagon): adapt logic from chrome's version
|
if (!script_url.SchemeIs(extensions::kExtensionScheme))
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
|
const extensions::Extension* extension =
|
||||||
|
extensions::RendererExtensionRegistry::Get()->GetExtensionOrAppByURL(
|
||||||
|
script_url);
|
||||||
|
|
||||||
|
if (!extension ||
|
||||||
|
!extensions::BackgroundInfo::IsServiceWorkerBased(extension))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (scope != extension->url())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const std::string& sw_script =
|
||||||
|
extensions::BackgroundInfo::GetBackgroundServiceWorkerScript(extension);
|
||||||
|
|
||||||
|
return extension->GetResourceURL(sw_script) == script_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ElectronExtensionsRendererClient::AllowPopup() {
|
bool ElectronExtensionsRendererClient::AllowPopup() {
|
||||||
|
|
|
@ -116,9 +116,6 @@ RendererClientBase::RendererClientBase() {
|
||||||
ParseSchemesCLISwitch(command_line, switches::kSecureSchemes);
|
ParseSchemesCLISwitch(command_line, switches::kSecureSchemes);
|
||||||
for (const std::string& scheme : secure_schemes_list)
|
for (const std::string& scheme : secure_schemes_list)
|
||||||
url::AddSecureScheme(scheme.data());
|
url::AddSecureScheme(scheme.data());
|
||||||
// In Chrome we should set extension's origins to match the pages they can
|
|
||||||
// work on, but in Electron currently we just let extensions do anything.
|
|
||||||
url::AddSecureScheme(extensions::kExtensionScheme);
|
|
||||||
// We rely on the unique process host id which is notified to the
|
// We rely on the unique process host id which is notified to the
|
||||||
// renderer process via command line switch from the content layer,
|
// renderer process via command line switch from the content layer,
|
||||||
// if this switch is removed from the content layer for some reason,
|
// if this switch is removed from the content layer for some reason,
|
||||||
|
@ -411,6 +408,63 @@ void RendererClientBase::RunScriptsAtDocumentEnd(
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RendererClientBase::AllowScriptExtensionForServiceWorker(
|
||||||
|
const url::Origin& script_origin) {
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
return script_origin.scheme() == extensions::kExtensionScheme;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererClientBase::DidInitializeServiceWorkerContextOnWorkerThread(
|
||||||
|
blink::WebServiceWorkerContextProxy* context_proxy,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
const GURL& script_url) {
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
extensions_renderer_client_->GetDispatcher()
|
||||||
|
->DidInitializeServiceWorkerContextOnWorkerThread(
|
||||||
|
context_proxy, service_worker_scope, script_url);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererClientBase::WillEvaluateServiceWorkerOnWorkerThread(
|
||||||
|
blink::WebServiceWorkerContextProxy* context_proxy,
|
||||||
|
v8::Local<v8::Context> v8_context,
|
||||||
|
int64_t service_worker_version_id,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
const GURL& script_url) {
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
extensions_renderer_client_->GetDispatcher()
|
||||||
|
->WillEvaluateServiceWorkerOnWorkerThread(
|
||||||
|
context_proxy, v8_context, service_worker_version_id,
|
||||||
|
service_worker_scope, script_url);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererClientBase::DidStartServiceWorkerContextOnWorkerThread(
|
||||||
|
int64_t service_worker_version_id,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
const GURL& script_url) {
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
extensions_renderer_client_->GetDispatcher()
|
||||||
|
->DidStartServiceWorkerContextOnWorkerThread(
|
||||||
|
service_worker_version_id, service_worker_scope, script_url);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void RendererClientBase::WillDestroyServiceWorkerContextOnWorkerThread(
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
int64_t service_worker_version_id,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
const GURL& script_url) {
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
extensions_renderer_client_->GetDispatcher()
|
||||||
|
->WillDestroyServiceWorkerContextOnWorkerThread(
|
||||||
|
context, service_worker_version_id, service_worker_scope, script_url);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
v8::Local<v8::Context> RendererClientBase::GetContext(
|
v8::Local<v8::Context> RendererClientBase::GetContext(
|
||||||
blink::WebLocalFrame* frame,
|
blink::WebLocalFrame* frame,
|
||||||
v8::Isolate* isolate) const {
|
v8::Isolate* isolate) const {
|
||||||
|
|
|
@ -117,6 +117,28 @@ class RendererClientBase : public content::ContentRendererClient
|
||||||
void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override;
|
void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override;
|
||||||
void RunScriptsAtDocumentIdle(content::RenderFrame* render_frame) override;
|
void RunScriptsAtDocumentIdle(content::RenderFrame* render_frame) override;
|
||||||
|
|
||||||
|
bool AllowScriptExtensionForServiceWorker(
|
||||||
|
const url::Origin& script_origin) override;
|
||||||
|
void DidInitializeServiceWorkerContextOnWorkerThread(
|
||||||
|
blink::WebServiceWorkerContextProxy* context_proxy,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
const GURL& script_url) override;
|
||||||
|
void WillEvaluateServiceWorkerOnWorkerThread(
|
||||||
|
blink::WebServiceWorkerContextProxy* context_proxy,
|
||||||
|
v8::Local<v8::Context> v8_context,
|
||||||
|
int64_t service_worker_version_id,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
const GURL& script_url) override;
|
||||||
|
void DidStartServiceWorkerContextOnWorkerThread(
|
||||||
|
int64_t service_worker_version_id,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
const GURL& script_url) override;
|
||||||
|
void WillDestroyServiceWorkerContextOnWorkerThread(
|
||||||
|
v8::Local<v8::Context> context,
|
||||||
|
int64_t service_worker_version_id,
|
||||||
|
const GURL& service_worker_scope,
|
||||||
|
const GURL& script_url) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
// app_shell embedders may need custom extensions client interfaces.
|
// app_shell embedders may need custom extensions client interfaces.
|
||||||
|
|
|
@ -656,4 +656,16 @@ describe('chrome extensions', () => {
|
||||||
expect(textContent).to.equal('script loaded ok\n');
|
expect(textContent).to.equal('script loaded ok\n');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('manifest v3', () => {
|
||||||
|
it('registers background service worker', async () => {
|
||||||
|
const customSession = session.fromPartition(`persist:${uuid.v4()}`);
|
||||||
|
const registrationPromise = new Promise<string>(resolve => {
|
||||||
|
customSession.serviceWorkers.once('registration-completed', (event, { scope }) => resolve(scope));
|
||||||
|
});
|
||||||
|
const extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'mv3-service-worker'));
|
||||||
|
const scope = await registrationPromise;
|
||||||
|
expect(scope).equals(extension.url);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
console.log('service worker installed');
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"name": "MV3 Service Worker",
|
||||||
|
"description": "Test for extension service worker support.",
|
||||||
|
"version": "1.0",
|
||||||
|
"manifest_version": 3,
|
||||||
|
"background": {
|
||||||
|
"service_worker": "background.js"
|
||||||
|
},
|
||||||
|
"permissions": ["scripting"]
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue