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 |