LEFT | RIGHT |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-2017 eyeo GmbH | 3 * Copyright (C) 2006-present 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() | |
21 { | 20 { |
22 let nonEmptyPageMaps = new Set(); | 21 let nonEmptyPageMaps = new Set(); |
23 | 22 |
24 let PageMap = ext.PageMap = function() | 23 let PageMap = ext.PageMap = function() |
25 { | 24 { |
26 this._map = new Map(); | 25 this._map = new Map(); |
27 }; | 26 }; |
28 PageMap.prototype = { | 27 PageMap.prototype = { |
29 _delete(id) | 28 _delete(id) |
30 { | 29 { |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
94 let frame = frames.get(0); | 93 let frame = frames.get(0); |
95 if (frame) | 94 if (frame) |
96 return frame.url; | 95 return frame.url; |
97 } | 96 } |
98 }, | 97 }, |
99 sendMessage(message, responseCallback, frameId) | 98 sendMessage(message, responseCallback, frameId) |
100 { | 99 { |
101 let options = {}; | 100 let options = {}; |
102 if (typeof frameId != "undefined") | 101 if (typeof frameId != "undefined") |
103 options.frameId = frameId; | 102 options.frameId = frameId; |
104 chrome.tabs.sendMessage(this.id, message, options, responseCallback); | 103 browser.tabs.sendMessage(this.id, message, options, responseCallback); |
105 } | 104 } |
106 }; | 105 }; |
107 | 106 |
108 ext.getPage = id => new Page({id: parseInt(id, 10)}); | 107 ext.getPage = id => new Page({id: parseInt(id, 10)}); |
109 | 108 |
110 function afterTabLoaded(callback) | 109 function afterTabLoaded(callback) |
111 { | 110 { |
112 return openedTab => | 111 return openedTab => |
113 { | 112 { |
114 let onUpdated = (tabId, changeInfo, tab) => | 113 let onUpdated = (tabId, changeInfo, tab) => |
115 { | 114 { |
116 if (tabId == openedTab.id && changeInfo.status == "complete") | 115 if (tabId == openedTab.id && changeInfo.status == "complete") |
117 { | 116 { |
118 chrome.tabs.onUpdated.removeListener(onUpdated); | 117 browser.tabs.onUpdated.removeListener(onUpdated); |
119 callback(new Page(openedTab)); | 118 callback(new Page(openedTab)); |
120 } | 119 } |
121 }; | 120 }; |
122 chrome.tabs.onUpdated.addListener(onUpdated); | 121 browser.tabs.onUpdated.addListener(onUpdated); |
123 }; | 122 }; |
124 } | 123 } |
125 | 124 |
126 ext.pages = { | 125 ext.pages = { |
127 open(url, callback) | |
128 { | |
129 chrome.tabs.create({url}, callback && afterTabLoaded(callback)); | |
130 }, | |
131 query(info, callback) | |
132 { | |
133 let rawInfo = {}; | |
134 for (let property in info) | |
135 { | |
136 switch (property) | |
137 { | |
138 case "active": | |
139 case "lastFocusedWindow": | |
140 rawInfo[property] = info[property]; | |
141 } | |
142 } | |
143 | |
144 chrome.tabs.query(rawInfo, tabs => | |
145 { | |
146 callback(tabs.map(tab => new Page(tab))); | |
147 }); | |
148 }, | |
149 onLoading: new ext._EventTarget(), | 126 onLoading: new ext._EventTarget(), |
150 onActivated: new ext._EventTarget(), | 127 onActivated: new ext._EventTarget(), |
151 onRemoved: new ext._EventTarget() | 128 onRemoved: new ext._EventTarget() |
152 }; | 129 }; |
153 | 130 |
154 chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => | 131 browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => |
155 { | 132 { |
156 if (changeInfo.status == "loading") | 133 if (changeInfo.status == "loading") |
157 ext.pages.onLoading._dispatch(new Page(tab)); | 134 ext.pages.onLoading._dispatch(new Page(tab)); |
158 }); | 135 }); |
159 | 136 |
160 function createFrame(tabId, frameId) | 137 function createFrame(tabId, frameId) |
161 { | 138 { |
162 let frames = framesOfTabs.get(tabId); | 139 let frames = framesOfTabs.get(tabId); |
163 if (!frames) | 140 if (!frames) |
164 { | 141 { |
(...skipping 12 matching lines...) Expand all Loading... |
177 } | 154 } |
178 | 155 |
179 function updatePageFrameStructure(frameId, tabId, url, parentFrameId) | 156 function updatePageFrameStructure(frameId, tabId, url, parentFrameId) |
180 { | 157 { |
181 if (frameId == 0) | 158 if (frameId == 0) |
182 { | 159 { |
183 let page = new Page({id: tabId, url}); | 160 let page = new Page({id: tabId, url}); |
184 | 161 |
185 ext._removeFromAllPageMaps(tabId); | 162 ext._removeFromAllPageMaps(tabId); |
186 | 163 |
187 chrome.tabs.get(tabId, () => | 164 browser.tabs.get(tabId, () => |
188 { | 165 { |
189 // If the tab is prerendered, chrome.tabs.get() sets | 166 // If the tab is prerendered, browser.tabs.get() sets |
190 // chrome.runtime.lastError and we have to dispatch the onLoading event, | 167 // browser.runtime.lastError and we have to dispatch the onLoading |
191 // since the onUpdated event isn't dispatched for prerendered tabs. | 168 // event, since the onUpdated event isn't dispatched for prerendered |
192 // However, we have to keep relying on the unUpdated event for tabs that | 169 // tabs. However, we have to keep relying on the onUpdated event for |
193 // are already visible. Otherwise browser action changes get overridden | 170 // tabs that are already visible. Otherwise browser action changes get |
194 // when Chrome automatically resets them on navigation. | 171 // overridden when Chrome automatically resets them on navigation. |
195 if (chrome.runtime.lastError) | 172 if (browser.runtime.lastError) |
196 ext.pages.onLoading._dispatch(page); | 173 ext.pages.onLoading._dispatch(page); |
197 }); | 174 }); |
198 } | 175 } |
199 | 176 |
200 // Update frame URL and parent in frame structure | 177 // Update frame URL and parent in frame structure |
201 let frame = createFrame(tabId, frameId); | 178 let frame = createFrame(tabId, frameId); |
202 frame.url = new URL(url); | 179 frame.url = new URL(url); |
203 | 180 |
204 let parentFrame = framesOfTabs.get(tabId).get(parentFrameId); | 181 let parentFrame = framesOfTabs.get(tabId).get(parentFrameId); |
205 if (parentFrame) | 182 if (parentFrame) |
206 frame.parent = parentFrame; | 183 frame.parent = parentFrame; |
207 } | 184 } |
208 | 185 |
209 chrome.webRequest.onHeadersReceived.addListener(details => | 186 browser.webRequest.onHeadersReceived.addListener(details => |
210 { | 187 { |
211 // We have to update the frame structure when switching to a new | 188 // We have to update the frame structure when switching to a new |
212 // document, so that we process any further requests made by that | 189 // document, so that we process any further requests made by that |
213 // document in the right context. Unfortunately, we cannot rely | 190 // document in the right context. Unfortunately, we cannot rely |
214 // on webNavigation.onCommitted since it isn't guaranteed to fire | 191 // on webNavigation.onCommitted since it isn't guaranteed to fire |
215 // before any subresources start downloading[1]. As an | 192 // before any subresources start downloading[1]. As an |
216 // alternative we use webRequest.onHeadersReceived for HTTP(S) | 193 // alternative we use webRequest.onHeadersReceived for HTTP(S) |
217 // URLs, being careful to ignore any responses that won't cause | 194 // URLs, being careful to ignore any responses that won't cause |
218 // the document to be replaced. | 195 // the document to be replaced. |
219 // [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=665843 | 196 // [1] - https://bugs.chromium.org/p/chromium/issues/detail?id=665843 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
277 return; | 254 return; |
278 } | 255 } |
279 } | 256 } |
280 | 257 |
281 updatePageFrameStructure(details.frameId, details.tabId, details.url, | 258 updatePageFrameStructure(details.frameId, details.tabId, details.url, |
282 details.parentFrameId); | 259 details.parentFrameId); |
283 }, | 260 }, |
284 {types: ["main_frame", "sub_frame"], urls: ["http://*/*", "https://*/*"]}, | 261 {types: ["main_frame", "sub_frame"], urls: ["http://*/*", "https://*/*"]}, |
285 ["responseHeaders"]); | 262 ["responseHeaders"]); |
286 | 263 |
287 chrome.webNavigation.onBeforeNavigate.addListener(details => | 264 browser.webNavigation.onBeforeNavigate.addListener(details => |
288 { | 265 { |
289 // Since we can only listen for HTTP(S) responses using | 266 // Since we can only listen for HTTP(S) responses using |
290 // webRequest.onHeadersReceived we must update the page structure here for | 267 // webRequest.onHeadersReceived we must update the page structure here for |
291 // other navigations. | 268 // other navigations. |
292 let url = new URL(details.url); | 269 let url = new URL(details.url); |
293 if (url.protocol != "http:" && url.protocol != "https:") | 270 if (url.protocol != "http:" && url.protocol != "https:") |
294 { | 271 { |
295 updatePageFrameStructure(details.frameId, details.tabId, details.url, | 272 updatePageFrameStructure(details.frameId, details.tabId, details.url, |
296 details.parentFrameId); | 273 details.parentFrameId); |
297 } | 274 } |
298 }); | 275 }); |
299 | 276 |
300 function forgetTab(tabId) | 277 function forgetTab(tabId) |
301 { | 278 { |
302 ext.pages.onRemoved._dispatch(tabId); | 279 ext.pages.onRemoved._dispatch(tabId); |
303 | 280 |
304 ext._removeFromAllPageMaps(tabId); | 281 ext._removeFromAllPageMaps(tabId); |
305 framesOfTabs.delete(tabId); | 282 framesOfTabs.delete(tabId); |
306 } | 283 } |
307 | 284 |
308 chrome.tabs.onReplaced.addListener((addedTabId, removedTabId) => | 285 browser.tabs.onReplaced.addListener((addedTabId, removedTabId) => |
309 { | 286 { |
310 forgetTab(removedTabId); | 287 forgetTab(removedTabId); |
311 }); | 288 }); |
312 | 289 |
313 chrome.tabs.onRemoved.addListener(forgetTab); | 290 browser.tabs.onRemoved.addListener(forgetTab); |
314 | 291 |
315 chrome.tabs.onActivated.addListener(details => | 292 browser.tabs.onActivated.addListener(details => |
316 { | 293 { |
317 ext.pages.onActivated._dispatch(new Page({id: details.tabId})); | 294 ext.pages.onActivated._dispatch(new Page({id: details.tabId})); |
318 }); | 295 }); |
319 | 296 |
320 | 297 |
321 /* Browser actions */ | 298 /* Browser actions */ |
322 | 299 |
323 let BrowserAction = function(tabId) | 300 let BrowserAction = function(tabId) |
324 { | 301 { |
325 this._tabId = tabId; | 302 this._tabId = tabId; |
326 this._changes = null; | 303 this._changes = null; |
327 }; | 304 }; |
328 BrowserAction.prototype = { | 305 BrowserAction.prototype = { |
329 _applyChanges() | 306 _applyChanges() |
330 { | 307 { |
331 if ("iconPath" in this._changes) | 308 if ("iconPath" in this._changes) |
332 { | 309 { |
333 chrome.browserAction.setIcon({ | 310 // Firefox for Android displays the browser action not as an icon but |
334 tabId: this._tabId, | 311 // as a menu item. There is no icon, but such an option may be added in |
335 path: { | 312 // the future. |
| 313 // https://bugzilla.mozilla.org/show_bug.cgi?id=1331746 |
| 314 if ("setIcon" in browser.browserAction) |
| 315 { |
| 316 let path = { |
336 16: this._changes.iconPath.replace("$size", "16"), | 317 16: this._changes.iconPath.replace("$size", "16"), |
337 19: this._changes.iconPath.replace("$size", "19"), | 318 19: this._changes.iconPath.replace("$size", "19"), |
338 20: this._changes.iconPath.replace("$size", "20"), | 319 20: this._changes.iconPath.replace("$size", "20"), |
339 32: this._changes.iconPath.replace("$size", "32"), | 320 32: this._changes.iconPath.replace("$size", "32"), |
340 38: this._changes.iconPath.replace("$size", "38"), | 321 38: this._changes.iconPath.replace("$size", "38"), |
341 40: this._changes.iconPath.replace("$size", "40") | 322 40: this._changes.iconPath.replace("$size", "40") |
| 323 }; |
| 324 try |
| 325 { |
| 326 browser.browserAction.setIcon({tabId: this._tabId, path}); |
342 } | 327 } |
343 }); | 328 catch (e) |
| 329 { |
| 330 // Edge throws if passed icon sizes different than 19,20,38,40px. |
| 331 delete path[16]; |
| 332 delete path[32]; |
| 333 browser.browserAction.setIcon({tabId: this._tabId, path}); |
| 334 } |
| 335 } |
344 } | 336 } |
345 | 337 |
346 if ("badgeText" in this._changes) | 338 if ("badgeText" in this._changes) |
347 { | 339 { |
348 chrome.browserAction.setBadgeText({ | 340 // There is no badge on Firefox for Android; the browser action is |
349 tabId: this._tabId, | 341 // simply a menu item. |
350 text: this._changes.badgeText | 342 if ("setBadgeText" in browser.browserAction) |
351 }); | 343 { |
| 344 browser.browserAction.setBadgeText({ |
| 345 tabId: this._tabId, |
| 346 text: this._changes.badgeText |
| 347 }); |
| 348 } |
352 } | 349 } |
353 | 350 |
354 if ("badgeColor" in this._changes) | 351 if ("badgeColor" in this._changes) |
355 { | 352 { |
356 chrome.browserAction.setBadgeBackgroundColor({ | 353 // There is no badge on Firefox for Android; the browser action is |
357 tabId: this._tabId, | 354 // simply a menu item. |
358 color: this._changes.badgeColor | 355 if ("setBadgeBackgroundColor" in browser.browserAction) |
359 }); | 356 { |
| 357 browser.browserAction.setBadgeBackgroundColor({ |
| 358 tabId: this._tabId, |
| 359 color: this._changes.badgeColor |
| 360 }); |
| 361 } |
360 } | 362 } |
361 | 363 |
362 this._changes = null; | 364 this._changes = null; |
363 }, | 365 }, |
364 _queueChanges() | 366 _queueChanges() |
365 { | 367 { |
366 chrome.tabs.get(this._tabId, () => | 368 browser.tabs.get(this._tabId, () => |
367 { | 369 { |
368 // If the tab is prerendered, chrome.tabs.get() sets | 370 // If the tab is prerendered, browser.tabs.get() sets |
369 // chrome.runtime.lastError and we have to delay our changes | 371 // browser.runtime.lastError and we have to delay our changes |
370 // until the currently visible tab is replaced with the | 372 // until the currently visible tab is replaced with the |
371 // prerendered tab. Otherwise chrome.browserAction.set* fails. | 373 // prerendered tab. Otherwise browser.browserAction.set* fails. |
372 if (chrome.runtime.lastError) | 374 if (browser.runtime.lastError) |
373 { | 375 { |
374 let onReplaced = (addedTabId, removedTabId) => | 376 let onReplaced = (addedTabId, removedTabId) => |
375 { | 377 { |
376 if (addedTabId == this._tabId) | 378 if (addedTabId == this._tabId) |
377 { | 379 { |
378 chrome.tabs.onReplaced.removeListener(onReplaced); | 380 browser.tabs.onReplaced.removeListener(onReplaced); |
379 this._applyChanges(); | 381 this._applyChanges(); |
380 } | 382 } |
381 }; | 383 }; |
382 chrome.tabs.onReplaced.addListener(onReplaced); | 384 browser.tabs.onReplaced.addListener(onReplaced); |
383 } | 385 } |
384 else | 386 else |
385 { | 387 { |
386 this._applyChanges(); | 388 this._applyChanges(); |
387 } | 389 } |
388 }); | 390 }); |
389 }, | 391 }, |
390 _addChange(name, value) | 392 _addChange(name, value) |
391 { | 393 { |
392 if (!this._changes) | 394 if (!this._changes) |
(...skipping 26 matching lines...) Expand all Loading... |
419 }; | 421 }; |
420 | 422 |
421 | 423 |
422 /* Context menus */ | 424 /* Context menus */ |
423 | 425 |
424 let contextMenuItems = new ext.PageMap(); | 426 let contextMenuItems = new ext.PageMap(); |
425 let contextMenuUpdating = false; | 427 let contextMenuUpdating = false; |
426 | 428 |
427 let updateContextMenu = () => | 429 let updateContextMenu = () => |
428 { | 430 { |
429 if (contextMenuUpdating) | 431 // Firefox for Android does not support context menus. |
| 432 // https://bugzilla.mozilla.org/show_bug.cgi?id=1269062 |
| 433 if (!("contextMenus" in browser) || contextMenuUpdating) |
430 return; | 434 return; |
431 | 435 |
432 contextMenuUpdating = true; | 436 contextMenuUpdating = true; |
433 | 437 |
434 chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => | 438 browser.tabs.query({active: true, lastFocusedWindow: true}, tabs => |
435 { | 439 { |
436 chrome.contextMenus.removeAll(() => | 440 browser.contextMenus.removeAll(() => |
437 { | 441 { |
438 contextMenuUpdating = false; | 442 contextMenuUpdating = false; |
439 | 443 |
440 if (tabs.length == 0) | 444 if (tabs.length == 0) |
441 return; | 445 return; |
442 | 446 |
443 let items = contextMenuItems.get({id: tabs[0].id}); | 447 let items = contextMenuItems.get({id: tabs[0].id}); |
444 | 448 |
445 if (!items) | 449 if (!items) |
446 return; | 450 return; |
447 | 451 |
448 items.forEach(item => | 452 items.forEach(item => |
449 { | 453 { |
450 chrome.contextMenus.create({ | 454 browser.contextMenus.create({ |
451 title: item.title, | 455 title: item.title, |
452 contexts: item.contexts, | 456 contexts: item.contexts, |
453 onclick(info, tab) | 457 onclick(info, tab) |
454 { | 458 { |
455 item.onclick(new Page(tab), info); | 459 item.onclick(new Page(tab), info); |
456 } | 460 } |
457 }); | 461 }); |
458 }); | 462 }); |
459 }); | 463 }); |
460 }); | 464 }); |
(...skipping 21 matching lines...) Expand all Loading... |
482 let index = items.indexOf(item); | 486 let index = items.indexOf(item); |
483 if (index != -1) | 487 if (index != -1) |
484 { | 488 { |
485 items.splice(index, 1); | 489 items.splice(index, 1); |
486 updateContextMenu(); | 490 updateContextMenu(); |
487 } | 491 } |
488 } | 492 } |
489 } | 493 } |
490 }; | 494 }; |
491 | 495 |
492 chrome.tabs.onActivated.addListener(updateContextMenu); | 496 browser.tabs.onActivated.addListener(updateContextMenu); |
493 | 497 |
494 chrome.windows.onFocusChanged.addListener(windowId => | 498 if ("windows" in browser) |
495 { | 499 { |
496 if (windowId != chrome.windows.WINDOW_ID_NONE) | 500 browser.windows.onFocusChanged.addListener(windowId => |
497 updateContextMenu(); | 501 { |
498 }); | 502 if (windowId != browser.windows.WINDOW_ID_NONE) |
| 503 updateContextMenu(); |
| 504 }); |
| 505 } |
499 | 506 |
500 | 507 |
501 /* Web requests */ | 508 /* Web requests */ |
502 | 509 |
503 let framesOfTabs = new Map(); | 510 let framesOfTabs = new Map(); |
504 | 511 |
505 ext.getFrame = (tabId, frameId) => | 512 ext.getFrame = (tabId, frameId) => |
506 { | 513 { |
507 let frames = framesOfTabs.get(tabId); | 514 let frames = framesOfTabs.get(tabId); |
508 return frames && frames.get(frameId); | 515 return frames && frames.get(frameId); |
509 }; | 516 }; |
510 | 517 |
511 let handlerBehaviorChangedQuota = | 518 let handlerBehaviorChangedQuota = |
512 chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES; | 519 browser.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES; |
513 | 520 |
514 function propagateHandlerBehaviorChange() | 521 function propagateHandlerBehaviorChange() |
515 { | 522 { |
516 // Make sure to not call handlerBehaviorChanged() more often than allowed | 523 // Make sure to not call handlerBehaviorChanged() more often than allowed |
517 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. | 524 // by browser.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. |
518 // Otherwise Chrome notifies the user that this extension is causing issues. | 525 // Otherwise Chrome notifies the user that this extension is causing issues. |
519 if (handlerBehaviorChangedQuota > 0) | 526 if (handlerBehaviorChangedQuota > 0) |
520 { | 527 { |
521 chrome.webNavigation.onBeforeNavigate.removeListener( | 528 browser.webNavigation.onBeforeNavigate.removeListener( |
522 propagateHandlerBehaviorChange | 529 propagateHandlerBehaviorChange |
523 ); | 530 ); |
524 chrome.webRequest.handlerBehaviorChanged(); | 531 browser.webRequest.handlerBehaviorChanged(); |
525 | 532 |
526 handlerBehaviorChangedQuota--; | 533 handlerBehaviorChangedQuota--; |
527 setTimeout(() => { handlerBehaviorChangedQuota++; }, 600000); | 534 setTimeout(() => { handlerBehaviorChangedQuota++; }, 600000); |
528 } | 535 } |
529 } | 536 } |
530 | 537 |
531 ext.webRequest = { | 538 ext.webRequest = { |
532 onBeforeRequest: new ext._EventTarget(), | 539 onBeforeRequest: new ext._EventTarget(), |
533 handlerBehaviorChanged() | 540 handlerBehaviorChanged() |
534 { | 541 { |
535 // Defer handlerBehaviorChanged() until navigation occurs. | 542 // Defer handlerBehaviorChanged() until navigation occurs. |
536 // There wouldn't be any visible effect when calling it earlier, | 543 // There wouldn't be any visible effect when calling it earlier, |
537 // but it's an expensive operation and that way we avoid to call | 544 // but it's an expensive operation and that way we avoid to call |
538 // it multiple times, if multiple filters are added/removed. | 545 // it multiple times, if multiple filters are added/removed. |
539 let {onBeforeNavigate} = chrome.webNavigation; | 546 let {onBeforeNavigate} = browser.webNavigation; |
540 if (!onBeforeNavigate.hasListener(propagateHandlerBehaviorChange)) | 547 if (!onBeforeNavigate.hasListener(propagateHandlerBehaviorChange)) |
541 onBeforeNavigate.addListener(propagateHandlerBehaviorChange); | 548 onBeforeNavigate.addListener(propagateHandlerBehaviorChange); |
542 } | 549 } |
543 }; | 550 }; |
544 | 551 |
545 chrome.tabs.query({}, tabs => | 552 browser.tabs.query({}, tabs => |
546 { | 553 { |
547 tabs.forEach(tab => | 554 tabs.forEach(tab => |
548 { | 555 { |
549 chrome.webNavigation.getAllFrames({tabId: tab.id}, details => | 556 browser.webNavigation.getAllFrames({tabId: tab.id}, details => |
550 { | 557 { |
551 if (details && details.length > 0) | 558 if (details && details.length > 0) |
552 { | 559 { |
553 let frames = new Map(); | 560 let frames = new Map(); |
554 framesOfTabs.set(tab.id, frames); | 561 framesOfTabs.set(tab.id, frames); |
555 | 562 |
556 for (let detail of details) | 563 for (let detail of details) |
557 { | 564 { |
558 let frame = {url: new URL(detail.url)}; | 565 let frame = {url: new URL(detail.url)}; |
559 frames.set(detail.frameId, frame); | 566 frames.set(detail.frameId, frame); |
560 | 567 |
561 if (detail.parentFrameId != -1) | 568 if (detail.parentFrameId != -1) |
562 frame.parent = frames.get(detail.parentFrameId); | 569 frame.parent = frames.get(detail.parentFrameId); |
563 } | 570 } |
564 } | 571 } |
565 }); | 572 }); |
566 }); | 573 }); |
567 }); | 574 }); |
568 | 575 |
569 chrome.webRequest.onBeforeRequest.addListener(details => | 576 browser.webRequest.onBeforeRequest.addListener(details => |
570 { | 577 { |
571 // The high-level code isn't interested in requests that aren't | 578 // The high-level code isn't interested in requests that aren't |
572 // related to a tab or requests loading a top-level document, | 579 // related to a tab or requests loading a top-level document, |
573 // those should never be blocked. | 580 // those should never be blocked. |
574 if (details.type == "main_frame") | 581 if (details.type == "main_frame") |
575 return; | 582 return; |
576 | 583 |
577 // Filter out requests from non web protocols. Ideally, we'd explicitly | 584 // Filter out requests from non web protocols. Ideally, we'd explicitly |
578 // specify the protocols we are interested in (i.e. http://, https://, | 585 // specify the protocols we are interested in (i.e. http://, https://, |
579 // ws:// and wss://) with the url patterns, given below, when adding this | 586 // ws:// and wss://) with the url patterns, given below, when adding this |
(...skipping 23 matching lines...) Expand all Loading... |
603 } | 610 } |
604 | 611 |
605 if (ext.webRequest.onBeforeRequest._dispatch( | 612 if (ext.webRequest.onBeforeRequest._dispatch( |
606 url, type, page, frame).includes(false)) | 613 url, type, page, frame).includes(false)) |
607 return {cancel: true}; | 614 return {cancel: true}; |
608 }, {urls: ["<all_urls>"]}, ["blocking"]); | 615 }, {urls: ["<all_urls>"]}, ["blocking"]); |
609 | 616 |
610 | 617 |
611 /* Message passing */ | 618 /* Message passing */ |
612 | 619 |
613 chrome.runtime.onMessage.addListener((message, rawSender, sendResponse) => | 620 browser.runtime.onMessage.addListener((message, rawSender, sendResponse) => |
614 { | 621 { |
615 let sender = {}; | 622 let sender = {}; |
616 | 623 |
617 // Add "page" and "frame" if the message was sent by a content script. | 624 // Add "page" and "frame" if the message was sent by a content script. |
618 // If sent by popup or the background page itself, there is no "tab". | 625 // If sent by popup or the background page itself, there is no "tab". |
619 if ("tab" in rawSender) | 626 if ("tab" in rawSender) |
620 { | 627 { |
621 sender.page = new Page(rawSender.tab); | 628 sender.page = new Page(rawSender.tab); |
622 sender.frame = { | 629 sender.frame = { |
623 id: rawSender.frameId, | 630 id: rawSender.frameId, |
(...skipping 11 matching lines...) Expand all Loading... |
635 if (frame) | 642 if (frame) |
636 return frame.parent || null; | 643 return frame.parent || null; |
637 | 644 |
638 return frames.get(0) || null; | 645 return frames.get(0) || null; |
639 } | 646 } |
640 }; | 647 }; |
641 } | 648 } |
642 | 649 |
643 return ext.onMessage._dispatch( | 650 return ext.onMessage._dispatch( |
644 message, sender, sendResponse | 651 message, sender, sendResponse |
645 ).indexOf(true) != -1; | 652 ).includes(true); |
646 }); | 653 }); |
647 | 654 |
648 | 655 |
649 /* Storage */ | 656 /* Storage */ |
650 | 657 |
651 ext.storage = { | 658 ext.storage = { |
652 get(keys, callback) | 659 get(keys, callback) |
653 { | 660 { |
654 chrome.storage.local.get(keys, callback); | 661 browser.storage.local.get(keys, callback); |
655 }, | 662 }, |
656 set(key, value, callback) | 663 set(key, value, callback) |
657 { | 664 { |
658 let items = {}; | 665 let items = {}; |
659 items[key] = value; | 666 items[key] = value; |
660 chrome.storage.local.set(items, callback); | 667 browser.storage.local.set(items, callback); |
661 }, | 668 }, |
662 remove(key, callback) | 669 remove(key, callback) |
663 { | 670 { |
664 chrome.storage.local.remove(key, callback); | 671 browser.storage.local.remove(key, callback); |
665 }, | 672 }, |
666 onChanged: chrome.storage.onChanged | 673 onChanged: browser.storage.onChanged |
667 }; | 674 }; |
668 | |
669 /* Options */ | |
670 | |
671 if ("openOptionsPage" in chrome.runtime) | |
672 { | |
673 ext.showOptions = callback => | |
674 { | |
675 if (!callback) | |
676 { | |
677 chrome.runtime.openOptionsPage(); | |
678 } | |
679 else | |
680 { | |
681 chrome.runtime.openOptionsPage(() => | |
682 { | |
683 if (chrome.runtime.lastError) | |
684 return; | |
685 | |
686 chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => | |
687 { | |
688 if (tabs.length > 0) | |
689 { | |
690 if (tabs[0].status == "complete") | |
691 callback(new Page(tabs[0])); | |
692 else | |
693 afterTabLoaded(callback)(tabs[0]); | |
694 } | |
695 }); | |
696 }); | |
697 } | |
698 }; | |
699 } | |
700 else | |
701 { | |
702 // Edge does not yet support runtime.openOptionsPage (tested version 38) | |
703 // and so this workaround needs to stay for now. | |
704 // We are not using extension.getURL to get the absolute path here | |
705 // because of the Edge issue: | |
706 // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/1027
6332/ | |
707 ext.showOptions = callback => | |
708 { | |
709 chrome.windows.getLastFocused(win => | |
710 { | |
711 let optionsUrl = "options.html"; | |
712 let queryInfo = {url: optionsUrl}; | |
713 | |
714 // extension pages can't be accessed in incognito windows. In order to | |
715 // correctly mimic the way in which Chrome opens extension options, | |
716 // we have to focus the options page in any other window. | |
717 if (!win.incognito) | |
718 queryInfo.windowId = win.id; | |
719 | |
720 chrome.tabs.query(queryInfo, tabs => | |
721 { | |
722 if (tabs.length > 0) | |
723 { | |
724 let tab = tabs[0]; | |
725 | |
726 chrome.windows.update(tab.windowId, {focused: true}); | |
727 chrome.tabs.update(tab.id, {active: true}); | |
728 | |
729 if (callback) | |
730 callback(new Page(tab)); | |
731 } | |
732 else | |
733 { | |
734 ext.pages.open(optionsUrl, callback); | |
735 } | |
736 }); | |
737 }); | |
738 }; | |
739 } | |
740 | 675 |
741 /* Windows */ | 676 /* Windows */ |
742 ext.windows = { | 677 ext.windows = { |
743 create(createData, callback) | 678 create(createData, callback) |
744 { | 679 { |
745 chrome.windows.create(createData, createdWindow => | 680 browser.windows.create(createData, createdWindow => |
746 { | 681 { |
747 afterTabLoaded(callback)(createdWindow.tabs[0]); | 682 afterTabLoaded(callback)(createdWindow.tabs[0]); |
748 }); | 683 }); |
749 } | 684 } |
750 }; | 685 }; |
751 }()); | 686 } |
LEFT | RIGHT |