Merge pull request #5967 from electron/window-opener-webview
Support window.opener from windows opened from a <webview>
This commit is contained in:
commit
21081549fd
3 changed files with 88 additions and 38 deletions
|
@ -1,14 +1,13 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const ipcMain = require('electron').ipcMain
|
const {BrowserWindow, ipcMain, webContents} = require('electron')
|
||||||
const BrowserWindow = require('electron').BrowserWindow
|
|
||||||
|
|
||||||
var hasProp = {}.hasOwnProperty
|
const hasProp = {}.hasOwnProperty
|
||||||
var frameToGuest = {}
|
const frameToGuest = {}
|
||||||
|
|
||||||
// Copy attribute of |parent| to |child| if it is not defined in |child|.
|
// Copy attribute of |parent| to |child| if it is not defined in |child|.
|
||||||
var mergeOptions = function (child, parent) {
|
const mergeOptions = function (child, parent) {
|
||||||
var key, value
|
let key, value
|
||||||
for (key in parent) {
|
for (key in parent) {
|
||||||
if (!hasProp.call(parent, key)) continue
|
if (!hasProp.call(parent, key)) continue
|
||||||
value = parent[key]
|
value = parent[key]
|
||||||
|
@ -24,7 +23,7 @@ var mergeOptions = function (child, parent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge |options| with the |embedder|'s window's options.
|
// Merge |options| with the |embedder|'s window's options.
|
||||||
var mergeBrowserWindowOptions = function (embedder, options) {
|
const mergeBrowserWindowOptions = function (embedder, options) {
|
||||||
if (embedder.browserWindowOptions != null) {
|
if (embedder.browserWindowOptions != null) {
|
||||||
// Inherit the original options if it is a BrowserWindow.
|
// Inherit the original options if it is a BrowserWindow.
|
||||||
mergeOptions(options, embedder.browserWindowOptions)
|
mergeOptions(options, embedder.browserWindowOptions)
|
||||||
|
@ -45,9 +44,8 @@ var mergeBrowserWindowOptions = function (embedder, options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new guest created by |embedder| with |options|.
|
// Create a new guest created by |embedder| with |options|.
|
||||||
var createGuest = function (embedder, url, frameName, options) {
|
const createGuest = function (embedder, url, frameName, options) {
|
||||||
var closedByEmbedder, closedByUser, guest, guestId, ref1
|
let guest = frameToGuest[frameName]
|
||||||
guest = frameToGuest[frameName]
|
|
||||||
if (frameName && (guest != null)) {
|
if (frameName && (guest != null)) {
|
||||||
guest.loadURL(url)
|
guest.loadURL(url)
|
||||||
return guest.id
|
return guest.id
|
||||||
|
@ -57,24 +55,26 @@ var createGuest = function (embedder, url, frameName, options) {
|
||||||
if (options.webPreferences == null) {
|
if (options.webPreferences == null) {
|
||||||
options.webPreferences = {}
|
options.webPreferences = {}
|
||||||
}
|
}
|
||||||
options.webPreferences.openerId = (ref1 = BrowserWindow.fromWebContents(embedder)) != null ? ref1.id : void 0
|
options.webPreferences.openerId = embedder.id
|
||||||
guest = new BrowserWindow(options)
|
guest = new BrowserWindow(options)
|
||||||
guest.loadURL(url)
|
guest.loadURL(url)
|
||||||
|
|
||||||
// When |embedder| is destroyed we should also destroy attached guest, and if
|
// When |embedder| is destroyed we should also destroy attached guest, and if
|
||||||
// guest is closed by user then we should prevent |embedder| from double
|
// guest is closed by user then we should prevent |embedder| from double
|
||||||
// closing guest.
|
// closing guest.
|
||||||
guestId = guest.id
|
const guestId = guest.webContents.id
|
||||||
closedByEmbedder = function () {
|
|
||||||
|
const closedByEmbedder = function () {
|
||||||
guest.removeListener('closed', closedByUser)
|
guest.removeListener('closed', closedByUser)
|
||||||
return guest.destroy()
|
guest.destroy()
|
||||||
}
|
}
|
||||||
closedByUser = function () {
|
const closedByUser = function () {
|
||||||
embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
|
embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
|
||||||
return embedder.removeListener('render-view-deleted', closedByEmbedder)
|
embedder.removeListener('render-view-deleted', closedByEmbedder)
|
||||||
}
|
}
|
||||||
embedder.once('render-view-deleted', closedByEmbedder)
|
embedder.once('render-view-deleted', closedByEmbedder)
|
||||||
guest.once('closed', closedByUser)
|
guest.once('closed', closedByUser)
|
||||||
|
|
||||||
if (frameName) {
|
if (frameName) {
|
||||||
frameToGuest[frameName] = guest
|
frameToGuest[frameName] = guest
|
||||||
guest.frameName = frameName
|
guest.frameName = frameName
|
||||||
|
@ -82,7 +82,22 @@ var createGuest = function (embedder, url, frameName, options) {
|
||||||
delete frameToGuest[frameName]
|
delete frameToGuest[frameName]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return guest.id
|
|
||||||
|
return guestId
|
||||||
|
}
|
||||||
|
|
||||||
|
const getGuestWindow = function (guestId) {
|
||||||
|
const guestContents = webContents.fromId(guestId)
|
||||||
|
if (guestContents == null) return
|
||||||
|
|
||||||
|
let guestWindow = BrowserWindow.fromWebContents(guestContents)
|
||||||
|
if (guestWindow == null) {
|
||||||
|
const hostContents = guestContents.hostWebContents
|
||||||
|
if (hostContents != null) {
|
||||||
|
guestWindow = BrowserWindow.fromWebContents(hostContents)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return guestWindow
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routed window.open messages.
|
// Routed window.open messages.
|
||||||
|
@ -97,28 +112,26 @@ ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function (event, url, fr
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
|
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
|
||||||
var ref1
|
const guestWindow = getGuestWindow(guestId)
|
||||||
(ref1 = BrowserWindow.fromId(guestId)) != null ? ref1.destroy() : void 0
|
if (guestWindow != null) guestWindow.destroy()
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
|
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
|
||||||
var ref1
|
const guestWindow = getGuestWindow(guestId)
|
||||||
event.returnValue = (ref1 = BrowserWindow.fromId(guestId)) != null ? ref1[method].apply(ref1, args) : void 0
|
event.returnValue = guestWindow != null ? guestWindow[method].apply(guestWindow, args) : null
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
|
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
|
||||||
var guestContents, ref1, ref2, sourceId
|
const guestContents = webContents.fromId(guestId)
|
||||||
sourceId = (ref1 = BrowserWindow.fromWebContents(event.sender)) != null ? ref1.id : void 0
|
if (guestContents == null) return
|
||||||
if (sourceId == null) {
|
|
||||||
return
|
if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
|
||||||
}
|
const sourceId = event.sender.id
|
||||||
guestContents = (ref2 = BrowserWindow.fromId(guestId)) != null ? ref2.webContents : void 0
|
guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
|
||||||
if ((guestContents != null ? guestContents.getURL().indexOf(targetOrigin) : void 0) === 0 || targetOrigin === '*') {
|
|
||||||
guestContents != null ? guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin) : void 0
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
|
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
|
||||||
var ref1, ref2
|
const guestContents = webContents.fromId(guestId)
|
||||||
(ref1 = BrowserWindow.fromId(guestId)) != null ? (ref2 = ref1.webContents) != null ? ref2[method].apply(ref2, args) : void 0 : void 0
|
if (guestContents != null) guestContents[method].apply(guestContents, args)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
|
|
||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const http = require('http')
|
const http = require('http')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const ws = require('ws')
|
const ws = require('ws')
|
||||||
|
const url = require('url')
|
||||||
const remote = require('electron').remote
|
const remote = require('electron').remote
|
||||||
|
|
||||||
const BrowserWindow = remote.require('electron').BrowserWindow
|
const {BrowserWindow, session, webContents} = remote
|
||||||
const session = remote.require('electron').session
|
|
||||||
|
|
||||||
const isCI = remote.getGlobal('isCi')
|
const isCI = remote.getGlobal('isCi')
|
||||||
|
|
||||||
|
@ -235,7 +234,7 @@ describe('chromium feature', function () {
|
||||||
else
|
else
|
||||||
targetURL = 'file://' + fixtures + '/pages/base-page.html'
|
targetURL = 'file://' + fixtures + '/pages/base-page.html'
|
||||||
b = window.open(targetURL)
|
b = window.open(targetURL)
|
||||||
BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () {
|
webContents.fromId(b.guestId).once('did-finish-load', function () {
|
||||||
assert.equal(b.location, targetURL)
|
assert.equal(b.location, targetURL)
|
||||||
b.close()
|
b.close()
|
||||||
done()
|
done()
|
||||||
|
@ -246,10 +245,10 @@ describe('chromium feature', function () {
|
||||||
// Load a page that definitely won't redirect
|
// Load a page that definitely won't redirect
|
||||||
var b
|
var b
|
||||||
b = window.open('about:blank')
|
b = window.open('about:blank')
|
||||||
BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () {
|
webContents.fromId(b.guestId).once('did-finish-load', function () {
|
||||||
// When it loads, redirect
|
// When it loads, redirect
|
||||||
b.location = 'file://' + fixtures + '/pages/base-page.html'
|
b.location = 'file://' + fixtures + '/pages/base-page.html'
|
||||||
BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () {
|
webContents.fromId(b.guestId).once('did-finish-load', function () {
|
||||||
// After our second redirect, cleanup and callback
|
// After our second redirect, cleanup and callback
|
||||||
b.close()
|
b.close()
|
||||||
done()
|
done()
|
||||||
|
@ -308,7 +307,7 @@ describe('chromium feature', function () {
|
||||||
}
|
}
|
||||||
window.addEventListener('message', listener)
|
window.addEventListener('message', listener)
|
||||||
b = window.open('file://' + fixtures + '/pages/window-open-postMessage.html', '', 'show=no')
|
b = window.open('file://' + fixtures + '/pages/window-open-postMessage.html', '', 'show=no')
|
||||||
BrowserWindow.fromId(b.guestId).webContents.once('did-finish-load', function () {
|
webContents.fromId(b.guestId).once('did-finish-load', function () {
|
||||||
b.postMessage('testing', '*')
|
b.postMessage('testing', '*')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -327,6 +326,25 @@ describe('chromium feature', function () {
|
||||||
window.addEventListener('message', listener)
|
window.addEventListener('message', listener)
|
||||||
b = window.open('file://' + fixtures + '/pages/window-opener-postMessage.html', '', 'show=no')
|
b = window.open('file://' + fixtures + '/pages/window-opener-postMessage.html', '', 'show=no')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('supports windows opened from a <webview>', function (done) {
|
||||||
|
const webview = new WebView()
|
||||||
|
webview.addEventListener('console-message', function (e) {
|
||||||
|
webview.remove()
|
||||||
|
assert.equal(e.message, 'message')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
webview.allowpopups = true
|
||||||
|
webview.src = url.format({
|
||||||
|
pathname: `${fixtures}/pages/webview-opener-postMessage.html`,
|
||||||
|
protocol: 'file',
|
||||||
|
query: {
|
||||||
|
p: `${fixtures}/pages/window-opener-postMessage.html`
|
||||||
|
},
|
||||||
|
slashes: true
|
||||||
|
})
|
||||||
|
document.body.appendChild(webview)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('creating a Uint8Array under browser side', function () {
|
describe('creating a Uint8Array under browser side', function () {
|
||||||
|
|
19
spec/fixtures/pages/webview-opener-postMessage.html
vendored
Normal file
19
spec/fixtures/pages/webview-opener-postMessage.html
vendored
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script>
|
||||||
|
var windowUrl = decodeURIComponent(window.location.search.substring(3))
|
||||||
|
var opened = window.open(windowUrl, '', 'show=no')
|
||||||
|
window.addEventListener('message', function (event) {
|
||||||
|
try {
|
||||||
|
opened.close()
|
||||||
|
} finally {
|
||||||
|
console.log(event.data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue