| 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-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 allPageMaps = 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; | 27 |
| 28 allPageMaps.add(this); |
| 29 }; | 29 }; |
| 30 PageMap.prototype = { | 30 PageMap.prototype = { |
| 31 _delete(id) | 31 _delete(id) |
| 32 { | 32 { |
| 33 delete this._map[id]; | 33 this._map.delete(id); |
| 34 | |
| 35 if (Object.keys(this._map).length == 0) | |
| 36 delete nonEmptyPageMaps[this._id]; | |
| 37 }, | 34 }, |
| 38 keys() | 35 keys() |
| 39 { | 36 { |
| 40 return Object.keys(this._map).map(ext.getPage); | 37 return this._map.keys(); |
| 41 }, | 38 }, |
| 42 get(page) | 39 get(page) |
| 43 { | 40 { |
| 44 return this._map[page.id]; | 41 return this._map.get(page.id); |
| 45 }, | 42 }, |
| 46 set(page, value) | 43 set(page, value) |
| 47 { | 44 { |
| 48 this._map[page.id] = value; | 45 this._map.set(page.id, value); |
| 49 nonEmptyPageMaps[this._id] = this; | |
| 50 }, | 46 }, |
| 51 has(page) | 47 has(page) |
| 52 { | 48 { |
| 53 return page.id in this._map; | 49 return this._map.has(page.id); |
| 54 }, | 50 }, |
| 55 clear() | 51 clear() |
| 56 { | 52 { |
| 57 for (let id in this._map) | 53 this._map.clear(); |
| 58 this._delete(id); | |
| 59 }, | 54 }, |
| 60 delete(page) | 55 delete(page) |
| 61 { | 56 { |
| 62 this._delete(page.id); | 57 this._delete(page.id); |
| 63 } | 58 } |
| 64 }; | 59 }; |
| 65 | 60 |
| 66 ext._removeFromAllPageMaps = pageId => | 61 ext._removeFromAllPageMaps = pageId => |
| 67 { | 62 { |
| 68 for (let pageMapId in nonEmptyPageMaps) | 63 for (let pageMap of allPageMaps) |
| 69 nonEmptyPageMaps[pageMapId]._delete(pageId); | 64 pageMap._delete(pageId); |
| 70 }; | 65 }; |
| 71 | 66 |
| 72 /* Pages */ | 67 /* Pages */ |
| 73 | 68 |
| 74 let Page = ext.Page = function(tab) | 69 let Page = ext.Page = function(tab) |
| 75 { | 70 { |
| 76 this.id = tab.id; | 71 this.id = tab.id; |
| 77 this._url = tab.url && new URL(tab.url); | 72 this._url = tab.url && new URL(tab.url); |
| 78 | 73 |
| 79 this.browserAction = new BrowserAction(tab.id); | 74 this.browserAction = new BrowserAction(tab.id); |
| 80 this.contextMenus = new ContextMenus(this); | 75 this.contextMenus = new ContextMenus(this); |
| 81 }; | 76 }; |
| 82 Page.prototype = { | 77 Page.prototype = { |
| 83 get url() | 78 get url() |
| 84 { | 79 { |
| 85 // usually our Page objects are created from Chrome's Tab objects, which | 80 // 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. | 81 // provide the url. So we can return the url given in the constructor. |
| 87 if (this._url) | 82 if (this._url) |
| 88 return this._url; | 83 return this._url; |
| 89 | 84 |
| 90 // but sometimes we only have the tab id when we create a Page object. | 85 // 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 | 86 // In that case we get the url from top frame of the tab, recorded by |
| 92 // the onBeforeRequest handler. | 87 // the onBeforeRequest handler. |
| 93 let frames = framesOfTabs[this.id]; | 88 let frames = framesOfTabs.get(this.id); |
| 94 if (frames) | 89 if (frames) |
| 95 { | 90 { |
| 96 let frame = frames[0]; | 91 let frame = frames.get(0); |
| 97 if (frame) | 92 if (frame) |
| 98 return frame.url; | 93 return frame.url; |
| 99 } | 94 } |
| 100 }, | 95 }, |
| 101 sendMessage(message, responseCallback) | 96 sendMessage(message, responseCallback) |
| 102 { | 97 { |
| 103 chrome.tabs.sendMessage(this.id, message, responseCallback); | 98 chrome.tabs.sendMessage(this.id, message, responseCallback); |
| 104 } | 99 } |
| 105 }; | 100 }; |
| 106 | 101 |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 }; | 146 }; |
| 152 | 147 |
| 153 chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => | 148 chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => |
| 154 { | 149 { |
| 155 if (changeInfo.status == "loading") | 150 if (changeInfo.status == "loading") |
| 156 ext.pages.onLoading._dispatch(new Page(tab)); | 151 ext.pages.onLoading._dispatch(new Page(tab)); |
| 157 }); | 152 }); |
| 158 | 153 |
| 159 function createFrame(tabId, frameId) | 154 function createFrame(tabId, frameId) |
| 160 { | 155 { |
| 161 let frames = framesOfTabs[tabId]; | 156 let frames = framesOfTabs.get(tabId); |
| 162 if (!frames) | 157 if (!frames) |
| 163 frames = framesOfTabs[tabId] = Object.create(null); | 158 { |
| 159 frames = new Map(); |
| 160 framesOfTabs.set(tabId, frames); |
| 161 } |
| 164 | 162 |
| 165 let frame = frames[frameId]; | 163 let frame = frames.get(frameId); |
| 166 if (!frame) | 164 if (!frame) |
| 167 frame = frames[frameId] = {}; | 165 { |
| 166 frame = {}; |
| 167 frames.set(frameId, frame); |
| 168 } |
| 168 | 169 |
| 169 return frame; | 170 return frame; |
| 170 } | 171 } |
| 171 | 172 |
| 172 function updatePageFrameStructure(frameId, tabId, url, parentFrameId) | 173 function updatePageFrameStructure(frameId, tabId, url, parentFrameId) |
| 173 { | 174 { |
| 174 if (frameId == 0) | 175 if (frameId == 0) |
| 175 { | 176 { |
| 176 let page = new Page({id: tabId, url}); | 177 let page = new Page({id: tabId, url}); |
| 177 | 178 |
| 178 ext._removeFromAllPageMaps(tabId); | 179 ext._removeFromAllPageMaps(tabId); |
| 179 | 180 |
| 180 chrome.tabs.get(tabId, () => | 181 chrome.tabs.get(tabId, () => |
| 181 { | 182 { |
| 182 // If the tab is prerendered, chrome.tabs.get() sets | 183 // If the tab is prerendered, chrome.tabs.get() sets |
| 183 // chrome.runtime.lastError and we have to dispatch the onLoading event, | 184 // chrome.runtime.lastError and we have to dispatch the onLoading event, |
| 184 // since the onUpdated event isn't dispatched for prerendered tabs. | 185 // since the onUpdated event isn't dispatched for prerendered tabs. |
| 185 // However, we have to keep relying on the unUpdated event for tabs that | 186 // However, we have to keep relying on the unUpdated event for tabs that |
| 186 // are already visible. Otherwise browser action changes get overridden | 187 // are already visible. Otherwise browser action changes get overridden |
| 187 // when Chrome automatically resets them on navigation. | 188 // when Chrome automatically resets them on navigation. |
| 188 if (chrome.runtime.lastError) | 189 if (chrome.runtime.lastError) |
| 189 ext.pages.onLoading._dispatch(page); | 190 ext.pages.onLoading._dispatch(page); |
| 190 }); | 191 }); |
| 191 } | 192 } |
| 192 | 193 |
| 193 // Update frame parent and URL in frame structure | 194 // Update frame URL and parent in frame structure |
| 194 let frame = createFrame(tabId, frameId); | 195 let frame = createFrame(tabId, frameId); |
| 195 frame.url = new URL(url); | 196 frame.url = new URL(url); |
| 196 frame.parent = framesOfTabs[tabId][parentFrameId] || null; | 197 |
| 198 let parentFrame = framesOfTabs.get(tabId).get(parentFrameId); |
| 199 if (parentFrame) |
| 200 frame.parent = parentFrame; |
| 197 } | 201 } |
| 198 | 202 |
| 199 chrome.webRequest.onHeadersReceived.addListener(details => | 203 chrome.webRequest.onHeadersReceived.addListener(details => |
| 200 { | 204 { |
| 201 // We have to update the frame structure when switching to a new | 205 // We have to update the frame structure when switching to a new |
| 202 // document, so that we process any further requests made by that | 206 // document, so that we process any further requests made by that |
| 203 // document in the right context. Unfortunately, we cannot rely | 207 // document in the right context. Unfortunately, we cannot rely |
| 204 // on webNavigation.onCommitted since it isn't guaranteed to fire | 208 // on webNavigation.onCommitted since it isn't guaranteed to fire |
| 205 // before any subresources start downloading[1]. As an | 209 // before any subresources start downloading[1]. As an |
| 206 // alternative we use webRequest.onHeadersReceived for HTTP(S) | 210 // 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, | 289 updatePageFrameStructure(details.frameId, details.tabId, details.url, |
| 286 details.parentFrameId); | 290 details.parentFrameId); |
| 287 } | 291 } |
| 288 }); | 292 }); |
| 289 | 293 |
| 290 function forgetTab(tabId) | 294 function forgetTab(tabId) |
| 291 { | 295 { |
| 292 ext.pages.onRemoved._dispatch(tabId); | 296 ext.pages.onRemoved._dispatch(tabId); |
| 293 | 297 |
| 294 ext._removeFromAllPageMaps(tabId); | 298 ext._removeFromAllPageMaps(tabId); |
| 295 delete framesOfTabs[tabId]; | 299 framesOfTabs.delete(tabId); |
| 296 } | 300 } |
| 297 | 301 |
| 298 chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => | 302 chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => |
| 299 { | 303 { |
| 300 forgetTab(removedTabId); | 304 forgetTab(removedTabId); |
| 301 }); | 305 }); |
| 302 | 306 |
| 303 chrome.tabs.onRemoved.addListener(forgetTab); | 307 chrome.tabs.onRemoved.addListener(forgetTab); |
| 304 | 308 |
| 305 chrome.tabs.onActivated.addListener(details => | 309 chrome.tabs.onActivated.addListener(details => |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 483 | 487 |
| 484 chrome.windows.onFocusChanged.addListener(windowId => | 488 chrome.windows.onFocusChanged.addListener(windowId => |
| 485 { | 489 { |
| 486 if (windowId != chrome.windows.WINDOW_ID_NONE) | 490 if (windowId != chrome.windows.WINDOW_ID_NONE) |
| 487 updateContextMenu(); | 491 updateContextMenu(); |
| 488 }); | 492 }); |
| 489 | 493 |
| 490 | 494 |
| 491 /* Web requests */ | 495 /* Web requests */ |
| 492 | 496 |
| 493 let framesOfTabs = Object.create(null); | 497 let framesOfTabs = new Map(); |
| 494 | 498 |
| 495 ext.getFrame = (tabId, frameId) => | 499 ext.getFrame = (tabId, frameId) => |
| 496 { | 500 { |
| 497 return (framesOfTabs[tabId] || {})[frameId]; | 501 let frames = framesOfTabs.get(tabId); |
| 502 return frames && frames.get(frameId); |
| 498 }; | 503 }; |
| 499 | 504 |
| 500 let handlerBehaviorChangedQuota = | 505 let handlerBehaviorChangedQuota = |
| 501 chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES; | 506 chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES; |
| 502 | 507 |
| 503 function propagateHandlerBehaviorChange() | 508 function propagateHandlerBehaviorChange() |
| 504 { | 509 { |
| 505 // Make sure to not call handlerBehaviorChanged() more often than allowed | 510 // Make sure to not call handlerBehaviorChanged() more often than allowed |
| 506 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. | 511 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. |
| 507 // Otherwise Chrome notifies the user that this extension is causing issues. | 512 // Otherwise Chrome notifies the user that this extension is causing issues. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 532 }; | 537 }; |
| 533 | 538 |
| 534 chrome.tabs.query({}, tabs => | 539 chrome.tabs.query({}, tabs => |
| 535 { | 540 { |
| 536 tabs.forEach(tab => | 541 tabs.forEach(tab => |
| 537 { | 542 { |
| 538 chrome.webNavigation.getAllFrames({tabId: tab.id}, details => | 543 chrome.webNavigation.getAllFrames({tabId: tab.id}, details => |
| 539 { | 544 { |
| 540 if (details && details.length > 0) | 545 if (details && details.length > 0) |
| 541 { | 546 { |
| 542 let frames = framesOfTabs[tab.id] = Object.create(null); | 547 let frames = new Map(); |
| 548 framesOfTabs.set(tab.id, frames); |
| 543 | 549 |
| 544 for (let i = 0; i < details.length; i++) | 550 for (let detail of details) |
| 551 frames.set(detail.frameId, {url: new URL(detail.url)}); |
| 552 |
| 553 for (let detail of details) |
| 545 { | 554 { |
| 546 frames[details[i].frameId] = { | 555 let {parentFrameId} = detail; |
| 547 url: new URL(details[i].url), | |
| 548 parent: null | |
| 549 }; | |
| 550 } | |
| 551 | |
| 552 for (let i = 0; i < details.length; i++) | |
| 553 { | |
| 554 let {parentFrameId} = details[i]; | |
| 555 | 556 |
| 556 if (parentFrameId != -1) | 557 if (parentFrameId != -1) |
| 557 frames[details[i].frameId].parent = frames[parentFrameId]; | 558 frames.get(detail.frameId).parent = frames.get(parentFrameId); |
| 558 } | 559 } |
| 559 } | 560 } |
| 560 }); | 561 }); |
| 561 }); | 562 }); |
| 562 }); | 563 }); |
| 563 | 564 |
| 564 chrome.webRequest.onBeforeRequest.addListener(details => | 565 chrome.webRequest.onBeforeRequest.addListener(details => |
| 565 { | 566 { |
| 566 // The high-level code isn't interested in requests that aren't | 567 // The high-level code isn't interested in requests that aren't |
| 567 // related to a tab or requests loading a top-level document, | 568 // related to a tab or requests loading a top-level document, |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 604 | 605 |
| 605 // Add "page" and "frame" if the message was sent by a content script. | 606 // Add "page" and "frame" if the message was sent by a content script. |
| 606 // If sent by popup or the background page itself, there is no "tab". | 607 // If sent by popup or the background page itself, there is no "tab". |
| 607 if ("tab" in rawSender) | 608 if ("tab" in rawSender) |
| 608 { | 609 { |
| 609 sender.page = new Page(rawSender.tab); | 610 sender.page = new Page(rawSender.tab); |
| 610 sender.frame = { | 611 sender.frame = { |
| 611 url: new URL(rawSender.url), | 612 url: new URL(rawSender.url), |
| 612 get parent() | 613 get parent() |
| 613 { | 614 { |
| 614 let frames = framesOfTabs[rawSender.tab.id]; | 615 let frames = framesOfTabs.get(rawSender.tab.id); |
| 615 | 616 |
| 616 if (!frames) | 617 if (!frames) |
| 617 return null; | 618 return null; |
| 618 | 619 |
| 619 let frame = frames[rawSender.frameId]; | 620 let frame = frames.get(rawSender.frameId); |
| 620 if (frame) | 621 if (frame) |
| 621 return frame.parent; | 622 return frame.parent || null; |
| 622 | 623 |
| 623 return frames[0]; | 624 return frames.get(0) || null; |
| 624 } | 625 } |
| 625 }; | 626 }; |
| 626 } | 627 } |
| 627 | 628 |
| 628 return ext.onMessage._dispatch( | 629 return ext.onMessage._dispatch( |
| 629 message, sender, sendResponse | 630 message, sender, sendResponse |
| 630 ).indexOf(true) != -1; | 631 ).indexOf(true) != -1; |
| 631 }); | 632 }); |
| 632 | 633 |
| 633 | 634 |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 724 ext.windows = { | 725 ext.windows = { |
| 725 create(createData, callback) | 726 create(createData, callback) |
| 726 { | 727 { |
| 727 chrome.windows.create(createData, createdWindow => | 728 chrome.windows.create(createData, createdWindow => |
| 728 { | 729 { |
| 729 afterTabLoaded(callback)(createdWindow.tabs[0]); | 730 afterTabLoaded(callback)(createdWindow.tabs[0]); |
| 730 }); | 731 }); |
| 731 } | 732 } |
| 732 }; | 733 }; |
| 733 }()); | 734 }()); |
| OLD | NEW |