Left: | ||
Right: |
OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 })(); |
OLD | NEW |