host.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.createRenderer = createRenderer;
  6. exports.destroyRenderer = destroyRenderer;
  7. exports.eventHandler = eventHandler;
  8. var Backoff = require('./Backoff');
  9. var _require = require('electron'),
  10. BrowserWindow = _require.BrowserWindow,
  11. _ipcMain = _require.ipcMain;
  12. var Overlay = require('./overlay_module.js');
  13. var _require2 = require('./securityUtils'),
  14. saferShellOpenExternal = _require2.saferShellOpenExternal;
  15. var path = require('path'); // IPC events must be prefixed with `DISCORD_`
  16. var ipcMain = {
  17. on: function on(event, callback) {
  18. return _ipcMain.on("DISCORD_".concat(event), callback);
  19. },
  20. removeListener: function removeListener(event, callback) {
  21. return _ipcMain.removeListener("DISCORD_".concat(event), callback);
  22. }
  23. };
  24. ipcMain.on('OPEN_EXTERNAL_URL', function (e, externalUrl) {
  25. saferShellOpenExternal(externalUrl);
  26. });
  27. function webContentsSend(win, event) {
  28. if (win != null && win.webContents != null) {
  29. var _win$webContents;
  30. for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
  31. args[_key - 2] = arguments[_key];
  32. }
  33. (_win$webContents = win.webContents).send.apply(_win$webContents, ["DISCORD_".concat(event)].concat(args));
  34. }
  35. }
  36. var renderers = {};
  37. var accelerators = {
  38. Z: 'undo',
  39. Y: 'redo',
  40. X: 'cut',
  41. C: 'copy',
  42. V: 'paste',
  43. A: 'selectAll'
  44. };
  45. function handleAccelerators(contents, event) {
  46. if (event['type'] !== 'keyDown') {
  47. return false;
  48. }
  49. if (!event['modifiers'] || !event['modifiers'].includes('control')) {
  50. return false;
  51. }
  52. var fname = accelerators[event['keyCode']];
  53. if (!fname) {
  54. return false;
  55. }
  56. var fn = contents[fname];
  57. if (!fn) {
  58. return false;
  59. }
  60. fn.apply(contents);
  61. return true;
  62. }
  63. function createRenderer(pid, url) {
  64. if (renderers[pid]) {
  65. return;
  66. }
  67. var _require3 = require('url'),
  68. URL = _require3.URL;
  69. var urlWithPid = new URL(url);
  70. urlWithPid.searchParams.append('pid', pid.toString());
  71. url = urlWithPid.toString();
  72. renderers[pid] = {
  73. pid: pid,
  74. url: "file://".concat(__dirname, "/start.html?pid=").concat(pid.toString()),
  75. overlayURL: url,
  76. backoff: new Backoff(1000, 30000),
  77. window: new BrowserWindow({
  78. show: false,
  79. skipTaskbar: true,
  80. webPreferences: {
  81. offscreen: true,
  82. transparent: true,
  83. nodeIntegration: false,
  84. preload: require.resolve('discord_desktop_core/core/app/mainScreenPreload.js'),
  85. enableRemoteModule: false,
  86. contextIsolation: true
  87. }
  88. })
  89. };
  90. var renderer = renderers[pid];
  91. Overlay.connectProcess(pid);
  92. if (renderer.window.webContents._setDiscordOverlayProcessId) {
  93. renderer.window.webContents._setDiscordOverlayProcessId(pid);
  94. } // "paint" event will be skipped if direct frame delivery is enabled.
  95. renderer.window.webContents.on('crashed', function (e, killed) {
  96. Overlay.logMessage("Overlay for pid ".concat(renderer.pid, " crashed").concat(killed ? ' (killed)' : ''));
  97. Overlay.sendCommand(renderer.pid, {
  98. message: 'relay',
  99. _relay: 'renderer_crashed'
  100. });
  101. destroyRenderer(pid);
  102. });
  103. renderer.window.webContents.on('console-message', function (_event, _level, message, _line_no, _source_id) {
  104. Overlay.logMessage("OverlayRenderer[".concat(pid, "]: ").concat(message));
  105. });
  106. renderer.window.webContents.on('paint', function (_event, _dirty, image, _legacy_width, _legacy_height) {
  107. // [adill] support electron <=1.8.4 which sent a (buffer, width, height) instead of (image)
  108. if (Buffer.isBuffer(image)) {
  109. var width = _legacy_width;
  110. var height = _legacy_height;
  111. Overlay.sendFramebuffer(renderer.pid, image, width, height);
  112. return;
  113. }
  114. Overlay.sendFramebuffer(renderer.pid, image.getBitmap(), image.getSize().width, image.getSize().height);
  115. });
  116. renderer.window.webContents.on('new-window', function (e, url) {
  117. e.preventDefault();
  118. webContentsSend(renderer.window, 'REQUEST_OPEN_EXTERNAL_URL', url);
  119. });
  120. renderer.window.loadURL(renderer.url);
  121. }
  122. function loadOverlay(pid) {
  123. var renderer = renderers[pid];
  124. if (renderer == null) {
  125. return;
  126. }
  127. if (renderer.url === renderer.overlayURL) {
  128. return;
  129. }
  130. renderer.window.webContents.on('cursor-changed', function (_event, type) {
  131. var cursor;
  132. switch (type) {
  133. case 'default':
  134. cursor = 'IDC_ARROW';
  135. break;
  136. case 'pointer':
  137. cursor = 'IDC_HAND';
  138. break;
  139. case 'crosshair':
  140. cursor = 'IDC_CROSS';
  141. break;
  142. case 'text':
  143. cursor = 'IDC_IBEAM';
  144. break;
  145. case 'wait':
  146. cursor = 'IDC_WAIT';
  147. break;
  148. case 'help':
  149. cursor = 'IDC_HELP';
  150. break;
  151. case 'move':
  152. cursor = 'IDC_SIZEALL';
  153. break;
  154. case 'ns-resize':
  155. cursor = 'IDC_SIZENS';
  156. break;
  157. case 'ew-resize':
  158. cursor = 'IDC_SIZEWE';
  159. break;
  160. case 'nwse-resize':
  161. cursor = 'IDC_SIZENWSE';
  162. break;
  163. case 'nesw-resize':
  164. cursor = 'IDC_SIZENESW';
  165. break;
  166. case 'none':
  167. cursor = '';
  168. break;
  169. }
  170. if (cursor) {
  171. Overlay.sendCommand(renderer.pid, {
  172. message: 'set_cursor',
  173. cursor: cursor
  174. });
  175. }
  176. });
  177. renderer.window.webContents.on('start-drag', function (_event, image, offset) {
  178. Overlay.sendCommand(renderer.pid, {
  179. message: 'set_drag_state',
  180. dragging: true,
  181. image: image.getBitmap().toJSON().data,
  182. size: image.getSize(),
  183. offset: offset
  184. });
  185. });
  186. renderer.window.webContents.on('stop-drag', function (_event) {
  187. Overlay.sendCommand(renderer.pid, {
  188. message: 'set_drag_state',
  189. dragging: false
  190. });
  191. });
  192. renderer.window.webContents.on('ime-composition-range-changed', function (_event, start, end, bounds) {
  193. Overlay.sendCommand(renderer.pid, {
  194. message: 'ime_composition_range_changed',
  195. start: start,
  196. end: end,
  197. bounds: bounds
  198. });
  199. });
  200. renderer.window.webContents.on('selection-bounds-changed', function (_event, anchor, focus, isAnchorFirst) {
  201. Overlay.sendCommand(renderer.pid, {
  202. message: 'ime_selection_bounds_changed',
  203. anchor: anchor,
  204. focus: focus,
  205. isAnchorFirst: isAnchorFirst
  206. });
  207. });
  208. renderer.window.webContents.on('did-fail-load', function (e, errCode, errDesc, validatedURL) {
  209. if (validatedURL !== renderer.url) {
  210. Overlay.logMessage("Failed non-overlay URL load (".concat(validatedURL, ") with code ").concat(errCode, " and description ").concat(errDesc));
  211. return;
  212. }
  213. Overlay.logMessage("Failed overlay URL load (".concat(validatedURL, ") with code ").concat(errCode, " and description ").concat(errDesc));
  214. Overlay.sendCommand(renderer.pid, {
  215. message: 'relay',
  216. _relay: 'renderer_load_failed'
  217. });
  218. renderer.backoff.fail(function () {
  219. Overlay.logMessage("Retrying overlay URL load ".concat(renderer.url));
  220. renderer.window.loadURL(renderer.url);
  221. });
  222. });
  223. renderer.window.webContents.on('did-finish-load', function () {
  224. if (renderer.window.webContents.getURL() === renderer.overlayURL) {
  225. renderer.window.focusOnWebView();
  226. renderer.backoff.succeed();
  227. Overlay.logMessage('Overlay is ready to show');
  228. renderer.window.webContents.invalidate(); // NB: allow a short window for repainting to ameliorate white-screen flashes some users experience.
  229. // see https://github.com/electron/electron/pull/25448 for context as to why we aren't using
  230. // `ready-to-show` -- tl;dr: it is not consistent and the hack fix in electron only "fixes"
  231. // the event on the first page load. we load `start.html` as a canary test *before* loading the
  232. // overlay url and, thusly, miss `ready-to-show` on some systems / chromium versions.
  233. setTimeout(function () {
  234. Overlay.sendCommand(renderer.pid, {
  235. message: 'relay',
  236. _relay: 'ready_to_show'
  237. });
  238. }, 200);
  239. }
  240. });
  241. if (renderer.window.webContents.sendImeEvent) {
  242. Overlay.sendCommand(renderer.pid, {
  243. message: 'notify_ime_supported'
  244. });
  245. }
  246. renderer.url = renderer.overlayURL;
  247. renderer.window.loadURL(renderer.url);
  248. }
  249. function destroyRenderer(pid) {
  250. var renderer = renderers[pid];
  251. if (renderer == null) {
  252. return;
  253. }
  254. Overlay.disconnectProcess(pid);
  255. if (renderer.backoff) {
  256. renderer.backoff.cancel();
  257. }
  258. if (renderer.window && !renderer.window.isDestroyed()) {
  259. renderer.window.destroy();
  260. }
  261. delete renderers[pid];
  262. }
  263. function needsTranslation(event) {
  264. return 'msg' in event || !'type' in event;
  265. }
  266. function eventHandler(pid, event) {
  267. var renderer = renderers[pid];
  268. if (renderer == null || renderer.window == null || renderer.window.isDestroyed()) {
  269. return;
  270. }
  271. var _screen = require('electron').screen;
  272. if (event.message === 'graphics_info') {
  273. if (event.width > 0 && event.height > 0) {
  274. Overlay.logMessage("Resizing overlay renderer to ".concat(event.width, "x").concat(event.height));
  275. if (global.features.supports('overlay-hidpi')) {
  276. var screenRect = {
  277. x: 0,
  278. y: 0,
  279. width: event.width,
  280. height: event.height
  281. };
  282. var dipRect = _screen.screenToDipRect(renderer.window, screenRect);
  283. renderer.window.setContentSize(dipRect.width, dipRect.height);
  284. } else {
  285. renderer.window.setContentSize(event.width, event.height);
  286. }
  287. renderer.window.webContents.setFrameRate(60);
  288. } else {
  289. renderer.window.webContents.setFrameRate(1);
  290. }
  291. renderer.window.webContents.invalidate();
  292. } else if (event.message === 'input_event') {
  293. renderer.window.focusOnWebView();
  294. var translated = needsTranslation(event) ? Overlay.translateInputEvent(event) : event;
  295. if (translated) {
  296. if (!handleAccelerators(renderer.window.webContents, translated)) {
  297. if (global.features.supports('overlay-hidpi') && translated.x && translated.y) {
  298. var dipPoint = _screen.screenToDipPoint({
  299. x: translated.x,
  300. y: translated.y
  301. });
  302. translated.x = dipPoint.x;
  303. translated.y = dipPoint.y;
  304. }
  305. renderer.window.webContents.sendInputEvent(translated);
  306. }
  307. }
  308. } else if (event.message === 'renderer_started') {
  309. Overlay.logMessage("Overlay renderer for ".concat(pid, " started successfully."));
  310. renderer.started = true;
  311. if (!renderer.first_framebuffer) {
  312. renderer.window.webContents.invalidate();
  313. } else {
  314. loadOverlay(pid);
  315. }
  316. } else if (event.message === 'first_framebuffer') {
  317. renderer.first_framebuffer = true;
  318. if (renderer.started) {
  319. loadOverlay(pid);
  320. }
  321. } else if (event.message === 'ime') {
  322. renderer.window.webContents.sendImeEvent(event);
  323. }
  324. }