Merge branch 'master' into native-window-open
This commit is contained in:
commit
7ac93045b7
157 changed files with 2239 additions and 1058 deletions
|
@ -533,4 +533,17 @@ describe('app module', function () {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getAppMemoryInfo() API', function () {
|
||||
it('returns the process memory of all running electron processes', function () {
|
||||
const appMemoryInfo = app.getAppMemoryInfo()
|
||||
assert.ok(appMemoryInfo.length > 0, 'App memory info object is not > 0')
|
||||
for (const {memory, pid} of appMemoryInfo) {
|
||||
assert.ok(memory.workingSetSize > 0, 'working set size is not > 0')
|
||||
assert.ok(memory.privateBytes > 0, 'private bytes is not > 0')
|
||||
assert.ok(memory.sharedBytes > 0, 'shared bytes is not > 0')
|
||||
assert.ok(pid > 0, 'pid is not > 0')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -86,6 +86,42 @@ describe('BrowserWindow module', function () {
|
|||
})
|
||||
|
||||
describe('BrowserWindow.close()', function () {
|
||||
let server
|
||||
|
||||
before(function (done) {
|
||||
server = http.createServer((request, response) => {
|
||||
switch (request.url) {
|
||||
case '/404':
|
||||
response.statusCode = '404'
|
||||
response.end()
|
||||
break
|
||||
case '/301':
|
||||
response.statusCode = '301'
|
||||
response.setHeader('Location', '/200')
|
||||
response.end()
|
||||
break
|
||||
case '/200':
|
||||
response.statusCode = '200'
|
||||
response.end('hello')
|
||||
break
|
||||
case '/title':
|
||||
response.statusCode = '200'
|
||||
response.end('<title>Hello</title>')
|
||||
break
|
||||
default:
|
||||
done('unsupported endpoint')
|
||||
}
|
||||
}).listen(0, '127.0.0.1', () => {
|
||||
server.url = 'http://127.0.0.1:' + server.address().port
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
after(function () {
|
||||
server.close()
|
||||
server = null
|
||||
})
|
||||
|
||||
it('should emit unload handler', function (done) {
|
||||
w.webContents.on('did-finish-load', function () {
|
||||
w.close()
|
||||
|
@ -109,6 +145,38 @@ describe('BrowserWindow module', function () {
|
|||
})
|
||||
w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false.html'))
|
||||
})
|
||||
|
||||
it('should not crash when invoked synchronously inside navigation observer', function (done) {
|
||||
const events = [
|
||||
{ name: 'did-start-loading', url: `${server.url}/200` },
|
||||
{ name: 'did-get-redirect-request', url: `${server.url}/301` },
|
||||
{ name: 'did-get-response-details', url: `${server.url}/200` },
|
||||
{ name: 'dom-ready', url: `${server.url}/200` },
|
||||
{ name: 'page-title-updated', url: `${server.url}/title` },
|
||||
{ name: 'did-stop-loading', url: `${server.url}/200` },
|
||||
{ name: 'did-finish-load', url: `${server.url}/200` },
|
||||
{ name: 'did-frame-finish-load', url: `${server.url}/200` },
|
||||
{ name: 'did-fail-load', url: `${server.url}/404` }
|
||||
]
|
||||
const responseEvent = 'window-webContents-destroyed'
|
||||
|
||||
function* genNavigationEvent () {
|
||||
let eventOptions = null
|
||||
while ((eventOptions = events.shift()) && events.length) {
|
||||
let w = new BrowserWindow({show: false})
|
||||
eventOptions.id = w.id
|
||||
eventOptions.responseEvent = responseEvent
|
||||
ipcRenderer.send('test-webcontents-navigation-observer', eventOptions)
|
||||
yield 1
|
||||
}
|
||||
}
|
||||
|
||||
let gen = genNavigationEvent()
|
||||
ipcRenderer.on(responseEvent, function () {
|
||||
if (!gen.next().value) done()
|
||||
})
|
||||
gen.next()
|
||||
})
|
||||
})
|
||||
|
||||
describe('window.close()', function () {
|
||||
|
@ -1082,6 +1150,29 @@ describe('BrowserWindow module', function () {
|
|||
})
|
||||
w.loadURL('file://' + path.join(fixtures, 'pages', 'window-open.html'))
|
||||
})
|
||||
|
||||
it('releases memory after popup is closed', (done) => {
|
||||
w.destroy()
|
||||
w = new BrowserWindow({
|
||||
show: false,
|
||||
webPreferences: {
|
||||
preload: preload,
|
||||
sandbox: true
|
||||
}
|
||||
})
|
||||
w.loadURL('file://' + path.join(fixtures, 'api', 'sandbox.html?allocate-memory'))
|
||||
w.webContents.openDevTools({mode: 'detach'})
|
||||
ipcMain.once('answer', function (event, {bytesBeforeOpen, bytesAfterOpen, bytesAfterClose}) {
|
||||
const memoryIncreaseByOpen = bytesAfterOpen - bytesBeforeOpen
|
||||
const memoryDecreaseByClose = bytesAfterOpen - bytesAfterClose
|
||||
// decreased memory should be less than increased due to factors we
|
||||
// can't control, but given the amount of memory allocated in the
|
||||
// fixture, we can reasonably expect decrease to be at least 70% of
|
||||
// increase
|
||||
assert(memoryDecreaseByClose > memoryIncreaseByOpen * 0.7)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('nativeWindowOpen option', () => {
|
||||
|
@ -1170,6 +1261,60 @@ describe('BrowserWindow module', function () {
|
|||
})
|
||||
w.loadURL('file://' + path.join(fixtures, 'api', 'close-beforeunload-empty-string.html'))
|
||||
})
|
||||
|
||||
it('emits for each close attempt', function (done) {
|
||||
var beforeUnloadCount = 0
|
||||
w.on('onbeforeunload', function () {
|
||||
beforeUnloadCount++
|
||||
if (beforeUnloadCount < 3) {
|
||||
w.close()
|
||||
} else if (beforeUnloadCount === 3) {
|
||||
done()
|
||||
}
|
||||
})
|
||||
w.webContents.once('did-finish-load', function () {
|
||||
w.close()
|
||||
})
|
||||
w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false-prevent3.html'))
|
||||
})
|
||||
|
||||
it('emits for each reload attempt', function (done) {
|
||||
var beforeUnloadCount = 0
|
||||
w.on('onbeforeunload', function () {
|
||||
beforeUnloadCount++
|
||||
if (beforeUnloadCount < 3) {
|
||||
w.reload()
|
||||
} else if (beforeUnloadCount === 3) {
|
||||
done()
|
||||
}
|
||||
})
|
||||
w.webContents.once('did-finish-load', function () {
|
||||
w.webContents.once('did-finish-load', function () {
|
||||
assert.fail('Reload was not prevented')
|
||||
})
|
||||
w.reload()
|
||||
})
|
||||
w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false-prevent3.html'))
|
||||
})
|
||||
|
||||
it('emits for each navigation attempt', function (done) {
|
||||
var beforeUnloadCount = 0
|
||||
w.on('onbeforeunload', function () {
|
||||
beforeUnloadCount++
|
||||
if (beforeUnloadCount < 3) {
|
||||
w.loadURL('about:blank')
|
||||
} else if (beforeUnloadCount === 3) {
|
||||
done()
|
||||
}
|
||||
})
|
||||
w.webContents.once('did-finish-load', function () {
|
||||
w.webContents.once('did-finish-load', function () {
|
||||
assert.fail('Navigation was not prevented')
|
||||
})
|
||||
w.loadURL('about:blank')
|
||||
})
|
||||
w.loadURL('file://' + path.join(fixtures, 'api', 'beforeunload-false-prevent3.html'))
|
||||
})
|
||||
})
|
||||
|
||||
describe('new-window event', function () {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const assert = require('assert')
|
||||
const http = require('http')
|
||||
const path = require('path')
|
||||
const {closeWindow} = require('./window-helpers')
|
||||
const BrowserWindow = require('electron').remote.BrowserWindow
|
||||
|
@ -70,6 +71,15 @@ describe('debugger module', function () {
|
|||
})
|
||||
|
||||
describe('debugger.sendCommand', function () {
|
||||
let server
|
||||
|
||||
afterEach(function () {
|
||||
if (server != null) {
|
||||
server.close()
|
||||
server = null
|
||||
}
|
||||
})
|
||||
|
||||
it('retuns response', function (done) {
|
||||
w.webContents.loadURL('about:blank')
|
||||
try {
|
||||
|
@ -125,5 +135,33 @@ describe('debugger module', function () {
|
|||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('handles invalid unicode characters in message', function (done) {
|
||||
try {
|
||||
w.webContents.debugger.attach()
|
||||
} catch (err) {
|
||||
done('unexpected error : ' + err)
|
||||
}
|
||||
|
||||
w.webContents.debugger.on('message', (event, method, params) => {
|
||||
if (method === 'Network.loadingFinished') {
|
||||
w.webContents.debugger.sendCommand('Network.getResponseBody', {
|
||||
requestId: params.requestId
|
||||
}, () => {
|
||||
done()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
server = http.createServer((req, res) => {
|
||||
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
|
||||
res.end('\uFFFF')
|
||||
})
|
||||
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
w.webContents.debugger.sendCommand('Network.enable')
|
||||
w.loadURL(`http://127.0.0.1:${server.address().port}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
26
spec/api-process-spec.js
Normal file
26
spec/api-process-spec.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
const assert = require('assert')
|
||||
|
||||
describe('process module', function () {
|
||||
describe('process.getCPUUsage()', function () {
|
||||
it('returns a cpu usage object', function () {
|
||||
const cpuUsage = process.getCPUUsage()
|
||||
assert.equal(typeof cpuUsage.percentCPUUsage, 'number')
|
||||
assert.equal(typeof cpuUsage.idleWakeupsPerSecond, 'number')
|
||||
})
|
||||
})
|
||||
|
||||
describe('process.getIOCounters()', function () {
|
||||
it('returns an io counters object', function () {
|
||||
if (process.platform === 'darwin') {
|
||||
return
|
||||
}
|
||||
const ioCounters = process.getIOCounters()
|
||||
assert.equal(typeof ioCounters.readOperationCount, 'number')
|
||||
assert.equal(typeof ioCounters.writeOperationCount, 'number')
|
||||
assert.equal(typeof ioCounters.otherOperationCount, 'number')
|
||||
assert.equal(typeof ioCounters.readTransferCount, 'number')
|
||||
assert.equal(typeof ioCounters.writeTransferCount, 'number')
|
||||
assert.equal(typeof ioCounters.otherTransferCount, 'number')
|
||||
})
|
||||
})
|
||||
})
|
|
@ -542,4 +542,70 @@ describe('webContents module', function () {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('destroy()', () => {
|
||||
let server
|
||||
|
||||
before(function (done) {
|
||||
server = http.createServer((request, response) => {
|
||||
switch (request.url) {
|
||||
case '/404':
|
||||
response.statusCode = '404'
|
||||
response.end()
|
||||
break
|
||||
case '/301':
|
||||
response.statusCode = '301'
|
||||
response.setHeader('Location', '/200')
|
||||
response.end()
|
||||
break
|
||||
case '/200':
|
||||
response.statusCode = '200'
|
||||
response.end('hello')
|
||||
break
|
||||
default:
|
||||
done('unsupported endpoint')
|
||||
}
|
||||
}).listen(0, '127.0.0.1', () => {
|
||||
server.url = 'http://127.0.0.1:' + server.address().port
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
after(function () {
|
||||
server.close()
|
||||
server = null
|
||||
})
|
||||
|
||||
it('should not crash when invoked synchronously inside navigation observer', (done) => {
|
||||
const events = [
|
||||
{ name: 'did-start-loading', url: `${server.url}/200` },
|
||||
{ name: 'did-get-redirect-request', url: `${server.url}/301` },
|
||||
{ name: 'did-get-response-details', url: `${server.url}/200` },
|
||||
{ name: 'dom-ready', url: `${server.url}/200` },
|
||||
{ name: 'did-stop-loading', url: `${server.url}/200` },
|
||||
{ name: 'did-finish-load', url: `${server.url}/200` },
|
||||
// FIXME: Multiple Emit calls inside an observer assume that object
|
||||
// will be alive till end of the observer. Synchronous `destroy` api
|
||||
// violates this contract and crashes.
|
||||
// { name: 'did-frame-finish-load', url: `${server.url}/200` },
|
||||
{ name: 'did-fail-load', url: `${server.url}/404` }
|
||||
]
|
||||
const responseEvent = 'webcontents-destroyed'
|
||||
|
||||
function* genNavigationEvent () {
|
||||
let eventOptions = null
|
||||
while ((eventOptions = events.shift()) && events.length) {
|
||||
eventOptions.responseEvent = responseEvent
|
||||
ipcRenderer.send('test-webcontents-navigation-observer', eventOptions)
|
||||
yield 1
|
||||
}
|
||||
}
|
||||
|
||||
let gen = genNavigationEvent()
|
||||
ipcRenderer.on(responseEvent, () => {
|
||||
if (!gen.next().value) done()
|
||||
})
|
||||
gen.next()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const assert = require('assert')
|
||||
const fs = require('fs')
|
||||
const http = require('http')
|
||||
const path = require('path')
|
||||
const ws = require('ws')
|
||||
|
@ -618,6 +619,39 @@ describe('chromium feature', function () {
|
|||
})
|
||||
document.body.appendChild(webview)
|
||||
})
|
||||
|
||||
describe('targetOrigin argument', function () {
|
||||
let serverURL
|
||||
let server
|
||||
|
||||
beforeEach(function (done) {
|
||||
server = http.createServer(function (req, res) {
|
||||
res.writeHead(200)
|
||||
const filePath = path.join(fixtures, 'pages', 'window-opener-targetOrigin.html')
|
||||
res.end(fs.readFileSync(filePath, 'utf8'))
|
||||
})
|
||||
server.listen(0, '127.0.0.1', function () {
|
||||
serverURL = `http://127.0.0.1:${server.address().port}`
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
server.close()
|
||||
})
|
||||
|
||||
it('delivers messages that match the origin', function (done) {
|
||||
let b
|
||||
listener = function (event) {
|
||||
window.removeEventListener('message', listener)
|
||||
b.close()
|
||||
assert.equal(event.data, 'deliver')
|
||||
done()
|
||||
}
|
||||
window.addEventListener('message', listener)
|
||||
b = window.open(serverURL, '', 'show=no')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('creating a Uint8Array under browser side', function () {
|
||||
|
|
11
spec/fixtures/api/allocate-memory.html
vendored
Normal file
11
spec/fixtures/api/allocate-memory.html
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.bigBuffer = new Uint8Array(1024 * 1024 * 64)
|
||||
window.bigBuffer.fill(5, 50, 1024 * 1024)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
17
spec/fixtures/api/beforeunload-false-prevent3.html
vendored
Normal file
17
spec/fixtures/api/beforeunload-false-prevent3.html
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
// Only prevent unload on the first three window closes
|
||||
var unloadPreventedCount = 0;
|
||||
window.onbeforeunload = function() {
|
||||
setTimeout(function() {
|
||||
require('electron').remote.getCurrentWindow().emit('onbeforeunload');
|
||||
}, 0);
|
||||
if (unloadPreventedCount < 3) {
|
||||
unloadPreventedCount++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
27
spec/fixtures/api/sandbox.html
vendored
27
spec/fixtures/api/sandbox.html
vendored
|
@ -1,5 +1,18 @@
|
|||
<html>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
function timeout(ms) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms)
|
||||
})
|
||||
}
|
||||
async function invokeGc () {
|
||||
// it seems calling window.gc once does not guarantee garbage will be
|
||||
// collected, so we repeat 10 times with interval of 100 ms
|
||||
for (let i = 0; i < 10; i++) {
|
||||
window.gc()
|
||||
await timeout(100)
|
||||
}
|
||||
}
|
||||
if (window.opener) {
|
||||
window.callback = () => {
|
||||
opener.require('electron').ipcRenderer.send('answer', document.body.innerHTML)
|
||||
|
@ -7,6 +20,20 @@
|
|||
} else {
|
||||
const {ipcRenderer} = require('electron')
|
||||
const tests = {
|
||||
'allocate-memory': async () => {
|
||||
await invokeGc()
|
||||
const {privateBytes: bytesBeforeOpen} = process.getProcessMemoryInfo()
|
||||
let w = open('./allocate-memory.html')
|
||||
await invokeGc()
|
||||
const {privateBytes: bytesAfterOpen} = process.getProcessMemoryInfo()
|
||||
w.close()
|
||||
w = null
|
||||
await invokeGc()
|
||||
const {privateBytes: bytesAfterClose} = process.getProcessMemoryInfo()
|
||||
ipcRenderer.send('answer', {
|
||||
bytesBeforeOpen, bytesAfterOpen, bytesAfterClose
|
||||
})
|
||||
},
|
||||
'window-events': () => {
|
||||
document.title = 'changed'
|
||||
},
|
||||
|
|
24
spec/fixtures/pages/window-opener-targetOrigin.html
vendored
Normal file
24
spec/fixtures/pages/window-opener-targetOrigin.html
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
<html>
|
||||
<body>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
const url = require('url')
|
||||
if (url.parse(window.location.href, true).query.opened != null) {
|
||||
// Ensure origins are properly checked by removing a single character from the end
|
||||
window.opener.postMessage('do not deliver substring origin', window.location.origin.substring(0, window.location.origin.length - 1))
|
||||
window.opener.postMessage('do not deliver file://', 'file://')
|
||||
window.opener.postMessage('do not deliver http without port', 'http://127.0.0.1')
|
||||
window.opener.postMessage('do not deliver atom', 'atom://')
|
||||
window.opener.postMessage('do not deliver null', 'null')
|
||||
window.opener.postMessage('do not deliver \\:/', '\\:/')
|
||||
window.opener.postMessage('do not deliver empty', '')
|
||||
window.opener.postMessage('deliver', window.location.origin)
|
||||
} else {
|
||||
const opened = window.open(`${window.location.href}?opened=true`, '', 'show=no')
|
||||
window.addEventListener('message', function (event) {
|
||||
window.opener.postMessage(event.data, '*')
|
||||
opened.close()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -338,6 +338,27 @@ ipcMain.on('crash-service-pid', (event, pid) => {
|
|||
event.returnValue = null
|
||||
})
|
||||
|
||||
ipcMain.on('test-webcontents-navigation-observer', (event, options) => {
|
||||
let contents = null
|
||||
let destroy = () => {}
|
||||
if (options.id) {
|
||||
const w = BrowserWindow.fromId(options.id)
|
||||
contents = w.webContents
|
||||
destroy = () => w.close()
|
||||
} else {
|
||||
contents = webContents.create()
|
||||
destroy = () => contents.destroy()
|
||||
}
|
||||
|
||||
contents.once(options.name, () => destroy())
|
||||
|
||||
contents.once('destroyed', () => {
|
||||
event.sender.send(options.responseEvent)
|
||||
})
|
||||
|
||||
contents.loadURL(options.url)
|
||||
})
|
||||
|
||||
// Suspend listeners until the next event and then restore them
|
||||
const suspendListeners = (emitter, eventName, callback) => {
|
||||
const listeners = emitter.listeners(eventName)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue