| Left: | ||
| Right: |
| 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) | |
|
Sebastian Noack
2015/03/03 19:51:34
This function is new.
| |
| 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 | |
|
kzar
2015/03/04 10:46:57
Seems a shame you stripped out the useful comment
Sebastian Noack
2015/03/04 11:04:35
I've re-added that comment. Also added some more c
| |
| 246 if (!prerendered) | |
| 247 replacePage(page); | |
| 248 | |
| 249 return pageId; | |
| 250 }; | |
| 251 | |
| 252 ext.pages = { | |
| 253 open: function(url, callback) | |
| 254 { | |
| 255 var tab = safari.application.activeBrowserWindow.openTab(); | |
| 256 tab.url = url; | |
| 257 | |
| 258 if (callback) | |
| 259 { | |
| 260 var onLoading = function(page) | |
| 261 { | |
| 262 if (page._tab == tab) | |
| 263 { | |
| 264 ext.pages.onLoading.removeListener(onLoading); | |
| 265 callback(page); | |
| 266 } | |
| 267 }; | |
| 268 ext.pages.onLoading.addListener(onLoading); | |
| 269 } | |
| 270 }, | |
| 271 query: function(info, callback) | |
| 272 { | |
| 273 var matchedPages = []; | |
| 274 | |
| 275 for (var id in pages) | |
| 276 { | |
| 277 var page = pages[id]; | |
| 278 var win = page._tab.browserWindow; | |
| 279 | |
| 280 if ("active" in info && info.active != isPageActive(page)) | |
| 281 continue; | |
| 282 if ("lastFocusedWindow" in info && info.lastFocusedWindow != (win == saf ari.application.activeBrowserWindow)) | |
| 283 continue; | |
| 284 | |
| 285 matchedPages.push(page); | |
| 286 }; | |
| 287 | |
| 288 callback(matchedPages); | |
| 289 }, | |
| 290 onLoading: new ext._EventTarget() | |
| 291 }; | |
| 292 | |
| 293 safari.application.addEventListener("close", function(event) | |
| 294 { | |
| 295 // this event is dispatched on closing windows and tabs. However when a | |
| 296 // window is closed, it is first dispatched on each tab in the window and | |
| 297 // then on the window itself. But we are only interested in closed tabs. | |
| 298 if (!(event.target instanceof SafariBrowserTab)) | |
| 259 return; | 299 return; |
| 260 | 300 |
| 261 var pageId = event.userInfo.pageId; | 301 // when a tab is closed, forget the previous page associated with that |
| 262 if (!pageId) | 302 // tab. Note that it wouldn't be sufficient do that when the old page |
| 263 return; | 303 // is unloading, because Safari dispatches window.onunload only when |
| 304 // reloading the page or following links, but not when closing the tab. | |
| 305 for (var id in event.target._pages) | |
| 306 forgetPage(id); | |
| 307 }, true); | |
| 264 | 308 |
| 265 var page = pages[event.userInfo.pageId]; | 309 safari.application.browserWindows.forEach(function(win) |
|
Sebastian Noack
2015/03/03 19:51:34
This is where we detect existing tabs now.
| |
| 266 var items = contextMenuItems.get(page); | 310 { |
| 267 if (!items) | 311 for (var i = 0; i < win.tabs.length; i++) |
| 268 return; | 312 { |
| 313 var tab = win.tabs[i]; | |
| 314 var url = tab.url; | |
| 269 | 315 |
| 270 var context = event.userInfo.tagName; | 316 if (url) |
| 271 if (context == "img") | 317 addPage(tab, url, false); |
| 272 context = "image"; | |
| 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 } | 318 } |
| 283 }); | 319 }); |
| 284 | 320 |
| 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 | 321 |
| 294 /* Web requests */ | 322 /* Web requests */ |
|
Sebastian Noack
2015/03/03 19:51:34
All other code above this line is unchanged. I mer
| |
| 295 | 323 |
| 296 ext.webRequest = { | 324 ext.webRequest = { |
| 297 onBeforeRequest: new ext._EventTarget(), | 325 onBeforeRequest: new ext._EventTarget(), |
| 298 handlerBehaviorChanged: function() {}, | 326 handlerBehaviorChanged: function() {}, |
| 299 indistinguishableTypes: [["OTHER", "FONT"]] | 327 indistinguishableTypes: [["OTHER", "FONT"]] |
| 300 }; | 328 }; |
| 301 | 329 |
| 302 | 330 |
| 303 /* Background page proxy (for access from content scripts) */ | 331 /* Background page proxy (for access from content scripts) */ |
| 304 | 332 |
| (...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 518 { | 546 { |
| 519 case "loading": | 547 case "loading": |
| 520 var tab = event.target; | 548 var tab = event.target; |
| 521 var message = event.message; | 549 var message = event.message; |
| 522 | 550 |
| 523 var pageId; | 551 var pageId; |
| 524 var frameId; | 552 var frameId; |
| 525 | 553 |
| 526 if (message.isTopLevel) | 554 if (message.isTopLevel) |
| 527 { | 555 { |
| 528 pageId = ++pageCounter; | 556 pageId = addPage(tab, message.url, message.isPrerendered); |
| 529 frameId = 0; | 557 frameId = 0; |
| 530 | 558 |
| 531 if (!('_pages' in tab)) | 559 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 } | 560 } |
| 548 else | 561 else |
| 549 { | 562 { |
| 550 var page; | 563 var page; |
| 551 var parentFrame; | 564 var parentFrame; |
| 552 | 565 |
| 553 var lastPageId; | 566 var lastPageId; |
| 554 var lastPage; | 567 var lastPage; |
| 555 var lastPageTopLevelFrame; | 568 var lastPageTopLevelFrame; |
| 556 | 569 |
| (...skipping 30 matching lines...) Expand all Loading... | |
| 587 if (!page) | 600 if (!page) |
| 588 { | 601 { |
| 589 pageId = lastPageId; | 602 pageId = lastPageId; |
| 590 page = lastPage; | 603 page = lastPage; |
| 591 parentFrame = lastPageTopLevelFrame; | 604 parentFrame = lastPageTopLevelFrame; |
| 592 } | 605 } |
| 593 | 606 |
| 594 frameId = page._frames.length; | 607 frameId = page._frames.length; |
| 595 page._frames.push({url: new URL(message.url), parent: parentFrame} ); | 608 page._frames.push({url: new URL(message.url), parent: parentFrame} ); |
| 596 } | 609 } |
| 597 | |
| 598 event.message = {pageId: pageId, frameId: frameId}; | 610 event.message = {pageId: pageId, frameId: frameId}; |
| 599 break; | 611 break; |
| 600 case "webRequest": | 612 case "webRequest": |
| 601 var page = pages[event.message.pageId]; | 613 var page = pages[event.message.pageId]; |
| 602 var frame = page._frames[event.message.frameId]; | 614 var frame = page._frames[event.message.frameId]; |
| 603 | 615 |
| 604 var results = ext.webRequest.onBeforeRequest._dispatch( | 616 var results = ext.webRequest.onBeforeRequest._dispatch( |
| 605 new URL(event.message.url, frame.url), | 617 new URL(event.message.url, frame.url), |
| 606 event.message.type, page, frame | 618 event.message.type, page, frame |
| 607 ); | 619 ); |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 666 tab.activate(); | 678 tab.activate(); |
| 667 if (callback) | 679 if (callback) |
| 668 callback(page); | 680 callback(page); |
| 669 return; | 681 return; |
| 670 } | 682 } |
| 671 } | 683 } |
| 672 | 684 |
| 673 ext.pages.open(optionsUrl, callback); | 685 ext.pages.open(optionsUrl, callback); |
| 674 }; | 686 }; |
| 675 })(); | 687 })(); |
| OLD | NEW |