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.

205 lines
6.6 KiB

  1. /*******************************************************************************
  2. µBlock - a browser extension to block requests.
  3. Copyright (C) 2014 The µBlock 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/uBlock
  15. */
  16. /* global Services, Components, XPCOMUtils */
  17. 'use strict';
  18. /******************************************************************************/
  19. this.EXPORTED_SYMBOLS = ['contentPolicy', 'docObserver'];
  20. const {interfaces: Ci, utils: Cu} = Components;
  21. const appName = __URI__.match(/:\/\/([^\/]+)/)[1];
  22. Cu['import']('resource://gre/modules/Services.jsm');
  23. Cu['import']('resource://gre/modules/XPCOMUtils.jsm');
  24. // Cu['import']('resource://gre/modules/devtools/Console.jsm');
  25. /******************************************************************************/
  26. const getMessager = win =>
  27. win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDocShell)
  28. .sameTypeRootTreeItem.QueryInterface(Ci.nsIDocShell)
  29. .QueryInterface(Ci.nsIInterfaceRequestor)
  30. .getInterface(Ci.nsIContentFrameMessageManager);
  31. /******************************************************************************/
  32. const contentPolicy = {
  33. classDescription: 'content-policy implementation for ' + appName,
  34. classID: Components.ID('{e6d173c8-8dbf-4189-a6fd-189e8acffd27}'),
  35. contractID: '@' + appName + '/content-policy;1',
  36. ACCEPT: Ci.nsIContentPolicy.ACCEPT,
  37. REJECT: Ci.nsIContentPolicy.REJECT_REQUEST,
  38. requestMessageName: appName + ':onBeforeRequest',
  39. get componentRegistrar() {
  40. return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
  41. },
  42. get categoryManager() {
  43. return Components.classes['@mozilla.org/categorymanager;1']
  44. .getService(Ci.nsICategoryManager);
  45. },
  46. QueryInterface: XPCOMUtils.generateQI([
  47. Ci.nsIFactory,
  48. Ci.nsIContentPolicy,
  49. Ci.nsISupportsWeakReference
  50. ]),
  51. createInstance: function(outer, iid) {
  52. if (outer) {
  53. throw Components.results.NS_ERROR_NO_AGGREGATION;
  54. }
  55. return this.QueryInterface(iid);
  56. },
  57. register: function() {
  58. this.componentRegistrar.registerFactory(
  59. this.classID,
  60. this.classDescription,
  61. this.contractID,
  62. this
  63. );
  64. this.categoryManager.addCategoryEntry(
  65. 'content-policy',
  66. this.contractID,
  67. this.contractID,
  68. false,
  69. true
  70. );
  71. },
  72. unregister: function() {
  73. this.componentRegistrar.unregisterFactory(this.classID, this);
  74. this.categoryManager.deleteCategoryEntry(
  75. 'content-policy',
  76. this.contractID,
  77. false
  78. );
  79. },
  80. shouldLoad: function(type, location, origin, context) {
  81. if (!context || !/^https?$/.test(location.scheme)) {
  82. return this.ACCEPT;
  83. }
  84. let win = type === 6
  85. ? context.contentWindow || context
  86. : (context.ownerDocument || context).defaultView;
  87. if (!win) {
  88. return this.ACCEPT;
  89. }
  90. let result = getMessager(win).sendSyncMessage(this.requestMessageName, {
  91. url: location.spec,
  92. type: type,
  93. tabId: -1, // determined in background script
  94. frameId: type === 6 ? -1 : (win === win.top ? 0 : 1),
  95. parentFrameId: win === win.top ? -1 : 0
  96. })[0];
  97. return result === true ? this.REJECT : this.ACCEPT;
  98. }/*,
  99. shouldProcess: function() {
  100. return this.ACCEPT;
  101. }*/
  102. };
  103. /******************************************************************************/
  104. const docObserver = {
  105. contentBaseURI: 'chrome://' + appName + '/content/',
  106. initContext: function(win, sandbox) {
  107. let messager = getMessager(win);
  108. if (sandbox) {
  109. win = Cu.Sandbox([win], {
  110. sandboxPrototype: win,
  111. wantComponents: false,
  112. wantXHRConstructor: false
  113. });
  114. win.self = win;
  115. // anonymous function needs to be used here
  116. win.injectScript = Cu.exportFunction(
  117. function(script, evalCode) {
  118. if (evalCode) {
  119. Cu.evalInSandbox(script, win);
  120. return;
  121. }
  122. Services.scriptloader.loadSubScript(
  123. docObserver.contentBaseURI + script,
  124. win
  125. );
  126. },
  127. win
  128. );
  129. }
  130. win.sendAsyncMessage = messager.sendAsyncMessage;
  131. win.addMessageListener = messager.ublock_addMessageListener;
  132. win.removeMessageListener = messager.ublock_removeMessageListener;
  133. return win;
  134. },
  135. register: function() {
  136. Services.obs.addObserver(this, 'document-element-inserted', false);
  137. },
  138. unregister: function() {
  139. Services.obs.removeObserver(this, 'document-element-inserted');
  140. },
  141. observe: function(doc) {
  142. let win = doc.defaultView;
  143. if (!win) {
  144. return;
  145. }
  146. if (!/^https?:$/.test(win.location.protocol)) {
  147. if (win.location.protocol === 'chrome:'
  148. && win.location.host === appName) {
  149. this.initContext(win);
  150. }
  151. return;
  152. }
  153. let lss = Services.scriptloader.loadSubScript;
  154. win = this.initContext(win, true);
  155. lss(this.contentBaseURI + 'js/vapi-client.js', win);
  156. lss(this.contentBaseURI + 'js/contentscript-start.js', win);
  157. let docReady = function(e) {
  158. this.removeEventListener(e.type, docReady, true);
  159. lss(docObserver.contentBaseURI + 'js/contentscript-end.js', win);
  160. };
  161. doc.addEventListener('DOMContentLoaded', docReady, true);
  162. }
  163. };
  164. /******************************************************************************/
  165. contentPolicy.register();
  166. docObserver.register();
  167. /******************************************************************************/