Skip to content

Commit 7bdd60c

Browse files
committed
break out guest-window-proxy code move in its own commit for easier review
1 parent fd0ca40 commit 7bdd60c

File tree

2 files changed

+172
-171
lines changed

2 files changed

+172
-171
lines changed

lib/browser/guest-window-manager.ts

Lines changed: 1 addition & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
'use strict'
22

3-
const { BrowserWindow, webContents } = require('electron')
4-
const { isSameOrigin } = process.electronBinding('v8_util')
5-
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
6-
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
7-
const { convertFeaturesString } = require('@electron/internal/common/parse-features-string')
3+
const { BrowserWindow } = require('electron')
84

95
const hasProp = {}.hasOwnProperty
106
const frameToGuest = new Map()
@@ -154,169 +150,3 @@ const createGuest = function (embedder, url, referrer, frameName, options, postD
154150

155151
return setupGuest(embedder, frameName, guest, options)
156152
}
157-
158-
const getGuestWindow = function (guestContents) {
159-
let guestWindow = BrowserWindow.fromWebContents(guestContents)
160-
if (guestWindow == null) {
161-
const hostContents = guestContents.hostWebContents
162-
if (hostContents != null) {
163-
guestWindow = BrowserWindow.fromWebContents(hostContents)
164-
}
165-
}
166-
if (!guestWindow) {
167-
throw new Error('getGuestWindow failed')
168-
}
169-
return guestWindow
170-
}
171-
172-
const isChildWindow = function (sender, target) {
173-
return target.getLastWebPreferences().openerId === sender.id
174-
}
175-
176-
const isRelatedWindow = function (sender, target) {
177-
return isChildWindow(sender, target) || isChildWindow(target, sender)
178-
}
179-
180-
const isScriptableWindow = function (sender, target) {
181-
return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL())
182-
}
183-
184-
const isNodeIntegrationEnabled = function (sender) {
185-
return sender.getLastWebPreferences().nodeIntegration === true
186-
}
187-
188-
// Checks whether |sender| can access the |target|:
189-
const canAccessWindow = function (sender, target) {
190-
return isChildWindow(sender, target) ||
191-
isScriptableWindow(sender, target) ||
192-
isNodeIntegrationEnabled(sender)
193-
}
194-
195-
// Routed window.open messages with raw options
196-
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
197-
if (url == null || url === '') url = 'about:blank'
198-
if (frameName == null) frameName = ''
199-
if (features == null) features = ''
200-
201-
const disposition = 'new-window'
202-
const { options, additionalFeatures } = convertFeaturesString(features, frameName)
203-
const referrer = { url: '', policy: 'default' }
204-
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event,
205-
url, referrer, frameName, disposition, options, additionalFeatures, null)
206-
})
207-
208-
// Routed window.open messages with fully parsed options
209-
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, referrer,
210-
frameName, disposition, options, additionalFeatures, postData) {
211-
options = mergeBrowserWindowOptions(event.sender, options)
212-
const postBody = postData ? {
213-
data: postData,
214-
headers: headersForPostData(postData)
215-
} : null
216-
217-
event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer, postBody)
218-
const { newGuest } = event
219-
if ((event.sender.getType() === 'webview' && event.sender.getLastWebPreferences().disablePopups) || event.defaultPrevented) {
220-
if (newGuest != null) {
221-
if (options.webContents === newGuest.webContents) {
222-
// the webContents is not changed, so set defaultPrevented to false to
223-
// stop the callers of this event from destroying the webContents.
224-
event.defaultPrevented = false
225-
}
226-
event.returnValue = setupGuest(event.sender, frameName, newGuest, options)
227-
} else {
228-
event.returnValue = null
229-
}
230-
} else {
231-
event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData)
232-
}
233-
})
234-
235-
const makeSafeHandler = function (handler) {
236-
return (event, guestId, ...args) => {
237-
const guestContents = webContents.fromId(guestId)
238-
if (!guestContents) {
239-
throw new Error(`Invalid guestId: ${guestId}`)
240-
}
241-
242-
return handler(event, guestContents, ...args)
243-
}
244-
}
245-
246-
const handleMessage = function (channel, handler) {
247-
ipcMainInternal.handle(channel, makeSafeHandler(handler))
248-
}
249-
250-
const handleMessageSync = function (channel, handler) {
251-
ipcMainUtils.handleSync(channel, makeSafeHandler(handler))
252-
}
253-
254-
const assertCanAccessWindow = function (contents, guestContents) {
255-
if (!canAccessWindow(contents, guestContents)) {
256-
console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`)
257-
throw new Error(`Access denied to guestId: ${guestContents.id}`)
258-
}
259-
}
260-
261-
const windowMethods = new Set([
262-
'destroy',
263-
'focus',
264-
'blur'
265-
])
266-
267-
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestContents, method, ...args) => {
268-
assertCanAccessWindow(event.sender, guestContents)
269-
270-
if (!windowMethods.has(method)) {
271-
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
272-
throw new Error(`Invalid method: ${method}`)
273-
}
274-
275-
return getGuestWindow(guestContents)[method](...args)
276-
})
277-
278-
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestContents, message, targetOrigin, sourceOrigin) => {
279-
if (targetOrigin == null) {
280-
targetOrigin = '*'
281-
}
282-
283-
// The W3C does not seem to have word on how postMessage should work when the
284-
// origins do not match, so we do not do |canAccessWindow| check here since
285-
// postMessage across origins is useful and not harmful.
286-
if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) {
287-
const sourceId = event.sender.id
288-
guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
289-
}
290-
})
291-
292-
const webContentsMethodsAsync = new Set([
293-
'loadURL',
294-
'executeJavaScript',
295-
'print'
296-
])
297-
298-
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
299-
assertCanAccessWindow(event.sender, guestContents)
300-
301-
if (!webContentsMethodsAsync.has(method)) {
302-
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
303-
throw new Error(`Invalid method: ${method}`)
304-
}
305-
306-
return guestContents[method](...args)
307-
})
308-
309-
const webContentsMethodsSync = new Set([
310-
'getURL'
311-
])
312-
313-
handleMessageSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
314-
assertCanAccessWindow(event.sender, guestContents)
315-
316-
if (!webContentsMethodsSync.has(method)) {
317-
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
318-
throw new Error(`Invalid method: ${method}`)
319-
}
320-
321-
return guestContents[method](...args)
322-
})

lib/browser/guest-window-proxy.js

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
const { BrowserWindow, webContents } = require('electron')
2+
const { isSameOrigin } = process.electronBinding('v8_util')
3+
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal')
4+
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils')
5+
const { convertFeaturesString } = require('@electron/internal/common/parse-features-string')
6+
7+
const getGuestWindow = function (guestContents) {
8+
let guestWindow = BrowserWindow.fromWebContents(guestContents)
9+
if (guestWindow == null) {
10+
const hostContents = guestContents.hostWebContents
11+
if (hostContents != null) {
12+
guestWindow = BrowserWindow.fromWebContents(hostContents)
13+
}
14+
}
15+
if (!guestWindow) {
16+
throw new Error('getGuestWindow failed')
17+
}
18+
return guestWindow
19+
}
20+
21+
const isChildWindow = function (sender, target) {
22+
return target.getLastWebPreferences().openerId === sender.id
23+
}
24+
25+
const isRelatedWindow = function (sender, target) {
26+
return isChildWindow(sender, target) || isChildWindow(target, sender)
27+
}
28+
29+
const isScriptableWindow = function (sender, target) {
30+
return isRelatedWindow(sender, target) && isSameOrigin(sender.getURL(), target.getURL())
31+
}
32+
33+
const isNodeIntegrationEnabled = function (sender) {
34+
return sender.getLastWebPreferences().nodeIntegration === true
35+
}
36+
37+
// Checks whether |sender| can access the |target|:
38+
const canAccessWindow = function (sender, target) {
39+
return isChildWindow(sender, target) ||
40+
isScriptableWindow(sender, target) ||
41+
isNodeIntegrationEnabled(sender)
42+
}
43+
44+
// Routed window.open messages with raw options
45+
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, url, frameName, features) => {
46+
if (url == null || url === '') url = 'about:blank'
47+
if (frameName == null) frameName = ''
48+
if (features == null) features = ''
49+
50+
const disposition = 'new-window'
51+
const { options, additionalFeatures } = convertFeaturesString(features, frameName)
52+
const referrer = { url: '', policy: 'default' }
53+
ipcMainInternal.emit('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', event,
54+
url, referrer, frameName, disposition, options, additionalFeatures, null)
55+
})
56+
57+
// Routed window.open messages with fully parsed options
58+
ipcMainInternal.on('ELECTRON_GUEST_WINDOW_MANAGER_INTERNAL_WINDOW_OPEN', function (event, url, referrer,
59+
frameName, disposition, options, additionalFeatures, postData) {
60+
options = mergeBrowserWindowOptions(event.sender, options)
61+
const postBody = postData ? {
62+
data: postData,
63+
headers: headersForPostData(postData)
64+
} : null
65+
66+
event.sender.emit('new-window', event, url, frameName, disposition, options, additionalFeatures, referrer, postBody)
67+
const { newGuest } = event
68+
if ((event.sender.getType() === 'webview' && event.sender.getLastWebPreferences().disablePopups) || event.defaultPrevented) {
69+
if (newGuest != null) {
70+
if (options.webContents === newGuest.webContents) {
71+
// the webContents is not changed, so set defaultPrevented to false to
72+
// stop the callers of this event from destroying the webContents.
73+
event.defaultPrevented = false
74+
}
75+
event.returnValue = setupGuest(event.sender, frameName, newGuest, options)
76+
} else {
77+
event.returnValue = null
78+
}
79+
} else {
80+
event.returnValue = createGuest(event.sender, url, referrer, frameName, options, postData)
81+
}
82+
})
83+
84+
const makeSafeHandler = function (handler) {
85+
return (event, guestId, ...args) => {
86+
const guestContents = webContents.fromId(guestId)
87+
if (!guestContents) {
88+
throw new Error(`Invalid guestId: ${guestId}`)
89+
}
90+
91+
return handler(event, guestContents, ...args)
92+
}
93+
}
94+
95+
const handleMessage = function (channel, handler) {
96+
ipcMainInternal.handle(channel, makeSafeHandler(handler))
97+
}
98+
99+
const handleMessageSync = function (channel, handler) {
100+
ipcMainUtils.handleSync(channel, makeSafeHandler(handler))
101+
}
102+
103+
const assertCanAccessWindow = function (contents, guestContents) {
104+
if (!canAccessWindow(contents, guestContents)) {
105+
console.error(`Blocked ${contents.getURL()} from accessing guestId: ${guestContents.id}`)
106+
throw new Error(`Access denied to guestId: ${guestContents.id}`)
107+
}
108+
}
109+
110+
const windowMethods = new Set([
111+
'destroy',
112+
'focus',
113+
'blur'
114+
])
115+
116+
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', (event, guestContents, method, ...args) => {
117+
assertCanAccessWindow(event.sender, guestContents)
118+
119+
if (!windowMethods.has(method)) {
120+
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
121+
throw new Error(`Invalid method: ${method}`)
122+
}
123+
124+
return getGuestWindow(guestContents)[method](...args)
125+
})
126+
127+
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', (event, guestContents, message, targetOrigin, sourceOrigin) => {
128+
if (targetOrigin == null) {
129+
targetOrigin = '*'
130+
}
131+
132+
// The W3C does not seem to have word on how postMessage should work when the
133+
// origins do not match, so we do not do |canAccessWindow| check here since
134+
// postMessage across origins is useful and not harmful.
135+
if (targetOrigin === '*' || isSameOrigin(guestContents.getURL(), targetOrigin)) {
136+
const sourceId = event.sender.id
137+
guestContents._sendInternal('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
138+
}
139+
})
140+
141+
const webContentsMethodsAsync = new Set([
142+
'loadURL',
143+
'executeJavaScript',
144+
'print'
145+
])
146+
147+
handleMessage('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
148+
assertCanAccessWindow(event.sender, guestContents)
149+
150+
if (!webContentsMethodsAsync.has(method)) {
151+
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
152+
throw new Error(`Invalid method: ${method}`)
153+
}
154+
155+
return guestContents[method](...args)
156+
})
157+
158+
const webContentsMethodsSync = new Set([
159+
'getURL'
160+
])
161+
162+
handleMessageSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', (event, guestContents, method, ...args) => {
163+
assertCanAccessWindow(event.sender, guestContents)
164+
165+
if (!webContentsMethodsSync.has(method)) {
166+
console.error(`Blocked ${event.sender.getURL()} from calling method: ${method}`)
167+
throw new Error(`Invalid method: ${method}`)
168+
}
169+
170+
return guestContents[method](...args)
171+
})

0 commit comments

Comments
 (0)