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

Side by Side Diff: safari/ext/content.js

Issue 5464830253203456: Refactored the abstraction layer to address prerendered pages on Safari caused by leaky abstraction (Closed)
Patch Set: Created Feb. 22, 2014, 10:45 a.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
1 /* 1 /*
2 * This file is part of Adblock Plus <http://adblockplus.org/>, 2 * This file is part of Adblock Plus <http://adblockplus.org/>,
3 * Copyright (C) 2006-2013 Eyeo GmbH 3 * Copyright (C) 2006-2013 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 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 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details. 12 * GNU General Public License for more details.
13 * 13 *
14 * You should have received a copy of the GNU General Public License 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/>. 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 17
18 (function() 18 (function()
19 { 19 {
20 if (window == window.top) 20 /* Intialization */
21 safari.self.tab.dispatchMessage("loading"); 21
22 var beforeLoadEvent = document.createEvent("Event");
23 beforeLoadEvent.initEvent("beforeload");
24
25 var isTopLevel = window == window.top;
26 var isPrerendered = document.visibilityState == "prerender";
27
28 var documentInfo = safari.self.tab.canLoad(
29 beforeLoadEvent,
30 {
31 category: "loading",
32 url: document.location.href,
33 referrer: document.referrer,
34 isTopLevel: isTopLevel,
35 isPrerendered: isPrerendered
36 }
37 );
38
39 if (isTopLevel && isPrerendered)
40 {
41 var onVisibilitychange = function()
42 {
43 safari.self.tab.dispatchMessage("replaced", {pageId: documentInfo.pageId}) ;
44 document.removeEventListener("visibilitychange", onVisibilitychange);
45 };
46 document.addEventListener("visibilitychange", onVisibilitychange);
47 }
22 48
23 49
24 /* Events */ 50 /* Web requests */
25 51
26 var ContentMessageEventTarget = function() 52 document.addEventListener("beforeload", function(event)
27 { 53 {
28 MessageEventTarget.call(this, safari.self); 54 var type;
29 }; 55 switch(event.target.localName)
30 ContentMessageEventTarget.prototype = {
31 __proto__: MessageEventTarget.prototype,
32 _getResponseDispatcher: function(event)
33 { 56 {
34 return event.target.tab; 57 case "frame":
35 }, 58 case "iframe":
36 _getSenderDetails: function(event) 59 type = "sub_frame";
60 break;
61 case "img":
62 type = "image";
63 break;
64 case "object":
65 case "embed":
66 type = "object";
67 break;
68 case "script":
69 type = "script";
70 break;
71 case "link":
72 if (/\bstylesheet\b/i.test(event.target.rel))
73 {
74 type = "stylesheet";
75 break;
76 }
77 default:
78 type = "other";
79 }
80
81 if (!safari.self.tab.canLoad(
82 event, {
83 category: "webRequest",
84 url: event.url,
85 type: type,
86 pageId: documentInfo.pageId,
87 frameId: documentInfo.frameId
88 }
89 ))
37 { 90 {
38 return {}; 91 event.preventDefault();
92
93 // Safari doesn't dispatch an "error" or "load" event when preventing an
94 // element from loading by cancelling the "beforeload" event. So we have
95 // to dispatch it manually. Otherwise element collapsing wouldn't work.
96 var evt = document.createEvent("Event");
97 evt.initEvent(type == "sub_frame" ? "load" : "error");
98 event.target.dispatchEvent(evt);
39 } 99 }
40 }; 100 }, true);
41 101
42 102
43 /* Background page proxy */ 103 /* Context menus */
44 var proxy = { 104
105 document.addEventListener("contextmenu", function(event)
106 {
107 var element = event.srcElement;
108 safari.self.tab.setContextMenuEventUserInfo(event, {
109 pageId: documentInfo.pageId,
110 srcUrl: ("src" in element) ? element.src : null,
111 tagName: element.localName
112 });
113 });
114
115
116 /* Background page */
117
118 var backgroundPageProxy = {
45 objects: [], 119 objects: [],
46 callbacks: [], 120 callbacks: [],
47 121
48 send: function(message) 122 send: function(message)
49 { 123 {
50 var evt = document.createEvent("Event"); 124 message.category = "proxy";
51 evt.initEvent("beforeload"); 125 message.pageId = documentInfo.pageId;
52 return safari.self.tab.canLoad(evt, {type: "proxy", payload: message}); 126
127 return safari.self.tab.canLoad(beforeLoadEvent, message);
53 }, 128 },
54 checkResult: function(result) 129 checkResult: function(result)
55 { 130 {
56 if (!result.succeed) 131 if (!result.succeed)
57 throw result.error; 132 throw result.error;
58 }, 133 },
59 deserializeResult: function(result) 134 deserializeResult: function(result)
60 { 135 {
61 this.checkResult(result); 136 this.checkResult(result);
62 return this.deserialize(result.result); 137 return this.deserialize(result.result);
63 }, 138 },
64 serialize: function(obj, memo) 139 serialize: function(obj, memo)
65 { 140 {
66 var objectId = this.objects.indexOf(obj); 141 var objectId = this.objects.indexOf(obj);
67 if (objectId != -1) 142 if (objectId != -1)
68 return {type: "hosted", objectId: objectId}; 143 return {type: "hosted", objectId: objectId};
69 144
70 if (typeof obj == "function") 145 if (typeof obj == "function")
71 { 146 {
72 var callbackId = this.callbacks.indexOf(obj); 147 var callbackId = this.callbacks.indexOf(obj);
73
74 if (callbackId == -1) 148 if (callbackId == -1)
75 {
76 callbackId = this.callbacks.push(obj) - 1; 149 callbackId = this.callbacks.push(obj) - 1;
77 150
78 safari.self.addEventListener("message", function(event) 151 return {type: "callback", callbackId: callbackId, frameId: documentInfo. frameId};
79 {
80 if (event.name == "proxyCallback")
81 if (event.message.callbackId == callbackId)
82 obj.apply(
83 this.getObject(event.message.contextId),
84 this.deserializeSequence(event.message.args)
85 );
86 }.bind(this));
87 }
88
89 return {type: "callback", callbackId: callbackId};
90 } 152 }
91 153
92 if (typeof obj == "object" && 154 if (typeof obj == "object" &&
93 obj != null && 155 obj != null &&
94 obj.constructor != Date && 156 obj.constructor != Date &&
95 obj.constructor != RegExp) 157 obj.constructor != RegExp)
96 { 158 {
97 if (!memo) 159 if (!memo)
98 memo = {specs: [], objects: []}; 160 memo = {specs: [], objects: []};
99 161
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 functionId: objectId, 276 functionId: objectId,
215 contextId: proxy.getObjectId(this), 277 contextId: proxy.getObjectId(this),
216 args: Array.prototype.map.call( 278 args: Array.prototype.map.call(
217 arguments, 279 arguments,
218 proxy.serialize.bind(proxy) 280 proxy.serialize.bind(proxy)
219 ) 281 )
220 }) 282 })
221 ); 283 );
222 }; 284 };
223 }, 285 },
224 getObject: function(objectId) { 286 handleCallback: function(message)
287 {
288 this.callbacks[message.callbackId].apply(
289 this.getObject(message.contextId),
290 this.deserializeSequence(message.args)
291 );
292 },
293 getObject: function(objectId)
294 {
225 var objectInfo = this.send({ 295 var objectInfo = this.send({
226 type: "inspectObject", 296 type: "inspectObject",
227 objectId: objectId 297 objectId: objectId
228 }); 298 });
229 299
230 var obj = this.objects[objectId]; 300 var obj = this.objects[objectId];
231 if (obj) 301 if (obj)
232 Object.getOwnPropertyNames(obj).forEach(function(prop) { delete obj[prop ]; }); 302 Object.getOwnPropertyNames(obj).forEach(function(prop) { delete obj[prop ]; });
233 else 303 else
234 { 304 {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
269 property, objectInfo.properties[property].enumerable 339 property, objectInfo.properties[property].enumerable
270 )); 340 ));
271 341
272 if (objectInfo.isFunction) 342 if (objectInfo.isFunction)
273 obj.prototype = this.getProperty(objectId, "prototype"); 343 obj.prototype = this.getProperty(objectId, "prototype");
274 344
275 return obj; 345 return obj;
276 } 346 }
277 }; 347 };
278 348
349 ext.backgroundPage = {
350 sendMessage: function(message, responseCallback)
351 {
352 messageProxy.sendMessage(message, responseCallback, documentInfo);
353 },
354 getWindow: function()
355 {
356 return backgroundPageProxy.getObject(0);
357 }
358 };
359
279 360
280 /* Web request blocking */ 361 /* Message processing */
281 362
282 document.addEventListener("beforeload", function(event) 363 var messageProxy = new ext._MessageProxy(safari.self.tab);
364
365 safari.self.addEventListener("message", function(event)
283 { 366 {
284 var type; 367 if (event.message.pageId == documentInfo.pageId)
368 {
369 if (event.name == "request")
370 {
371 messageProxy.handleRequest(event.message, {});
372 return;
373 }
285 374
286 switch(event.target.localName) 375 if (event.message.frameId == documentInfo.frameId)
287 { 376 {
288 case "frame": 377 switch (event.name)
289 case "iframe":
290 type = "sub_frame";
291 break;
292 case "img":
293 type = "image";
294 break;
295 case "object":
296 case "embed":
297 type = "object";
298 break;
299 case "script":
300 type = "script";
301 break;
302 case "link":
303 if (/\bstylesheet\b/i.test(event.target.rel))
304 { 378 {
305 type = "stylesheet"; 379 case "response":
306 break; 380 messageProxy.handleResponse(event.message);
307 } 381 break;
308 default: 382 case "proxyCallback":
309 type = "other"; 383 backgroundPageProxy.handleCallback(event.message);
310 } 384 break;
311
312 if (!safari.self.tab.canLoad(
313 event, {
314 type: "webRequest",
315 payload: {
316 url: event.url,
317 type: type,
318 documentUrl: document.location.href,
319 isTopLevel: window == window.top
320 } 385 }
321 } 386 }
322 ))
323 {
324 event.preventDefault();
325
326 // Safari doesn't dispatch an "error" or "load" event when preventing an
327 // element from loading by cancelling the "beforeload" event. So we have
328 // to dispatch it manually. Otherwise element collapsing wouldn't work.
329 var evt = document.createEvent("Event");
330 evt.initEvent(type == "sub_frame" ? "load" : "error");
331 event.target.dispatchEvent(evt);
332 } 387 }
333 }, true); 388 });
334
335
336 /* API */
337
338 ext.backgroundPage = {
339 sendMessage: function(message, responseCallback)
340 {
341 _sendMessage(
342 message, responseCallback,
343 safari.self.tab, safari.self,
344 {
345 documentUrl: document.location.href,
346 isTopLevel: window == window.top
347 }
348 );
349 },
350 getWindow: function()
351 {
352 return proxy.getObject(0);
353 }
354 };
355
356 ext.onMessage = new ContentMessageEventTarget();
357
358
359 // Safari does not pass the element which the context menu is refering to
360 // so we need to add it to the event's user info.
361 document.addEventListener("contextmenu", function(event)
362 {
363 var element = event.srcElement;
364 safari.self.tab.setContextMenuEventUserInfo(event, {
365 srcUrl: ("src" in element) ? element.src : null,
366 tagName: element.localName
367 });
368 }, false);
369 })(); 389 })();
OLDNEW
« notification.js ('K') | « safari/ext/common.js ('k') | safari/ext/popup.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld