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 |