| OLD | NEW | 
|    1 /* |    1 /* | 
|    2  * This file is part of Adblock Plus <https://adblockplus.org/>, |    2  * This file is part of Adblock Plus <https://adblockplus.org/>, | 
|    3  * Copyright (C) 2006-2016 Eyeo GmbH |    3  * Copyright (C) 2006-2016 Eyeo GmbH | 
|    4  * |    4  * | 
|    5  * Adblock Plus is free software: you can redistribute it and/or modify |    5  * Adblock Plus is free software: you can redistribute it and/or modify | 
|    6  * it under the terms of the GNU General Public License version 3 as |    6  * it under the terms of the GNU General Public License version 3 as | 
|    7  * published by the Free Software Foundation. |    7  * published by the Free Software Foundation. | 
|    8  * |    8  * | 
|    9  * Adblock Plus is distributed in the hope that it will be useful, |    9  * Adblock Plus is distributed in the hope that it will be useful, | 
|   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of |   10  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| (...skipping 17 matching lines...) Expand all  Loading... | 
|   28     delete proto.Components; |   28     delete proto.Components; | 
|   29 } |   29 } | 
|   30 catch (e) |   30 catch (e) | 
|   31 { |   31 { | 
|   32   Cu.reportError(e); |   32   Cu.reportError(e); | 
|   33 } |   33 } | 
|   34  |   34  | 
|   35 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); |   35 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); | 
|   36  |   36  | 
|   37 let {shouldAllowAsync} = require("child/contentPolicy"); |   37 let {shouldAllowAsync} = require("child/contentPolicy"); | 
 |   38 let {port} = require("messaging"); | 
|   38 let {Utils} = require("utils"); |   39 let {Utils} = require("utils"); | 
|   39  |   40  | 
|   40 // The allowXBL binding below won't have any effect on the element. For elements |   41 // The allowXBL binding below won't have any effect on the element. For elements | 
|   41 // that should be hidden however we don't return any binding at all, this makes |   42 // that should be hidden however we don't return any binding at all, this makes | 
|   42 // Gecko stop constructing the node - it cannot be shown. |   43 // Gecko stop constructing the node - it cannot be shown. | 
|   43 const allowXBL = "<bindings xmlns='http://www.mozilla.org/xbl'><binding id='dumm
     y' bindToUntrustedContent='true'/></bindings>"; |   44 const allowXBL = "<bindings xmlns='http://www.mozilla.org/xbl'><binding id='dumm
     y' bindToUntrustedContent='true'/></bindings>"; | 
|   44 const hideXBL = "<bindings xmlns='http://www.mozilla.org/xbl'/>"; |   45 const hideXBL = "<bindings xmlns='http://www.mozilla.org/xbl'/>"; | 
|   45  |   46  | 
 |   47 const notImplemented = () => Cr.NS_ERROR_NOT_IMPLEMENTED; | 
 |   48  | 
|   46 /** |   49 /** | 
|   47  * about: URL module used to count hits. |   50  * about: URL module used to count hits. | 
|   48  * @class |   51  * @class | 
|   49  */ |   52  */ | 
|   50 let AboutHandler = |   53 let AboutHandler = | 
|   51 { |   54 { | 
|   52   classID: Components.ID("{55fb7be0-1dd2-11b2-98e6-9e97caf8ba67}"), |   55   classID: Components.ID("{55fb7be0-1dd2-11b2-98e6-9e97caf8ba67}"), | 
|   53   classDescription: "Element hiding hit registration protocol handler", |   56   classDescription: "Element hiding hit registration protocol handler", | 
|   54   aboutPrefix: "abp-elemhidehit", |   57   aboutPrefix: "abp-elemhidehit", | 
|   55  |   58  | 
| (...skipping 27 matching lines...) Expand all  Loading... | 
|   83   // About module implementation |   86   // About module implementation | 
|   84   // |   87   // | 
|   85  |   88  | 
|   86   getURIFlags: function(uri) |   89   getURIFlags: function(uri) | 
|   87   { |   90   { | 
|   88     return Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT; |   91     return Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT; | 
|   89   }, |   92   }, | 
|   90  |   93  | 
|   91   newChannel: function(uri, loadInfo) |   94   newChannel: function(uri, loadInfo) | 
|   92   { |   95   { | 
|   93     let match = /\?(\d+)/.exec(uri.path); |   96     let match = /\?(\d+|css)$/.exec(uri.path); | 
|   94     if (!match) |   97     if (!match) | 
|   95       throw Cr.NS_ERROR_FAILURE; |   98       throw Cr.NS_ERROR_FAILURE; | 
|   96  |   99  | 
|   97     return new HitRegistrationChannel(uri, loadInfo, match[1]); |  100     if (match[1] == "css") | 
 |  101       return new StyleDataChannel(uri, loadInfo); | 
 |  102     else | 
 |  103       return new HitRegistrationChannel(uri, loadInfo, match[1]); | 
|   98   }, |  104   }, | 
|   99  |  105  | 
|  100   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory, Ci.nsIAboutModule]) |  106   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory, Ci.nsIAboutModule]) | 
|  101 }; |  107 }; | 
|  102 AboutHandler.init(); |  108 AboutHandler.init(); | 
|  103  |  109  | 
|  104 /** |  110 /** | 
|  105  * Channel returning data for element hiding hits. |  111  * Base class for channel implementations, subclasses usually only need to | 
 |  112  * override BaseChannel._getResponse() method. | 
|  106  * @constructor |  113  * @constructor | 
|  107  */ |  114  */ | 
|  108 function HitRegistrationChannel(uri, loadInfo, key) |  115 function BaseChannel(uri, loadInfo) | 
|  109 { |  116 { | 
|  110   this.key = key; |  | 
|  111   this.URI = this.originalURI = uri; |  117   this.URI = this.originalURI = uri; | 
|  112   this.loadInfo = loadInfo; |  118   this.loadInfo = loadInfo; | 
|  113 } |  119 } | 
|  114 HitRegistrationChannel.prototype = { |  120 BaseChannel.prototype = { | 
|  115   key: null, |  | 
|  116   URI: null, |  121   URI: null, | 
|  117   originalURI: null, |  122   originalURI: null, | 
|  118   contentCharset: "utf-8", |  123   contentCharset: "utf-8", | 
|  119   contentLength: 0, |  124   contentLength: 0, | 
|  120   contentType: "text/xml", |  125   contentType: null, | 
|  121   owner: Utils.systemPrincipal, |  126   owner: Utils.systemPrincipal, | 
|  122   securityInfo: null, |  127   securityInfo: null, | 
|  123   notificationCallbacks: null, |  128   notificationCallbacks: null, | 
|  124   loadFlags: 0, |  129   loadFlags: 0, | 
|  125   loadGroup: null, |  130   loadGroup: null, | 
|  126   name: null, |  131   name: null, | 
|  127   status: Cr.NS_OK, |  132   status: Cr.NS_OK, | 
|  128  |  133  | 
|  129   asyncOpen: function(listener, context) |  134   _getResponse: notImplemented, | 
|  130   { |  135  | 
|  131     let processResponse = (allow) => |  136   _checkSecurity: function() | 
|  132     { |  | 
|  133       let data = (allow ? allowXBL : hideXBL); |  | 
|  134       let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci
     .nsIStringInputStream); |  | 
|  135       stream.setData(data, data.length); |  | 
|  136  |  | 
|  137       try { |  | 
|  138         listener.onStartRequest(this, context); |  | 
|  139       } catch(e) {} |  | 
|  140       try { |  | 
|  141         listener.onDataAvailable(this, context, stream, 0, stream.available()); |  | 
|  142       } catch(e) {} |  | 
|  143       try { |  | 
|  144         listener.onStopRequest(this, context, Cr.NS_OK); |  | 
|  145       } catch(e) {} |  | 
|  146     }; |  | 
|  147  |  | 
|  148     let window = Utils.getRequestWindow(this); |  | 
|  149     shouldAllowAsync(window, window.document, "ELEMHIDE", this.key, processRespo
     nse); |  | 
|  150   }, |  | 
|  151  |  | 
|  152   asyncOpen2: function(listener) |  | 
|  153   { |  137   { | 
|  154     if (!this.loadInfo.triggeringPrincipal.equals(Utils.systemPrincipal)) |  138     if (!this.loadInfo.triggeringPrincipal.equals(Utils.systemPrincipal)) | 
|  155       throw Cr.NS_ERROR_FAILURE; |  139       throw Cr.NS_ERROR_FAILURE; | 
 |  140   }, | 
 |  141  | 
 |  142   asyncOpen: function() | 
 |  143   { | 
 |  144     Promise.resolve(this._getResponse()).then(data => | 
 |  145     { | 
 |  146       let stream = Cc["@mozilla.org/io/string-input-stream;1"] | 
 |  147                      .createInstance(Ci.nsIStringInputStream); | 
 |  148       stream.setData(data, data.length); | 
 |  149  | 
 |  150       try | 
 |  151       { | 
 |  152         listener.onStartRequest(this, context); | 
 |  153       } | 
 |  154       catch(e) | 
 |  155       { | 
 |  156         // Listener failing isn't our problem | 
 |  157       } | 
 |  158  | 
 |  159       try | 
 |  160       { | 
 |  161         listener.onDataAvailable(this, context, stream, 0, stream.available()); | 
 |  162       } | 
 |  163       catch(e) | 
 |  164       { | 
 |  165         // Listener failing isn't our problem | 
 |  166       } | 
 |  167  | 
 |  168       try | 
 |  169       { | 
 |  170         listener.onStopRequest(this, context, Cr.NS_OK); | 
 |  171       } | 
 |  172       catch(e) | 
 |  173       { | 
 |  174         // Listener failing isn't our problem | 
 |  175       } | 
 |  176     }); | 
 |  177   }, | 
 |  178  | 
 |  179   asyncOpen2: function(listener) | 
 |  180   { | 
 |  181     this._checkSecurity(); | 
|  156     this.asyncOpen(listener, null); |  182     this.asyncOpen(listener, null); | 
|  157   }, |  183   }, | 
|  158  |  184  | 
|  159   open: function() |  185   open: function() | 
|  160   { |  186   { | 
|  161     throw Cr.NS_ERROR_NOT_IMPLEMENTED; |  187     let data = this._getResponse(); | 
|  162   }, |  188     if (typeof data.then == "function") | 
|  163   isPending: function() |  189       throw Cr.NS_ERROR_NOT_IMPLEMENTED; | 
|  164   { |  190  | 
|  165     return false; |  191     let stream = Cc["@mozilla.org/io/string-input-stream;1"] | 
|  166   }, |  192                    .createInstance(Ci.nsIStringInputStream); | 
|  167   cancel: function() |  193     stream.setData(data, data.length); | 
|  168   { |  194     return stream; | 
|  169     throw Cr.NS_ERROR_NOT_IMPLEMENTED; |  195   }, | 
|  170   }, |  196  | 
|  171   suspend: function() |  197   open2: function() | 
|  172   { |  198   { | 
|  173     throw Cr.NS_ERROR_NOT_IMPLEMENTED; |  199     this._checkSecurity(); | 
|  174   }, |  200     return this.open(); | 
|  175   resume: function() |  201   }, | 
|  176   { |  202  | 
|  177     throw Cr.NS_ERROR_NOT_IMPLEMENTED; |  203   isPending: () => false, | 
|  178   }, |  204   cancel: notImplemented, | 
 |  205   suspend: notImplemented, | 
 |  206   resume: notImplemented, | 
|  179  |  207  | 
|  180   QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest]) |  208   QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest]) | 
|  181 }; |  209 }; | 
 |  210  | 
 |  211 /** | 
 |  212  * Channel returning CSS data for the global stylesheet. | 
 |  213  * @constructor | 
 |  214  */ | 
 |  215 function StyleDataChannel(uri, loadInfo) | 
 |  216 { | 
 |  217   BaseChannel.call(this, uri, loadInfo); | 
 |  218 } | 
 |  219 StyleDataChannel.prototype = { | 
 |  220   __proto__: BaseChannel.prototype, | 
 |  221   contentType: "text/css", | 
 |  222  | 
 |  223   _getResponse: function() | 
 |  224   { | 
 |  225     function escapeChar(match) | 
 |  226     { | 
 |  227       return "\\" + match.charCodeAt(0).toString(16) + " "; | 
 |  228     } | 
 |  229  | 
 |  230     // Would be great to avoid sync messaging here but nsIStyleSheetService | 
 |  231     // insists on opening channels synchronously. | 
 |  232     let domains = port.emitSync("getSelectors"); | 
 |  233  | 
 |  234     let cssPrefix = "{-moz-binding: url(about:abp-elemhidehit?"; | 
 |  235     let cssSuffix = "#dummy) !important;}\n"; | 
 |  236     let result = []; | 
 |  237  | 
 |  238     for (let [domain, selectors] of domains) | 
 |  239     { | 
 |  240       if (domain) | 
 |  241       { | 
 |  242         result.push('@-moz-document domain("', | 
 |  243             domain.replace(/[^\x01-\x7F]/g, escapeChar) | 
 |  244                   .split(",").join('"),domain("'), | 
 |  245             '"){\n'); | 
 |  246       } | 
 |  247       else | 
 |  248       { | 
 |  249         // Only allow unqualified rules on a few protocols to prevent them | 
 |  250         // from blocking chrome content | 
 |  251         result.push('@-moz-document url-prefix("http://"),', | 
 |  252             'url-prefix("https://"),url-prefix("mailbox://"),', | 
 |  253             'url-prefix("imap://"),url-prefix("news://"),', | 
 |  254             'url-prefix("snews://"){\n'); | 
 |  255       } | 
 |  256  | 
 |  257       for (let [selector, key] of selectors) | 
 |  258       { | 
 |  259         result.push(selector.replace(/[^\x01-\x7F]/g, escapeChar), | 
 |  260             cssPrefix, key, cssSuffix); | 
 |  261       } | 
 |  262  | 
 |  263       result.push("}\n"); | 
 |  264     } | 
 |  265  | 
 |  266     return result.join(""); | 
 |  267   } | 
 |  268 }; | 
 |  269  | 
 |  270 /** | 
 |  271  * Channel returning data for element hiding hits. | 
 |  272  * @constructor | 
 |  273  */ | 
 |  274 function HitRegistrationChannel(uri, loadInfo, key) | 
 |  275 { | 
 |  276   BaseChannel.call(this, uri, loadInfo); | 
 |  277   this.key = key; | 
 |  278 } | 
 |  279 HitRegistrationChannel.prototype = { | 
 |  280   __proto__: BaseChannel.prototype, | 
 |  281   key: null, | 
 |  282   contentType: "text/xml", | 
 |  283  | 
 |  284   _getResponse: function() | 
 |  285   { | 
 |  286     return new Promise((resolve, reject) => | 
 |  287     { | 
 |  288       let window = Utils.getRequestWindow(this); | 
 |  289       shouldAllowAsync(window, window.document, "ELEMHIDE", this.key, allow => | 
 |  290       { | 
 |  291         resolve(allow ? allowXBL : hideXBL); | 
 |  292       }); | 
 |  293     }); | 
 |  294   } | 
 |  295 }; | 
| OLD | NEW |