bootstrap.js 8.0 KB

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