You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

245 lines
7.9 KiB

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