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.

191 lines
5.6 KiB

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