chrome-api.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. const {ipcRenderer} = require('electron')
  2. const Event = require('./extensions/event')
  3. const url = require('url')
  4. let nextId = 0
  5. class Tab {
  6. constructor (tabId) {
  7. this.id = tabId
  8. }
  9. }
  10. class MessageSender {
  11. constructor (tabId, extensionId) {
  12. this.tab = tabId ? new Tab(tabId) : null
  13. this.id = extensionId
  14. this.url = `chrome-extension://${extensionId}`
  15. }
  16. }
  17. class Port {
  18. constructor (tabId, portId, extensionId, name) {
  19. this.tabId = tabId
  20. this.portId = portId
  21. this.disconnected = false
  22. this.name = name
  23. this.onDisconnect = new Event()
  24. this.onMessage = new Event()
  25. this.sender = new MessageSender(tabId, extensionId)
  26. ipcRenderer.once(`CHROME_PORT_DISCONNECT_${portId}`, () => {
  27. this._onDisconnect()
  28. })
  29. ipcRenderer.on(`CHROME_PORT_POSTMESSAGE_${portId}`, (event, message) => {
  30. const sendResponse = function () { console.error('sendResponse is not implemented') }
  31. this.onMessage.emit(message, this.sender, sendResponse)
  32. })
  33. }
  34. disconnect () {
  35. if (this.disconnected) return
  36. ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`)
  37. this._onDisconnect()
  38. }
  39. postMessage (message) {
  40. ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, message)
  41. }
  42. _onDisconnect () {
  43. this.disconnected = true
  44. ipcRenderer.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`)
  45. this.onDisconnect.emit()
  46. }
  47. }
  48. // Inject chrome API to the |context|
  49. exports.injectTo = function (extensionId, isBackgroundPage, context) {
  50. const chrome = context.chrome = context.chrome || {}
  51. let originResultID = 1
  52. ipcRenderer.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, (event, tabId, portId, connectInfo) => {
  53. chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name))
  54. })
  55. ipcRenderer.on(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, (event, tabId, message, resultID) => {
  56. chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), (messageResult) => {
  57. ipcRenderer.send(`CHROME_RUNTIME_ONMESSAGE_RESULT_${resultID}`, messageResult)
  58. })
  59. })
  60. ipcRenderer.on('CHROME_TABS_ONCREATED', (event, tabId) => {
  61. chrome.tabs.onCreated.emit(new Tab(tabId))
  62. })
  63. ipcRenderer.on('CHROME_TABS_ONREMOVED', (event, tabId) => {
  64. chrome.tabs.onRemoved.emit(tabId)
  65. })
  66. chrome.runtime = {
  67. id: extensionId,
  68. getURL: function (path) {
  69. return url.format({
  70. protocol: 'chrome-extension',
  71. slashes: true,
  72. hostname: extensionId,
  73. pathname: path
  74. })
  75. },
  76. connect (...args) {
  77. if (isBackgroundPage) {
  78. console.error('chrome.runtime.connect is not supported in background page')
  79. return
  80. }
  81. // Parse the optional args.
  82. let targetExtensionId = extensionId
  83. let connectInfo = {name: ''}
  84. if (args.length === 1) {
  85. connectInfo = args[0]
  86. } else if (args.length === 2) {
  87. [targetExtensionId, connectInfo] = args
  88. }
  89. const {tabId, portId} = ipcRenderer.sendSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo)
  90. return new Port(tabId, portId, extensionId, connectInfo.name)
  91. },
  92. sendMessage (...args) {
  93. if (isBackgroundPage) {
  94. console.error('chrome.runtime.sendMessage is not supported in background page')
  95. return
  96. }
  97. // Parse the optional args.
  98. let targetExtensionId = extensionId
  99. let message
  100. if (args.length === 1) {
  101. message = args[0]
  102. } else if (args.length === 2) {
  103. // A case of not provide extension-id: (message, responseCallback)
  104. if (typeof args[1] === 'function') {
  105. ipcRenderer.on(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, (event, result) => args[1](result))
  106. message = args[0]
  107. } else {
  108. [targetExtensionId, message] = args
  109. }
  110. } else {
  111. console.error('options is not supported')
  112. ipcRenderer.on(`CHROME_RUNTIME_SENDMESSAGE_RESULT_${originResultID}`, (event, result) => args[2](result))
  113. }
  114. ipcRenderer.send('CHROME_RUNTIME_SENDMESSAGE', targetExtensionId, message, originResultID)
  115. originResultID++
  116. },
  117. onConnect: new Event(),
  118. onMessage: new Event(),
  119. onInstalled: new Event()
  120. }
  121. chrome.tabs = {
  122. executeScript (tabId, details, callback) {
  123. const requestId = ++nextId
  124. ipcRenderer.once(`CHROME_TABS_EXECUTESCRIPT_RESULT_${requestId}`, (event, result) => {
  125. // Disabled due to false positive in StandardJS
  126. // eslint-disable-next-line standard/no-callback-literal
  127. callback([event.result])
  128. })
  129. ipcRenderer.send('CHROME_TABS_EXECUTESCRIPT', requestId, tabId, extensionId, details)
  130. },
  131. sendMessage (tabId, message, options, responseCallback) {
  132. if (responseCallback) {
  133. ipcRenderer.on(`CHROME_TABS_SEND_MESSAGE_RESULT_${originResultID}`, (event, result) => responseCallback(result))
  134. }
  135. ipcRenderer.send('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, isBackgroundPage, message, originResultID)
  136. originResultID++
  137. },
  138. onUpdated: new Event(),
  139. onCreated: new Event(),
  140. onRemoved: new Event()
  141. }
  142. chrome.extension = {
  143. getURL: chrome.runtime.getURL,
  144. connect: chrome.runtime.connect,
  145. onConnect: chrome.runtime.onConnect,
  146. sendMessage: chrome.runtime.sendMessage,
  147. onMessage: chrome.runtime.onMessage
  148. }
  149. chrome.storage = require('./extensions/storage').setup(extensionId)
  150. chrome.pageAction = {
  151. show () {},
  152. hide () {},
  153. setTitle () {},
  154. getTitle () {},
  155. setIcon () {},
  156. setPopup () {},
  157. getPopup () {}
  158. }
  159. chrome.i18n = require('./extensions/i18n').setup(extensionId)
  160. chrome.webNavigation = require('./extensions/web-navigation').setup()
  161. }