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

Side by Side Diff: safari/background.js

Issue 16067002: Added Safari Support (Closed)
Patch Set: Fixed js errors in popup.html. Created Nov. 11, 2013, 5 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
(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);
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) {
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) { return new Tab(tab); }));
151 },
152 getActiveTab: function(callback)
153 {
154 callback(new Tab(this._win.activeTab));
155 },
156 openTab: function(url, callback)
157 {
158 var tab = this._win.openTab();
159 tab.url = url;
160
161 if (callback)
162 callback(new Tab(tab));
163 }
164 };
165
166 if (safari.extension.globalPage.contentWindow == window)
167 {
168 /* Background page proxy */
169
170 var proxy = {
171 tabs: [],
172 objects: [],
173
174 registerObject: function(obj, objects)
175 {
176 var objectId = objects.indexOf(obj);
177
178 if (objectId == -1)
179 objectId = objects.push(obj) - 1;
180
181 return objectId;
182 },
183 serializeSequence: function(sequence, objects, memo)
184 {
185 if (!memo)
186 memo = {specs: [], arrays: []};
187
188 var items = [];
189 for (var i = 0; i < sequence.length; i++)
190 items.push(this.serialize(sequence[i], objects, memo));
191
192 return items;
193 },
194 serialize: function(obj, objects, memo)
195 {
196 if (typeof obj == "object" && obj != null || typeof obj == "function")
197 {
198 if (obj.constructor == Array)
199 {
200 if (!memo)
201 memo = {specs: [], arrays: []};
202
203 var idx = memo.arrays.indexOf(obj);
204 if (idx != -1)
205 return memo.specs[idx];
206
207 var spec = {type: "array"};
208 memo.specs.push(spec);
209 memo.arrays.push(obj);
210
211 spec.items = this.serializeSequence(obj, objects, memo);
212 return spec;
213 }
214
215 if (obj.constructor != Date && obj.constructor != RegExp)
216 return {type: "object", objectId: this.registerObject(obj, objects)} ;
217 }
218
219 return {type: "value", value: obj};
220 },
221 createCallback: function(callbackId, tab)
222 {
223 var proxy = this;
224
225 return function()
226 {
227 var idx = proxy.tabs.indexOf(tab);
228
229 if (idx != -1) {
230 var objects = proxy.objects[idx];
231
232 tab.page.dispatchMessage("proxyCallback",
233 {
234 callbackId: callbackId,
235 contextId: proxy.registerObject(this, objects),
236 args: proxy.serializeSequence(arguments, objects)
237 });
238 }
239 };
240 },
241 deserialize: function(spec, objects, tab, memo)
242 {
243 switch (spec.type)
244 {
245 case "value":
246 return spec.value;
247 case "hosted":
248 return objects[spec.objectId];
249 case "callback":
250 return this.createCallback(spec.callbackId, tab);
251 case "object":
252 case "array":
253 if (!memo)
254 memo = {specs: [], objects: []};
255
256 var idx = memo.specs.indexOf(spec);
257 if (idx != -1)
258 return memo.objects[idx];
259
260 var obj;
261 if (spec.type == "array")
262 obj = [];
263 else
264 obj = {};
265
266 memo.specs.push(spec);
267 memo.objects.push(obj);
268
269 if (spec.type == "array")
270 for (var i = 0; i < spec.items.length; i++)
271 obj.push(this.deserialize(spec.items[i], objects, tab, memo));
272 else
273 for (var k in spec.properties)
274 obj[k] = this.deserialize(spec.properties[k], objects, tab, memo );
275
276 return obj;
277 }
278 },
279 createObjectCache: function(tab)
280 {
281 var objects = [window];
282
283 this.tabs.push(tab);
284 this.objects.push(objects);
285
286 tab.addEventListener("close", function()
287 {
288 var idx = this.tabs.indexOf(tab);
289
290 if (idx != -1)
291 {
292 this.tabs.splice(idx, 1);
293 this.objects.splice(idx, 1);
294 }
295 }.bind(this));
296
297 return objects;
298 },
299 getObjectCache: function(tab)
300 {
301 var idx = this.tabs.indexOf(tab);
302 var objects;
303
304 if (idx != -1)
305 objects = this.objects[idx];
306 else
307 objects = this.objects[idx] = this.createObjectCache(tab);
308
309 return objects;
310 },
311 fail: function(error)
312 {
313 if (error instanceof Error)
314 error = error.message;
315 return {succeed: false, error: error};
316 },
317 _handleMessage: function(message, tab)
318 {
319 var objects = this.getObjectCache(tab);
320
321 switch (message.type)
322 {
323 case "getProperty":
324 var obj = objects[message.objectId];
325
326 try
327 {
328 var value = obj[message.property];
329 }
330 catch (e)
331 {
332 return this.fail(e);
333 }
334
335 return {succeed: true, result: this.serialize(value, objects)};
336 case "setProperty":
337 var obj = objects[message.objectId];
338 var value = this.deserialize(message.value, objects, tab);
339
340 try
341 {
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
359 {
360 var result = func.apply(context, args);
361 }
362 catch (e)
363 {
364 return this.fail(e);
365 }
366
367 return {succeed: true, result: this.serialize(result, objects)};
368 case "inspectObject":
369 var obj = objects[message.objectId];
370 var objectInfo = {properties: {}, isFunction: typeof obj == "functio n"};
371
372 Object.getOwnPropertyNames(obj).forEach(function(prop)
373 {
374 objectInfo.properties[prop] = {
375 enumerable: Object.prototype.propertyIsEnumerable.call(obj, prop )
376 };
377 });
378
379 if (obj.__proto__)
380 objectInfo.prototypeId = this.registerObject(obj.__proto__, object s);
381
382 if (obj == Object.prototype)
383 objectInfo.prototypeOf = "Object";
384 if (obj == Function.prototype)
385 objectInfo.prototypeOf = "Function";
386
387 return objectInfo;
388 }
389 }
390 };
391
392
393 /* Web request blocking */
394
395 ext.webRequest = {
396 onBeforeRequest: {
397 _listeners: [],
398 _urlPatterns: [],
399
400 _handleMessage: function(message, tab)
401 {
402 tab = new Tab(tab);
403
404 for (var i = 0; i < this._listeners.length; i++)
405 {
406 var regex = this._urlPatterns[i];
407
408 if (!regex || regex.test(message))
409 if (this._listeners[i](message.url, message.type, tab, 0, -1) === fa lse)
410 return false;
411 }
412
413 return true;
414 },
415 addListener: function(listener, urls)
416 {
417 var regex;
418
419 if (urls)
420 regex = new RegExp("^(?:" + urls.map(function(url)
421 {
422 return url.split("*").map(function(s)
423 {
424 return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1");
425 }).join(".*");
426 }).join("|") + ")($|[?#])");
427
428 this._listeners.push(listener);
429 this._urlPatterns.push(regex);
430 },
431 removeListener: function(listener)
432 {
433 var idx = this._listeners.indexOf(listener);
434
435 if (idx != -1)
436 {
437 this._listeners.splice(idx, 1);
438 this._urlPatterns.splice(idx, 1);
439 }
440 }
441 },
442 handlerBehaviorChanged: function() {}
443 };
444
445
446 /* Synchronous messaging */
447
448 safari.application.addEventListener("message", function(event)
449 {
450 if (event.name == "canLoad")
451 {
452 var handler;
453
454 switch (event.message.type)
455 {
456 case "proxy":
457 handler = proxy;
458 break;
459 case "webRequest":
460 handler = ext.webRequest.onBeforeRequest;
461 break;
462 }
463
464 event.message = handler._handleMessage(event.message.payload, event.targ et);
465 }
466 }, true);
467 }
468
469
470 /* API */
471
472 ext.windows = {
473 getAll: function(callback)
474 {
475 callback(safari.application.browserWindows.map(function(win)
476 {
477 return new Window(win);
478 }));
479 },
480 getLastFocused: function(callback)
481 {
482 callback(new Window(safari.application.activeBrowserWindow));
483 }
484 };
485
486 ext.tabs = {
487 onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t rue),
488 onCompleted: new TabEventTarget(safari.application, "navigate", true),
489 onActivated: new TabEventTarget(safari.application, "activate", true),
490 onRemoved: new TabEventTarget(safari.application, "close", true)
491 };
492
493 ext.backgroundPage = {
494 getWindow: function()
495 {
496 return safari.extension.globalPage.contentWindow;
497 }
498 };
499
500 ext.onMessage = new MessageEventTarget(safari.application);
501 })();
OLDNEW
« no previous file with comments | « popupBlocker.js ('k') | safari/common.js » ('j') | safari/common.js » ('J')

Powered by Google App Engine
This is Rietveld