Merge pull request #7157 from Mossop/moveguest

Allow moving a webcontents to a different webview
This commit is contained in:
Cheng Zhao 2016-09-20 14:28:42 +09:00 committed by GitHub
commit e3e450613d
12 changed files with 424 additions and 63 deletions

View file

@ -1455,6 +1455,25 @@ content::WebContents* WebContents::HostWebContents() {
return embedder_->web_contents();
}
void WebContents::SetEmbedder(const WebContents* embedder) {
if (embedder) {
NativeWindow* owner_window = nullptr;
auto relay = NativeWindowRelay::FromWebContents(embedder->web_contents());
if (relay) {
owner_window = relay->window.get();
}
if (owner_window)
SetOwnerWindow(owner_window);
content::RenderWidgetHostView* rwhv =
web_contents()->GetRenderWidgetHostView();
if (rwhv) {
rwhv->Hide();
rwhv->Show();
}
}
}
v8::Local<v8::Value> WebContents::DevToolsWebContents(v8::Isolate* isolate) {
if (devtools_web_contents_.IsEmpty())
return v8::Null(isolate);
@ -1552,6 +1571,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
&WebContents::ShowDefinitionForSelection)
.SetMethod("copyImageAt", &WebContents::CopyImageAt)
.SetMethod("capturePage", &WebContents::CapturePage)
.SetMethod("setEmbedder", &WebContents::SetEmbedder)
.SetProperty("id", &WebContents::ID)
.SetProperty("session", &WebContents::Session)
.SetProperty("hostWebContents", &WebContents::HostWebContents)

View file

