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

Delta Between Two Patch Sets: chrome/ext/background.js

Issue 6393086494113792: Issue 154 - Added devtools panel showing blocked and blockable items (Closed)
Left Patch Set: Rebased and fixed various issues Created March 12, 2015, 3:32 p.m.
Right Patch Set: Adapt for UI changes generating domain specific filters when necessary Created Feb. 3, 2016, 10:40 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
« no previous file with change/comment | « chrome/devtools.js ('k') | chrome/ext/devtools.js » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 *
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 } 92 }
93 93
94 chrome.tabs.query(rawInfo, function(tabs) 94 chrome.tabs.query(rawInfo, function(tabs)
95 { 95 {
96 callback(tabs.map(function(tab) 96 callback(tabs.map(function(tab)
97 { 97 {
98 return new Page(tab); 98 return new Page(tab);
99 })); 99 }));
100 }); 100 });
101 }, 101 },
102 onLoading: new ext._EventTarget() 102 onLoading: new ext._EventTarget(),
103 onActivated: new ext._EventTarget()
103 }; 104 };
104 105
105 chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) 106 chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab)
106 { 107 {
107 if (changeInfo.status == "loading") 108 if (changeInfo.status == "loading")
108 ext.pages.onLoading._dispatch(new Page(tab)); 109 ext.pages.onLoading._dispatch(new Page(tab));
109 }); 110 });
110 111
111 chrome.webNavigation.onBeforeNavigate.addListener(function(details) 112 chrome.webNavigation.onBeforeNavigate.addListener(function(details)
112 { 113 {
(...skipping 27 matching lines...) Expand all
140 ext._removeFromAllPageMaps(tabId); 141 ext._removeFromAllPageMaps(tabId);
141 delete framesOfTabs[tabId]; 142 delete framesOfTabs[tabId];
142 } 143 }
143 144
144 chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) 145 chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId)
145 { 146 {
146 forgetTab(removedTabId); 147 forgetTab(removedTabId);
147 }); 148 });
148 149
149 chrome.tabs.onRemoved.addListener(forgetTab); 150 chrome.tabs.onRemoved.addListener(forgetTab);
151
152 chrome.tabs.onActivated.addListener(details =>
153 {
154 ext.pages.onActivated._dispatch(new Page({id: details.tabId}));
155 });
150 156
151 157
152 /* Browser actions */ 158 /* Browser actions */
153 159
154 var BrowserAction = function(tabId) 160 var BrowserAction = function(tabId)
155 { 161 {
156 this._tabId = tabId; 162 this._tabId = tabId;
157 this._changes = null; 163 this._changes = null;
158 }; 164 };
159 BrowserAction.prototype = { 165 BrowserAction.prototype = {
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
294 ContextMenus.prototype = { 300 ContextMenus.prototype = {
295 create: function(item) 301 create: function(item)
296 { 302 {
297 var items = contextMenuItems.get(this._page); 303 var items = contextMenuItems.get(this._page);
298 if (!items) 304 if (!items)
299 contextMenuItems.set(this._page, items = []); 305 contextMenuItems.set(this._page, items = []);
300 306
301 items.push(item); 307 items.push(item);
302 updateContextMenu(); 308 updateContextMenu();
303 }, 309 },
304 removeAll: function() 310 remove: function(item)
305 { 311 {
306 contextMenuItems.delete(this._page); 312 let items = contextMenuItems.get(this._page);
307 updateContextMenu(); 313 if (items)
314 {
315 let index = items.indexOf(item);
316 if (index != -1)
317 {
318 items.splice(index, 1);
319 updateContextMenu();
320 }
321 }
308 } 322 }
309 }; 323 };
310 324
311 chrome.tabs.onActivated.addListener(updateContextMenu); 325 chrome.tabs.onActivated.addListener(updateContextMenu);
312 326
313 chrome.windows.onFocusChanged.addListener(function(windowId) 327 chrome.windows.onFocusChanged.addListener(function(windowId)
314 { 328 {
315 if (windowId != chrome.windows.WINDOW_ID_NONE) 329 if (windowId != chrome.windows.WINDOW_ID_NONE)
316 updateContextMenu(); 330 updateContextMenu();
317 }); 331 });
318 332
319 333
320 /* Web requests */ 334 /* Web requests */
321 335
322 var framesOfTabs = Object.create(null); 336 var framesOfTabs = Object.create(null);
323 337
324 ext.getFrame = function(tabId, frameId) 338 ext.getFrame = function(tabId, frameId)
325 { 339 {
326 return (framesOfTabs[tabId] || {})[frameId]; 340 return (framesOfTabs[tabId] || {})[frameId];
327 }; 341 };
328 342
343 var handlerBehaviorChangedQuota = chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANG ED_CALLS_PER_10_MINUTES;
344
345 function propagateHandlerBehaviorChange()
346 {
347 // Make sure to not call handlerBehaviorChanged() more often than allowed
348 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES.
349 // Otherwise Chrome notifies the user that this extension is causing issues.
350 if (handlerBehaviorChangedQuota > 0)
351 {
352 chrome.webNavigation.onBeforeNavigate.removeListener(propagateHandlerBehav iorChange);
353 chrome.webRequest.handlerBehaviorChanged();
354
355 handlerBehaviorChangedQuota--;
356 setTimeout(function() { handlerBehaviorChangedQuota++; }, 600000);
357 }
358 }
359
329 ext.webRequest = { 360 ext.webRequest = {
330 onBeforeRequest: new ext._EventTarget(), 361 onBeforeRequest: new ext._EventTarget(),
331 handlerBehaviorChanged: chrome.webRequest.handlerBehaviorChanged 362 handlerBehaviorChanged: function()
332 }; 363 {
333 364 // Defer handlerBehaviorChanged() until navigation occurs.
334 // Since Chrome 38 requests of type 'object' (e.g. requests 365 // There wouldn't be any visible effect when calling it earlier,
335 // initiated by Flash) are mistakenly reported with the type 'other'. 366 // but it's an expensive operation and that way we avoid to call
336 // https://code.google.com/p/chromium/issues/detail?id=410382 367 // it multiple times, if multiple filters are added/removed.
337 if (parseInt(navigator.userAgent.match(/\bChrome\/(\d+)/)[1], 10) >= 38) 368 var onBeforeNavigate = chrome.webNavigation.onBeforeNavigate;
338 { 369 if (!onBeforeNavigate.hasListener(propagateHandlerBehaviorChange))
339 ext.webRequest.indistinguishableTypes = [ 370 onBeforeNavigate.addListener(propagateHandlerBehaviorChange);
340 ["OTHER", "OBJECT", "OBJECT_SUBREQUEST"] 371 },
341 ]; 372 getIndistinguishableTypes: function()
342 } 373 {
343 else 374 // Chrome 38-48 mistakenly reports requests of type `object`
344 { 375 // (e.g. requests initiated by Flash) with the type `other`.
345 ext.webRequest.indistinguishableTypes = [ 376 // https://code.google.com/p/chromium/issues/detail?id=410382
346 ["OBJECT", "OBJECT_SUBREQUEST"], 377 var match = navigator.userAgent.match(/\bChrome\/(\d+)/);
347 ["OTHER", "MEDIA", "FONT"] 378 if (match)
348 ]; 379 {
349 } 380 var version = parseInt(match[1], 10);
381 if (version >= 38 && version <= 48)
382 return [["OTHER", "OBJECT", "OBJECT_SUBREQUEST"]];
383 }
384
385 // Chrome <44 doesn't have ResourceType.
386 var ResourceType = chrome.webRequest.ResourceType || {};
387
388 // Before Chrome 49, requests of the type `font` and `ping`
389 // have been reported with the type `other`.
390 // https://code.google.com/p/chromium/issues/detail?id=410382
391 var otherTypes = ["OTHER", "MEDIA"];
392 if (!("FONT" in ResourceType))
393 otherTypes.push("FONT");
394 if (!("PING" in ResourceType))
395 otherTypes.push("PING");
396
397 return [["OBJECT", "OBJECT_SUBREQUEST"], otherTypes];
398 }
399 };
350 400
351 chrome.tabs.query({}, function(tabs) 401 chrome.tabs.query({}, function(tabs)
352 { 402 {
353 tabs.forEach(function(tab) 403 tabs.forEach(function(tab)
354 { 404 {
355 chrome.webNavigation.getAllFrames({tabId: tab.id}, function(details) 405 chrome.webNavigation.getAllFrames({tabId: tab.id}, function(details)
356 { 406 {
357 if (details && details.length > 0) 407 if (details && details.length > 0)
358 { 408 {
359 var frames = framesOfTabs[tab.id] = Object.create(null); 409 var frames = framesOfTabs[tab.id] = Object.create(null);
360 410
361 for (var i = 0; i < details.length; i++) 411 for (var i = 0; i < details.length; i++)
362 frames[details[i].frameId] = {url: new URL(details[i].url), parent: null}; 412 frames[details[i].frameId] = {url: new URL(details[i].url), parent: null};
363 413
364 for (var i = 0; i < details.length; i++) 414 for (var i = 0; i < details.length; i++)
365 { 415 {
366 var parentFrameId = details[i].parentFrameId; 416 var parentFrameId = details[i].parentFrameId;
367 417
368 if (parentFrameId != -1) 418 if (parentFrameId != -1)
369 frames[details[i].frameId].parent = frames[parentFrameId]; 419 frames[details[i].frameId].parent = frames[parentFrameId];
370 } 420 }
371 } 421 }
372 }); 422 });
373 }); 423 });
374 }); 424 });
375 425
376 chrome.webRequest.onBeforeRequest.addListener(function(details) 426 chrome.webRequest.onBeforeRequest.addListener(function(details)
377 { 427 {
378 try 428 // the high-level code isn't interested in requests that aren't related
379 { 429 // to a tab and since those can only be handled in Chrome, we ignore
380 // the high-level code isn't interested in requests that aren't related 430 // them here instead of in the browser independent high-level code.
381 // to a tab and since those can only be handled in Chrome, we ignore 431 if (details.tabId == -1)
382 // them here instead of in the browser independent high-level code. 432 return;
383 if (details.tabId == -1) 433
384 return; 434 var isMainFrame = details.type == "main_frame" || (
385 435
386 var isMainFrame = details.type == "main_frame" || ( 436 // assume that the first request belongs to the top frame. Chrome 29
387 437 // may give the top frame the type "object" instead of "main_frame".
388 // assume that the first request belongs to the top frame. Chrome 438 // https://code.google.com/p/chromium/issues/detail?id=281711
389 // may give the top frame the type "object" instead of "main_frame". 439 details.frameId == 0 && !(details.tabId in framesOfTabs)
390 // https://code.google.com/p/chromium/issues/detail?id=281711 440 );
391 details.frameId == 0 && !(details.tabId in framesOfTabs) 441
392 ); 442 var frames = null;
393 443 if (!isMainFrame)
394 var frames = null; 444 frames = framesOfTabs[details.tabId];
395 if (!isMainFrame) 445 if (!frames)
396 frames = framesOfTabs[details.tabId]; 446 frames = framesOfTabs[details.tabId] = Object.create(null);
397 if (!frames) 447
398 frames = framesOfTabs[details.tabId] = Object.create(null); 448 var frame = null;
399 449 var url = new URL(details.url);
400 var frame = null; 450 if (!isMainFrame)
401 var url = new URL(details.url); 451 {
402 if (!isMainFrame) 452 // we are looking for the frame that contains the element that
403 { 453 // is about to load, however if a frame is loading the surrounding
404 // we are looking for the frame that contains the element that 454 // frame is indicated by parentFrameId instead of frameId
405 // is about to load, however if a frame is loading the surrounding 455 var frameId;
406 // frame is indicated by parentFrameId instead of frameId 456 var requestType;
407 var frameId; 457 if (details.type == "sub_frame")
408 var requestType; 458 {
409 if (details.type == "sub_frame") 459 frameId = details.parentFrameId;
410 { 460 requestType = "SUBDOCUMENT";
411 frameId = details.parentFrameId; 461 }
412 requestType = "SUBDOCUMENT"; 462 else
413 } 463 {
414 else 464 frameId = details.frameId;
415 { 465 requestType = details.type.toUpperCase();
416 frameId = details.frameId; 466 }
417 requestType = details.type.toUpperCase(); 467
418 } 468 frame = frames[frameId] || frames[Object.keys(frames)[0]];
419 469
420 frame = frames[frameId] || frames[Object.keys(frames)[0]]; 470 if (frame)
421 471 {
422 if (frame) 472 var results = ext.webRequest.onBeforeRequest._dispatch(
423 { 473 url,
424 var results = ext.webRequest.onBeforeRequest._dispatch( 474 requestType,
425 url, 475 new Page({id: details.tabId}),
426 requestType, 476 frame
427 new Page({id: details.tabId}), 477 );
428 frame 478
429 ); 479 if (results.indexOf(false) != -1)
430 480 return {cancel: true};
431 if (results.indexOf(false) != -1) 481 }
432 return {cancel: true}; 482 }
433 } 483
434 } 484 if (isMainFrame || details.type == "sub_frame")
435 485 frames[details.frameId] = {url: url, parent: frame};
436 if (isMainFrame || details.type == "sub_frame")
437 frames[details.frameId] = {url: url, parent: frame};
438 }
439 catch (e)
440 {
441 // recent versions of Chrome cancel the request when an error occurs in
442 // the onBeforeRequest listener. However in our case it is preferred, to
443 // let potentially some ads through, rather than blocking legit requests.
444 console.error(e);
445 }
446 }, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]); 486 }, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]);
447 487
448 488
449 /* Message passing */ 489 /* Message passing */
450 490
451 chrome.runtime.onMessage.addListener(function(message, rawSender, sendResponse ) 491 chrome.runtime.onMessage.addListener(function(message, rawSender, sendResponse )
452 { 492 {
453 var sender = {}; 493 var sender = {};
454 494
455 // Add "page" and "frame" if the message was sent by a content script. 495 // Add "page" and "frame" if the message was sent by a content script.
(...skipping 28 matching lines...) Expand all
484 } 524 }
485 525
486 return frames[0]; 526 return frames[0];
487 } 527 }
488 }; 528 };
489 } 529 }
490 530
491 return ext.onMessage._dispatch(message, sender, sendResponse).indexOf(true) != -1; 531 return ext.onMessage._dispatch(message, sender, sendResponse).indexOf(true) != -1;
492 }); 532 });
493 533
494 // We have to ensure there is at least one listener for the onConnect event.
495 // Otherwise we can't connect a port later, which we need to do in order to
496 // detect when the extension is reloaded, disabled or uninstalled.
497 chrome.runtime.onConnect.addListener(function() {});
498
499 534
500 /* Storage */ 535 /* Storage */
501 536
502 ext.storage = localStorage; 537 ext.storage = {
503 538 get: function(keys, callback)
539 {
540 chrome.storage.local.get(keys, callback);
541 },
542 set: function(key, value, callback)
543 {
544 let items = {};
545 items[key] = value;
546 chrome.storage.local.set(items, callback);
547 },
548 remove: function(key, callback)
549 {
550 chrome.storage.local.remove(key, callback);
551 },
552 onChanged: chrome.storage.onChanged
553 };
504 554
505 /* Options */ 555 /* Options */
506 556
507 ext.showOptions = function(callback) 557 ext.showOptions = function(callback)
508 { 558 {
509 chrome.windows.getLastFocused(function(win) 559 chrome.windows.getLastFocused(function(win)
510 { 560 {
511 var optionsUrl = chrome.extension.getURL("options.html"); 561 var optionsUrl = chrome.extension.getURL("options.html");
512 var queryInfo = {url: optionsUrl}; 562 var queryInfo = {url: optionsUrl};
513 563
(...skipping 15 matching lines...) Expand all
529 if (callback) 579 if (callback)
530 callback(new Page(tab)); 580 callback(new Page(tab));
531 } 581 }
532 else 582 else
533 { 583 {
534 ext.pages.open(optionsUrl, callback); 584 ext.pages.open(optionsUrl, callback);
535 } 585 }
536 }); 586 });
537 }); 587 });
538 }; 588 };
539
540
541 /* Devtools panel */
542
543 var Panel = function(inspectedTabId, port)
544 {
545 this.inspectedTabId = inspectedTabId;
546 this._port = port;
547 };
548 Panel.prototype = {
549 sendMessage: function(message)
550 {
551 this._port.postMessage(message);
552 },
553 get onRemoved()
554 {
555 return this._port.onDisconnect;
556 }
557 };
558
559 ext.devtools = {
560 onCreated: new ext._EventTarget()
561 };
562
563 chrome.runtime.onConnect.addListener(function(port)
564 {
565 var match = port.name.match(/^devtools-(\d+)$/);
566 if (match)
567 {
568 var panel = new Panel(parseInt(match[1], 10), port);
569 ext.devtools.onCreated._dispatch(panel);
570 }
571 });
572 })(); 589 })();
LEFTRIGHT

Powered by Google App Engine
This is Rietveld