diff --git a/shell/browser/api/electron_api_web_contents.cc b/shell/browser/api/electron_api_web_contents.cc index 197742ea66999..4c0fe53067b6d 100644 --- a/shell/browser/api/electron_api_web_contents.cc +++ b/shell/browser/api/electron_api_web_contents.cc @@ -116,6 +116,7 @@ #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) #include "extensions/browser/script_executor.h" +#include "extensions/browser/view_type_utils.h" #include "shell/browser/extensions/electron_extension_web_contents_observer.h" #endif @@ -376,21 +377,49 @@ base::string16 GetDefaultPrinterAsync() { } // namespace +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + +WebContents::Type GetTypeFromViewType(extensions::ViewType view_type) { + switch (view_type) { + case extensions::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE: + return WebContents::Type::BACKGROUND_PAGE; + + case extensions::VIEW_TYPE_APP_WINDOW: + case extensions::VIEW_TYPE_COMPONENT: + case extensions::VIEW_TYPE_EXTENSION_DIALOG: + case extensions::VIEW_TYPE_EXTENSION_POPUP: + case extensions::VIEW_TYPE_BACKGROUND_CONTENTS: + case extensions::VIEW_TYPE_EXTENSION_GUEST: + case extensions::VIEW_TYPE_TAB_CONTENTS: + case extensions::VIEW_TYPE_INVALID: + return WebContents::Type::REMOTE; + } +} + +#endif + WebContents::WebContents(v8::Isolate* isolate, content::WebContents* web_contents) : content::WebContentsObserver(web_contents), type_(Type::REMOTE), weak_factory_(this) { - web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent(), - false); - Init(isolate); - AttachAsUserData(web_contents); - InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate)); #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + // WebContents created by extension host will have valid ViewType set. + extensions::ViewType view_type = extensions::GetViewType(web_contents); + if (view_type != extensions::VIEW_TYPE_INVALID) { + InitWithExtensionView(isolate, web_contents, view_type); + } + extensions::ElectronExtensionWebContentsObserver::CreateForWebContents( web_contents); script_executor_.reset(new extensions::ScriptExecutor(web_contents)); #endif + + web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent(), + false); + Init(isolate); + AttachAsUserData(web_contents); + InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate)); registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser, base::Unretained(this))); bindings_.set_connection_error_handler(base::BindRepeating( @@ -579,6 +608,23 @@ void WebContents::InitWithSessionAndOptions( AttachAsUserData(web_contents()); } +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +void WebContents::InitWithExtensionView(v8::Isolate* isolate, + content::WebContents* web_contents, + extensions::ViewType view_type) { + // Must reassign type prior to calling `Init`. + type_ = GetTypeFromViewType(view_type); + if (GetType() == Type::REMOTE) + return; + + // Allow toggling DevTools for background pages + Observe(web_contents); + InitWithWebContents(web_contents, GetBrowserContext(), IsGuest()); + managed_web_contents()->GetView()->SetDelegate(this); + SecurityStateTabHelper::CreateForWebContents(web_contents); +} +#endif + WebContents::~WebContents() { // The destroy() is called. if (managed_web_contents()) { @@ -586,6 +632,13 @@ WebContents::~WebContents() { RenderViewDeleted(web_contents()->GetRenderViewHost()); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + if (type_ == Type::BACKGROUND_PAGE) { + // Background pages are owned by extensions::ExtensionHost + managed_web_contents()->ReleaseWebContents(); + } +#endif + if (type_ == Type::BROWSER_WINDOW && owner_window()) { // For BrowserWindow we should close the window and clean up everything // before WebContents is destroyed. @@ -1261,6 +1314,7 @@ void WebContents::DevToolsFocused() { void WebContents::DevToolsOpened() { v8::Locker locker(isolate()); v8::HandleScope handle_scope(isolate()); + DCHECK(managed_web_contents()); auto handle = FromOrCreate(isolate(), managed_web_contents()->GetDevToolsWebContents()); devtools_web_contents_.Reset(isolate(), handle.ToV8()); @@ -1573,7 +1627,8 @@ void WebContents::OpenDevTools(gin_helper::Arguments* args) { return; std::string state; - if (type_ == Type::WEB_VIEW || !owner_window()) { + if (type_ == Type::WEB_VIEW || type_ == Type::BACKGROUND_PAGE || + !owner_window()) { state = "detach"; } bool activate = true; @@ -1584,6 +1639,8 @@ void WebContents::OpenDevTools(gin_helper::Arguments* args) { options.Get("activate", &activate); } } + + DCHECK(managed_web_contents()); managed_web_contents()->SetDockState(state); managed_web_contents()->ShowDevTools(activate); } @@ -1592,6 +1649,7 @@ void WebContents::CloseDevTools() { if (type_ == Type::REMOTE) return; + DCHECK(managed_web_contents()); managed_web_contents()->CloseDevTools(); } @@ -1599,6 +1657,7 @@ bool WebContents::IsDevToolsOpened() { if (type_ == Type::REMOTE) return false; + DCHECK(managed_web_contents()); return managed_web_contents()->IsDevToolsViewShowing(); } @@ -1606,6 +1665,7 @@ bool WebContents::IsDevToolsFocused() { if (type_ == Type::REMOTE) return false; + DCHECK(managed_web_contents()); return managed_web_contents()->GetView()->IsDevToolsViewFocused(); } @@ -1614,6 +1674,7 @@ void WebContents::EnableDeviceEmulation( if (type_ == Type::REMOTE) return; + DCHECK(web_contents()); auto* frame_host = web_contents()->GetMainFrame(); if (frame_host) { auto* widget_host = @@ -1629,6 +1690,7 @@ void WebContents::DisableDeviceEmulation() { if (type_ == Type::REMOTE) return; + DCHECK(web_contents()); auto* frame_host = web_contents()->GetMainFrame(); if (frame_host) { auto* widget_host = @@ -1654,6 +1716,7 @@ void WebContents::InspectElement(int x, int y) { if (!enable_devtools_) return; + DCHECK(managed_web_contents()); if (!managed_web_contents()->GetDevToolsWebContents()) OpenDevTools(nullptr); managed_web_contents()->InspectElement(x, y); diff --git a/shell/browser/api/electron_api_web_contents.h b/shell/browser/api/electron_api_web_contents.h index 47c3a0162bb63..99fa855ca2310 100644 --- a/shell/browser/api/electron_api_web_contents.h +++ b/shell/browser/api/electron_api_web_contents.h @@ -42,6 +42,8 @@ #endif #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#include "extensions/common/view_type.h" + namespace extensions { class ScriptExecutor; } @@ -98,7 +100,7 @@ class WebContents : public gin_helper::TrackableObject, public mojom::ElectronBrowser { public: enum class Type { - BACKGROUND_PAGE, // A DevTools extension background page. + BACKGROUND_PAGE, // An extension background page. BROWSER_WINDOW, // Used by BrowserWindow. BROWSER_VIEW, // Used by BrowserView. REMOTE, // Thin wrap around an existing WebContents. @@ -373,6 +375,12 @@ class WebContents : public gin_helper::TrackableObject, gin::Handle session, const gin_helper::Dictionary& options); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + void InitWithExtensionView(v8::Isolate* isolate, + content::WebContents* web_contents, + extensions::ViewType view_type); +#endif + // content::WebContentsDelegate: bool DidAddMessageToConsole(content::WebContents* source, blink::mojom::ConsoleMessageLevel level, diff --git a/shell/browser/extensions/electron_extensions_browser_client.h b/shell/browser/extensions/electron_extensions_browser_client.h index 3086750994057..29201a925f414 100644 --- a/shell/browser/extensions/electron_extensions_browser_client.h +++ b/shell/browser/extensions/electron_extensions_browser_client.h @@ -31,8 +31,6 @@ namespace electron { // An ExtensionsBrowserClient that supports a single content::BrowserContext // with no related incognito context. -// Must be initialized via InitWithBrowserContext() once the BrowserContext is -// created. class ElectronExtensionsBrowserClient : public extensions::ExtensionsBrowserClient { public: @@ -121,11 +119,6 @@ class ElectronExtensionsBrowserClient content::RenderFrameHost* render_frame_host, const extensions::Extension* extension) const override; - // |context| is the single BrowserContext used for IsValidContext(). - // |pref_service| is used for GetPrefServiceForContext(). - void InitWithBrowserContext(content::BrowserContext* context, - PrefService* pref_service); - // Sets the API client. void SetAPIClientForTest(extensions::ExtensionsAPIClient* api_client); diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index 77ff3d4f562c7..41b20f9fa4c23 100644 --- a/spec-main/extensions-spec.ts +++ b/spec-main/extensions-spec.ts @@ -262,6 +262,17 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex const receivedMessage = await w.webContents.executeJavaScript(`window.completionPromise`) expect(receivedMessage).to.deep.equal({ some: 'message' }) }) + + it('can open devtools of background page', async () => { + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`) + await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page')) + const w = new BrowserWindow({ show: false, webPreferences: { session: customSession } }) + const promise = emittedOnce(app, 'web-contents-created') + await w.loadURL(`about:blank`) + const [, bgPageContents] = await promise + expect(bgPageContents.getType()).to.equal('backgroundPage') + bgPageContents.openDevTools() + }) }) describe('devtools extensions', () => { diff --git a/spec-main/fixtures/extensions/persistent-background-page/background.js b/spec-main/fixtures/extensions/persistent-background-page/background.js new file mode 100644 index 0000000000000..2a49b2bd9e686 --- /dev/null +++ b/spec-main/fixtures/extensions/persistent-background-page/background.js @@ -0,0 +1 @@ +/* eslint-disable no-undef */ diff --git a/spec-main/fixtures/extensions/persistent-background-page/manifest.json b/spec-main/fixtures/extensions/persistent-background-page/manifest.json new file mode 100644 index 0000000000000..bbfe42a5d94b6 --- /dev/null +++ b/spec-main/fixtures/extensions/persistent-background-page/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "persistent-background-page", + "version": "1.0", + "background": { + "scripts": ["background.js"], + "persistent": true + }, + "manifest_version": 2 +}