fix: use appropriate site instance for cross-site nav's (#15821)

* fix: use Chromium's determined new site instance as candidate when navigating.

When navigating to a new address, consider using Chromium's determined site instance
for the new page as it should belong to an existing browsing instance when the
navigation was triggered by window.open().

fixes 8100.

* Revert "fix: use Chromium's determined new site instance as candidate when navigating."

This reverts commit eb95f935654a2c4d4457821297670836c10fdfd5.

* fix: delegate site instance creation back to content when sandboxed.

* fix: ensure site isolation is on

* test: adapt ut for cross-site navigation

* fix: register pending processes during a navigation.

* refactor: dont call loadURL for a window constructed from an existing webContents.

* test: add sandboxed affinity UT's.

* fix: check affinity before deciding if to force a new site instance.

* chore: adapt subsequent patch.

* refactor: constify logically const methods.

* fix: do not reuse site instances when navigation redirects cross-site.

* test: ensure localStorage accessible after x-site redirect.

* test: adapt localStorage acess denied UT for site isolation.

* fix: do not send render-view-deleted for speculative frames.

* chore: amend tests after rebase.

* test: add ut for webContents' render-view-deleted emission

* fix: introduce current-render-view-deleted for current RVH's deletions.

Revert render-view-deleted to being emitted with any RVH's deletion.
current-render-view-deleted is emitted only when the RVH being deleted
is the current one.

* refactor: style and comments fixed.
This commit is contained in:
Pedro Pontes 2018-12-05 09:03:39 +01:00 committed by Cheng Zhao
parent 46e7214974
commit d5d1fa8290
14 changed files with 581 additions and 240 deletions

View file

@ -90,6 +90,8 @@ describe('BrowserWindow module', () => {
res.end()
} else if (req.url === '/navigate-302') {
res.end(`<html><body><script>window.location='${server.url}/302'</script></body></html>`)
} else if (req.url === '/cross-site') {
res.end(`<html><body><h1>${req.url}</h1></body></html>`)
} else {
res.end()
}
@ -1470,29 +1472,6 @@ describe('BrowserWindow module', () => {
const preload = path.join(fixtures, 'module', 'preload-sandbox.js')
// http protocol to simulate accessing another domain. This is required
// because the code paths for cross domain popups is different.
function crossDomainHandler (request, callback) {
// Disabled due to false positive in StandardJS
// eslint-disable-next-line standard/no-callback-literal
callback({
mimeType: 'text/html',
data: `<html><body><h1>${request.url}</h1></body></html>`
})
}
before((done) => {
protocol.interceptStringProtocol('http', crossDomainHandler, () => {
done()
})
})
after((done) => {
protocol.uninterceptProtocol('http', () => {
done()
})
})
it('exposes ipcRenderer to preload script', (done) => {
ipcMain.once('answer', function (event, test) {
assert.strictEqual(test, 'preload')
@ -1587,35 +1566,53 @@ describe('BrowserWindow module', () => {
})
ipcRenderer.send('set-web-preferences-on-next-new-window', w.webContents.id, 'preload', preload)
const openerWindowOpen = emittedOnce(ipcMain, 'opener-loaded')
w.loadFile(
path.join(fixtures, 'api', 'sandbox.html'),
{ search: 'window-open-external' }
)
// Wait for a message from the main window saying that it's ready.
await openerWindowOpen
// Ask the opener to open a popup with window.opener.
const expectedPopupUrl = `${server.url}/cross-site` // Set in "sandbox.html".
const browserWindowCreated = emittedOnce(app, 'browser-window-created')
const childLoaded = emittedOnce(ipcMain, 'child-loaded')
w.loadFile(path.join(fixtures, 'api', 'sandbox.html'), { search: 'window-open-external' })
const expectedPopupUrl = 'http://www.google.com/#q=electron' // Set in the "sandbox.html".
w.webContents.send('open-the-popup', expectedPopupUrl)
// The page is going to open a popup that it won't be able to close.
// We have to close it from here later.
// XXX(alexeykuzmin): It will leak if the test fails too soon.
const [, popupWindow] = await browserWindowCreated
// Wait for a message from the popup's preload script.
const [, openerIsNull, html, locationHref] = await childLoaded
expect(openerIsNull).to.be.true('window.opener is not null')
expect(html).to.equal(`<h1>${expectedPopupUrl}</h1>`,
'looks like a http: request has not been intercepted locally')
// Ask the popup window for details.
const detailsAnswer = emittedOnce(ipcMain, 'child-loaded')
popupWindow.webContents.send('provide-details')
const [, openerIsNull, , locationHref] = await detailsAnswer
expect(openerIsNull).to.be.false('window.opener is null')
expect(locationHref).to.equal(expectedPopupUrl)
// Ask the page to access the popup.
const answer = emittedOnce(ipcMain, 'answer')
const touchPopupResult = emittedOnce(ipcMain, 'answer')
w.webContents.send('touch-the-popup')
const [, exceptionMessage] = await answer
const [, popupAccessMessage] = await touchPopupResult
// Ask the popup to access the opener.
const touchOpenerResult = emittedOnce(ipcMain, 'answer')
popupWindow.webContents.send('touch-the-opener')
const [, openerAccessMessage] = await touchOpenerResult
// We don't need the popup anymore, and its parent page can't close it,
// so let's close it from here before we run any checks.
await closeWindow(popupWindow, { assertSingleWindow: false })
expect(exceptionMessage).to.be.a('string',
expect(popupAccessMessage).to.be.a('string',
`child's .document is accessible from its parent window`)
expect(exceptionMessage).to.match(/^Blocked a frame with origin/)
expect(popupAccessMessage).to.match(/^Blocked a frame with origin/)
expect(openerAccessMessage).to.be.a('string',
`opener .document is accessible from a popup window`)
expect(openerAccessMessage).to.match(/^Blocked a frame with origin/)
})
it('should inherit the sandbox setting in opened windows', (done) => {