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: Added missing copyright disclaimers and websql implementation Created Oct. 23, 2013, 2:46 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 /* Tabs */
20
21 var TabEventTarget = function() {
22 WrappedEventTarget.apply(this, arguments);
23 };
24 TabEventTarget.prototype = {
25 __proto__: WrappedEventTarget.prototype,
26 _wrapListener: function(listener) {
27 return function(event) {
28 listener(new Tab(event.target));
29 };
30 }
31 };
32
33 Tab = function(tab) {
34 this._tab = tab;
35
36 this._eventTarget = tab;
37 this._messageDispatcher = tab.page;
38
39 this.url = tab.url;
40
41 this.onBeforeNavigate = new TabEventTarget(tab, "beforeNavigate", false);
42 this.onCompleted = new TabEventTarget(tab, "navigate", false);
43 this.onActivated = new TabEventTarget(tab, "activate", false);
44 this.onRemoved = new TabEventTarget(tab, "close", false);
45 };
46 Tab.prototype = {
47 close: function() {
48 this._tab.close();
49 },
50 activate: function() {
51 this._tab.activate();
52 },
53 sendMessage: sendMessage,
54 pageAction: {
55 // there are no page actions in safari, so we use toolbar items instead
56 setIcon: function(path) {
57 safari.extension.toolbarItems[0].image = safari.extension.baseURI + path ;
58 },
59 setTitle: function(title) {
60 safari.extension.toolbarItems[0].toolTip = title;
61 },
62
63 // toolbar items in safari can"t get hidden
64 hide: function() {},
65 show: function() {}
66 }
67 };
68
69 TabMap = function() {
70 this._tabs = [];
71 this._values = [];
72
73 this._onClosed = this._onClosed.bind(this);
74 };
75 TabMap.prototype = {
76 get: function(tab) {
77 var idx;
78
79 if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1)
80 return null;
81
82 return this._values[idx];
83 },
84 set: function(tab, value) {
85 var idx = this._tabs.indexOf(tab._tab);
86
87 if (idx != -1)
88 this._values[idx] = value;
89 else {
90 this._tabs.push(tab._tab);
91 this._values.push(value);
92
93 tab._tab.addEventListener("close", this._onClosed, false);
94 }
95 },
96 has: function(tab) {
97 return this._tabs.indexOf(tab._tab) != -1;
98 },
99 clear: function() {
100 while (this._tabs.length > 0)
101 this._delete(this._tabs[0]);
102 },
103 _delete: function(tab) {
104 var idx = this._tabs.indexOf(tab);
105
106 if (idx != -1) {
107 this._tabs.splice(idx, 1);
108 this._values.splice(idx, 1);
109
110 tab.removeEventListener("close", this._onClosed, false);
111 }
112 },
113 _onClosed: function(event) {
114 this._delete(event.target);
115 }
116 };
117 TabMap.prototype["delete"] = function(tab) {
118 this._delete(tab._tab);
119 };
120
121
122 /* Windows */
123
124 Window = function(win) {
125 this._win = win;
126 this.visible = win.visible;
127 }
128 Window.prototype = {
129 getAllTabs: function(callback) {
130 callback(this._win.tabs.map(function(tab) {
131 return new Tab(tab);
132 }));
133 },
134 getActiveTab: function(callback) {
135 callback(new Tab(this._win.activeTab));
136 },
137 openTab: function(url, callback) {
138 var tab = this._win.openTab();
139 tab.url = url;
140
141 if (callback)
142 callback(new Tab(tab));
143 }
144 };
145
146 if (safari.extension.globalPage.contentWindow == window) {
147 /* Background page proxy */
148
149 var proxy = {
150 tabs: [],
151 objects: [],
152
153 registerObject: function(obj, objects) {
154 var objectId = objects.indexOf(obj);
155
156 if (objectId == -1)
157 objectId = objects.push(obj) - 1;
158
159 return objectId;
160 },
161 serializeSequence: function(sequence, objects, memo) {
162 if (!memo)
163 memo = {specs: [], arrays: []};
164
165 var items = [];
166 for (var i = 0; i < sequence.length; i++)
167 items.push(this.serialize(sequence[i], objects, memo));
168
169 return items;
170 },
171 serialize: function(obj, objects, memo) {
172 if (typeof obj == "function")
173 return {type: "function", objectId: this.registerObject(obj, objects)} ;
174
175 if (typeof obj == "object" && obj != null) {
176 if (obj.constructor == Array) {
177 if (!memo)
178 memo = {specs: [], arrays: []};
179
180 var idx = memo.arrays.indexOf(obj);
181 if (idx != -1)
182 return memo.specs[idx];
183
184 var spec = {type: "array"};
185 memo.specs.push(spec);
186 memo.arrays.push(obj);
187
188 spec.items = this.serializeSequence(obj, objects, memo);
189 return spec;
190 }
191
192 if (obj.constructor != Date)
193 if (obj.constructor != RegExp)
194 return {type: "object", objectId: this.registerObject(obj, objects)} ;
195 }
196
197 return {type: "value", value: obj};
198 },
199 createCallback: function(callbackId, tab) {
200 var proxy = this;
201
202 return function() {
203 var idx = proxy.tabs.indexOf(tab);
204
205 if (idx != -1) {
206 var objects = proxy.objects[idx];
207
208 tab.page.dispatchMessage("proxyCallback", {
209 callbackId: callbackId,
210 contextId: proxy.registerObject(this, objects),
211 args: proxy.serializeSequence(arguments, objects)
212 });
213 }
214 };
215 },
216 deserialize: function(spec, objects, tab, memo) {
217 switch (spec.type) {
218 case "value":
219 return spec.value;
220 case "hosted":
221 return objects[spec.objectId];
222 case "callback":
223 return this.createCallback(spec.callbackId, tab);
224 case "object":
225 case "array":
226 if (!memo)
227 memo = {specs: [], objects: []};
228
229 var idx = memo.specs.indexOf(spec);
230 if (idx != -1)
231 return memo.objects[idx];
232
233 var obj;
234 if (spec.type == "array")
235 obj = [];
236 else
237 obj = {};
238
239 memo.specs.push(spec);
240 memo.objects.push(obj);
241
242 if (spec.type == "array")
243 for (var i = 0; i < spec.items.length; i++)
244 obj.push(this.deserialize(spec.items[i], objects, tab, memo));
245 else
246 for (var k in spec.properties)
247 obj[k] = this.deserialize(spec.properties[k], objects, tab, memo );
248
249 return obj;
250 }
251 },
252 createObjectCache: function(tab) {
253 var objects = [window];
254
255 this.tabs.push(tab);
256 this.objects.push(objects);
257
258 tab.addEventListener("close", function() {
259 var idx = this.tabs.indexOf(tab);
260
261 if (idx != -1) {
262 this.tabs.splice(idx, 1);
263 this.objects.splice(idx, 1);
264 }
265 }.bind(this));
266
267 return objects;
268 },
269 getObjectCache: function(tab) {
270 var idx = this.tabs.indexOf(tab);
271 var objects;
272
273 if (idx != -1)
274 objects = this.objects[idx];
275 else
276 objects = this.objects[idx] = this.createObjectCache(tab);
277
278 return objects;
279 },
280 fail: function(error) {
281 if (error instanceof Error)
282 error = error.message;
283 return {succeed: false, error: error};
284 },
285 _handleMessage: function(message, tab) {
286 var objects = this.getObjectCache(tab);
287
288 switch (message.type) {
289 case "getProperty":
290 var obj = objects[message.objectId];
291
292 try {
293 var value = obj[message.property];
294 } catch (e) {
295 return this.fail(e);
296 }
297
298 return {succeed: true, result: this.serialize(value, objects)};
299 case "setProperty":
300 var obj = objects[message.objectId];
301 var value = this.deserialize(message.value, objects, tab);
302
303 try {
304 obj[message.property] = value;
305 } catch (e) {
306 return this.fail(e);
307 }
308
309 return {succeed: true};
310 case "callFunction":
311 var func = objects[message.functionId];
312 var context = objects[message.contextId];
313
314 var args = [];
315 for (var i = 0; i < message.args.length; i++)
316 args.push(this.deserialize(message.args[i], objects, tab));
317
318 try {
319 var result = func.apply(context, args);
320 } catch (e) {
321 return this.fail(e);
322 }
323
324 return {succeed: true, result: this.serialize(result, objects)};
325 case "inspectObject":
326 var obj = objects[message.objectId];
327
328 var prototype = Object.getPrototypeOf(obj);
329 var prototypeId;
330 if (prototype != null)
331 prototypeId = this.registerObject(prototype, objects);
332 else
333 prototypeId = null;
334
335 var properties = {};
336 Object.getOwnPropertyNames(obj).forEach(function(prop) {
337 if (obj != Object.prototype || prop == "constructor")
338 properties[prop] = {
339 enumerable: Object.getOwnPropertyDescriptor(obj, prop).enumera ble
340 };
341 });
342
343 return {prototypeId: prototypeId, properties: properties};
344 }
345 }
346 };
347
348
349 /* Web request blocking */
350
351 ext.webRequest = {
352 onBeforeRequest: {
353 _listeners: [],
354 _urlPatterns: [],
355
356 _handleMessage: function(message, tab) {
357 tab = new Tab(tab);
358
359 for (var i = 0; i < this._listeners.length; i++) {
360 var regex = this._urlPatterns[i];
361
362 if (!regex || regex.test(message))
363 if (this._listeners[i](message.url, message.type, tab, 0, -1) === fa lse) {
364 return false;
365 }
366 }
367
368 return true;
369 },
370 addListener: function(listener, urls) {
371 var regex;
372
373 if (urls)
374 regex = new RegExp("^(?:" + urls.map(function(url) {
375 return url.split("*").map(function(s) {
376 return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1");
377 }).join(".*");
378 }).join("|") + ")($|[?#])");
379
380 this._listeners.push(listener);
381 this._urlPatterns.push(regex);
382 },
383 removeListener: function(listener) {
384 var idx = this._listeners.indexOf(listener);
385
386 if (idx != -1) {
387 this._listeners.splice(idx, 1);
388 this._urlPatterns.splice(idx, 1);
389 }
390 }
391 },
392 handlerBehaviorChanged: function() {}
393 };
394
395
396 /* Synchronous messaging */
397
398 safari.application.addEventListener("message", function(event) {
399 if (event.name == "canLoad") {
400 var handler;
401
402 switch (event.message.type) {
403 case "proxy":
404 handler = proxy;
405 break;
406 case "webRequest":
407 handler = ext.webRequest.onBeforeRequest;
408 break;
409 }
410
411 event.message = handler._handleMessage(event.message.payload, event.targ et);
412 }
413 }, true);
414 }
415
416
417 /* API */
418
419 ext.windows = {
420 getAll: function(callback) {
421 callback(safari.application.browserWindows.map(function(win) {
422 return new Window(win);
423 }));
424 },
425 getLastFocused: function(callback) {
426 callback(new Window(safari.application.activeBrowserWindow));
427 }
428 };
429
430 ext.tabs = {
431 onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t rue),
432 onCompleted: new TabEventTarget(safari.application, "navigate", t rue),
433 onActivated: new TabEventTarget(safari.application, "activate", t rue),
434 onRemoved: new TabEventTarget(safari.application, "close", t rue)
435 };
436
437 ext.backgroundPage = {
438 getWindow: function() {
439 return safari.extension.globalPage.contentWindow;
440 }
441 };
442
443 ext.onMessage = new MessageEventTarget(safari.application);
444 })();
OLDNEW
« chrome/common.js ('K') | « popupBlocker.js ('k') | safari/common.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld