diff --git a/atom/browser/extensions/tab_helper.cc b/atom/browser/extensions/tab_helper.cc index dc5b1f7e20..7e660367a8 100644 --- a/atom/browser/extensions/tab_helper.cc +++ b/atom/browser/extensions/tab_helper.cc @@ -468,6 +468,9 @@ void TabHelper::SetWindowId(const int32_t& id) { SessionID session; session.set_id(id); SessionTabHelper::FromWebContents(web_contents())->SetWindowID(session); + if (guest()) { + guest()->WindowIdChanged(); + } } int32_t TabHelper::window_id() const { 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 fafb999686..a114f3de6a 100644 --- a/brave/browser/guest_view/tab_view/tab_view_guest.cc +++ b/brave/browser/guest_view/tab_view/tab_view_guest.cc @@ -232,6 +232,17 @@ void TabViewGuest::TabIdChanged() { "webViewInternal.onTabIdChanged", std::move(args))); } +void TabViewGuest::WindowIdChanged() { + DCHECK(api_web_contents_); + if (api_web_contents_) { + if (owner_web_contents()) { + api_web_contents_->Emit("set-window", owner_web_contents()); + return; + } + api_web_contents_->Emit("set-window"); + } +} + void TabViewGuest::DidInitialize(const base::DictionaryValue& create_params) { v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Locker locker(isolate); @@ -346,6 +357,7 @@ void TabViewGuest::ApplyAttributes(const base::DictionaryValue& params) { } } } + api_web_contents_->Emit("guest-created", owner_web_contents()); } void TabViewGuest::DidAttachToEmbedder() { 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 ec687d869a..f53c37ad97 100644 --- a/brave/browser/guest_view/tab_view/tab_view_guest.h +++ b/brave/browser/guest_view/tab_view/tab_view_guest.h @@ -31,7 +31,8 @@ class TabViewGuest : public guest_view::GuestView { void DetachGuest(); void SetCanRunInDetachedState(bool can_run_detached); void TabIdChanged(); - + void WindowIdChanged(); + void Load(); private: diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.js index e44c303db4..309a25529a 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.js @@ -201,8 +201,12 @@ WebContents.prototype._init = function () { this.reload() }) - this.on('will-attach', function (event, embedder) { - guestViewManager.registerGuest(event.sender, embedder) + this.on('set-window', function (event, embedder) { + if (embedder) { + guestViewManager.registerGuest(event.sender, embedder) + } else { + guestViewManager.deregisterGuest(event.sender) + } }) if (!this.isRemote()) { diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index c34c3f557d..40f932ba02 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -7,6 +7,8 @@ let supportedWebViewEvents = [ 'tab-replaced-at', 'load-start', 'did-attach', + 'set-window', + 'guest-created', 'guest-ready', 'will-detach', 'did-detach', @@ -45,53 +47,114 @@ let supportedWebViewEvents = [ ] let guests = {} +// ensure webcontent events (from guest) are fired to embedder (window) so they can be forwarded to webview / interested-party const registerGuest = function (guest, embedder) { const tabId = guest.getId() + + const oldEmbedderDetails = guests[tabId] + + const isNewEmbedder = !oldEmbedderDetails || oldEmbedderDetails.embedder !== embedder - embedder.once('destroyed', function () { - if (guests[tabId] === embedder) { + // Events were previously setup and we don't have a different embedder + if (oldEmbedderDetails && !isNewEmbedder) { + return + } + + if (!oldEmbedderDetails) { + guests[tabId] = {} + } + // always update the embedder so events get routed to the correct window + guests[tabId].embedder = embedder + // listen for destroyed event on new embedders + const destroyedListener = function () { + if (guests[tabId] && guests[tabId].embedder === embedder) { + const guestHandlers = guests[tabId].guestHandlers guests[tabId] = false + // remove handlers + // since now that the reference is reset, + // next time tab is attached, new handlers will be created + if (guest && !guest.isDestroyed()) { + for (const event in guestHandlers) { + guest.removeListener(event, guestHandlers[event]) + } + } } - }) + } + embedder.once('destroyed', destroyedListener) - const oldEmbedder = guests[tabId] - guests[tabId] = embedder - if (oldEmbedder !== undefined) { + // Events were previously setup, but we have a new embedder + if (oldEmbedderDetails && isNewEmbedder) { + // stop listening to destroyed event on the embedder + oldEmbedderDetails.embedder.removeListener('destroyed', oldEmbedderDetails.destroyedListener) + // remember the new embedders destroy listener, so we can remove that + // next time the embedder changes + guests[tabId].destroyedListener = destroyedListener + // no need to re-attach event listeners if we've already done that, the events + // will dispatch to the new embedder when they fire return } + guests[tabId].destroyedListener = destroyedListener + // Dispatch events to embedder. - const fn = function (event) { - guest.on(event, function (_, ...args) { - const embedder = guests[tabId] + const guestHandlers = { } + for (const event of supportedWebViewEvents) { + guestHandlers[event] = function (_, ...args) { + const embedder = guests[tabId] && guests[tabId].embedder if (!embedder || embedder.isDestroyed()) return let forceSend = false - if (['destroyed', 'did-detach', 'will-detach'].includes(event)) { + if (['destroyed'].includes(event)) { delete guests[tabId] forceSend = true } - if (guest.isDestroyed() && !forceSend) + if (guest.isDestroyed() && !forceSend) { return + } embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT', tabId, event].concat(args)) - }) - } - for (const event of supportedWebViewEvents) { - fn(event) + } + guest.on(event, guestHandlers[event]) } - - // Dispatch guest's IPC messages to embedder. - guest.on('ipc-message-host', function (_, [channel, ...args]) { - const embedder = guests[tabId] + const handleIpcMessage = function (_, [channel, ...args]) { + const embedder = guests[tabId] && guests[tabId].embedder if (!embedder || embedder.isDestroyed() || guest.isDestroyed()) return embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', tabId, channel].concat(args)) - }) + } + // Dispatch guest's IPC messages to embedder. + guest.on('ipc-message-host', handleIpcMessage) + guestHandlers['ipc-message-host'] = handleIpcMessage + // store handlers so we can deregister them when: + // - embedder is destroyed + // - guest does not have an embedder at all + guests[tabId].guestHandlers = guestHandlers +} + +const deregisterGuest = function (guest) { + if (!guest || guest.isDestroyed()) { + return + } + const tabId = guest.getId() + const embedderDetails = guests[tabId] + if (!embedderDetails) { + return + } + guests[tabId] = false + const guestHandlers = guests[tabId].guestHandlers + if (guestHandlers) { + for (const event in guestHandlers) { + guest.removeListener(event, guestHandlers[event]) + } + } + if (embedderDetails.embedder && embedderDetails.destroyedListener) { + embedderDetails.embedder.removeListener('destroyed', embedderDetails.destroyedListener) + } } exports.registerGuest = registerGuest +exports.deregisterGuest = deregisterGuest diff --git a/lib/renderer/web-view/guest-view-internal.js b/lib/renderer/web-view/guest-view-internal.js index 5ca2630f8f..910eb5a144 100644 --- a/lib/renderer/web-view/guest-view-internal.js +++ b/lib/renderer/web-view/guest-view-internal.js @@ -8,6 +8,8 @@ var WEB_VIEW_EVENTS = { 'load-start': ['url', 'isMainFrame', 'isErrorPage'], 'will-detach': [], 'did-attach': ['tabId'], + 'guest-created': ['embedderWebContents'], + 'set-window': ['embedderWebContents'], 'guest-ready': ['tabId', 'guestInstanceId'], 'did-detach': [], 'did-finish-load': ['validatedURL'], @@ -50,6 +52,9 @@ var dispatchEvent = function (tabId, eventName, ...args) { var domEvent, f, i, j, len, ref1 domEvent = new Event(eventName) ref1 = WEB_VIEW_EVENTS[eventName] + if (!ref1) { + return + } for (i = j = 0, len = ref1.length; j < len; i = ++j) { f = ref1[i] domEvent[f] = args[i]