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: Addressed comments Created April 11, 2014, 2:47 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 | « safari/ext/common.js ('k') | safari/ext/popup.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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-2014 Eyeo GmbH 3 * Copyright (C) 2006-2014 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 // the safari object is missing in frames created from javascript: URLs. 20 // the safari object is missing in frames created from javascript: URLs.
21 // So we have to fallback to the safari object from the parent frame. 21 // So we have to fallback to the safari object from the parent frame.
22 if (!("safari" in window)) 22 if (!("safari" in window))
23 window.safari = window.parent.safari; 23 window.safari = window.parent.safari;
24 24
25 if (window == window.top) 25
26 safari.self.tab.dispatchMessage("loading"); 26 /* Intialization */
27
28 var beforeLoadEvent = document.createEvent("Event");
29 beforeLoadEvent.initEvent("beforeload");
30
31 var isTopLevel = window == window.top;
32 var isPrerendered = document.visibilityState == "prerender";
33
34 var documentInfo = safari.self.tab.canLoad(
35 beforeLoadEvent,
36 {
37 category: "loading",
38 url: document.location.href,
39 referrer: document.referrer,
40 isTopLevel: isTopLevel,
41 isPrerendered: isPrerendered
42 }
43 );
44
45 if (isTopLevel && isPrerendered)
46 {
47 var onVisibilitychange = function()
48 {
49 safari.self.tab.dispatchMessage("replaced", {pageId: documentInfo.pageId}) ;
50 document.removeEventListener("visibilitychange", onVisibilitychange);
51 };
52 document.addEventListener("visibilitychange", onVisibilitychange);
53 }
27 54
28 55
29 /* Events */ 56 /* Web requests */
30 57
31 var ContentMessageEventTarget = function() 58 document.addEventListener("beforeload", function(event)
32 { 59 {
33 MessageEventTarget.call(this, safari.self); 60 // we don't block non-HTTP requests anyway, so we can bail out
34 }; 61 // without asking the background page. This is even necessary
35 ContentMessageEventTarget.prototype = { 62 // because passing large data (like a photo encoded as data: URL)
36 __proto__: MessageEventTarget.prototype, 63 // to the background page, freezes Safari.
37 _getResponseDispatcher: function(event) 64 if (!/^https?:/.test(event.url))
65 return;
66
67 var type;
68 switch(event.target.localName)
38 { 69 {
39 return event.target.tab; 70 case "frame":
40 }, 71 case "iframe":
41 _getSenderDetails: function(event) 72 type = "sub_frame";
73 break;
74 case "img":
75 type = "image";
76 break;
77 case "object":
78 case "embed":
79 type = "object";
80 break;
81 case "script":
82 type = "script";
83 break;
84 case "link":
85 if (/\bstylesheet\b/i.test(event.target.rel))
86 {
87 type = "stylesheet";
88 break;
89 }
90 default:
91 type = "other";
92 }
93
94 if (!safari.self.tab.canLoad(
95 event, {
96 category: "webRequest",
97 url: event.url,
98 type: type,
99 pageId: documentInfo.pageId,
100 frameId: documentInfo.frameId
101 }
102 ))
42 { 103 {
43 return {}; 104 event.preventDefault();
105
106 // Safari doesn't dispatch an "error" event when preventing an element
107 // from loading by cancelling the "beforeload" event. So we have to
108 // dispatch it manually. Otherwise element collapsing wouldn't work.
109 if (type != "sub_frame")
110 {
111 var evt = document.createEvent("Event");
112 evt.initEvent("error");
113 event.target.dispatchEvent(evt);
114 }
44 } 115 }
45 }; 116 }, true);
46 117
47 118
48 /* Background page proxy */ 119 /* Context menus */
49 var proxy = { 120
121 document.addEventListener("contextmenu", function(event)
122 {
123 var element = event.srcElement;
124 safari.self.tab.setContextMenuEventUserInfo(event, {
125 pageId: documentInfo.pageId,
126 srcUrl: ("src" in element) ? element.src : null,
127 tagName: element.localName
128 });
129 });
130
131
132 /* Background page */
133
134 var backgroundPageProxy = {
50 objects: [], 135 objects: [],
51 callbacks: [], 136 callbacks: [],
52 137
53 send: function(message) 138 send: function(message)
54 { 139 {
55 var evt = document.createEvent("Event"); 140 message.category = "proxy";
56 evt.initEvent("beforeload"); 141 message.pageId = documentInfo.pageId;
57 return safari.self.tab.canLoad(evt, {type: "proxy", payload: message}); 142
143 return safari.self.tab.canLoad(beforeLoadEvent, message);
58 }, 144 },
59 checkResult: function(result) 145 checkResult: function(result)
60 { 146 {
61 if (!result.succeed) 147 if (!result.succeed)
62 throw result.error; 148 throw result.error;
63 }, 149 },
64 deserializeResult: function(result) 150 deserializeResult: function(result)
65 { 151 {
66 this.checkResult(result); 152 this.checkResult(result);
67 return this.deserialize(result.result); 153 return this.deserialize(result.result);
68 }, 154 },
69 serialize: function(obj, memo) 155 serialize: function(obj, memo)
70 { 156 {
71 var objectId = this.objects.indexOf(obj); 157 var objectId = this.objects.indexOf(obj);
72 if (objectId != -1) 158 if (objectId != -1)
73 return {type: "hosted", objectId: objectId}; 159 return {type: "hosted", objectId: objectId};
74 160
75 if (typeof obj == "function") 161 if (typeof obj == "function")
76 { 162 {
77 var callbackId = this.callbacks.indexOf(obj); 163 var callbackId = this.callbacks.indexOf(obj);
78
79 if (callbackId == -1) 164 if (callbackId == -1)
80 {
81 callbackId = this.callbacks.push(obj) - 1; 165 callbackId = this.callbacks.push(obj) - 1;
82 166
83 safari.self.addEventListener("message", function(event) 167 return {type: "callback", callbackId: callbackId, frameId: documentInfo. frameId};
84 {
85 if (event.name == "proxyCallback")
86 if (event.message.callbackId == callbackId)
87 obj.apply(
88 this.getObject(event.message.contextId),
89 this.deserializeSequence(event.message.args)
90 );
91 }.bind(this));
92 }
93
94 return {type: "callback", callbackId: callbackId};
95 } 168 }
96 169
97 if (typeof obj == "object" && 170 if (typeof obj == "object" &&
98 obj != null && 171 obj != null &&
99 obj.constructor != Date && 172 obj.constructor != Date &&
100 obj.constructor != RegExp) 173 obj.constructor != RegExp)
101 { 174 {
102 if (!memo) 175 if (!memo)
103 memo = {specs: [], objects: []}; 176 memo = {specs: [], objects: []};
104 177
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 functionId: objectId, 292 functionId: objectId,
220 contextId: proxy.getObjectId(this), 293 contextId: proxy.getObjectId(this),
221 args: Array.prototype.map.call( 294 args: Array.prototype.map.call(
222 arguments, 295 arguments,
223 proxy.serialize.bind(proxy) 296 proxy.serialize.bind(proxy)
224 ) 297 )
225 }) 298 })
226 ); 299 );
227 }; 300 };
228 }, 301 },
229 getObject: function(objectId) { 302 handleCallback: function(message)
303 {
304 this.callbacks[message.callbackId].apply(
305 this.getObject(message.contextId),
306 this.deserializeSequence(message.args)
307 );
308 },
309 getObject: function(objectId)
310 {
230 var objectInfo = this.send({ 311 var objectInfo = this.send({
231 type: "inspectObject", 312 type: "inspectObject",
232 objectId: objectId 313 objectId: objectId
233 }); 314 });
234 315
235 var obj = this.objects[objectId]; 316 var obj = this.objects[objectId];
236 if (obj) 317 if (obj)
237 Object.getOwnPropertyNames(obj).forEach(function(prop) { delete obj[prop ]; }); 318 Object.getOwnPropertyNames(obj).forEach(function(prop) { delete obj[prop ]; });
238 else 319 else
239 { 320 {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
274 property, objectInfo.properties[property].enumerable 355 property, objectInfo.properties[property].enumerable
275 )); 356 ));
276 357
277 if (objectInfo.isFunction) 358 if (objectInfo.isFunction)
278 obj.prototype = this.getProperty(objectId, "prototype"); 359 obj.prototype = this.getProperty(objectId, "prototype");
279 360
280 return obj; 361 return obj;
281 } 362 }
282 }; 363 };
283 364
365 ext.backgroundPage = {
366 sendMessage: function(message, responseCallback)
367 {
368 messageProxy.sendMessage(message, responseCallback, documentInfo);
369 },
370 getWindow: function()
371 {
372 return backgroundPageProxy.getObject(0);
373 }
374 };
375
284 376
285 /* Web request blocking */ 377 /* Message processing */
286 378
287 document.addEventListener("beforeload", function(event) 379 var messageProxy = new ext._MessageProxy(safari.self.tab);
380
381 safari.self.addEventListener("message", function(event)
288 { 382 {
289 // we don't block non-HTTP requests anyway, so we can bail out 383 if (event.message.pageId == documentInfo.pageId)
290 // without asking the background page. This is even necessary 384 {
291 // because passing large data (like a photo encoded as data: URL) 385 if (event.name == "request")
292 // to the background page, freezes Safari. 386 {
293 if (!/^https?:/.test(event.url)) 387 messageProxy.handleRequest(event.message, {});
294 return; 388 return;
389 }
295 390
296 var type; 391 if (event.message.frameId == documentInfo.frameId)
297 392 {
298 switch(event.target.localName) 393 switch (event.name)
299 {
300 case "frame":
301 case "iframe":
302 type = "sub_frame";
303 break;
304 case "img":
305 type = "image";
306 break;
307 case "object":
308 case "embed":
309 type = "object";
310 break;
311 case "script":
312 type = "script";
313 break;
314 case "link":
315 if (/\bstylesheet\b/i.test(event.target.rel))
316 { 394 {
317 type = "stylesheet"; 395 case "response":
318 break; 396 messageProxy.handleResponse(event.message);
319 } 397 break;
320 default: 398 case "proxyCallback":
321 type = "other"; 399 backgroundPageProxy.handleCallback(event.message);
322 } 400 break;
323
324 if (!safari.self.tab.canLoad(
325 event, {
326 type: "webRequest",
327 payload: {
328 url: event.url,
329 type: type,
330 documentUrl: document.location.href,
331 isTopLevel: window == window.top
332 } 401 }
333 } 402 }
334 ))
335 {
336 event.preventDefault();
337
338 // Safari doesn't dispatch an "error" event when preventing an element
339 // from loading by cancelling the "beforeload" event. So we have to
340 // dispatch it manually. Otherwise element collapsing wouldn't work.
341 if (type != "sub_frame")
342 {
343 var evt = document.createEvent("Event");
344 evt.initEvent("error");
345 event.target.dispatchEvent(evt);
346 }
347 } 403 }
348 }, true); 404 });
349
350
351 /* API */
352
353 ext.backgroundPage = {
354 sendMessage: function(message, responseCallback)
355 {
356 _sendMessage(
357 message, responseCallback,
358 safari.self.tab, safari.self,
359 {
360 documentUrl: document.location.href,
361 isTopLevel: window == window.top
362 }
363 );
364 },
365 getWindow: function()
366 {
367 return proxy.getObject(0);
368 }
369 };
370
371 ext.onMessage = new ContentMessageEventTarget();
372
373
374 // Safari does not pass the element which the context menu is refering to
375 // so we need to add it to the event's user info.
376 document.addEventListener("contextmenu", function(event)
377 {
378 var element = event.srcElement;
379 safari.self.tab.setContextMenuEventUserInfo(event, {
380 srcUrl: ("src" in element) ? element.src : null,
381 tagName: element.localName
382 });
383 }, false);
384 })(); 405 })();
OLDNEW
« no previous file with comments | « safari/ext/common.js ('k') | safari/ext/popup.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld