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 |