@ -102,6 +102,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
void SetAudioMuted(bool muted);
bool IsAudioMuted();
void Print(mate::Arguments* args);
void SetEmbedder(const WebContents* embedder);
// Print current page as PDF.
void PrintToPDF(const base::DictionaryValue& setting,

View file

@ -110,6 +110,10 @@ void WebFrame::AttachGuest(int id) {
content::RenderFrame::FromWebFrame(web_frame_)->AttachGuest(id);
}
void WebFrame::DetachGuest(int id) {
content::RenderFrame::FromWebFrame(web_frame_)->DetachGuest(id);
}
void WebFrame::SetSpellCheckProvider(mate::Arguments* args,
const std::string& language,
bool auto_spell_correct_turned_on,
@ -202,6 +206,7 @@ void WebFrame::BuildPrototype(
.SetMethod("registerElementResizeCallback",
&WebFrame::RegisterElementResizeCallback)
.SetMethod("attachGuest", &WebFrame::AttachGuest)
.SetMethod("detachGuest", &WebFrame::DetachGuest)
.SetMethod("setSpellCheckProvider", &WebFrame::SetSpellCheckProvider)
.SetMethod("registerURLSchemeAsSecure",
&WebFrame::RegisterURLSchemeAsSecure)

View file

@ -53,6 +53,7 @@ class WebFrame : public mate::Wrappable<WebFrame> {
int element_instance_id,
const GuestViewContainer::ResizeCallback& callback);
void AttachGuest(int element_instance_id);
void DetachGuest(int element_instance_id);
// Set the provider that will be used by SpellCheckClient for spell check.
void SetSpellCheckProvider(mate::Arguments* args,

View file

@ -214,6 +214,21 @@ A list of strings which specifies the blink features to be disabled separated by
The full list of supported feature strings can be found in the
[RuntimeEnabledFeatures.in][blink-feature-string] file.
### `guestinstance`
```html
<webview src="https://www.github.com/" guestinstance="3"></webview>
```
A value that links the webview to a specific webContents. When a webview
first loads a new webContents is created and this attribute is set to its
instance identifier. Setting this attribute on a new or existing webview
connects it to the existing webContents that currently renders in a different
webview.
The existing webview will see the `destroy` event and will then create a new
webContents when a new url is loaded.
## Methods
The `webview` tag has the following methods:

View file

@ -8,6 +8,7 @@ let webViewManager = null
const supportedWebViewEvents = [
'load-commit',
'did-attach',
'did-finish-load',
'did-fail-load',
'did-frame-finish-load',
@ -40,10 +41,9 @@ const supportedWebViewEvents = [
'update-target-url'
]
let nextInstanceId = 0
let nextGuestInstanceId = 0
const guestInstances = {}
const embedderElementsMap = {}
const reverseEmbedderElementsMap = {}
// Moves the last element of array to the first one.
const moveLastToFirst = function (list) {
@ -51,8 +51,8 @@ const moveLastToFirst = function (list) {
}
// Generate guestInstanceId.
const getNextInstanceId = function () {
return ++nextInstanceId
const getNextGuestInstanceId = function () {
return ++nextGuestInstanceId
}
// Create a new guest instance.
@ -61,43 +61,21 @@ const createGuest = function (embedder, params) {
webViewManager = process.atomBinding('web_view_manager')
}
const id = getNextInstanceId(embedder)
const guestInstanceId = getNextGuestInstanceId(embedder)
const guest = webContents.create({
isGuest: true,
partition: params.partition,
embedder: embedder
})
guestInstances[id] = {
guestInstances[guestInstanceId] = {
guest: guest,
embedder: embedder
}
// Destroy guest when the embedder is gone or navigated.
const destroyEvents = ['will-destroy', 'crashed', 'did-navigate']
const destroy = function () {
if (guestInstances[id] != null) {
destroyGuest(embedder, id)
}
}
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)
}
}
guest.once('destroyed', function () {
for (const event of destroyEvents) {
embedder.removeListener(event, destroy)
}
})
watchEmbedder(embedder)
// Init guest web view after attached.
guest.once('did-attach', function () {
guest.on('did-attach', function () {
let opts
params = this.attachParams
delete this.attachParams
@ -133,6 +111,10 @@ const createGuest = function (embedder, params) {
// Dispatch events to embedder.
const fn = function (event) {
guest.on(event, function (_, ...args) {
const embedder = getEmbedder(guestInstanceId)
if (!embedder) {
return
}
embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + guest.viewInstanceId, event].concat(args))
})
}
@ -142,35 +124,56 @@ const createGuest = function (embedder, params) {
// Dispatch guest's IPC messages to embedder.
guest.on('ipc-message-host', function (_, [channel, ...args]) {
const embedder = getEmbedder(guestInstanceId)
if (!embedder) {
return
}
embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + guest.viewInstanceId, channel].concat(args))
})
// Autosize.
guest.on('size-changed', function (_, ...args) {
const embedder = getEmbedder(guestInstanceId)
if (!embedder) {
return
}
embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + guest.viewInstanceId].concat(args))
})
return id
return guestInstanceId
}
// Attach the guest to an element of embedder.
const attachGuest = function (embedder, elementInstanceId, guestInstanceId, params) {
let guest, key, oldGuestInstanceId, ref1, webPreferences
guest = guestInstances[guestInstanceId].guest
let guest, guestInstance, key, oldKey, oldGuestInstanceId, ref1, webPreferences
// Destroy the old guest when attaching.
key = (embedder.getId()) + '-' + elementInstanceId
oldGuestInstanceId = embedderElementsMap[key]
if (oldGuestInstanceId != null) {
// Reattachment to the same guest is not currently supported.
// Reattachment to the same guest is just a no-op.
if (oldGuestInstanceId === guestInstanceId) {
return
}
if (guestInstances[oldGuestInstanceId] == null) {
return
}
destroyGuest(embedder, oldGuestInstanceId)
}
guestInstance = guestInstances[guestInstanceId]
// If this isn't a valid guest instance then do nothing.
if (!guestInstance) {
return
}
guest = guestInstance.guest
// If this guest is already attached to an element then remove it
if (guestInstance.elementInstanceId) {
oldKey = (guestInstance.embedder.getId()) + '-' + guestInstance.elementInstanceId
delete embedderElementsMap[oldKey]
webViewManager.removeGuest(guestInstance.embedder, guestInstanceId)
guestInstance.embedder.send('ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-' + guest.viewInstanceId)
}
webPreferences = {
guestInstanceId: guestInstanceId,
nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false,
@ -187,19 +190,67 @@ const attachGuest = function (embedder, elementInstanceId, guestInstanceId, para
webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences)
guest.attachParams = params
embedderElementsMap[key] = guestInstanceId
reverseEmbedderElementsMap[guestInstanceId] = key
guest.setEmbedder(embedder)
guestInstance.embedder = embedder
guestInstance.elementInstanceId = elementInstanceId
watchEmbedder(embedder)
}
// Destroy an existing guest instance.
const destroyGuest = function (embedder, id) {
webViewManager.removeGuest(embedder, id)
guestInstances[id].guest.destroy()
delete guestInstances[id]
const destroyGuest = function (embedder, guestInstanceId) {
if (!(guestInstanceId in guestInstances)) {
return
}
const key = reverseEmbedderElementsMap[id]
if (key != null) {
delete reverseEmbedderElementsMap[id]
return delete embedderElementsMap[key]
let guestInstance = guestInstances[guestInstanceId]
if (embedder !== guestInstance.embedder) {
return
}
webViewManager.removeGuest(embedder, guestInstanceId)
guestInstance.guest.destroy()
delete guestInstances[guestInstanceId]
const key = embedder.getId() + '-' + guestInstance.elementInstanceId
return delete embedderElementsMap[key]
}
// Once an embedder has had a guest attached we watch it for destruction to
// destroy any remaining guests.
const watchedEmbedders = new Set()
const watchEmbedder = function (embedder) {
if (watchedEmbedders.has(embedder)) {
return
}
watchedEmbedders.add(embedder)
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))
}
}
for (const event of destroyEvents) {
embedder.removeListener(event, destroy)
}
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)
}
}
}
@ -211,23 +262,24 @@ ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, elementI
attachGuest(event.sender, elementInstanceId, guestInstanceId, params)
})
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, id) {
destroyGuest(event.sender, id)
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, id, params) {
const guestInstance = guestInstances[id]
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_SET_SIZE', function (event, guestInstanceId, params) {
const guestInstance = guestInstances[guestInstanceId]
return guestInstance != null ? guestInstance.guest.setSize(params) : void 0
})
// Returns WebContents from its guest id.
exports.getGuest = function (id) {
const guestInstance = guestInstances[id]
exports.getGuest = function (guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId]
return guestInstance != null ? guestInstance.guest : void 0
}
// Returns the embedder of the guest.
exports.getEmbedder = function (id) {
const guestInstance = guestInstances[id]
const getEmbedder = function (guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId]
return guestInstance != null ? guestInstance.embedder : void 0
}
exports.getEmbedder = getEmbedder

