guest-window-manager-spec.ts 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import { BrowserWindow } from 'electron';
  2. import { expect, assert } from 'chai';
  3. import { closeAllWindows } from './window-helpers';
  4. const { emittedOnce } = require('./events-helpers');
  5. describe('webContents.setWindowOpenHandler', () => {
  6. let browserWindow: BrowserWindow;
  7. beforeEach(async () => {
  8. browserWindow = new BrowserWindow({ show: false });
  9. await browserWindow.loadURL('about:blank');
  10. });
  11. afterEach(closeAllWindows);
  12. it('does not fire window creation events if the handler callback throws an error', (done) => {
  13. const error = new Error('oh no');
  14. const listeners = process.listeners('uncaughtException');
  15. process.removeAllListeners('uncaughtException');
  16. process.on('uncaughtException', (thrown) => {
  17. try {
  18. expect(thrown).to.equal(error);
  19. done();
  20. } catch (e) {
  21. done(e);
  22. } finally {
  23. process.removeAllListeners('uncaughtException');
  24. listeners.forEach((listener) => process.on('uncaughtException', listener));
  25. }
  26. });
  27. browserWindow.webContents.on('did-create-window', () => {
  28. assert.fail('did-create-window should not be called with an overridden window.open');
  29. });
  30. browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
  31. browserWindow.webContents.setWindowOpenHandler(() => {
  32. throw error;
  33. });
  34. });
  35. it('does not fire window creation events if the handler callback returns a bad result', async () => {
  36. const bad = new Promise((resolve) => {
  37. browserWindow.webContents.setWindowOpenHandler(() => {
  38. setTimeout(resolve);
  39. return [1, 2, 3] as any;
  40. });
  41. });
  42. browserWindow.webContents.on('did-create-window', () => {
  43. assert.fail('did-create-window should not be called with an overridden window.open');
  44. });
  45. browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
  46. await bad;
  47. });
  48. it('does not fire window creation events if an override returns action: deny', async () => {
  49. const denied = new Promise((resolve) => {
  50. browserWindow.webContents.setWindowOpenHandler(() => {
  51. setTimeout(resolve);
  52. return { action: 'deny' };
  53. });
  54. });
  55. browserWindow.webContents.on('did-create-window', () => {
  56. assert.fail('did-create-window should not be called with an overridden window.open');
  57. });
  58. browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
  59. await denied;
  60. });
  61. it('is called when clicking on a target=_blank link', async () => {
  62. const denied = new Promise((resolve) => {
  63. browserWindow.webContents.setWindowOpenHandler(() => {
  64. setTimeout(resolve);
  65. return { action: 'deny' };
  66. });
  67. });
  68. browserWindow.webContents.on('did-create-window', () => {
  69. assert.fail('did-create-window should not be called with an overridden window.open');
  70. });
  71. await browserWindow.webContents.loadURL('data:text/html,<a target="_blank" href="http://example.com" style="display: block; width: 100%; height: 100%; position: fixed; left: 0; top: 0;">link</a>');
  72. browserWindow.webContents.sendInputEvent({ type: 'mouseDown', x: 10, y: 10, button: 'left', clickCount: 1 });
  73. browserWindow.webContents.sendInputEvent({ type: 'mouseUp', x: 10, y: 10, button: 'left', clickCount: 1 });
  74. await denied;
  75. });
  76. it('is called when shift-clicking on a link', async () => {
  77. const denied = new Promise((resolve) => {
  78. browserWindow.webContents.setWindowOpenHandler(() => {
  79. setTimeout(resolve);
  80. return { action: 'deny' };
  81. });
  82. });
  83. browserWindow.webContents.on('did-create-window', () => {
  84. assert.fail('did-create-window should not be called with an overridden window.open');
  85. });
  86. await browserWindow.webContents.loadURL('data:text/html,<a href="http://example.com" style="display: block; width: 100%; height: 100%; position: fixed; left: 0; top: 0;">link</a>');
  87. browserWindow.webContents.sendInputEvent({ type: 'mouseDown', x: 10, y: 10, button: 'left', clickCount: 1, modifiers: ['shift'] });
  88. browserWindow.webContents.sendInputEvent({ type: 'mouseUp', x: 10, y: 10, button: 'left', clickCount: 1, modifiers: ['shift'] });
  89. await denied;
  90. });
  91. it('fires handler with correct params', async () => {
  92. const testFrameName = 'test-frame-name';
  93. const testFeatures = 'top=10&left=10&something-unknown&show=no';
  94. const testUrl = 'app://does-not-exist/';
  95. const details = await new Promise<Electron.HandlerDetails>(resolve => {
  96. browserWindow.webContents.setWindowOpenHandler((details) => {
  97. setTimeout(() => resolve(details));
  98. return { action: 'deny' };
  99. });
  100. browserWindow.webContents.executeJavaScript(`window.open('${testUrl}', '${testFrameName}', '${testFeatures}') && true`);
  101. });
  102. const { url, frameName, features, disposition, referrer } = details;
  103. expect(url).to.equal(testUrl);
  104. expect(frameName).to.equal(testFrameName);
  105. expect(features).to.equal(testFeatures);
  106. expect(disposition).to.equal('new-window');
  107. expect(referrer).to.deep.equal({
  108. policy: 'strict-origin-when-cross-origin',
  109. url: ''
  110. });
  111. });
  112. it('includes post body', async () => {
  113. const details = await new Promise<Electron.HandlerDetails>(resolve => {
  114. browserWindow.webContents.setWindowOpenHandler((details) => {
  115. setTimeout(() => resolve(details));
  116. return { action: 'deny' };
  117. });
  118. browserWindow.webContents.loadURL(`data:text/html,${encodeURIComponent(`
  119. <form action="http://example.com" target="_blank" method="POST" id="form">
  120. <input name="key" value="value"></input>
  121. </form>
  122. <script>form.submit()</script>
  123. `)}`);
  124. });
  125. const { url, frameName, features, disposition, referrer, postBody } = details;
  126. expect(url).to.equal('http://example.com/');
  127. expect(frameName).to.equal('');
  128. expect(features).to.deep.equal('');
  129. expect(disposition).to.equal('foreground-tab');
  130. expect(referrer).to.deep.equal({
  131. policy: 'strict-origin-when-cross-origin',
  132. url: ''
  133. });
  134. expect(postBody).to.deep.equal({
  135. contentType: 'application/x-www-form-urlencoded',
  136. data: [{
  137. type: 'rawData',
  138. bytes: Buffer.from('key=value')
  139. }]
  140. });
  141. });
  142. it('does fire window creation events if an override returns action: allow', async () => {
  143. browserWindow.webContents.setWindowOpenHandler(() => ({ action: 'allow' }));
  144. setImmediate(() => {
  145. browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
  146. });
  147. await emittedOnce(browserWindow.webContents, 'did-create-window');
  148. });
  149. it('can change webPreferences of child windows', (done) => {
  150. browserWindow.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { defaultFontSize: 30 } } }));
  151. browserWindow.webContents.on('did-create-window', async (childWindow) => {
  152. await childWindow.webContents.executeJavaScript("document.write('hello')");
  153. const size = await childWindow.webContents.executeJavaScript("getComputedStyle(document.querySelector('body')).fontSize");
  154. expect(size).to.equal('30px');
  155. done();
  156. });
  157. browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true");
  158. });
  159. it('does not hang parent window when denying window.open', async () => {
  160. browserWindow.webContents.setWindowOpenHandler(() => ({ action: 'deny' }));
  161. browserWindow.webContents.executeJavaScript("window.open('https://127.0.0.1')");
  162. expect(await browserWindow.webContents.executeJavaScript('42')).to.equal(42);
  163. });
  164. });