chromium-spec.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
  1. const assert = require('assert')
  2. const fs = require('fs')
  3. const http = require('http')
  4. const path = require('path')
  5. const ws = require('ws')
  6. const url = require('url')
  7. const {ipcRenderer, remote, webFrame} = require('electron')
  8. const {closeWindow} = require('./window-helpers')
  9. const {app, BrowserWindow, ipcMain, protocol, session, webContents} = remote
  10. const isCI = remote.getGlobal('isCi')
  11. describe('chromium feature', function () {
  12. var fixtures = path.resolve(__dirname, 'fixtures')
  13. var listener = null
  14. let w = null
  15. afterEach(function () {
  16. if (listener != null) {
  17. window.removeEventListener('message', listener)
  18. }
  19. listener = null
  20. })
  21. afterEach(function () {
  22. return closeWindow(w).then(function () { w = null })
  23. })
  24. describe('heap snapshot', function () {
  25. it('does not crash', function () {
  26. if (process.env.TRAVIS === 'true') return
  27. process.atomBinding('v8_util').takeHeapSnapshot()
  28. })
  29. })
  30. describe('sending request of http protocol urls', function () {
  31. it('does not crash', function (done) {
  32. var server = http.createServer(function (req, res) {
  33. res.end()
  34. server.close()
  35. done()
  36. })
  37. server.listen(0, '127.0.0.1', function () {
  38. var port = server.address().port
  39. $.get('http://127.0.0.1:' + port)
  40. })
  41. })
  42. })
  43. describe('document.hidden', function () {
  44. var url = 'file://' + fixtures + '/pages/document-hidden.html'
  45. it('is set correctly when window is not shown', function (done) {
  46. w = new BrowserWindow({
  47. show: false
  48. })
  49. w.webContents.once('ipc-message', function (event, args) {
  50. assert.deepEqual(args, ['hidden', true])
  51. done()
  52. })
  53. w.loadURL(url)
  54. })
  55. it('is set correctly when window is inactive', function (done) {
  56. if (isCI && process.platform === 'win32') return done()
  57. w = new BrowserWindow({
  58. show: false
  59. })
  60. w.webContents.once('ipc-message', function (event, args) {
  61. assert.deepEqual(args, ['hidden', false])
  62. done()
  63. })
  64. w.showInactive()
  65. w.loadURL(url)
  66. })
  67. })
  68. xdescribe('navigator.webkitGetUserMedia', function () {
  69. it('calls its callbacks', function (done) {
  70. navigator.webkitGetUserMedia({
  71. audio: true,
  72. video: false
  73. }, function () {
  74. done()
  75. }, function () {
  76. done()
  77. })
  78. })
  79. })
  80. describe('navigator.mediaDevices', function () {
  81. if (isCI) {
  82. return
  83. }
  84. it('can return labels of enumerated devices', function (done) {
  85. navigator.mediaDevices.enumerateDevices().then((devices) => {
  86. const labels = devices.map((device) => device.label)
  87. const labelFound = labels.some((label) => !!label)
  88. if (labelFound) {
  89. done()
  90. } else {
  91. done(new Error(`No device labels found: ${JSON.stringify(labels)}`))
  92. }
  93. }).catch(done)
  94. })
  95. it('can return new device id when cookie storage is cleared', function (done) {
  96. const options = {
  97. origin: null,
  98. storages: ['cookies']
  99. }
  100. const deviceIds = []
  101. const ses = session.fromPartition('persist:media-device-id')
  102. w = new BrowserWindow({
  103. show: false,
  104. webPreferences: {
  105. session: ses
  106. }
  107. })
  108. w.webContents.on('ipc-message', function (event, args) {
  109. if (args[0] === 'deviceIds') {
  110. deviceIds.push(args[1])
  111. }
  112. if (deviceIds.length === 2) {
  113. assert.notDeepEqual(deviceIds[0], deviceIds[1])
  114. closeWindow(w).then(function () {
  115. w = null
  116. done()
  117. }).catch(function (error) {
  118. done(error)
  119. })
  120. } else {
  121. ses.clearStorageData(options, function () {
  122. w.webContents.reload()
  123. })
  124. }
  125. })
  126. w.loadURL('file://' + fixtures + '/pages/media-id-reset.html')
  127. })
  128. })
  129. describe('navigator.language', function () {
  130. it('should not be empty', function () {
  131. assert.notEqual(navigator.language, '')
  132. })
  133. })
  134. describe('navigator.serviceWorker', function () {
  135. var url = 'file://' + fixtures + '/pages/service-worker/index.html'
  136. it('should register for file scheme', function (done) {
  137. w = new BrowserWindow({
  138. show: false
  139. })
  140. w.webContents.on('ipc-message', function (event, args) {
  141. if (args[0] === 'reload') {
  142. w.webContents.reload()
  143. } else if (args[0] === 'error') {
  144. done('unexpected error : ' + args[1])
  145. } else if (args[0] === 'response') {
  146. assert.equal(args[1], 'Hello from serviceWorker!')
  147. session.defaultSession.clearStorageData({
  148. storages: ['serviceworkers']
  149. }, function () {
  150. done()
  151. })
  152. }
  153. })
  154. w.loadURL(url)
  155. })
  156. })
  157. describe('window.open', function () {
  158. if (process.env.TRAVIS === 'true' && process.platform === 'darwin') {
  159. return
  160. }
  161. it('returns a BrowserWindowProxy object', function () {
  162. var b = window.open('about:blank', '', 'show=no')
  163. assert.equal(b.closed, false)
  164. assert.equal(b.constructor.name, 'BrowserWindowProxy')
  165. b.close()
  166. })
  167. it('accepts "nodeIntegration" as feature', function (done) {
  168. var b
  169. listener = function (event) {
  170. assert.equal(event.data.isProcessGlobalUndefined, true)
  171. b.close()
  172. done()
  173. }
  174. window.addEventListener('message', listener)
  175. b = window.open('file://' + fixtures + '/pages/window-opener-node.html', '', 'nodeIntegration=no,show=no')
  176. })
  177. it('inherit options of parent window', function (done) {
  178. var b
  179. listener = function (event) {
  180. var ref1 = remote.getCurrentWindow().getSize()
  181. var width = ref1[0]
  182. var height = ref1[1]
  183. assert.equal(event.data, 'size: ' + width + ' ' + height)
  184. b.close()
  185. done()
  186. }
  187. window.addEventListener('message', listener)
  188. b = window.open('file://' + fixtures + '/pages/window-open-size.html', '', 'show=no')
  189. })
  190. it('disables node integration when it is disabled on the parent window', function (done) {
  191. var b
  192. listener = function (event) {
  193. assert.equal(event.data.isProcessGlobalUndefined, true)
  194. b.close()
  195. done()
  196. }
  197. window.addEventListener('message', listener)
  198. var windowUrl = require('url').format({
  199. pathname: `${fixtures}/pages/window-opener-no-node-integration.html`,
  200. protocol: 'file',
  201. query: {
  202. p: `${fixtures}/pages/window-opener-node.html`
  203. },
  204. slashes: true
  205. })
  206. b = window.open(windowUrl, '', 'nodeIntegration=no,show=no')
  207. })
  208. it('disables node integration when it is disabled on the parent window for chrome devtools URLs', function (done) {
  209. var b
  210. app.once('web-contents-created', (event, contents) => {
  211. contents.once('did-finish-load', () => {
  212. contents.executeJavaScript('typeof process').then((typeofProcessGlobal) => {
  213. assert.equal(typeofProcessGlobal, 'undefined')
  214. b.close()
  215. done()
  216. }).catch(done)
  217. })
  218. })
  219. b = window.open('chrome-devtools://devtools/bundled/inspector.html', '', 'nodeIntegration=no,show=no')
  220. })
  221. it('disables JavaScript when it is disabled on the parent window', function (done) {
  222. var b
  223. app.once('web-contents-created', (event, contents) => {
  224. contents.once('did-finish-load', () => {
  225. app.once('browser-window-created', (event, window) => {
  226. const preferences = window.webContents.getWebPreferences()
  227. assert.equal(preferences.javascript, false)
  228. window.destroy()
  229. b.close()
  230. done()
  231. })
  232. // Click link on page
  233. contents.sendInputEvent({type: 'mouseDown', clickCount: 1, x: 1, y: 1})
  234. contents.sendInputEvent({type: 'mouseUp', clickCount: 1, x: 1, y: 1})
  235. })
  236. })
  237. var windowUrl = require('url').format({
  238. pathname: `${fixtures}/pages/window-no-javascript.html`,
  239. protocol: 'file',
  240. slashes: true
  241. })
  242. b = window.open(windowUrl, '', 'javascript=no,show=no')
  243. })
  244. it('does not override child options', function (done) {
  245. var b, size
  246. size = {
  247. width: 350,
  248. height: 450
  249. }
  250. listener = function (event) {
  251. assert.equal(event.data, 'size: ' + size.width + ' ' + size.height)
  252. b.close()
  253. done()
  254. }
  255. window.addEventListener('message', listener)
  256. b = window.open('file://' + fixtures + '/pages/window-open-size.html', '', 'show=no,width=' + size.width + ',height=' + size.height)
  257. })
  258. it('handles cycles when merging the parent options into the child options', (done) => {
  259. w = BrowserWindow.fromId(ipcRenderer.sendSync('create-window-with-options-cycle'))
  260. w.loadURL('file://' + fixtures + '/pages/window-open.html')
  261. w.webContents.once('new-window', (event, url, frameName, disposition, options) => {
  262. assert.equal(options.show, false)
  263. assert.deepEqual(options.foo, {
  264. bar: null,
  265. baz: {
  266. hello: {
  267. world: true
  268. }
  269. },
  270. baz2: {
  271. hello: {
  272. world: true
  273. }
  274. }
  275. })
  276. done()
  277. })
  278. })
  279. it('defines a window.location getter', function (done) {
  280. var b, targetURL
  281. if (process.platform === 'win32') {
  282. targetURL = 'file:///' + fixtures.replace(/\\/g, '/') + '/pages/base-page.html'
  283. } else {
  284. targetURL = 'file://' + fixtures + '/pages/base-page.html'
  285. }
  286. app.once('browser-window-created', (event, window) => {
  287. window.webContents.once('did-finish-load', () => {
  288. assert.equal(b.location, targetURL)
  289. b.close()
  290. done()
  291. })
  292. })
  293. b = window.open(targetURL)
  294. })
  295. it('defines a window.location setter', function (done) {
  296. let b
  297. app.once('browser-window-created', (event, {webContents}) => {
  298. webContents.once('did-finish-load', function () {
  299. // When it loads, redirect
  300. b.location = 'file://' + fixtures + '/pages/base-page.html'
  301. webContents.once('did-finish-load', function () {
  302. // After our second redirect, cleanup and callback
  303. b.close()
  304. done()
  305. })
  306. })
  307. })
  308. // Load a page that definitely won't redirect
  309. b = window.open('about:blank')
  310. })
  311. it('open a blank page when no URL is specified', function (done) {
  312. let b
  313. app.once('browser-window-created', (event, {webContents}) => {
  314. webContents.once('did-finish-load', function () {
  315. const {location} = b
  316. b.close()
  317. assert.equal(location, 'about:blank')
  318. let c
  319. app.once('browser-window-created', (event, {webContents}) => {
  320. webContents.once('did-finish-load', function () {
  321. const {location} = c
  322. c.close()
  323. assert.equal(location, 'about:blank')
  324. done()
  325. })
  326. })
  327. c = window.open('')
  328. })
  329. })
  330. b = window.open()
  331. })
  332. it('throws an exception when the arguments cannot be converted to strings', function () {
  333. assert.throws(function () {
  334. window.open('', {toString: null})
  335. }, /Cannot convert object to primitive value/)
  336. assert.throws(function () {
  337. window.open('', '', {toString: 3})
  338. }, /Cannot convert object to primitive value/)
  339. })
  340. it('sets the window title to the specified frameName', function (done) {
  341. let b
  342. app.once('browser-window-created', (event, createdWindow) => {
  343. assert.equal(createdWindow.getTitle(), 'hello')
  344. b.close()
  345. done()
  346. })
  347. b = window.open('', 'hello')
  348. })
  349. it('does not throw an exception when the frameName is a built-in object property', function (done) {
  350. let b
  351. app.once('browser-window-created', (event, createdWindow) => {
  352. assert.equal(createdWindow.getTitle(), '__proto__')
  353. b.close()
  354. done()
  355. })
  356. b = window.open('', '__proto__')
  357. })
  358. it('does not throw an exception when the features include webPreferences', function () {
  359. let b
  360. assert.doesNotThrow(function () {
  361. b = window.open('', '', 'webPreferences=')
  362. })
  363. b.close()
  364. })
  365. })
  366. describe('window.opener', function () {
  367. let url = 'file://' + fixtures + '/pages/window-opener.html'
  368. it('is null for main window', function (done) {
  369. w = new BrowserWindow({
  370. show: false
  371. })
  372. w.webContents.once('ipc-message', function (event, args) {
  373. assert.deepEqual(args, ['opener', null])
  374. done()
  375. })
  376. w.loadURL(url)
  377. })
  378. it('is not null for window opened by window.open', function (done) {
  379. let b
  380. listener = function (event) {
  381. assert.equal(event.data, 'object')
  382. b.close()
  383. done()
  384. }
  385. window.addEventListener('message', listener)
  386. b = window.open(url, '', 'show=no')
  387. })
  388. })
  389. describe('window.opener access from BrowserWindow', function () {
  390. const scheme = 'other'
  391. let url = `${scheme}://${fixtures}/pages/window-opener-location.html`
  392. let w = null
  393. before(function (done) {
  394. protocol.registerFileProtocol(scheme, function (request, callback) {
  395. callback(`${fixtures}/pages/window-opener-location.html`)
  396. }, function (error) {
  397. done(error)
  398. })
  399. })
  400. after(function () {
  401. protocol.unregisterProtocol(scheme)
  402. })
  403. afterEach(function () {
  404. w.close()
  405. })
  406. it('does nothing when origin of current window does not match opener', function (done) {
  407. listener = function (event) {
  408. assert.equal(event.data, undefined)
  409. done()
  410. }
  411. window.addEventListener('message', listener)
  412. w = window.open(url, '', 'show=no')
  413. })
  414. it('works when origin matches', function (done) {
  415. listener = function (event) {
  416. assert.equal(event.data, location.href)
  417. done()
  418. }
  419. window.addEventListener('message', listener)
  420. w = window.open(`file://${fixtures}/pages/window-opener-location.html`, '', 'show=no')
  421. })
  422. it('works when origin does not match opener but has node integration', function (done) {
  423. listener = function (event) {
  424. assert.equal(event.data, location.href)
  425. done()
  426. }
  427. window.addEventListener('message', listener)
  428. w = window.open(url, '', 'show=no,nodeIntegration=yes')
  429. })
  430. })
  431. describe('window.opener access from <webview>', function () {
  432. const scheme = 'other'
  433. const srcPath = `${fixtures}/pages/webview-opener-postMessage.html`
  434. const pageURL = `file://${fixtures}/pages/window-opener-location.html`
  435. let webview = null
  436. before(function (done) {
  437. protocol.registerFileProtocol(scheme, function (request, callback) {
  438. callback(srcPath)
  439. }, function (error) {
  440. done(error)
  441. })
  442. })
  443. after(function () {
  444. protocol.unregisterProtocol(scheme)
  445. })
  446. afterEach(function () {
  447. if (webview != null) webview.remove()
  448. })
  449. it('does nothing when origin of webview src URL does not match opener', function (done) {
  450. webview = new WebView()
  451. webview.addEventListener('console-message', function (e) {
  452. assert.equal(e.message, 'null')
  453. done()
  454. })
  455. webview.setAttribute('allowpopups', 'on')
  456. webview.src = url.format({
  457. pathname: srcPath,
  458. protocol: scheme,
  459. query: {
  460. p: pageURL
  461. },
  462. slashes: true
  463. })
  464. document.body.appendChild(webview)
  465. })
  466. it('works when origin matches', function (done) {
  467. webview = new WebView()
  468. webview.addEventListener('console-message', function (e) {
  469. assert.equal(e.message, webview.src)
  470. done()
  471. })
  472. webview.setAttribute('allowpopups', 'on')
  473. webview.src = url.format({
  474. pathname: srcPath,
  475. protocol: 'file',
  476. query: {
  477. p: pageURL
  478. },
  479. slashes: true
  480. })
  481. document.body.appendChild(webview)
  482. })
  483. it('works when origin does not match opener but has node integration', function (done) {
  484. webview = new WebView()
  485. webview.addEventListener('console-message', function (e) {
  486. webview.remove()
  487. assert.equal(e.message, webview.src)
  488. done()
  489. })
  490. webview.setAttribute('allowpopups', 'on')
  491. webview.setAttribute('nodeintegration', 'on')
  492. webview.src = url.format({
  493. pathname: srcPath,
  494. protocol: scheme,
  495. query: {
  496. p: pageURL
  497. },
  498. slashes: true
  499. })
  500. document.body.appendChild(webview)
  501. })
  502. })
  503. describe('window.postMessage', function () {
  504. it('sets the source and origin correctly', function (done) {
  505. var b
  506. listener = function (event) {
  507. window.removeEventListener('message', listener)
  508. b.close()
  509. var message = JSON.parse(event.data)
  510. assert.equal(message.data, 'testing')
  511. assert.equal(message.origin, 'file://')
  512. assert.equal(message.sourceEqualsOpener, true)
  513. assert.equal(event.origin, 'file://')
  514. done()
  515. }
  516. window.addEventListener('message', listener)
  517. app.once('browser-window-created', (event, {webContents}) => {
  518. webContents.once('did-finish-load', function () {
  519. b.postMessage('testing', '*')
  520. })
  521. })
  522. b = window.open('file://' + fixtures + '/pages/window-open-postMessage.html', '', 'show=no')
  523. })
  524. it('throws an exception when the targetOrigin cannot be converted to a string', function () {
  525. var b = window.open('')
  526. assert.throws(function () {
  527. b.postMessage('test', {toString: null})
  528. }, /Cannot convert object to primitive value/)
  529. b.close()
  530. })
  531. })
  532. describe('window.opener.postMessage', function () {
  533. it('sets source and origin correctly', function (done) {
  534. var b
  535. listener = function (event) {
  536. window.removeEventListener('message', listener)
  537. b.close()
  538. assert.equal(event.source, b)
  539. assert.equal(event.origin, 'file://')
  540. done()
  541. }
  542. window.addEventListener('message', listener)
  543. b = window.open('file://' + fixtures + '/pages/window-opener-postMessage.html', '', 'show=no')
  544. })
  545. it('supports windows opened from a <webview>', function (done) {
  546. const webview = new WebView()
  547. webview.addEventListener('console-message', function (e) {
  548. webview.remove()
  549. assert.equal(e.message, 'message')
  550. done()
  551. })
  552. webview.allowpopups = true
  553. webview.src = url.format({
  554. pathname: `${fixtures}/pages/webview-opener-postMessage.html`,
  555. protocol: 'file',
  556. query: {
  557. p: `${fixtures}/pages/window-opener-postMessage.html`
  558. },
  559. slashes: true
  560. })
  561. document.body.appendChild(webview)
  562. })
  563. describe('targetOrigin argument', function () {
  564. let serverURL
  565. let server
  566. beforeEach(function (done) {
  567. server = http.createServer(function (req, res) {
  568. res.writeHead(200)
  569. const filePath = path.join(fixtures, 'pages', 'window-opener-targetOrigin.html')
  570. res.end(fs.readFileSync(filePath, 'utf8'))
  571. })
  572. server.listen(0, '127.0.0.1', function () {
  573. serverURL = `http://127.0.0.1:${server.address().port}`
  574. done()
  575. })
  576. })
  577. afterEach(function () {
  578. server.close()
  579. })
  580. it('delivers messages that match the origin', function (done) {
  581. let b
  582. listener = function (event) {
  583. window.removeEventListener('message', listener)
  584. b.close()
  585. assert.equal(event.data, 'deliver')
  586. done()
  587. }
  588. window.addEventListener('message', listener)
  589. b = window.open(serverURL, '', 'show=no')
  590. })
  591. })
  592. })
  593. describe('creating a Uint8Array under browser side', function () {
  594. it('does not crash', function () {
  595. var RUint8Array = remote.getGlobal('Uint8Array')
  596. var arr = new RUint8Array()
  597. assert(arr)
  598. })
  599. })
  600. describe('webgl', function () {
  601. if (isCI && process.platform === 'win32') {
  602. return
  603. }
  604. it('can be get as context in canvas', function () {
  605. if (process.platform === 'linux') return
  606. var webgl = document.createElement('canvas').getContext('webgl')
  607. assert.notEqual(webgl, null)
  608. })
  609. })
  610. describe('web workers', function () {
  611. it('Worker can work', function (done) {
  612. var worker = new Worker('../fixtures/workers/worker.js')
  613. var message = 'ping'
  614. worker.onmessage = function (event) {
  615. assert.equal(event.data, message)
  616. worker.terminate()
  617. done()
  618. }
  619. worker.postMessage(message)
  620. })
  621. it('Worker has no node integration by default', function (done) {
  622. let worker = new Worker('../fixtures/workers/worker_node.js')
  623. worker.onmessage = function (event) {
  624. assert.equal(event.data, 'undefined undefined undefined undefined')
  625. worker.terminate()
  626. done()
  627. }
  628. })
  629. it('Worker has node integration with nodeIntegrationInWorker', function (done) {
  630. let webview = new WebView()
  631. webview.addEventListener('ipc-message', function (e) {
  632. assert.equal(e.channel, 'object function object function')
  633. webview.remove()
  634. done()
  635. })
  636. webview.src = 'file://' + fixtures + '/pages/worker.html'
  637. webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker')
  638. document.body.appendChild(webview)
  639. })
  640. it('SharedWorker can work', function (done) {
  641. var worker = new SharedWorker('../fixtures/workers/shared_worker.js')
  642. var message = 'ping'
  643. worker.port.onmessage = function (event) {
  644. assert.equal(event.data, message)
  645. done()
  646. }
  647. worker.port.postMessage(message)
  648. })
  649. it('SharedWorker has no node integration by default', function (done) {
  650. let worker = new SharedWorker('../fixtures/workers/shared_worker_node.js')
  651. worker.port.onmessage = function (event) {
  652. assert.equal(event.data, 'undefined undefined undefined undefined')
  653. done()
  654. }
  655. })
  656. it('SharedWorker has node integration with nodeIntegrationInWorker', function (done) {
  657. let webview = new WebView()
  658. webview.addEventListener('console-message', function (e) {
  659. console.log(e)
  660. })
  661. webview.addEventListener('ipc-message', function (e) {
  662. assert.equal(e.channel, 'object function object function')
  663. webview.remove()
  664. done()
  665. })
  666. webview.src = 'file://' + fixtures + '/pages/shared_worker.html'
  667. webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker')
  668. document.body.appendChild(webview)
  669. })
  670. })
  671. describe('iframe', function () {
  672. var iframe = null
  673. beforeEach(function () {
  674. iframe = document.createElement('iframe')
  675. })
  676. afterEach(function () {
  677. document.body.removeChild(iframe)
  678. })
  679. it('does not have node integration', function (done) {
  680. iframe.src = 'file://' + fixtures + '/pages/set-global.html'
  681. document.body.appendChild(iframe)
  682. iframe.onload = function () {
  683. assert.equal(iframe.contentWindow.test, 'undefined undefined undefined')
  684. done()
  685. }
  686. })
  687. })
  688. describe('storage', function () {
  689. it('requesting persitent quota works', function (done) {
  690. navigator.webkitPersistentStorage.requestQuota(1024 * 1024, function (grantedBytes) {
  691. assert.equal(grantedBytes, 1048576)
  692. done()
  693. })
  694. })
  695. describe('custom non standard schemes', function () {
  696. const protocolName = 'storage'
  697. let contents = null
  698. before(function (done) {
  699. const handler = function (request, callback) {
  700. let parsedUrl = url.parse(request.url)
  701. let filename
  702. switch (parsedUrl.pathname) {
  703. case '/localStorage' : filename = 'local_storage.html'; break
  704. case '/sessionStorage' : filename = 'session_storage.html'; break
  705. case '/WebSQL' : filename = 'web_sql.html'; break
  706. case '/indexedDB' : filename = 'indexed_db.html'; break
  707. case '/cookie' : filename = 'cookie.html'; break
  708. default : filename = ''
  709. }
  710. callback({path: fixtures + '/pages/storage/' + filename})
  711. }
  712. protocol.registerFileProtocol(protocolName, handler, function (error) {
  713. done(error)
  714. })
  715. })
  716. after(function (done) {
  717. protocol.unregisterProtocol(protocolName, () => done())
  718. })
  719. beforeEach(function () {
  720. contents = webContents.create({})
  721. })
  722. afterEach(function () {
  723. contents.destroy()
  724. contents = null
  725. })
  726. it('cannot access localStorage', function (done) {
  727. ipcMain.once('local-storage-response', function (event, error) {
  728. assert.equal(
  729. error,
  730. 'Failed to read the \'localStorage\' property from \'Window\': Access is denied for this document.')
  731. done()
  732. })
  733. contents.loadURL(protocolName + '://host/localStorage')
  734. })
  735. it('cannot access sessionStorage', function (done) {
  736. ipcMain.once('session-storage-response', function (event, error) {
  737. assert.equal(
  738. error,
  739. 'Failed to read the \'sessionStorage\' property from \'Window\': Access is denied for this document.')
  740. done()
  741. })
  742. contents.loadURL(protocolName + '://host/sessionStorage')
  743. })
  744. it('cannot access WebSQL database', function (done) {
  745. ipcMain.once('web-sql-response', function (event, error) {
  746. assert.equal(
  747. error,
  748. 'An attempt was made to break through the security policy of the user agent.')
  749. done()
  750. })
  751. contents.loadURL(protocolName + '://host/WebSQL')
  752. })
  753. it('cannot access indexedDB', function (done) {
  754. ipcMain.once('indexed-db-response', function (event, error) {
  755. assert.equal(error, 'The user denied permission to access the database.')
  756. done()
  757. })
  758. contents.loadURL(protocolName + '://host/indexedDB')
  759. })
  760. it('cannot access cookie', function (done) {
  761. ipcMain.once('cookie-response', function (event, cookie) {
  762. assert(!cookie)
  763. done()
  764. })
  765. contents.loadURL(protocolName + '://host/cookie')
  766. })
  767. })
  768. })
  769. describe('websockets', function () {
  770. var wss = null
  771. var server = null
  772. var WebSocketServer = ws.Server
  773. afterEach(function () {
  774. wss.close()
  775. server.close()
  776. })
  777. it('has user agent', function (done) {
  778. server = http.createServer()
  779. server.listen(0, '127.0.0.1', function () {
  780. var port = server.address().port
  781. wss = new WebSocketServer({
  782. server: server
  783. })
  784. wss.on('error', done)
  785. wss.on('connection', function (ws) {
  786. if (ws.upgradeReq.headers['user-agent']) {
  787. done()
  788. } else {
  789. done('user agent is empty')
  790. }
  791. })
  792. var socket = new WebSocket(`ws://127.0.0.1:${port}`)
  793. assert(socket)
  794. })
  795. })
  796. })
  797. describe('Promise', function () {
  798. it('resolves correctly in Node.js calls', function (done) {
  799. document.registerElement('x-element', {
  800. prototype: Object.create(HTMLElement.prototype, {
  801. createdCallback: {
  802. value: function () {}
  803. }
  804. })
  805. })
  806. setImmediate(function () {
  807. var called = false
  808. Promise.resolve().then(function () {
  809. done(called ? void 0 : new Error('wrong sequence'))
  810. })
  811. document.createElement('x-element')
  812. called = true
  813. })
  814. })
  815. it('resolves correctly in Electron calls', function (done) {
  816. document.registerElement('y-element', {
  817. prototype: Object.create(HTMLElement.prototype, {
  818. createdCallback: {
  819. value: function () {}
  820. }
  821. })
  822. })
  823. remote.getGlobal('setImmediate')(function () {
  824. var called = false
  825. Promise.resolve().then(function () {
  826. done(called ? void 0 : new Error('wrong sequence'))
  827. })
  828. document.createElement('y-element')
  829. called = true
  830. })
  831. })
  832. })
  833. describe('fetch', function () {
  834. it('does not crash', function (done) {
  835. const server = http.createServer(function (req, res) {
  836. res.end('test')
  837. server.close()
  838. })
  839. server.listen(0, '127.0.0.1', function () {
  840. const port = server.address().port
  841. fetch(`http://127.0.0.1:${port}`).then((res) => {
  842. return res.body.getReader()
  843. }).then((reader) => {
  844. reader.read().then((r) => {
  845. reader.cancel()
  846. done()
  847. })
  848. }).catch(function (e) {
  849. done(e)
  850. })
  851. })
  852. })
  853. })
  854. describe('PDF Viewer', function () {
  855. const pdfSource = url.format({
  856. pathname: path.join(fixtures, 'assets', 'cat.pdf').replace(/\\/g, '/'),
  857. protocol: 'file',
  858. slashes: true
  859. })
  860. function createBrowserWindow ({plugins}) {
  861. w = new BrowserWindow({
  862. show: false,
  863. webPreferences: {
  864. preload: path.join(fixtures, 'module', 'preload-inject-ipc.js'),
  865. plugins: plugins
  866. }
  867. })
  868. }
  869. it('opens when loading a pdf resource as top level navigation', function (done) {
  870. createBrowserWindow({plugins: true})
  871. ipcMain.once('pdf-loaded', function (event, success) {
  872. if (success) done()
  873. })
  874. w.webContents.on('page-title-updated', function () {
  875. const source = `
  876. if (window.viewer) {
  877. window.viewer.setLoadCallback(function(success) {
  878. window.ipcRenderer.send('pdf-loaded', success);
  879. });
  880. }
  881. `
  882. const parsedURL = url.parse(w.webContents.getURL(), true)
  883. assert.equal(parsedURL.protocol, 'chrome:')
  884. assert.equal(parsedURL.hostname, 'pdf-viewer')
  885. assert.equal(parsedURL.query.src, pdfSource)
  886. assert.equal(w.webContents.getTitle(), 'cat.pdf')
  887. w.webContents.executeJavaScript(source)
  888. })
  889. w.webContents.loadURL(pdfSource)
  890. })
  891. it('should download a pdf when plugins are disabled', function (done) {
  892. createBrowserWindow({plugins: false})
  893. ipcRenderer.sendSync('set-download-option', false, false)
  894. ipcRenderer.once('download-done', function (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) {
  895. assert.equal(state, 'completed')
  896. assert.equal(filename, 'cat.pdf')
  897. assert.equal(mimeType, 'application/pdf')
  898. fs.unlinkSync(path.join(fixtures, 'mock.pdf'))
  899. done()
  900. })
  901. w.webContents.loadURL(pdfSource)
  902. })
  903. it('should not open when pdf is requested as sub resource', function (done) {
  904. createBrowserWindow({plugins: true})
  905. webFrame.registerURLSchemeAsPrivileged('file', {
  906. secure: false,
  907. bypassCSP: false,
  908. allowServiceWorkers: false,
  909. corsEnabled: false
  910. })
  911. fetch(pdfSource).then(function (res) {
  912. assert.equal(res.status, 200)
  913. assert.notEqual(document.title, 'cat.pdf')
  914. done()
  915. }).catch(function (e) {
  916. done(e)
  917. })
  918. })
  919. })
  920. describe('window.alert(message, title)', function () {
  921. it('throws an exception when the arguments cannot be converted to strings', function () {
  922. assert.throws(function () {
  923. window.alert({toString: null})
  924. }, /Cannot convert object to primitive value/)
  925. assert.throws(function () {
  926. window.alert('message', {toString: 3})
  927. }, /Cannot convert object to primitive value/)
  928. })
  929. })
  930. describe('window.confirm(message, title)', function () {
  931. it('throws an exception when the arguments cannot be converted to strings', function () {
  932. assert.throws(function () {
  933. window.confirm({toString: null}, 'title')
  934. }, /Cannot convert object to primitive value/)
  935. assert.throws(function () {
  936. window.confirm('message', {toString: 3})
  937. }, /Cannot convert object to primitive value/)
  938. })
  939. })
  940. describe('window.history.go(offset)', function () {
  941. it('throws an exception when the argumnet cannot be converted to a string', function () {
  942. assert.throws(function () {
  943. window.history.go({toString: null})
  944. }, /Cannot convert object to primitive value/)
  945. })
  946. })
  947. })