Left: | ||
Right: |
OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * This file is part of Adblock Plus <http://adblockplus.org/>, | |
3 * Copyright (C) 2006-2013 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 /* Tabs */ | |
21 | |
22 var TabEventTarget = function() | |
23 { | |
24 WrappedEventTarget.apply(this, arguments); | |
25 }; | |
26 TabEventTarget.prototype = { | |
27 __proto__: WrappedEventTarget.prototype, | |
28 _wrapListener: function(listener) | |
29 { | |
30 return function(event) | |
31 { | |
32 listener(new Tab(event.target)); | |
33 }; | |
34 } | |
35 }; | |
36 | |
37 Tab = function(tab) | |
38 { | |
39 this._tab = tab; | |
40 | |
41 this._eventTarget = tab; | |
42 this._messageDispatcher = tab.page; | |
43 | |
44 this.url = tab.url; | |
45 | |
46 this.onBeforeNavigate = new TabEventTarget(tab, "beforeNavigate", false); | |
47 this.onCompleted = new TabEventTarget(tab, "navigate", false); | |
Felix Dahlke
2013/11/10 01:07:00
We don't typically align things on column.
| |
48 this.onActivated = new TabEventTarget(tab, "activate", false); | |
49 this.onRemoved = new TabEventTarget(tab, "close", false); | |
50 }; | |
51 Tab.prototype = { | |
52 close: function() | |
53 { | |
54 this._tab.close(); | |
55 }, | |
56 activate: function() | |
57 { | |
58 this._tab.activate(); | |
59 }, | |
60 sendMessage: sendMessage, | |
61 pageAction: { | |
62 // there are no page actions in safari, so we use toolbar items instead | |
63 setIcon: function(path) | |
64 { | |
65 safari.extension.toolbarItems[0].image = safari.extension.baseURI + path ; | |
66 }, | |
67 setTitle: function(title) | |
68 { | |
69 safari.extension.toolbarItems[0].toolTip = title; | |
70 }, | |
71 | |
72 // toolbar items in safari can"t get hidden | |
73 hide: function() {}, | |
74 show: function() {} | |
75 } | |
76 }; | |
77 | |
78 TabMap = function() | |
79 { | |
80 this._tabs = []; | |
81 this._values = []; | |
82 | |
83 this._onClosed = this._onClosed.bind(this); | |
84 }; | |
85 TabMap.prototype = | |
86 { | |
87 get: function(tab) { | |
88 var idx; | |
89 | |
90 if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1) | |
91 return null; | |
92 | |
93 return this._values[idx]; | |
94 }, | |
95 set: function(tab, value) | |
96 { | |
97 var idx = this._tabs.indexOf(tab._tab); | |
98 | |
99 if (idx != -1) | |
100 this._values[idx] = value; | |
101 else | |
102 { | |
103 this._tabs.push(tab._tab); | |
104 this._values.push(value); | |
105 | |
106 tab._tab.addEventListener("close", this._onClosed, false); | |
107 } | |
108 }, | |
109 has: function(tab) | |
110 { | |
111 return this._tabs.indexOf(tab._tab) != -1; | |
112 }, | |
113 clear: function() | |
114 { | |
115 while (this._tabs.length > 0) | |
116 this._delete(this._tabs[0]); | |
117 }, | |
118 _delete: function(tab) | |
119 { | |
120 var idx = this._tabs.indexOf(tab); | |
121 | |
122 if (idx != -1) | |
123 { | |
124 this._tabs.splice(idx, 1); | |
125 this._values.splice(idx, 1); | |
126 | |
127 tab.removeEventListener("close", this._onClosed, false); | |
128 } | |
129 }, | |
130 _onClosed: function(event) | |
131 { | |
132 this._delete(event.target); | |
133 } | |
134 }; | |
135 TabMap.prototype["delete"] = function(tab) { | |
Felix Dahlke
2013/11/10 01:07:00
Opening brace should go on its own line.
Felix Dahlke
2013/11/12 09:50:24
This hasn't been addressed.
| |
136 this._delete(tab._tab); | |
137 }; | |
138 | |
139 | |
140 /* Windows */ | |
141 | |
142 Window = function(win) | |
143 { | |
144 this._win = win; | |
145 this.visible = win.visible; | |
146 } | |
147 Window.prototype = { | |
148 getAllTabs: function(callback) | |
149 { | |
150 callback(this._win.tabs.map(function(tab) { | |
Felix Dahlke
2013/11/10 01:07:00
Opening brace should go on its own line.
| |
151 return new Tab(tab); | |
152 })); | |
153 }, | |
154 getActiveTab: function(callback) | |
155 { | |
156 callback(new Tab(this._win.activeTab)); | |
157 }, | |
158 openTab: function(url, callback) | |
159 { | |
160 var tab = this._win.openTab(); | |
161 tab.url = url; | |
162 | |
163 if (callback) | |
164 callback(new Tab(tab)); | |
165 } | |
166 }; | |
167 | |
168 if (safari.extension.globalPage.contentWindow == window) | |
169 { | |
170 /* Background page proxy */ | |
171 | |
172 var proxy = { | |
173 tabs: [], | |
174 objects: [], | |
175 | |
176 registerObject: function(obj, objects) | |
177 { | |
178 var objectId = objects.indexOf(obj); | |
179 | |
180 if (objectId == -1) | |
181 objectId = objects.push(obj) - 1; | |
182 | |
183 return objectId; | |
184 }, | |
185 serializeSequence: function(sequence, objects, memo) | |
186 { | |
187 if (!memo) | |
188 memo = {specs: [], arrays: []}; | |
189 | |
190 var items = []; | |
191 for (var i = 0; i < sequence.length; i++) | |
192 items.push(this.serialize(sequence[i], objects, memo)); | |
193 | |
194 return items; | |
195 }, | |
196 serialize: function(obj, objects, memo) | |
197 { | |
198 if (typeof obj == "object" && obj != null || typeof obj == "function") | |
199 { | |
200 if (obj.constructor == Array) | |
201 { | |
202 if (!memo) | |
203 memo = {specs: [], arrays: []}; | |
204 | |
205 var idx = memo.arrays.indexOf(obj); | |
206 if (idx != -1) | |
207 return memo.specs[idx]; | |
208 | |
209 var spec = {type: "array"}; | |
210 memo.specs.push(spec); | |
211 memo.arrays.push(obj); | |
212 | |
213 spec.items = this.serializeSequence(obj, objects, memo); | |
214 return spec; | |
215 } | |
216 | |
217 if (obj.constructor != Date) | |
218 if (obj.constructor != RegExp) | |
Felix Dahlke
2013/11/10 01:07:00
How about && instead of nested ifs?
| |
219 return {type: "object", objectId: this.registerObject(obj, objects)} ; | |
220 } | |
221 | |
222 return {type: "value", value: obj}; | |
223 }, | |
224 createCallback: function(callbackId, tab) | |
225 { | |
226 var proxy = this; | |
227 | |
228 return function() | |
229 { | |
230 var idx = proxy.tabs.indexOf(tab); | |
231 | |
232 if (idx != -1) { | |
Felix Dahlke
2013/11/10 01:07:00
Opening brace should go on its own line.
| |
233 var objects = proxy.objects[idx]; | |
234 | |
235 tab.page.dispatchMessage("proxyCallback", { | |
Felix Dahlke
2013/11/10 01:07:00
Opening brace should go on its own line.
| |
236 callbackId: callbackId, | |
237 contextId: proxy.registerObject(this, objects), | |
238 args: proxy.serializeSequence(arguments, objects) | |
239 }); | |
240 } | |
241 }; | |
242 }, | |
243 deserialize: function(spec, objects, tab, memo) | |
244 { | |
245 switch (spec.type) | |
246 { | |
247 case "value": | |
248 return spec.value; | |
249 case "hosted": | |
250 return objects[spec.objectId]; | |
251 case "callback": | |
252 return this.createCallback(spec.callbackId, tab); | |
253 case "object": | |
254 case "array": | |
255 if (!memo) | |
256 memo = {specs: [], objects: []}; | |
257 | |
258 var idx = memo.specs.indexOf(spec); | |
259 if (idx != -1) | |
260 return memo.objects[idx]; | |
261 | |
262 var obj; | |
263 if (spec.type == "array") | |
264 obj = []; | |
265 else | |
266 obj = {}; | |
267 | |
268 memo.specs.push(spec); | |
269 memo.objects.push(obj); | |
270 | |
271 if (spec.type == "array") | |
272 for (var i = 0; i < spec.items.length; i++) | |
273 obj.push(this.deserialize(spec.items[i], objects, tab, memo)); | |
274 else | |
275 for (var k in spec.properties) | |
276 obj[k] = this.deserialize(spec.properties[k], objects, tab, memo ); | |
277 | |
278 return obj; | |
279 } | |
280 }, | |
281 createObjectCache: function(tab) | |
282 { | |
283 var objects = [window]; | |
284 | |
285 this.tabs.push(tab); | |
286 this.objects.push(objects); | |
287 | |
288 tab.addEventListener("close", function() | |
289 { | |
290 var idx = this.tabs.indexOf(tab); | |
291 | |
292 if (idx != -1) | |
293 { | |
294 this.tabs.splice(idx, 1); | |
295 this.objects.splice(idx, 1); | |
296 } | |
297 }.bind(this)); | |
298 | |
299 return objects; | |
300 }, | |
301 getObjectCache: function(tab) | |
302 { | |
303 var idx = this.tabs.indexOf(tab); | |
304 var objects; | |
305 | |
306 if (idx != -1) | |
307 objects = this.objects[idx]; | |
308 else | |
309 objects = this.objects[idx] = this.createObjectCache(tab); | |
310 | |
311 return objects; | |
312 }, | |
313 fail: function(error) | |
314 { | |
315 if (error instanceof Error) | |
316 error = error.message; | |
317 return {succeed: false, error: error}; | |
318 }, | |
319 _handleMessage: function(message, tab) | |
320 { | |
321 var objects = this.getObjectCache(tab); | |
322 | |
323 switch (message.type) | |
324 { | |
325 case "getProperty": | |
326 var obj = objects[message.objectId]; | |
327 | |
328 try { | |
Felix Dahlke
2013/11/10 01:07:00
Opening brace should go on its own line.
| |
329 var value = obj[message.property]; | |
330 } | |
331 catch (e) | |
332 { | |
333 return this.fail(e); | |
334 } | |
335 | |
336 return {succeed: true, result: this.serialize(value, objects)}; | |
337 case "setProperty": | |
338 var obj = objects[message.objectId]; | |
339 var value = this.deserialize(message.value, objects, tab); | |
340 | |
341 try { | |
Felix Dahlke
2013/11/10 01:07:00
Opening brace should go on its own line.
| |
342 obj[message.property] = value; | |
343 } | |
344 catch (e) | |
345 { | |
346 return this.fail(e); | |
347 } | |
348 | |
349 return {succeed: true}; | |
350 case "callFunction": | |
351 var func = objects[message.functionId]; | |
352 var context = objects[message.contextId]; | |
353 | |
354 var args = []; | |
355 for (var i = 0; i < message.args.length; i++) | |
356 args.push(this.deserialize(message.args[i], objects, tab)); | |
357 | |
358 try { | |
Felix Dahlke
2013/11/10 01:07:00
Opening brace should go on its own line.
| |
359 var result = func.apply(context, args); | |
360 } | |
361 catch (e) | |
362 { | |
363 return this.fail(e); | |
364 } | |
365 | |
366 return {succeed: true, result: this.serialize(result, objects)}; | |
367 case "inspectObject": | |
368 var obj = objects[message.objectId]; | |
369 var objectInfo = {properties: {}, isFunction: typeof obj == "functio n"}; | |
370 | |
371 Object.getOwnPropertyNames(obj).forEach(function(prop) | |
372 { | |
373 objectInfo.properties[prop] = { | |
374 enumerable: Object.prototype.propertyIsEnumerable.call(obj, prop ) | |
375 }; | |
376 }); | |
377 | |
378 if (obj.__proto__) | |
379 objectInfo.prototypeId = this.registerObject(obj.__proto__, object s); | |
380 | |
381 if (obj == Object.prototype) | |
382 objectInfo.prototypeOf = "Object"; | |
383 if (obj == Function.prototype) | |
384 objectInfo.prototypeOf = "Function"; | |
385 | |
386 return objectInfo; | |
387 } | |
388 } | |
389 }; | |
390 | |
391 | |
392 /* Web request blocking */ | |
393 | |
394 ext.webRequest = { | |
395 onBeforeRequest: { | |
396 _listeners: [], | |
397 _urlPatterns: [], | |
398 | |
399 _handleMessage: function(message, tab) | |
400 { | |
401 tab = new Tab(tab); | |
402 | |
403 for (var i = 0; i < this._listeners.length; i++) | |
404 { | |
405 var regex = this._urlPatterns[i]; | |
406 | |
407 if (!regex || regex.test(message)) | |
408 if (this._listeners[i](message.url, message.type, tab, 0, -1) === fa lse) | |
Felix Dahlke
2013/11/10 01:07:00
As above, I'd prefer a logical operator to nested
Sebastian Noack
2013/11/10 14:40:09
Think of it as a more readable alternative way of
Felix Dahlke
2013/11/10 14:48:32
I think it's pretty confusing, why not just wrap t
Wladimir Palant
2013/11/12 10:37:02
I agree with Felix. Please use && and insert a lin
| |
409 return false; | |
410 } | |
411 | |
412 return true; | |
413 }, | |
414 addListener: function(listener, urls) | |
415 { | |
416 var regex; | |
417 | |
418 if (urls) | |
419 regex = new RegExp("^(?:" + urls.map(function(url) | |
420 { | |
421 return url.split("*").map(function(s) | |
422 { | |
423 return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1"); | |
424 }).join(".*"); | |
425 }).join("|") + ")($|[?#])"); | |
426 | |
427 this._listeners.push(listener); | |
428 this._urlPatterns.push(regex); | |
429 }, | |
430 removeListener: function(listener) | |
431 { | |
432 var idx = this._listeners.indexOf(listener); | |
433 | |
434 if (idx != -1) | |
435 { | |
436 this._listeners.splice(idx, 1); | |
437 this._urlPatterns.splice(idx, 1); | |
438 } | |
439 } | |
440 }, | |
441 handlerBehaviorChanged: function() {} | |
442 }; | |
443 | |
444 | |
445 /* Synchronous messaging */ | |
446 | |
447 safari.application.addEventListener("message", function(event) | |
448 { | |
449 if (event.name == "canLoad") | |
450 { | |
451 var handler; | |
452 | |
453 switch (event.message.type) | |
454 { | |
455 case "proxy": | |
456 handler = proxy; | |
457 break; | |
458 case "webRequest": | |
459 handler = ext.webRequest.onBeforeRequest; | |
460 break; | |
461 } | |
462 | |
463 event.message = handler._handleMessage(event.message.payload, event.targ et); | |
464 } | |
465 }, true); | |
466 } | |
467 | |
468 | |
469 /* API */ | |
470 | |
471 ext.windows = { | |
472 getAll: function(callback) | |
473 { | |
474 callback(safari.application.browserWindows.map(function(win) | |
475 { | |
476 return new Window(win); | |
477 })); | |
478 }, | |
479 getLastFocused: function(callback) | |
480 { | |
481 callback(new Window(safari.application.activeBrowserWindow)); | |
482 } | |
483 }; | |
484 | |
485 ext.tabs = { | |
486 onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t rue), | |
487 onCompleted: new TabEventTarget(safari.application, "navigate", t rue), | |
Felix Dahlke
2013/11/10 01:07:00
We typically don't align things on column.
| |
488 onActivated: new TabEventTarget(safari.application, "activate", t rue), | |
489 onRemoved: new TabEventTarget(safari.application, "close", t rue) | |
490 }; | |
491 | |
492 ext.backgroundPage = { | |
493 getWindow: function() | |
494 { | |
495 return safari.extension.globalPage.contentWindow; | |
496 } | |
497 }; | |
498 | |
499 ext.onMessage = new MessageEventTarget(safari.application); | |
500 })(); | |
OLD | NEW |