Left: | ||
Right: |
LEFT | RIGHT |
---|---|
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-2017 eyeo GmbH | 3 * Copyright (C) 2006-2017 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 "use strict"; | 18 "use strict"; |
19 | 19 |
20 (function() | 20 (function() |
21 { | 21 { |
22 let nonEmptyPageMaps = Object.create(null); | 22 let nonEmptyPageMaps = new Set(); |
23 let pageMapCounter = 0; | |
24 | 23 |
25 let PageMap = ext.PageMap = function() | 24 let PageMap = ext.PageMap = function() |
26 { | 25 { |
27 this._map = Object.create(null); | 26 this._map = new Map(); |
28 this._id = ++pageMapCounter; | |
29 }; | 27 }; |
30 PageMap.prototype = { | 28 PageMap.prototype = { |
31 _delete(id) | 29 _delete(id) |
32 { | 30 { |
33 delete this._map[id]; | 31 this._map.delete(id); |
34 | 32 |
35 if (Object.keys(this._map).length == 0) | 33 if (this._map.size == 0) |
36 delete nonEmptyPageMaps[this._id]; | 34 nonEmptyPageMaps.delete(this); |
37 }, | 35 }, |
38 keys() | 36 keys() |
39 { | 37 { |
40 return Object.keys(this._map).map(ext.getPage); | 38 return Array.from(this._map.keys()).map(ext.getPage); |
41 }, | 39 }, |
42 get(page) | 40 get(page) |
43 { | 41 { |
44 return this._map[page.id]; | 42 return this._map.get(page.id); |
45 }, | 43 }, |
46 set(page, value) | 44 set(page, value) |
47 { | 45 { |
48 this._map[page.id] = value; | 46 this._map.set(page.id, value); |
49 nonEmptyPageMaps[this._id] = this; | 47 nonEmptyPageMaps.add(this); |
50 }, | 48 }, |
51 has(page) | 49 has(page) |
52 { | 50 { |
53 return page.id in this._map; | 51 return this._map.has(page.id); |
54 }, | 52 }, |
55 clear() | 53 clear() |
56 { | 54 { |
57 for (let id in this._map) | 55 this._map.clear(); |
58 this._delete(id); | 56 nonEmptyPageMaps.delete(this); |
59 }, | 57 }, |
60 delete(page) | 58 delete(page) |
61 { | 59 { |
62 this._delete(page.id); | 60 this._delete(page.id); |
63 } | 61 } |
64 }; | 62 }; |
65 | 63 |
66 ext._removeFromAllPageMaps = pageId => | 64 ext._removeFromAllPageMaps = pageId => |
67 { | 65 { |
68 for (let pageMapId in nonEmptyPageMaps) | 66 for (let pageMap of nonEmptyPageMaps) |
69 nonEmptyPageMaps[pageMapId]._delete(pageId); | 67 pageMap._delete(pageId); |
70 }; | 68 }; |
71 | 69 |
72 /* Pages */ | 70 /* Pages */ |
73 | 71 |
74 let Page = ext.Page = function(tab) | 72 let Page = ext.Page = function(tab) |
75 { | 73 { |
76 this.id = tab.id; | 74 this.id = tab.id; |
77 this._url = tab.url && new URL(tab.url); | 75 this._url = tab.url && new URL(tab.url); |
78 | 76 |
79 this.browserAction = new BrowserAction(tab.id); | 77 this.browserAction = new BrowserAction(tab.id); |
80 this.contextMenus = new ContextMenus(this); | 78 this.contextMenus = new ContextMenus(this); |
81 }; | 79 }; |
82 Page.prototype = { | 80 Page.prototype = { |
83 get url() | 81 get url() |
84 { | 82 { |
85 // usually our Page objects are created from Chrome's Tab objects, which | 83 // usually our Page objects are created from Chrome's Tab objects, which |
86 // provide the url. So we can return the url given in the constructor. | 84 // provide the url. So we can return the url given in the constructor. |
87 if (this._url) | 85 if (this._url) |
88 return this._url; | 86 return this._url; |
89 | 87 |
90 // but sometimes we only have the tab id when we create a Page object. | 88 // but sometimes we only have the tab id when we create a Page object. |
91 // In that case we get the url from top frame of the tab, recorded by | 89 // In that case we get the url from top frame of the tab, recorded by |
92 // the onBeforeRequest handler. | 90 // the onBeforeRequest handler. |
93 let frames = framesOfTabs[this.id]; | 91 let frames = framesOfTabs.get(this.id); |
94 if (frames) | 92 if (frames) |
95 { | 93 { |
96 let frame = frames[0]; | 94 let frame = frames.get(0); |
97 if (frame) | 95 if (frame) |
98 return frame.url; | 96 return frame.url; |
99 } | 97 } |
100 }, | 98 }, |
101 sendMessage(message, responseCallback) | 99 sendMessage(message, responseCallback) |
102 { | 100 { |
103 chrome.tabs.sendMessage(this.id, message, responseCallback); | 101 chrome.tabs.sendMessage(this.id, message, responseCallback); |
104 } | 102 } |
105 }; | 103 }; |
106 | 104 |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
151 }; | 149 }; |
152 | 150 |
153 chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => | 151 chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => |
154 { | 152 { |
155 if (changeInfo.status == "loading") | 153 if (changeInfo.status == "loading") |
156 ext.pages.onLoading._dispatch(new Page(tab)); | 154 ext.pages.onLoading._dispatch(new Page(tab)); |
157 }); | 155 }); |
158 | 156 |
159 function createFrame(tabId, frameId) | 157 function createFrame(tabId, frameId) |
160 { | 158 { |
161 let frames = framesOfTabs[tabId]; | 159 let frames = framesOfTabs.get(tabId); |
162 if (!frames) | 160 if (!frames) |
163 frames = framesOfTabs[tabId] = Object.create(null); | 161 { |
164 | 162 frames = new Map(); |
165 let frame = frames[frameId]; | 163 framesOfTabs.set(tabId, frames); |
164 } | |
165 | |
166 let frame = frames.get(frameId); | |
166 if (!frame) | 167 if (!frame) |
167 frame = frames[frameId] = {}; | 168 { |
169 frame = {}; | |
170 frames.set(frameId, frame); | |
171 } | |
168 | 172 |
169 return frame; | 173 return frame; |
170 } | 174 } |
171 | 175 |
172 function updatePageFrameStructure(frameId, tabId, url, parentFrameId) | 176 function updatePageFrameStructure(frameId, tabId, url, parentFrameId) |
173 { | 177 { |
174 if (frameId == 0) | 178 if (frameId == 0) |
175 { | 179 { |
176 let page = new Page({id: tabId, url}); | 180 let page = new Page({id: tabId, url}); |
177 | 181 |
178 ext._removeFromAllPageMaps(tabId); | 182 ext._removeFromAllPageMaps(tabId); |
179 | 183 |
180 chrome.tabs.get(tabId, () => | 184 chrome.tabs.get(tabId, () => |
181 { | 185 { |
182 // If the tab is prerendered, chrome.tabs.get() sets | 186 // If the tab is prerendered, chrome.tabs.get() sets |
183 // chrome.runtime.lastError and we have to dispatch the onLoading event, | 187 // chrome.runtime.lastError and we have to dispatch the onLoading event, |
184 // since the onUpdated event isn't dispatched for prerendered tabs. | 188 // since the onUpdated event isn't dispatched for prerendered tabs. |
185 // However, we have to keep relying on the unUpdated event for tabs that | 189 // However, we have to keep relying on the unUpdated event for tabs that |
186 // are already visible. Otherwise browser action changes get overridden | 190 // are already visible. Otherwise browser action changes get overridden |
187 // when Chrome automatically resets them on navigation. | 191 // when Chrome automatically resets them on navigation. |
188 if (chrome.runtime.lastError) | 192 if (chrome.runtime.lastError) |
189 ext.pages.onLoading._dispatch(page); | 193 ext.pages.onLoading._dispatch(page); |
190 }); | 194 }); |
191 } | 195 } |
192 | 196 |
193 // Update frame parent and URL in frame structure | 197 // Update frame URL and parent in frame structure |
194 let frame = createFrame(tabId, frameId); | 198 let frame = createFrame(tabId, frameId); |
195 frame.url = new URL(url); | 199 frame.url = new URL(url); |
196 frame.parent = framesOfTabs[tabId][parentFrameId] || null; | 200 |
201 let parentFrame = framesOfTabs.get(tabId).get(parentFrameId); | |
202 if (parentFrame) | |
203 frame.parent = parentFrame; | |
197 } | 204 } |
198 | 205 |
199 chrome.webRequest.onHeadersReceived.addListener(details => | 206 chrome.webRequest.onHeadersReceived.addListener(details => |
200 { | 207 { |
201 // We have to update the frame structure when switching to a new | 208 // We have to update the frame structure when switching to a new |
202 // document, so that we process any further requests made by that | 209 // document, so that we process any further requests made by that |
203 // document in the right context. Unfortunately, we cannot rely | 210 // document in the right context. Unfortunately, we cannot rely |
204 // on webNavigation.onCommitted since it isn't guaranteed to fire | 211 // on webNavigation.onCommitted since it isn't guaranteed to fire |
205 // before any subresources start downloading[1]. As an | 212 // before any subresources start downloading[1]. As an |
206 // alternative we use webRequest.onHeadersReceived for HTTP(S) | 213 // alternative we use webRequest.onHeadersReceived for HTTP(S) |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
285 updatePageFrameStructure(details.frameId, details.tabId, details.url, | 292 updatePageFrameStructure(details.frameId, details.tabId, details.url, |
286 details.parentFrameId); | 293 details.parentFrameId); |
287 } | 294 } |
288 }); | 295 }); |
289 | 296 |
290 function forgetTab(tabId) | 297 function forgetTab(tabId) |
291 { | 298 { |
292 ext.pages.onRemoved._dispatch(tabId); | 299 ext.pages.onRemoved._dispatch(tabId); |
293 | 300 |
294 ext._removeFromAllPageMaps(tabId); | 301 ext._removeFromAllPageMaps(tabId); |
295 delete framesOfTabs[tabId]; | 302 framesOfTabs.delete(tabId); |
296 } | 303 } |
297 | 304 |
298 chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => | 305 chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => |
299 { | 306 { |
300 forgetTab(removedTabId); | 307 forgetTab(removedTabId); |
301 }); | 308 }); |
302 | 309 |
303 chrome.tabs.onRemoved.addListener(forgetTab); | 310 chrome.tabs.onRemoved.addListener(forgetTab); |
304 | 311 |
305 chrome.tabs.onActivated.addListener(details => | 312 chrome.tabs.onActivated.addListener(details => |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
483 | 490 |
484 chrome.windows.onFocusChanged.addListener(windowId => | 491 chrome.windows.onFocusChanged.addListener(windowId => |
485 { | 492 { |
486 if (windowId != chrome.windows.WINDOW_ID_NONE) | 493 if (windowId != chrome.windows.WINDOW_ID_NONE) |
487 updateContextMenu(); | 494 updateContextMenu(); |
488 }); | 495 }); |
489 | 496 |
490 | 497 |
491 /* Web requests */ | 498 /* Web requests */ |
492 | 499 |
493 let framesOfTabs = Object.create(null); | 500 let framesOfTabs = new Map(); |
494 | 501 |
495 ext.getFrame = (tabId, frameId) => | 502 ext.getFrame = (tabId, frameId) => |
496 { | 503 { |
497 return (framesOfTabs[tabId] || {})[frameId]; | 504 let frames = framesOfTabs.get(tabId); |
505 return frames && frames.get(frameId); | |
498 }; | 506 }; |
499 | 507 |
500 let handlerBehaviorChangedQuota = | 508 let handlerBehaviorChangedQuota = |
501 chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES; | 509 chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES; |
502 | 510 |
503 function propagateHandlerBehaviorChange() | 511 function propagateHandlerBehaviorChange() |
504 { | 512 { |
505 // Make sure to not call handlerBehaviorChanged() more often than allowed | 513 // Make sure to not call handlerBehaviorChanged() more often than allowed |
506 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. | 514 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. |
507 // Otherwise Chrome notifies the user that this extension is causing issues. | 515 // Otherwise Chrome notifies the user that this extension is causing issues. |
(...skipping 24 matching lines...) Expand all Loading... | |
532 }; | 540 }; |
533 | 541 |
534 chrome.tabs.query({}, tabs => | 542 chrome.tabs.query({}, tabs => |
535 { | 543 { |
536 tabs.forEach(tab => | 544 tabs.forEach(tab => |
537 { | 545 { |
538 chrome.webNavigation.getAllFrames({tabId: tab.id}, details => | 546 chrome.webNavigation.getAllFrames({tabId: tab.id}, details => |
539 { | 547 { |
540 if (details && details.length > 0) | 548 if (details && details.length > 0) |
541 { | 549 { |
542 let frames = framesOfTabs[tab.id] = Object.create(null); | 550 let frames = new Map(); |
543 | 551 framesOfTabs.set(tab.id, frames); |
544 for (let i = 0; i < details.length; i++) | 552 |
553 for (let detail of details) | |
545 { | 554 { |
546 frames[details[i].frameId] = { | 555 let frame = {url: new URL(detail.url)}; |
547 url: new URL(details[i].url), | 556 frames.set(detail.frameId, frame); |
548 parent: null | 557 |
549 }; | 558 if (detail.parentFrameId != -1) |
550 } | 559 frame.parent = frames.get(detail.parentFrameId); |
551 | |
552 for (let i = 0; i < details.length; i++) | |
553 { | |
554 let {parentFrameId} = details[i]; | |
555 | |
556 if (parentFrameId != -1) | |
557 frames[details[i].frameId].parent = frames[parentFrameId]; | |
558 } | 560 } |
559 } | 561 } |
560 }); | 562 }); |
561 }); | 563 }); |
562 }); | 564 }); |
563 | 565 |
564 chrome.webRequest.onBeforeRequest.addListener(details => | 566 chrome.webRequest.onBeforeRequest.addListener(details => |
565 { | 567 { |
566 // The high-level code isn't interested in requests that aren't | 568 // The high-level code isn't interested in requests that aren't |
567 // related to a tab or requests loading a top-level document, | 569 // related to a tab or requests loading a top-level document, |
568 // those should never be blocked. | 570 // those should never be blocked. |
569 if (details.type == "main_frame") | 571 if (details.type == "main_frame") |
570 return; | 572 return; |
571 | 573 |
572 // Filter out requests from non web protocols. Ideally, we'd explicitly | 574 // Filter out requests from non web protocols. Ideally, we'd explicitly |
573 // specify the protocols we are interested in (i.e. http://, https://, | 575 // specify the protocols we are interested in (i.e. http://, https://, |
574 // ws:// and wss://) with the url patterns, given below, when adding this | 576 // ws:// and wss://) with the url patterns, given below, when adding this |
575 // listener. But unfortunately, Chrome <=57 doesn't support the WebSocket | 577 // listener. But unfortunately, Chrome <=57 doesn't support the WebSocket |
576 // protocol and is causing an error if it is given. | 578 // protocol and is causing an error if it is given. |
577 let url = new URL(details.url); | 579 let url = new URL(details.url); |
578 if (url.protocol != "http:" && url.protocol != "https:" && | 580 if (url.protocol != "http:" && url.protocol != "https:" && |
579 url.protocol != "ws:" && url.protocol != "wss:") | 581 url.protocol != "ws:" && url.protocol != "wss:") |
580 return; | 582 return; |
581 | 583 |
582 // We are looking for the frame that contains the element which | 584 // We are looking for the frame that contains the element which |
583 // has triggered this request. For most requests (e.g. images) we | 585 // has triggered this request. For most requests (e.g. images) we |
584 // can just use the request's frame ID, but for subdocument requests | 586 // can just use the request's frame ID, but for subdocument requests |
585 // (e.g. iframes) we must instead use the request's parent frame ID. | 587 // (e.g. iframes) we must instead use the request's parent frame ID. |
588 let {frameId, type} = details; | |
586 if (type == "sub_frame") | 589 if (type == "sub_frame") |
587 { | |
588 frameId = details.parentFrameId; | 590 frameId = details.parentFrameId; |
589 type = "SUBDOCUMENT"; | |
590 } | |
591 | |
592 let {frameId, type} = details; | |
593 | 591 |
594 // Sometimes requests are not associated with a browser tab and | 592 // Sometimes requests are not associated with a browser tab and |
595 // in this case we want to still be able to view the url being called. | 593 // in this case we want to still be able to view the url being called. |
596 // However, since tabId's are assumed to be unique elsewhere in the code | |
Sebastian Noack
2017/04/21 08:39:53
IMO, the second sentence is rather confusing, and
Jon Sonesen
2017/04/24 08:40:55
Acknowledged.
| |
597 // it seems safer to initialize frame and page as null and then only | |
598 // initiate a page, or fetch a frame if we know it will have a unique tabId. | |
599 let frame = null; | 594 let frame = null; |
600 let page = null; | 595 let page = null; |
601 if (details.tabId != -1) | 596 if (details.tabId != -1) |
602 { | 597 { |
603 frame = ext.getFrame(details.tabId, frameId); | 598 frame = ext.getFrame(details.tabId, frameId); |
604 page = new Page({id: details.tabId}); | 599 page = new Page({id: details.tabId}); |
605 } | 600 } |
606 | 601 |
607 let results = ext.webRequest.onBeforeRequest._dispatch( | 602 if (ext.webRequest.onBeforeRequest._dispatch( |
608 url, type.toUpperCase(), page, frame | 603 url, type, page, frame).includes(false)) |
609 ); | |
610 | |
611 if (results.indexOf(false) != -1) | |
Sebastian Noack
2017/04/21 08:39:53
Please use results.includes(false), which is equiv
Jon Sonesen
2017/04/24 08:40:56
Acknowledged.
| |
612 return {cancel: true}; | 604 return {cancel: true}; |
613 }, {urls: ["<all_urls>"]}, ["blocking"]); | 605 }, {urls: ["<all_urls>"]}, ["blocking"]); |
614 | 606 |
615 | 607 |
616 /* Message passing */ | 608 /* Message passing */ |
617 | 609 |
618 chrome.runtime.onMessage.addListener((message, rawSender, sendResponse) => | 610 chrome.runtime.onMessage.addListener((message, rawSender, sendResponse) => |
619 { | 611 { |
620 let sender = {}; | 612 let sender = {}; |
621 | 613 |
622 // Add "page" and "frame" if the message was sent by a content script. | 614 // Add "page" and "frame" if the message was sent by a content script. |
623 // If sent by popup or the background page itself, there is no "tab". | 615 // If sent by popup or the background page itself, there is no "tab". |
624 if ("tab" in rawSender) | 616 if ("tab" in rawSender) |
625 { | 617 { |
626 sender.page = new Page(rawSender.tab); | 618 sender.page = new Page(rawSender.tab); |
627 sender.frame = { | 619 sender.frame = { |
628 url: new URL(rawSender.url), | 620 url: new URL(rawSender.url), |
629 get parent() | 621 get parent() |
630 { | 622 { |
631 let frames = framesOfTabs[rawSender.tab.id]; | 623 let frames = framesOfTabs.get(rawSender.tab.id); |
632 | 624 |
633 if (!frames) | 625 if (!frames) |
634 return null; | 626 return null; |
635 | 627 |
636 let frame = frames[rawSender.frameId]; | 628 let frame = frames.get(rawSender.frameId); |
637 if (frame) | 629 if (frame) |
638 return frame.parent; | 630 return frame.parent || null; |
639 | 631 |
640 return frames[0]; | 632 return frames.get(0) || null; |
641 } | 633 } |
642 }; | 634 }; |
643 } | 635 } |
644 | 636 |
645 return ext.onMessage._dispatch( | 637 return ext.onMessage._dispatch( |
646 message, sender, sendResponse | 638 message, sender, sendResponse |
647 ).indexOf(true) != -1; | 639 ).indexOf(true) != -1; |
648 }); | 640 }); |
649 | 641 |
650 | 642 |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
741 ext.windows = { | 733 ext.windows = { |
742 create(createData, callback) | 734 create(createData, callback) |
743 { | 735 { |
744 chrome.windows.create(createData, createdWindow => | 736 chrome.windows.create(createData, createdWindow => |
745 { | 737 { |
746 afterTabLoaded(callback)(createdWindow.tabs[0]); | 738 afterTabLoaded(callback)(createdWindow.tabs[0]); |
747 }); | 739 }); |
748 } | 740 } |
749 }; | 741 }; |
750 }()); | 742 }()); |
LEFT | RIGHT |