| 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-2016 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 * |
| 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 (function() | 18 "use strict"; |
| 19 |
| 19 { | 20 { |
| 20 /* Pages */ | 21 /* Pages */ |
| 21 | 22 |
| 22 var Page = ext.Page = function(tab) | 23 let Page = ext.Page = function(tab) |
| 23 { | 24 { |
| 24 this.id = tab.id; | 25 this.id = tab.id; |
| 25 this._url = tab.url && new URL(tab.url); | 26 this._url = tab.url && new URL(tab.url); |
| 26 | 27 |
| 27 this.browserAction = new BrowserAction(tab.id); | 28 this.browserAction = new BrowserAction(tab.id); |
| 28 this.contextMenus = new ContextMenus(this); | 29 this.contextMenus = new ContextMenus(this); |
| 29 }; | 30 }; |
| 30 Page.prototype = { | 31 Page.prototype = { |
| 31 get url() | 32 get url() |
| 32 { | 33 { |
| 33 // usually our Page objects are created from Chrome's Tab objects, which | 34 // usually our Page objects are created from Chrome's Tab objects, which |
| 34 // provide the url. So we can return the url given in the constructor. | 35 // provide the url. So we can return the url given in the constructor. |
| 35 if (this._url) | 36 if (this._url) |
| 36 return this._url; | 37 return this._url; |
| 37 | 38 |
| 38 // but sometimes we only have the tab id when we create a Page object. | 39 // but sometimes we only have the tab id when we create a Page object. |
| 39 // In that case we get the url from top frame of the tab, recorded by | 40 // In that case we get the url from top frame of the tab, recorded by |
| 40 // the onBeforeRequest handler. | 41 // the onBeforeRequest handler. |
| 41 var frames = framesOfTabs[this.id]; | 42 let frames = framesOfTabs[this.id]; |
| 42 if (frames) | 43 if (frames) |
| 43 { | 44 { |
| 44 var frame = frames[0]; | 45 let frame = frames[0]; |
| 45 if (frame) | 46 if (frame) |
| 46 return frame.url; | 47 return frame.url; |
| 47 } | 48 } |
| 48 }, | 49 }, |
| 49 sendMessage: function(message, responseCallback) | 50 sendMessage(message, responseCallback) |
| 50 { | 51 { |
| 51 chrome.tabs.sendMessage(this.id, message, responseCallback); | 52 chrome.tabs.sendMessage(this.id, message, responseCallback); |
| 52 } | 53 } |
| 53 }; | 54 }; |
| 54 | 55 |
| 55 ext.getPage = function(id) | 56 ext.getPage = id => new Page({id: parseInt(id, 10)}); |
| 56 { | |
| 57 return new Page({id: parseInt(id, 10)}); | |
| 58 }; | |
| 59 | 57 |
| 60 function afterTabLoaded(callback) | 58 function afterTabLoaded(callback) |
| 61 { | 59 { |
| 62 return function(openedTab) | 60 return openedTab => |
| 63 { | 61 { |
| 64 var onUpdated = function(tabId, changeInfo, tab) | 62 let onUpdated = (tabId, changeInfo, tab) => |
| 65 { | 63 { |
| 66 if (tabId == openedTab.id && changeInfo.status == "complete") | 64 if (tabId == openedTab.id && changeInfo.status == "complete") |
| 67 { | 65 { |
| 68 chrome.tabs.onUpdated.removeListener(onUpdated); | 66 chrome.tabs.onUpdated.removeListener(onUpdated); |
| 69 callback(new Page(openedTab)); | 67 callback(new Page(openedTab)); |
| 70 } | 68 } |
| 71 }; | 69 }; |
| 72 chrome.tabs.onUpdated.addListener(onUpdated); | 70 chrome.tabs.onUpdated.addListener(onUpdated); |
| 73 }; | 71 }; |
| 74 } | 72 } |
| 75 | 73 |
| 76 ext.pages = { | 74 ext.pages = { |
| 77 open: function(url, callback) | 75 open(url, callback) |
| 78 { | 76 { |
| 79 chrome.tabs.create({url: url}, callback && afterTabLoaded(callback)); | 77 chrome.tabs.create({url: url}, callback && afterTabLoaded(callback)); |
| 80 }, | 78 }, |
| 81 query: function(info, callback) | 79 query(info, callback) |
| 82 { | 80 { |
| 83 var rawInfo = {}; | 81 let rawInfo = {}; |
| 84 for (var property in info) | 82 for (let property in info) |
| 85 { | 83 { |
| 86 switch (property) | 84 switch (property) |
| 87 { | 85 { |
| 88 case "active": | 86 case "active": |
| 89 case "lastFocusedWindow": | 87 case "lastFocusedWindow": |
| 90 rawInfo[property] = info[property]; | 88 rawInfo[property] = info[property]; |
| 91 } | 89 } |
| 92 } | 90 } |
| 93 | 91 |
| 94 chrome.tabs.query(rawInfo, function(tabs) | 92 chrome.tabs.query(rawInfo, tabs => |
| 95 { | 93 { |
| 96 callback(tabs.map(function(tab) | 94 callback(tabs.map(tab => new Page(tab))); |
| 97 { | |
| 98 return new Page(tab); | |
| 99 })); | |
| 100 }); | 95 }); |
| 101 }, | 96 }, |
| 102 onLoading: new ext._EventTarget(), | 97 onLoading: new ext._EventTarget(), |
| 103 onActivated: new ext._EventTarget(), | 98 onActivated: new ext._EventTarget(), |
| 104 onRemoved: new ext._EventTarget() | 99 onRemoved: new ext._EventTarget() |
| 105 }; | 100 }; |
| 106 | 101 |
| 107 chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) | 102 chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => |
| 108 { | 103 { |
| 109 if (changeInfo.status == "loading") | 104 if (changeInfo.status == "loading") |
| 110 ext.pages.onLoading._dispatch(new Page(tab)); | 105 ext.pages.onLoading._dispatch(new Page(tab)); |
| 111 }); | 106 }); |
| 112 | 107 |
| 113 function createFrame(tabId, frameId) | 108 function createFrame(tabId, frameId) |
| 114 { | 109 { |
| 115 var frames = framesOfTabs[tabId]; | 110 let frames = framesOfTabs[tabId]; |
| 116 if (!frames) | 111 if (!frames) |
| 117 frames = framesOfTabs[tabId] = Object.create(null); | 112 frames = framesOfTabs[tabId] = Object.create(null); |
| 118 | 113 |
| 119 var frame = frames[frameId]; | 114 let frame = frames[frameId]; |
| 120 if (!frame) | 115 if (!frame) |
| 121 frame = frames[frameId] = {}; | 116 frame = frames[frameId] = {}; |
| 122 | 117 |
| 123 return frame; | 118 return frame; |
| 124 } | 119 } |
| 125 | 120 |
| 126 function updatePageFrameStructure(frameId, tabId, url) | 121 function updatePageFrameStructure(frameId, tabId, url) |
| 127 { | 122 { |
| 128 if (frameId == 0) | 123 if (frameId == 0) |
| 129 { | 124 { |
| 130 let page = new Page({id: tabId, url: url}); | 125 let page = new Page({id: tabId, url: url}); |
| 131 | 126 |
| 132 chrome.tabs.get(tabId, function() | 127 chrome.tabs.get(tabId, () => |
| 133 { | 128 { |
| 134 // If the tab is prerendered, chrome.tabs.get() sets | 129 // If the tab is prerendered, chrome.tabs.get() sets |
| 135 // chrome.runtime.lastError and we have to dispatch the onLoading event, | 130 // chrome.runtime.lastError and we have to dispatch the onLoading event, |
| 136 // since the onUpdated event isn't dispatched for prerendered tabs. | 131 // since the onUpdated event isn't dispatched for prerendered tabs. |
| 137 // However, we have to keep relying on the unUpdated event for tabs that | 132 // However, we have to keep relying on the unUpdated event for tabs that |
| 138 // are already visible. Otherwise browser action changes get overridden | 133 // are already visible. Otherwise browser action changes get overridden |
| 139 // when Chrome automatically resets them on navigation. | 134 // when Chrome automatically resets them on navigation. |
| 140 if (chrome.runtime.lastError) | 135 if (chrome.runtime.lastError) |
| 141 ext.pages.onLoading._dispatch(page); | 136 ext.pages.onLoading._dispatch(page); |
| 142 }); | 137 }); |
| 143 } | 138 } |
| 144 | 139 |
| 145 // Update frame URL in frame structure | 140 // Update frame URL in frame structure |
| 146 var frame = createFrame(tabId, frameId); | 141 let frame = createFrame(tabId, frameId); |
| 147 frame.url = new URL(url); | 142 frame.url = new URL(url); |
| 148 }; | 143 }; |
| 149 | 144 |
| 150 chrome.webNavigation.onCommitted.addListener(function(details) | 145 chrome.webNavigation.onCommitted.addListener(details => |
| 151 { | 146 { |
| 152 updatePageFrameStructure(details.frameId, details.tabId, details.url); | 147 updatePageFrameStructure(details.frameId, details.tabId, details.url); |
| 153 }); | 148 }); |
| 154 | 149 |
| 155 chrome.webRequest.onHeadersReceived.addListener(function(details) | 150 chrome.webRequest.onHeadersReceived.addListener(details => |
| 156 { | 151 { |
| 157 // Ideally we would only need the above chrome.webNavigation.onCommitted | 152 // Ideally we would only need the above chrome.webNavigation.onCommitted |
| 158 // listener to update the page state but unfortunately pages can make web | 153 // listener to update the page state but unfortunately pages can make web |
| 159 // requests before their onCommitted event fires[1]. | 154 // requests before their onCommitted event fires[1]. |
| 160 // So for HTTP/S requests we can also use onHeadersReceived, being careful | 155 // So for HTTP/S requests we can also use onHeadersReceived, being careful |
| 161 // to ignore responses which won't directly result in a navigation, for | 156 // to ignore responses which won't directly result in a navigation, for |
| 162 // example redirections. | 157 // example redirections. |
| 163 // [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=665843 | 158 // [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=665843 |
| 164 /**** FIXME - Check Chromium code to ensure we got these right! ****/ | 159 /**** FIXME - Check Chromium code to ensure we got these right! ****/ |
| 165 if (!(details.statusCode > 299 && details.statusCode < 400 && | 160 if (!(details.statusCode > 299 && details.statusCode < 400 && |
| 166 details.statusCode != 304) || details.statusCode == 204) | 161 details.statusCode != 304) || details.statusCode == 204) |
| 167 updatePageFrameStructure(details.frameId, details.tabId, details.url); | 162 updatePageFrameStructure(details.frameId, details.tabId, details.url); |
| 168 }, {types: ["main_frame", "sub_frame"], urls: ["http://*/*", "https://*/*"]}); | 163 }, {types: ["main_frame", "sub_frame"], urls: ["http://*/*", "https://*/*"]}); |
| 169 | 164 |
| 170 chrome.webNavigation.onBeforeNavigate.addListener(function(details) | 165 chrome.webNavigation.onBeforeNavigate.addListener(details => |
| 171 { | 166 { |
| 172 // Capture parent frame here because onCommitted doesn't get this info. | 167 // Capture parent frame here because onCommitted doesn't get this info. |
| 173 var frame = createFrame(details.tabId, details.frameId); | 168 let frame = createFrame(details.tabId, details.frameId); |
| 174 frame.parent = framesOfTabs[details.tabId][details.parentFrameId] || null; | 169 frame.parent = framesOfTabs[details.tabId][details.parentFrameId] || null; |
| 175 | 170 |
| 176 // Since we can only listen for HTTP/S requests with onHeadersReceived we | 171 // Since we can only listen for HTTP/S requests with onHeadersReceived we |
| 177 // must update the page structure here for other navigations which might | 172 // must update the page structure here for other navigations which might |
| 178 // result in further requests. | 173 // result in further requests. |
| 179 let url = new URL(details.url); | 174 let url = new URL(details.url); |
| 180 if (url.protocol == "about:" || url.protocol == "data:") | 175 if (url.protocol == "about:" || url.protocol == "data:") |
| 181 updatePageFrameStructure(details.frameId, details.tabId, details.url); | 176 updatePageFrameStructure(details.frameId, details.tabId, details.url); |
| 182 }); | 177 }); |
| 183 | 178 |
| 184 function forgetTab(tabId) | 179 function forgetTab(tabId) |
| 185 { | 180 { |
| 186 ext.pages.onRemoved._dispatch(tabId); | 181 ext.pages.onRemoved._dispatch(tabId); |
| 187 | 182 |
| 188 ext._removeFromAllPageMaps(tabId); | 183 ext._removeFromAllPageMaps(tabId); |
| 189 delete framesOfTabs[tabId]; | 184 delete framesOfTabs[tabId]; |
| 190 } | 185 } |
| 191 | 186 |
| 192 chrome.tabs.onReplaced.addListener(function(addedTabId, removedTabId) | 187 chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => |
| 193 { | 188 { |
| 194 forgetTab(removedTabId); | 189 forgetTab(removedTabId); |
| 195 }); | 190 }); |
| 196 | 191 |
| 197 chrome.tabs.onRemoved.addListener(forgetTab); | 192 chrome.tabs.onRemoved.addListener(forgetTab); |
| 198 | 193 |
| 199 chrome.tabs.onActivated.addListener(function(details) | 194 chrome.tabs.onActivated.addListener(details => |
| 200 { | 195 { |
| 201 ext.pages.onActivated._dispatch(new Page({id: details.tabId})); | 196 ext.pages.onActivated._dispatch(new Page({id: details.tabId})); |
| 202 }); | 197 }); |
| 203 | 198 |
| 204 | 199 |
| 205 /* Browser actions */ | 200 /* Browser actions */ |
| 206 | 201 |
| 207 var BrowserAction = function(tabId) | 202 let BrowserAction = function(tabId) |
| 208 { | 203 { |
| 209 this._tabId = tabId; | 204 this._tabId = tabId; |
| 210 this._changes = null; | 205 this._changes = null; |
| 211 }; | 206 }; |
| 212 BrowserAction.prototype = { | 207 BrowserAction.prototype = { |
| 213 _applyChanges: function() | 208 _applyChanges() |
| 214 { | 209 { |
| 215 if ("iconPath" in this._changes) | 210 if ("iconPath" in this._changes) |
| 216 { | 211 { |
| 217 chrome.browserAction.setIcon({ | 212 chrome.browserAction.setIcon({ |
| 218 tabId: this._tabId, | 213 tabId: this._tabId, |
| 219 path: { | 214 path: { |
| 220 16: this._changes.iconPath.replace("$size", "16"), | 215 16: this._changes.iconPath.replace("$size", "16"), |
| 221 19: this._changes.iconPath.replace("$size", "19"), | 216 19: this._changes.iconPath.replace("$size", "19"), |
| 222 20: this._changes.iconPath.replace("$size", "20"), | 217 20: this._changes.iconPath.replace("$size", "20"), |
| 223 32: this._changes.iconPath.replace("$size", "32"), | 218 32: this._changes.iconPath.replace("$size", "32"), |
| (...skipping 14 matching lines...) Expand all Loading... |
| 238 if ("badgeColor" in this._changes) | 233 if ("badgeColor" in this._changes) |
| 239 { | 234 { |
| 240 chrome.browserAction.setBadgeBackgroundColor({ | 235 chrome.browserAction.setBadgeBackgroundColor({ |
| 241 tabId: this._tabId, | 236 tabId: this._tabId, |
| 242 color: this._changes.badgeColor | 237 color: this._changes.badgeColor |
| 243 }); | 238 }); |
| 244 } | 239 } |
| 245 | 240 |
| 246 this._changes = null; | 241 this._changes = null; |
| 247 }, | 242 }, |
| 248 _queueChanges: function() | 243 _queueChanges() |
| 249 { | 244 { |
| 250 chrome.tabs.get(this._tabId, function() | 245 chrome.tabs.get(this._tabId, () => |
| 251 { | 246 { |
| 252 // If the tab is prerendered, chrome.tabs.get() sets | 247 // If the tab is prerendered, chrome.tabs.get() sets |
| 253 // chrome.runtime.lastError and we have to delay our changes | 248 // chrome.runtime.lastError and we have to delay our changes |
| 254 // until the currently visible tab is replaced with the | 249 // until the currently visible tab is replaced with the |
| 255 // prerendered tab. Otherwise chrome.browserAction.set* fails. | 250 // prerendered tab. Otherwise chrome.browserAction.set* fails. |
| 256 if (chrome.runtime.lastError) | 251 if (chrome.runtime.lastError) |
| 257 { | 252 { |
| 258 var onReplaced = function(addedTabId, removedTabId) | 253 let onReplaced = (addedTabId, removedTabId) => |
| 259 { | 254 { |
| 260 if (addedTabId == this._tabId) | 255 if (addedTabId == this._tabId) |
| 261 { | 256 { |
| 262 chrome.tabs.onReplaced.removeListener(onReplaced); | 257 chrome.tabs.onReplaced.removeListener(onReplaced); |
| 263 this._applyChanges(); | 258 this._applyChanges(); |
| 264 } | 259 } |
| 265 }.bind(this); | 260 }; |
| 266 chrome.tabs.onReplaced.addListener(onReplaced); | 261 chrome.tabs.onReplaced.addListener(onReplaced); |
| 267 } | 262 } |
| 268 else | 263 else |
| 269 { | 264 { |
| 270 this._applyChanges(); | 265 this._applyChanges(); |
| 271 } | 266 } |
| 272 }.bind(this)); | 267 }); |
| 273 }, | 268 }, |
| 274 _addChange: function(name, value) | 269 _addChange(name, value) |
| 275 { | 270 { |
| 276 if (!this._changes) | 271 if (!this._changes) |
| 277 { | 272 { |
| 278 this._changes = {}; | 273 this._changes = {}; |
| 279 this._queueChanges(); | 274 this._queueChanges(); |
| 280 } | 275 } |
| 281 | 276 |
| 282 this._changes[name] = value; | 277 this._changes[name] = value; |
| 283 }, | 278 }, |
| 284 setIcon: function(path) | 279 setIcon(path) |
| 285 { | 280 { |
| 286 this._addChange("iconPath", path); | 281 this._addChange("iconPath", path); |
| 287 }, | 282 }, |
| 288 setBadge: function(badge) | 283 setBadge(badge) |
| 289 { | 284 { |
| 290 if (!badge) | 285 if (!badge) |
| 291 { | 286 { |
| 292 this._addChange("badgeText", ""); | 287 this._addChange("badgeText", ""); |
| 293 } | 288 } |
| 294 else | 289 else |
| 295 { | 290 { |
| 296 if ("number" in badge) | 291 if ("number" in badge) |
| 297 this._addChange("badgeText", badge.number.toString()); | 292 this._addChange("badgeText", badge.number.toString()); |
| 298 | 293 |
| 299 if ("color" in badge) | 294 if ("color" in badge) |
| 300 this._addChange("badgeColor", badge.color); | 295 this._addChange("badgeColor", badge.color); |
| 301 } | 296 } |
| 302 } | 297 } |
| 303 }; | 298 }; |
| 304 | 299 |
| 305 | 300 |
| 306 /* Context menus */ | 301 /* Context menus */ |
| 307 | 302 |
| 308 var contextMenuItems = new ext.PageMap(); | 303 let contextMenuItems = new ext.PageMap(); |
| 309 var contextMenuUpdating = false; | 304 let contextMenuUpdating = false; |
| 310 | 305 |
| 311 var updateContextMenu = function() | 306 let updateContextMenu = () => |
| 312 { | 307 { |
| 313 if (contextMenuUpdating) | 308 if (contextMenuUpdating) |
| 314 return; | 309 return; |
| 315 | 310 |
| 316 contextMenuUpdating = true; | 311 contextMenuUpdating = true; |
| 317 | 312 |
| 318 chrome.tabs.query({active: true, lastFocusedWindow: true}, function(tabs) | 313 chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => |
| 319 { | 314 { |
| 320 chrome.contextMenus.removeAll(function() | 315 chrome.contextMenus.removeAll(() => |
| 321 { | 316 { |
| 322 contextMenuUpdating = false; | 317 contextMenuUpdating = false; |
| 323 | 318 |
| 324 if (tabs.length == 0) | 319 if (tabs.length == 0) |
| 325 return; | 320 return; |
| 326 | 321 |
| 327 var items = contextMenuItems.get({id: tabs[0].id}); | 322 let items = contextMenuItems.get({id: tabs[0].id}); |
| 328 | 323 |
| 329 if (!items) | 324 if (!items) |
| 330 return; | 325 return; |
| 331 | 326 |
| 332 items.forEach(function(item) | 327 items.forEach(item => |
| 333 { | 328 { |
| 334 chrome.contextMenus.create({ | 329 chrome.contextMenus.create({ |
| 335 title: item.title, | 330 title: item.title, |
| 336 contexts: item.contexts, | 331 contexts: item.contexts, |
| 337 onclick: function(info, tab) | 332 onclick(info, tab) |
| 338 { | 333 { |
| 339 item.onclick(new Page(tab)); | 334 item.onclick(new Page(tab)); |
| 340 } | 335 } |
| 341 }); | 336 }); |
| 342 }); | 337 }); |
| 343 }); | 338 }); |
| 344 }); | 339 }); |
| 345 }; | 340 }; |
| 346 | 341 |
| 347 var ContextMenus = function(page) | 342 let ContextMenus = function(page) |
| 348 { | 343 { |
| 349 this._page = page; | 344 this._page = page; |
| 350 }; | 345 }; |
| 351 ContextMenus.prototype = { | 346 ContextMenus.prototype = { |
| 352 create: function(item) | 347 create(item) |
| 353 { | 348 { |
| 354 var items = contextMenuItems.get(this._page); | 349 let items = contextMenuItems.get(this._page); |
| 355 if (!items) | 350 if (!items) |
| 356 contextMenuItems.set(this._page, items = []); | 351 contextMenuItems.set(this._page, items = []); |
| 357 | 352 |
| 358 items.push(item); | 353 items.push(item); |
| 359 updateContextMenu(); | 354 updateContextMenu(); |
| 360 }, | 355 }, |
| 361 remove: function(item) | 356 remove(item) |
| 362 { | 357 { |
| 363 let items = contextMenuItems.get(this._page); | 358 let items = contextMenuItems.get(this._page); |
| 364 if (items) | 359 if (items) |
| 365 { | 360 { |
| 366 let index = items.indexOf(item); | 361 let index = items.indexOf(item); |
| 367 if (index != -1) | 362 if (index != -1) |
| 368 { | 363 { |
| 369 items.splice(index, 1); | 364 items.splice(index, 1); |
| 370 updateContextMenu(); | 365 updateContextMenu(); |
| 371 } | 366 } |
| 372 } | 367 } |
| 373 } | 368 } |
| 374 }; | 369 }; |
| 375 | 370 |
| 376 chrome.tabs.onActivated.addListener(updateContextMenu); | 371 chrome.tabs.onActivated.addListener(updateContextMenu); |
| 377 | 372 |
| 378 chrome.windows.onFocusChanged.addListener(function(windowId) | 373 chrome.windows.onFocusChanged.addListener(windowId => |
| 379 { | 374 { |
| 380 if (windowId != chrome.windows.WINDOW_ID_NONE) | 375 if (windowId != chrome.windows.WINDOW_ID_NONE) |
| 381 updateContextMenu(); | 376 updateContextMenu(); |
| 382 }); | 377 }); |
| 383 | 378 |
| 384 | 379 |
| 385 /* Web requests */ | 380 /* Web requests */ |
| 386 | 381 |
| 387 var framesOfTabs = Object.create(null); | 382 let framesOfTabs = Object.create(null); |
| 388 | 383 |
| 389 ext.getFrame = function(tabId, frameId) | 384 ext.getFrame = (tabId, frameId) => |
| 390 { | 385 { |
| 391 return (framesOfTabs[tabId] || {})[frameId]; | 386 return (framesOfTabs[tabId] || {})[frameId]; |
| 392 }; | 387 }; |
| 393 | 388 |
| 394 var handlerBehaviorChangedQuota = chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANG
ED_CALLS_PER_10_MINUTES; | 389 let handlerBehaviorChangedQuota = chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANG
ED_CALLS_PER_10_MINUTES; |
| 395 | 390 |
| 396 function propagateHandlerBehaviorChange() | 391 function propagateHandlerBehaviorChange() |
| 397 { | 392 { |
| 398 // Make sure to not call handlerBehaviorChanged() more often than allowed | 393 // Make sure to not call handlerBehaviorChanged() more often than allowed |
| 399 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. | 394 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. |
| 400 // Otherwise Chrome notifies the user that this extension is causing issues. | 395 // Otherwise Chrome notifies the user that this extension is causing issues. |
| 401 if (handlerBehaviorChangedQuota > 0) | 396 if (handlerBehaviorChangedQuota > 0) |
| 402 { | 397 { |
| 403 chrome.webNavigation.onBeforeNavigate.removeListener(propagateHandlerBehav
iorChange); | 398 chrome.webNavigation.onBeforeNavigate.removeListener(propagateHandlerBehav
iorChange); |
| 404 chrome.webRequest.handlerBehaviorChanged(); | 399 chrome.webRequest.handlerBehaviorChanged(); |
| 405 | 400 |
| 406 handlerBehaviorChangedQuota--; | 401 handlerBehaviorChangedQuota--; |
| 407 setTimeout(function() { handlerBehaviorChangedQuota++; }, 600000); | 402 setTimeout(() => { handlerBehaviorChangedQuota++; }, 600000); |
| 408 } | 403 } |
| 409 } | 404 } |
| 410 | 405 |
| 411 ext.webRequest = { | 406 ext.webRequest = { |
| 412 onBeforeRequest: new ext._EventTarget(), | 407 onBeforeRequest: new ext._EventTarget(), |
| 413 handlerBehaviorChanged: function() | 408 handlerBehaviorChanged() |
| 414 { | 409 { |
| 415 // Defer handlerBehaviorChanged() until navigation occurs. | 410 // Defer handlerBehaviorChanged() until navigation occurs. |
| 416 // There wouldn't be any visible effect when calling it earlier, | 411 // There wouldn't be any visible effect when calling it earlier, |
| 417 // but it's an expensive operation and that way we avoid to call | 412 // but it's an expensive operation and that way we avoid to call |
| 418 // it multiple times, if multiple filters are added/removed. | 413 // it multiple times, if multiple filters are added/removed. |
| 419 var onBeforeNavigate = chrome.webNavigation.onBeforeNavigate; | 414 let onBeforeNavigate = chrome.webNavigation.onBeforeNavigate; |
| 420 if (!onBeforeNavigate.hasListener(propagateHandlerBehaviorChange)) | 415 if (!onBeforeNavigate.hasListener(propagateHandlerBehaviorChange)) |
| 421 onBeforeNavigate.addListener(propagateHandlerBehaviorChange); | 416 onBeforeNavigate.addListener(propagateHandlerBehaviorChange); |
| 422 } | 417 } |
| 423 }; | 418 }; |
| 424 | 419 |
| 425 chrome.tabs.query({}, function(tabs) | 420 chrome.tabs.query({}, tabs => |
| 426 { | 421 { |
| 427 tabs.forEach(function(tab) | 422 tabs.forEach(tab => |
| 428 { | 423 { |
| 429 chrome.webNavigation.getAllFrames({tabId: tab.id}, function(details) | 424 chrome.webNavigation.getAllFrames({tabId: tab.id}, details => |
| 430 { | 425 { |
| 431 if (details && details.length > 0) | 426 if (details && details.length > 0) |
| 432 { | 427 { |
| 433 var frames = framesOfTabs[tab.id] = Object.create(null); | 428 let frames = framesOfTabs[tab.id] = Object.create(null); |
| 434 | 429 |
| 435 for (var i = 0; i < details.length; i++) | 430 for (let i = 0; i < details.length; i++) |
| 436 frames[details[i].frameId] = {url: new URL(details[i].url), parent:
null}; | 431 frames[details[i].frameId] = {url: new URL(details[i].url), parent:
null}; |
| 437 | 432 |
| 438 for (var i = 0; i < details.length; i++) | 433 for (let i = 0; i < details.length; i++) |
| 439 { | 434 { |
| 440 var parentFrameId = details[i].parentFrameId; | 435 let parentFrameId = details[i].parentFrameId; |
| 441 | 436 |
| 442 if (parentFrameId != -1) | 437 if (parentFrameId != -1) |
| 443 frames[details[i].frameId].parent = frames[parentFrameId]; | 438 frames[details[i].frameId].parent = frames[parentFrameId]; |
| 444 } | 439 } |
| 445 } | 440 } |
| 446 }); | 441 }); |
| 447 }); | 442 }); |
| 448 }); | 443 }); |
| 449 | 444 |
| 450 chrome.webRequest.onBeforeRequest.addListener(function(details) | 445 chrome.webRequest.onBeforeRequest.addListener(details => |
| 451 { | 446 { |
| 452 // The high-level code isn't interested in requests that aren't | 447 // The high-level code isn't interested in requests that aren't |
| 453 // related to a tab or requests loading a top-level document, | 448 // related to a tab or requests loading a top-level document, |
| 454 // those should never be blocked. | 449 // those should never be blocked. |
| 455 if (details.tabId == -1 || details.type == "main_frame") | 450 if (details.tabId == -1 || details.type == "main_frame") |
| 456 return; | 451 return; |
| 457 | 452 |
| 458 // We are looking for the frame that contains the element which | 453 // We are looking for the frame that contains the element which |
| 459 // has triggered this request. For most requests (e.g. images) we | 454 // has triggered this request. For most requests (e.g. images) we |
| 460 // can just use the request's frame ID, but for subdocument requests | 455 // can just use the request's frame ID, but for subdocument requests |
| 461 // (e.g. iframes) we must instead use the request's parent frame ID. | 456 // (e.g. iframes) we must instead use the request's parent frame ID. |
| 462 var frameId; | 457 let frameId; |
| 463 var requestType; | 458 let requestType; |
| 464 if (details.type == "sub_frame") | 459 if (details.type == "sub_frame") |
| 465 { | 460 { |
| 466 frameId = details.parentFrameId; | 461 frameId = details.parentFrameId; |
| 467 requestType = "SUBDOCUMENT"; | 462 requestType = "SUBDOCUMENT"; |
| 468 } | 463 } |
| 469 else | 464 else |
| 470 { | 465 { |
| 471 frameId = details.frameId; | 466 frameId = details.frameId; |
| 472 requestType = details.type.toUpperCase(); | 467 requestType = details.type.toUpperCase(); |
| 473 } | 468 } |
| 474 | 469 |
| 475 var frame = ext.getFrame(details.tabId, frameId); | 470 let frame = ext.getFrame(details.tabId, frameId); |
| 476 if (frame) | 471 if (frame) |
| 477 { | 472 { |
| 478 var results = ext.webRequest.onBeforeRequest._dispatch( | 473 let results = ext.webRequest.onBeforeRequest._dispatch( |
| 479 new URL(details.url), | 474 new URL(details.url), |
| 480 requestType, | 475 requestType, |
| 481 new Page({id: details.tabId}), | 476 new Page({id: details.tabId}), |
| 482 frame | 477 frame |
| 483 ); | 478 ); |
| 484 | 479 |
| 485 if (results.indexOf(false) != -1) | 480 if (results.indexOf(false) != -1) |
| 486 return {cancel: true}; | 481 return {cancel: true}; |
| 487 } | 482 } |
| 488 }, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]); | 483 }, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]); |
| 489 | 484 |
| 490 | 485 |
| 491 /* Message passing */ | 486 /* Message passing */ |
| 492 | 487 |
| 493 chrome.runtime.onMessage.addListener(function(message, rawSender, sendResponse
) | 488 chrome.runtime.onMessage.addListener((message, rawSender, sendResponse) => |
| 494 { | 489 { |
| 495 var sender = {}; | 490 let sender = {}; |
| 496 | 491 |
| 497 // Add "page" and "frame" if the message was sent by a content script. | 492 // Add "page" and "frame" if the message was sent by a content script. |
| 498 // If sent by popup or the background page itself, there is no "tab". | 493 // If sent by popup or the background page itself, there is no "tab". |
| 499 if ("tab" in rawSender) | 494 if ("tab" in rawSender) |
| 500 { | 495 { |
| 501 sender.page = new Page(rawSender.tab); | 496 sender.page = new Page(rawSender.tab); |
| 502 sender.frame = { | 497 sender.frame = { |
| 503 url: new URL(rawSender.url), | 498 url: new URL(rawSender.url), |
| 504 get parent() | 499 get parent() |
| 505 { | 500 { |
| 506 var frames = framesOfTabs[rawSender.tab.id]; | 501 let frames = framesOfTabs[rawSender.tab.id]; |
| 507 | 502 |
| 508 if (!frames) | 503 if (!frames) |
| 509 return null; | 504 return null; |
| 510 | 505 |
| 511 var frame = frames[rawSender.frameId]; | 506 let frame = frames[rawSender.frameId]; |
| 512 if (frame) | 507 if (frame) |
| 513 return frame.parent; | 508 return frame.parent; |
| 514 | 509 |
| 515 return frames[0]; | 510 return frames[0]; |
| 516 } | 511 } |
| 517 }; | 512 }; |
| 518 } | 513 } |
| 519 | 514 |
| 520 return ext.onMessage._dispatch(message, sender, sendResponse).indexOf(true)
!= -1; | 515 return ext.onMessage._dispatch(message, sender, sendResponse).indexOf(true)
!= -1; |
| 521 }); | 516 }); |
| 522 | 517 |
| 523 | 518 |
| 524 /* Storage */ | 519 /* Storage */ |
| 525 | 520 |
| 526 ext.storage = { | 521 ext.storage = { |
| 527 get: function(keys, callback) | 522 get(keys, callback) |
| 528 { | 523 { |
| 529 chrome.storage.local.get(keys, callback); | 524 chrome.storage.local.get(keys, callback); |
| 530 }, | 525 }, |
| 531 set: function(key, value, callback) | 526 set(key, value, callback) |
| 532 { | 527 { |
| 533 let items = {}; | 528 let items = {}; |
| 534 items[key] = value; | 529 items[key] = value; |
| 535 chrome.storage.local.set(items, callback); | 530 chrome.storage.local.set(items, callback); |
| 536 }, | 531 }, |
| 537 remove: function(key, callback) | 532 remove(key, callback) |
| 538 { | 533 { |
| 539 chrome.storage.local.remove(key, callback); | 534 chrome.storage.local.remove(key, callback); |
| 540 }, | 535 }, |
| 541 onChanged: chrome.storage.onChanged | 536 onChanged: chrome.storage.onChanged |
| 542 }; | 537 }; |
| 543 | 538 |
| 544 /* Options */ | 539 /* Options */ |
| 545 | 540 |
| 546 if ("openOptionsPage" in chrome.runtime) | 541 if ("openOptionsPage" in chrome.runtime) |
| 547 { | 542 { |
| 548 ext.showOptions = chrome.runtime.openOptionsPage; | 543 ext.showOptions = callback => |
| 544 { |
| 545 if (!callback) |
| 546 { |
| 547 chrome.runtime.openOptionsPage(); |
| 548 } |
| 549 else |
| 550 { |
| 551 chrome.runtime.openOptionsPage(() => |
| 552 { |
| 553 if (chrome.runtime.lastError) |
| 554 return; |
| 555 |
| 556 chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => |
| 557 { |
| 558 if (tabs.length > 0) |
| 559 { |
| 560 window.setTimeout(() => |
| 561 { |
| 562 callback(new Page(tabs[0])); |
| 563 }); |
| 564 } |
| 565 }); |
| 566 }); |
| 567 } |
| 568 }; |
| 549 } | 569 } |
| 550 else | 570 else |
| 551 { | 571 { |
| 552 // Edge does not yet support runtime.openOptionsPage (tested version 38) | 572 // Edge does not yet support runtime.openOptionsPage (tested version 38) |
| 553 // and so this workaround needs to stay for now. | 573 // and so this workaround needs to stay for now. |
| 554 ext.showOptions = function(callback) | 574 ext.showOptions = callback => |
| 555 { | 575 { |
| 556 chrome.windows.getLastFocused(function(win) | 576 chrome.windows.getLastFocused(win => |
| 557 { | 577 { |
| 558 var optionsUrl = chrome.extension.getURL("options.html"); | 578 let optionsUrl = chrome.extension.getURL("options.html"); |
| 559 var queryInfo = {url: optionsUrl}; | 579 let queryInfo = {url: optionsUrl}; |
| 560 | 580 |
| 561 // extension pages can't be accessed in incognito windows. In order to | 581 // extension pages can't be accessed in incognito windows. In order to |
| 562 // correctly mimic the way in which Chrome opens extension options, | 582 // correctly mimic the way in which Chrome opens extension options, |
| 563 // we have to focus the options page in any other window. | 583 // we have to focus the options page in any other window. |
| 564 if (!win.incognito) | 584 if (!win.incognito) |
| 565 queryInfo.windowId = win.id; | 585 queryInfo.windowId = win.id; |
| 566 | 586 |
| 567 chrome.tabs.query(queryInfo, function(tabs) | 587 chrome.tabs.query(queryInfo, tabs => |
| 568 { | 588 { |
| 569 if (tabs.length > 0) | 589 if (tabs.length > 0) |
| 570 { | 590 { |
| 571 var tab = tabs[0]; | 591 let tab = tabs[0]; |
| 572 | 592 |
| 573 chrome.windows.update(tab.windowId, {focused: true}); | 593 chrome.windows.update(tab.windowId, {focused: true}); |
| 574 chrome.tabs.update(tab.id, {active: true}); | 594 chrome.tabs.update(tab.id, {active: true}); |
| 575 | 595 |
| 576 if (callback) | 596 if (callback) |
| 577 callback(new Page(tab)); | 597 callback(new Page(tab)); |
| 578 } | 598 } |
| 579 else | 599 else |
| 580 { | 600 { |
| 581 ext.pages.open(optionsUrl, callback); | 601 ext.pages.open(optionsUrl, callback); |
| 582 } | 602 } |
| 583 }); | 603 }); |
| 584 }); | 604 }); |
| 585 }; | 605 }; |
| 586 } | 606 } |
| 587 | 607 |
| 588 /* Windows */ | 608 /* Windows */ |
| 589 ext.windows = { | 609 ext.windows = { |
| 590 create: function(createData, callback) | 610 create(createData, callback) |
| 591 { | 611 { |
| 592 chrome.windows.create(createData, function(createdWindow) | 612 chrome.windows.create(createData, createdWindow => |
| 593 { | 613 { |
| 594 afterTabLoaded(callback)(createdWindow.tabs[0]); | 614 afterTabLoaded(callback)(createdWindow.tabs[0]); |
| 595 }); | 615 }); |
| 596 } | 616 } |
| 597 }; | 617 }; |
| 598 })(); | 618 } |
| LEFT | RIGHT |