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.browserAction = new BrowserAction(this); | 84 this.browserAction = new BrowserAction(this); |
62 | 85 |
63 this.onLoading = new LoadingTabEventTarget(tab); | 86 this.onLoading = new LoadingTabEventTarget(tab); |
| 87 this.onBeforeNavigate = new TabEventTarget(tab, "beforeNavigate", false); |
64 this.onCompleted = new TabEventTarget(tab, "navigate", false); | 88 this.onCompleted = new TabEventTarget(tab, "navigate", false); |
65 this.onActivated = new TabEventTarget(tab, "activate", false); | 89 this.onActivated = new TabEventTarget(tab, "activate", false); |
66 this.onRemoved = new TabEventTarget(tab, "close", false); | 90 this.onRemoved = new TabEventTarget(tab, "close", false); |
67 }; | 91 }; |
68 Tab.prototype = { | 92 Tab.prototype = { |
69 get url() | 93 get url() |
70 { | 94 { |
71 return this._tab.url; | 95 return this._tab.url; |
72 }, | 96 }, |
73 close: function() | 97 close: function() |
74 { | 98 { |
75 this._tab.close(); | 99 this._tab.close(); |
76 }, | 100 }, |
77 activate: function() | 101 activate: function() |
78 { | 102 { |
79 this._tab.activate(); | 103 this._tab.activate(); |
80 }, | 104 }, |
81 sendMessage: sendMessage | 105 sendMessage: function(message, responseCallback) |
| 106 { |
| 107 _sendMessage( |
| 108 message, responseCallback, |
| 109 this._tab.page, this._tab |
| 110 ); |
| 111 } |
82 }; | 112 }; |
83 | 113 |
84 TabMap = function() | 114 TabMap = function(deleteTabOnBeforeNavigate) |
85 { | 115 { |
86 this._tabs = []; | 116 this._tabs = []; |
87 this._values = []; | 117 this._values = []; |
88 | 118 |
89 this._onClosed = this._onClosed.bind(this); | 119 this._deleteOnEvent = this._deleteOnEvent.bind(this); |
| 120 this._deleteTabOnBeforeNavigate = deleteTabOnBeforeNavigate; |
90 }; | 121 }; |
91 TabMap.prototype = | 122 TabMap.prototype = |
92 { | 123 { |
93 get: function(tab) { | 124 get: function(tab) { |
94 var idx; | 125 var idx; |
95 | 126 |
96 if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1) | 127 if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1) |
97 return null; | 128 return null; |
98 | 129 |
99 return this._values[idx]; | 130 return this._values[idx]; |
100 }, | 131 }, |
101 set: function(tab, value) | 132 set: function(tab, value) |
102 { | 133 { |
103 var idx = this._tabs.indexOf(tab._tab); | 134 var idx = this._tabs.indexOf(tab._tab); |
104 | 135 |
105 if (idx != -1) | 136 if (idx != -1) |
106 this._values[idx] = value; | 137 this._values[idx] = value; |
107 else | 138 else |
108 { | 139 { |
109 this._tabs.push(tab._tab); | 140 this._tabs.push(tab._tab); |
110 this._values.push(value); | 141 this._values.push(value); |
111 | 142 |
112 tab._tab.addEventListener("close", this._onClosed, false); | 143 tab._tab.addEventListener("close", this._deleteOnEvent, false); |
| 144 if (this._deleteTabOnBeforeNavigate) |
| 145 tab._tab.addEventListener("beforeNavigate", this._deleteOnEvent, false
); |
113 } | 146 } |
114 }, | 147 }, |
115 has: function(tab) | 148 has: function(tab) |
116 { | 149 { |
117 return this._tabs.indexOf(tab._tab) != -1; | 150 return this._tabs.indexOf(tab._tab) != -1; |
118 }, | 151 }, |
119 clear: function() | 152 clear: function() |
120 { | 153 { |
121 while (this._tabs.length > 0) | 154 while (this._tabs.length > 0) |
122 this._delete(this._tabs[0]); | 155 this._delete(this._tabs[0]); |
123 }, | 156 }, |
124 _delete: function(tab) | 157 _delete: function(tab) |
125 { | 158 { |
126 var idx = this._tabs.indexOf(tab); | 159 var idx = this._tabs.indexOf(tab); |
127 | 160 |
128 if (idx != -1) | 161 if (idx != -1) |
129 { | 162 { |
130 this._tabs.splice(idx, 1); | 163 this._tabs.splice(idx, 1); |
131 this._values.splice(idx, 1); | 164 this._values.splice(idx, 1); |
132 | 165 |
133 tab.removeEventListener("close", this._onClosed, false); | 166 tab.removeEventListener("close", this._deleteOnEvent, false); |
| 167 tab.removeEventListener("beforeNavigate", this._deleteOnEvent, false); |
134 } | 168 } |
135 }, | 169 }, |
136 _onClosed: function(event) | 170 _deleteOnEvent: function(event) |
137 { | 171 { |
138 this._delete(event.target); | 172 // delay so that other event handlers can still look this tab up |
| 173 setTimeout(this._delete.bind(this, event.target), 0); |
139 } | 174 } |
140 }; | 175 }; |
141 TabMap.prototype["delete"] = function(tab) | 176 TabMap.prototype["delete"] = function(tab) |
142 { | 177 { |
143 this._delete(tab._tab); | 178 this._delete(tab._tab); |
144 }; | 179 }; |
145 | 180 |
146 ext.tabs = { | 181 ext.tabs = { |
147 onLoading: new LoadingTabEventTarget(safari.application), | 182 onLoading: new LoadingTabEventTarget(safari.application), |
| 183 onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t
rue), |
148 onCompleted: new TabEventTarget(safari.application, "navigate", true), | 184 onCompleted: new TabEventTarget(safari.application, "navigate", true), |
149 onActivated: new TabEventTarget(safari.application, "activate", true), | 185 onActivated: new TabEventTarget(safari.application, "activate", true), |
150 onRemoved: new TabEventTarget(safari.application, "close", true) | 186 onRemoved: new TabEventTarget(safari.application, "close", true) |
151 }; | 187 }; |
152 | 188 |
153 | 189 |
154 /* Browser actions */ | 190 /* Browser actions */ |
155 | 191 |
156 var toolbarItemProperties = {}; | 192 var toolbarItemProperties = {}; |
157 | 193 |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 { | 297 { |
262 var tab = this._win.openTab(); | 298 var tab = this._win.openTab(); |
263 tab.url = url; | 299 tab.url = url; |
264 | 300 |
265 if (callback) | 301 if (callback) |
266 callback(new Tab(tab)); | 302 callback(new Tab(tab)); |
267 } | 303 } |
268 }; | 304 }; |
269 | 305 |
270 | 306 |
| 307 /* Frames */ |
| 308 |
| 309 Frame = function(url, isTopLevel, tab) |
| 310 { |
| 311 this.url = url; |
| 312 |
| 313 // there is no way to discover frames with Safari's API. |
| 314 // so if this isn't the top level frame, assume that the parent is. |
| 315 // this is the best we can do for Safari. :( |
| 316 if (!isTopLevel) |
| 317 this.parent = new Frame(tab.url, true); |
| 318 else |
| 319 this.parent = null; |
| 320 }; |
| 321 |
| 322 |
271 /* Background page proxy */ | 323 /* Background page proxy */ |
272 | 324 |
273 var proxy = { | 325 var proxy = { |
274 tabs: [], | 326 tabs: [], |
275 objects: [], | 327 objects: [], |
276 | 328 |
277 registerObject: function(obj, objects) | 329 registerObject: function(obj, objects) |
278 { | 330 { |
279 var objectId = objects.indexOf(obj); | 331 var objectId = objects.indexOf(obj); |
280 | 332 |
(...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
491 } | 543 } |
492 } | 544 } |
493 }; | 545 }; |
494 | 546 |
495 | 547 |
496 /* Web request blocking */ | 548 /* Web request blocking */ |
497 | 549 |
498 ext.webRequest = { | 550 ext.webRequest = { |
499 onBeforeRequest: { | 551 onBeforeRequest: { |
500 _listeners: [], | 552 _listeners: [], |
501 _urlPatterns: [], | |
502 | 553 |
503 _handleMessage: function(message, tab) | 554 _handleMessage: function(message, rawTab) |
504 { | 555 { |
505 tab = new Tab(tab); | 556 var tab = new Tab(rawTab); |
| 557 var frame = new Frame(message.documentUrl, message.isTopLevel, rawTab); |
506 | 558 |
507 for (var i = 0; i < this._listeners.length; i++) | 559 for (var i = 0; i < this._listeners.length; i++) |
508 { | 560 { |
509 var regex = this._urlPatterns[i]; | 561 if (this._listeners[i](message.url, message.type, tab, frame) === fals
e) |
510 | |
511 if ((!regex || regex.test(message.url)) && this._listeners[i](message.
url, message.type, tab, 0, -1) === false) | |
512 return false; | 562 return false; |
513 } | 563 } |
514 | 564 |
515 return true; | 565 return true; |
516 }, | 566 }, |
517 addListener: function(listener, urls) | 567 addListener: function(listener) |
518 { | 568 { |
519 var regex; | |
520 | |
521 if (urls) | |
522 regex = new RegExp("^(?:" + urls.map(function(url) | |
523 { | |
524 return url.split("*").map(function(s) | |
525 { | |
526 return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1"); | |
527 }).join(".*"); | |
528 }).join("|") + ")($|[?#])"); | |
529 | |
530 this._listeners.push(listener); | 569 this._listeners.push(listener); |
531 this._urlPatterns.push(regex); | |
532 }, | 570 }, |
533 removeListener: function(listener) | 571 removeListener: function(listener) |
534 { | 572 { |
535 var idx = this._listeners.indexOf(listener); | 573 var idx = this._listeners.indexOf(listener); |
536 | |
537 if (idx != -1) | 574 if (idx != -1) |
538 { | |
539 this._listeners.splice(idx, 1); | 575 this._listeners.splice(idx, 1); |
540 this._urlPatterns.splice(idx, 1); | |
541 } | |
542 } | 576 } |
543 }, | 577 }, |
544 handlerBehaviorChanged: function() {} | 578 handlerBehaviorChanged: function() {} |
545 }; | 579 }; |
546 | 580 |
547 | 581 |
548 /* Synchronous messaging */ | 582 /* Synchronous messaging */ |
549 | 583 |
550 safari.application.addEventListener("message", function(event) | 584 safari.application.addEventListener("message", function(event) |
551 { | 585 { |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
584 } | 618 } |
585 }; | 619 }; |
586 | 620 |
587 ext.backgroundPage = { | 621 ext.backgroundPage = { |
588 getWindow: function() | 622 getWindow: function() |
589 { | 623 { |
590 return safari.extension.globalPage.contentWindow; | 624 return safari.extension.globalPage.contentWindow; |
591 } | 625 } |
592 }; | 626 }; |
593 | 627 |
594 ext.onMessage = new MessageEventTarget(safari.application); | 628 ext.onMessage = new BackgroundMessageEventTarget(); |
595 | 629 |
596 var contextMenuItems = []; | 630 var contextMenuItems = []; |
597 var isContextMenuHidden = true; | 631 var isContextMenuHidden = true; |
598 ext.contextMenus = { | 632 ext.contextMenus = { |
599 addMenuItem: function(title, contexts, onclick) | 633 addMenuItem: function(title, contexts, onclick) |
600 { | 634 { |
601 contextMenuItems.push({ | 635 contextMenuItems.push({ |
602 id: String(contextMenuItems.length), | 636 id: String(contextMenuItems.length), |
603 title: title, | 637 title: title, |
604 item: null, | 638 item: null, |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
651 for (var i = 0; i < contextMenuItems.length; i++) | 685 for (var i = 0; i < contextMenuItems.length; i++) |
652 { | 686 { |
653 if (contextMenuItems[i].id == event.command) | 687 if (contextMenuItems[i].id == event.command) |
654 { | 688 { |
655 contextMenuItems[i].onclick(event.userInfo.srcUrl, new Tab(safari.applic
ation.activeBrowserWindow.activeTab)); | 689 contextMenuItems[i].onclick(event.userInfo.srcUrl, new Tab(safari.applic
ation.activeBrowserWindow.activeTab)); |
656 break; | 690 break; |
657 } | 691 } |
658 } | 692 } |
659 }, false); | 693 }, false); |
660 })(); | 694 })(); |
OLD | NEW |