Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: safari/ext/background.js

Issue 4937860104847360: Issue 2082 - Detect existing tabs when loading extension on Safari (Closed)
Patch Set: Added comments Created March 4, 2015, 11:03 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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 })();
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld