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

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

Issue 29363445: Issue 4577 - Remove Safari support (Closed)
Patch Set: Created Nov. 17, 2016, 11:36 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 | « safari/contentBlocking.js ('k') | safari/ext/common.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * This file is part of Adblock Plus <https://adblockplus.org/>,
3 * Copyright (C) 2006-2016 Eyeo GmbH
4 *
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
7 * published by the Free Software Foundation.
8 *
9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
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/>.
16 */
17
18 (function()
19 {
20 /* Context menus */
21
22 var contextMenuItems = new ext.PageMap();
23 var lastContextMenuTab;
24
25 var ContextMenus = function(page)
26 {
27 this._page = page;
28 };
29 ContextMenus.prototype = {
30 create: function(item)
31 {
32 var items = contextMenuItems.get(this._page);
33 if (!items)
34 contextMenuItems.set(this._page, items = []);
35
36 items.push(item);
37 },
38 remove: function(item)
39 {
40 let items = contextMenuItems.get(this._page);
41 if (items)
42 {
43 let index = items.indexOf(item);
44 if (index != -1)
45 items.splice(index, 1);
46 }
47 }
48 };
49
50 safari.application.addEventListener("contextmenu", function(event)
51 {
52 lastContextMenuTab = event.target;
53
54 if (!event.userInfo)
55 return;
56
57 var documentId = event.userInfo.documentId;
58 if (!documentId)
59 return;
60
61 var page = pages[event.target._documentLookup[documentId].pageId];
62 var items = contextMenuItems.get(page);
63 if (!items)
64 return;
65
66 var context = event.userInfo.tagName;
67 if (context == "img")
68 context = "image";
69
70 for (var i = 0; i < items.length; i++)
71 {
72 // Supported contexts are: all, audio, image, video
73 var menuItem = items[i];
74 if (menuItem.contexts.indexOf("all") == -1 && menuItem.contexts.indexOf(co ntext) == -1)
75 continue;
76
77 event.contextMenu.appendContextMenuItem(i, menuItem.title);
78 }
79 });
80
81 safari.application.addEventListener("command", function(event)
82 {
83 var documentId = event.userInfo.documentId;
84 var page = pages[lastContextMenuTab._documentLookup[documentId].pageId];
85 var items = contextMenuItems.get(page);
86
87 items[event.command].onclick(page);
88 });
89
90
91 /* Browser actions */
92
93 var toolbarItemProperties = {};
94
95 var getToolbarItemForWindow = function(win)
96 {
97 for (var i = 0; i < safari.extension.toolbarItems.length; i++)
98 {
99 var toolbarItem = safari.extension.toolbarItems[i];
100
101 if (toolbarItem.browserWindow == win)
102 return toolbarItem;
103 }
104
105 return null;
106 };
107
108 var updateToolbarItemForPage = function(page, win) {
109 var toolbarItem = getToolbarItemForWindow(win);
110 if (!toolbarItem)
111 return;
112
113 for (var name in toolbarItemProperties)
114 {
115 var property = toolbarItemProperties[name];
116
117 if (page && property.pages.has(page))
118 toolbarItem[name] = property.pages.get(page);
119 else
120 toolbarItem[name] = property.global;
121 }
122 };
123
124 var BrowserAction = function(page)
125 {
126 this._page = page;
127 };
128 BrowserAction.prototype = {
129 _set: function(name, value)
130 {
131 var toolbarItem = getToolbarItemForWindow(this._page._tab.browserWindow);
132 if (!toolbarItem)
133 return;
134
135 var property = toolbarItemProperties[name];
136 if (!property)
137 property = toolbarItemProperties[name] = {
138 pages: new ext.PageMap(),
139 global: toolbarItem[name]
140 };
141
142 property.pages.set(this._page, value);
143
144 if (isPageActive(this._page))
145 toolbarItem[name] = value;
146 },
147 setIcon: function(path)
148 {
149 this._set("image", safari.extension.baseURI + path.replace("$size", "16")) ;
150 },
151 setBadge: function(badge)
152 {
153 if (!badge)
154 this._set("badge", 0);
155 else if ("number" in badge)
156 this._set("badge", badge.number);
157 }
158 };
159
160 safari.application.addEventListener("activate", function(event)
161 {
162 // this event is also dispatched on windows that got focused. But we
163 // are only interested in tabs, which became active in their window.
164 if (!(event.target instanceof SafariBrowserTab))
165 return;
166
167 let visiblePage = event.target._visiblePage;
168 if (visiblePage)
169 ext.pages.onActivated._dispatch(visiblePage);
170
171 // update the toolbar item for the page visible in the tab that just
172 // became active. If we can't find that page (e.g. when a page was
173 // opened in a new tab, and our content script didn't run yet), the
174 // toolbar item of the window, is reset to its intial configuration.
175 updateToolbarItemForPage(visiblePage, event.target.browserWindow);
176 }, true);
177
178
179 /* Pages */
180
181 var pages = Object.create(null);
182 var pageCounter = 0;
183
184 var Page = function(id, tab, url)
185 {
186 this.id = id;
187 this._tab = tab;
188 this._frames = [{url: new URL(url), parent: null}];
189
190 if (tab.page)
191 this._messageProxy = new ext._MessageProxy(tab.page);
192 else
193 // while the new tab page is shown on Safari 7, the 'page' property
194 // of the tab is undefined, and we can't send messages to that page
195 this._messageProxy = {
196 handleRequest: function() {},
197 handleResponse: function() {},
198 sendMessage: function() {}
199 };
200
201 this.browserAction = new BrowserAction(this);
202 this.contextMenus = new ContextMenus(this);
203 };
204 Page.prototype = {
205 get url()
206 {
207 return this._frames[0].url;
208 },
209 sendMessage: function(message, responseCallback)
210 {
211 var documentIds = [];
212 for (var documentId in this._tab._documentLookup)
213 if (this._tab._documentLookup[documentId].pageId == this.id)
214 documentIds.push(documentId);
215
216 this._messageProxy.sendMessage(message, responseCallback,
217 {targetDocuments: documentIds});
218 }
219 };
220
221 ext.getPage = function(id)
222 {
223 return pages[id];
224 };
225
226 var isPageActive = function(page)
227 {
228 var tab = page._tab;
229 var win = tab.browserWindow;
230 return win && tab == win.activeTab && page == tab._visiblePage;
231 };
232
233 var forgetPage = function(id)
234 {
235 ext.pages.onRemoved._dispatch(id);
236
237 ext._removeFromAllPageMaps(id);
238
239 var tab = pages[id]._tab;
240
241 for (var documentId in tab._documentLookup)
242 {
243 if (tab._documentLookup[documentId].pageId == id)
244 delete tab._documentLookup[documentId];
245 }
246
247 delete tab._pages[id];
248 delete pages[id];
249 };
250
251 var replacePage = function(page)
252 {
253 var tab = page._tab;
254 tab._visiblePage = page;
255
256 for (var id in tab._pages)
257 {
258 if (id != page.id)
259 forgetPage(id);
260 }
261
262 if (isPageActive(page))
263 updateToolbarItemForPage(page, tab.browserWindow);
264 };
265
266 var addPage = function(tab, url, prerendered)
267 {
268 var pageId = ++pageCounter;
269
270 if (!('_pages' in tab))
271 tab._pages = Object.create(null);
272
273 if (!('_documentLookup' in tab))
274 tab._documentLookup = Object.create(null);
275
276 var page = new Page(pageId, tab, url);
277 pages[pageId] = tab._pages[pageId] = page;
278
279 // When a new page is shown, forget the previous page associated
280 // with its tab, and reset the toolbar item if necessary.
281 // Note that it wouldn't be sufficient to do that when the old
282 // page is unloading, because Safari dispatches window.onunload
283 // only when reloading the page or following links, but not when
284 // you enter a new URL in the address bar.
285 if (!prerendered)
286 replacePage(page);
287
288 return pageId;
289 };
290
291 ext.pages = {
292 open: function(url, callback)
293 {
294 var tab = safari.application.activeBrowserWindow.openTab();
295 tab.url = url;
296
297 if (callback)
298 {
299 var onNavigate = function(event)
300 {
301 if (event.target == tab)
302 {
303 safari.application.removeEventListener(onNavigate);
304 callback(tab._visiblePage);
305 }
306 };
307
308 safari.application.addEventListener("navigate", onNavigate);
309 }
310 },
311 query: function(info, callback)
312 {
313 var matchedPages = [];
314
315 for (var id in pages)
316 {
317 var page = pages[id];
318 var win = page._tab.browserWindow;
319
320 if ("active" in info && info.active != isPageActive(page))
321 continue;
322 if ("lastFocusedWindow" in info && info.lastFocusedWindow != (win == saf ari.application.activeBrowserWindow))
323 continue;
324
325 matchedPages.push(page);
326 };
327
328 callback(matchedPages);
329 },
330 onLoading: new ext._EventTarget(),
331 onActivated: new ext._EventTarget(),
332 onRemoved: new ext._EventTarget()
333 };
334
335 safari.application.addEventListener("close", function(event)
336 {
337 // this event is dispatched on closing windows and tabs. However when a
338 // window is closed, it is first dispatched on each tab in the window and
339 // then on the window itself. But we are only interested in closed tabs.
340 if (!(event.target instanceof SafariBrowserTab))
341 return;
342
343 // when a tab is closed, forget the previous page associated with that
344 // tab. Note that it wouldn't be sufficient do that when the old page
345 // is unloading, because Safari dispatches window.onunload only when
346 // reloading the page or following links, but not when closing the tab.
347 for (var id in event.target._pages)
348 forgetPage(id);
349 }, true);
350
351 // We generally rely on content scripts to report new pages,
352 // since Safari's extension API doesn't consider pre-rendered
353 // pages. However, when the extension initializes we have to
354 // use Safari's extension API to detect existing tabs.
355 safari.application.browserWindows.forEach(function(win)
356 {
357 for (var i = 0; i < win.tabs.length; i++)
358 {
359 var tab = win.tabs[i];
360 var url = tab.url;
361
362 // For the new tab page the url property is undefined.
363 if (url)
364 {
365 var pageId = addPage(tab, url, false);
366 tab.page.dispatchMessage("requestDocumentId", {pageId: pageId});
367 }
368 }
369 });
370
371
372 /* Web requests */
373
374 ext.webRequest = {
375 onBeforeRequest: new ext._EventTarget(),
376 handlerBehaviorChanged: function()
377 {
378 },
379 getIndistinguishableTypes: function()
380 {
381 return [];
382 }
383 };
384
385 /* Message processing */
386
387 var dispatchedLegacyAPISupportMessage = false;
388 safari.application.addEventListener("message", function(event)
389 {
390 var tab = event.target;
391 var message = event.message;
392 var sender;
393 if ("documentId" in message && "_documentLookup" in tab)
394 {
395 sender = tab._documentLookup[message.documentId];
396 if (sender)
397 {
398 sender.page = pages[sender.pageId];
399 sender.frame = sender.page._frames[sender.frameId];
400 }
401 }
402
403 switch (event.name)
404 {
405 case "canLoad":
406 switch (message.category)
407 {
408 case "webRequest":
409 var results = ext.webRequest.onBeforeRequest._dispatch(
410 new URL(message.url, sender.frame.url),
411 message.type, sender.page, sender.frame
412 );
413
414 event.message = (results.indexOf(false) == -1);
415 break;
416 case "request":
417 var response = null;
418 var sendResponse = function(message) { response = message; };
419
420 ext.onMessage._dispatch(message.payload, sender, sendResponse);
421
422 event.message = response;
423 break;
424 }
425 break;
426 case "request":
427 sender.page._messageProxy.handleRequest(message, sender);
428 break;
429 case "response":
430 // All documents within a page have the same pageId and that's all we
431 // care about here.
432 var pageId = tab._documentLookup[message.targetDocuments[0]].pageId;
433 pages[pageId]._messageProxy.handleResponse(message);
434 break;
435 case "replaced":
436 // when a prerendered page is shown, forget the previous page
437 // associated with its tab, and reset the toolbar item if necessary.
438 // Note that it wouldn't be sufficient to do that when the old
439 // page is unloading, because Safari dispatches window.onunload
440 // only when reloading the page or following links, but not when
441 // the current page is replaced with a prerendered page.
442 replacePage(sender.page);
443 break;
444 case "loading":
445 var pageId;
446 var frameId;
447 var documentId = message.documentId;
448
449 if (message.isTopLevel)
450 {
451 pageId = addPage(tab, message.url, message.isPrerendered);
452 frameId = 0;
453
454 ext.pages.onLoading._dispatch(pages[pageId]);
455 }
456 else
457 {
458 var page;
459 var parentFrame;
460
461 var lastPageId;
462 var lastPage;
463 var lastPageTopLevelFrame;
464
465 // find the parent frame and its page for this sub frame,
466 // by matching its referrer with the URL of frames previously
467 // loaded in the same tab. If there is more than one match,
468 // the most recent loaded page and frame is preferred.
469 for (var curPageId in tab._pages)
470 {
471 var curPage = pages[curPageId];
472
473 for (var i = 0; i < curPage._frames.length; i++)
474 {
475 var curFrame = curPage._frames[i];
476
477 if (curFrame.url.href == message.referrer)
478 {
479 pageId = curPageId;
480 page = curPage;
481 parentFrame = curFrame;
482 }
483
484 if (i == 0)
485 {
486 lastPageId = curPageId;
487 lastPage = curPage;
488 lastPageTopLevelFrame = curFrame;
489 }
490 }
491 }
492
493 // if we can't find the parent frame and its page, fall back to
494 // the page most recently loaded in the tab and its top level frame
495 if (!page)
496 {
497 pageId = lastPageId;
498 page = lastPage;
499 parentFrame = lastPageTopLevelFrame;
500 }
501
502 frameId = page._frames.length;
503 page._frames.push({url: new URL(message.url), parent: parentFrame});
504 }
505
506 tab._documentLookup[documentId] = {pageId: pageId, frameId: frameId};
507
508 if (!dispatchedLegacyAPISupportMessage)
509 {
510 ext.onMessage._dispatch({
511 type: "safari.legacyAPISupported",
512 legacyAPISupported: message.legacyAPISupported
513 });
514 dispatchedLegacyAPISupportMessage = true;
515 }
516 break;
517 case "documentId":
518 tab._documentLookup[message.documentId] = {
519 pageId: message.pageId, frameId: 0
520 };
521 break;
522 }
523 });
524
525
526 /* Storage */
527
528 ext.storage = {
529 get: function(keys, callback)
530 {
531 var items = {};
532 var settings = safari.extension.settings;
533
534 for (var i = 0; i < keys.length; i++)
535 {
536 var key = keys[i];
537 if (key in settings)
538 items[key] = settings[key];
539 }
540
541 setTimeout(callback, 0, items);
542 },
543 set: function(key, value, callback)
544 {
545 safari.extension.settings[key] = value;
546
547 if (callback)
548 setTimeout(callback, 0);
549 },
550 remove: function(key, callback)
551 {
552 delete safari.extension.settings[key];
553
554 if (callback)
555 setTimeout(callback, 0);
556 },
557 onChanged: new ext._EventTarget()
558 };
559
560 safari.extension.settings.addEventListener("change", function(event)
561 {
562 var changes = {};
563 var change = changes[event.key] = {};
564
565 if (event.oldValue != null)
566 change.oldValue = event.oldValue;
567 if (event.newValue != null)
568 change.newValue = event.newValue;
569
570 ext.storage.onChanged._dispatch(changes);
571 });
572
573
574 /* Options */
575
576 ext.showOptions = function(callback)
577 {
578 var optionsUrl = safari.extension.baseURI + "options.html";
579
580 for (var id in pages)
581 {
582 var page = pages[id];
583 var tab = page._tab;
584
585 if (page.url.href == optionsUrl && tab.browserWindow == safari.application .activeBrowserWindow)
586 {
587 tab.activate();
588 if (callback)
589 callback(page);
590 return;
591 }
592 }
593
594 ext.pages.open(optionsUrl, callback);
595 };
596
597 /* Windows */
598 ext.windows = {
599 // Safari doesn't provide as rich a windows API as Chrome does, so instead
600 // of chrome.windows.create we have to fall back to just opening a new tab.
601 create: function(createData, callback)
602 {
603 ext.pages.open(createData.url, callback);
604 }
605 };
606 })();
OLDNEW
« no previous file with comments | « safari/contentBlocking.js ('k') | safari/ext/common.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld