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

Delta Between Two Patch Sets: safari/background.js

Issue 16067002: Added Safari Support (Closed)
Left Patch Set: Created Oct. 21, 2013, 8:11 p.m.
Right Patch Set: Bugfixes Created Nov. 15, 2013, 8:58 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « popupBlocker.js ('k') | safari/common.js » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 (function() { 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 {
2 /* Tabs */ 20 /* Tabs */
3 21
4 var TabEventTarget = function() { 22 var TabEventTarget = function()
23 {
5 WrappedEventTarget.apply(this, arguments); 24 WrappedEventTarget.apply(this, arguments);
6 }; 25 };
7 TabEventTarget.prototype = { 26 TabEventTarget.prototype = {
8 __proto__: WrappedEventTarget.prototype, 27 __proto__: WrappedEventTarget.prototype,
9 _wrapListener: function(listener) { 28 _wrapListener: function(listener)
10 return function(event) { 29 {
11 listener(new Tab(event.target)); 30 return function(event)
31 {
32 if (event.target instanceof SafariBrowserTab)
33 listener(new Tab(event.target));
12 }; 34 };
13 } 35 }
14 }; 36 };
15 37
16 Tab = function(tab) { 38 Tab = function(tab)
39 {
17 this._tab = tab; 40 this._tab = tab;
18 41
19 this._eventTarget = tab; 42 this._eventTarget = tab;
20 this._messageDispatcher = tab.page; 43 this._messageDispatcher = tab.page;
21 44
22 this.url = tab.url;
23
24 this.onBeforeNavigate = new TabEventTarget(tab, "beforeNavigate", false); 45 this.onBeforeNavigate = new TabEventTarget(tab, "beforeNavigate", false);
25 this.onCompleted = new TabEventTarget(tab, "navigate", false); 46 this.onCompleted = new TabEventTarget(tab, "navigate", false);
26 this.onActivated = new TabEventTarget(tab, "activate", false); 47 this.onActivated = new TabEventTarget(tab, "activate", false);
27 this.onRemoved = new TabEventTarget(tab, "close", false); 48 this.onRemoved = new TabEventTarget(tab, "close", false);
28 }; 49 };
29 Tab.prototype = { 50 Tab.prototype = {
30 close: function() { 51 get url()
52 {
53 return this._tab.url;
54 },
55 close: function()
56 {
31 this._tab.close(); 57 this._tab.close();
32 }, 58 },
33 activate: function() { 59 activate: function()
60 {
34 this._tab.activate(); 61 this._tab.activate();
35 }, 62 },
36 sendMessage: sendMessage, 63 sendMessage: sendMessage,
37 pageAction: { 64 pageAction: {
38 // there are no page actions in safari, so we use toolbar items instead 65 // there are no page actions in safari, so we use toolbar items instead
39 setIcon: function(path) { 66 setIcon: function(path)
67 {
40 safari.extension.toolbarItems[0].image = safari.extension.baseURI + path ; 68 safari.extension.toolbarItems[0].image = safari.extension.baseURI + path ;
41 }, 69 },
42 setTitle: function(title) { 70 setTitle: function(title)
71 {
43 safari.extension.toolbarItems[0].toolTip = title; 72 safari.extension.toolbarItems[0].toolTip = title;
44 }, 73 },
45 74
46 // toolbar items in safari can"t get hidden 75 // toolbar items in safari can"t get hidden
47 hide: function() {}, 76 hide: function() {},
48 show: function() {} 77 show: function() {}
49 } 78 }
50 }; 79 };
51 80
52 TabMap = function() { 81 TabMap = function()
82 {
53 this._tabs = []; 83 this._tabs = [];
54 this._values = []; 84 this._values = [];
55 85
56 this._onClosed = this._onClosed.bind(this); 86 this._onClosed = this._onClosed.bind(this);
57 }; 87 };
58 TabMap.prototype = { 88 TabMap.prototype =
89 {
59 get: function(tab) { 90 get: function(tab) {
60 var idx; 91 var idx;
61 92
62 if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1) 93 if (!tab || (idx = this._tabs.indexOf(tab._tab)) == -1)
63 return null; 94 return null;
64 95
65 return this._values[idx]; 96 return this._values[idx];
66 }, 97 },
67 set: function(tab, value) { 98 set: function(tab, value)
99 {
68 var idx = this._tabs.indexOf(tab._tab); 100 var idx = this._tabs.indexOf(tab._tab);
69 101
70 if (idx != -1) 102 if (idx != -1)
71 this._values[idx] = value; 103 this._values[idx] = value;
72 else { 104 else
105 {
73 this._tabs.push(tab._tab); 106 this._tabs.push(tab._tab);
74 this._values.push(value); 107 this._values.push(value);
75 108
76 tab._tab.addEventListener("close", this._onClosed, false); 109 tab._tab.addEventListener("close", this._onClosed, false);
77 } 110 }
78 }, 111 },
79 has: function(tab) { 112 has: function(tab)
113 {
80 return this._tabs.indexOf(tab._tab) != -1; 114 return this._tabs.indexOf(tab._tab) != -1;
81 }, 115 },
82 clear: function() { 116 clear: function()
117 {
83 while (this._tabs.length > 0) 118 while (this._tabs.length > 0)
84 this._delete(this._tabs[0]); 119 this._delete(this._tabs[0]);
85 }, 120 },
86 _delete: function(tab) { 121 _delete: function(tab)
122 {
87 var idx = this._tabs.indexOf(tab); 123 var idx = this._tabs.indexOf(tab);
88 124
89 if (idx != -1) { 125 if (idx != -1)
126 {
90 this._tabs.splice(idx, 1); 127 this._tabs.splice(idx, 1);
91 this._values.splice(idx, 1); 128 this._values.splice(idx, 1);
92 129
93 tab.removeEventListener("close", this._onClosed, false); 130 tab.removeEventListener("close", this._onClosed, false);
94 } 131 }
95 }, 132 },
96 _onClosed: function(event) { 133 _onClosed: function(event)
134 {
97 this._delete(event.target); 135 this._delete(event.target);
98 } 136 }
99 }; 137 };
100 TabMap.prototype["delete"] = function(tab) { 138 TabMap.prototype["delete"] = function(tab)
139 {
101 this._delete(tab._tab); 140 this._delete(tab._tab);
102 }; 141 };
103 142
104 143
105 /* Windows */ 144 /* Windows */
106 145
107 Window = function(win) { 146 Window = function(win)
147 {
108 this._win = win; 148 this._win = win;
109 this.visible = win.visible;
110 } 149 }
111 Window.prototype = { 150 Window.prototype = {
112 getAllTabs: function(callback) { 151 get visible()
113 callback(this._win.tabs.map(function(tab) { 152 {
114 return new Tab(tab); 153 return this._win.visible;
115 })); 154 },
116 }, 155 getAllTabs: function(callback)
117 getActiveTab: function(callback) { 156 {
157 callback(this._win.tabs.map(function(tab) { return new Tab(tab); }));
158 },
159 getActiveTab: function(callback)
160 {
118 callback(new Tab(this._win.activeTab)); 161 callback(new Tab(this._win.activeTab));
119 }, 162 },
120 openTab: function(url, callback) { 163 openTab: function(url, callback)
164 {
121 var tab = this._win.openTab(); 165 var tab = this._win.openTab();
122 tab.url = url; 166 tab.url = url;
123 167
124 if (callback) 168 if (callback)
125 callback(new Tab(tab)); 169 callback(new Tab(tab));
126 } 170 }
127 }; 171 };
128 172
129 if (safari.extension.globalPage.contentWindow == window) { 173 if (safari.extension.globalPage.contentWindow == window)
174 {
130 /* Background page proxy */ 175 /* Background page proxy */
131 176
132 var proxy = { 177 var proxy = {
133 tabs: [], 178 tabs: [],
134 objects: [], 179 objects: [],
135 180
136 registerObject: function(obj, objects) { 181 registerObject: function(obj, objects)
182 {
137 var objectId = objects.indexOf(obj); 183 var objectId = objects.indexOf(obj);
138 184
139 if (objectId == -1) 185 if (objectId == -1)
140 objectId = objects.push(obj) - 1; 186 objectId = objects.push(obj) - 1;
141 187
142 return objectId; 188 return objectId;
143 }, 189 },
144 serializeSequence: function(sequence, objects, memo) { 190 serializeSequence: function(sequence, objects, memo)
191 {
145 if (!memo) 192 if (!memo)
146 memo = {specs: [], arrays: []}; 193 memo = {specs: [], arrays: []};
147 194
148 var items = []; 195 var items = [];
149 for (var i = 0; i < sequence.length; i++) 196 for (var i = 0; i < sequence.length; i++)
150 items.push(this.serialize(sequence[i], objects, memo)); 197 items.push(this.serialize(sequence[i], objects, memo));
151 198
152 return items; 199 return items;
153 }, 200 },
154 serialize: function(obj, objects, memo) { 201 serialize: function(obj, objects, memo)
155 if (typeof obj == "function") 202 {
156 return {type: "function", objectId: this.registerObject(obj, objects)} ; 203 if (typeof obj == "object" && obj != null || typeof obj == "function")
157 204 {
158 if (typeof obj == "object" && obj != null) { 205 if (obj.constructor == Array)
159 if (obj.constructor == Array) { 206 {
160 if (!memo) 207 if (!memo)
161 memo = {specs: [], arrays: []}; 208 memo = {specs: [], arrays: []};
162 209
163 var idx = memo.arrays.indexOf(obj); 210 var idx = memo.arrays.indexOf(obj);
164 if (idx != -1) 211 if (idx != -1)
165 return memo.specs[idx]; 212 return memo.specs[idx];
166 213
167 var spec = {type: "array"}; 214 var spec = {type: "array"};
168 memo.specs.push(spec); 215 memo.specs.push(spec);
169 memo.arrays.push(obj); 216 memo.arrays.push(obj);
170 217
171 spec.items = this.serializeSequence(obj, objects, memo); 218 spec.items = this.serializeSequence(obj, objects, memo);
172 return spec; 219 return spec;
173 } 220 }
174 221
175 if (obj.constructor != Date) 222 if (obj.constructor != Date && obj.constructor != RegExp)
176 if (obj.constructor != RegExp)
177 return {type: "object", objectId: this.registerObject(obj, objects)} ; 223 return {type: "object", objectId: this.registerObject(obj, objects)} ;
178 } 224 }
179 225
180 return {type: "value", value: obj}; 226 return {type: "value", value: obj};
181 }, 227 },
182 createCallback: function(callbackId, tab) { 228 createCallback: function(callbackId, tab)
229 {
183 var proxy = this; 230 var proxy = this;
184 231
185 return function() { 232 return function()
233 {
186 var idx = proxy.tabs.indexOf(tab); 234 var idx = proxy.tabs.indexOf(tab);
187 235
188 if (idx != -1) { 236 if (idx != -1) {
189 var objects = proxy.objects[idx]; 237 var objects = proxy.objects[idx];
190 238
191 tab.page.dispatchMessage("proxyCallback", { 239 tab.page.dispatchMessage("proxyCallback",
240 {
192 callbackId: callbackId, 241 callbackId: callbackId,
193 contextId: proxy.registerObject(this, objects), 242 contextId: proxy.registerObject(this, objects),
194 args: proxy.serializeSequence(arguments, objects) 243 args: proxy.serializeSequence(arguments, objects)
195 }); 244 });
196 } 245 }
197 }; 246 };
198 }, 247 },
199 deserialize: function(spec, objects, tab, memo) { 248 deserialize: function(spec, objects, tab, memo)
200 switch (spec.type) { 249 {
250 switch (spec.type)
251 {
201 case "value": 252 case "value":
202 return spec.value; 253 return spec.value;
203 case "hosted": 254 case "hosted":
204 return objects[spec.objectId]; 255 return objects[spec.objectId];
205 case "callback": 256 case "callback":
206 return this.createCallback(spec.callbackId, tab); 257 return this.createCallback(spec.callbackId, tab);
207 case "object": 258 case "object":
208 case "array": 259 case "array":
209 if (!memo) 260 if (!memo)
210 memo = {specs: [], objects: []}; 261 memo = {specs: [], objects: []};
(...skipping 14 matching lines...) Expand all
225 if (spec.type == "array") 276 if (spec.type == "array")
226 for (var i = 0; i < spec.items.length; i++) 277 for (var i = 0; i < spec.items.length; i++)
227 obj.push(this.deserialize(spec.items[i], objects, tab, memo)); 278 obj.push(this.deserialize(spec.items[i], objects, tab, memo));
228 else 279 else
229 for (var k in spec.properties) 280 for (var k in spec.properties)
230 obj[k] = this.deserialize(spec.properties[k], objects, tab, memo ); 281 obj[k] = this.deserialize(spec.properties[k], objects, tab, memo );
231 282
232 return obj; 283 return obj;
233 } 284 }
234 }, 285 },
235 createObjectCache: function(tab) { 286 createObjectCache: function(tab)
287 {
236 var objects = [window]; 288 var objects = [window];
237 289
238 this.tabs.push(tab); 290 this.tabs.push(tab);
239 this.objects.push(objects); 291 this.objects.push(objects);
240 292
241 tab.addEventListener("close", function() { 293 tab.addEventListener("close", function()
294 {
242 var idx = this.tabs.indexOf(tab); 295 var idx = this.tabs.indexOf(tab);
243 296
244 if (idx != -1) { 297 if (idx != -1)
298 {
245 this.tabs.splice(idx, 1); 299 this.tabs.splice(idx, 1);
246 this.objects.splice(idx, 1); 300 this.objects.splice(idx, 1);
247 } 301 }
248 }.bind(this)); 302 }.bind(this));
249 303
250 return objects; 304 return objects;
251 }, 305 },
252 getObjectCache: function(tab) { 306 getObjectCache: function(tab)
307 {
253 var idx = this.tabs.indexOf(tab); 308 var idx = this.tabs.indexOf(tab);
254 var objects; 309 var objects;
255 310
256 if (idx != -1) 311 if (idx != -1)
257 objects = this.objects[idx]; 312 objects = this.objects[idx];
258 else 313 else
259 objects = this.objects[idx] = this.createObjectCache(tab); 314 objects = this.objects[idx] = this.createObjectCache(tab);
260 315
261 return objects; 316 return objects;
262 }, 317 },
263 fail: function(error) { 318 fail: function(error)
319 {
264 if (error instanceof Error) 320 if (error instanceof Error)
265 error = error.message; 321 error = error.message;
266 return {succeed: false, error: error}; 322 return {succeed: false, error: error};
267 }, 323 },
268 _handleMessage: function(message, tab) { 324 _handleMessage: function(message, tab)
325 {
269 var objects = this.getObjectCache(tab); 326 var objects = this.getObjectCache(tab);
270 327
271 switch (message.type) { 328 switch (message.type)
329 {
272 case "getProperty": 330 case "getProperty":
273 var obj = objects[message.objectId]; 331 var obj = objects[message.objectId];
274 332
275 try { 333 try
334 {
276 var value = obj[message.property]; 335 var value = obj[message.property];
277 } catch (e) { 336 }
337 catch (e)
338 {
278 return this.fail(e); 339 return this.fail(e);
279 } 340 }
280 341
281 return {succeed: true, result: this.serialize(value, objects)}; 342 return {succeed: true, result: this.serialize(value, objects)};
282 case "setProperty": 343 case "setProperty":
283 var obj = objects[message.objectId]; 344 var obj = objects[message.objectId];
284 var value = this.deserialize(message.value, objects, tab); 345 var value = this.deserialize(message.value, objects, tab);
285 346
286 try { 347 try
348 {
287 obj[message.property] = value; 349 obj[message.property] = value;
288 } catch (e) { 350 }
351 catch (e)
352 {
289 return this.fail(e); 353 return this.fail(e);
290 } 354 }
291 355
292 return {succeed: true}; 356 return {succeed: true};
293 case "callFunction": 357 case "callFunction":
294 var func = objects[message.functionId]; 358 var func = objects[message.functionId];
295 var context = objects[message.contextId]; 359 var context = objects[message.contextId];
296 360
297 var args = []; 361 var args = [];
298 for (var i = 0; i < message.args.length; i++) 362 for (var i = 0; i < message.args.length; i++)
299 args.push(this.deserialize(message.args[i], objects, tab)); 363 args.push(this.deserialize(message.args[i], objects, tab));
300 364
301 try { 365 try
366 {
302 var result = func.apply(context, args); 367 var result = func.apply(context, args);
303 } catch (e) { 368 }
369 catch (e)
370 {
304 return this.fail(e); 371 return this.fail(e);
305 } 372 }
306 373
307 return {succeed: true, result: this.serialize(result, objects)}; 374 return {succeed: true, result: this.serialize(result, objects)};
308 case "inspectObject": 375 case "inspectObject":
309 var obj = objects[message.objectId]; 376 var obj = objects[message.objectId];
310 377 var objectInfo = {properties: {}, isFunction: typeof obj == "functio n"};
311 var prototype = Object.getPrototypeOf(obj); 378
312 var prototypeId; 379 Object.getOwnPropertyNames(obj).forEach(function(prop)
313 if (prototype != null) 380 {
314 prototypeId = this.registerObject(prototype, objects); 381 objectInfo.properties[prop] = {
315 else 382 enumerable: Object.prototype.propertyIsEnumerable.call(obj, prop )
316 prototypeId = null; 383 };
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 }); 384 });
325 385
326 return {prototypeId: prototypeId, properties: properties}; 386 if (obj.__proto__)
387 objectInfo.prototypeId = this.registerObject(obj.__proto__, object s);
388
389 if (obj == Object.prototype)
390 objectInfo.prototypeOf = "Object";
391 if (obj == Function.prototype)
392 objectInfo.prototypeOf = "Function";
393
394 return objectInfo;
327 } 395 }
328 } 396 }
329 }; 397 };
330 398
331 399
332 /* Web request blocking */ 400 /* Web request blocking */
333 401
334 ext.webRequest = { 402 ext.webRequest = {
335 onBeforeRequest: { 403 onBeforeRequest: {
336 _listeners: [], 404 _listeners: [],
337 _urlPatterns: [], 405 _urlPatterns: [],
338 406
339 _handleMessage: function(message, tab) { 407 _handleMessage: function(message, tab)
408 {
340 tab = new Tab(tab); 409 tab = new Tab(tab);
341 410
342 for (var i = 0; i < this._listeners.length; i++) { 411 for (var i = 0; i < this._listeners.length; i++)
412 {
343 var regex = this._urlPatterns[i]; 413 var regex = this._urlPatterns[i];
344 414
345 if (!regex || regex.test(message)) 415 if ((!regex || regex.test(message.url)) && this._listeners[i](messag e.url, message.type, tab, 0, -1) === false)
346 if (this._listeners[i](message.url, message.type, tab, 0, -1) === fa lse) {
347 return false; 416 return false;
348 }
349 } 417 }
350 418
351 return true; 419 return true;
352 }, 420 },
353 addListener: function(listener, urls) { 421 addListener: function(listener, urls)
422 {
354 var regex; 423 var regex;
355 424
356 if (urls) 425 if (urls)
357 regex = new RegExp("^(?:" + urls.map(function(url) { 426 regex = new RegExp("^(?:" + urls.map(function(url)
358 return url.split("*").map(function(s) { 427 {
428 return url.split("*").map(function(s)
429 {
359 return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1"); 430 return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1");
360 }).join(".*"); 431 }).join(".*");
361 }).join("|") + ")($|[?#])"); 432 }).join("|") + ")($|[?#])");
362 433
363 this._listeners.push(listener); 434 this._listeners.push(listener);
364 this._urlPatterns.push(regex); 435 this._urlPatterns.push(regex);
365 }, 436 },
366 removeListener: function(listener) { 437 removeListener: function(listener)
438 {
367 var idx = this._listeners.indexOf(listener); 439 var idx = this._listeners.indexOf(listener);
368 440
369 if (idx != -1) { 441 if (idx != -1)
442 {
370 this._listeners.splice(idx, 1); 443 this._listeners.splice(idx, 1);
371 this._urlPatterns.splice(idx, 1); 444 this._urlPatterns.splice(idx, 1);
372 } 445 }
373 } 446 }
374 }, 447 },
375 handlerBehaviorChanged: function() {} 448 handlerBehaviorChanged: function() {}
376 }; 449 };
377 450
378 451
379 /* Synchronous messaging */ 452 /* Synchronous messaging */
380 453
381 safari.application.addEventListener("message", function(event) { 454 safari.application.addEventListener("message", function(event)
382 if (event.name == "canLoad") { 455 {
456 if (event.name == "canLoad")
457 {
383 var handler; 458 var handler;
384 459
385 switch (event.message.type) { 460 switch (event.message.type)
461 {
386 case "proxy": 462 case "proxy":
387 handler = proxy; 463 handler = proxy;
388 break; 464 break;
389 case "webRequest": 465 case "webRequest":
390 handler = ext.webRequest.onBeforeRequest; 466 handler = ext.webRequest.onBeforeRequest;
391 break; 467 break;
392 } 468 }
393 469
394 event.message = handler._handleMessage(event.message.payload, event.targ et); 470 event.message = handler._handleMessage(event.message.payload, event.targ et);
395 } 471 }
396 }, true); 472 }, true);
397 } 473 }
398 474
399 475
400 /* API */ 476 /* API */
401 477
402 ext.windows = { 478 ext.windows = {
403 getAll: function(callback) { 479 getAll: function(callback)
404 callback(safari.application.browserWindows.map(function(win) { 480 {
481 callback(safari.application.browserWindows.map(function(win)
482 {
405 return new Window(win); 483 return new Window(win);
406 })); 484 }));
407 }, 485 },
408 getLastFocused: function(callback) { 486 getLastFocused: function(callback)
487 {
409 callback(new Window(safari.application.activeBrowserWindow)); 488 callback(new Window(safari.application.activeBrowserWindow));
410 } 489 }
411 }; 490 };
412 491
413 ext.tabs = { 492 ext.tabs = {
414 onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t rue), 493 onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", t rue),
415 onCompleted: new TabEventTarget(safari.application, "navigate", t rue), 494 onCompleted: new TabEventTarget(safari.application, "navigate", true),
416 onActivated: new TabEventTarget(safari.application, "activate", t rue), 495 onActivated: new TabEventTarget(safari.application, "activate", true),
417 onRemoved: new TabEventTarget(safari.application, "close", t rue) 496 onRemoved: new TabEventTarget(safari.application, "close", true)
418 }; 497 };
419 498
420 ext.backgroundPage = { 499 ext.backgroundPage = {
421 getWindow: function() { 500 getWindow: function()
501 {
422 return safari.extension.globalPage.contentWindow; 502 return safari.extension.globalPage.contentWindow;
423 } 503 }
424 }; 504 };
425 505
426 ext.onMessage = new MessageEventTarget(safari.application); 506 ext.onMessage = new MessageEventTarget(safari.application);
507
508
509 // Safari will load the bubble once, and then show it everytime the icon is
510 // clicked. While Chrome loads it everytime you click the icon. So in order to
511 // force the same behavior in Safari, we are going to reload the page of the
512 // bubble everytime it is shown.
513 if (safari.extension.globalPage.contentWindow != window)
514 safari.application.addEventListener("popover", function()
515 {
516 document.documentElement.style.display = "none";
517 document.location.reload();
518 }, true);
427 })(); 519 })();
LEFTRIGHT

Powered by Google App Engine
This is Rietveld