fix: use OOPIF for webview tag (#13869)

* fix: use OOIF for webview tag

* fix: do not call GetNativeView for webview

* fix: OOIPF webview's WebContents is managed by embedder frame

* fix: guest view can not be focused

* fix: clear zoom controller when guest is destroyed

* fix: implement the webview resize event

The webview is no longer a browser plugin with the resize event, use
ResizeObserver instead.

* test: disable failed tests due to OOPIF webview

* fix: embedder can be destroyed earlier than guest

This happens when embedder is manually destroyed.

* fix: don't double attach

* fix: recreate iframe when webview is reattached

* fix: resize event may happen very early

* test: some tests are working after OOPIF webview

* chore: remove unused browser plugin webview code

* fix: get embedder via closure

When the "destroyed" event is emitted, the entry in guestInstances would be
cleared.

* chore: rename browserPluginNode to internalElement

* test: make the visibilityState test more robust

* chore: guestinstance can not work with OOPIF webview

* fix: element could be detached before got response from browser
This commit is contained in:
Cheng Zhao 2018-08-16 15:57:40 -07:00 committed by Charles Kerr
parent 48407c5b93
commit dd5b8769be
28 changed files with 268 additions and 1008 deletions

View file

@ -46,11 +46,6 @@ let nextGuestInstanceId = 0
const guestInstances = {}
const embedderElementsMap = {}
// Moves the last element of array to the first one.
const moveLastToFirst = function (list) {
list.unshift(list.pop())
}
// Generate guestInstanceId.
const getNextGuestInstanceId = function () {
return ++nextGuestInstanceId
@ -73,10 +68,15 @@ const createGuest = function (embedder, params) {
embedder: embedder
}
watchEmbedder(embedder)
// Clear the guest from map when it is destroyed.
guest.once('destroyed', () => {
if (guestInstanceId in guestInstances) {
detachGuest(embedder, guestInstanceId)
}
})
// Init guest web view after attached.
guest.on('did-attach', function (event) {
guest.once('did-attach', function (event) {
params = this.attachParams
delete this.attachParams
@ -88,21 +88,6 @@ const createGuest = function (embedder, params) {
return
}
this.setSize({
normal: {
width: params.elementWidth,
height: params.elementHeight
},
enableAutoSize: params.autosize,
min: {
width: params.minwidth,
height: params.minheight
},
max: {
width: params.maxwidth,
height: params.maxheight
}
})
if (params.src) {
const opts = {}
if (params.httpreferrer) {
@ -118,8 +103,7 @@ const createGuest = function (embedder, params) {
})
const sendToEmbedder = (channel, ...args) => {
const embedder = getEmbedder(guestInstanceId)
if (embedder != null) {
if (!embedder.isDestroyed()) {
embedder.send(`${channel}-${guest.viewInstanceId}`, ...args)
}
}
@ -139,11 +123,6 @@ const createGuest = function (embedder, params) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE', channel, ...args)
})
// Autosize.
guest.on('size-changed', function (_, ...args) {
sendToEmbedder('ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED', ...args)
})
// Notify guest of embedder window visibility when it is ready
// FIXME Remove once https://github.com/electron/electron/issues/6828 is fixed
guest.on('dom-ready', function () {
@ -176,7 +155,7 @@ const createGuest = function (embedder, params) {
}
// Attach the guest to an element of embedder.
const attachGuest = function (event, elementInstanceId, guestInstanceId, params) {
const attachGuest = function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
const embedder = event.sender
// Destroy the old guest when attaching.
const key = `${embedder.id}-${elementInstanceId}`
@ -187,7 +166,10 @@ const attachGuest = function (event, elementInstanceId, guestInstanceId, params)
return
}
destroyGuest(embedder, oldGuestInstanceId)
const oldGuestInstance = guestInstances[oldGuestInstanceId]
if (oldGuestInstance) {
oldGuestInstance.guest.destroy()
}
}
const guestInstance = guestInstances[guestInstanceId]
@ -260,11 +242,10 @@ const attachGuest = function (event, elementInstanceId, guestInstanceId, params)
embedder.emit('will-attach-webview', event, webPreferences, params)
if (event.defaultPrevented) {
if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId
destroyGuest(embedder, guestInstanceId)
guest.destroy()
return
}
webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences)
guest.attachParams = params
embedderElementsMap[key] = guestInstanceId
@ -273,21 +254,19 @@ const attachGuest = function (event, elementInstanceId, guestInstanceId, params)
guestInstance.elementInstanceId = elementInstanceId
watchEmbedder(embedder)
webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences)
guest.attachToIframe(embedder, embedderFrameId)
}
// Destroy an existing guest instance.
const destroyGuest = function (embedder, guestInstanceId) {
if (!(guestInstanceId in guestInstances)) {
return
}
// Remove an guest-embedder relationship.
const detachGuest = function (embedder, guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId]
if (embedder !== guestInstance.embedder) {
return
}
webViewManager.removeGuest(embedder, guestInstanceId)
guestInstance.guest.destroy()
delete guestInstances[guestInstanceId]
const key = `${embedder.id}-${guestInstance.elementInstanceId}`
@ -305,7 +284,7 @@ const watchEmbedder = function (embedder) {
// Forward embedder window visiblity change events to guest
const onVisibilityChange = function (visibilityState) {
for (const guestInstanceId of Object.keys(guestInstances)) {
for (const guestInstanceId in guestInstances) {
const guestInstance = guestInstances[guestInstanceId]
guestInstance.visibilityState = visibilityState
if (guestInstance.embedder === embedder) {
@ -315,33 +294,20 @@ const watchEmbedder = function (embedder) {
}
embedder.on('-window-visibility-change', onVisibilityChange)
const destroyEvents = ['will-destroy', 'crashed', 'did-navigate']
const destroy = function () {
for (const guestInstanceId of Object.keys(guestInstances)) {
if (guestInstances[guestInstanceId].embedder === embedder) {
destroyGuest(embedder, parseInt(guestInstanceId))
embedder.once('will-destroy', () => {
// Usually the guestInstances is cleared when guest is destroyed, but it
// may happen that the embedder gets manually destroyed earlier than guest,
// and the embedder will be invalid in the usual code path.
for (const guestInstanceId in guestInstances) {
const guestInstance = guestInstances[guestInstanceId]
if (guestInstance.embedder === embedder) {
detachGuest(embedder, parseInt(guestInstanceId))
}
}
for (const event of destroyEvents) {
embedder.removeListener(event, destroy)
}
// Clear the listeners.
embedder.removeListener('-window-visibility-change', onVisibilityChange)
watchedEmbedders.delete(embedder)
}
for (const event of destroyEvents) {
embedder.once(event, destroy)
// Users might also listen to the crashed event, so we must ensure the guest
// is destroyed before users' listener gets called. It is done by moving our
// listener to the first one in queue.
const listeners = embedder._events[event]
if (Array.isArray(listeners)) {
moveLastToFirst(listeners)
}
}
})
}
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) {
@ -352,17 +318,8 @@ ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, par
event.returnValue = createGuest(event.sender, params)
})
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, elementInstanceId, guestInstanceId, params) {
attachGuest(event, elementInstanceId, guestInstanceId, params)
})
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, guestInstanceId) {
destroyGuest(event.sender, guestInstanceId)
})
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_SET_SIZE', function (event, guestInstanceId, params) {
const guest = getGuest(guestInstanceId)
if (guest != null) guest.setSize(params)
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedderFrameId, elementInstanceId, guestInstanceId, params) {
attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params)
})
// Returns WebContents from its guest id.