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: Rebased on upstream and addressed comments Created Oct. 25, 2013, 4:13 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
« no previous file with comments | « popupBlocker.js ('k') | safari/common.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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) {
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 == "function")
199 return {type: "function", objectId: this.registerObject(obj, objects)} ;
200
201 if (typeof obj == "object" && obj != null)
202 {
203 if (obj.constructor == Array)
204 {
205 if (!memo)
206 memo = {specs: [], arrays: []};
207
208 var idx = memo.arrays.indexOf(obj);
209 if (idx != -1)
210 return memo.specs[idx];
211
212 var spec = {type: "array"};
213 memo.specs.push(spec);
214 memo.arrays.push(obj);
215
216 spec.items = this.serializeSequence(obj, objects, memo);
217 return spec;
218 }
219
220 if (obj.constructor != Date)
221 if (obj.constructor != RegExp)
222 return {type: "object", objectId: this.registerObject(obj, objects)} ;
223 }
224
225 return {type: "value", value: obj};
226 },
227 createCallback: function(callbackId, tab)
228 {
229 var proxy = this;
230
231 return function()
232 {
233 var idx = proxy.tabs.indexOf(tab);
234
235 if (idx != -1) {
236 var objects = proxy.objects[idx];
237
238 tab.page.dispatchMessage("proxyCallback", {
239 callbackId: callbackId,
240 contextId: proxy.registerObject(this, objects),
241 args: proxy.serializeSequence(arguments, objects)
242 });
243 }
244 };
245 },
246 deserialize: function(spec, objects, tab, memo)
247 {
248 switch (spec.type)
249 {
250 case "value":
251 return spec.value;
252 case "hosted":
253 return objects[spec.objectId];
254 case "callback":
255 return this.createCallback(spec.callbackId, tab);
256 case "object":
257 case "array":
258 if (!memo)
259 memo = {specs: [], objects: []};
260
261 var idx = memo.specs.indexOf(spec);
262 if (idx != -1)
263 return memo.objects[idx];
264
265 var obj;
266 if (spec.type == "array")
267 obj = [];
268 else
269 obj = {};
270
271 memo.specs.push(spec);
272 memo.objects.push(obj);
273
274 if (spec.type == "array")
275 for (var i = 0; i < spec.items.length; i++)
276 obj.push(this.deserialize(spec.items[i], objects, tab, memo));
277 else
278 for (var k in spec.properties)
279 obj[k] = this.deserialize(spec.properties[k], objects, tab, memo );
280
281 return obj;
282 }
283 },
284 createObjectCache: function(tab)
285 {
286 var objects = [window];
287
288 this.tabs.push(tab);
289 this.objects.push(objects);
290
291 tab.addEventListener("close", function()
292 {
293 var idx = this.tabs.indexOf(tab);
294
295 if (idx != -1)
296 {
297 this.tabs.splice(idx, 1);
298 this.objects.splice(idx, 1);
299 }
300 }.bind(this));
301
302 return objects;
303 },
304 getObjectCache: function(tab)
305 {
306 var idx = this.tabs.indexOf(tab);
307 var objects;
308
309 if (idx != -1)
310 objects = this.objects[idx];
311 else
312 objects = this.objects[idx] = this.createObjectCache(tab);
313
314 return objects;
315 },
316 fail: function(error)
317 {
318 if (error instanceof Error)
319 error = error.message;
320 return {succeed: false, error: error};
321 },
322 _handleMessage: function(message, tab)
323 {
324 var objects = this.getObjectCache(tab);
325
326 switch (message.type)
327 {
328 case "getProperty":
329 var obj = objects[message.objectId];
330
331 try {
332 var value = obj[message.property];
333 }
334 catch (e)
335 {
336 return this.fail(e);
337 }
338
339 return {succeed: true, result: this.serialize(value, objects)};
340 case "setProperty":
341 var obj = objects[message.objectId];
342 var value = this.deserialize(message.value, objects, tab);
343
344 try {
345 obj[message.property] = value;
346 }
347 catch (e)
348 {
349 return this.fail(e);
350 }
351
352 return {succeed: true};
353 case "callFunction":
354 var func = objects[message.functionId];
355 var context = objects[message.contextId];
356
357 var args = [];
358 for (var i = 0; i < message.args.length; i++)
359 args.push(this.deserialize(message.args[i], objects, tab));
360
361 try {
362 var result = func.apply(context, args);
363 }
364 catch (e)
365 {
366 return this.fail(e);
367 }
368
369 return {succeed: true, result: this.serialize(result, objects)};
370 case "inspectObject":
371 var obj = objects[message.objectId];
372
373 var prototype = Object.getPrototypeOf(obj);
374 var prototypeId;
375 if (prototype != null)
376 prototypeId = this.registerObject(prototype, objects);
377 else
378 prototypeId = null;
379
380 var properties = {};
381 Object.getOwnPropertyNames(obj).forEach(function(prop)
382 {
383 if (obj != Object.prototype || prop == "constructor")
384 properties[prop] = {
385 enumerable: Object.getOwnPropertyDescriptor(obj, prop).enumera ble
386 };
387 });
388
389 return {prototypeId: prototypeId, properties: properties};
390 }
391 }
392 };
393
394
395 /* Web request blocking */
396
397 ext.webRequest = {
398 onBeforeRequest: {
399 _listeners: [],
400 _urlPatterns: [],
401
402 _handleMessage: function(message, tab)
403 {
404 tab = new Tab(tab);
405
406 for (var i = 0; i < this._listeners.length; i++)
407 {
408 var regex = this._urlPatterns[i];
409
410 if (!regex || regex.test(message))
411 if (this._listeners[i](message.url, message.type, tab, 0, -1) === fa lse)
412 return false;
413 }
414
415 return true;
416 },
417 addListener: function(listener, urls)
418 {
419 var regex;
420
421 if (urls)
422 regex = new RegExp("^(?:" + urls.map(function(url)
423 {
424 return url.split("*").map(function(s)
425 {
426 return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1");
427 }).join(".*");
428 }).join("|") + ")($|[?#])");
429
430 this._listeners.push(listener);
431 this._urlPatterns.push(regex);
432 },
433 removeListener: function(listener)
434 {
435 var idx = this._listeners.indexOf(listener);
436
437 if (idx != -1)
438 {
439 this._listeners.splice(idx, 1);
440 this._urlPatterns.splice(idx, 1);
441 }
442 }
443 },
444 handlerBehaviorChanged: function() {}
445 };
446
447
448 /* Synchronous messaging */
449
450 safari.application.addEventListener("message", function(event)
451 {
452 if (event.name == "canLoad")
453 {
454 var handler;
455
456 switch (event.message.type)
457 {
458 case "proxy":
459 handler = proxy;
460 break;
461 case "webRequest":
462 handler = ext.webRequest.onBeforeRequest;
463 break;
464 }
465
466 event.message = handler._handleMessage(event.message.payload, event.targ et);
467 }
468 }, true);
469 }
470
471
472 /* API */
473
474 ext.windows = {
475 getAll: function(callback)
476 {
477 callback(safari.application.browserWindows.map(function(win)
478 {
479 return new Window(win);
480 }));
481 },
482 getLastFocused: function(callback)
483 {
484 callback(new Window(safari.application.activeBrowserWindow));
485 }
486 };
487
488 ext.tabs = {
489 onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t rue),
490 onCompleted: new TabEventTarget(safari.application, "navigate", t rue),
491 onActivated: new TabEventTarget(safari.application, "activate", t rue),
492 onRemoved: new TabEventTarget(safari.application, "close", t rue)
493 };
494
495 ext.backgroundPage = {
496 getWindow: function()
497 {
498 return safari.extension.globalPage.contentWindow;
499 }
500 };
501
502 ext.onMessage = new MessageEventTarget(safari.application);
503 })();
OLDNEW
« no previous file with comments | « popupBlocker.js ('k') | safari/common.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld