Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: lib/contentPolicy.js

Issue 6337686776315904: Issue 394 - hit statistics tool data collection (Closed)
Left Patch Set: Rebase to changeset: 4095 Created Dec. 15, 2015, 6:09 p.m.
Right Patch Set: Use Downloader to send the data to server Created April 6, 2016, 3 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
LEFTRIGHT
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-2015 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
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 /** 18 /**
19 * @fileOverview Content policy implementation, responsible for blocking things. 19 * @fileOverview Content policy implementation, responsible for blocking things.
20 */ 20 */
21 21
22 "use strict"; 22 "use strict";
23 23
24 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {}); 24 let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
25 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); 25 let {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
26 26
27 let {Utils} = require("utils"); 27 let {Utils} = require("utils");
28 let {port} = require("messaging");
28 let {Prefs} = require("prefs"); 29 let {Prefs} = require("prefs");
29 let {FilterStorage} = require("filterStorage"); 30 let {FilterStorage} = require("filterStorage");
30 let {BlockingFilter, WhitelistFilter, RegExpFilter} = require("filterClasses"); 31 let {BlockingFilter, WhitelistFilter, RegExpFilter} = require("filterClasses");
31 let {defaultMatcher} = require("matcher"); 32 let {defaultMatcher} = require("matcher");
32 let {ElemHide} = require("elemHide"); 33 let {ElemHide} = require("elemHide");
33 34
34 /** 35 /**
35 * Public policy checking functions and auxiliary objects 36 * Public policy checking functions and auxiliary objects
36 * @class 37 * @class
37 */ 38 */
38 var Policy = exports.Policy = 39 var Policy = exports.Policy =
39 { 40 {
40 /** 41 /**
41 * Set of explicitly supported content types 42 * Map of content types reported by Firefox to the respecitve content types
42 * @type Set.<string> 43 * used by Adblock Plus. Other content types are simply mapped to OTHER.
43 */ 44 * @type Map.<string,string>
44 contentTypes: new Set([ 45 */
45 "OTHER", "SCRIPT", "IMAGE", "STYLESHEET", "OBJECT", "SUBDOCUMENT", "DOCUMENT ", 46 contentTypes: new Map(function* ()
46 "XMLHTTPREQUEST", "OBJECT_SUBREQUEST", "FONT", "MEDIA", "ELEMHIDE", "POPUP", 47 {
47 "GENERICHIDE", "GENERICBLOCK" 48 // Treat navigator.sendBeacon() the same as <a ping>,
48 ]), 49 // it's essentially the same concept - merely generalized.
50 yield ["BEACON", "PING"];
51
52 // Treat <img srcset> and <picture> the same as other images.
53 yield ["IMAGESET", "IMAGE"];
54
55 // Treat fetch() the same as XMLHttpRequest,
56 // it's essentially the same - merely a more modern API.
57 yield ["FETCH", "XMLHTTPREQUEST"];
58
59 // Everything else is mapped to itself
60 for (let contentType of ["OTHER", "SCRIPT", "IMAGE", "STYLESHEET", "OBJECT",
61 "SUBDOCUMENT", "DOCUMENT", "XMLHTTPREQUEST",
62 "OBJECT_SUBREQUEST", "FONT", "MEDIA", "PING",
63 "ELEMHIDE", "POPUP", "GENERICHIDE", "GENERICBLOCK"] )
64 yield [contentType, contentType];
65 }()),
49 66
50 /** 67 /**
51 * Set of content types that aren't associated with a visual document area 68 * Set of content types that aren't associated with a visual document area
52 * @type Set.<string> 69 * @type Set.<string>
53 */ 70 */
54 nonVisualTypes: new Set([ 71 nonVisualTypes: new Set([
55 "SCRIPT", "STYLESHEET", "XMLHTTPREQUEST", "OBJECT_SUBREQUEST", "FONT", 72 "SCRIPT", "STYLESHEET", "XMLHTTPREQUEST", "OBJECT_SUBREQUEST", "FONT",
56 "ELEMHIDE", "POPUP", "GENERICHIDE", "GENERICBLOCK" 73 "PING", "ELEMHIDE", "POPUP", "GENERICHIDE", "GENERICBLOCK"
57 ]), 74 ]),
58 75
59 /** 76 /**
60 * Map containing all schemes that should be ignored by content policy. 77 * Map containing all schemes that should be ignored by content policy.
61 * @type Set.<string> 78 * @type Set.<string>
62 */ 79 */
63 whitelistSchemes: new Set(), 80 whitelistSchemes: new Set(),
64 81
65 /** 82 /**
66 * Called on module startup, initializes various exported properties. 83 * Called on module startup, initializes various exported properties.
67 */ 84 */
68 init: function() 85 init: function()
69 { 86 {
70 // whitelisted URL schemes 87 // whitelisted URL schemes
71 for (let scheme of Prefs.whitelistschemes.toLowerCase().split(" ")) 88 for (let scheme of Prefs.whitelistschemes.toLowerCase().split(" "))
72 this.whitelistSchemes.add(scheme); 89 this.whitelistSchemes.add(scheme);
73 90
74 Utils.addChildMessageListener("AdblockPlus:ShouldAllow", message => this.sho uldAllow(message)); 91 port.on("shouldAllow", (message, sender) => this.shouldAllow(message));
75 92
76 // Generate class identifier used to collapse nodes and register 93 // Generate class identifier used to collapse nodes and register
77 // corresponding stylesheet. 94 // corresponding stylesheet.
78 let collapsedClass = ""; 95 let collapsedClass = "";
79 let offset = "a".charCodeAt(0); 96 let offset = "a".charCodeAt(0);
80 for (let i = 0; i < 20; i++) 97 for (let i = 0; i < 20; i++)
81 collapsedClass += String.fromCharCode(offset + Math.random() * 26); 98 collapsedClass += String.fromCharCode(offset + Math.random() * 26);
82 Utils.addChildMessageListener("AdblockPlus:GetCollapsedClass", () => collaps edClass); 99 port.on("getCollapsedClass", (message, sender) => collapsedClass);
83 100
84 let collapseStyle = Services.io.newURI("data:text/css," + 101 let collapseStyle = Services.io.newURI("data:text/css," +
85 encodeURIComponent("." + collapsedClass + 102 encodeURIComponent("." + collapsedClass +
86 "{-moz-binding: url(chrome://global/content/bindings/general.xml#foobarb azdummy) !important;}"), null, null); 103 "{-moz-binding: url(chrome://global/content/bindings/general.xml#foobarb azdummy) !important;}"), null, null);
87 Utils.styleService.loadAndRegisterSheet(collapseStyle, Ci.nsIStyleSheetServi ce.USER_SHEET); 104 Utils.styleService.loadAndRegisterSheet(collapseStyle, Ci.nsIStyleSheetServi ce.USER_SHEET);
88 onShutdown.add(() => 105 onShutdown.add(() =>
89 { 106 {
90 Utils.styleService.unregisterSheet(collapseStyle, Ci.nsIStyleSheetService. USER_SHEET); 107 Utils.styleService.unregisterSheet(collapseStyle, Ci.nsIStyleSheetService. USER_SHEET);
91 }); 108 });
92 }, 109 },
93 110
94 /** 111 /**
95 * Checks whether a node should be blocked, hides it if necessary 112 * Checks whether a node should be blocked, hides it if necessary
96 * @param {Object} data request data 113 * @param {Object} data request data
97 * @param {String} data.contentType 114 * @param {String} data.contentType
98 * @param {String} data.location location of the request, filter key if conte ntType is ELEMHIDE 115 * @param {String} data.location location of the request, filter key if conte ntType is ELEMHIDE
99 * @param {Object[]} data.frames 116 * @param {Object[]} data.frames
100 * @param {Boolean} data.isPrivate true if the request belongs to a private b rowsing window 117 * @param {Boolean} data.isPrivate true if the request belongs to a private b rowsing window
101 * @return {Object} An object containing properties allow, collapse and hits 118 * @return {Object} An object containing properties allow, collapse and hits
102 * indicating how this request should be handled. 119 * indicating how this request should be handled.
103 */ 120 */
104 shouldAllow: function({contentType, location, frames, isPrivate}) 121 shouldAllow: function({contentType, location, frames, isPrivate})
105 { 122 {
106 let hits = []; 123 let hits = [];
107 124
108 function addHit(frameIndex, contentType, docDomain, thirdParty, location, fi lter) 125 function addHit(frameIndex, contentType, docDomain, thirdParty, location, fi lter)
109 { 126 {
110 if (filter && !isPrivate) 127 if (filter && !isPrivate)
111 { 128 FilterStorage.increaseHitCount(filter, docDomain, thirdParty);
112 let topWindowLocation = frames[frames.length-1].location; 129
113 FilterStorage.increaseHitCount(filter, getHostname(topWindowLocation));
Wladimir Palant 2016/02/15 12:25:40 getHostname(topWindowLocation) should be identical
saroyanm 2016/02/19 17:38:11 Done.
114 }
115 hits.push({ 130 hits.push({
116 frameIndex, contentType, docDomain, thirdParty, location, 131 frameIndex, contentType, docDomain, thirdParty, location,
117 filter: filter ? filter.text : null, 132 filter: filter ? filter.text : null,
118 filterType: filter ? filter.type : null 133 filterType: filter ? filter.type : null
119 }); 134 });
120 } 135 }
121 136
122 function response(allow, collapse) 137 function response(allow, collapse)
123 { 138 {
124 return {allow, collapse, hits}; 139 return {allow, collapse, hits};
125 } 140 }
126 141
127 // Ignore whitelisted schemes 142 // Ignore whitelisted schemes
128 if (!this.isBlockableScheme(location)) 143 if (!this.isBlockableScheme(location))
129 return response(true, false); 144 return response(true, false);
130 145
131 // Interpret unknown types as "other" 146 // Interpret unknown types as "other"
132 if (!this.contentTypes.has(contentType)) 147 contentType = this.contentTypes.get(contentType) || "OTHER";
133 contentType = "OTHER"; 148
134 149 let nogeneric = false;
150 if (Prefs.enabled)
151 {
152 let whitelistHit =
153 this.isFrameWhitelisted(frames, contentType == "ELEMHIDE");
154 if (whitelistHit)
155 {
156 let [frameIndex, matchType, docDomain, thirdParty, location, filter] = w hitelistHit;
157 addHit(frameIndex, matchType, docDomain, thirdParty, location, filter);
158 if (matchType == "DOCUMENT" || matchType == "ELEMHIDE")
159 return response(true, false);
160 else
161 nogeneric = true;
162 }
163 }
164
165 let match = null;
135 let wndLocation = frames[0].location; 166 let wndLocation = frames[0].location;
136 let docDomain = getHostname(wndLocation); 167 let docDomain = getHostname(wndLocation);
137 let match = null;
138 let [sitekey, sitekeyFrame] = getSitekey(frames); 168 let [sitekey, sitekeyFrame] = getSitekey(frames);
139 let nogeneric = false; 169 if (contentType == "ELEMHIDE")
140 if (!match && Prefs.enabled)
141 {
142 let testSitekey = sitekey;
143 let testSitekeyFrame = sitekeyFrame;
144 for (let i = 0; i < frames.length; i++)
145 {
146 let frame = frames[i];
147 let testWndLocation = frame.location;
148 let parentWndLocation = frames[Math.min(i + 1, frames.length - 1)].locat ion;
149 let parentDocDomain = getHostname(parentWndLocation);
150
151 let typeMap = RegExpFilter.typeMap.DOCUMENT;
152 if (contentType == "ELEMHIDE")
153 typeMap = typeMap | RegExpFilter.typeMap.ELEMHIDE;
154 let whitelistMatch = defaultMatcher.matchesAny(testWndLocation, typeMap, parentDocDomain, false, testSitekey);
155 if (whitelistMatch instanceof WhitelistFilter)
156 {
157 let whitelistType = (whitelistMatch.contentType & RegExpFilter.typeMap .DOCUMENT) ? "DOCUMENT" : "ELEMHIDE";
158 addHit(i, whitelistType, parentDocDomain, false, testWndLocation,
159 whitelistMatch);
160 return response(true, false);
161 }
162
163 let genericType = (contentType == "ELEMHIDE" ? "GENERICHIDE" : "GENERICB LOCK");
164 let nogenericMatch = defaultMatcher.matchesAny(testWndLocation,
165 RegExpFilter.typeMap[genericType], parentDocDomain, false, testSitek ey);
166 if (nogenericMatch instanceof WhitelistFilter)
167 {
168 nogeneric = true;
169 addHit(i, genericType, parentDocDomain, false, testWndLocation,
170 nogenericMatch);
171 }
172
173 if (frame == testSitekeyFrame)
174 [testSitekey, testSitekeyFrame] = getSitekey(frames.slice(i + 1));
175 }
176 }
177
178 if (!match && contentType == "ELEMHIDE")
179 { 170 {
180 match = ElemHide.getFilterByKey(location); 171 match = ElemHide.getFilterByKey(location);
181 location = match.text.replace(/^.*?#/, '#'); 172 location = match.text.replace(/^.*?#/, '#');
182 173
183 if (!match.isActiveOnDomain(docDomain)) 174 if (!match.isActiveOnDomain(docDomain))
184 return response(true, false); 175 return response(true, false);
185 176
186 let exception = ElemHide.getException(match, docDomain); 177 let exception = ElemHide.getException(match, docDomain);
187 if (exception) 178 if (exception)
188 { 179 {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 { 211 {
221 let match = /^([\w\-]+):/.exec(location); 212 let match = /^([\w\-]+):/.exec(location);
222 scheme = match ? match[1] : null; 213 scheme = match ? match[1] : null;
223 } 214 }
224 else 215 else
225 scheme = location.scheme; 216 scheme = location.scheme;
226 return !this.whitelistSchemes.has(scheme); 217 return !this.whitelistSchemes.has(scheme);
227 }, 218 },
228 219
229 /** 220 /**
230 * Checks whether a page is whitelisted. 221 * Checks whether a top-level window is whitelisted.
231 * @param {String} url 222 * @param {String} url
232 * @param {String} [parentUrl] location of the parent page 223 * URL of the document loaded into the window
233 * @param {String} [sitekey] public key provided on the page 224 * @return {?WhitelistFilter}
234 * @return {Filter} filter that matched the URL or null if not whitelisted 225 * exception rule that matched the URL if any
235 */ 226 */
236 isWhitelisted: function(url, parentUrl, sitekey) 227 isWhitelisted: function(url)
237 { 228 {
238 if (!url) 229 if (!url)
239 return null; 230 return null;
240 231
241 // Do not apply exception rules to schemes on our whitelistschemes list. 232 // Do not apply exception rules to schemes on our whitelistschemes list.
242 if (!this.isBlockableScheme(url)) 233 if (!this.isBlockableScheme(url))
243 return null; 234 return null;
244
245 if (!parentUrl)
246 parentUrl = url;
247 235
248 // Ignore fragment identifier 236 // Ignore fragment identifier
249 let index = url.indexOf("#"); 237 let index = url.indexOf("#");
250 if (index >= 0) 238 if (index >= 0)
251 url = url.substring(0, index); 239 url = url.substring(0, index);
252 240
253 let result = defaultMatcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT, g etHostname(parentUrl), false, sitekey); 241 let result = defaultMatcher.matchesAny(url, RegExpFilter.typeMap.DOCUMENT,
242 getHostname(url), false, null);
254 return (result instanceof WhitelistFilter ? result : null); 243 return (result instanceof WhitelistFilter ? result : null);
255 }, 244 },
256 245
257 /** 246 /**
258 * Checks whether the page loaded in a window is whitelisted for indication in the UI. 247 * Checks whether a frame is whitelisted.
259 * @param wnd {nsIDOMWindow} 248 * @param {Array} frames
260 * @return {Filter} matching exception rule or null if not whitelisted 249 * frame structure as returned by getFrames() in child/utils module.
261 */ 250 * @param {boolean} isElemHide
262 isWindowWhitelisted: function(wnd) 251 * true if element hiding whitelisting should be considered
263 { 252 * @return {?Array}
264 return this.isWhitelisted(getWindowLocation(wnd)); 253 * An array with the hit parameters: frameIndex, contentType, docDomain,
254 * thirdParty, location, filter. Note that the filter could be a
255 * genericblock/generichide exception rule. If nothing matched null is
256 * returned.
257 */
258 isFrameWhitelisted: function(frames, isElemHide)
259 {
260 let [sitekey, sitekeyFrame] = getSitekey(frames);
261 let nogenericHit = null;
262
263 let typeMap = RegExpFilter.typeMap.DOCUMENT;
264 if (isElemHide)
265 typeMap = typeMap | RegExpFilter.typeMap.ELEMHIDE;
266 let genericType = (isElemHide ? "GENERICHIDE" : "GENERICBLOCK");
267
268 for (let i = 0; i < frames.length; i++)
269 {
270 let frame = frames[i];
271 let wndLocation = frame.location;
272 let parentWndLocation = frames[Math.min(i + 1, frames.length - 1)].locatio n;
273 let parentDocDomain = getHostname(parentWndLocation);
274
275 let match = defaultMatcher.matchesAny(wndLocation, typeMap, parentDocDomai n, false, sitekey);
276 if (match instanceof WhitelistFilter)
277 {
278 let whitelistType = (whitelistMatch.contentType & RegExpFilter.typeMap.D OCUMENT) ? "DOCUMENT" : "ELEMHIDE";
279 return [i, whitelistType, parentDocDomain, false, wndLocation, match];
280 }
281
282 if (!nogenericHit)
283 {
284 match = defaultMatcher.matchesAny(wndLocation,
285 RegExpFilter.typeMap[genericType], parentDocDomain, false, sitekey);
286 if (match instanceof WhitelistFilter)
287 nogenericHit = [i, genericType, parentDocDomain, false, wndLocation, m atch];
288 }
289
290 if (frame == sitekeyFrame)
291 [sitekey, sitekeyFrame] = getSitekey(frames.slice(i + 1));
292 }
293
294 return nogenericHit;
265 }, 295 },
266 296
267 /** 297 /**
268 * Deletes nodes that were previously stored with a 298 * Deletes nodes that were previously stored with a
269 * RequestNotifier.storeNodesForEntries() call or similar. 299 * RequestNotifier.storeNodesForEntries() call or similar.
270 * @param {string} id unique ID of the nodes 300 * @param {string} id unique ID of the nodes
271 */ 301 */
272 deleteNodes: function(id) 302 deleteNodes: function(id)
273 { 303 {
274 let messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"] 304 port.emit("deleteNodes", id);
275 .getService(Ci.nsIMessageBroadcaster);
276 messageManager.broadcastAsyncMessage("AdblockPlus:DeleteNodes", id);
277 }, 305 },
278 306
279 /** 307 /**
280 * Asynchronously re-checks filters for nodes given by an ID previously 308 * Asynchronously re-checks filters for nodes given by an ID previously
281 * returned by a RequestNotifier.storeNodesForEntries() call or similar. 309 * returned by a RequestNotifier.storeNodesForEntries() call or similar.
282 * @param {string} id unique ID of the nodes 310 * @param {string} id unique ID of the nodes
283 * @param {RequestEntry} entry 311 * @param {RequestEntry} entry
284 */ 312 */
285 refilterNodes: function(id, entry) 313 refilterNodes: function(id, entry)
286 { 314 {
287 let messageManager = Cc["@mozilla.org/parentprocessmessagemanager;1"] 315 port.emit("refilterNodes", {
288 .getService(Ci.nsIMessageBroadcaster);
289 messageManager.broadcastAsyncMessage("AdblockPlus:RefilterNodes", {
290 nodesID: id, 316 nodesID: id,
291 entry: entry 317 entry: entry
292 }); 318 });
293 } 319 }
294 }; 320 };
295 Policy.init(); 321 Policy.init();
296 322
297 /** 323 /**
298 * Extracts the hostname from a URL (might return null). 324 * Extracts the hostname from a URL (might return null).
299 */ 325 */
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
398 { 424 {
399 // EffectiveTLDService throws on IP addresses, just compare the host name 425 // EffectiveTLDService throws on IP addresses, just compare the host name
400 let host = ""; 426 let host = "";
401 try 427 try
402 { 428 {
403 host = uri.host; 429 host = uri.host;
404 } catch (e) {} 430 } catch (e) {}
405 return host != docDomain; 431 return host != docDomain;
406 } 432 }
407 } 433 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld