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

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

Issue 6346177440120832: Added abstraction for frames, to fix domain-based rules, whitelisting and ad counter on Safari (Closed)
Patch Set: Created Dec. 21, 2013, 7:48 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <http://adblockplus.org/>, 2 * This file is part of Adblock Plus <http://adblockplus.org/>,
3 * Copyright (C) 2006-2013 Eyeo GmbH 3 * Copyright (C) 2006-2013 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 /* Tabs */ 20 /* Events */
21 21
22 var TabEventTarget = function() 22 var TabEventTarget = function()
23 { 23 {
24 WrappedEventTarget.apply(this, arguments); 24 WrappedEventTarget.apply(this, arguments);
25 }; 25 };
26 TabEventTarget.prototype = { 26 TabEventTarget.prototype = {
27 __proto__: WrappedEventTarget.prototype, 27 __proto__: WrappedEventTarget.prototype,
28 _wrapListener: function(listener) 28 _wrapListener: function(listener)
29 { 29 {
30 return function(event) 30 return function(event)
(...skipping 13 matching lines...) Expand all
44 _wrapListener: function(listener) 44 _wrapListener: function(listener)
45 { 45 {
46 return function (event) 46 return function (event)
47 { 47 {
48 if (event.name == "loading") 48 if (event.name == "loading")
49 listener(new Tab(event.target)); 49 listener(new Tab(event.target));
50 }; 50 };
51 } 51 }
52 }; 52 };
53 53
54 var BackgroundMessageEventTarget = function()
55 {
56 MessageEventTarget.call(this, safari.application);
57 };
58 BackgroundMessageEventTarget.prototype = {
59 __proto__: MessageEventTarget.prototype,
60 _getResponseDispatcher: function(event)
61 {
62 return event.target.page;
63 },
64 _getSenderDetails: function(event)
65 {
66 return {
67 tab: new Tab(event.target),
68 frame: new Frame(
69 event.message.documentUrl,
70 event.message.isTopLevel,
71 event.target
72 )
73 };
74 }
75 };
76
77
78 /* Tabs */
79
54 Tab = function(tab) 80 Tab = function(tab)
55 { 81 {
56 this._tab = tab; 82 this._tab = tab;
57 83
58 this._eventTarget = tab;
59 this._messageDispatcher = tab.page;
60
61 this.onLoading = new LoadingTabEventTarget(tab); 84 this.onLoading = new LoadingTabEventTarget(tab);
85 this.onBeforeNavigate = new TabEventTarget(tab, "beforeNavigate", false);
62 this.onCompleted = new TabEventTarget(tab, "navigate", false); 86 this.onCompleted = new TabEventTarget(tab, "navigate", false);
63 this.onActivated = new TabEventTarget(tab, "activate", false); 87 this.onActivated = new TabEventTarget(tab, "activate", false);
64 this.onRemoved = new TabEventTarget(tab, "close", false); 88 this.onRemoved = new TabEventTarget(tab, "close", false);
65 }; 89 };
66 Tab.prototype = { 90 Tab.prototype = {
67 get url() 91 get url()
68 { 92 {
69 return this._tab.url; 93 return this._tab.url;
70 }, 94 },
71 close: function() 95 close: function()
72 { 96 {
73 this._tab.close(); 97 this._tab.close();
74 }, 98 },
75 activate: function() 99 activate: function()
76 { 100 {
77 this._tab.activate(); 101 this._tab.activate();
78 }, 102 },
79 sendMessage: sendMessage, 103 sendMessage: function(message, responseCallback)
104 {
105 _sendMessage(
106 message, responseCallback,
107 this._tab.page, this._tab
108 );
109 },
80 browserAction: { 110 browserAction: {
81 setIcon: function(path) 111 setIcon: function(path)
82 { 112 {
83 safari.extension.toolbarItems[0].image = safari.extension.baseURI + path ; 113 safari.extension.toolbarItems[0].image = safari.extension.baseURI + path ;
84 }, 114 },
85 setTitle: function(title) 115 setTitle: function(title)
86 { 116 {
87 safari.extension.toolbarItems[0].toolTip = title; 117 safari.extension.toolbarItems[0].toolTip = title;
88 }, 118 },
89 setBadge: function(badge) 119 setBadge: function(badge)
90 { 120 {
91 if (!badge) 121 if (!badge)
92 safari.extension.toolbarItems[0].badge = 0; 122 safari.extension.toolbarItems[0].badge = 0;
93 else if ("number" in badge) 123 else if ("number" in badge)
94 safari.extension.toolbarItems[0].badge = badge.number; 124 safari.extension.toolbarItems[0].badge = badge.number;
95 } 125 }
96 } 126 }
97 }; 127 };
98 128
99 TabMap = function() 129 TabMap = function(deleteTabOnBeforeNavigate)
100 { 130 {
101 this._tabs = []; 131 this._tabs = [];
102 this._values = []; 132 this._values = [];
103 133
104 this._onClosed = this._onClosed.bind(this); 134 this._deleteOnEvent = this._deleteOnEvent.bind(this);
135 this._deleteTabOnBeforeNavigate = deleteTabOnBeforeNavigate;
105 }; 136 };
106 TabMap.prototype = 137 TabMap.prototype =
107 { 138 {
108 get: function(tab) { 139 get: function(tab) {
109 var idx; 140 var idx;
110 141
111 if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1) 142 if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1)
112 return null; 143 return null;
113 144
114 return this._values[idx]; 145 return this._values[idx];
115 }, 146 },
116 set: function(tab, value) 147 set: function(tab, value)
117 { 148 {
118 var idx = this._tabs.indexOf(tab._tab); 149 var idx = this._tabs.indexOf(tab._tab);
119 150
120 if (idx != -1) 151 if (idx != -1)
121 this._values[idx] = value; 152 this._values[idx] = value;
122 else 153 else
123 { 154 {
124 this._tabs.push(tab._tab); 155 this._tabs.push(tab._tab);
125 this._values.push(value); 156 this._values.push(value);
126 157
127 tab._tab.addEventListener("close", this._onClosed, false); 158 tab._tab.addEventListener("close", this._deleteOnEvent, false);
159 if (this._deleteTabOnBeforeNavigate)
160 tab._tab.addEventListener("beforeNavigate", this._deleteOnEvent, false );
128 } 161 }
129 }, 162 },
130 has: function(tab) 163 has: function(tab)
131 { 164 {
132 return this._tabs.indexOf(tab._tab) != -1; 165 return this._tabs.indexOf(tab._tab) != -1;
133 }, 166 },
134 clear: function() 167 clear: function()
135 { 168 {
136 while (this._tabs.length > 0) 169 while (this._tabs.length > 0)
137 this._delete(this._tabs[0]); 170 this._delete(this._tabs[0]);
138 }, 171 },
139 _delete: function(tab) 172 _delete: function(tab)
140 { 173 {
141 var idx = this._tabs.indexOf(tab); 174 var idx = this._tabs.indexOf(tab);
142 175
143 if (idx != -1) 176 if (idx != -1)
144 { 177 {
145 this._tabs.splice(idx, 1); 178 this._tabs.splice(idx, 1);
146 this._values.splice(idx, 1); 179 this._values.splice(idx, 1);
147 180
148 tab.removeEventListener("close", this._onClosed, false); 181 tab.removeEventListener("close", this._deleteOnEvent, false);
182 tab.removeEventListener("beforeNavigate", this._deleteOnEvent, false);
149 } 183 }
150 }, 184 },
151 _onClosed: function(event) 185 _deleteOnEvent: function(event)
152 { 186 {
153 this._delete(event.target); 187 // delay so that other event handlers can still lookup this tab
188 setTimeout(this._delete.bind(this, event.target), 0);
Felix Dahlke 2014/01/18 13:39:19 s/lookup this tab/look this tab up/
Sebastian Noack 2014/01/19 10:19:40 Done.
154 } 189 }
155 }; 190 };
156 TabMap.prototype["delete"] = function(tab) 191 TabMap.prototype["delete"] = function(tab)
157 { 192 {
158 this._delete(tab._tab); 193 this._delete(tab._tab);
159 }; 194 };
160 195
161 196
162 /* Windows */ 197 /* Windows */
163 198
(...skipping 18 matching lines...) Expand all
182 { 217 {
183 var tab = this._win.openTab(); 218 var tab = this._win.openTab();
184 tab.url = url; 219 tab.url = url;
185 220
186 if (callback) 221 if (callback)
187 callback(new Tab(tab)); 222 callback(new Tab(tab));
188 } 223 }
189 }; 224 };
190 225
191 226
227 /* Frames */
228
229 Frame = function(url, isTopLevel, tab)
230 {
231 this.url = url;
232
233 // there is no way to discover frames with Safari's API.
234 // so if this isn't the top level frame, assume that the parent is.
235 // this is the best we can do for Safari. :(
Felix Dahlke 2014/01/18 13:39:19 We could cache a chain of frames like we do in And
236 if (!isTopLevel)
237 this.parent = new Frame(tab.url, true);
238 else
239 this.parent = null;
240 };
241
242
192 /* Background page proxy */ 243 /* Background page proxy */
193 244
194 var proxy = { 245 var proxy = {
195 tabs: [], 246 tabs: [],
196 objects: [], 247 objects: [],
197 248
198 registerObject: function(obj, objects) 249 registerObject: function(obj, objects)
199 { 250 {
200 var objectId = objects.indexOf(obj); 251 var objectId = objects.indexOf(obj);
201 252
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after
412 } 463 }
413 } 464 }
414 }; 465 };
415 466
416 467
417 /* Web request blocking */ 468 /* Web request blocking */
418 469
419 ext.webRequest = { 470 ext.webRequest = {
420 onBeforeRequest: { 471 onBeforeRequest: {
421 _listeners: [], 472 _listeners: [],
422 _urlPatterns: [],
423 473
424 _handleMessage: function(message, tab) 474 _handleMessage: function(message, rawTab)
425 { 475 {
426 tab = new Tab(tab); 476 var tab = new Tab(rawTab);
477 var frame = new Frame(message.documentUrl, message.isTopLevel, rawTab);
427 478
428 for (var i = 0; i < this._listeners.length; i++) 479 for (var i = 0; i < this._listeners.length; i++)
429 { 480 {
430 var regex = this._urlPatterns[i]; 481 if (this._listeners[i](message.url, message.type, tab, frame) === fals e)
431
432 if ((!regex || regex.test(message.url)) && this._listeners[i](message. url, message.type, tab, 0, -1) === false)
433 return false; 482 return false;
434 } 483 }
435 484
436 return true; 485 return true;
437 }, 486 },
438 addListener: function(listener, urls) 487 addListener: function(listener)
439 { 488 {
440 var regex;
441
442 if (urls)
443 regex = new RegExp("^(?:" + urls.map(function(url)
444 {
445 return url.split("*").map(function(s)
446 {
447 return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1");
448 }).join(".*");
449 }).join("|") + ")($|[?#])");
450
451 this._listeners.push(listener); 489 this._listeners.push(listener);
452 this._urlPatterns.push(regex);
453 }, 490 },
454 removeListener: function(listener) 491 removeListener: function(listener)
455 { 492 {
456 var idx = this._listeners.indexOf(listener); 493 var idx = this._listeners.indexOf(listener);
457
458 if (idx != -1) 494 if (idx != -1)
459 {
460 this._listeners.splice(idx, 1); 495 this._listeners.splice(idx, 1);
461 this._urlPatterns.splice(idx, 1);
462 }
463 } 496 }
464 }, 497 },
465 handlerBehaviorChanged: function() {} 498 handlerBehaviorChanged: function() {}
466 }; 499 };
467 500
468 501
469 /* Synchronous messaging */ 502 /* Synchronous messaging */
470 503
471 safari.application.addEventListener("message", function(event) 504 safari.application.addEventListener("message", function(event)
472 { 505 {
(...skipping 27 matching lines...) Expand all
500 })); 533 }));
501 }, 534 },
502 getLastFocused: function(callback) 535 getLastFocused: function(callback)
503 { 536 {
504 callback(new Window(safari.application.activeBrowserWindow)); 537 callback(new Window(safari.application.activeBrowserWindow));
505 } 538 }
506 }; 539 };
507 540
508 ext.tabs = { 541 ext.tabs = {
509 onLoading: new LoadingTabEventTarget(safari.application), 542 onLoading: new LoadingTabEventTarget(safari.application),
543 onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t rue),
510 onCompleted: new TabEventTarget(safari.application, "navigate", true), 544 onCompleted: new TabEventTarget(safari.application, "navigate", true),
511 onActivated: new TabEventTarget(safari.application, "activate", true), 545 onActivated: new TabEventTarget(safari.application, "activate", true),
512 onRemoved: new TabEventTarget(safari.application, "close", true) 546 onRemoved: new TabEventTarget(safari.application, "close", true)
513 }; 547 };
514 548
515 ext.backgroundPage = { 549 ext.backgroundPage = {
516 getWindow: function() 550 getWindow: function()
517 { 551 {
518 return safari.extension.globalPage.contentWindow; 552 return safari.extension.globalPage.contentWindow;
519 } 553 }
520 }; 554 };
521 555
522 ext.onMessage = new MessageEventTarget(safari.application); 556 ext.onMessage = new BackgroundMessageEventTarget();
523 557
524 // TODO: Implement context menu 558 // TODO: Implement context menu
525 ext.contextMenus = { 559 ext.contextMenus = {
526 create: function(title, contexts, onclick) {}, 560 create: function(title, contexts, onclick) {},
527 removeAll: function(callback) {} 561 removeAll: function(callback) {}
528 }; 562 };
529 })(); 563 })();
OLDNEW

Powered by Google App Engine
This is Rietveld