OLD | NEW |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-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 "use strict"; | 18 "use strict"; |
19 | 19 |
| 20 (function() |
20 { | 21 { |
21 /* Pages */ | 22 /* Pages */ |
22 | 23 |
23 let Page = ext.Page = function(tab) | 24 let Page = ext.Page = function(tab) |
24 { | 25 { |
25 this.id = tab.id; | 26 this.id = tab.id; |
26 this._url = tab.url && new URL(tab.url); | 27 this._url = tab.url && new URL(tab.url); |
27 | 28 |
28 this.browserAction = new BrowserAction(tab.id); | 29 this.browserAction = new BrowserAction(tab.id); |
29 this.contextMenus = new ContextMenus(this); | 30 this.contextMenus = new ContextMenus(this); |
(...skipping 20 matching lines...) Expand all Loading... |
50 sendMessage(message, responseCallback) | 51 sendMessage(message, responseCallback) |
51 { | 52 { |
52 chrome.tabs.sendMessage(this.id, message, responseCallback); | 53 chrome.tabs.sendMessage(this.id, message, responseCallback); |
53 } | 54 } |
54 }; | 55 }; |
55 | 56 |
56 ext.getPage = id => new Page({id: parseInt(id, 10)}); | 57 ext.getPage = id => new Page({id: parseInt(id, 10)}); |
57 | 58 |
58 function afterTabLoaded(callback) | 59 function afterTabLoaded(callback) |
59 { | 60 { |
60 return openedTab => | 61 return openedTab => |
61 { | 62 { |
62 let onUpdated = (tabId, changeInfo, tab) => | 63 let onUpdated = (tabId, changeInfo, tab) => |
63 { | 64 { |
64 if (tabId == openedTab.id && changeInfo.status == "complete") | 65 if (tabId == openedTab.id && changeInfo.status == "complete") |
65 { | 66 { |
66 chrome.tabs.onUpdated.removeListener(onUpdated); | 67 chrome.tabs.onUpdated.removeListener(onUpdated); |
67 callback(new Page(openedTab)); | 68 callback(new Page(openedTab)); |
68 } | 69 } |
69 }; | 70 }; |
70 chrome.tabs.onUpdated.addListener(onUpdated); | 71 chrome.tabs.onUpdated.addListener(onUpdated); |
71 }; | 72 }; |
72 } | 73 } |
73 | 74 |
74 ext.pages = { | 75 ext.pages = { |
75 open(url, callback) | 76 open(url, callback) |
76 { | 77 { |
77 chrome.tabs.create({url: url}, callback && afterTabLoaded(callback)); | 78 chrome.tabs.create({url}, callback && afterTabLoaded(callback)); |
78 }, | 79 }, |
79 query(info, callback) | 80 query(info, callback) |
80 { | 81 { |
81 let rawInfo = {}; | 82 let rawInfo = {}; |
82 for (let property in info) | 83 for (let property in info) |
83 { | 84 { |
84 switch (property) | 85 switch (property) |
85 { | 86 { |
86 case "active": | 87 case "active": |
87 case "lastFocusedWindow": | 88 case "lastFocusedWindow": |
(...skipping 27 matching lines...) Expand all Loading... |
115 if (!frame) | 116 if (!frame) |
116 frame = frames[frameId] = {}; | 117 frame = frames[frameId] = {}; |
117 | 118 |
118 return frame; | 119 return frame; |
119 } | 120 } |
120 | 121 |
121 function updatePageFrameStructure(frameId, tabId, url, parentFrameId) | 122 function updatePageFrameStructure(frameId, tabId, url, parentFrameId) |
122 { | 123 { |
123 if (frameId == 0) | 124 if (frameId == 0) |
124 { | 125 { |
125 let page = new Page({id: tabId, url: url}); | 126 let page = new Page({id: tabId, url}); |
126 | 127 |
127 ext._removeFromAllPageMaps(tabId); | 128 ext._removeFromAllPageMaps(tabId); |
128 | 129 |
129 chrome.tabs.get(tabId, () => | 130 chrome.tabs.get(tabId, () => |
130 { | 131 { |
131 // If the tab is prerendered, chrome.tabs.get() sets | 132 // If the tab is prerendered, chrome.tabs.get() sets |
132 // chrome.runtime.lastError and we have to dispatch the onLoading event, | 133 // chrome.runtime.lastError and we have to dispatch the onLoading event, |
133 // since the onUpdated event isn't dispatched for prerendered tabs. | 134 // since the onUpdated event isn't dispatched for prerendered tabs. |
134 // However, we have to keep relying on the unUpdated event for tabs that | 135 // However, we have to keep relying on the unUpdated event for tabs that |
135 // are already visible. Otherwise browser action changes get overridden | 136 // are already visible. Otherwise browser action changes get overridden |
136 // when Chrome automatically resets them on navigation. | 137 // when Chrome automatically resets them on navigation. |
137 if (chrome.runtime.lastError) | 138 if (chrome.runtime.lastError) |
138 ext.pages.onLoading._dispatch(page); | 139 ext.pages.onLoading._dispatch(page); |
139 }); | 140 }); |
140 } | 141 } |
141 | 142 |
142 // Update frame parent and URL in frame structure | 143 // Update frame parent and URL in frame structure |
143 let frame = createFrame(tabId, frameId); | 144 let frame = createFrame(tabId, frameId); |
144 frame.url = new URL(url); | 145 frame.url = new URL(url); |
145 frame.parent = framesOfTabs[tabId][parentFrameId] || null; | 146 frame.parent = framesOfTabs[tabId][parentFrameId] || null; |
146 }; | 147 } |
147 | 148 |
148 chrome.webRequest.onHeadersReceived.addListener(details => | 149 chrome.webRequest.onHeadersReceived.addListener(details => |
149 { | 150 { |
150 // We have to update the frame structure when switching to a new | 151 // We have to update the frame structure when switching to a new |
151 // document, so that we process any further requests made by that | 152 // document, so that we process any further requests made by that |
152 // document in the right context. Unfortunately, we cannot rely | 153 // document in the right context. Unfortunately, we cannot rely |
153 // on webNavigation.onCommitted since it isn't guaranteed to fire | 154 // on webNavigation.onCommitted since it isn't guaranteed to fire |
154 // before any subresources start downloading[1]. As an | 155 // before any subresources start downloading[1]. As an |
155 // alternative we use webRequest.onHeadersReceived for HTTP(S) | 156 // alternative we use webRequest.onHeadersReceived for HTTP(S) |
156 // URLs, being careful to ignore any responses that won't cause | 157 // URLs, being careful to ignore any responses that won't cause |
(...skipping 25 matching lines...) Expand all Loading... |
182 // "Content-Disposition" with a valid and non-empty value other | 183 // "Content-Disposition" with a valid and non-empty value other |
183 // than "inline". | 184 // than "inline". |
184 // https://chromium.googlesource.com/chromium/src/+/02d3f50b/content/brows
er/loader/mime_sniffing_resource_handler.cc#534 | 185 // https://chromium.googlesource.com/chromium/src/+/02d3f50b/content/brows
er/loader/mime_sniffing_resource_handler.cc#534 |
185 // https://chromium.googlesource.com/chromium/src/+/02d3f50b/net/http/http
_content_disposition.cc#374 | 186 // https://chromium.googlesource.com/chromium/src/+/02d3f50b/net/http/http
_content_disposition.cc#374 |
186 // https://chromium.googlesource.com/chromium/src/+/16e2688e/net/http/http
_util.cc#431 | 187 // https://chromium.googlesource.com/chromium/src/+/16e2688e/net/http/http
_util.cc#431 |
187 if (headerName == "content-disposition") | 188 if (headerName == "content-disposition") |
188 { | 189 { |
189 let disposition = header.value.split(";")[0].replace(/[ \t]+$/, ""); | 190 let disposition = header.value.split(";")[0].replace(/[ \t]+$/, ""); |
190 if (disposition.toLowerCase() != "inline" && | 191 if (disposition.toLowerCase() != "inline" && |
191 /^[\x21-\x7E]+$/.test(disposition) && | 192 /^[\x21-\x7E]+$/.test(disposition) && |
192 !/[()<>@,;:\\"/\[\]?={}]/.test(disposition)) | 193 !/[()<>@,;:\\"/[\]?={}]/.test(disposition)) |
193 return; | 194 return; |
194 } | 195 } |
195 | 196 |
196 // The value of the "Content-Type" header also determines if Chrome will | 197 // The value of the "Content-Type" header also determines if Chrome will |
197 // initiate a download, or otherwise how the response will be rendered. | 198 // initiate a download, or otherwise how the response will be rendered. |
198 // We only need to consider responses which will result in a navigation | 199 // We only need to consider responses which will result in a navigation |
199 // and be rendered as HTML or similar. | 200 // and be rendered as HTML or similar. |
200 // Note: Chrome might render the response as HTML if the "Content-Type" | 201 // Note: Chrome might render the response as HTML if the "Content-Type" |
201 // header is missing, invalid or unknown. | 202 // header is missing, invalid or unknown. |
202 // https://chromium.googlesource.com/chromium/src/+/99f41af9/net/http/http
_util.cc#66 | 203 // https://chromium.googlesource.com/chromium/src/+/99f41af9/net/http/http
_util.cc#66 |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
295 chrome.browserAction.setBadgeBackgroundColor({ | 296 chrome.browserAction.setBadgeBackgroundColor({ |
296 tabId: this._tabId, | 297 tabId: this._tabId, |
297 color: this._changes.badgeColor | 298 color: this._changes.badgeColor |
298 }); | 299 }); |
299 } | 300 } |
300 | 301 |
301 this._changes = null; | 302 this._changes = null; |
302 }, | 303 }, |
303 _queueChanges() | 304 _queueChanges() |
304 { | 305 { |
305 chrome.tabs.get(this._tabId, function() | 306 chrome.tabs.get(this._tabId, () => |
306 { | 307 { |
307 // If the tab is prerendered, chrome.tabs.get() sets | 308 // If the tab is prerendered, chrome.tabs.get() sets |
308 // chrome.runtime.lastError and we have to delay our changes | 309 // chrome.runtime.lastError and we have to delay our changes |
309 // until the currently visible tab is replaced with the | 310 // until the currently visible tab is replaced with the |
310 // prerendered tab. Otherwise chrome.browserAction.set* fails. | 311 // prerendered tab. Otherwise chrome.browserAction.set* fails. |
311 if (chrome.runtime.lastError) | 312 if (chrome.runtime.lastError) |
312 { | 313 { |
313 let onReplaced = (addedTabId, removedTabId) => | 314 let onReplaced = (addedTabId, removedTabId) => |
314 { | 315 { |
315 if (addedTabId == this._tabId) | 316 if (addedTabId == this._tabId) |
316 { | 317 { |
317 chrome.tabs.onReplaced.removeListener(onReplaced); | 318 chrome.tabs.onReplaced.removeListener(onReplaced); |
318 this._applyChanges(); | 319 this._applyChanges(); |
319 } | 320 } |
320 }; | 321 }; |
321 chrome.tabs.onReplaced.addListener(onReplaced); | 322 chrome.tabs.onReplaced.addListener(onReplaced); |
322 } | 323 } |
323 else | 324 else |
324 { | |
325 this._applyChanges(); | 325 this._applyChanges(); |
326 } | 326 }); |
327 }.bind(this)); | |
328 }, | 327 }, |
329 _addChange(name, value) | 328 _addChange(name, value) |
330 { | 329 { |
331 if (!this._changes) | 330 if (!this._changes) |
332 { | 331 { |
333 this._changes = {}; | 332 this._changes = {}; |
334 this._queueChanges(); | 333 this._queueChanges(); |
335 } | 334 } |
336 | 335 |
337 this._changes[name] = value; | 336 this._changes[name] = value; |
338 }, | 337 }, |
339 setIcon(path) | 338 setIcon(path) |
340 { | 339 { |
341 this._addChange("iconPath", path); | 340 this._addChange("iconPath", path); |
342 }, | 341 }, |
343 setBadge(badge) | 342 setBadge(badge) |
344 { | 343 { |
345 if (!badge) | 344 if (!badge) |
346 { | |
347 this._addChange("badgeText", ""); | 345 this._addChange("badgeText", ""); |
348 } | |
349 else | 346 else |
350 { | 347 { |
351 if ("number" in badge) | 348 if ("number" in badge) |
352 this._addChange("badgeText", badge.number.toString()); | 349 this._addChange("badgeText", badge.number.toString()); |
353 | 350 |
354 if ("color" in badge) | 351 if ("color" in badge) |
355 this._addChange("badgeColor", badge.color); | 352 this._addChange("badgeColor", badge.color); |
356 } | 353 } |
357 } | 354 } |
358 }; | 355 }; |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
439 | 436 |
440 /* Web requests */ | 437 /* Web requests */ |
441 | 438 |
442 let framesOfTabs = Object.create(null); | 439 let framesOfTabs = Object.create(null); |
443 | 440 |
444 ext.getFrame = (tabId, frameId) => | 441 ext.getFrame = (tabId, frameId) => |
445 { | 442 { |
446 return (framesOfTabs[tabId] || {})[frameId]; | 443 return (framesOfTabs[tabId] || {})[frameId]; |
447 }; | 444 }; |
448 | 445 |
449 let handlerBehaviorChangedQuota = chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANG
ED_CALLS_PER_10_MINUTES; | 446 let handlerBehaviorChangedQuota = |
| 447 chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES; |
450 | 448 |
451 function propagateHandlerBehaviorChange() | 449 function propagateHandlerBehaviorChange() |
452 { | 450 { |
453 // Make sure to not call handlerBehaviorChanged() more often than allowed | 451 // Make sure to not call handlerBehaviorChanged() more often than allowed |
454 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. | 452 // by chrome.webRequest.MAX_HANDLER_BEHAVIOR_CHANGED_CALLS_PER_10_MINUTES. |
455 // Otherwise Chrome notifies the user that this extension is causing issues. | 453 // Otherwise Chrome notifies the user that this extension is causing issues. |
456 if (handlerBehaviorChangedQuota > 0) | 454 if (handlerBehaviorChangedQuota > 0) |
457 { | 455 { |
458 chrome.webNavigation.onBeforeNavigate.removeListener(propagateHandlerBehav
iorChange); | 456 chrome.webNavigation.onBeforeNavigate.removeListener( |
| 457 propagateHandlerBehaviorChange |
| 458 ); |
459 chrome.webRequest.handlerBehaviorChanged(); | 459 chrome.webRequest.handlerBehaviorChanged(); |
460 | 460 |
461 handlerBehaviorChangedQuota--; | 461 handlerBehaviorChangedQuota--; |
462 setTimeout(() => { handlerBehaviorChangedQuota++; }, 600000); | 462 setTimeout(() => { handlerBehaviorChangedQuota++; }, 600000); |
463 } | 463 } |
464 } | 464 } |
465 | 465 |
466 ext.webRequest = { | 466 ext.webRequest = { |
467 onBeforeRequest: new ext._EventTarget(), | 467 onBeforeRequest: new ext._EventTarget(), |
468 handlerBehaviorChanged() | 468 handlerBehaviorChanged() |
469 { | 469 { |
470 // Defer handlerBehaviorChanged() until navigation occurs. | 470 // Defer handlerBehaviorChanged() until navigation occurs. |
471 // There wouldn't be any visible effect when calling it earlier, | 471 // There wouldn't be any visible effect when calling it earlier, |
472 // but it's an expensive operation and that way we avoid to call | 472 // but it's an expensive operation and that way we avoid to call |
473 // it multiple times, if multiple filters are added/removed. | 473 // it multiple times, if multiple filters are added/removed. |
474 let onBeforeNavigate = chrome.webNavigation.onBeforeNavigate; | 474 let {onBeforeNavigate} = chrome.webNavigation; |
475 if (!onBeforeNavigate.hasListener(propagateHandlerBehaviorChange)) | 475 if (!onBeforeNavigate.hasListener(propagateHandlerBehaviorChange)) |
476 onBeforeNavigate.addListener(propagateHandlerBehaviorChange); | 476 onBeforeNavigate.addListener(propagateHandlerBehaviorChange); |
477 } | 477 } |
478 }; | 478 }; |
479 | 479 |
480 chrome.tabs.query({}, tabs => | 480 chrome.tabs.query({}, tabs => |
481 { | 481 { |
482 tabs.forEach(tab => | 482 tabs.forEach(tab => |
483 { | 483 { |
484 chrome.webNavigation.getAllFrames({tabId: tab.id}, details => | 484 chrome.webNavigation.getAllFrames({tabId: tab.id}, details => |
485 { | 485 { |
486 if (details && details.length > 0) | 486 if (details && details.length > 0) |
487 { | 487 { |
488 let frames = framesOfTabs[tab.id] = Object.create(null); | 488 let frames = framesOfTabs[tab.id] = Object.create(null); |
489 | 489 |
490 for (let i = 0; i < details.length; i++) | 490 for (let i = 0; i < details.length; i++) |
491 frames[details[i].frameId] = {url: new URL(details[i].url), parent:
null}; | 491 { |
| 492 frames[details[i].frameId] = { |
| 493 url: new URL(details[i].url), |
| 494 parent: null |
| 495 }; |
| 496 } |
492 | 497 |
493 for (let i = 0; i < details.length; i++) | 498 for (let i = 0; i < details.length; i++) |
494 { | 499 { |
495 let parentFrameId = details[i].parentFrameId; | 500 let {parentFrameId} = details[i]; |
496 | 501 |
497 if (parentFrameId != -1) | 502 if (parentFrameId != -1) |
498 frames[details[i].frameId].parent = frames[parentFrameId]; | 503 frames[details[i].frameId].parent = frames[parentFrameId]; |
499 } | 504 } |
500 } | 505 } |
501 }); | 506 }); |
502 }); | 507 }); |
503 }); | 508 }); |
504 | 509 |
505 chrome.webRequest.onBeforeRequest.addListener(details => | 510 chrome.webRequest.onBeforeRequest.addListener(details => |
506 { | 511 { |
507 // The high-level code isn't interested in requests that aren't | 512 // The high-level code isn't interested in requests that aren't |
508 // related to a tab or requests loading a top-level document, | 513 // related to a tab or requests loading a top-level document, |
509 // those should never be blocked. | 514 // those should never be blocked. |
510 if (details.tabId == -1 || details.type == "main_frame") | 515 if (details.tabId == -1 || details.type == "main_frame") |
511 return; | 516 return; |
512 | 517 |
513 // We are looking for the frame that contains the element which | 518 // We are looking for the frame that contains the element which |
514 // has triggered this request. For most requests (e.g. images) we | 519 // has triggered this request. For most requests (e.g. images) we |
515 // can just use the request's frame ID, but for subdocument requests | 520 // can just use the request's frame ID, but for subdocument requests |
516 // (e.g. iframes) we must instead use the request's parent frame ID. | 521 // (e.g. iframes) we must instead use the request's parent frame ID. |
517 let frameId; | 522 let {frameId, type} = details; |
518 let requestType; | 523 if (type == "sub_frame") |
519 if (details.type == "sub_frame") | |
520 { | 524 { |
521 frameId = details.parentFrameId; | 525 frameId = details.parentFrameId; |
522 requestType = "SUBDOCUMENT"; | 526 type = "SUBDOCUMENT"; |
523 } | |
524 else | |
525 { | |
526 frameId = details.frameId; | |
527 requestType = details.type.toUpperCase(); | |
528 } | 527 } |
529 | 528 |
530 let frame = ext.getFrame(details.tabId, frameId); | 529 let frame = ext.getFrame(details.tabId, frameId); |
531 if (frame) | 530 if (frame) |
532 { | 531 { |
533 let results = ext.webRequest.onBeforeRequest._dispatch( | 532 let results = ext.webRequest.onBeforeRequest._dispatch( |
534 new URL(details.url), | 533 new URL(details.url), |
535 requestType, | 534 type.toUpperCase(), |
536 new Page({id: details.tabId}), | 535 new Page({id: details.tabId}), |
537 frame | 536 frame |
538 ); | 537 ); |
539 | 538 |
540 if (results.indexOf(false) != -1) | 539 if (results.indexOf(false) != -1) |
541 return {cancel: true}; | 540 return {cancel: true}; |
542 } | 541 } |
543 }, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]); | 542 }, {urls: ["http://*/*", "https://*/*"]}, ["blocking"]); |
544 | 543 |
545 | 544 |
(...skipping 19 matching lines...) Expand all Loading... |
565 | 564 |
566 let frame = frames[rawSender.frameId]; | 565 let frame = frames[rawSender.frameId]; |
567 if (frame) | 566 if (frame) |
568 return frame.parent; | 567 return frame.parent; |
569 | 568 |
570 return frames[0]; | 569 return frames[0]; |
571 } | 570 } |
572 }; | 571 }; |
573 } | 572 } |
574 | 573 |
575 return ext.onMessage._dispatch(message, sender, sendResponse).indexOf(true)
!= -1; | 574 return ext.onMessage._dispatch( |
| 575 message, sender, sendResponse |
| 576 ).indexOf(true) != -1; |
576 }); | 577 }); |
577 | 578 |
578 | 579 |
579 /* Storage */ | 580 /* Storage */ |
580 | 581 |
581 ext.storage = { | 582 ext.storage = { |
582 get(keys, callback) | 583 get(keys, callback) |
583 { | 584 { |
584 chrome.storage.local.get(keys, callback); | 585 chrome.storage.local.get(keys, callback); |
585 }, | 586 }, |
(...skipping 10 matching lines...) Expand all Loading... |
596 onChanged: chrome.storage.onChanged | 597 onChanged: chrome.storage.onChanged |
597 }; | 598 }; |
598 | 599 |
599 /* Options */ | 600 /* Options */ |
600 | 601 |
601 if ("openOptionsPage" in chrome.runtime) | 602 if ("openOptionsPage" in chrome.runtime) |
602 { | 603 { |
603 ext.showOptions = callback => | 604 ext.showOptions = callback => |
604 { | 605 { |
605 if (!callback) | 606 if (!callback) |
606 { | |
607 chrome.runtime.openOptionsPage(); | 607 chrome.runtime.openOptionsPage(); |
608 } | |
609 else | 608 else |
610 { | 609 { |
611 chrome.runtime.openOptionsPage(() => | 610 chrome.runtime.openOptionsPage(() => |
612 { | 611 { |
613 if (chrome.runtime.lastError) | 612 if (chrome.runtime.lastError) |
614 return; | 613 return; |
615 | 614 |
616 chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => | 615 chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => |
617 { | 616 { |
618 if (tabs.length > 0) | 617 if (tabs.length > 0) |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
650 { | 649 { |
651 let tab = tabs[0]; | 650 let tab = tabs[0]; |
652 | 651 |
653 chrome.windows.update(tab.windowId, {focused: true}); | 652 chrome.windows.update(tab.windowId, {focused: true}); |
654 chrome.tabs.update(tab.id, {active: true}); | 653 chrome.tabs.update(tab.id, {active: true}); |
655 | 654 |
656 if (callback) | 655 if (callback) |
657 callback(new Page(tab)); | 656 callback(new Page(tab)); |
658 } | 657 } |
659 else | 658 else |
660 { | |
661 ext.pages.open(optionsUrl, callback); | 659 ext.pages.open(optionsUrl, callback); |
662 } | |
663 }); | 660 }); |
664 }); | 661 }); |
665 }; | 662 }; |
666 } | 663 } |
667 | 664 |
668 /* Windows */ | 665 /* Windows */ |
669 ext.windows = { | 666 ext.windows = { |
670 create(createData, callback) | 667 create(createData, callback) |
671 { | 668 { |
672 chrome.windows.create(createData, createdWindow => | 669 chrome.windows.create(createData, createdWindow => |
673 { | 670 { |
674 afterTabLoaded(callback)(createdWindow.tabs[0]); | 671 afterTabLoaded(callback)(createdWindow.tabs[0]); |
675 }); | 672 }); |
676 } | 673 } |
677 }; | 674 }; |
678 } | 675 }()); |
OLD | NEW |