guest-view-manager.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. 'use strict'
  2. const ipcMain = require('electron').ipcMain
  3. const webContents = require('electron').webContents
  4. // Doesn't exist in early initialization.
  5. let webViewManager = null
  6. const supportedWebViewEvents = [
  7. 'load-commit',
  8. 'did-finish-load',
  9. 'did-fail-load',
  10. 'did-frame-finish-load',
  11. 'did-start-loading',
  12. 'did-stop-loading',
  13. 'did-get-response-details',
  14. 'did-get-redirect-request',
  15. 'dom-ready',
  16. 'console-message',
  17. 'devtools-opened',
  18. 'devtools-closed',
  19. 'devtools-focused',
  20. 'new-window',
  21. 'will-navigate',
  22. 'did-navigate',
  23. 'did-navigate-in-page',
  24. 'close',
  25. 'crashed',
  26. 'gpu-crashed',
  27. 'plugin-crashed',
  28. 'destroyed',
  29. 'page-title-updated',
  30. 'page-favicon-updated',
  31. 'enter-html-full-screen',
  32. 'leave-html-full-screen',
  33. 'media-started-playing',
  34. 'media-paused',
  35. 'found-in-page',
  36. 'did-change-theme-color',
  37. 'update-target-url'
  38. ]
  39. let nextInstanceId = 0
  40. const guestInstances = {}
  41. const embedderElementsMap = {}
  42. const reverseEmbedderElementsMap = {}
  43. // Moves the last element of array to the first one.
  44. const moveLastToFirst = function (list) {
  45. return list.unshift(list.pop())
  46. }
  47. // Generate guestInstanceId.
  48. const getNextInstanceId = function () {
  49. return ++nextInstanceId
  50. }
  51. // Create a new guest instance.
  52. const createGuest = function (embedder, params) {
  53. if (webViewManager == null) {
  54. webViewManager = process.atomBinding('web_view_manager')
  55. }
  56. const id = getNextInstanceId(embedder)
  57. const guest = webContents.create({
  58. isGuest: true,
  59. partition: params.partition,
  60. embedder: embedder
  61. })
  62. guestInstances[id] = {
  63. guest: guest,
  64. embedder: embedder
  65. }
  66. // Destroy guest when the embedder is gone or navigated.
  67. const destroyEvents = ['will-destroy', 'crashed', 'did-navigate']
  68. const destroy = function () {
  69. if (guestInstances[id] != null) {
  70. destroyGuest(embedder, id)
  71. }
  72. }
  73. for (const event of destroyEvents) {
  74. embedder.once(event, destroy)
  75. // Users might also listen to the crashed event, so we must ensure the guest
  76. // is destroyed before users' listener gets called. It is done by moving our
  77. // listener to the first one in queue.
  78. const listeners = embedder._events[event]
  79. if (Array.isArray(listeners)) {
  80. moveLastToFirst(listeners)
  81. }
  82. }
  83. guest.once('destroyed', function () {
  84. for (const event of destroyEvents) {
  85. embedder.removeListener(event, destroy)
  86. }
  87. })
  88. // Init guest web view after attached.
  89. guest.once('did-attach', function () {
  90. let opts
  91. params = this.attachParams
  92. delete this.attachParams
  93. this.viewInstanceId = params.instanceId
  94. this.setSize({
  95. normal: {
  96. width: params.elementWidth,
  97. height: params.elementHeight
  98. },
  99. enableAutoSize: params.autosize,
  100. min: {
  101. width: params.minwidth,
  102. height: params.minheight
  103. },
  104. max: {
  105. width: params.maxwidth,
  106. height: params.maxheight
  107. }
  108. })
  109. if (params.src) {
  110. opts = {}
  111. if (params.httpreferrer) {
  112. opts.httpReferrer = params.httpreferrer
  113. }
  114. if (params.useragent) {
  115. opts.userAgent = params.useragent
  116. }
  117. this.loadURL(params.src, opts)
  118. }
  119. guest.allowPopups = params.allowpopups
  120. })
  121. // Dispatch events to embedder.
  122. const fn = function (event) {
  123. guest.on(event, function (_, ...args) {
  124. embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + guest.viewInstanceId, event].concat(args))
  125. })
  126. }
  127. for (const event of supportedWebViewEvents) {
  128. fn(event)
  129. }
  130. // Dispatch guest's IPC messages to embedder.
  131. guest.on('ipc-message-host', function (_, [channel, ...args]) {
  132. embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + guest.viewInstanceId, channel].concat(args))
  133. })
  134. // Autosize.
  135. guest.on('size-changed', function (_, ...args) {
  136. embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + guest.viewInstanceId].concat(args))
  137. })
  138. return id
  139. }
  140. // Attach the guest to an element of embedder.
  141. const attachGuest = function (embedder, elementInstanceId, guestInstanceId, params) {
  142. let guest, key, oldGuestInstanceId, ref1, webPreferences
  143. guest = guestInstances[guestInstanceId].guest
  144. // Destroy the old guest when attaching.
  145. key = (embedder.getId()) + '-' + elementInstanceId
  146. oldGuestInstanceId = embedderElementsMap[key]
  147. if (oldGuestInstanceId != null) {
  148. // Reattachment to the same guest is not currently supported.
  149. if (oldGuestInstanceId === guestInstanceId) {
  150. return
  151. }
  152. if (guestInstances[oldGuestInstanceId] == null) {
  153. return
  154. }
  155. destroyGuest(embedder, oldGuestInstanceId)
  156. }
  157. webPreferences = {
  158. guestInstanceId: guestInstanceId,
  159. nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false,
  160. plugins: params.plugins,
  161. zoomFactor: params.zoomFactor,
  162. webSecurity: !params.disablewebsecurity,
  163. blinkFeatures: params.blinkfeatures,
  164. disableBlinkFeatures: params.disableblinkfeatures
  165. }
  166. if (params.preload) {
  167. webPreferences.preloadURL = params.preload
  168. }
  169. webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences)
  170. guest.attachParams = params
  171. embedderElementsMap[key] = guestInstanceId
  172. reverseEmbedderElementsMap[guestInstanceId] = key
  173. }
  174. // Destroy an existing guest instance.
  175. const destroyGuest = function (embedder, id) {
  176. webViewManager.removeGuest(embedder, id)
  177. guestInstances[id].guest.destroy()
  178. delete guestInstances[id]
  179. const key = reverseEmbedderElementsMap[id]
  180. if (key != null) {
  181. delete reverseEmbedderElementsMap[id]
  182. return delete embedderElementsMap[key]
  183. }
  184. }
  185. ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) {
  186. event.sender.send('ELECTRON_RESPONSE_' + requestId, createGuest(event.sender, params))
  187. })
  188. ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, elementInstanceId, guestInstanceId, params) {
  189. attachGuest(event.sender, elementInstanceId, guestInstanceId, params)
  190. })
  191. ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, id) {
  192. destroyGuest(event.sender, id)
  193. })
  194. ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_SET_SIZE', function (event, id, params) {
  195. const guestInstance = guestInstances[id]
  196. return guestInstance != null ? guestInstance.guest.setSize(params) : void 0
  197. })
  198. // Returns WebContents from its guest id.
  199. exports.getGuest = function (id) {
  200. const guestInstance = guestInstances[id]
  201. return guestInstance != null ? guestInstance.guest : void 0
  202. }
  203. // Returns the embedder of the guest.
  204. exports.getEmbedder = function (id) {
  205. const guestInstance = guestInstances[id]
  206. return guestInstance != null ? guestInstance.embedder : void 0
  207. }