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

Delta Between Two Patch Sets: safari/ext/background.js

Issue 5693109165883392: Issue 2040 - Replaced localStorage with chrome.storage.local (Closed)
Left Patch Set: Rebased Created March 3, 2015, 3:11 p.m.
Right Patch Set: Fixed typo in comment Created April 13, 2015, 10:30 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « qunit/common.js ('k') | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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 /* Context menus */
21
22 var contextMenuItems = new ext.PageMap();
23
24 var ContextMenus = function(page)
25 {
26 this._page = page;
27 };
28 ContextMenus.prototype = {
29 create: function(item)
30 {
31 var items = contextMenuItems.get(this._page);
32 if (!items)
33 contextMenuItems.set(this._page, items = []);
34
35 items.push(item);
36 },
37 removeAll: function()
38 {
39 contextMenuItems.delete(this._page);
40 }
41 };
42
43 safari.application.addEventListener("contextmenu", function(event)
44 {
45 if (!event.userInfo)
46 return;
47
48 var pageId = event.userInfo.pageId;
49 if (!pageId)
50 return;
51
52 var page = pages[event.userInfo.pageId];
53 var items = contextMenuItems.get(page);
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 });
79
80
81 /* Browser actions */
82
83 var toolbarItemProperties = {};
84
85 var getToolbarItemForWindow = function(win)
86 {
87 for (var i = 0; i < safari.extension.toolbarItems.length; i++)
88 {
89 var toolbarItem = safari.extension.toolbarItems[i];
90
91 if (toolbarItem.browserWindow == win)
92 return toolbarItem;
93 }
94
95 return null;
96 };
97
98 var updateToolbarItemForPage = function(page, win) {
99 var toolbarItem = getToolbarItemForWindow(win);
100 if (!toolbarItem)
101 return;
102
103 for (var name in toolbarItemProperties)
104 {
105 var property = toolbarItemProperties[name];
106
107 if (page && property.pages.has(page))
108 toolbarItem[name] = property.pages.get(page);
109 else
110 toolbarItem[name] = property.global;
111 }
112 };
113
114 var BrowserAction = function(page)
115 {
116 this._page = page;
117 };
118 BrowserAction.prototype = {
119 _set: function(name, value)
120 {
121 var toolbarItem = getToolbarItemForWindow(this._page._tab.browserWindow);
122 if (!toolbarItem)
123 return;
124
125 var property = toolbarItemProperties[name];
126 if (!property)
127 property = toolbarItemProperties[name] = {
128 pages: new ext.PageMap(),
129 global: toolbarItem[name]
130 };
131
132 property.pages.set(this._page, value);
133
134 if (isPageActive(this._page))
135 toolbarItem[name] = value;
136 },
137 setIcon: function(path)
138 {
139 this._set("image", safari.extension.baseURI + path.replace("$size", "16")) ;
140 },
141 setBadge: function(badge)
142 {
143 if (!badge)
144 this._set("badge", 0);
145 else if ("number" in badge)
146 this._set("badge", badge.number);
147 }
148 };
149
150 safari.application.addEventListener("activate", function(event)
151 {
152 // this event is also dispatched on windows that got focused. But we
153 // are only interested in tabs, which became active in their window.
154 if (!(event.target instanceof SafariBrowserTab))
155 return;
156
157 // update the toolbar item for the page visible in the tab that just
158 // became active. If we can't find that page (e.g. when a page was
159 // opened in a new tab, and our content script didn't run yet), the
160 // toolbar item of the window, is reset to its intial configuration.
161 updateToolbarItemForPage(event.target._visiblePage, event.target.browserWind ow);
162 }, true);
163
164
20 /* Pages */ 165 /* Pages */
21 166
22 var pages = Object.create(null); 167 var pages = Object.create(null);
23 var pageCounter = 0; 168 var pageCounter = 0;
24 169
170 var Frame = function(url, parent)
171 {
172 this._urlString = url;
173 this._urlObj = null;
174
175 this.parent = parent;
176 }
177 Frame.prototype = {
178 get url()
179 {
180 // On Safari 6 and before, the URL constuctor doesn't exist.
181 // The "urls" module provides a polifill, but it might not
182 // be loaded yet. So we have to lazily parse URLs.
183 if (!this._urlObj)
184 {
185 this._urlObj = new URL(this._urlString);
186 this._urlString = null;
187 }
188
189 return this._urlObj;
190 }
191 };
192
25 var Page = function(id, tab, url) 193 var Page = function(id, tab, url)
26 { 194 {
27 this._id = id; 195 this._id = id;
28 this._tab = tab; 196 this._tab = tab;
29 this._frames = [{url: new URL(url), parent: null}]; 197 this._frames = [new Frame(url, null)];
30 198
31 if (tab.page) 199 if (tab.page)
32 this._messageProxy = new ext._MessageProxy(tab.page); 200 this._messageProxy = new ext._MessageProxy(tab.page);
33 else 201 else
34 // while the new tab page is shown on Safari 7, the 'page' property 202 // 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 203 // of the tab is undefined, and we can't send messages to that page
36 this._messageProxy = { 204 this._messageProxy = {
37 handleRequest: function() {}, 205 handleRequest: function() {},
38 handleResponse: function() {}, 206 handleResponse: function() {},
39 sendMessage: function() {} 207 sendMessage: function() {}
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
81 for (var id in tab._pages) 249 for (var id in tab._pages)
82 { 250 {
83 if (id != page._id) 251 if (id != page._id)
84 forgetPage(id); 252 forgetPage(id);
85 } 253 }
86 254
87 if (isPageActive(page)) 255 if (isPageActive(page))
88 updateToolbarItemForPage(page, tab.browserWindow); 256 updateToolbarItemForPage(page, tab.browserWindow);
89 }; 257 };
90 258
259 var addPage = function(tab, url, prerendered)
260 {
261 var pageId = ++pageCounter;
262
263 if (!('_pages' in tab))
264 tab._pages = Object.create(null);
265
266 var page = new Page(pageId, tab, url);
267 pages[pageId] = tab._pages[pageId] = page;
268
269 // When a new page is shown, forget the previous page associated
270 // with its tab, and reset the toolbar item if necessary.
271 // Note that it wouldn't be sufficient to do that when the old
272 // page is unloading, because Safari dispatches window.onunload
273 // only when reloading the page or following links, but not when
274 // you enter a new URL in the address bar.
275 if (!prerendered)
276 replacePage(page);
277
278 return pageId;
279 };
280
91 ext.pages = { 281 ext.pages = {
92 open: function(url, callback) 282 open: function(url, callback)
93 { 283 {
94 var tab = safari.application.activeBrowserWindow.openTab(); 284 var tab = safari.application.activeBrowserWindow.openTab();
95 tab.url = url; 285 tab.url = url;
96 286
97 if (callback) 287 if (callback)
98 { 288 {
99 var onLoading = function(page) 289 var onLoading = function(page)
100 { 290 {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
138 return; 328 return;
139 329
140 // when a tab is closed, forget the previous page associated with that 330 // when a tab is closed, forget the previous page associated with that
141 // tab. Note that it wouldn't be sufficient do that when the old page 331 // tab. Note that it wouldn't be sufficient do that when the old page
142 // is unloading, because Safari dispatches window.onunload only when 332 // is unloading, because Safari dispatches window.onunload only when
143 // reloading the page or following links, but not when closing the tab. 333 // reloading the page or following links, but not when closing the tab.
144 for (var id in event.target._pages) 334 for (var id in event.target._pages)
145 forgetPage(id); 335 forgetPage(id);
146 }, true); 336 }, true);
147 337
148 338 // We generally rely on content scripts to report new pages,
149 /* Browser actions */ 339 // since Safari's extension API doesn't consider pre-rendered
150 340 // pages. However, when the extension initializes we have to
151 var toolbarItemProperties = {}; 341 // use Safari's extension API to detect existing tabs.
152 342 safari.application.browserWindows.forEach(function(win)
153 var getToolbarItemForWindow = function(win) 343 {
154 { 344 for (var i = 0; i < win.tabs.length; i++)
155 for (var i = 0; i < safari.extension.toolbarItems.length; i++) 345 {
156 { 346 var tab = win.tabs[i];
157 var toolbarItem = safari.extension.toolbarItems[i]; 347 var url = tab.url;
158 348
159 if (toolbarItem.browserWindow == win) 349 // For the new tab page the url property is undefined.
160 return toolbarItem; 350 if (url)
161 } 351 addPage(tab, url, false);
162 352 }
163 return null;
164 };
165
166 var updateToolbarItemForPage = function(page, win) {
167 var toolbarItem = getToolbarItemForWindow(win);
168 if (!toolbarItem)
169 return;
170
171 for (var name in toolbarItemProperties)
172 {
173 var property = toolbarItemProperties[name];
174
175 if (page && property.pages.has(page))
176 toolbarItem[name] = property.pages.get(page);
177 else
178 toolbarItem[name] = property.global;
179 }
180 };
181
182 var BrowserAction = function(page)
183 {
184 this._page = page;
185 };
186 BrowserAction.prototype = {
187 _set: function(name, value)
188 {
189 var toolbarItem = getToolbarItemForWindow(this._page._tab.browserWindow);
190 if (!toolbarItem)
191 return;
192
193 var property = toolbarItemProperties[name];
194 if (!property)
195 property = toolbarItemProperties[name] = {
196 pages: new ext.PageMap(),
197 global: toolbarItem[name]
198 };
199
200 property.pages.set(this._page, value);
201
202 if (isPageActive(this._page))
203 toolbarItem[name] = value;
204 },
205 setIcon: function(path)
206 {
207 this._set("image", safari.extension.baseURI + path.replace("$size", "16")) ;
208 },
209 setBadge: function(badge)
210 {
211 if (!badge)
212 this._set("badge", 0);
213 else if ("number" in badge)
214 this._set("badge", badge.number);
215 }
216 };
217
218 safari.application.addEventListener("activate", function(event)
219 {
220 // this event is also dispatched on windows that got focused. But we
221 // are only interested in tabs, which became active in their window.
222 if (!(event.target instanceof SafariBrowserTab))
223 return;
224
225 // 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
227 // 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.
229 updateToolbarItemForPage(event.target._visiblePage, event.target.browserWind ow);
230 }, true);
231
232
233 /* Context menus */
234
235 var contextMenuItems = new ext.PageMap();
236
237 var ContextMenus = function(page)
238 {
239 this._page = page;
240 };
241 ContextMenus.prototype = {
242 create: function(item)
243 {
244 var items = contextMenuItems.get(this._page);
245 if (!items)
246 contextMenuItems.set(this._page, items = []);
247
248 items.push(item);
249 },
250 removeAll: function()
251 {
252 contextMenuItems.delete(this._page);
253 }
254 };
255
256 safari.application.addEventListener("contextmenu", function(event)
257 {
258 if (!event.userInfo)
259 return;
260
261 var pageId = event.userInfo.pageId;
262 if (!pageId)
263 return;
264
265 var page = pages[event.userInfo.pageId];
266 var items = contextMenuItems.get(page);
267 if (!items)
268 return;
269
270 var context = event.userInfo.tagName;
271 if (context == "img")
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 }
283 });
284
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 }); 353 });
292 354
293 355
294 /* Web requests */ 356 /* Web requests */
295 357
296 ext.webRequest = { 358 ext.webRequest = {
297 onBeforeRequest: new ext._EventTarget(), 359 onBeforeRequest: new ext._EventTarget(),
298 handlerBehaviorChanged: function() {}, 360 handlerBehaviorChanged: function() {},
299 indistinguishableTypes: [["OTHER", "FONT"]] 361 indistinguishableTypes: [["OTHER", "FONT"]]
300 }; 362 };
(...skipping 217 matching lines...) Expand 10 before | Expand all | Expand 10 after
518 { 580 {
519 case "loading": 581 case "loading":
520 var tab = event.target; 582 var tab = event.target;
521 var message = event.message; 583 var message = event.message;
522 584
523 var pageId; 585 var pageId;
524 var frameId; 586 var frameId;
525 587
526 if (message.isTopLevel) 588 if (message.isTopLevel)
527 { 589 {
528 pageId = ++pageCounter; 590 pageId = addPage(tab, message.url, message.isPrerendered);
529 frameId = 0; 591 frameId = 0;
530 592
531 if (!('_pages' in tab)) 593 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 } 594 }
548 else 595 else
549 { 596 {
550 var page; 597 var page;
551 var parentFrame; 598 var parentFrame;
552 599
553 var lastPageId; 600 var lastPageId;
554 var lastPage; 601 var lastPage;
555 var lastPageTopLevelFrame; 602 var lastPageTopLevelFrame;
556 603
(...skipping 28 matching lines...) Expand all
585 // if we can't find the parent frame and its page, fall back to 632 // if we can't find the parent frame and its page, fall back to
586 // the page most recently loaded in the tab and its top level fram e 633 // the page most recently loaded in the tab and its top level fram e
587 if (!page) 634 if (!page)
588 { 635 {
589 pageId = lastPageId; 636 pageId = lastPageId;
590 page = lastPage; 637 page = lastPage;
591 parentFrame = lastPageTopLevelFrame; 638 parentFrame = lastPageTopLevelFrame;
592 } 639 }
593 640
594 frameId = page._frames.length; 641 frameId = page._frames.length;
595 page._frames.push({url: new URL(message.url), parent: parentFrame} ); 642 page._frames.push(new Frame(message.url, parentFrame));
596 } 643 }
597
598 event.message = {pageId: pageId, frameId: frameId}; 644 event.message = {pageId: pageId, frameId: frameId};
599 break; 645 break;
600 case "webRequest": 646 case "webRequest":
601 var page = pages[event.message.pageId]; 647 var page = pages[event.message.pageId];
602 var frame = page._frames[event.message.frameId]; 648 var frame = page._frames[event.message.frameId];
603 649
604 var results = ext.webRequest.onBeforeRequest._dispatch( 650 var results = ext.webRequest.onBeforeRequest._dispatch(
605 new URL(event.message.url, frame.url), 651 new URL(event.message.url, frame.url),
606 event.message.type, page, frame 652 event.message.type, page, frame
607 ); 653 );
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
653 var items = {}; 699 var items = {};
654 var settings = safari.extension.settings; 700 var settings = safari.extension.settings;
655 701
656 for (var i = 0; i < keys.length; i++) 702 for (var i = 0; i < keys.length; i++)
657 { 703 {
658 var key = keys[i]; 704 var key = keys[i];
659 if (key in settings) 705 if (key in settings)
660 items[key] = settings[key]; 706 items[key] = settings[key];
661 } 707 }
662 708
663 setTimeout(callback, 0, items); 709 setTimeout(callback, 0, items);
Wladimir Palant 2015/03/12 23:05:37 Did you verify that this works on all Safari versi
Sebastian Noack 2015/03/12 23:35:29 I saw this one coming. I checked MDN and made the
664 }, 710 },
665 set: function(key, value, callback) 711 set: function(key, value, callback)
666 { 712 {
667 safari.extension.settings[key] = value; 713 safari.extension.settings[key] = value;
668 714
669 if (callback) 715 if (callback)
670 setTimeout(callback, 0); 716 setTimeout(callback, 0);
671 }, 717 },
672 remove: function(key, callback) 718 remove: function(key, callback)
673 { 719 {
674 delete safari.extension.settings[key]; 720 delete safari.extension.settings[key];
675 721
676 if (callback) 722 if (callback)
677 setTimeout(callback, 0); 723 setTimeout(callback, 0);
678 }, 724 },
679 onChanged: new ext._EventTarget(), 725 onChanged: new ext._EventTarget(),
680 726
681 // Preferences were previously encoded as JSON for compatibility 727 // Preferences were previously encoded as JSON for compatibility
682 // with localStorage, which has been used on Chrome. 728 // with localStorage, which has been used on Chrome.
683 migratePrefs: function(prefs) 729 migratePrefs: function(hooks)
684 { 730 {
685 var settings = safari.extension.settings; 731 var settings = safari.extension.settings;
686 732
687 for (var key in settings) 733 for (var key in settings)
688 { 734 {
689 if (key in prefs) 735 var item = hooks.map(key, settings[key]);
736
737 if (item)
690 { 738 {
691 try
692 {
693 settings["pref:" + key] = JSON.parse(settings[key]);
694 }
695 catch (e)
696 {
697 }
698
699 delete settings[key]; 739 delete settings[key];
740 settings[item.key] = item.value;
700 } 741 }
701 } 742 }
743
744 hooks.done();
702 } 745 }
703 }; 746 };
704 747
705 safari.extension.settings.addEventListener("change", function(event) 748 safari.extension.settings.addEventListener("change", function(event)
706 { 749 {
707 var changes = {}; 750 var changes = {};
708 var change = changes[event.key] = {}; 751 var change = changes[event.key] = {};
709 752
710 if (event.oldValue != null) 753 if (event.oldValue != null)
711 change.oldValue = event.oldValue; 754 change.oldValue = event.oldValue;
(...skipping 20 matching lines...) Expand all
732 tab.activate(); 775 tab.activate();
733 if (callback) 776 if (callback)
734 callback(page); 777 callback(page);
735 return; 778 return;
736 } 779 }
737 } 780 }
738 781
739 ext.pages.open(optionsUrl, callback); 782 ext.pages.open(optionsUrl, callback);
740 }; 783 };
741 })(); 784 })();
LEFTRIGHT

Powered by Google App Engine
This is Rietveld