fix(extensions): devtools now open for background pages (#22217)
refactor(extensions): remove unused InitWithBrowserContext method fix(extensions): release background page WebContents to avoid crash The background page WebContents instance is managed by the ExtensionHost. fix(extensions): open background page devtools detached by default test(extensions): add background page devtools test chore: test fix for null web_contents() fix: close background page devtools in test after opening
This commit is contained in:
parent
ae5776041e
commit
5a8046c994
4 changed files with 103 additions and 24 deletions
|
@ -132,6 +132,7 @@
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
#include "extensions/browser/script_executor.h"
|
#include "extensions/browser/script_executor.h"
|
||||||
|
#include "extensions/browser/view_type_utils.h"
|
||||||
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
|
#include "shell/browser/extensions/electron_extension_web_contents_observer.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -409,12 +410,45 @@ const void* kElectronApiWebContentsKey = &kElectronApiWebContentsKey;
|
||||||
|
|
||||||
} // namespace
|
} // 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,
|
WebContents::WebContents(v8::Isolate* isolate,
|
||||||
content::WebContents* web_contents)
|
content::WebContents* web_contents)
|
||||||
: content::WebContentsObserver(web_contents),
|
: content::WebContentsObserver(web_contents),
|
||||||
type_(Type::REMOTE),
|
type_(Type::REMOTE),
|
||||||
id_(GetAllWebContents().Add(this)),
|
id_(GetAllWebContents().Add(this)),
|
||||||
weak_factory_(this) {
|
weak_factory_(this) {
|
||||||
|
#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
|
||||||
|
|
||||||
auto session = Session::CreateFrom(isolate, GetBrowserContext());
|
auto session = Session::CreateFrom(isolate, GetBrowserContext());
|
||||||
session_.Reset(isolate, session.ToV8());
|
session_.Reset(isolate, session.ToV8());
|
||||||
|
|
||||||
|
@ -424,11 +458,7 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||||
web_contents->SetUserData(kElectronApiWebContentsKey,
|
web_contents->SetUserData(kElectronApiWebContentsKey,
|
||||||
std::make_unique<UserDataLink>(GetWeakPtr()));
|
std::make_unique<UserDataLink>(GetWeakPtr()));
|
||||||
InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate));
|
InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate));
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
|
||||||
extensions::ElectronExtensionWebContentsObserver::CreateForWebContents(
|
|
||||||
web_contents);
|
|
||||||
script_executor_.reset(new extensions::ScriptExecutor(web_contents));
|
|
||||||
#endif
|
|
||||||
registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser,
|
registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser,
|
||||||
base::Unretained(this)));
|
base::Unretained(this)));
|
||||||
receivers_.set_disconnect_handler(base::BindRepeating(
|
receivers_.set_disconnect_handler(base::BindRepeating(
|
||||||
|
@ -636,13 +666,39 @@ void WebContents::InitWithSessionAndOptions(
|
||||||
std::make_unique<UserDataLink>(GetWeakPtr()));
|
std::make_unique<UserDataLink>(GetWeakPtr()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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() {
|
WebContents::~WebContents() {
|
||||||
MarkDestroyed();
|
MarkDestroyed();
|
||||||
// The destroy() is called.
|
// The destroy() is called.
|
||||||
if (managed_web_contents()) {
|
if (managed_web_contents()) {
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
if (type_ == Type::BACKGROUND_PAGE) {
|
||||||
|
// Background pages are owned by extensions::ExtensionHost
|
||||||
|
managed_web_contents()->ReleaseWebContents();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
managed_web_contents()->GetView()->SetDelegate(nullptr);
|
managed_web_contents()->GetView()->SetDelegate(nullptr);
|
||||||
|
|
||||||
|
if (web_contents()) {
|
||||||
RenderViewDeleted(web_contents()->GetRenderViewHost());
|
RenderViewDeleted(web_contents()->GetRenderViewHost());
|
||||||
|
}
|
||||||
|
|
||||||
if (type_ == Type::BROWSER_WINDOW && owner_window()) {
|
if (type_ == Type::BROWSER_WINDOW && owner_window()) {
|
||||||
// For BrowserWindow we should close the window and clean up everything
|
// For BrowserWindow we should close the window and clean up everything
|
||||||
|
@ -1379,6 +1435,7 @@ void WebContents::DevToolsOpened() {
|
||||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||||
v8::Locker locker(isolate);
|
v8::Locker locker(isolate);
|
||||||
v8::HandleScope handle_scope(isolate);
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
DCHECK(managed_web_contents());
|
||||||
auto handle =
|
auto handle =
|
||||||
FromOrCreate(isolate, managed_web_contents()->GetDevToolsWebContents());
|
FromOrCreate(isolate, managed_web_contents()->GetDevToolsWebContents());
|
||||||
devtools_web_contents_.Reset(isolate, handle.ToV8());
|
devtools_web_contents_.Reset(isolate, handle.ToV8());
|
||||||
|
@ -1720,7 +1777,8 @@ void WebContents::OpenDevTools(gin::Arguments* args) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string state;
|
std::string state;
|
||||||
if (type_ == Type::WEB_VIEW || !owner_window()) {
|
if (type_ == Type::WEB_VIEW || type_ == Type::BACKGROUND_PAGE ||
|
||||||
|
!owner_window()) {
|
||||||
state = "detach";
|
state = "detach";
|
||||||
}
|
}
|
||||||
bool activate = true;
|
bool activate = true;
|
||||||
|
@ -1731,6 +1789,8 @@ void WebContents::OpenDevTools(gin::Arguments* args) {
|
||||||
options.Get("activate", &activate);
|
options.Get("activate", &activate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DCHECK(managed_web_contents());
|
||||||
managed_web_contents()->SetDockState(state);
|
managed_web_contents()->SetDockState(state);
|
||||||
managed_web_contents()->ShowDevTools(activate);
|
managed_web_contents()->ShowDevTools(activate);
|
||||||
}
|
}
|
||||||
|
@ -1739,6 +1799,7 @@ void WebContents::CloseDevTools() {
|
||||||
if (type_ == Type::REMOTE)
|
if (type_ == Type::REMOTE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
DCHECK(managed_web_contents());
|
||||||
managed_web_contents()->CloseDevTools();
|
managed_web_contents()->CloseDevTools();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1746,6 +1807,7 @@ bool WebContents::IsDevToolsOpened() {
|
||||||
if (type_ == Type::REMOTE)
|
if (type_ == Type::REMOTE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
DCHECK(managed_web_contents());
|
||||||
return managed_web_contents()->IsDevToolsViewShowing();
|
return managed_web_contents()->IsDevToolsViewShowing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1753,6 +1815,7 @@ bool WebContents::IsDevToolsFocused() {
|
||||||
if (type_ == Type::REMOTE)
|
if (type_ == Type::REMOTE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
DCHECK(managed_web_contents());
|
||||||
return managed_web_contents()->GetView()->IsDevToolsViewFocused();
|
return managed_web_contents()->GetView()->IsDevToolsViewFocused();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1761,6 +1824,7 @@ void WebContents::EnableDeviceEmulation(
|
||||||
if (type_ == Type::REMOTE)
|
if (type_ == Type::REMOTE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
DCHECK(web_contents());
|
||||||
auto* frame_host = web_contents()->GetMainFrame();
|
auto* frame_host = web_contents()->GetMainFrame();
|
||||||
if (frame_host) {
|
if (frame_host) {
|
||||||
auto* widget_host_impl =
|
auto* widget_host_impl =
|
||||||
|
@ -1778,6 +1842,7 @@ void WebContents::DisableDeviceEmulation() {
|
||||||
if (type_ == Type::REMOTE)
|
if (type_ == Type::REMOTE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
DCHECK(web_contents());
|
||||||
auto* frame_host = web_contents()->GetMainFrame();
|
auto* frame_host = web_contents()->GetMainFrame();
|
||||||
if (frame_host) {
|
if (frame_host) {
|
||||||
auto* widget_host_impl =
|
auto* widget_host_impl =
|
||||||
|
@ -1805,6 +1870,7 @@ void WebContents::InspectElement(int x, int y) {
|
||||||
if (!enable_devtools_)
|
if (!enable_devtools_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
DCHECK(managed_web_contents());
|
||||||
if (!managed_web_contents()->GetDevToolsWebContents())
|
if (!managed_web_contents()->GetDevToolsWebContents())
|
||||||
OpenDevTools(nullptr);
|
OpenDevTools(nullptr);
|
||||||
managed_web_contents()->InspectElement(x, y);
|
managed_web_contents()->InspectElement(x, y);
|
||||||
|
|
|
@ -48,6 +48,8 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
#include "extensions/common/view_type.h"
|
||||||
|
|
||||||
namespace extensions {
|
namespace extensions {
|
||||||
class ScriptExecutor;
|
class ScriptExecutor;
|
||||||
}
|
}
|
||||||
|
@ -144,7 +146,7 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
public mojom::ElectronBrowser {
|
public mojom::ElectronBrowser {
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
BACKGROUND_PAGE, // A DevTools extension background page.
|
BACKGROUND_PAGE, // An extension background page.
|
||||||
BROWSER_WINDOW, // Used by BrowserWindow.
|
BROWSER_WINDOW, // Used by BrowserWindow.
|
||||||
BROWSER_VIEW, // Used by BrowserView.
|
BROWSER_VIEW, // Used by BrowserView.
|
||||||
REMOTE, // Thin wrap around an existing WebContents.
|
REMOTE, // Thin wrap around an existing WebContents.
|
||||||
|
@ -453,6 +455,12 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
gin::Handle<class Session> session,
|
gin::Handle<class Session> session,
|
||||||
const gin_helper::Dictionary& options);
|
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:
|
// content::WebContentsDelegate:
|
||||||
bool DidAddMessageToConsole(content::WebContents* source,
|
bool DidAddMessageToConsole(content::WebContents* source,
|
||||||
blink::mojom::ConsoleMessageLevel level,
|
blink::mojom::ConsoleMessageLevel level,
|
||||||
|
|
|
@ -32,8 +32,6 @@ namespace electron {
|
||||||
|
|
||||||
// An ExtensionsBrowserClient that supports a single content::BrowserContext
|
// An ExtensionsBrowserClient that supports a single content::BrowserContext
|
||||||
// with no related incognito context.
|
// with no related incognito context.
|
||||||
// Must be initialized via InitWithBrowserContext() once the BrowserContext is
|
|
||||||
// created.
|
|
||||||
class ElectronExtensionsBrowserClient
|
class ElectronExtensionsBrowserClient
|
||||||
: public extensions::ExtensionsBrowserClient {
|
: public extensions::ExtensionsBrowserClient {
|
||||||
public:
|
public:
|
||||||
|
@ -122,11 +120,6 @@ class ElectronExtensionsBrowserClient
|
||||||
content::RenderFrameHost* render_frame_host,
|
content::RenderFrameHost* render_frame_host,
|
||||||
const extensions::Extension* extension) const override;
|
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.
|
// Sets the API client.
|
||||||
void SetAPIClientForTest(extensions::ExtensionsAPIClient* api_client);
|
void SetAPIClientForTest(extensions::ExtensionsAPIClient* api_client);
|
||||||
|
|
||||||
|
|
|
@ -317,7 +317,6 @@ describe('chrome extensions', () => {
|
||||||
const receivedMessage = await w.webContents.executeJavaScript('window.completionPromise');
|
const receivedMessage = await w.webContents.executeJavaScript('window.completionPromise');
|
||||||
expect(receivedMessage).to.deep.equal({ some: 'message' });
|
expect(receivedMessage).to.deep.equal({ some: 'message' });
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('has session in background page', async () => {
|
it('has session in background page', async () => {
|
||||||
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
|
const customSession = session.fromPartition(`persist:${require('uuid').v4()}`);
|
||||||
|
@ -329,6 +328,19 @@ describe('chrome extensions', () => {
|
||||||
expect(bgPageContents.session).to.not.equal(undefined);
|
expect(bgPageContents.session).to.not.equal(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
bgPageContents.closeDevTools();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('devtools extensions', () => {
|
describe('devtools extensions', () => {
|
||||||
let showPanelTimeoutId: any = null;
|
let showPanelTimeoutId: any = null;
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|
Loading…
Reference in a new issue