| Index: safari/ext/content.js | 
| =================================================================== | 
| --- a/safari/ext/content.js | 
| +++ b/safari/ext/content.js | 
| @@ -18,7 +18,7 @@ | 
| (function() | 
| { | 
| if (window == window.top) | 
| - safari.self.tab.dispatchMessage("loading"); | 
| + safari.self.tab.dispatchMessage("loading", {isDocumentHidden: document.hidden}); | 
| /* Events */ | 
| @@ -36,6 +36,14 @@ | 
| _getSenderDetails: function(event) | 
| { | 
| return {}; | 
| + }, | 
| + _ignoreIf: function(event) | 
| + { | 
| + // If the current page is a preloaded page in Safari 7.0, we have to | 
| + // ignore all messages sent from the background page. The high-level code | 
| + // doesn't know anything about preloaded pages and expects messages to be | 
| + // handled only by the current visible page. | 
| + return document.hidden && event.message.isTabVisible; | 
| } | 
| }; | 
| @@ -279,11 +287,10 @@ | 
| /* Web request blocking */ | 
| - document.addEventListener("beforeload", function(event) | 
| + var canLoad = function(event) | 
| { | 
| var type; | 
| - | 
| - switch(event.target.localName) | 
| + switch (event.target.localName) | 
| { | 
| case "frame": | 
| case "iframe": | 
| @@ -309,17 +316,24 @@ | 
| type = "other"; | 
| } | 
| - if (!safari.self.tab.canLoad( | 
| + return safari.self.tab.canLoad( | 
| event, { | 
| type: "webRequest", | 
| payload: { | 
| url: event.url, | 
| type: type, | 
| documentUrl: document.location.href, | 
| - isTopLevel: window == window.top | 
| + isTopLevel: window == window.top, | 
| + isDocumentHidden: document.hidden | 
| } | 
| } | 
| - )) | 
| + ); | 
| + }; | 
| + | 
| + document.addEventListener("beforeload", function(event) | 
| + { | 
| + var result; | 
| + if (event.target._blocked || (result = canLoad(event)) == "blocked") | 
| { | 
| event.preventDefault(); | 
| @@ -327,9 +341,11 @@ | 
| // element from loading by cancelling the "beforeload" event. So we have | 
| // to dispatch it manually. Otherwise element collapsing wouldn't work. | 
| var evt = document.createEvent("Event"); | 
| - evt.initEvent(type == "sub_frame" ? "load" : "error"); | 
| + evt.initEvent(/^i?frame$/.test(event.target.localName) ? "load" : "error"); | 
| event.target.dispatchEvent(evt); | 
| } | 
| + else if (result == "deferred") | 
| + deferredBeforeLoadEvents.push(event); | 
| }, true); | 
| @@ -339,11 +355,25 @@ | 
| sendMessage: function(message, responseCallback) | 
| { | 
| _sendMessage( | 
| - message, responseCallback, | 
| - safari.self.tab, safari.self, | 
| + // message payload | 
| + message, | 
| + // response callback | 
| + function(response) | 
| + { | 
| + if (response.deferred) | 
| + deferredMessages.push([message, responseCallback]); | 
| + else if (responseCallback) | 
| + responseCallback(response.payload); | 
| + }, | 
| + // message dispatcher | 
| + safari.self.tab, | 
| + // response event target | 
| + safari.self, | 
| + // extra data | 
| { | 
| documentUrl: document.location.href, | 
| - isTopLevel: window == window.top | 
| + isTopLevel: window == window.top, | 
| + isDocumentHidden: document.hidden | 
| } | 
| ); | 
| }, | 
| @@ -356,6 +386,60 @@ | 
| ext.onMessage = new ContentMessageEventTarget(); | 
| + // Starting with Safari 7.0 content scripts also run in preloaded pages. | 
| + // However our high-level code doesn't know anything about preloaded pages | 
| + // and expects only one page running in each tab. So we have to defer | 
| + // all requests to the background page, until the preloaded page is shown. | 
| + | 
| + var deferredMessages = []; | 
| + var deferredBeforeLoadEvents = []; | 
| + | 
| + document.addEventListener("visibilitychange", function() | 
| + { | 
| + if (document.hidden) | 
| + return; | 
| + | 
| + // notify background page, that it can catch up on onLoading events | 
| + if (window == window.top) | 
| + safari.self.tab.dispatchMessage("show"); | 
| + | 
| + // catch up on deferred messages | 
| + for (var i = 0; i < deferredMessages.length; i++) | 
| + ext.backgroundPage.sendMessage.apply(null, deferredMessages[i]); | 
| + deferredMessages = []; | 
| + | 
| + // catch up on deferred resource blocking | 
| + for (var i = 0; i < deferredBeforeLoadEvents.length; i++) | 
| + { | 
| + var result = canLoad(deferredBeforeLoadEvents[i]); | 
| + if (result == "blocked") | 
| + { | 
| + // if at least one script was supposed to be blocked, | 
| + // we have to reload the preloaded page. Executed scripts | 
| + // won't undo themselves by removing them from the DOM | 
| + var element = deferredBeforeLoadEvents[i].target; | 
| + if (element.localName == "script") | 
| + { | 
| + document.documentElement.style.display = "none"; | 
| + document.location.reload(); | 
| + return; | 
| + } | 
| + | 
| + // other resources that were supposed to be blocked are replaced | 
| + // with an identical clone, in order to trigger "beforeload" again | 
| + if (element.parentNode) | 
| + { | 
| + var clone = element.cloneNode(true); | 
| + clone._blocked = true; // remember that this should be blocked, | 
| + // to avoid checking twice | 
| + element.parentNode.replaceChild(clone, element); | 
| + } | 
| + } | 
| + } | 
| + deferredBeforeLoadEvents = []; | 
| + }); | 
| + | 
| + | 
| // Safari does not pass the element which the context menu is refering to | 
| // so we need to add it to the event's user info. | 
| document.addEventListener("contextmenu", function(event) |