Merge pull request #5967 from electron/window-opener-webview

Support window.opener from windows opened from a <webview>
This commit is contained in:
Cheng Zhao 2016-06-10 11:52:37 +00:00 committed by GitHub
commit 21081549fd
3 changed files with 88 additions and 38 deletions

View file

@ -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)
}) })

View file

@ -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 () {

View 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>