View file

@ -7,6 +7,7 @@ var requestId = 0
var WEB_VIEW_EVENTS = {
'load-commit': ['url', 'isMainFrame'],
'did-attach': [],
'did-finish-load': [],
'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL', 'isMainFrame'],
'did-frame-finish-load': ['isMainFrame'],
@ -62,6 +63,15 @@ var dispatchEvent = function (webView, eventName, eventKey, ...args) {
module.exports = {
registerEvents: function (webView, viewInstanceId) {
ipcRenderer.on('ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-' + viewInstanceId, function () {
var domEvent
webFrame.detachGuest(webView.internalInstanceId)
webView.guestInstanceId = undefined
webView.reset()
domEvent = new Event('destroyed')
webView.dispatchEvent(domEvent)
})
ipcRenderer.on('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + viewInstanceId, function (event, eventName, ...args) {
dispatchEvent.apply(null, [webView, eventName, eventName].concat(args))
})
@ -85,6 +95,7 @@ module.exports = {
})
},
deregisterEvents: function (viewInstanceId) {
ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-' + viewInstanceId)
ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + viewInstanceId)
ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + viewInstanceId)
return ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + viewInstanceId)

View file

@ -130,6 +130,46 @@ class PartitionAttribute extends WebViewAttribute {
}
}
// An attribute that controls the guest instance this webview is connected to
class GuestInstanceAttribute extends WebViewAttribute {
constructor (webViewImpl) {
super(webViewConstants.ATTRIBUTE_GUESTINSTANCE, webViewImpl)
}
// Retrieves and returns the attribute's value.
getValue () {
if (this.webViewImpl.webviewNode.hasAttribute(this.name)) {
return parseInt(this.webViewImpl.webviewNode.getAttribute(this.name))
}
return undefined
}
// Sets the attribute's value.
setValue (value) {
if (!value) {
return this.webViewImpl.webviewNode.removeAttribute(this.name)
}
if (isNaN(value)) {
return
}
return this.webViewImpl.webviewNode.setAttribute(this.name, value)
}
handleMutation (oldValue, newValue) {
if (!newValue) {
this.webViewImpl.reset()
return
}
const intVal = parseInt(newValue)
if (!isNaN(newValue) && remote.getGuestWebContents(intVal)) {
this.webViewImpl.attachGuestInstance(intVal)
} else {
this.setValueIgnoreMutation(oldValue)
}
}
}
// Attribute that handles the location and navigation of the webview.
class SrcAttribute extends WebViewAttribute {
constructor (webViewImpl) {
@ -287,6 +327,7 @@ WebViewImpl.prototype.setupWebViewAttributes = function () {
this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this)
this.attributes[webViewConstants.ATTRIBUTE_GUESTINSTANCE] = new GuestInstanceAttribute(this)
const autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH]
autosizeAttributes.forEach((attribute) => {

View file

@ -17,6 +17,7 @@ module.exports = {
ATTRIBUTE_USERAGENT: 'useragent',
ATTRIBUTE_BLINKFEATURES: 'blinkfeatures',
ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures',
ATTRIBUTE_GUESTINSTANCE: 'guestinstance',
// Internal attribute.
ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid',

View file

@ -71,12 +71,13 @@ var WebViewImpl = (function () {
// that we don't end up allocating a second guest.
if (this.guestInstanceId) {
guestViewInternal.destroyGuest(this.guestInstanceId)
this.webContents = null
this.guestInstanceId = void 0
this.beforeFirstNavigation = true
this.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true
}
this.internalInstanceId = 0
this.webContents = null
this.attributes[webViewConstants.ATTRIBUTE_GUESTINSTANCE].setValueIgnoreMutation(undefined)
this.beforeFirstNavigation = true
this.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true
}
// Sets the <webview>.request property.
@ -184,7 +185,7 @@ var WebViewImpl = (function () {
WebViewImpl.prototype.createGuest = function () {
return guestViewInternal.createGuest(this.buildParams(), (event, guestInstanceId) => {
this.attachWindow(guestInstanceId)
this.attachGuestInstance(guestInstanceId)
})
}
@ -257,8 +258,9 @@ var WebViewImpl = (function () {
return params
}
WebViewImpl.prototype.attachWindow = function (guestInstanceId) {
WebViewImpl.prototype.attachGuestInstance = function (guestInstanceId) {
this.guestInstanceId = guestInstanceId
this.attributes[webViewConstants.ATTRIBUTE_GUESTINSTANCE].setValueIgnoreMutation(guestInstanceId)
this.webContents = remote.getGuestWebContents(this.guestInstanceId)
if (!this.internalInstanceId) {
return true
@ -324,10 +326,11 @@ var registerWebViewElement = function () {
}
guestViewInternal.deregisterEvents(internal.viewInstanceId)
internal.elementAttached = false
return internal.reset()
internal.reset()
this.internalInstanceId = 0
}
proto.attachedCallback = function () {
var internal
var internal, instance
internal = v8Util.getHiddenValue(this, 'internal')
if (!internal) {
return
@ -335,6 +338,10 @@ var registerWebViewElement = function () {
if (!internal.elementAttached) {
guestViewInternal.registerEvents(internal, internal.viewInstanceId)
internal.elementAttached = true
instance = internal.attributes[webViewConstants.ATTRIBUTE_GUESTINSTANCE].getValue()
if (instance) {
return internal.attachGuestInstance(instance)
}
return internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse()
}
}

View file

@ -0,0 +1,17 @@
<html>
<body>
<script type="text/javascript" charset="utf-8">
var ipcRenderer = require('electron').ipcRenderer
ipcRenderer.on('guestinstance', function(event, instance) {
var webview = new WebView()
webview.setAttribute('guestinstance', instance)
document.body.appendChild(webview)
webview.addEventListener('did-attach', function (){
ipcRenderer.send('pong')
})
})
</script>
</body>
</html>

View file

@ -2,7 +2,7 @@ const assert = require('assert')
const path = require('path')
const http = require('http')
const url = require('url')
const {app, session, ipcMain, BrowserWindow} = require('electron').remote
const {app, session, getGuestWebContents, ipcMain, BrowserWindow} = require('electron').remote
describe('<webview> tag', function () {
this.timeout(20000)
@ -1018,4 +1018,194 @@ describe('<webview> tag', function () {
done()
})
})
describe('guestinstance attribute', function () {
it('before loading there is no attribute', function () {
document.body.appendChild(webview)
assert(!webview.hasAttribute('guestinstance'))
})
it('loading a page sets the guest view', function (done) {
var loadListener = function () {
webview.removeEventListener('did-finish-load', loadListener, false)
var instance = webview.getAttribute('guestinstance')
assert.equal(instance, parseInt(instance))
var guest = getGuestWebContents(parseInt(instance))
assert.equal(guest, webview.getWebContents())
done()
}
webview.addEventListener('did-finish-load', loadListener, false)
webview.src = 'file://' + fixtures + '/api/blank.html'
document.body.appendChild(webview)
})
it('deleting the attribute destroys the webview', function (done) {
var loadListener = function () {
webview.removeEventListener('did-finish-load', loadListener, false)
var destroyListener = function () {
webview.removeEventListener('destroyed', destroyListener, false)
assert.equal(getGuestWebContents(instance), null)
done()
}
webview.addEventListener('destroyed', destroyListener, false)
var instance = parseInt(webview.getAttribute('guestinstance'))
webview.removeAttribute('guestinstance')
}
webview.addEventListener('did-finish-load', loadListener, false)
webview.src = 'file://' + fixtures + '/api/blank.html'
document.body.appendChild(webview)
})
it('setting the attribute on a new webview moves the contents', function (done) {
var loadListener = function () {
webview.removeEventListener('did-finish-load', loadListener, false)
var webContents = webview.getWebContents()
var instance = webview.getAttribute('guestinstance')
var destroyListener = function () {
webview.removeEventListener('destroyed', destroyListener, false)
assert.equal(webContents, webview2.getWebContents())
// Make sure that events are hooked up to the right webview now
webview2.addEventListener('console-message', function (e) {
assert.equal(e.message, 'a')
document.body.removeChild(webview2)
done()
})
webview2.src = 'file://' + fixtures + '/pages/a.html'
}
webview.addEventListener('destroyed', destroyListener, false)
var webview2 = new WebView()
webview2.setAttribute('guestinstance', instance)
document.body.appendChild(webview2)
}
webview.addEventListener('did-finish-load', loadListener, false)
webview.src = 'file://' + fixtures + '/api/blank.html'
document.body.appendChild(webview)
})
it('setting the attribute to an invalid guestinstance does nothing', function (done) {
var loadListener = function () {
webview.removeEventListener('did-finish-load', loadListener, false)
webview.setAttribute('guestinstance', 55)
// Make sure that events are still hooked up to the webview
webview.addEventListener('console-message', function (e) {
assert.equal(e.message, 'a')
done()
})
webview.src = 'file://' + fixtures + '/pages/a.html'
}
webview.addEventListener('did-finish-load', loadListener, false)
webview.src = 'file://' + fixtures + '/api/blank.html'
document.body.appendChild(webview)
})
it('setting the attribute on an existing webview moves the contents', function (done) {
var load1Listener = function () {
webview.removeEventListener('did-finish-load', load1Listener, false)
var webContents = webview.getWebContents()
var instance = webview.getAttribute('guestinstance')
var destroyedInstance
var destroyListener = function () {
webview.removeEventListener('destroyed', destroyListener, false)
assert.equal(webContents, webview2.getWebContents())
assert.equal(null, getGuestWebContents(parseInt(destroyedInstance)))
// Make sure that events are hooked up to the right webview now
webview2.addEventListener('console-message', function (e) {
assert.equal(e.message, 'a')
document.body.removeChild(webview2)
done()
})
webview2.src = 'file://' + fixtures + '/pages/a.html'
}
webview.addEventListener('destroyed', destroyListener, false)
var webview2 = new WebView()
var load2Listener = function () {
webview2.removeEventListener('did-finish-load', load2Listener, false)
destroyedInstance = webview2.getAttribute('guestinstance')
assert.notEqual(instance, destroyedInstance)
webview2.setAttribute('guestinstance', instance)
}
webview2.addEventListener('did-finish-load', load2Listener, false)
webview2.src = 'file://' + fixtures + '/api/blank.html'
document.body.appendChild(webview2)
}
webview.addEventListener('did-finish-load', load1Listener, false)
webview.src = 'file://' + fixtures + '/api/blank.html'
document.body.appendChild(webview)
})
it('moving a guest back to its original webview should work', function (done) {
var loadListener = function () {
webview.removeEventListener('did-finish-load', loadListener, false)
var webContents = webview.getWebContents()
var instance = webview.getAttribute('guestinstance')
var destroy1Listener = function () {
webview.removeEventListener('destroyed', destroy1Listener, false)
assert.equal(webContents, webview2.getWebContents())
assert.equal(null, webview.getWebContents())
var destroy2Listener = function () {
webview2.removeEventListener('destroyed', destroy2Listener, false)
assert.equal(webContents, webview.getWebContents())
assert.equal(null, webview2.getWebContents())
// Make sure that events are hooked up to the right webview now
webview.addEventListener('console-message', function (e) {
assert.equal(e.message, 'a')
document.body.removeChild(webview2)
done()
})
webview.src = 'file://' + fixtures + '/pages/a.html'
}
webview2.addEventListener('destroyed', destroy2Listener, false)
webview.setAttribute('guestinstance', instance)
}
webview.addEventListener('destroyed', destroy1Listener, false)
var webview2 = new WebView()
webview2.setAttribute('guestinstance', instance)
document.body.appendChild(webview2)
}
webview.addEventListener('did-finish-load', loadListener, false)
webview.src = 'file://' + fixtures + '/api/blank.html'
document.body.appendChild(webview)
})
it('setting the attribute on a webview in a different window moves the contents', function (done) {
var loadListener = function () {
webview.removeEventListener('did-finish-load', loadListener, false)
var instance = webview.getAttribute('guestinstance')
w = new BrowserWindow({ show: false })
w.webContents.once('did-finish-load', function () {
ipcMain.once('pong', function () {
assert(!webview.hasAttribute('guestinstance'))
done()
})
w.webContents.send('guestinstance', instance)
})
w.loadURL('file://' + fixtures + '/pages/webview-move-to-window.html')
}
webview.addEventListener('did-finish-load', loadListener, false)
webview.src = 'file://' + fixtures + '/api/blank.html'
document.body.appendChild(webview)
})
})
})