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: Converted new calls to chrome.* to ext.* and fixed issue with background page proxy about added/rem… Created Nov. 9, 2013, 6:38 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 == "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)
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) {
233 var objects = proxy.objects[idx];
234
235 tab.page.dispatchMessage("proxyCallback", {
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 {
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 {
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 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)
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 default:
462 console.log(event.message);
463 }
464
465 event.message = handler._handleMessage(event.message.payload, event.targ et);
466 }
467 }, true);
468 }
469
470
471 /* API */
472
473 ext.windows = {
474 getAll: function(callback)
475 {
476 callback(safari.application.browserWindows.map(function(win)
477 {
478 return new Window(win);
479 }));
480 },
481 getLastFocused: function(callback)
482 {
483 callback(new Window(safari.application.activeBrowserWindow));
484 }
485 };
486
487 ext.tabs = {
488 onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t rue),
489 onCompleted: new TabEventTarget(safari.application, "navigate", t rue),
490 onActivated: new TabEventTarget(safari.application, "activate", t rue),
491 onRemoved: new TabEventTarget(safari.application, "close", t rue)
492 };
493
494 ext.backgroundPage = {
495 getWindow: function()
496 {
497 return safari.extension.globalPage.contentWindow;
498 }
499 };
500
501 ext.onMessage = new MessageEventTarget(safari.application);
502 })();
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