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

Delta Between Two Patch Sets: lib/crawler.js

Issue 5288886037118976: Adblock Plus Crawler rewrite (Closed)
Left Patch Set: Created April 24, 2015, 3:38 p.m.
Right Patch Set: Addressed comments Created May 7, 2015, 12:04 a.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 Source Code is subject to the terms of the Mozilla Public License 2 * This Source Code is subject to the terms of the Mozilla Public License
3 * version 2.0 (the "License"). You can obtain a copy of the License at 3 * version 2.0 (the "License"). You can obtain a copy of the License at
4 * http://mozilla.org/MPL/2.0/. 4 * http://mozilla.org/MPL/2.0/.
5 */ 5 */
6 6
7 /** 7 /**
8 * @module crawler 8 * @module crawler
saroyanm 2015/05/04 18:13:43 I think this should be file overview, maybe smth l
Wladimir Palant 2015/05/07 00:04:59 As Sebastian noted elsewhere, @module is actually
9 */ 9 */
10 10
11 Cu.import("resource://gre/modules/Services.jsm"); 11 Cu.import("resource://gre/modules/Services.jsm");
12 Cu.import("resource://gre/modules/Task.jsm"); 12 Cu.import("resource://gre/modules/Task.jsm");
13 Cu.import("resource://gre/modules/Promise.jsm"); 13 Cu.import("resource://gre/modules/Promise.jsm");
14
15 let {reportException} = require("debug");
14 16
15 function abprequire(module) 17 function abprequire(module)
16 { 18 {
17 let result = {}; 19 let result = {};
saroyanm 2015/05/04 18:13:43 We can create here Object without Object prototype
Sebastian Noack 2015/05/04 20:39:01 It seems that we don't need to bother about confli
Wladimir Palant 2015/05/07 00:04:59 Yes, this isn't a place where property conflicts c
18 result.wrappedJSObject = result; 20 result.wrappedJSObject = result;
19 Services.obs.notifyObservers(result, "adblockplus-require", module); 21 Services.obs.notifyObservers(result, "adblockplus-require", module);
20 return result.exports; 22 return result.exports;
21 } 23 }
22 24
23 let {Policy} = abprequire("contentPolicy"); 25 let {Policy} = abprequire("contentPolicy");
24 let {RequestNotifier} = abprequire("requestNotifier"); 26 let {RequestNotifier} = abprequire("requestNotifier");
25 let {Utils} = abprequire("utils"); 27 let {Utils} = abprequire("utils");
26 28
27 let dataForTab = new WeakMap(); 29 let dataForTab = new WeakMap();
28 30
29 /** 31 /**
30 * Creates a pool of tabs and allocates them to tasks on request. 32 * Creates a pool of tabs and allocates them to tasks on request.
31 * 33 *
32 * @param {tabbrowser} browser 34 * @param {tabbrowser} browser
33 * The tabbed browser where tabs should be created 35 * The tabbed browser where tabs should be created
34 * @param {int} maxtabs 36 * @param {int} maxtabs
35 * The maximum number of tabs to be allocated 37 * The maximum number of tabs to be allocated
36 * @constructor 38 * @constructor
37 */ 39 */
38 function TabAllocator(browser, maxtabs) 40 function TabAllocator(browser, maxtabs)
saroyanm 2015/05/04 18:13:43 Why don't we use default amount of maxtab equal to
Wladimir Palant 2015/05/07 00:04:59 Because the text file has 50 thousand URLs.
39 { 41 {
40 browser.removeAllTabsBut(browser.tabs[0]) 42 browser.removeAllTabsBut(browser.tabs[0])
41 43
42 this._tabs = []; 44 this._tabs = [];
43 for (let i = 0; i < maxtabs; i++) 45 for (let i = 0; i < maxtabs; i++)
44 this._tabs.push(browser.addTab("about:blank")); 46 this._tabs.push(browser.addTab("about:blank"));
45 47
46 browser.removeTab(browser.tabs[0]); 48 browser.removeTab(browser.tabs[0]);
47 49
48 this._deferred = []; 50 this._queue = [];
49 } 51 }
50 TabAllocator.prototype = { 52 TabAllocator.prototype = {
51 /** 53 /**
52 * Returns a promise that will resolve into a tab once a tab can be allocated. 54 * Returns a promise that will resolve into a tab once a tab can be allocated.
53 * The tab cannot be used by other tasks until releaseTab() is called. 55 * The tab cannot be used by other tasks until releaseTab() is called.
54 * 56 *
55 * @result {Promise} 57 * @return {Promise}
saroyanm 2015/05/04 18:13:43 Nit: @return ?
Wladimir Palant 2015/05/07 00:04:59 Done.
56 */ 58 */
57 getTab: function() 59 getTab: function()
58 { 60 {
59 if (this._tabs.length) 61 if (this._tabs.length)
60 return this._tabs.shift(); 62 return this._tabs.shift();
61 else 63 else
62 { 64 return new Promise((resolve, reject) => this._queue.push(resolve));
63 let deferred = Promise.defer();
saroyanm 2015/05/04 18:13:43 The Deffered object is obsolete starting from Geck
Wladimir Palant 2015/05/07 00:04:59 Promise constructor in Promise.jsm is actually sup
64 this._deferred.push(deferred);
65 return deferred.promise;
66 }
67 }, 65 },
68 66
69 /** 67 /**
70 * Adds a tab back to the pool so that it can be used by other tasks. 68 * Adds a tab back to the pool so that it can be used by other tasks.
71 * 69 *
72 * @param {tab} tab 70 * @param {tab} tab
73 */ 71 */
74 releaseTab: function(tab) 72 releaseTab: function(tab)
75 { 73 {
76 let browser = tab.parentNode.tabbrowser; 74 let browser = tab.parentNode.tabbrowser;
77 browser.removeTab(tab); 75 browser.removeTab(tab);
78 tab = browser.addTab("about:blank"); 76 tab = browser.addTab("about:blank");
79 77
80 if (this._deferred.length) 78 if (this._queue.length)
81 this._deferred.shift().resolve(tab); 79 this._queue.shift()(tab);
saroyanm 2015/05/04 18:13:43 As mentioned above, the Deffered object is obsolet
Wladimir Palant 2015/05/07 00:04:59 Done.
82 else 80 else
83 this._tabs.push(tab); 81 this._tabs.push(tab);
84 } 82 }
85 }; 83 };
86 84
87 /** 85 /**
88 * Observes page loads in a particular tabbed browser. 86 * Observes page loads in a particular tabbed browser.
89 * 87 *
90 * @param {tabbrowser} browser 88 * @param {tabbrowser} browser
91 * The tabbed browser to be observed 89 * The tabbed browser to be observed
92 * @param {int} timeout 90 * @param {int} timeout
93 * Load timeout in milliseconds 91 * Load timeout in milliseconds
94 * @constructor 92 * @constructor
95 */ 93 */
96 function LoadListener(browser, timeout) 94 function LoadListener(browser, timeout)
97 { 95 {
98 this._browser = browser; 96 this._browser = browser;
99 this._deferred = new Map(); 97 this._queue = new Map();
100 this._timeout = timeout; 98 this._timeout = timeout;
101 browser.addTabsProgressListener(this); 99 browser.addTabsProgressListener(this);
102 } 100 }
103 LoadListener.prototype = { 101 LoadListener.prototype = {
104 /** 102 /**
105 * Returns a promise that will be resolved when the page in the specified tab 103 * Returns a promise that will be resolved when the page in the specified tab
106 * finishes loading. Loading will be stopped if the timeout is reached. 104 * finishes loading. Loading will be stopped if the timeout is reached.
107 * 105 *
108 * @param {tab} tab 106 * @param {tab} tab
109 * @result {Promise} 107 * @return {Promise}
saroyanm 2015/05/04 18:13:43 Nit: @return
Wladimir Palant 2015/05/07 00:04:59 Done.
110 */ 108 */
111 waitForLoad: function(tab) 109 waitForLoad: function(tab)
112 { 110 {
113 let deferred = Promise.defer(); 111 return new Promise((resolve, reject) =>
saroyanm 2015/05/04 18:13:43 As mentioned above this is Obsolete, this comment
Wladimir Palant 2015/05/07 00:04:59 Done.
114 this._deferred.set(tab.linkedBrowser, deferred); 112 {
115 113 this._queue.set(tab.linkedBrowser, resolve);
116 tab.ownerDocument.defaultView.setTimeout(function() 114
117 { 115 tab.ownerDocument.defaultView.setTimeout(function()
118 tab.linkedBrowser.stop(); 116 {
119 }, this._timeout); 117 tab.linkedBrowser.stop();
120 118 }, this._timeout);
121 return deferred.promise; 119 });
122 }, 120 },
123 121
124 /** 122 /**
125 * Deactivates this object. 123 * Deactivates this object.
126 */ 124 */
127 stop: function() 125 stop: function()
128 { 126 {
129 this._browser.removeTabsProgressListener(this); 127 this._browser.removeTabsProgressListener(this);
130 }, 128 },
131 129
132 onStateChange: function(browser, progress, request, flags, status) 130 onStateChange: function(browser, progress, request, flags, status)
133 { 131 {
134 if ((flags & Ci.nsIWebProgressListener.STATE_STOP) && (flags & Ci.nsIWebProg ressListener.STATE_IS_WINDOW)) 132 if ((flags & Ci.nsIWebProgressListener.STATE_STOP) &&
saroyanm 2015/05/04 18:13:43 Nit: This case can be splitted into multiple lines
saroyanm 2015/05/07 13:19:00 Nit: We usually also move operators to new line, f
135 { 133 (flags & Ci.nsIWebProgressListener.STATE_IS_WINDOW))
136 let deferred = this._deferred.get(browser); 134 {
137 if (deferred) 135 let resolve = this._queue.get(browser);
136 if (resolve)
138 { 137 {
139 this._deferred.delete(browser); 138 this._queue.delete(browser);
140 139
141 let headers = []; 140 let headers = [];
142 if (request instanceof Ci.nsIHttpChannel) 141 if (request instanceof Ci.nsIHttpChannel)
143 { 142 {
144 try 143 try
145 { 144 {
146 headers.push("HTTP/x.x " + request.responseStatus + " " + request.re sponseStatusText); 145 headers.push("HTTP/x.x " + request.responseStatus + " " + request.re sponseStatusText);
147 request.visitResponseHeaders((header, value) => headers.push(header + ": " + value)); 146 request.visitResponseHeaders((header, value) => headers.push(header + ": " + value));
148 } 147 }
149 catch (e) 148 catch (e)
150 { 149 {
151 // Exceptions are expected here 150 // Exceptions are expected here
saroyanm 2015/05/04 18:13:43 Please also handle the exception. reportException
Wladimir Palant 2015/05/07 00:04:59 As the comment explains, exceptions are expected h
152 } 151 }
153 } 152 }
154 deferred.resolve([status, headers]); 153 resolve([status, headers]);
155 } 154 }
156 } 155 }
157 } 156 }
158 }; 157 };
159 158
160 /** 159 /**
161 * Once created, this object will make sure all new windows are dismissed 160 * Once created, this object will make sure all new windows are dismissed
162 * immediately. 161 * immediately.
163 * 162 *
164 * @constructor 163 * @constructor
165 */ 164 */
166 function WindowCloser() 165 function WindowCloser()
167 { 166 {
168 Services.obs.addObserver(this, "xul-window-registered", true) 167 Services.obs.addObserver(this, "xul-window-registered", true);
saroyanm 2015/05/04 18:13:43 Missing semicolon
Wladimir Palant 2015/05/07 00:04:59 Done.
169 } 168 }
170 WindowCloser.prototype = { 169 WindowCloser.prototype = {
171 /** 170 /**
172 * Deactivates this object. 171 * Deactivates this object.
173 */ 172 */
174 stop: function() 173 stop: function()
175 { 174 {
176 Services.obs.removeObserver(this, "xul-window-registered") 175 Services.obs.removeObserver(this, "xul-window-registered");
saroyanm 2015/05/04 18:13:43 Missing semicolon
Wladimir Palant 2015/05/07 00:04:59 Done.
177 }, 176 },
178 177
179 observe: function(subject, topic, data) 178 observe: function(subject, topic, data)
180 { 179 {
181 let window = subject.QueryInterface(Ci.nsIInterfaceRequestor) 180 let window = subject.QueryInterface(Ci.nsIInterfaceRequestor)
182 .getInterface(Ci.nsIDOMWindow) 181 .getInterface(Ci.nsIDOMWindow);
saroyanm 2015/05/04 18:13:43 Missing semicolon
Wladimir Palant 2015/05/07 00:04:59 Done.
183 window.addEventListener("load", function() 182 window.addEventListener("load", function()
184 { 183 {
185 if (window.document.documentElement.localName == 'dialog') 184 if (window.document.documentElement.localName == "dialog")
saroyanm 2015/05/04 18:13:43 Nit: Please use double quote.
Wladimir Palant 2015/05/07 00:04:59 Done.
186 window.document.documentElement.acceptDialog(); 185 window.document.documentElement.acceptDialog();
187 else 186 else
188 window.close(); 187 window.close();
189 }, false); 188 }, false);
190 }, 189 },
191 190
192 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakRefer ence]) 191 QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakRefer ence])
193 }; 192 };
194 193
195 /** 194 /**
196 * Retrieves crawler results associated with a particular content window. 195 * Retrieves crawler results associated with a particular content window.
197 * 196 *
198 * @param {Window} window 197 * @param {Window} window
199 * Content window to retrieve crawler results for 198 * Content window to retrieve crawler results for
200 * @result {Object} 199 * @return {Object}
saroyanm 2015/05/04 18:13:43 Nit: @return
Wladimir Palant 2015/05/07 00:04:59 Done.
201 * Crawler results or undefined if the window wasn't created by the crawler. 200 * Crawler results or undefined if the window wasn't created by the crawler.
202 */ 201 */
203 function getDataForWindow(window) 202 function getDataForWindow(window)
204 { 203 {
205 let topWindow = window.top; 204 let topWindow = window.top;
206 if (!topWindow.document) 205 if (!topWindow.document)
207 throw new Error("No document associated with the node's top window"); 206 throw new Error("No document associated with the node's top window");
208 let tabbrowser = Utils.getChromeWindow(topWindow).getBrowser(); 207 let tabbrowser = Utils.getChromeWindow(topWindow).getBrowser();
209 if (!tabbrowser) 208 if (!tabbrowser)
210 throw new Error("Unable to get a tabbrowser reference from the window"); 209 throw new Error("Unable to get a tabbrowser reference from the window");
211 let browser = tabbrowser.getBrowserForDocument(topWindow.document); 210 let browser = tabbrowser.getBrowserForDocument(topWindow.document);
212 if (!browser) 211 if (!browser)
213 throw new Error("Unable to get browser for the content window"); 212 throw new Error("Unable to get browser for the content window");
214 let tab = tabbrowser.getTabForBrowser(browser); 213 let tab = tabbrowser.getTabForBrowser(browser);
215 if (!tab) 214 if (!tab)
216 throw new Error("Unable to get tab for the browser"); 215 throw new Error("Unable to get tab for the browser");
217 return dataForTab.get(tab); 216 return dataForTab.get(tab);
218 }; 217 }
saroyanm 2015/05/04 18:13:43 Nit: Semicolon is redundant here.
Wladimir Palant 2015/05/07 00:04:59 Done.
219 218
220 /** 219 /**
221 * Starts the crawling session. The crawler opens each URL in a tab and stores 220 * Starts the crawling session. The crawler opens each URL in a tab and stores
222 * the results. 221 * the results.
223 * 222 *
224 * @param {Window} window 223 * @param {Window} window
225 * The browser window we're operating in 224 * The browser window we're operating in
226 * @param {String[]} urls 225 * @param {String[]} urls
227 * URLs to be crawled 226 * URLs to be crawled
228 * @param {int} number_of_tabs 227 * @param {int} number_of_tabs
229 * Maximum number of tabs to be opened 228 * Maximum number of tabs to be opened
230 * @param {String} targetURL 229 * @param {String} targetURL
231 * URL that should receive the results 230 * URL that should receive the results
saroyanm 2015/05/04 18:13:43 Nit: Please also document onDone parameter for con
Sebastian Noack 2015/05/04 20:39:01 Also note that JsDoc 3 will mistakenly document th
Wladimir Palant 2015/05/07 00:04:59 Done.
231 * @param {Function} onDone
232 * Callback to be executed once the processing finishes
233 * @static
232 */ 234 */
233 function run(window, urls, timeout, maxtabs, targetURL, onDone) 235 function run(window, urls, timeout, maxtabs, targetURL, onDone)
234 { 236 {
235 let requestNotifier = new RequestNotifier(null, function() {}); 237 let requestNotifier = new RequestNotifier(null, function() {});
236 238
237 let origProcessNode = Policy.processNode; 239 let origProcessNode = Policy.processNode;
238 Policy.processNode = processNodeReplacement.bind(null, origProcessNode, reques tNotifier); 240 Policy.processNode = processNodeReplacement.bind(null, origProcessNode, reques tNotifier);
239 241
240 let tabAllocator = new TabAllocator(window.getBrowser(), maxtabs); 242 let tabAllocator = new TabAllocator(window.getBrowser(), maxtabs);
241 let loadListener = new LoadListener(window.getBrowser(), timeout); 243 let loadListener = new LoadListener(window.getBrowser(), timeout);
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
281 } 283 }
282 } 284 }
283 exports.run = run; 285 exports.run = run;
284 286
285 /** 287 /**
286 * Crawls a URL. This is a generator meant to be used via a Task object. 288 * Crawls a URL. This is a generator meant to be used via a Task object.
287 * 289 *
288 * @param {String} url 290 * @param {String} url
289 * @param {TabAllocator} tabAllocator 291 * @param {TabAllocator} tabAllocator
290 * @param {loadListener} loadListener 292 * @param {loadListener} loadListener
291 * @result {Object} 293 * @return {Object}
saroyanm 2015/05/04 18:13:43 Nit: @return {Object} Crawling result
Wladimir Palant 2015/05/07 00:04:59 Done.
292 * Crawling result 294 * Crawling result
293 */ 295 */
294 function* crawl_url(url, tabAllocator, loadListener) 296 function* crawl_url(url, tabAllocator, loadListener)
Sebastian Noack 2015/05/07 12:33:04 Nit: camel case
295 { 297 {
296 let tab = yield tabAllocator.getTab(); 298 let tab = yield tabAllocator.getTab();
297 let result = {url: url}; 299 let result = {url: url};
298 300
299 dataForTab.set(tab, result); 301 dataForTab.set(tab, result);
300 try 302 try
301 { 303 {
302 result.startTime = Date.now(); 304 result.startTime = Date.now();
303 tab.linkedBrowser.loadURI(url, null, null); 305 tab.linkedBrowser.loadURI(url, null, null);
304 [result.channelStatus, result.headers] = yield loadListener.waitForLoad(tab) ; 306 [result.channelStatus, result.headers] = yield loadListener.waitForLoad(tab) ;
305 result.endTime = Date.now(); 307 result.endTime = Date.now();
306 result.finalUrl = tab.linkedBrowser.currentURI.spec; 308 result.finalUrl = tab.linkedBrowser.currentURI.spec;
307 309
308 let document = tab.linkedBrowser.contentDocument; 310 let document = tab.linkedBrowser.contentDocument;
309 if (document.documentElement) 311 if (document.documentElement)
310 { 312 {
311 try 313 try
312 { 314 {
313 let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "c anvas"); 315 let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "c anvas");
314 canvas.width = document.documentElement.scrollWidth; 316 canvas.width = document.documentElement.scrollWidth;
315 canvas.height = document.documentElement.scrollHeight; 317 canvas.height = document.documentElement.scrollHeight;
316 318
317 let context = canvas.getContext("2d"); 319 let context = canvas.getContext("2d");
318 context.drawWindow(document.defaultView, 0, 0, canvas.width, canvas.heig ht, "rgb(255, 255, 255)"); 320 context.drawWindow(document.defaultView, 0, 0, canvas.width, canvas.heig ht, "rgb(255, 255, 255)");
319 result.screenshot = canvas.toDataURL("image/jpeg", 0.8); 321 result.screenshot = canvas.toDataURL("image/jpeg", 0.8);
saroyanm 2015/05/04 18:13:43 Maybe make sense to let user specify the quality o
Wladimir Palant 2015/05/07 00:04:59 Well, changing maxtabs is very useful when testing
saroyanm 2015/05/07 13:19:00 Good point.
320 } 322 }
321 catch (e) 323 catch (e)
322 { 324 {
323 reportException(e); 325 reportException(e);
324 result.error = "Capturing screenshot failed: " + e; 326 result.error = "Capturing screenshot failed: " + e;
saroyanm 2015/05/04 18:13:43 Isn't result.error redundant ?
Wladimir Palant 2015/05/07 00:04:59 No. The exception is merely reported to the consol
saroyanm 2015/05/07 13:19:00 Yes, missed that. Good point.
325 } 327 }
326 328
327 // TODO: Capture frames as well? 329 // TODO: Capture frames as well?
saroyanm 2015/05/04 18:13:43 Nit: I think we shouldn't have TODO comments in re
Wladimir Palant 2015/05/07 00:04:59 Why not? This is something we might want to do in
328 let serializer = new tab.ownerDocument.defaultView.XMLSerializer(); 330 let serializer = new tab.ownerDocument.defaultView.XMLSerializer();
329 result.source = serializer.serializeToString(document.documentElement); 331 result.source = serializer.serializeToString(document.documentElement);
330 } 332 }
331 } 333 }
332 finally 334 finally
333 { 335 {
334 tabAllocator.releaseTab(tab); 336 tabAllocator.releaseTab(tab);
335 } 337 }
336 return result; 338 return result;
337 }
338
339 function reportException(e)
340 {
341 let stack = "";
342 if (e && typeof e == "object" && "stack" in e)
343 stack = e.stack + "\n";
344
345 Cu.reportError(e);
346 dump(e + "\n" + stack + "\n");
347 } 339 }
348 340
349 /** 341 /**
350 * Wrapper for the Policy.processNode() function in ABP. Calls the original 342 * Wrapper for the Policy.processNode() function in ABP. Calls the original
351 * function and records all the data. 343 * function and records all the data.
352 * 344 *
353 * @param {Function} origProcessNode 345 * @param {Function} origProcessNode
354 * The original processNode function. 346 * The original processNode function.
355 * @param {RequestNotifier} requestNotifier 347 * @param {RequestNotifier} requestNotifier
356 * The crawler's RequestNotifier object instance. 348 * The crawler's RequestNotifier object instance.
357 * @param {nsIDOMWindow} wnd 349 * @param {nsIDOMWindow} wnd
358 * @param {nsIDOMElement} node 350 * @param {nsIDOMElement} node
359 * @param {Number} contentType 351 * @param {Number} contentType
360 * @param {nsIURI} location 352 * @param {nsIURI} location
361 * @param {Boolean} collapse 353 * @param {Boolean} collapse
362 * @return {Boolean} 354 * @return {Boolean}
363 */ 355 */
364 function processNodeReplacement(origProcessNode, requestNotifier, wnd, node, con tentType, location, collapse) 356 function processNodeReplacement(origProcessNode, requestNotifier, wnd, node, con tentType, location, collapse)
365 { 357 {
366 let filters = []; 358 let filters = [];
367 let origListener = requestNotifier.listener; 359 let origListener = requestNotifier.listener;
368 requestNotifier.listener = function(window, node, entry) 360 requestNotifier.listener = function(window, node, entry)
369 { 361 {
370 if (entry.filter) 362 if (entry.filter)
371 filters.push(entry.filter.text); 363 filters.push(entry.filter.text);
372 }; 364 };
373 365
374 /* 366 // Call the original processNode. If the original throws, then we will too,
375 * Call the original processNode. If the original throws, then we will too, so this is outside a try clause. 367 // so this is outside a try clause.
saroyanm 2015/05/04 18:13:43 Nit: the line is too long.
Sebastian Noack 2015/05/04 20:39:01 Also I suggest to use simply // for comments expla
Wladimir Palant 2015/05/07 00:04:59 Done.
376 */
377 let result; 368 let result;
378 try 369 try
379 { 370 {
380 result = origProcessNode(wnd, node, contentType, location, collapse); 371 result = origProcessNode(wnd, node, contentType, location, collapse);
381 } 372 }
382 finally 373 finally
383 { 374 {
384 requestNotifier.listener = origListener; 375 requestNotifier.listener = origListener;
385 } 376 }
386 377
(...skipping 10 matching lines...) Expand all
397 blocked: result != Ci.nsIContentPolicy.ACCEPT, 388 blocked: result != Ci.nsIContentPolicy.ACCEPT,
398 filters: filters 389 filters: filters
399 }); 390 });
400 } 391 }
401 } 392 }
402 catch (e) 393 catch (e)
403 { 394 {
404 reportException(e); 395 reportException(e);
405 } 396 }
406 return result; 397 return result;
407 }; 398 }
saroyanm 2015/05/04 18:13:43 Nit: Semicolon is redundant here.
Wladimir Palant 2015/05/07 00:04:59 Done.
LEFTRIGHT

Powered by Google App Engine
This is Rietveld