bootstrap.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*******************************************************************************
  2. ηMatrix - a browser extension to black/white list requests.
  3. Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
  4. Copyright (C) 2019 Alessio Vanni
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see {http://www.gnu.org/licenses/}.
  15. Home: https://libregit.org/heckyel/ematrix
  16. uMatrix Home: https://github.com/gorhill/uMatrix
  17. */
  18. 'use strict';
  19. const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
  20. // Accessing the context of the background page:
  21. // var win = Services.appShell.hiddenDOMWindow.document.querySelector('iframe[src*=ematrix]').contentWindow;
  22. let windowlessBrowser = null;
  23. let windowlessBrowserPL = null;
  24. let bgProcess = null;
  25. let version;
  26. const hostName = 'ematrix';
  27. const restartListener = {
  28. get messageManager() {
  29. return Cc['@mozilla.org/parentprocessmessagemanager;1']
  30. .getService(Ci.nsIMessageListenerManager);
  31. },
  32. receiveMessage: function() {
  33. shutdown();
  34. startup();
  35. }
  36. };
  37. // https://github.com/gorhill/uBlock/issues/2493
  38. // Fix by https://github.com/gijsk
  39. // imported from https://github.com/gorhill/uBlock/pull/2497
  40. function startup(data, reason) {
  41. if (data !== undefined) {
  42. version = data.version;
  43. }
  44. // Already started?
  45. if (bgProcess !== null) {
  46. return;
  47. }
  48. waitForHiddenWindow();
  49. }
  50. function createBgProcess(parentDocument) {
  51. bgProcess = parentDocument
  52. .documentElement
  53. .appendChild(parentDocument
  54. .createElementNS('http://www.w3.org/1999/xhtml',
  55. 'iframe'));
  56. bgProcess.setAttribute('src',
  57. 'chrome://' + hostName
  58. + '/content/background.html#' + version);
  59. // https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIMessageListenerManager#addMessageListener%28%29
  60. // "If the same listener registers twice for the same message, the
  61. // "second registration is ignored."
  62. restartListener
  63. .messageManager
  64. .addMessageListener(hostName + '-restart', restartListener);
  65. }
  66. function getWindowlessBrowserFrame(appShell) {
  67. windowlessBrowser = appShell.createWindowlessBrowser(true);
  68. windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor);
  69. let webProgress = windowlessBrowser.getInterface(Ci.nsIWebProgress);
  70. Cu.import('resource://gre/modules/XPCOMUtils.jsm');
  71. windowlessBrowserPL = {
  72. QueryInterface: XPCOMUtils.generateQI([
  73. Ci.nsIWebProgressListener,
  74. Ci.nsIWebProgressListener2,
  75. Ci.nsISupportsWeakReference
  76. ]),
  77. onStateChange: function(wbp, request, stateFlags, status) {
  78. if (!request) {
  79. return;
  80. }
  81. if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
  82. webProgress.removeProgressListener(windowlessBrowserPL);
  83. windowlessBrowserPL = null;
  84. createBgProcess(windowlessBrowser.document);
  85. }
  86. }
  87. };
  88. webProgress.addProgressListener(windowlessBrowserPL,
  89. Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
  90. windowlessBrowser.document.location =
  91. "data:application/vnd.mozilla.xul+xml;charset=utf-8,<window%20id='"
  92. + hostName
  93. + "-win'/>";
  94. }
  95. function waitForHiddenWindow() {
  96. let appShell = Cc['@mozilla.org/appshell/appShellService;1']
  97. .getService(Ci.nsIAppShellService);
  98. let isReady = function() {
  99. var hiddenDoc;
  100. try {
  101. hiddenDoc = appShell.hiddenDOMWindow &&
  102. appShell.hiddenDOMWindow.document;
  103. } catch (ex) {
  104. }
  105. // Do not test against `loading`: it does appear `readyState` could be
  106. // undefined if looked up too early.
  107. if (!hiddenDoc || hiddenDoc.readyState !== 'complete') {
  108. return false;
  109. }
  110. // In theory, it should be possible to create a windowless browser
  111. // immediately, without waiting for the hidden window to have loaded
  112. // completely. However, in practice, on Windows this seems to lead
  113. // to a broken Firefox appearance. To avoid this, we only create the
  114. // windowless browser here. We'll use that rather than the hidden
  115. // window for the actual background page (windowless browsers are
  116. // also what the webextension implementation in Firefox uses for
  117. // background pages).
  118. getWindowlessBrowserFrame(appShell);
  119. return true;
  120. };
  121. if (isReady()) {
  122. return;
  123. }
  124. // https://github.com/gorhill/uBlock/issues/749
  125. // Poll until the proper environment is set up -- or give up eventually.
  126. // We poll frequently early on but relax poll delay as time pass.
  127. let tryDelay = 5;
  128. let trySum = 0;
  129. // https://trac.torproject.org/projects/tor/ticket/19438
  130. // Try for a longer period.
  131. // ηMatrix: I doubt this applies to us...
  132. let tryMax = 600011;
  133. let timer = Cc['@mozilla.org/timer;1']
  134. .createInstance(Ci.nsITimer);
  135. let checkLater = function() {
  136. trySum += tryDelay;
  137. if (trySum >= tryMax) {
  138. timer = null;
  139. return;
  140. }
  141. timer.init(timerObserver, tryDelay, timer.TYPE_ONE_SHOT);
  142. tryDelay *= 2;
  143. if (tryDelay > 503) {
  144. tryDelay = 503;
  145. }
  146. };
  147. var timerObserver = {
  148. observe: function() {
  149. timer.cancel();
  150. if (isReady()) {
  151. timer = null;
  152. } else {
  153. checkLater();
  154. }
  155. }
  156. };
  157. checkLater();
  158. }
  159. function shutdown(data, reason) {
  160. if (reason === APP_SHUTDOWN) {
  161. return;
  162. }
  163. if (bgProcess !== null) {
  164. bgProcess.parentNode.removeChild(bgProcess);
  165. bgProcess = null;
  166. }
  167. if (windowlessBrowser !== null) {
  168. // close() does not exist for older versions of Firefox.
  169. // ηMatrix: how old? But keeping it doesn't really hurt that much.
  170. if (typeof windowlessBrowser.close === 'function') {
  171. windowlessBrowser.close();
  172. }
  173. windowlessBrowser = null;
  174. windowlessBrowserPL = null;
  175. }
  176. if (data === undefined) {
  177. return;
  178. }
  179. // Remove the restartObserver only when the extension is being disabled
  180. restartListener
  181. .messageManager
  182. .removeMessageListener(hostName + '-restart',
  183. restartListener);
  184. }
  185. function install(data, reason) {
  186. // https://bugzil.la/719376
  187. Cc['@mozilla.org/intl/stringbundle;1']
  188. .getService(Ci.nsIStringBundleService)
  189. .flushBundles();
  190. if (reason === ADDON_UPGRADE) {
  191. let Services =
  192. Cu.import('resource://gre/modules/Services.jsm', null).Services
  193. Services.obs.notifyObservers(null, 'chrome-flush-caches', null);
  194. Services.obs.notifyObservers(null, 'message-manager-flush-caches', null);
  195. }
  196. }
  197. function uninstall(data, aReason) {
  198. if (aReason !== ADDON_UNINSTALL) {
  199. return;
  200. }
  201. // To cleanup vAPI.localStorage in vapi-common.js, aka
  202. // "extensions.ematrix.*" in `about:config`.
  203. Cu.import('resource://gre/modules/Services.jsm', null)
  204. .Services.prefs.getBranch('extensions.' + hostName + '.')
  205. .deleteBranch('');
  206. }