From 8a21e63d3906cbc9455bfad975bb4e06e303c059 Mon Sep 17 00:00:00 2001 From: bridiver Date: Wed, 29 Mar 2017 17:43:53 -0700 Subject: [PATCH] attach/detach --- atom/atom_resources.grd | 4 + atom/browser/api/atom_api_app.cc | 2 - atom/browser/api/atom_api_web_contents.cc | 30 ++--- atom/browser/extensions/tab_helper.cc | 118 ++++++++++++++++-- atom/browser/extensions/tab_helper.h | 36 +++++- .../api/resources/guest_view_api_bindings.js | 12 ++ .../api/resources/web_view_api_bindings.js | 80 +++++++----- .../resources/web_view_events_api_bindings.js | 38 ++++++ brave/browser/BUILD.gn | 15 +++ .../guest_view/tab_view/tab_view_guest.cc | 66 ++++++++-- .../guest_view/tab_view/tab_view_guest.h | 5 + brave/browser/memory/guest_tab_manager.cc | 51 ++++++++ brave/browser/memory/guest_tab_manager.h | 35 ++++++ chromium_src/BUILD.gn | 2 +- .../chrome/browser/browser_process_impl.cc | 4 +- .../chrome/browser/ui/tabs/tab_strip_model.cc | 23 ++-- .../chrome_extensions_dispatcher_delegate.cc | 6 + lib/browser/guest-view-manager.js | 20 +-- lib/renderer/web-view/guest-view-internal.js | 1 + patches/master_patch.patch | 92 ++++++++++++++ 20 files changed, 543 insertions(+), 97 deletions(-) create mode 100644 atom/common/api/resources/guest_view_api_bindings.js create mode 100644 atom/common/api/resources/web_view_events_api_bindings.js create mode 100644 brave/browser/memory/guest_tab_manager.cc create mode 100644 brave/browser/memory/guest_tab_manager.h diff --git a/atom/atom_resources.grd b/atom/atom_resources.grd index 4336751948..2549ba1542 100644 --- a/atom/atom_resources.grd +++ b/atom/atom_resources.grd @@ -25,6 +25,10 @@ + + + + diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index 31fca948e4..4a03faf13a 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -532,8 +532,6 @@ void App::Observe( content::WebContents* web_contents = content::Source(source).ptr(); auto browser_context = web_contents->GetBrowserContext(); - auto url = web_contents->GetURL(); - #if BUILDFLAG(ENABLE_EXTENSIONS) // make sure background pages get a webcontents // api wrapper so they can communicate via IPC diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 0a75cbae52..523cc910d3 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -1747,6 +1747,7 @@ void WebContents::Clone(mate::Arguments* args) { } else { options = mate::Dictionary::CreateEmpty(isolate()); } + options.Set("userGesture", true); base::Callback callback; if (!args->GetNext(&callback)) { @@ -1766,18 +1767,8 @@ void WebContents::Clone(mate::Arguments* args) { create_params.SetBoolean("clone", true); - auto guest_view_manager = - static_cast(GetBrowserContext()->GetGuestManager()); - - if (!guest_view_manager) { - callback.Run(nullptr); - return; - } - - options.Set("userGesture", true); - - guest_view_manager->CreateGuest(brave::TabViewGuest::Type, - HostWebContents(), + extensions::TabHelper::CreateTab(HostWebContents(), + GetBrowserContext(), create_params, base::Bind(&WebContents::OnCloneCreated, base::Unretained(this), options, base::Bind(&WebContents::OnTabCreated, base::Unretained(this), @@ -1840,6 +1831,8 @@ void WebContents::Discard() { if (tab_helper) { if (!Emit("will-discard") && tab_helper->Discard()) Emit("discarded"); + else + Emit("discard-aborted"); } } @@ -2216,6 +2209,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("getContentWindowId", &WebContents::GetContentWindowId) .SetMethod("setActive", &WebContents::SetActive) .SetMethod("setTabIndex", &WebContents::SetTabIndex) + .SetMethod("discard", &WebContents::Discard) .SetMethod("setWebRTCIPHandlingPolicy", &WebContents::SetWebRTCIPHandlingPolicy) .SetMethod("getWebRTCIPHandlingPolicy", @@ -2356,14 +2350,6 @@ void WebContents::CreateTab(mate::Arguments* args) { auto browser_context = session->browser_context(); - auto guest_view_manager = - static_cast(browser_context->GetGuestManager()); - - if (!guest_view_manager) { - args->ThrowError("No guest view manager"); - return; - } - base::DictionaryValue create_params; std::string src; if (options.Get("src", &src) || options.Get("url", &src)) { @@ -2373,8 +2359,8 @@ void WebContents::CreateTab(mate::Arguments* args) { static_cast( browser_context)->partition_with_prefix()); - guest_view_manager->CreateGuest(brave::TabViewGuest::Type, - owner->web_contents(), + extensions::TabHelper::CreateTab(owner->web_contents(), + browser_context, create_params, base::Bind(&WebContents::OnTabCreated, base::Unretained(owner), options, callback)); diff --git a/atom/browser/extensions/tab_helper.cc b/atom/browser/extensions/tab_helper.cc index 4ac46a1c07..b24cfa277d 100644 --- a/atom/browser/extensions/tab_helper.cc +++ b/atom/browser/extensions/tab_helper.cc @@ -12,6 +12,7 @@ #include "atom/common/native_mate_converters/gurl_converter.h" #include "atom/common/native_mate_converters/value_converter.h" #include "base/strings/utf_string_conversions.h" +#include "brave/browser/guest_view/tab_view/tab_view_guest.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/memory/tab_manager.h" #include "chrome/browser/sessions/session_tab_helper.h" @@ -35,6 +36,7 @@ #include "net/base/filename_util.h" #include "ui/base/resource/resource_bundle.h" +using guest_view::GuestViewManager; using memory::TabManager; DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::TabHelper); @@ -81,9 +83,97 @@ TabHelper::~TabHelper() { BrowserList::RemoveObserver(this); } +// static +void TabHelper::CreateTab(content::WebContents* owner, + content::BrowserContext* browser_context, + const base::DictionaryValue& create_params, + const GuestViewManager::WebContentsCreatedCallback& callback) { + + auto guest_view_manager = + static_cast(browser_context->GetGuestManager()); + DCHECK(guest_view_manager); + + guest_view_manager->CreateGuest(brave::TabViewGuest::Type, + owner, + create_params, + callback); +} + +// static +content::WebContents* TabHelper::CreateTab(content::WebContents* owner, + content::WebContents::CreateParams create_params) { + auto guest_view_manager = static_cast( + create_params.browser_context->GetGuestManager()); + DCHECK(guest_view_manager); + + return guest_view_manager->CreateGuestWithWebContentsParams( + brave::TabViewGuest::Type, + owner, + create_params); +} + +// static +void TabHelper::DestroyTab(content::WebContents* tab) { + auto guest = brave::TabViewGuest::FromWebContents(tab); + DCHECK(guest); + guest->Destroy(); +} + void TabHelper::OnBrowserRemoved(Browser* browser) { - if (browser_ == browser) + if (browser_ != nullptr && browser_ == browser) { + index_ = TabStripModel::kNoTab; + browser_->tab_strip_model()->RemoveObserver(this); browser_ = nullptr; + } +} + +void TabHelper::TabInsertedAt(TabStripModel* tab_strip_model, + content::WebContents* contents, + int index, + bool foreground) { + if (contents != web_contents()) + return; + + DCHECK(index != TabStripModel::kNoTab); + index_ = index; + // TODO(bridiver) - deal with foreground +} + +void TabHelper::TabReplacedAt(TabStripModel* tab_strip_model, + content::WebContents* old_contents, + content::WebContents* new_contents, + int index) { + if (old_contents != web_contents()) + return; + + auto old_browser = browser_; + + brave::TabViewGuest* old_guest = guest(); + int guest_instance_id = old_guest->guest_instance_id(); + + auto new_helper = TabHelper::FromWebContents(new_contents); + new_helper->index_ = index_; + new_helper->pinned_ = pinned_; + + OnBrowserRemoved(old_browser); + new_helper->UpdateBrowser(old_browser); + + brave::TabViewGuest* new_guest = new_helper->guest(); + // always attach first because detach disconnects the webview + old_guest->AttachGuest(new_guest->guest_instance_id()); + old_guest->DetachGuest(false); +} + +void TabHelper::TabDetachedAt(content::WebContents* contents, int index) { + if (contents != web_contents()) + return; + + OnBrowserRemoved(browser_); +} + +void TabHelper::UpdateBrowser(Browser* browser) { + browser_ = browser; + browser_->tab_strip_model()->AddObserver(this); } void TabHelper::SetBrowser(Browser* browser) { @@ -93,13 +183,16 @@ void TabHelper::SetBrowser(Browser* browser) { if (browser_) { if (index_ != TabStripModel::kNoTab) browser_->tab_strip_model()->DetachWebContentsAt(index_); - } - if (!browser) - return; + OnBrowserRemoved(browser_); + } - browser_ = browser; - browser_->tab_strip_model()->AppendWebContents(web_contents(), false); + if (browser) { + UpdateBrowser(browser); + browser_->tab_strip_model()->AppendWebContents(web_contents(), false); + } else { + browser_ = nullptr; + } } void TabHelper::SetWindowId(const int32_t& id) { @@ -119,10 +212,11 @@ void TabHelper::SetAutoDiscardable(bool auto_discardable) { bool TabHelper::Discard() { int64_t web_contents_id = TabManager::IdFromWebContents(web_contents()); - if (g_browser_process->GetTabManager()->DiscardTabById(web_contents_id)) - return true; + return !!g_browser_process->GetTabManager()->DiscardTabById(web_contents_id); +} - return false; +bool TabHelper::IsDiscarded() { + return g_browser_process->GetTabManager()->IsTabDiscarded(web_contents()); } void TabHelper::SetPinned(bool pinned) { @@ -142,6 +236,12 @@ bool TabHelper::is_active() const { } } +brave::TabViewGuest* TabHelper::guest() const { + auto guest = brave::TabViewGuest::FromWebContents(web_contents()); + DCHECK(guest); + return guest; +} + void TabHelper::SetTabValues(const base::DictionaryValue& values) { values_->MergeDictionary(&values); } diff --git a/atom/browser/extensions/tab_helper.h b/atom/browser/extensions/tab_helper.h index a49dfb5ebb..ddcb938fd2 100644 --- a/atom/browser/extensions/tab_helper.h +++ b/atom/browser/extensions/tab_helper.h @@ -10,6 +10,8 @@ #include "base/macros.h" #include "chrome/browser/ui/browser_list_observer.h" +#include "chrome/browser/ui/tabs/tab_strip_model_observer.h" +#include "components/guest_view/browser/guest_view_manager.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" #include "extensions/browser/extension_function_dispatcher.h" @@ -22,6 +24,10 @@ namespace base { class DictionaryValue; } +namespace brave { +class TabViewGuest; +} + namespace content { class BrowserContext; class RenderFrameHost; @@ -42,6 +48,8 @@ extern const char kAudibleKey[]; extern const char kMutedKey[]; } +using guest_view::GuestViewManager; + namespace extensions { class Extension; @@ -50,10 +58,19 @@ class Extension; // window of the tab. class TabHelper : public content::WebContentsObserver, public content::WebContentsUserData, - public chrome::BrowserListObserver { + public chrome::BrowserListObserver, + public TabStripModelObserver { public: ~TabHelper() override; + static void CreateTab(content::WebContents* owner, + content::BrowserContext* browser_context, + const base::DictionaryValue& create_params, + const GuestViewManager::WebContentsCreatedCallback& callback); + static content::WebContents* CreateTab(content::WebContents* owner, + content::WebContents::CreateParams create_params); + static void DestroyTab(content::WebContents* tab); + // Identifier of the tab. void SetTabId(content::RenderFrameHost* render_frame_host); int32_t session_id() const; @@ -72,6 +89,8 @@ class TabHelper : public content::WebContentsObserver, bool Discard(); + bool IsDiscarded(); + void SetTabValues(const base::DictionaryValue& values); base::DictionaryValue* getTabValues() { return values_.get(); @@ -87,6 +106,8 @@ class TabHelper : public content::WebContentsObserver, return browser_; } + brave::TabViewGuest* guest() const; + int get_index() const { return index_; } bool is_pinned() const { return pinned_; } bool is_active() const; @@ -112,7 +133,18 @@ class TabHelper : public content::WebContentsObserver, explicit TabHelper(content::WebContents* contents); friend class content::WebContentsUserData; - void OnBrowserRemoved(Browser* browser); + void TabDetachedAt(content::WebContents* contents, int index) override; + void TabInsertedAt(TabStripModel* tab_strip_model, + content::WebContents* contents, + int index, + bool foreground) override; + void TabReplacedAt(TabStripModel* tab_strip_model, + content::WebContents* old_contents, + content::WebContents* new_contents, + int index) override; + void OnBrowserRemoved(Browser* browser) override; + void UpdateBrowser(Browser* browser); + void ExecuteScript( std::string extension_id, std::unique_ptr options, diff --git a/atom/common/api/resources/guest_view_api_bindings.js b/atom/common/api/resources/guest_view_api_bindings.js new file mode 100644 index 0000000000..7edff7196a --- /dev/null +++ b/atom/common/api/resources/guest_view_api_bindings.js @@ -0,0 +1,12 @@ +// Copyright 2014 The Brave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This module implements the public-facing API functions for the tag. + +const GuestView = require('guestView').GuestView; + +GuestView.prototype.getState = function() { + var internal = privates(this).internal; + return internal.state +} diff --git a/atom/common/api/resources/web_view_api_bindings.js b/atom/common/api/resources/web_view_api_bindings.js index d1813e730e..737ff36434 100644 --- a/atom/common/api/resources/web_view_api_bindings.js +++ b/atom/common/api/resources/web_view_api_bindings.js @@ -8,7 +8,10 @@ const GuestViewInternal = require('guest-view-internal').GuestViewInternal const TabViewInternal = require('tabViewInternal').TabViewInternal; const WebViewInternal = require('webViewInternal').WebViewInternal; const WebViewImpl = require('webView').WebViewImpl; +const GuestViewImpl = require('guestView').GuestViewImpl const remote = require('remote') +const GuestViewContainer = require('guestViewContainer').GuestViewContainer; +const GuestView = require('guestView').GuestView; const asyncMethods = [ 'loadURL', @@ -62,14 +65,14 @@ const syncMethods = [ 'getCurrentEntryIndex', 'getURLAtIndex', 'getTitleAtIndex', - 'getId', 'isFocused', 'getZoomPercent', 'getURL', ] var WEB_VIEW_API_METHODS = [ - 'setGuestInstanceId', + 'attachGuest', + 'detachGuest', // Returns Chrome's internal process ID for the guest web page's current // process. 'getProcessId', @@ -77,12 +80,10 @@ var WEB_VIEW_API_METHODS = [ asyncMethods.forEach((method) => { WebViewImpl.prototype[method] = function () { - if (!this.guest.getId()) + if (!this.tabID) return - this.getTabID(this.guest.getId(), (tabID) => { - remote.callAsyncWebContentsFunction(tabID, method, arguments) - }) + remote.callAsyncWebContentsFunction(this.tabID, method, arguments) } }) @@ -101,46 +102,61 @@ syncMethods.forEach((method) => { // ----------------------------------------------------------------------------- // Custom API method implementations. -WebViewImpl.prototype.getTabID = function (instanceId, cb) { - if (!this.tabID) { - TabViewInternal.getTabID(instanceId, (tabID) => { - this.tabID = tabID - cb(tabID) - }) - } else { - cb(this.tabID) - } -} - const attachWindow = WebViewImpl.prototype.attachWindow$ -WebViewImpl.prototype.attachWindow$ = function(opt_guestInstanceId) { - let attached = attachWindow.bind(this)(opt_guestInstanceId) - // preload the webcontents and tabID +WebViewImpl.prototype.attachWindow$ = function (opt_guestInstanceId) { + console.log('attachWindow ' + opt_guestInstanceId) + if (this.guest.getId() === opt_guestInstanceId && + this.guest.getState() === GuestViewImpl.GuestState.GUEST_STATE_ATTACHED) { + return + } const guestInstanceId = opt_guestInstanceId || this.guest.getId() - WebViewInternal.getWebContents(guestInstanceId, (webContents) => { - // cache webContents_ - this.webContents_ = webContents - }) - this.getTabID(guestInstanceId, (tabID) => { - // cache tabId - this.tabID = tabID - GuestViewInternal.registerEvents(this, tabID) - }) + if (opt_guestInstanceId || this.guest.getState() === GuestViewImpl.GuestState.GUEST_STATE_ATTACHED) { + this.guest.detach(); + this.guest = new GuestView('webview', guestInstanceId); + } + + const attached = GuestViewContainer.prototype.attachWindow$.call(this); + if (attached) { + WebViewInternal.getWebContents(guestInstanceId, (webContents) => { + // cache webContents_ + this.webContents_ = webContents + }) + } return attached } -WebViewImpl.prototype.setGuestInstanceId = function (guestInstanceId) { - return this.attachWindow$(guestInstanceId) +WebViewImpl.prototype.detachGuest = function () { + const newGuest = () => { + this.guest = new GuestView('webview') + } + if (this.guest.getState() === GuestViewImpl.GuestState.GUEST_STATE_ATTACHED) { + this.guest.detach(() => newGuest()) + } else { + newGuest() + } } WebViewImpl.prototype.getProcessId = function() { return this.processId } +WebViewImpl.prototype.attachGuest = function (guestInstanceId) { + return this.attachWindow$(guestInstanceId) +} + +WebViewImpl.prototype.setTabId = function (tabID) { + this.tabID = tabID + GuestViewInternal.registerEvents(this, tabID) +} + +WebViewImpl.prototype.getId = function() { + return this.tabID +} + // ----------------------------------------------------------------------------- -WebViewImpl.getApiMethods = function() { +WebViewImpl.getApiMethods = function () { return WEB_VIEW_API_METHODS; }; diff --git a/atom/common/api/resources/web_view_events_api_bindings.js b/atom/common/api/resources/web_view_events_api_bindings.js new file mode 100644 index 0000000000..2996e5b615 --- /dev/null +++ b/atom/common/api/resources/web_view_events_api_bindings.js @@ -0,0 +1,38 @@ +// Copyright 2014 The Brave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This module implements the public-facing API functions for the tag. + +const WebViewEvents = require('webViewEvents').WebViewEvents; +const CreateEvent = require('guestViewEvents').CreateEvent; + +WebViewEvents.EVENTS['detachguest'] = { + evt: CreateEvent('webViewInternal.onDetachGuest'), + fields: [], + handler: 'handleDetachGuest', +} + +WebViewEvents.EVENTS['attachguest'] = { + evt: CreateEvent('webViewInternal.onAttachGuest'), + fields: ['guestInstanceId'], + handler: 'handleAttachGuest' +} + +WebViewEvents.EVENTS['tabidchanged'] = { + evt: CreateEvent('webViewInternal.onTabIdChanged'), + fields: ['tabID'], + handler: 'handleTabIdChanged' +} + +WebViewEvents.prototype.handleDetachGuest = function(event, eventName) { + this.view.detachGuest() +} + +WebViewEvents.prototype.handleAttachGuest = function(event, eventName) { + this.view.attachGuest(event.guestInstanceId) +} + +WebViewEvents.prototype.handleTabIdChanged = function(event, eventName) { + this.view.setTabId(event.tabID) +} diff --git a/brave/browser/BUILD.gn b/brave/browser/BUILD.gn index f55f99c614..c88ac39d10 100644 --- a/brave/browser/BUILD.gn +++ b/brave/browser/BUILD.gn @@ -108,6 +108,21 @@ source_set("browser") { } } +source_set("tab_manager") { + configs += [ + "//electron/build:electron_config", + ] + + sources = [ + "memory/guest_tab_manager.cc", + "memory/guest_tab_manager.h", + ] + + deps = [ + "//electron/chromium_src:tab_manager" + ] +} + source_set("component_updater_api") { configs += [ "//electron/build:electron_config", diff --git a/brave/browser/guest_view/tab_view/tab_view_guest.cc b/brave/browser/guest_view/tab_view/tab_view_guest.cc index 3ddb861514..b77b26d93b 100644 --- a/brave/browser/guest_view/tab_view/tab_view_guest.cc +++ b/brave/browser/guest_view/tab_view/tab_view_guest.cc @@ -63,8 +63,15 @@ GuestViewBase* TabViewGuest::Create(WebContents* owner_web_contents) { // static const char TabViewGuest::Type[] = "webview"; +void TabViewGuest::SetCanRunInDetachedState(bool can_run_detached) { + can_run_detached_ = can_run_detached; + if (!can_run_detached_ && !attached()) { + Destroy(); + } +} + bool TabViewGuest::CanRunInDetachedState() const { - return true; + return can_run_detached_; } void TabViewGuest::GuestDestroyed() { @@ -181,6 +188,25 @@ void TabViewGuest::DidCommitProvisionalLoadForFrame( // find_helper_.CancelAllFindSessions(); } +void TabViewGuest::AttachGuest(int guestInstanceId) { + std::unique_ptr args(new base::DictionaryValue()); + args->SetInteger("guestInstanceId", guestInstanceId); + DispatchEventToView(base::MakeUnique( + "webViewInternal.onAttachGuest", std::move(args))); +} + +void TabViewGuest::DetachGuest(bool notify_view) { + if (notify_view) { + std::unique_ptr args(new base::DictionaryValue()); + DispatchEventToView(base::MakeUnique( + "webViewInternal.onDetachGuest", std::move(args))); + } +#if BUILDFLAG(ENABLE_EXTENSIONS) + api_web_contents_->Emit("did-detach", + extensions::TabHelper::IdForTab(web_contents())); +#endif +} + void TabViewGuest::DidInitialize(const base::DictionaryValue& create_params) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Locker locker(isolate); @@ -268,24 +294,24 @@ void TabViewGuest::ApplyAttributes(const base::DictionaryValue& params) { void TabViewGuest::DidAttachToEmbedder() { DCHECK(api_web_contents_); + auto tab_helper = extensions::TabHelper::FromWebContents(web_contents()); api_web_contents_->ResumeLoadingCreatedWebContents(); web_contents()->WasHidden(); web_contents()->WasShown(); - ApplyAttributes(*attach_params()); + if (!tab_helper->IsDiscarded()) { + ApplyAttributes(*attach_params()); - if (web_contents()->GetController().IsInitialNavigation()) { - web_contents()->GetController().LoadIfNecessary(); + if (web_contents()->GetController().IsInitialNavigation()) { + web_contents()->GetController().LoadIfNecessary(); + } + } else { + web_contents()->WasHidden(); } -#if BUILDFLAG(ENABLE_EXTENSIONS) api_web_contents_->Emit("did-attach", extensions::TabHelper::IdForTab(web_contents())); -#else - api_web_contents_->Emit("did-attach", - web_contents()->GetRenderProcessHost()->GetID()); -#endif } bool TabViewGuest::ZoomPropagatesFromEmbedderToGuest() const { @@ -310,6 +336,11 @@ void TabViewGuest::GuestReady() { ->GetWidget() ->GetView() ->SetBackgroundColorToDefault(); + +#if BUILDFLAG(ENABLE_EXTENSIONS) + api_web_contents_->Emit("guest-ready", + extensions::TabHelper::IdForTab(web_contents()), guest_instance_id()); +#endif } void TabViewGuest::WillDestroy() { @@ -336,7 +367,8 @@ bool TabViewGuest::IsAutoSizeSupported() const { TabViewGuest::TabViewGuest(WebContents* owner_web_contents) : GuestView(owner_web_contents), api_web_contents_(nullptr), - clone_(false) { + clone_(false), + can_run_detached_(true) { } TabViewGuest::~TabViewGuest() { @@ -353,8 +385,20 @@ void TabViewGuest::WillAttachToEmbedder() { if (relay) owner_window = relay->window.get(); - if (owner_window) + if (owner_window) { + std::unique_ptr args(new base::DictionaryValue()); + args->SetInteger("tabID", +#if BUILDFLAG(ENABLE_EXTENSIONS) + extensions::TabHelper::IdForTab(web_contents()) +#else + -1 +#endif + ); + DispatchEventToView(base::MakeUnique( + "webViewInternal.onTabIdChanged", std::move(args))); + api_web_contents_->SetOwnerWindow(web_contents(), owner_window); + } } } // namespace brave diff --git a/brave/browser/guest_view/tab_view/tab_view_guest.h b/brave/browser/guest_view/tab_view/tab_view_guest.h index 2775e585e8..667b4a0e2c 100644 --- a/brave/browser/guest_view/tab_view/tab_view_guest.h +++ b/brave/browser/guest_view/tab_view/tab_view_guest.h @@ -27,6 +27,10 @@ class TabViewGuest : public guest_view::GuestView { static const char Type[]; + void AttachGuest(int guest_instance_id); + void DetachGuest(bool notify_view = true); + void SetCanRunInDetachedState(bool can_run_detached); + private: explicit TabViewGuest(content::WebContents* owner_web_contents); @@ -80,6 +84,7 @@ class TabViewGuest : public guest_view::GuestView { bool clone_; + bool can_run_detached_; // Stores the src URL of the WebView. GURL src_; diff --git a/brave/browser/memory/guest_tab_manager.cc b/brave/browser/memory/guest_tab_manager.cc new file mode 100644 index 0000000000..f6eee68303 --- /dev/null +++ b/brave/browser/memory/guest_tab_manager.cc @@ -0,0 +1,51 @@ +// Copyright 2017 The Brave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "brave/browser/memory/guest_tab_manager.h" + +#include "atom/browser/extensions/tab_helper.h" +#include "brave/browser/guest_view/tab_view/tab_view_guest.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/tabs/tab_strip_model.h" +#include "content/public/browser/web_contents.h" + +using content::WebContents; + +namespace memory { + +GuestTabManager::GuestTabManager() : TabManager() {} + +WebContents* GuestTabManager::CreateNullContents( + TabStripModel* model, WebContents* old_contents) { + auto tab_helper = extensions::TabHelper::FromWebContents(old_contents); + DCHECK(tab_helper && tab_helper->guest()); + + auto embedder = tab_helper->guest()->embedder_web_contents(); + + return extensions::TabHelper::CreateTab(embedder, + WebContents::CreateParams(old_contents->GetBrowserContext())); +} + +void GuestTabManager::DestroyOldContents(WebContents* old_contents) { + auto tab_helper = extensions::TabHelper::FromWebContents(old_contents); + DCHECK(tab_helper && tab_helper->guest()); + // Let the guest destroy itself after the detach message has been received + tab_helper->guest()->SetCanRunInDetachedState(false); +} + +void GuestTabManager::ActiveTabChanged(content::WebContents* old_contents, + content::WebContents* new_contents, + int index, + int reason) { + bool discarded = IsTabDiscarded(new_contents); + TabManager::ActiveTabChanged(old_contents, new_contents, index, reason); + if (discarded) { + if (reason == TabStripModelObserver::CHANGE_REASON_USER_GESTURE) + new_contents->UserGestureDone(); + + new_contents->GetController().Reload(content::ReloadType::NORMAL, true); + } +} + +} // namespace diff --git a/brave/browser/memory/guest_tab_manager.h b/brave/browser/memory/guest_tab_manager.h new file mode 100644 index 0000000000..e6705b09ab --- /dev/null +++ b/brave/browser/memory/guest_tab_manager.h @@ -0,0 +1,35 @@ +// Copyright (c) 2017 The Brave Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BRAVE_BROWSER_MEMORY_GUEST_TAB_MANAGER_H_ +#define BRAVE_BROWSER_MEMORY_GUEST_TAB_MANAGER_H_ + +#include "chrome/browser/memory/tab_manager.h" + +namespace content { +class WebContents; +} + +namespace memory { + +class GuestTabManager : public TabManager { + public: + GuestTabManager(); + + private: + void ActiveTabChanged(content::WebContents* old_contents, + content::WebContents* new_contents, + int index, + int reason) override; + // MUON(bridiver): override to create/destroy guests webcontents + virtual content::WebContents* CreateNullContents( + TabStripModel* model, content::WebContents* old_contents); + virtual void DestroyOldContents(content::WebContents* old_contents); + + DISALLOW_COPY_AND_ASSIGN(GuestTabManager); +}; + +} // namespace memory + +#endif // BRAVE_BROWSER_MEMORY_GUEST_T#include "chrome/browser/memory/tab_manager.h diff --git a/chromium_src/BUILD.gn b/chromium_src/BUILD.gn index b065c35075..db12a4ad67 100644 --- a/chromium_src/BUILD.gn +++ b/chromium_src/BUILD.gn @@ -131,7 +131,7 @@ source_set("browser") { ":devtools", ":importer", ":sessions", - ":tab_manager", + "//electron/brave/browser:tab_manager", "//chrome/common", "//chrome/utility", "//components/certificate_transparency", diff --git a/chromium_src/chrome/browser/browser_process_impl.cc b/chromium_src/chrome/browser/browser_process_impl.cc index d56c29e704..c2b1d2a5f7 100644 --- a/chromium_src/chrome/browser/browser_process_impl.cc +++ b/chromium_src/chrome/browser/browser_process_impl.cc @@ -14,10 +14,10 @@ #include "base/threading/thread_restrictions.h" #include "brave/browser/brave_content_browser_client.h" #include "brave/browser/component_updater/brave_component_updater_configurator.h" +#include "brave/browser/memory/guest_tab_manager.h" #include "brightray/browser/brightray_paths.h" #include "chrome/browser/background/background_mode_manager.h" #include "chrome/browser/browser_shutdown.h" -#include "chrome/browser/memory/tab_manager.h" #include "chrome/browser/printing/print_job_manager.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/shell_integration.h" @@ -252,7 +252,7 @@ memory::TabManager* BrowserProcessImpl::GetTabManager() { DCHECK(thread_checker_.CalledOnValidThread()); #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) if (!tab_manager_.get()) - tab_manager_.reset(new memory::TabManager()); + tab_manager_.reset(new memory::GuestTabManager()); return tab_manager_.get(); #else return nullptr; diff --git a/chromium_src/chrome/browser/ui/tabs/tab_strip_model.cc b/chromium_src/chrome/browser/ui/tabs/tab_strip_model.cc index a631b0496b..8d8d22476a 100644 --- a/chromium_src/chrome/browser/ui/tabs/tab_strip_model.cc +++ b/chromium_src/chrome/browser/ui/tabs/tab_strip_model.cc @@ -82,7 +82,9 @@ void TabStripModel::AppendWebContents(WebContents* contents, base::MakeUnique(contents); // the TabHelper tracks the actual index for now - int index = tab_helper->get_index() || contents_data_.size() - 1; + int index = tab_helper->get_index() != kNoTab + ? tab_helper->get_index() + : contents_data_.size(); contents_data_.push_back(std::move(data)); for (auto& observer : observers_) observer.TabInsertedAt(this, contents, index, foreground); @@ -96,7 +98,7 @@ WebContents* TabStripModel::ReplaceWebContentsAt(int index, WebContents* old_contents = GetWebContentsAt(index); for (size_t i = 0; i < contents_data_.size(); ++i) { - if (contents_data_[i]->tab_helper()->get_index() == index) { + if (contents_data_[i]->web_contents() == old_contents) { contents_data_[i]->SetWebContents(new_contents); break; } @@ -126,8 +128,8 @@ WebContents* TabStripModel::DetachWebContentsAt(int index) { WebContents* removed_contents = GetWebContentsAt(index); for (size_t i = 0; i < contents_data_.size(); ++i) { - if (contents_data_[i]->tab_helper()->get_index() == index) { - contents_data_.erase(contents_data_.begin() + index); + if (contents_data_[i]->web_contents() == removed_contents) { + contents_data_.erase(contents_data_.begin() + i); break; } } @@ -159,16 +161,21 @@ WebContents* TabStripModel::GetActiveWebContents() const { WebContents* TabStripModel::GetWebContentsAt(int index) const { for (size_t i = 0; i < contents_data_.size(); ++i) { - if (contents_data_[i]->tab_helper()->get_index() == index) + auto tab_helper = contents_data_[i]->tab_helper(); + if (tab_helper && tab_helper->get_index() == index) return contents_data_[i]->web_contents(); } return nullptr; } int TabStripModel::GetIndexOfWebContents(const WebContents* contents) const { - auto tab_helper = TabHelper::FromWebContents(contents); - if (tab_helper) - return tab_helper->get_index(); + for (size_t i = 0; i < contents_data_.size(); ++i) { + // make sure the webcontents is in this tab strip model + if (contents == contents_data_[i]->web_contents() && + contents_data_[i]->tab_helper()) { + return contents_data_[i]->tab_helper()->get_index(); + } + } return kNoTab; } diff --git a/chromium_src/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc b/chromium_src/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc index 8114e01ae4..c17d2d3997 100644 --- a/chromium_src/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc +++ b/chromium_src/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc @@ -210,6 +210,10 @@ void ChromeExtensionsDispatcherDelegate::PopulateSourceMap( IDR_ATOM_TAB_VIEW_INTERNAL_BINDINGS_JS); source_map->RegisterSource("webViewApiMethods", IDR_ATOM_WEB_VIEW_API_BINDINGS_JS); + source_map->RegisterSource("webViewEventsApiMethods", + IDR_ATOM_WEB_VIEW_EVENTS_API_BINDINGS_JS); + source_map->RegisterSource("guestViewApiMethods", + IDR_ATOM_GUEST_VIEW_API_BINDINGS_JS); } void ChromeExtensionsDispatcherDelegate::RequireAdditionalModules( @@ -223,6 +227,8 @@ void ChromeExtensionsDispatcherDelegate::RequireAdditionalModules( module_system->Require("webViewInternal"); module_system->Require("webViewApiMethods"); module_system->Require("webViewAttributes"); + module_system->Require("webViewEventsApiMethods"); + module_system->Require("guestViewApiMethods"); } if (context_type == extensions::Feature::WEBUI_CONTEXT || diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index 08e1b3f905..3b6e0e2541 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -7,6 +7,7 @@ let supportedWebViewEvents = [ 'load-start', 'load-commit', 'did-attach', + 'guest-ready', 'did-detach', 'did-finish-load', 'did-fail-provisional-load', @@ -68,18 +69,21 @@ const registerGuest = function (guest, embedder) { return } - guest.once('destroyed', function () { - delete guests[tabId] - }) - guest.once('crashed', function () { - delete guests[tabId] - }) - // Dispatch events to embedder. const fn = function (event) { guest.on(event, function (_, ...args) { const embedder = guests[tabId] - if (!embedder || embedder.isDestroyed() || guest.isDestroyed()) + + if (!embedder || embedder.isDestroyed()) + return + + let forceSend = false + if (['destroyed', 'crashed', 'did-detach'].includes(event)) { + delete guests[tabId] + forceSend = true + } + + if (guest.isDestroyed() && !forceSend) return embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + tabId, event].concat(args)) diff --git a/lib/renderer/web-view/guest-view-internal.js b/lib/renderer/web-view/guest-view-internal.js index b547a30c01..40e85a13ee 100644 --- a/lib/renderer/web-view/guest-view-internal.js +++ b/lib/renderer/web-view/guest-view-internal.js @@ -6,6 +6,7 @@ var WEB_VIEW_EVENTS = { 'load-start': ['url', 'isMainFrame', 'isErrorPage'], 'load-commit': ['url', 'isMainFrame'], 'did-attach': ['tabId'], + 'guest-ready': ['tabId', 'guestInstanceId'], 'did-detach': [], 'did-finish-load': ['validatedURL'], 'did-fail-provisional-load': ['errorCode', 'errorDescription', 'validatedURL', 'isMainFrame', 'currentURL'], diff --git a/patches/master_patch.patch b/patches/master_patch.patch index 04fbcab156..4eb1af148c 100644 --- a/patches/master_patch.patch +++ b/patches/master_patch.patch @@ -159,6 +159,82 @@ index b77fbdeea3cd28ead065828c3fffb19496f80ab8..86aad8c1312e801ff64a81b11700d3a4 return; } +diff --git a/chrome/browser/memory/tab_manager.cc b/chrome/browser/memory/tab_manager.cc +index 6c2cf66a1aba3f71310e4e3317e849839d3b0e0b..af107629afbea2e9c13686765cace0e55c70c4f4 100644 +--- a/chrome/browser/memory/tab_manager.cc ++++ b/chrome/browser/memory/tab_manager.cc +@@ -786,6 +786,17 @@ void TabManager::PurgeAndSuspendBackgroundedTabs() { + } + } + ++// MUON(bridiver): see tab_manager.h ++WebContents* TabManager::CreateNullContents( ++ TabStripModel* model, WebContents* old_contents) { ++ return WebContents::Create(WebContents::CreateParams(model->profile())); ++} ++ ++// MUON(bridiver): see tab_manager.h ++void TabManager::DestroyOldContents(WebContents* old_contents) { ++ delete old_contents; ++} ++ + WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { + // Can't discard active index. + if (model->active_index() == index) +@@ -805,8 +816,8 @@ WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { + "TabManager.Discarding.DiscardedTabHasBeforeUnloadHandler", + old_contents->NeedToFireBeforeUnload()); + +- WebContents* null_contents = +- WebContents::Create(WebContents::CreateParams(model->profile())); ++ // MUON(bridiver): see tab_manager.h ++ WebContents* null_contents = CreateNullContents(model, old_contents); + // Copy over the state from the navigation controller to preserve the + // back/forward history and to continue to display the correct title/favicon. + null_contents->GetController().CopyStateFrom(old_contents->GetController()); +@@ -818,7 +829,7 @@ WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { + + // Replace the discarded tab with the null version. + model->ReplaceWebContentsAt(index, null_contents); +- // Mark the tab so it will reload when clicked on. ++ // Mark the tab so it will reload when clicked on + GetWebContentsData(null_contents)->SetDiscardState(true); + GetWebContentsData(null_contents)->IncrementDiscardCount(); + +@@ -826,7 +837,8 @@ WebContents* TabManager::DiscardWebContentsAt(int index, TabStripModel* model) { + // TODO(jamescook): This breaks script connections with other tabs. + // Find a different approach that doesn't do that, perhaps based on navigation + // to swappedout://. +- delete old_contents; ++ // MUON(bridiver): see tab_manager.h ++ DestroyOldContents(old_contents); + recent_tab_discard_ = true; + + return null_contents; +diff --git a/chrome/browser/memory/tab_manager.h b/chrome/browser/memory/tab_manager.h +index 55b6d1d98314f0adcdd087b0c64379b453df24e5..4c72b67644434ed82e94c94dea57f8dffd5cf4a4 100644 +--- a/chrome/browser/memory/tab_manager.h ++++ b/chrome/browser/memory/tab_manager.h +@@ -181,6 +181,7 @@ class TabManager : public TabStripModelObserver { + // This is needed so WebContentsData can call OnDiscardedStateChange, and + // can use PurgeAndSuspendState. + friend class WebContentsData; ++ friend class GuestTabManager; + + // Called by WebContentsData whenever the discard state of a WebContents + // changes, so that observers can be informed. +@@ -331,6 +332,11 @@ class TabManager : public TabStripModelObserver { + // Returns true if tabs can be discarded only once. + bool CanOnlyDiscardOnce(); + ++ // MUON(bridiver): override to create/destroy guests webcontents ++ virtual content::WebContents* CreateNullContents( ++ TabStripModel* model, content::WebContents* old_contents); ++ virtual void DestroyOldContents(content::WebContents* old_contents); ++ + // Timer to periodically update the stats of the renderers. + base::RepeatingTimer update_timer_; + diff --git a/chrome/browser/plugins/chrome_plugin_service_filter.cc b/chrome/browser/plugins/chrome_plugin_service_filter.cc index 1783a101aa024a5aa39a7bd7c335427398d47093..ac11311d78e2271f9ca7f6016c79a37de078621c 100644 --- a/chrome/browser/plugins/chrome_plugin_service_filter.cc @@ -2259,6 +2335,21 @@ index 00a8accbe2cdc3c77c6fce3fd28cd697ff0524d5..8a9cf6907b0d27cb5b1a7fd8eed00500 #else is_elastic_overscroll_enabled_ = false; #endif +diff --git a/extensions/browser/guest_view/extensions_guest_view_manager_delegate.cc b/extensions/browser/guest_view/extensions_guest_view_manager_delegate.cc +index 061480b5acd6431aa9abb7e02c98ebe5e881d7f9..cb06be194f24f5b7865874101da696b8f168c3ab 100644 +--- a/extensions/browser/guest_view/extensions_guest_view_manager_delegate.cc ++++ b/extensions/browser/guest_view/extensions_guest_view_manager_delegate.cc +@@ -53,8 +53,8 @@ void ExtensionsGuestViewManagerDelegate::DispatchEvent( + // extensions::events::HistogramValue as an argument. + events::HistogramValue histogram_value = + guest_view_events::GetEventHistogramValue(event_name); +- DCHECK_NE(events::UNKNOWN, histogram_value) << "Event " << event_name +- << " must have a histogram value"; ++ // DCHECK_NE(events::UNKNOWN, histogram_value) << "Event " << event_name ++ // << " must have a histogram value"; + + content::WebContents* owner = guest->owner_web_contents(); + if (!owner) diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index 57c9a2e6a43558e2325a76b5b147a3c7e2b99fd3..5ee029882c5ecf0104f83ee61e4a0ba729a7a516 100644 --- a/extensions/common/api/_api_features.json @@ -2278,6 +2369,7 @@ index 57c9a2e6a43558e2325a76b5b147a3c7e2b99fd3..5ee029882c5ecf0104f83ee61e4a0ba7 @@ -510,6 +513,7 @@ "internal": true, "contexts": ["webui"], ++ // MUON(bridiver): creating new histograms is a pain "matches": [ + "chrome://brave/*", "chrome://chrome-signin/*",