 Issue 29417597:
  Issue 5161 - Use maps and sets where appropriate  (Closed) 
  Base URL: https://hg.adblockplus.org/adblockpluschrome/
    
  
    Issue 29417597:
  Issue 5161 - Use maps and sets where appropriate  (Closed) 
  Base URL: https://hg.adblockplus.org/adblockpluschrome/| Left: | ||
| Right: | 
| 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 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 let map = this._map; | 
| 34 | 32 | 
| 35 if (Object.keys(this._map).length == 0) | 33 let deleted = map.delete(id); | 
| 36 delete nonEmptyPageMaps[this._id]; | 34 | 
| 35 if (deleted && map.size == 0) | |
| 36 nonEmptyPageMaps.delete(this); | |
| 37 }, | 37 }, | 
| 38 keys() | 38 keys() | 
| 39 { | 39 { | 
| 40 return Object.keys(this._map).map(ext.getPage); | 40 return [...ext.mapIterable(this._map.keys(), ext.getPage)]; | 
| 41 }, | 41 }, | 
| 42 get(page) | 42 get(page) | 
| 43 { | 43 { | 
| 44 return this._map[page.id]; | 44 return this._map.get(page.id); | 
| 45 }, | 45 }, | 
| 46 set(page, value) | 46 set(page, value) | 
| 47 { | 47 { | 
| 48 this._map[page.id] = value; | 48 let map = this._map; | 
| 49 nonEmptyPageMaps[this._id] = this; | 49 let prevSize = map.size; | 
| 50 | |
| 51 map.set(page.id, value); | |
| 52 | |
| 53 if (prevSize == 0) | |
| 54 nonEmptyPageMaps.add(this); | |
| 50 }, | 55 }, | 
| 51 has(page) | 56 has(page) | 
| 52 { | 57 { | 
| 53 return page.id in this._map; | 58 return this._map.has(page.id); | 
| 54 }, | 59 }, | 
| 55 clear() | 60 clear() | 
| 56 { | 61 { | 
| 57 for (let id in this._map) | 62 let map = this._map; | 
| 
Sebastian Noack
2017/04/29 22:46:32
Why do you import this property into a local varia
 
Manish Jethani
2017/05/04 14:47:33
I wrongly assumed that this would make it faster.
 | |
| 58 this._delete(id); | 63 | 
| 64 if (map.size == 0) | |
| 65 return; | |
| 66 | |
| 67 map.clear(); | |
| 68 nonEmptyPageMaps.delete(this); | |
| 59 }, | 69 }, | 
| 60 delete(page) | 70 delete(page) | 
| 61 { | 71 { | 
| 62 this._delete(page.id); | 72 this._delete(page.id); | 
| 63 } | 73 } | 
| 64 }; | 74 }; | 
| 65 | 75 | 
| 66 ext._removeFromAllPageMaps = pageId => | 76 ext._removeFromAllPageMaps = pageId => | 
| 67 { | 77 { | 
| 68 for (let pageMapId in nonEmptyPageMaps) | 78 for (let pageMap of nonEmptyPageMaps) | 
| 69 nonEmptyPageMaps[pageMapId]._delete(pageId); | 79 pageMap._delete(pageId); | 
| 70 }; | 80 }; | 
| 71 | 81 | 
| 72 /* Pages */ | 82 /* Pages */ | 
| 73 | 83 | 
| 74 let Page = ext.Page = function(tab) | 84 let Page = ext.Page = function(tab) | 
| 75 { | 85 { | 
| 76 this.id = tab.id; | 86 this.id = tab.id; | 
| 77 this._url = tab.url && new URL(tab.url); | 87 this._url = tab.url && new URL(tab.url); | 
| 78 | 88 | 
| 79 this.browserAction = new BrowserAction(tab.id); | 89 this.browserAction = new BrowserAction(tab.id); | 
| 80 this.contextMenus = new ContextMenus(this); | 90 this.contextMenus = new ContextMenus(this); | 
| 81 }; | 91 }; | 
| 82 Page.prototype = { | 92 Page.prototype = { | 
| 83 get url() | 93 get url() | 
| 84 { | 94 { | 
| 85 // usually our Page objects are created from Chrome's Tab objects, which | 95 // 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. | 96 // provide the url. So we can return the url given in the constructor. | 
| 87 if (this._url) | 97 if (this._url) | 
| 88 return this._url; | 98 return this._url; | 
| 89 | 99 | 
| 90 // but sometimes we only have the tab id when we create a Page object. | 100 // 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 | 101 // In that case we get the url from top frame of the tab, recorded by | 
| 92 // the onBeforeRequest handler. | 102 // the onBeforeRequest handler. | 
| 93 let frames = framesOfTabs[this.id]; | 103 let frames = framesOfTabs.get(this.id); | 
| 94 if (frames) | 104 if (frames) | 
| 95 { | 105 { | 
| 96 let frame = frames[0]; | 106 let frame = frames.get(0); | 
| 97 if (frame) | 107 if (frame) | 
| 98 return frame.url; | 108 return frame.url; | 
| 99 } | 109 } | 
| 100 }, | 110 }, | 
| 101 sendMessage(message, responseCallback) | 111 sendMessage(message, responseCallback) | 
| 102 { | 112 { | 
| 103 chrome.tabs.sendMessage(this.id, message, responseCallback); | 113 chrome.tabs.sendMessage(this.id, message, responseCallback); | 
| 104 } | 114 } | 
| 105 }; | 115 }; | 
| 106 | 116 | 
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 151 }; | 161 }; | 
| 152 | 162 | 
| 153 chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => | 163 chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => | 
| 154 { | 164 { | 
| 155 if (changeInfo.status == "loading") | 165 if (changeInfo.status == "loading") | 
| 156 ext.pages.onLoading._dispatch(new Page(tab)); | 166 ext.pages.onLoading._dispatch(new Page(tab)); | 
| 157 }); | 167 }); | 
| 158 | 168 | 
| 159 function createFrame(tabId, frameId) | 169 function createFrame(tabId, frameId) | 
| 160 { | 170 { | 
| 161 let frames = framesOfTabs[tabId]; | 171 let frames = framesOfTabs.get(tabId); | 
| 162 if (!frames) | 172 if (!frames) | 
| 163 frames = framesOfTabs[tabId] = Object.create(null); | 173 { | 
| 174 frames = new Map(); | |
| 175 framesOfTabs.set(tabId, frames); | |
| 176 } | |
| 164 | 177 | 
| 165 let frame = frames[frameId]; | 178 let frame = frames.get(frameId); | 
| 166 if (!frame) | 179 if (!frame) | 
| 167 frame = frames[frameId] = {}; | 180 { | 
| 181 frame = Object.create(null); | |
| 
Sebastian Noack
2017/04/29 22:46:32
Why do we use an object with no prototype here aga
 
Manish Jethani
2017/05/04 14:47:33
Again I assumed that an object with no prototype w
 | |
| 182 frames.set(frameId, frame); | |
| 183 } | |
| 168 | 184 | 
| 169 return frame; | 185 return frame; | 
| 170 } | 186 } | 
| 171 | 187 | 
| 172 function updatePageFrameStructure(frameId, tabId, url, parentFrameId) | 188 function updatePageFrameStructure(frameId, tabId, url, parentFrameId) | 
| 173 { | 189 { | 
| 174 if (frameId == 0) | 190 if (frameId == 0) | 
| 175 { | 191 { | 
| 176 let page = new Page({id: tabId, url}); | 192 let page = new Page({id: tabId, url}); | 
| 177 | 193 | 
| 178 ext._removeFromAllPageMaps(tabId); | 194 ext._removeFromAllPageMaps(tabId); | 
| 179 | 195 | 
| 180 chrome.tabs.get(tabId, () => | 196 chrome.tabs.get(tabId, () => | 
| 181 { | 197 { | 
| 182 // If the tab is prerendered, chrome.tabs.get() sets | 198 // If the tab is prerendered, chrome.tabs.get() sets | 
| 183 // chrome.runtime.lastError and we have to dispatch the onLoading event, | 199 // chrome.runtime.lastError and we have to dispatch the onLoading event, | 
| 184 // since the onUpdated event isn't dispatched for prerendered tabs. | 200 // since the onUpdated event isn't dispatched for prerendered tabs. | 
| 185 // However, we have to keep relying on the unUpdated event for tabs that | 201 // However, we have to keep relying on the unUpdated event for tabs that | 
| 186 // are already visible. Otherwise browser action changes get overridden | 202 // are already visible. Otherwise browser action changes get overridden | 
| 187 // when Chrome automatically resets them on navigation. | 203 // when Chrome automatically resets them on navigation. | 
| 188 if (chrome.runtime.lastError) | 204 if (chrome.runtime.lastError) | 
| 189 ext.pages.onLoading._dispatch(page); | 205 ext.pages.onLoading._dispatch(page); | 
| 190 }); | 206 }); | 
| 191 } | 207 } | 
| 192 | 208 | 
| 193 // Update frame parent and URL in frame structure | 209 // Update frame URL and parent in frame structure | 
| 194 let frame = createFrame(tabId, frameId); | 210 let frame = createFrame(tabId, frameId); | 
| 195 frame.url = new URL(url); | 211 frame.url = new URL(url); | 
| 196 frame.parent = framesOfTabs[tabId][parentFrameId] || null; | 212 | 
| 213 let parentFrame = framesOfTabs.get(tabId).get(parentFrameId); | |
| 214 if (parentFrame) | |
| 215 frame.parent = parentFrame; | |
| 197 } | 216 } | 
| 198 | 217 | 
| 199 chrome.webRequest.onHeadersReceived.addListener(details => | 218 chrome.webRequest.onHeadersReceived.addListener(details => | 
| 200 { | 219 { | 
| 201 // We have to update the frame structure when switching to a new | 220 // We have to update the frame structure when switching to a new | 
| 202 // document, so that we process any further requests made by that | 221 // document, so that we process any further requests made by that | 
| 203 // document in the right context. Unfortunately, we cannot rely | 222 // document in the right context. Unfortunately, we cannot rely | 
| 204 // on webNavigation.onCommitted since it isn't guaranteed to fire | 223 // on webNavigation.onCommitted since it isn't guaranteed to fire | 
| 205 // before any subresources start downloading[1]. As an | 224 // before any subresources start downloading[1]. As an | 
| 206 // alternative we use webRequest.onHeadersReceived for HTTP(S) | 225 // 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, | 304 updatePageFrameStructure(details.frameId, details.tabId, details.url, | 
| 286 details.parentFrameId); | 305 details.parentFrameId); | 
| 287 } | 306 } | 
| 288 }); | 307 }); | 
| 289 | 308 | 
| 290 function forgetTab(tabId) | 309 function forgetTab(tabId) | 
| 291 { | 310 { | 
| 292 ext.pages.onRemoved._dispatch(tabId); | 311 ext.pages.onRemoved._dispatch(tabId); | 
| 293 | 312 | 
| 294 ext._removeFromAllPageMaps(tabId); | 313 ext._removeFromAllPageMaps(tabId); | 
| 295 delete framesOfTabs[tabId]; | 314 framesOfTabs.delete(tabId); | 
| 296 } | 315 } | 
| 297 | 316 | 
| 298 chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => | 317 chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => | 
| 299 { | 318 { | 
| 300 forgetTab(removedTabId); | 319 forgetTab(removedTabId); | 
| 301 }); | 320 }); | 
| 302 | 321 | 
| 303 chrome.tabs.onRemoved.addListener(forgetTab); | 322 chrome.tabs.onRemoved.addListener(forgetTab); | 
| 304 | 323 | 
| 305 chrome.tabs.onActivated.addListener(details => | 324 chrome.tabs.onActivated.addListener(details => | 
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 483 | 502 | 
| 484 chrome.windows.onFocusChanged.addListener(windowId => | 503 chrome.windows.onFocusChanged.addListener(windowId => | 
| 485 { | 504 { | 
| 486 if (windowId != chrome.windows.WINDOW_ID_NONE) | 505 if (windowId != chrome.windows.WINDOW_ID_NONE) | 
| 487 updateContextMenu(); | 506 updateContextMenu(); | 
| 488 }); | 507 }); | 
| 489 | 508 | 
| 490 | 509 | 
| 491 /* Web requests */ | 510 /* Web requests */ | 
| 492 | 511 | 
| 493 let framesOfTabs = Object.create(null); | 512 let framesOfTabs = new Map(); | 
| 494 | 513 | 
| 495 ext.getFrame = (tabId, frameId) => | 514 ext.getFrame = (tabId, frameId) => | 
| 496 { | 515 { | 
| 497 return (framesOfTabs[tabId] || {})[frameId]; | 516 let frames = framesOfTabs.get(tabId); | 
| 517 if (frames) | |
| 518 return frames.get(frameId); | |
| 
Sebastian Noack
2017/04/29 22:46:32
How about:
  return frames && frames.get(frameId)
 
Manish Jethani
2017/05/04 14:47:33
Done.
 | |
| 498 }; | 519 }; | 
| 499 | 520 | 
| 500 let handlerBehaviorChangedQuota = | 521 let handlerBehaviorChangedQuota = | 
| 501 chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES; | 522 chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES; | 
| 502 | 523 | 
| 503 function propagateHandlerBehaviorChange() | 524 function propagateHandlerBehaviorChange() | 
| 504 { | 525 { | 
| 505 // Make sure to not call handlerBehaviorChanged() more often than allowed | 526 // Make sure to not call handlerBehaviorChanged() more often than allowed | 
| 506 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. | 527 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. | 
| 507 // Otherwise Chrome notifies the user that this extension is causing issues. | 528 // Otherwise Chrome notifies the user that this extension is causing issues. | 
| (...skipping 24 matching lines...) Expand all Loading... | |
| 532 }; | 553 }; | 
| 533 | 554 | 
| 534 chrome.tabs.query({}, tabs => | 555 chrome.tabs.query({}, tabs => | 
| 535 { | 556 { | 
| 536 tabs.forEach(tab => | 557 tabs.forEach(tab => | 
| 537 { | 558 { | 
| 538 chrome.webNavigation.getAllFrames({tabId: tab.id}, details => | 559 chrome.webNavigation.getAllFrames({tabId: tab.id}, details => | 
| 539 { | 560 { | 
| 540 if (details && details.length > 0) | 561 if (details && details.length > 0) | 
| 541 { | 562 { | 
| 542 let frames = framesOfTabs[tab.id] = Object.create(null); | 563 let frames = new Map(); | 
| 564 framesOfTabs.set(tab.id, frames); | |
| 543 | 565 | 
| 544 for (let i = 0; i < details.length; i++) | 566 for (let detail of details) | 
| 545 { | 567 { | 
| 546 frames[details[i].frameId] = { | 568 let frame = Object.create(null); | 
| 547 url: new URL(details[i].url), | 569 frame.url = new URL(detail.url); | 
| 548 parent: null | 570 | 
| 549 }; | 571 frames.set(detail.frameId, frame); | 
| 550 } | 572 } | 
| 551 | 573 | 
| 552 for (let i = 0; i < details.length; i++) | 574 for (let detail of details) | 
| 553 { | 575 { | 
| 554 let {parentFrameId} = details[i]; | 576 let {parentFrameId} = detail; | 
| 555 | 577 | 
| 556 if (parentFrameId != -1) | 578 if (parentFrameId != -1) | 
| 557 frames[details[i].frameId].parent = frames[parentFrameId]; | 579 frames.get(detail.frameId).parent = frames.get(parentFrameId); | 
| 558 } | 580 } | 
| 559 } | 581 } | 
| 560 }); | 582 }); | 
| 561 }); | 583 }); | 
| 562 }); | 584 }); | 
| 563 | 585 | 
| 564 chrome.webRequest.onBeforeRequest.addListener(details => | 586 chrome.webRequest.onBeforeRequest.addListener(details => | 
| 565 { | 587 { | 
| 566 // The high-level code isn't interested in requests that aren't | 588 // The high-level code isn't interested in requests that aren't | 
| 567 // related to a tab or requests loading a top-level document, | 589 // 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 | 626 | 
| 605 // Add "page" and "frame" if the message was sent by a content script. | 627 // 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". | 628 // If sent by popup or the background page itself, there is no "tab". | 
| 607 if ("tab" in rawSender) | 629 if ("tab" in rawSender) | 
| 608 { | 630 { | 
| 609 sender.page = new Page(rawSender.tab); | 631 sender.page = new Page(rawSender.tab); | 
| 610 sender.frame = { | 632 sender.frame = { | 
| 611 url: new URL(rawSender.url), | 633 url: new URL(rawSender.url), | 
| 612 get parent() | 634 get parent() | 
| 613 { | 635 { | 
| 614 let frames = framesOfTabs[rawSender.tab.id]; | 636 let frames = framesOfTabs.get(rawSender.tab.id); | 
| 615 | 637 | 
| 616 if (!frames) | 638 if (!frames) | 
| 617 return null; | 639 return null; | 
| 618 | 640 | 
| 619 let frame = frames[rawSender.frameId]; | 641 let frame = frames.get(rawSender.frameId); | 
| 620 if (frame) | 642 if (frame) | 
| 621 return frame.parent; | 643 return frame.parent || null; | 
| 
Sebastian Noack
2017/04/29 22:46:32
This seems unrelated.
 
Manish Jethani
2017/05/04 14:47:33
frame.parent is either an object or undefined. I t
 | |
| 622 | 644 | 
| 623 return frames[0]; | 645 return frames.get(0) || null; | 
| 624 } | 646 } | 
| 625 }; | 647 }; | 
| 626 } | 648 } | 
| 627 | 649 | 
| 628 return ext.onMessage._dispatch( | 650 return ext.onMessage._dispatch( | 
| 629 message, sender, sendResponse | 651 message, sender, sendResponse | 
| 630 ).indexOf(true) != -1; | 652 ).indexOf(true) != -1; | 
| 631 }); | 653 }); | 
| 632 | 654 | 
| 633 | 655 | 
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 724 ext.windows = { | 746 ext.windows = { | 
| 725 create(createData, callback) | 747 create(createData, callback) | 
| 726 { | 748 { | 
| 727 chrome.windows.create(createData, createdWindow => | 749 chrome.windows.create(createData, createdWindow => | 
| 728 { | 750 { | 
| 729 afterTabLoaded(callback)(createdWindow.tabs[0]); | 751 afterTabLoaded(callback)(createdWindow.tabs[0]); | 
| 730 }); | 752 }); | 
| 731 } | 753 } | 
| 732 }; | 754 }; | 
| 733 }()); | 755 }()); | 
| OLD | NEW |