Skip to content
This repository has been archived by the owner on Jan 4, 2019. It is now read-only.

Commit

Permalink
Fix embedder only getting events for webContents when attached to a w…
Browse files Browse the repository at this point in the history
…ebview. Each time a webContents gets a new embedder contents (window), it will register with it.

Introduce 'set-window' and 'guest-created' WebContents events.

Fix #515
  • Loading branch information
petemill authored and bridiver committed Mar 6, 2018
1 parent f6d5888 commit d1ba583
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 23 deletions.
3 changes: 3 additions & 0 deletions atom/browser/extensions/tab_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
12 changes: 12 additions & 0 deletions brave/browser/guest_view/tab_view/tab_view_guest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -346,6 +357,7 @@ void TabViewGuest::ApplyAttributes(const base::DictionaryValue& params) {
}
}
}
api_web_contents_->Emit("guest-created", owner_web_contents());
}

void TabViewGuest::DidAttachToEmbedder() {
Expand Down
3 changes: 2 additions & 1 deletion brave/browser/guest_view/tab_view/tab_view_guest.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class TabViewGuest : public guest_view::GuestView<TabViewGuest> {
void DetachGuest();
void SetCanRunInDetachedState(bool can_run_detached);
void TabIdChanged();

void WindowIdChanged();

void Load();

private:
Expand Down
8 changes: 6 additions & 2 deletions lib/browser/api/web-contents.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
104 changes: 84 additions & 20 deletions lib/browser/guest-view-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ let supportedWebViewEvents = [
'tab-replaced-at',
'load-start',
'did-attach',
'set-window',
'guest-created',
'guest-ready',
'will-detach',
'did-detach',
Expand Down Expand Up @@ -45,53 +47,115 @@ 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) {
if (oldEmbedderDetails.embedder && oldEmbedderDetails.destroyedListener && !oldEmbedderDetails.embedder.isDestroyed())
// 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.isDestroyed()) {
embedderDetails.embedder.removeListener('destroyed', embedderDetails.destroyedListener)
}
}

exports.registerGuest = registerGuest
exports.deregisterGuest = deregisterGuest
5 changes: 5 additions & 0 deletions lib/renderer/web-view/guest-view-internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down Expand Up @@ -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]
Expand Down

0 comments on commit d1ba583

Please sign in to comment.