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.

210 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, __URI__ */
  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 getMessageManager = function(context) {
  27. return context
  28. .QueryInterface(Ci.nsIInterfaceRequestor)
  29. .getInterface(Ci.nsIDocShell)
  30. .sameTypeRootTreeItem
  31. .QueryInterface(Ci.nsIDocShell)
  32. .QueryInterface(Ci.nsIInterfaceRequestor)
  33. .getInterface(Ci.nsIContentFrameMessageManager);
  34. };
  35. /******************************************************************************/
  36. const contentPolicy = {
  37. classDescription: 'content-policy implementation for ' + appName,
  38. classID: Components.ID('{e6d173c8-8dbf-4189-a6fd-189e8acffd27}'),
  39. contractID: '@' + appName + '/content-policy;1',
  40. ACCEPT: Ci.nsIContentPolicy.ACCEPT,
  41. messageName: appName + ':shouldLoad',
  42. get componentRegistrar() {
  43. return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
  44. },
  45. get categoryManager() {
  46. return Components.classes['@mozilla.org/categorymanager;1']
  47. .getService(Ci.nsICategoryManager);
  48. },
  49. QueryInterface: XPCOMUtils.generateQI([
  50. Ci.nsIFactory,
  51. Ci.nsIContentPolicy,
  52. Ci.nsISupportsWeakReference
  53. ]),
  54. createInstance: function(outer, iid) {
  55. if (outer) {
  56. throw Components.results.NS_ERROR_NO_AGGREGATION;
  57. }
  58. return this.QueryInterface(iid);
  59. },
  60. register: function() {
  61. this.componentRegistrar.registerFactory(
  62. this.classID,
  63. this.classDescription,
  64. this.contractID,
  65. this
  66. );
  67. this.categoryManager.addCategoryEntry(
  68. 'content-policy',
  69. this.contractID,
  70. this.contractID,
  71. false,
  72. true
  73. );
  74. },
  75. unregister: function() {
  76. this.componentRegistrar.unregisterFactory(this.classID, this);
  77. this.categoryManager.deleteCategoryEntry(
  78. 'content-policy',
  79. this.contractID,
  80. false
  81. );
  82. },
  83. // https://bugzil.la/612921
  84. shouldLoad: function(type, location, origin, context) {
  85. // If we don't know what initiated the request, probably it's not a tab
  86. if ( !context ) {
  87. return this.ACCEPT;
  88. }
  89. if ( location.scheme !== 'http' && location.scheme !== 'https' ) {
  90. return this.ACCEPT;
  91. }
  92. let win = type === 6
  93. ? context.contentWindow || context
  94. : (context.ownerDocument || context).defaultView;
  95. if ( win ) {
  96. getMessageManager(win).sendSyncMessage(this.messageName, {
  97. url: location.spec,
  98. type: type,
  99. frameId: type === 6 ? -1 : (win === win.top ? 0 : 1),
  100. parentFrameId: win === win.top ? -1 : 0
  101. });
  102. }
  103. return this.ACCEPT;
  104. }
  105. };
  106. /******************************************************************************/
  107. const docObserver = {
  108. contentBaseURI: 'chrome://' + appName + '/content/js/',
  109. QueryInterface: XPCOMUtils.generateQI([
  110. Ci.nsIObserver,
  111. Ci.nsISupportsWeakReference
  112. ]),
  113. initContext: function(win, sandbox) {
  114. let messager = getMessageManager(win);
  115. if (sandbox) {
  116. win = Cu.Sandbox([win], {
  117. sandboxPrototype: win,
  118. wantComponents: false,
  119. wantXHRConstructor: false
  120. });
  121. win.self = win;
  122. // anonymous function needs to be used here
  123. win.injectScript = Cu.exportFunction(
  124. function(script, evalCode) {
  125. if (evalCode) {
  126. Cu.evalInSandbox(script, win);
  127. return;
  128. }
  129. Services.scriptloader.loadSubScript(script, win);
  130. },
  131. win
  132. );
  133. }
  134. win.sendAsyncMessage = messager.sendAsyncMessage;
  135. win.addMessageListener = messager.ublock_addMessageListener;
  136. win.removeMessageListener = messager.ublock_removeMessageListener;
  137. return win;
  138. },
  139. register: function() {
  140. Services.obs.addObserver(this, 'document-element-inserted', true);
  141. },
  142. unregister: function() {
  143. Services.obs.removeObserver(this, 'document-element-inserted');
  144. },
  145. observe: function(doc) {
  146. let win = doc.defaultView;
  147. if (!win) {
  148. return;
  149. }
  150. let loc = win.location;
  151. if (loc.protocol !== 'http:' && loc.protocol !== 'https:') {
  152. if (loc.protocol === 'chrome:' && loc.host === appName) {
  153. this.initContext(win);
  154. }
  155. return;
  156. }
  157. let lss = Services.scriptloader.loadSubScript;
  158. win = this.initContext(win, true);
  159. lss(this.contentBaseURI + 'vapi-client.js', win);
  160. lss(this.contentBaseURI + 'contentscript-start.js', win);
  161. let docReady = function(e) {
  162. this.removeEventListener(e.type, docReady, true);
  163. lss(docObserver.contentBaseURI + 'contentscript-end.js', win);
  164. };
  165. win.document.addEventListener('DOMContentLoaded', docReady, true);
  166. }
  167. };
  168. /******************************************************************************/
  169. contentPolicy.register();
  170. docObserver.register();
  171. /******************************************************************************/