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

Powered by Google App Engine
This is Rietveld