| 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-2015 Eyeo GmbH | 3 * Copyright (C) 2006-2015 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 (function() |
| 19 { | 19 { |
| 20 /* Pages */ | 20 /* Context menus */ |
| 21 | 21 |
| 22 var pages = Object.create(null); | 22 var contextMenuItems = new ext.PageMap(); |
| 23 var pageCounter = 0; | |
| 24 | 23 |
| 25 var Page = function(id, tab, url) | 24 var ContextMenus = function(page) |
| 26 { | 25 { |
| 27 this._id = id; | 26 this._page = page; |
| 28 this._tab = tab; | 27 }; |
| 29 this._frames = [{url: new URL(url), parent: null}]; | 28 ContextMenus.prototype = { |
| 29 create: function(item) |
| 30 { |
| 31 var items = contextMenuItems.get(this._page); |
| 32 if (!items) |
| 33 contextMenuItems.set(this._page, items = []); |
| 30 | 34 |
| 31 if (tab.page) | 35 items.push(item); |
| 32 this._messageProxy = new ext._MessageProxy(tab.page); | 36 }, |
| 33 else | 37 removeAll: function() |
| 34 // while the new tab page is shown on Safari 7, the 'page' property | |
| 35 // of the tab is undefined, and we can't send messages to that page | |
| 36 this._messageProxy = { | |
| 37 handleRequest: function() {}, | |
| 38 handleResponse: function() {}, | |
| 39 sendMessage: function() {} | |
| 40 }; | |
| 41 | |
| 42 this.browserAction = new BrowserAction(this); | |
| 43 this.contextMenus = new ContextMenus(this); | |
| 44 }; | |
| 45 Page.prototype = { | |
| 46 get url() | |
| 47 { | 38 { |
| 48 return this._frames[0].url; | 39 contextMenuItems.delete(this._page); |
| 49 }, | |
| 50 sendMessage: function(message, responseCallback) | |
| 51 { | |
| 52 this._messageProxy.sendMessage(message, responseCallback, {pageId: this._i
d}); | |
| 53 } | 40 } |
| 54 }; | 41 }; |
| 55 | 42 |
| 56 ext._getPage = function(id) | 43 safari.application.addEventListener("contextmenu", function(event) |
| 57 { | 44 { |
| 58 return pages[id]; | 45 if (!event.userInfo) |
| 59 }; | |
| 60 | |
| 61 var isPageActive = function(page) | |
| 62 { | |
| 63 var tab = page._tab; | |
| 64 var win = tab.browserWindow; | |
| 65 return win && tab == win.activeTab && page == tab._visiblePage; | |
| 66 }; | |
| 67 | |
| 68 var forgetPage = function(id) | |
| 69 { | |
| 70 ext._removeFromAllPageMaps(id); | |
| 71 | |
| 72 delete pages[id]._tab._pages[id]; | |
| 73 delete pages[id]; | |
| 74 }; | |
| 75 | |
| 76 var replacePage = function(page) | |
| 77 { | |
| 78 var tab = page._tab; | |
| 79 tab._visiblePage = page; | |
| 80 | |
| 81 for (var id in tab._pages) | |
| 82 { | |
| 83 if (id != page._id) | |
| 84 forgetPage(id); | |
| 85 } | |
| 86 | |
| 87 if (isPageActive(page)) | |
| 88 updateToolbarItemForPage(page, tab.browserWindow); | |
| 89 }; | |
| 90 | |
| 91 ext.pages = { | |
| 92 open: function(url, callback) | |
| 93 { | |
| 94 var tab = safari.application.activeBrowserWindow.openTab(); | |
| 95 tab.url = url; | |
| 96 | |
| 97 if (callback) | |
| 98 { | |
| 99 var onLoading = function(page) | |
| 100 { | |
| 101 if (page._tab == tab) | |
| 102 { | |
| 103 ext.pages.onLoading.removeListener(onLoading); | |
| 104 callback(page); | |
| 105 } | |
| 106 }; | |
| 107 ext.pages.onLoading.addListener(onLoading); | |
| 108 } | |
| 109 }, | |
| 110 query: function(info, callback) | |
| 111 { | |
| 112 var matchedPages = []; | |
| 113 | |
| 114 for (var id in pages) | |
| 115 { | |
| 116 var page = pages[id]; | |
| 117 var win = page._tab.browserWindow; | |
| 118 | |
| 119 if ("active" in info && info.active != isPageActive(page)) | |
| 120 continue; | |
| 121 if ("lastFocusedWindow" in info && info.lastFocusedWindow != (win == saf
ari.application.activeBrowserWindow)) | |
| 122 continue; | |
| 123 | |
| 124 matchedPages.push(page); | |
| 125 }; | |
| 126 | |
| 127 callback(matchedPages); | |
| 128 }, | |
| 129 onLoading: new ext._EventTarget() | |
| 130 }; | |
| 131 | |
| 132 safari.application.addEventListener("close", function(event) | |
| 133 { | |
| 134 // this event is dispatched on closing windows and tabs. However when a | |
| 135 // window is closed, it is first dispatched on each tab in the window and | |
| 136 // then on the window itself. But we are only interested in closed tabs. | |
| 137 if (!(event.target instanceof SafariBrowserTab)) | |
| 138 return; | 46 return; |
| 139 | 47 |
| 140 // when a tab is closed, forget the previous page associated with that | 48 var pageId = event.userInfo.pageId; |
| 141 // tab. Note that it wouldn't be sufficient do that when the old page | 49 if (!pageId) |
| 142 // is unloading, because Safari dispatches window.onunload only when | 50 return; |
| 143 // reloading the page or following links, but not when closing the tab. | 51 |
| 144 for (var id in event.target._pages) | 52 var page = pages[event.userInfo.pageId]; |
| 145 forgetPage(id); | 53 var items = contextMenuItems.get(page); |
| 146 }, true); | 54 if (!items) |
| 55 return; |
| 56 |
| 57 var context = event.userInfo.tagName; |
| 58 if (context == "img") |
| 59 context = "image"; |
| 60 |
| 61 for (var i = 0; i < items.length; i++) |
| 62 { |
| 63 // Supported contexts are: all, audio, image, video |
| 64 var menuItem = items[i]; |
| 65 if (menuItem.contexts.indexOf("all") == -1 && menuItem.contexts.indexOf(co
ntext) == -1) |
| 66 continue; |
| 67 |
| 68 event.contextMenu.appendContextMenuItem(i, menuItem.title); |
| 69 } |
| 70 }); |
| 71 |
| 72 safari.application.addEventListener("command", function(event) |
| 73 { |
| 74 var page = pages[event.userInfo.pageId]; |
| 75 var items = contextMenuItems.get(page); |
| 76 |
| 77 items[event.command].onclick(page); |
| 78 }); |
| 147 | 79 |
| 148 | 80 |
| 149 /* Browser actions */ | 81 /* Browser actions */ |
| 150 | 82 |
| 151 var toolbarItemProperties = {}; | 83 var toolbarItemProperties = {}; |
| 152 | 84 |
| 153 var getToolbarItemForWindow = function(win) | 85 var getToolbarItemForWindow = function(win) |
| 154 { | 86 { |
| 155 for (var i = 0; i < safari.extension.toolbarItems.length; i++) | 87 for (var i = 0; i < safari.extension.toolbarItems.length; i++) |
| 156 { | 88 { |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 223 return; | 155 return; |
| 224 | 156 |
| 225 // update the toolbar item for the page visible in the tab that just | 157 // update the toolbar item for the page visible in the tab that just |
| 226 // became active. If we can't find that page (e.g. when a page was | 158 // became active. If we can't find that page (e.g. when a page was |
| 227 // opened in a new tab, and our content script didn't run yet), the | 159 // opened in a new tab, and our content script didn't run yet), the |
| 228 // toolbar item of the window, is reset to its intial configuration. | 160 // toolbar item of the window, is reset to its intial configuration. |
| 229 updateToolbarItemForPage(event.target._visiblePage, event.target.browserWind
ow); | 161 updateToolbarItemForPage(event.target._visiblePage, event.target.browserWind
ow); |
| 230 }, true); | 162 }, true); |
| 231 | 163 |
| 232 | 164 |
| 233 /* Context menus */ | 165 /* Pages */ |
| 234 | 166 |
| 235 var contextMenuItems = new ext.PageMap(); | 167 var pages = Object.create(null); |
| 168 var pageCounter = 0; |
| 236 | 169 |
| 237 var ContextMenus = function(page) | 170 var Page = function(id, tab, url) |
| 238 { | 171 { |
| 239 this._page = page; | 172 this._id = id; |
| 173 this._tab = tab; |
| 174 this._frames = [{url: new URL(url), parent: null}]; |
| 175 |
| 176 if (tab.page) |
| 177 this._messageProxy = new ext._MessageProxy(tab.page); |
| 178 else |
| 179 // while the new tab page is shown on Safari 7, the 'page' property |
| 180 // of the tab is undefined, and we can't send messages to that page |
| 181 this._messageProxy = { |
| 182 handleRequest: function() {}, |
| 183 handleResponse: function() {}, |
| 184 sendMessage: function() {} |
| 185 }; |
| 186 |
| 187 this.browserAction = new BrowserAction(this); |
| 188 this.contextMenus = new ContextMenus(this); |
| 240 }; | 189 }; |
| 241 ContextMenus.prototype = { | 190 Page.prototype = { |
| 242 create: function(item) | 191 get url() |
| 243 { | 192 { |
| 244 var items = contextMenuItems.get(this._page); | 193 return this._frames[0].url; |
| 245 if (!items) | |
| 246 contextMenuItems.set(this._page, items = []); | |
| 247 | |
| 248 items.push(item); | |
| 249 }, | 194 }, |
| 250 removeAll: function() | 195 sendMessage: function(message, responseCallback) |
| 251 { | 196 { |
| 252 contextMenuItems.delete(this._page); | 197 this._messageProxy.sendMessage(message, responseCallback, {pageId: this._i
d}); |
| 253 } | 198 } |
| 254 }; | 199 }; |
| 255 | 200 |
| 256 safari.application.addEventListener("contextmenu", function(event) | 201 ext._getPage = function(id) |
| 257 { | 202 { |
| 258 if (!event.userInfo) | 203 return pages[id]; |
| 204 }; |
| 205 |
| 206 var isPageActive = function(page) |
| 207 { |
| 208 var tab = page._tab; |
| 209 var win = tab.browserWindow; |
| 210 return win && tab == win.activeTab && page == tab._visiblePage; |
| 211 }; |
| 212 |
| 213 var forgetPage = function(id) |
| 214 { |
| 215 ext._removeFromAllPageMaps(id); |
| 216 |
| 217 delete pages[id]._tab._pages[id]; |
| 218 delete pages[id]; |
| 219 }; |
| 220 |
| 221 var replacePage = function(page) |
| 222 { |
| 223 var tab = page._tab; |
| 224 tab._visiblePage = page; |
| 225 |
| 226 for (var id in tab._pages) |
| 227 { |
| 228 if (id != page._id) |
| 229 forgetPage(id); |
| 230 } |
| 231 |
| 232 if (isPageActive(page)) |
| 233 updateToolbarItemForPage(page, tab.browserWindow); |
| 234 }; |
| 235 |
| 236 var addPage = function(tab, url, prerendered) |
| 237 { |
| 238 var pageId = ++pageCounter; |
| 239 |
| 240 if (!('_pages' in tab)) |
| 241 tab._pages = Object.create(null); |
| 242 |
| 243 var page = new Page(pageId, tab, url); |
| 244 pages[pageId] = tab._pages[pageId] = page; |
| 245 |
| 246 // When a new page is shown, forget the previous page associated |
| 247 // with its tab, and reset the toolbar item if necessary. |
| 248 // Note that it wouldn't be sufficient to do that when the old |
| 249 // page is unloading, because Safari dispatches window.onunload |
| 250 // only when reloading the page or following links, but not when |
| 251 // you enter a new URL in the address bar. |
| 252 if (!prerendered) |
| 253 replacePage(page); |
| 254 |
| 255 return pageId; |
| 256 }; |
| 257 |
| 258 ext.pages = { |
| 259 open: function(url, callback) |
| 260 { |
| 261 var tab = safari.application.activeBrowserWindow.openTab(); |
| 262 tab.url = url; |
| 263 |
| 264 if (callback) |
| 265 { |
| 266 var onLoading = function(page) |
| 267 { |
| 268 if (page._tab == tab) |
| 269 { |
| 270 ext.pages.onLoading.removeListener(onLoading); |
| 271 callback(page); |
| 272 } |
| 273 }; |
| 274 ext.pages.onLoading.addListener(onLoading); |
| 275 } |
| 276 }, |
| 277 query: function(info, callback) |
| 278 { |
| 279 var matchedPages = []; |
| 280 |
| 281 for (var id in pages) |
| 282 { |
| 283 var page = pages[id]; |
| 284 var win = page._tab.browserWindow; |
| 285 |
| 286 if ("active" in info && info.active != isPageActive(page)) |
| 287 continue; |
| 288 if ("lastFocusedWindow" in info && info.lastFocusedWindow != (win == saf
ari.application.activeBrowserWindow)) |
| 289 continue; |
| 290 |
| 291 matchedPages.push(page); |
| 292 }; |
| 293 |
| 294 callback(matchedPages); |
| 295 }, |
| 296 onLoading: new ext._EventTarget() |
| 297 }; |
| 298 |
| 299 safari.application.addEventListener("close", function(event) |
| 300 { |
| 301 // this event is dispatched on closing windows and tabs. However when a |
| 302 // window is closed, it is first dispatched on each tab in the window and |
| 303 // then on the window itself. But we are only interested in closed tabs. |
| 304 if (!(event.target instanceof SafariBrowserTab)) |
| 259 return; | 305 return; |
| 260 | 306 |
| 261 var pageId = event.userInfo.pageId; | 307 // when a tab is closed, forget the previous page associated with that |
| 262 if (!pageId) | 308 // tab. Note that it wouldn't be sufficient do that when the old page |
| 263 return; | 309 // is unloading, because Safari dispatches window.onunload only when |
| 310 // reloading the page or following links, but not when closing the tab. |
| 311 for (var id in event.target._pages) |
| 312 forgetPage(id); |
| 313 }, true); |
| 264 | 314 |
| 265 var page = pages[event.userInfo.pageId]; | 315 // We generally rely on content scripts to report new pages, |
| 266 var items = contextMenuItems.get(page); | 316 // since Safari's extension API doesn't consider pre-rendered |
| 267 if (!items) | 317 // pages. However, when the extension initializes we have to |
| 268 return; | 318 // use Safari's extension API to detect existing tabs. |
| 319 safari.application.browserWindows.forEach(function(win) |
| 320 { |
| 321 for (var i = 0; i < win.tabs.length; i++) |
| 322 { |
| 323 var tab = win.tabs[i]; |
| 324 var url = tab.url; |
| 269 | 325 |
| 270 var context = event.userInfo.tagName; | 326 // For the new tab page the url property is undefined. |
| 271 if (context == "img") | 327 if (url) |
| 272 context = "image"; | 328 addPage(tab, url, false); |
| 273 | |
| 274 for (var i = 0; i < items.length; i++) | |
| 275 { | |
| 276 // Supported contexts are: all, audio, image, video | |
| 277 var menuItem = items[i]; | |
| 278 if (menuItem.contexts.indexOf("all") == -1 && menuItem.contexts.indexOf(co
ntext) == -1) | |
| 279 continue; | |
| 280 | |
| 281 event.contextMenu.appendContextMenuItem(i, menuItem.title); | |
| 282 } | 329 } |
| 283 }); | 330 }); |
| 284 | 331 |
| 285 safari.application.addEventListener("command", function(event) | |
| 286 { | |
| 287 var page = pages[event.userInfo.pageId]; | |
| 288 var items = contextMenuItems.get(page); | |
| 289 | |
| 290 items[event.command].onclick(page); | |
| 291 }); | |
| 292 | |
| 293 | 332 |
| 294 /* Web requests */ | 333 /* Web requests */ |
| 295 | 334 |
| 296 ext.webRequest = { | 335 ext.webRequest = { |
| 297 onBeforeRequest: new ext._EventTarget(), | 336 onBeforeRequest: new ext._EventTarget(), |
| 298 handlerBehaviorChanged: function() {}, | 337 handlerBehaviorChanged: function() {}, |
| 299 indistinguishableTypes: [["OTHER", "FONT"]] | 338 indistinguishableTypes: [["OTHER", "FONT"]] |
| 300 }; | 339 }; |
| 301 | 340 |
| 302 | 341 |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 518 { | 557 { |
| 519 case "loading": | 558 case "loading": |
| 520 var tab = event.target; | 559 var tab = event.target; |
| 521 var message = event.message; | 560 var message = event.message; |
| 522 | 561 |
| 523 var pageId; | 562 var pageId; |
| 524 var frameId; | 563 var frameId; |
| 525 | 564 |
| 526 if (message.isTopLevel) | 565 if (message.isTopLevel) |
| 527 { | 566 { |
| 528 pageId = ++pageCounter; | 567 pageId = addPage(tab, message.url, message.isPrerendered); |
| 529 frameId = 0; | 568 frameId = 0; |
| 530 | 569 |
| 531 if (!('_pages' in tab)) | 570 ext.pages.onLoading._dispatch(pages[pageId]); |
| 532 tab._pages = Object.create(null); | |
| 533 | |
| 534 var page = new Page(pageId, tab, message.url); | |
| 535 pages[pageId] = tab._pages[pageId] = page; | |
| 536 | |
| 537 // when a new page is shown, forget the previous page associated | |
| 538 // with its tab, and reset the toolbar item if necessary. | |
| 539 // Note that it wouldn't be sufficient to do that when the old | |
| 540 // page is unloading, because Safari dispatches window.onunload | |
| 541 // only when reloading the page or following links, but not when | |
| 542 // you enter a new URL in the address bar. | |
| 543 if (!message.isPrerendered) | |
| 544 replacePage(page); | |
| 545 | |
| 546 ext.pages.onLoading._dispatch(page); | |
| 547 } | 571 } |
| 548 else | 572 else |
| 549 { | 573 { |
| 550 var page; | 574 var page; |
| 551 var parentFrame; | 575 var parentFrame; |
| 552 | 576 |
| 553 var lastPageId; | 577 var lastPageId; |
| 554 var lastPage; | 578 var lastPage; |
| 555 var lastPageTopLevelFrame; | 579 var lastPageTopLevelFrame; |
| 556 | 580 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 587 if (!page) | 611 if (!page) |
| 588 { | 612 { |
| 589 pageId = lastPageId; | 613 pageId = lastPageId; |
| 590 page = lastPage; | 614 page = lastPage; |
| 591 parentFrame = lastPageTopLevelFrame; | 615 parentFrame = lastPageTopLevelFrame; |
| 592 } | 616 } |
| 593 | 617 |
| 594 frameId = page._frames.length; | 618 frameId = page._frames.length; |
| 595 page._frames.push({url: new URL(message.url), parent: parentFrame}
); | 619 page._frames.push({url: new URL(message.url), parent: parentFrame}
); |
| 596 } | 620 } |
| 597 | |
| 598 event.message = {pageId: pageId, frameId: frameId}; | 621 event.message = {pageId: pageId, frameId: frameId}; |
| 599 break; | 622 break; |
| 600 case "webRequest": | 623 case "webRequest": |
| 601 var page = pages[event.message.pageId]; | 624 var page = pages[event.message.pageId]; |
| 602 var frame = page._frames[event.message.frameId]; | 625 var frame = page._frames[event.message.frameId]; |
| 603 | 626 |
| 604 var results = ext.webRequest.onBeforeRequest._dispatch( | 627 var results = ext.webRequest.onBeforeRequest._dispatch( |
| 605 new URL(event.message.url, frame.url), | 628 new URL(event.message.url, frame.url), |
| 606 event.message.type, page, frame | 629 event.message.type, page, frame |
| 607 ); | 630 ); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 666 tab.activate(); | 689 tab.activate(); |
| 667 if (callback) | 690 if (callback) |
| 668 callback(page); | 691 callback(page); |
| 669 return; | 692 return; |
| 670 } | 693 } |
| 671 } | 694 } |
| 672 | 695 |
| 673 ext.pages.open(optionsUrl, callback); | 696 ext.pages.open(optionsUrl, callback); |
| 674 }; | 697 }; |
| 675 })(); | 698 })(); |
| OLD | NEW |