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

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

Issue 5092502491103232: Deal with preloadded pages in Safari 7.0 (Closed)
Patch Set: Created Jan. 23, 2014, 2:03 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
« safari/ext/background.js ('K') | « safari/ext/common.js ('k') | no next file » | 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-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 if (window == window.top)
21 safari.self.tab.dispatchMessage("loading"); 21 safari.self.tab.dispatchMessage("loading", {isDocumentHidden: document.hidde n});
22 22
23 23
24 /* Events */ 24 /* Events */
25 25
26 var ContentMessageEventTarget = function() 26 var ContentMessageEventTarget = function()
27 { 27 {
28 MessageEventTarget.call(this, safari.self); 28 MessageEventTarget.call(this, safari.self);
29 }; 29 };
30 ContentMessageEventTarget.prototype = { 30 ContentMessageEventTarget.prototype = {
31 __proto__: MessageEventTarget.prototype, 31 __proto__: MessageEventTarget.prototype,
32 _getResponseDispatcher: function(event) 32 _getResponseDispatcher: function(event)
33 { 33 {
34 return event.target.tab; 34 return event.target.tab;
35 }, 35 },
36 _getSenderDetails: function(event) 36 _getSenderDetails: function(event)
37 { 37 {
38 return {}; 38 return {};
39 },
40 _ignoreIf: function(event)
41 {
42 // If the current page is a preloaded page in Safari 7.0, we have to
43 // ignore all messages sent from the background page. The high-level code
44 // doesn't know anything about preloaded pages and expects messages to be
45 // handled only by the current visible page.
46 return document.hidden && event.message.isTabVisible;
39 } 47 }
40 }; 48 };
41 49
42 50
43 /* Background page proxy */ 51 /* Background page proxy */
44 var proxy = { 52 var proxy = {
45 objects: [], 53 objects: [],
46 callbacks: [], 54 callbacks: [],
47 55
48 send: function(message) 56 send: function(message)
49 { 57 {
50 var evt = document.createEvent("Event"); 58 evt = document.createEvent("Event");
51 evt.initEvent("beforeload"); 59 evt.initEvent("beforeload");
52 return safari.self.tab.canLoad(evt, {type: "proxy", payload: message}); 60 return safari.self.tab.canLoad(evt, {type: "proxy", payload: message});
53 }, 61 },
54 checkResult: function(result) 62 checkResult: function(result)
55 { 63 {
56 if (!result.succeed) 64 if (!result.succeed)
57 throw result.error; 65 throw result.error;
58 }, 66 },
59 deserializeResult: function(result) 67 deserializeResult: function(result)
60 { 68 {
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 if (objectInfo.isFunction) 280 if (objectInfo.isFunction)
273 obj.prototype = this.getProperty(objectId, "prototype"); 281 obj.prototype = this.getProperty(objectId, "prototype");
274 282
275 return obj; 283 return obj;
276 } 284 }
277 }; 285 };
278 286
279 287
280 /* Web request blocking */ 288 /* Web request blocking */
281 289
282 document.addEventListener("beforeload", function(event) 290 var canLoad = function(event)
283 { 291 {
284 var type; 292 var type;
285 293 switch (event.target.localName)
286 switch(event.target.localName)
287 { 294 {
288 case "frame": 295 case "frame":
289 case "iframe": 296 case "iframe":
290 type = "sub_frame"; 297 type = "sub_frame";
291 break; 298 break;
292 case "img": 299 case "img":
293 type = "image"; 300 type = "image";
294 break; 301 break;
295 case "object": 302 case "object":
296 case "embed": 303 case "embed":
297 type = "object"; 304 type = "object";
298 break; 305 break;
299 case "script": 306 case "script":
300 type = "script"; 307 type = "script";
301 break; 308 break;
302 case "link": 309 case "link":
303 if (/\bstylesheet\b/i.test(event.target.rel)) 310 if (/\bstylesheet\b/i.test(event.target.rel))
304 { 311 {
305 type = "stylesheet"; 312 type = "stylesheet";
306 break; 313 break;
307 } 314 }
308 default: 315 default:
309 type = "other"; 316 type = "other";
310 } 317 }
311 318
312 if (!safari.self.tab.canLoad( 319 return safari.self.tab.canLoad(
313 event, { 320 event, {
314 type: "webRequest", 321 type: "webRequest",
315 payload: { 322 payload: {
316 url: event.url, 323 url: event.url,
317 type: type, 324 type: type,
318 documentUrl: document.location.href, 325 documentUrl: document.location.href,
319 isTopLevel: window == window.top 326 isTopLevel: window == window.top,
327 isDocumentHidden: document.hidden
320 } 328 }
321 } 329 }
322 )) 330 );
331 };
332
333 document.addEventListener("beforeload", function(event)
334 {
335 var result;
336 if (event.target._blocked || (result = canLoad(event)) == "blocked")
323 { 337 {
324 event.preventDefault(); 338 event.preventDefault();
325 339
326 // Safari doesn't dispatch an "error" or "load" event when preventing an 340 // Safari doesn't dispatch an "error" or "load" event when preventing an
327 // element from loading by cancelling the "beforeload" event. So we have 341 // element from loading by cancelling the "beforeload" event. So we have
328 // to dispatch it manually. Otherwise element collapsing wouldn't work. 342 // to dispatch it manually. Otherwise element collapsing wouldn't work.
329 var evt = document.createEvent("Event"); 343 var evt = document.createEvent("Event");
330 evt.initEvent(type == "sub_frame" ? "load" : "error"); 344 evt.initEvent(/^i?frame$/.test(event.target.localName) ? "load" : "error") ;
331 event.target.dispatchEvent(evt); 345 event.target.dispatchEvent(evt);
332 } 346 }
347 else if (result == "deferred")
348 deferredBeforeLoadEvents.push(event);
333 }, true); 349 }, true);
334 350
335 351
336 /* API */ 352 /* API */
337 353
338 ext.backgroundPage = { 354 ext.backgroundPage = {
339 sendMessage: function(message, responseCallback) 355 sendMessage: function(message, responseCallback)
340 { 356 {
341 _sendMessage( 357 _sendMessage(
342 message, responseCallback, 358 // message payload
343 safari.self.tab, safari.self, 359 message,
360 // response callback
361 function(response)
362 {
363 if (response.deferred)
364 deferredMessages.push([message, responseCallback]);
365 else if (responseCallback)
366 responseCallback(response.payload);
367 },
368 // message dispatcher
369 safari.self.tab,
370 // response event target
371 safari.self,
372 // extra data
344 { 373 {
345 documentUrl: document.location.href, 374 documentUrl: document.location.href,
346 isTopLevel: window == window.top 375 isTopLevel: window == window.top,
376 isDocumentHidden: document.hidden
347 } 377 }
348 ); 378 );
349 }, 379 },
350 getWindow: function() 380 getWindow: function()
351 { 381 {
352 return proxy.getObject(0); 382 return proxy.getObject(0);
353 } 383 }
354 }; 384 };
355 385
356 ext.onMessage = new ContentMessageEventTarget(); 386 ext.onMessage = new ContentMessageEventTarget();
357 387
358 388
389 // Starting with Safari 7.0 content scripts also run in preloaded pages.
390 // However our high-level code doesn't know anything about preloaded pages
391 // and expects only one page running in each tab. So we have to defer
392 // all requests to the background page, until the preloaded page is shown.
393
394 var deferredMessages = [];
395 var deferredBeforeLoadEvents = [];
396
397 document.addEventListener("visibilitychange", function()
398 {
399 if (document.hidden)
400 return;
401
402 // notify background page, that it can catch up on onLoading events
403 if (window == window.top)
404 safari.self.tab.dispatchMessage("show");
405
406 // catch up on deferred messages
407 for (var i = 0; i < deferredMessages.length; i++)
408 ext.backgroundPage.sendMessage.apply(null, deferredMessages[i]);
409 deferredMessages = [];
410
411 // catch up on deferred resource blocking
412 for (var i = 0; i < deferredBeforeLoadEvents.length; i++)
413 {
414 var result = canLoad(deferredBeforeLoadEvents[i]);
415 if (result == "blocked")
416 {
417 // if at least one script was supposed to be blocked,
418 // we have to reload the preloaded page. Executed scripts
419 // won't undo themselves by removing them from the DOM
420 var element = deferredBeforeLoadEvents[i].target;
421 if (element.localName == "script")
422 {
423 document.documentElement.style.display = "none";
424 document.location.reload();
425 return;
426 }
427
428 // other resources that were supposed to be blocked are replaced
429 // with an identical clone, in order to trigger "beforeload" again
430 if (element.parentNode)
431 {
432 var clone = element.cloneNode(true);
433 clone._blocked = true; // remember that this should be blocked,
434 // to avoid checking twice
435 element.parentNode.replaceChild(clone, element);
436 }
437 }
438 }
439 deferredBeforeLoadEvents = [];
440 });
441
442
359 // Safari does not pass the element which the context menu is refering to 443 // 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. 444 // so we need to add it to the event's user info.
361 document.addEventListener("contextmenu", function(event) 445 document.addEventListener("contextmenu", function(event)
362 { 446 {
363 var element = event.srcElement; 447 var element = event.srcElement;
364 safari.self.tab.setContextMenuEventUserInfo(event, { 448 safari.self.tab.setContextMenuEventUserInfo(event, {
365 srcUrl: ("src" in element) ? element.src : null, 449 srcUrl: ("src" in element) ? element.src : null,
366 tagName: element.localName 450 tagName: element.localName
367 }); 451 });
368 }, false); 452 }, false);
369 })(); 453 })();
OLDNEW
« safari/ext/background.js ('K') | « safari/ext/common.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld