refactor: cleanup how WebContents is destroyed (#27920)
This commit is contained in:
parent
b3a0743121
commit
f4e1a343b9
12 changed files with 131 additions and 235 deletions
|
@ -114,8 +114,7 @@ BaseWindow::BaseWindow(gin_helper::Arguments* args,
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseWindow::~BaseWindow() {
|
BaseWindow::~BaseWindow() {
|
||||||
if (!window_->IsClosed())
|
CloseImmediately();
|
||||||
window_->CloseImmediately();
|
|
||||||
|
|
||||||
// Destroy the native window in next tick because the native code might be
|
// Destroy the native window in next tick because the native code might be
|
||||||
// iterating all windows.
|
// iterating all windows.
|
||||||
|
@ -318,6 +317,11 @@ void BaseWindow::SetContentView(gin::Handle<View> view) {
|
||||||
window_->SetContentView(view->view());
|
window_->SetContentView(view->view());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseWindow::CloseImmediately() {
|
||||||
|
if (!window_->IsClosed())
|
||||||
|
window_->CloseImmediately();
|
||||||
|
}
|
||||||
|
|
||||||
void BaseWindow::Close() {
|
void BaseWindow::Close() {
|
||||||
window_->Close();
|
window_->Close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,7 @@ class BaseWindow : public gin_helper::TrackableObject<BaseWindow>,
|
||||||
// Public APIs of NativeWindow.
|
// Public APIs of NativeWindow.
|
||||||
void SetContentView(gin::Handle<View> view);
|
void SetContentView(gin::Handle<View> view);
|
||||||
void Close();
|
void Close();
|
||||||
|
virtual void CloseImmediately();
|
||||||
virtual void Focus();
|
virtual void Focus();
|
||||||
virtual void Blur();
|
virtual void Blur();
|
||||||
bool IsFocused();
|
bool IsFocused();
|
||||||
|
|
|
@ -100,11 +100,9 @@ BrowserView::BrowserView(gin::Arguments* args,
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserView::~BrowserView() {
|
BrowserView::~BrowserView() {
|
||||||
if (api_web_contents_) { // destroy() is called
|
if (api_web_contents_) { // destroy() called without closing WebContents
|
||||||
// Destroy WebContents asynchronously unless app is shutting down,
|
|
||||||
// because destroy() might be called inside WebContents's event handler.
|
|
||||||
api_web_contents_->RemoveObserver(this);
|
api_web_contents_->RemoveObserver(this);
|
||||||
api_web_contents_->DestroyWebContents(!Browser::Get()->is_shutting_down());
|
api_web_contents_->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,13 +104,16 @@ BrowserWindow::BrowserWindow(gin::Arguments* args,
|
||||||
}
|
}
|
||||||
|
|
||||||
BrowserWindow::~BrowserWindow() {
|
BrowserWindow::~BrowserWindow() {
|
||||||
// FIXME This is a hack rather than a proper fix preventing shutdown crashes.
|
if (api_web_contents_) {
|
||||||
if (api_web_contents_)
|
// Cleanup the observers if user destroyed this instance directly instead of
|
||||||
|
// gracefully closing content::WebContents.
|
||||||
|
auto* host = web_contents()->GetRenderViewHost();
|
||||||
|
if (host)
|
||||||
|
host->GetWidget()->RemoveInputEventObserver(this);
|
||||||
api_web_contents_->RemoveObserver(this);
|
api_web_contents_->RemoveObserver(this);
|
||||||
// Note that the OnWindowClosed will not be called after the destructor runs,
|
// Destroy the WebContents.
|
||||||
// since the window object is managed by the BaseWindow class.
|
OnCloseContents();
|
||||||
if (web_contents())
|
}
|
||||||
Cleanup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnInputEvent(const blink::WebInputEvent& event) {
|
void BrowserWindow::OnInputEvent(const blink::WebInputEvent& event) {
|
||||||
|
@ -173,34 +176,14 @@ void BrowserWindow::OnRendererUnresponsive(content::RenderProcessHost*) {
|
||||||
ScheduleUnresponsiveEvent(50);
|
ScheduleUnresponsiveEvent(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnCloseContents() {
|
void BrowserWindow::WebContentsDestroyed() {
|
||||||
// On some machines it may happen that the window gets destroyed for twice,
|
api_web_contents_ = nullptr;
|
||||||
// checking web_contents() can effectively guard against that.
|
CloseImmediately();
|
||||||
// https://github.com/electron/electron/issues/16202.
|
|
||||||
//
|
|
||||||
// TODO(zcbenz): We should find out the root cause and improve the closing
|
|
||||||
// procedure of BrowserWindow.
|
|
||||||
if (!web_contents())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Close all child windows before closing current window.
|
|
||||||
v8::Locker locker(isolate());
|
|
||||||
v8::HandleScope handle_scope(isolate());
|
|
||||||
for (v8::Local<v8::Value> value : child_windows_.Values(isolate())) {
|
|
||||||
gin::Handle<BrowserWindow> child;
|
|
||||||
if (gin::ConvertFromV8(isolate(), value, &child) && !child.IsEmpty())
|
|
||||||
child->window()->CloseImmediately();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the web contents is gone, close the window immediately, but the
|
void BrowserWindow::OnCloseContents() {
|
||||||
// memory will not be freed until you call delete.
|
BaseWindow::ResetBrowserViews();
|
||||||
// In this way, it would be safe to manage windows via smart pointers. If you
|
api_web_contents_->Destroy();
|
||||||
// want to free memory when the window is closed, you can do deleting by
|
|
||||||
// overriding the OnWindowClosed method in the observer.
|
|
||||||
window()->CloseImmediately();
|
|
||||||
|
|
||||||
// Do not sent "unresponsive" event after window is closed.
|
|
||||||
window_unresponsive_closure_.Cancel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnRendererResponsive(content::RenderProcessHost*) {
|
void BrowserWindow::OnRendererResponsive(content::RenderProcessHost*) {
|
||||||
|
@ -268,30 +251,22 @@ void BrowserWindow::OnCloseButtonClicked(bool* prevent_default) {
|
||||||
web_contents()->Close();
|
web_contents()->Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnWindowClosed() {
|
|
||||||
// We need to reset the browser views before we call Cleanup, because the
|
|
||||||
// browser views are child views of the main web contents view, which will be
|
|
||||||
// deleted by Cleanup.
|
|
||||||
BaseWindow::ResetBrowserViews();
|
|
||||||
Cleanup();
|
|
||||||
// See BaseWindow::OnWindowClosed on why calling InvalidateWeakPtrs.
|
|
||||||
weak_factory_.InvalidateWeakPtrs();
|
|
||||||
BaseWindow::OnWindowClosed();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserWindow::OnWindowBlur() {
|
void BrowserWindow::OnWindowBlur() {
|
||||||
|
if (api_web_contents_)
|
||||||
web_contents()->StoreFocus();
|
web_contents()->StoreFocus();
|
||||||
|
|
||||||
BaseWindow::OnWindowBlur();
|
BaseWindow::OnWindowBlur();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::OnWindowFocus() {
|
void BrowserWindow::OnWindowFocus() {
|
||||||
|
// focus/blur events might be emitted while closing window.
|
||||||
|
if (api_web_contents_) {
|
||||||
web_contents()->RestoreFocus();
|
web_contents()->RestoreFocus();
|
||||||
|
|
||||||
#if !defined(OS_MAC)
|
#if !defined(OS_MAC)
|
||||||
if (!api_web_contents_->IsDevToolsOpened())
|
if (!api_web_contents_->IsDevToolsOpened())
|
||||||
web_contents()->Focus();
|
web_contents()->Focus();
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
BaseWindow::OnWindowFocus();
|
BaseWindow::OnWindowFocus();
|
||||||
}
|
}
|
||||||
|
@ -325,6 +300,22 @@ void BrowserWindow::OnWindowLeaveFullScreen() {
|
||||||
BaseWindow::OnWindowLeaveFullScreen();
|
BaseWindow::OnWindowLeaveFullScreen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BrowserWindow::CloseImmediately() {
|
||||||
|
// Close all child windows before closing current window.
|
||||||
|
v8::Locker locker(isolate());
|
||||||
|
v8::HandleScope handle_scope(isolate());
|
||||||
|
for (v8::Local<v8::Value> value : child_windows_.Values(isolate())) {
|
||||||
|
gin::Handle<BrowserWindow> child;
|
||||||
|
if (gin::ConvertFromV8(isolate(), value, &child) && !child.IsEmpty())
|
||||||
|
child->window()->CloseImmediately();
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseWindow::CloseImmediately();
|
||||||
|
|
||||||
|
// Do not sent "unresponsive" event after window is closed.
|
||||||
|
window_unresponsive_closure_.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
void BrowserWindow::Focus() {
|
void BrowserWindow::Focus() {
|
||||||
if (api_web_contents_->IsOffScreen())
|
if (api_web_contents_->IsOffScreen())
|
||||||
FocusOnWebView();
|
FocusOnWebView();
|
||||||
|
@ -448,17 +439,6 @@ void BrowserWindow::NotifyWindowUnresponsive() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BrowserWindow::Cleanup() {
|
|
||||||
auto* host = web_contents()->GetRenderViewHost();
|
|
||||||
if (host)
|
|
||||||
host->GetWidget()->RemoveInputEventObserver(this);
|
|
||||||
|
|
||||||
// Destroy WebContents asynchronously unless app is shutting down,
|
|
||||||
// because destroy() might be called inside WebContents's event handler.
|
|
||||||
api_web_contents_->DestroyWebContents(!Browser::Get()->is_shutting_down());
|
|
||||||
Observe(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowserWindow::OnWindowShow() {
|
void BrowserWindow::OnWindowShow() {
|
||||||
web_contents()->WasShown();
|
web_contents()->WasShown();
|
||||||
BaseWindow::OnWindowShow();
|
BaseWindow::OnWindowShow();
|
||||||
|
|
|
@ -54,6 +54,7 @@ class BrowserWindow : public BaseWindow,
|
||||||
void OnRendererUnresponsive(content::RenderProcessHost*) override;
|
void OnRendererUnresponsive(content::RenderProcessHost*) override;
|
||||||
void OnRendererResponsive(
|
void OnRendererResponsive(
|
||||||
content::RenderProcessHost* render_process_host) override;
|
content::RenderProcessHost* render_process_host) override;
|
||||||
|
void WebContentsDestroyed() override;
|
||||||
|
|
||||||
// ExtendedWebContentsObserver:
|
// ExtendedWebContentsObserver:
|
||||||
void OnCloseContents() override;
|
void OnCloseContents() override;
|
||||||
|
@ -73,11 +74,11 @@ class BrowserWindow : public BaseWindow,
|
||||||
void OnWindowIsKeyChanged(bool is_key) override;
|
void OnWindowIsKeyChanged(bool is_key) override;
|
||||||
|
|
||||||
// BaseWindow:
|
// BaseWindow:
|
||||||
void OnWindowClosed() override;
|
|
||||||
void OnWindowBlur() override;
|
void OnWindowBlur() override;
|
||||||
void OnWindowFocus() override;
|
void OnWindowFocus() override;
|
||||||
void OnWindowResize() override;
|
void OnWindowResize() override;
|
||||||
void OnWindowLeaveFullScreen() override;
|
void OnWindowLeaveFullScreen() override;
|
||||||
|
void CloseImmediately() override;
|
||||||
void Focus() override;
|
void Focus() override;
|
||||||
void Blur() override;
|
void Blur() override;
|
||||||
void SetBackgroundColor(const std::string& color_name) override;
|
void SetBackgroundColor(const std::string& color_name) override;
|
||||||
|
@ -114,9 +115,6 @@ class BrowserWindow : public BaseWindow,
|
||||||
// Dispatch unresponsive event to observers.
|
// Dispatch unresponsive event to observers.
|
||||||
void NotifyWindowUnresponsive();
|
void NotifyWindowUnresponsive();
|
||||||
|
|
||||||
// Cleanup our WebContents observers.
|
|
||||||
void Cleanup();
|
|
||||||
|
|
||||||
// Closure that would be called when window is unresponsive when closing,
|
// Closure that would be called when window is unresponsive when closing,
|
||||||
// it should be cancelled when we can prove that the window is responsive.
|
// it should be cancelled when we can prove that the window is responsive.
|
||||||
base::CancelableClosure window_unresponsive_closure_;
|
base::CancelableClosure window_unresponsive_closure_;
|
||||||
|
|
|
@ -909,51 +909,49 @@ void WebContents::InitWithWebContents(content::WebContents* web_contents,
|
||||||
}
|
}
|
||||||
|
|
||||||
WebContents::~WebContents() {
|
WebContents::~WebContents() {
|
||||||
MarkDestroyed();
|
if (!inspectable_web_contents_) {
|
||||||
// The destroy() is called.
|
|
||||||
if (inspectable_web_contents_) {
|
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
|
||||||
if (type_ == Type::kBackgroundPage) {
|
|
||||||
// Background pages are owned by extensions::ExtensionHost
|
|
||||||
inspectable_web_contents_->ReleaseWebContents();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inspectable_web_contents_->GetView()->SetDelegate(nullptr);
|
|
||||||
|
|
||||||
if (web_contents()) {
|
|
||||||
RenderViewDeleted(web_contents()->GetRenderViewHost());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type_ == Type::kBrowserWindow && owner_window()) {
|
|
||||||
// For BrowserWindow we should close the window and clean up everything
|
|
||||||
// before WebContents is destroyed.
|
|
||||||
for (ExtendedWebContentsObserver& observer : observers_)
|
|
||||||
observer.OnCloseContents();
|
|
||||||
// BrowserWindow destroys WebContents asynchronously, manually emit the
|
|
||||||
// destroyed event here.
|
|
||||||
WebContentsDestroyed();
|
WebContentsDestroyed();
|
||||||
} else if (Browser::Get()->is_shutting_down()) {
|
return;
|
||||||
// Destroy WebContents directly when app is shutting down.
|
|
||||||
DestroyWebContents(false /* async */);
|
|
||||||
} else {
|
|
||||||
// Destroy WebContents asynchronously unless app is shutting down,
|
|
||||||
// because destroy() might be called inside WebContents's event handler.
|
|
||||||
bool is_browser_view = type_ == Type::kBrowserView;
|
|
||||||
DestroyWebContents(!(IsGuest() || is_browser_view) /* async */);
|
|
||||||
// The WebContentsDestroyed will not be called automatically because we
|
|
||||||
// destroy the webContents in the next tick. So we have to manually
|
|
||||||
// call it here to make sure "destroyed" event is emitted.
|
|
||||||
WebContentsDestroyed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::DestroyWebContents(bool async) {
|
if (guest_delegate_)
|
||||||
|
guest_delegate_->WillDestroy();
|
||||||
|
|
||||||
// This event is only for internal use, which is emitted when WebContents is
|
// This event is only for internal use, which is emitted when WebContents is
|
||||||
// being destroyed.
|
// being destroyed.
|
||||||
Emit("will-destroy");
|
Emit("will-destroy");
|
||||||
ResetManagedWebContents(async);
|
|
||||||
|
// For guest view based on OOPIF, the WebContents is released by the embedder
|
||||||
|
// frame, and we need to clear the reference to the memory.
|
||||||
|
bool not_owned_by_this = IsGuest() && attached_;
|
||||||
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
// And background pages are owned by extensions::ExtensionHost.
|
||||||
|
if (type_ == Type::kBackgroundPage)
|
||||||
|
not_owned_by_this = true;
|
||||||
|
#endif
|
||||||
|
if (not_owned_by_this) {
|
||||||
|
inspectable_web_contents_->ReleaseWebContents();
|
||||||
|
WebContentsDestroyed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// InspectableWebContents will be automatically destroyed.
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebContents::Destroy() {
|
||||||
|
// The content::WebContents should be destroyed asyncronously when possible
|
||||||
|
// as user may choose to destroy WebContents during an event of it.
|
||||||
|
if (Browser::Get()->is_shutting_down() || IsGuest() ||
|
||||||
|
type_ == Type::kBrowserView) {
|
||||||
|
delete this;
|
||||||
|
} else {
|
||||||
|
base::PostTask(FROM_HERE, {content::BrowserThread::UI},
|
||||||
|
base::BindOnce(
|
||||||
|
[](base::WeakPtr<WebContents> contents) {
|
||||||
|
if (contents)
|
||||||
|
delete contents.get();
|
||||||
|
},
|
||||||
|
GetWeakPtr()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebContents::DidAddMessageToConsole(
|
bool WebContents::DidAddMessageToConsole(
|
||||||
|
@ -1063,7 +1061,7 @@ void WebContents::AddNewContents(
|
||||||
initial_rect.x(), initial_rect.y(), initial_rect.width(),
|
initial_rect.x(), initial_rect.y(), initial_rect.width(),
|
||||||
initial_rect.height(), tracker->url, tracker->frame_name,
|
initial_rect.height(), tracker->url, tracker->frame_name,
|
||||||
tracker->referrer, tracker->raw_features, tracker->body)) {
|
tracker->referrer, tracker->raw_features, tracker->body)) {
|
||||||
api_web_contents->DestroyWebContents(false /* async */);
|
api_web_contents->Destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1133,8 +1131,6 @@ void WebContents::CloseContents(content::WebContents* source) {
|
||||||
autofill_driver_factory->CloseAllPopups();
|
autofill_driver_factory->CloseAllPopups();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inspectable_web_contents_)
|
|
||||||
inspectable_web_contents_->GetView()->SetDelegate(nullptr);
|
|
||||||
for (ExtendedWebContentsObserver& observer : observers_)
|
for (ExtendedWebContentsObserver& observer : observers_)
|
||||||
observer.OnCloseContents();
|
observer.OnCloseContents();
|
||||||
}
|
}
|
||||||
|
@ -1811,21 +1807,6 @@ void WebContents::SetOwnerWindow(content::WebContents* web_contents,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::ResetManagedWebContents(bool async) {
|
|
||||||
if (async) {
|
|
||||||
// Browser context should be destroyed only after the WebContents,
|
|
||||||
// this is guaranteed in the sync mode by the order of declaration,
|
|
||||||
// in the async version we maintain a reference until the WebContents
|
|
||||||
// is destroyed.
|
|
||||||
// //electron/patches/chromium/content_browser_main_loop.patch
|
|
||||||
// is required to get the right quit closure for the main message loop.
|
|
||||||
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
|
|
||||||
FROM_HERE, inspectable_web_contents_.release());
|
|
||||||
} else {
|
|
||||||
inspectable_web_contents_.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
content::WebContents* WebContents::GetWebContents() const {
|
content::WebContents* WebContents::GetWebContents() const {
|
||||||
if (!inspectable_web_contents_)
|
if (!inspectable_web_contents_)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1838,7 +1819,8 @@ content::WebContents* WebContents::GetDevToolsWebContents() const {
|
||||||
return inspectable_web_contents_->GetDevToolsWebContents();
|
return inspectable_web_contents_->GetDevToolsWebContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::MarkDestroyed() {
|
void WebContents::WebContentsDestroyed() {
|
||||||
|
// Clear the pointer stored in wrapper.
|
||||||
if (GetAllWebContents().Lookup(id_))
|
if (GetAllWebContents().Lookup(id_))
|
||||||
GetAllWebContents().Remove(id_);
|
GetAllWebContents().Remove(id_);
|
||||||
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
|
||||||
|
@ -1847,52 +1829,9 @@ void WebContents::MarkDestroyed() {
|
||||||
if (!GetWrapper(isolate).ToLocal(&wrapper))
|
if (!GetWrapper(isolate).ToLocal(&wrapper))
|
||||||
return;
|
return;
|
||||||
wrapper->SetAlignedPointerInInternalField(0, nullptr);
|
wrapper->SetAlignedPointerInInternalField(0, nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
// There are three ways of destroying a webContents:
|
Observe(nullptr);
|
||||||
// 1. call webContents.destroy();
|
|
||||||
// 2. garbage collection;
|
|
||||||
// 3. user closes the window of webContents;
|
|
||||||
// 4. the embedder detaches the frame.
|
|
||||||
// For webview only #4 will happen, for BrowserWindow both #1 and #3 may
|
|
||||||
// happen. The #2 should never happen for webContents, because webview is
|
|
||||||
// managed by GuestViewManager, and BrowserWindow's webContents is managed
|
|
||||||
// by api::BrowserWindow.
|
|
||||||
// For #1, the destructor will do the cleanup work and we only need to make
|
|
||||||
// sure "destroyed" event is emitted. For #3, the content::WebContents will
|
|
||||||
// be destroyed on close, and WebContentsDestroyed would be called for it, so
|
|
||||||
// we need to make sure the api::WebContents is also deleted.
|
|
||||||
// For #4, the WebContents will be destroyed by embedder.
|
|
||||||
void WebContents::WebContentsDestroyed() {
|
|
||||||
// Give chance for guest delegate to cleanup its observers
|
|
||||||
// since the native class is only destroyed in the next tick.
|
|
||||||
if (guest_delegate_)
|
|
||||||
guest_delegate_->WillDestroy();
|
|
||||||
|
|
||||||
// Cleanup relationships with other parts.
|
|
||||||
|
|
||||||
// We can not call Destroy here because we need to call Emit first, but we
|
|
||||||
// also do not want any method to be used, so just mark as destroyed here.
|
|
||||||
MarkDestroyed();
|
|
||||||
|
|
||||||
Observe(nullptr); // this->web_contents() will return nullptr
|
|
||||||
Emit("destroyed");
|
Emit("destroyed");
|
||||||
|
|
||||||
// For guest view based on OOPIF, the WebContents is released by the embedder
|
|
||||||
// frame, and we need to clear the reference to the memory.
|
|
||||||
if (IsGuest() && inspectable_web_contents_) {
|
|
||||||
inspectable_web_contents_->ReleaseWebContents();
|
|
||||||
ResetManagedWebContents(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy the native class in next tick.
|
|
||||||
base::ThreadTaskRunnerHandle::Get()->PostTask(
|
|
||||||
FROM_HERE, base::BindOnce(
|
|
||||||
[](base::WeakPtr<WebContents> wc) {
|
|
||||||
if (wc)
|
|
||||||
delete wc.get();
|
|
||||||
},
|
|
||||||
GetWeakPtr()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContents::NavigationEntryCommitted(
|
void WebContents::NavigationEntryCommitted(
|
||||||
|
@ -2885,6 +2824,7 @@ bool WebContents::IsGuest() const {
|
||||||
|
|
||||||
void WebContents::AttachToIframe(content::WebContents* embedder_web_contents,
|
void WebContents::AttachToIframe(content::WebContents* embedder_web_contents,
|
||||||
int embedder_frame_id) {
|
int embedder_frame_id) {
|
||||||
|
attached_ = true;
|
||||||
if (guest_delegate_)
|
if (guest_delegate_)
|
||||||
guest_delegate_->AttachToIframe(embedder_web_contents, embedder_frame_id);
|
guest_delegate_->AttachToIframe(embedder_web_contents, embedder_frame_id);
|
||||||
}
|
}
|
||||||
|
@ -3537,17 +3477,11 @@ v8::Local<v8::ObjectTemplate> WebContents::FillObjectTemplate(
|
||||||
gin::CreateFunctionTemplate(
|
gin::CreateFunctionTemplate(
|
||||||
isolate, base::BindRepeating(&gin_helper::Destroyable::IsDestroyed),
|
isolate, base::BindRepeating(&gin_helper::Destroyable::IsDestroyed),
|
||||||
options));
|
options));
|
||||||
templ->Set(
|
|
||||||
gin::StringToSymbol(isolate, "destroy"),
|
|
||||||
gin::CreateFunctionTemplate(
|
|
||||||
isolate, base::BindRepeating([](gin::Handle<WebContents> handle) {
|
|
||||||
delete handle.get();
|
|
||||||
}),
|
|
||||||
options));
|
|
||||||
// We use gin_helper::ObjectTemplateBuilder instead of
|
// We use gin_helper::ObjectTemplateBuilder instead of
|
||||||
// gin::ObjectTemplateBuilder here to handle the fact that WebContents is
|
// gin::ObjectTemplateBuilder here to handle the fact that WebContents is
|
||||||
// destroyable.
|
// destroyable.
|
||||||
return gin_helper::ObjectTemplateBuilder(isolate, templ)
|
return gin_helper::ObjectTemplateBuilder(isolate, templ)
|
||||||
|
.SetMethod("destroy", &WebContents::Destroy)
|
||||||
.SetMethod("getBackgroundThrottling",
|
.SetMethod("getBackgroundThrottling",
|
||||||
&WebContents::GetBackgroundThrottling)
|
&WebContents::GetBackgroundThrottling)
|
||||||
.SetMethod("setBackgroundThrottling",
|
.SetMethod("setBackgroundThrottling",
|
||||||
|
|
|
@ -144,23 +144,9 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
v8::Local<v8::ObjectTemplate>);
|
v8::Local<v8::ObjectTemplate>);
|
||||||
const char* GetTypeName() override;
|
const char* GetTypeName() override;
|
||||||
|
|
||||||
|
void Destroy();
|
||||||
base::WeakPtr<WebContents> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
|
base::WeakPtr<WebContents> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
|
||||||
|
|
||||||
// Destroy the managed content::WebContents instance.
|
|
||||||
//
|
|
||||||
// Note: The |async| should only be |true| when users are expecting to use the
|
|
||||||
// webContents immediately after the call. Always pass |false| if you are not
|
|
||||||
// sure.
|
|
||||||
// See https://github.com/electron/electron/issues/8930.
|
|
||||||
//
|
|
||||||
// Note: When destroying a webContents member inside a destructor, the |async|
|
|
||||||
// should always be |false|, otherwise the destroy task might be delayed after
|
|
||||||
// normal shutdown procedure, resulting in an assertion.
|
|
||||||
// The normal pattern for calling this method in destructor is:
|
|
||||||
// api_web_contents_->DestroyWebContents(!Browser::Get()->is_shutting_down())
|
|
||||||
// See https://github.com/electron/electron/issues/15133.
|
|
||||||
void DestroyWebContents(bool async);
|
|
||||||
|
|
||||||
bool GetBackgroundThrottling() const;
|
bool GetBackgroundThrottling() const;
|
||||||
void SetBackgroundThrottling(bool allowed);
|
void SetBackgroundThrottling(bool allowed);
|
||||||
int GetProcessID() const;
|
int GetProcessID() const;
|
||||||
|
@ -362,8 +348,6 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
return EmitCustomEvent(name, event, std::forward<Args>(args)...);
|
return EmitCustomEvent(name, event, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MarkDestroyed();
|
|
||||||
|
|
||||||
WebContents* embedder() { return embedder_; }
|
WebContents* embedder() { return embedder_; }
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
@ -672,9 +656,6 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
std::string* class_name) override;
|
std::string* class_name) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Destroy the managed InspectableWebContents object.
|
|
||||||
void ResetManagedWebContents(bool async);
|
|
||||||
|
|
||||||
// DevTools index event callbacks.
|
// DevTools index event callbacks.
|
||||||
void OnDevToolsIndexingWorkCalculated(int request_id,
|
void OnDevToolsIndexingWorkCalculated(int request_id,
|
||||||
const std::string& file_system_path,
|
const std::string& file_system_path,
|
||||||
|
@ -706,6 +687,9 @@ class WebContents : public gin::Wrappable<WebContents>,
|
||||||
// The host webcontents that may contain this webcontents.
|
// The host webcontents that may contain this webcontents.
|
||||||
WebContents* embedder_ = nullptr;
|
WebContents* embedder_ = nullptr;
|
||||||
|
|
||||||
|
// Whether the guest view has been attached.
|
||||||
|
bool attached_ = false;
|
||||||
|
|
||||||
// The zoom controller for this webContents.
|
// The zoom controller for this webContents.
|
||||||
WebContentsZoomController* zoom_controller_ = nullptr;
|
WebContentsZoomController* zoom_controller_ = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -44,18 +44,8 @@ WebContentsView::WebContentsView(v8::Isolate* isolate,
|
||||||
}
|
}
|
||||||
|
|
||||||
WebContentsView::~WebContentsView() {
|
WebContentsView::~WebContentsView() {
|
||||||
if (api_web_contents_) { // destroy() is called
|
if (api_web_contents_) // destroy() called without closing WebContents
|
||||||
// Destroy WebContents asynchronously, as we might be in GC currently and
|
api_web_contents_->Destroy();
|
||||||
// WebContents emits an event in its destructor.
|
|
||||||
base::PostTask(FROM_HERE, {content::BrowserThread::UI},
|
|
||||||
base::BindOnce(
|
|
||||||
[](base::WeakPtr<WebContents> contents) {
|
|
||||||
if (contents)
|
|
||||||
contents->DestroyWebContents(
|
|
||||||
!Browser::Get()->is_shutting_down());
|
|
||||||
},
|
|
||||||
api_web_contents_->GetWeakPtr()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gin::Handle<WebContents> WebContentsView::GetWebContents(v8::Isolate* isolate) {
|
gin::Handle<WebContents> WebContentsView::GetWebContents(v8::Isolate* isolate) {
|
||||||
|
|
|
@ -34,13 +34,13 @@ using electron::InspectableWebContentsViewMac;
|
||||||
BOOL devtools_visible_;
|
BOOL devtools_visible_;
|
||||||
BOOL devtools_docked_;
|
BOOL devtools_docked_;
|
||||||
BOOL devtools_is_first_responder_;
|
BOOL devtools_is_first_responder_;
|
||||||
|
BOOL attached_to_window_;
|
||||||
|
|
||||||
DevToolsContentsResizingStrategy strategy_;
|
DevToolsContentsResizingStrategy strategy_;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithInspectableWebContentsViewMac:
|
- (instancetype)initWithInspectableWebContentsViewMac:
|
||||||
(InspectableWebContentsViewMac*)view;
|
(InspectableWebContentsViewMac*)view;
|
||||||
- (void)removeObservers;
|
|
||||||
- (void)notifyDevToolsFocused;
|
- (void)notifyDevToolsFocused;
|
||||||
- (void)setDevToolsVisible:(BOOL)visible activate:(BOOL)activate;
|
- (void)setDevToolsVisible:(BOOL)visible activate:(BOOL)activate;
|
||||||
- (BOOL)isDevToolsVisible;
|
- (BOOL)isDevToolsVisible;
|
||||||
|
|
|
@ -35,18 +35,7 @@
|
||||||
devtools_visible_ = NO;
|
devtools_visible_ = NO;
|
||||||
devtools_docked_ = NO;
|
devtools_docked_ = NO;
|
||||||
devtools_is_first_responder_ = NO;
|
devtools_is_first_responder_ = NO;
|
||||||
|
attached_to_window_ = NO;
|
||||||
[[NSNotificationCenter defaultCenter]
|
|
||||||
addObserver:self
|
|
||||||
selector:@selector(viewDidBecomeFirstResponder:)
|
|
||||||
name:kViewDidBecomeFirstResponder
|
|
||||||
object:nil];
|
|
||||||
|
|
||||||
[[NSNotificationCenter defaultCenter]
|
|
||||||
addObserver:self
|
|
||||||
selector:@selector(parentWindowBecameMain:)
|
|
||||||
name:NSWindowDidBecomeMainNotification
|
|
||||||
object:nil];
|
|
||||||
|
|
||||||
if (inspectableWebContentsView_->inspectable_web_contents()->IsGuest()) {
|
if (inspectableWebContentsView_->inspectable_web_contents()->IsGuest()) {
|
||||||
fake_view_.reset([[NSView alloc] init]);
|
fake_view_.reset([[NSView alloc] init]);
|
||||||
|
@ -69,14 +58,34 @@
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)removeObservers {
|
- (void)dealloc {
|
||||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
[super dealloc];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
|
- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
|
||||||
[self adjustSubviews];
|
[self adjustSubviews];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)viewDidMoveToWindow {
|
||||||
|
if (attached_to_window_ && !self.window) {
|
||||||
|
attached_to_window_ = NO;
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||||
|
} else if (!attached_to_window_ && self.window) {
|
||||||
|
attached_to_window_ = YES;
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(viewDidBecomeFirstResponder:)
|
||||||
|
name:kViewDidBecomeFirstResponder
|
||||||
|
object:nil];
|
||||||
|
[[NSNotificationCenter defaultCenter]
|
||||||
|
addObserver:self
|
||||||
|
selector:@selector(parentWindowBecameMain:)
|
||||||
|
name:NSWindowDidBecomeMainNotification
|
||||||
|
object:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (IBAction)showDevTools:(id)sender {
|
- (IBAction)showDevTools:(id)sender {
|
||||||
inspectableWebContentsView_->inspectable_web_contents()->ShowDevTools(true);
|
inspectableWebContentsView_->inspectable_web_contents()->ShowDevTools(true);
|
||||||
}
|
}
|
||||||
|
@ -253,8 +262,7 @@
|
||||||
- (void)viewDidBecomeFirstResponder:(NSNotification*)notification {
|
- (void)viewDidBecomeFirstResponder:(NSNotification*)notification {
|
||||||
auto* inspectable_web_contents =
|
auto* inspectable_web_contents =
|
||||||
inspectableWebContentsView_->inspectable_web_contents();
|
inspectableWebContentsView_->inspectable_web_contents();
|
||||||
if (!inspectable_web_contents || inspectable_web_contents->IsGuest())
|
DCHECK(inspectable_web_contents);
|
||||||
return;
|
|
||||||
auto* webContents = inspectable_web_contents->GetWebContents();
|
auto* webContents = inspectable_web_contents->GetWebContents();
|
||||||
auto* webContentsView = webContents->GetNativeView().GetNativeNSView();
|
auto* webContentsView = webContents->GetNativeView().GetNativeNSView();
|
||||||
|
|
||||||
|
|
|
@ -371,13 +371,8 @@ InspectableWebContents::InspectableWebContents(
|
||||||
InspectableWebContents::~InspectableWebContents() {
|
InspectableWebContents::~InspectableWebContents() {
|
||||||
g_web_contents_instances_.remove(this);
|
g_web_contents_instances_.remove(this);
|
||||||
// Unsubscribe from devtools and Clean up resources.
|
// Unsubscribe from devtools and Clean up resources.
|
||||||
if (GetDevToolsWebContents()) {
|
if (GetDevToolsWebContents())
|
||||||
if (managed_devtools_web_contents_)
|
|
||||||
managed_devtools_web_contents_->SetDelegate(nullptr);
|
|
||||||
// Calling this also unsubscribes the observer, so WebContentsDestroyed
|
|
||||||
// won't be called again.
|
|
||||||
WebContentsDestroyed();
|
WebContentsDestroyed();
|
||||||
}
|
|
||||||
// Let destructor destroy managed_devtools_web_contents_.
|
// Let destructor destroy managed_devtools_web_contents_.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,6 +411,8 @@ bool InspectableWebContents::IsGuest() const {
|
||||||
|
|
||||||
void InspectableWebContents::ReleaseWebContents() {
|
void InspectableWebContents::ReleaseWebContents() {
|
||||||
web_contents_.release();
|
web_contents_.release();
|
||||||
|
WebContentsDestroyed();
|
||||||
|
view_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InspectableWebContents::SetDockState(const std::string& state) {
|
void InspectableWebContents::SetDockState(const std::string& state) {
|
||||||
|
@ -936,6 +933,9 @@ void InspectableWebContents::RenderFrameHostChanged(
|
||||||
}
|
}
|
||||||
|
|
||||||
void InspectableWebContents::WebContentsDestroyed() {
|
void InspectableWebContents::WebContentsDestroyed() {
|
||||||
|
if (managed_devtools_web_contents_)
|
||||||
|
managed_devtools_web_contents_->SetDelegate(nullptr);
|
||||||
|
|
||||||
frontend_loaded_ = false;
|
frontend_loaded_ = false;
|
||||||
external_devtools_web_contents_ = nullptr;
|
external_devtools_web_contents_ = nullptr;
|
||||||
Observe(nullptr);
|
Observe(nullptr);
|
||||||
|
|
|
@ -26,7 +26,6 @@ InspectableWebContentsViewMac::InspectableWebContentsViewMac(
|
||||||
initWithInspectableWebContentsViewMac:this]) {}
|
initWithInspectableWebContentsViewMac:this]) {}
|
||||||
|
|
||||||
InspectableWebContentsViewMac::~InspectableWebContentsViewMac() {
|
InspectableWebContentsViewMac::~InspectableWebContentsViewMac() {
|
||||||
[view_ removeObservers];
|
|
||||||
CloseDevTools();
|
CloseDevTools();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue