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.

169 lines
5.1 KiB

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