Index: safari/ext/content.js |
=================================================================== |
--- a/safari/ext/content.js |
+++ b/safari/ext/content.js |
@@ -22,39 +22,125 @@ |
if (!("safari" in window)) |
window.safari = window.parent.safari; |
- if (window == window.top) |
- safari.self.tab.dispatchMessage("loading"); |
+ /* Intialization */ |
- /* Events */ |
+ var beforeLoadEvent = document.createEvent("Event"); |
+ beforeLoadEvent.initEvent("beforeload"); |
- var ContentMessageEventTarget = function() |
+ var isTopLevel = window == window.top; |
+ var isPrerendered = document.visibilityState == "prerender"; |
+ |
+ var documentInfo = safari.self.tab.canLoad( |
+ beforeLoadEvent, |
+ { |
+ category: "loading", |
+ url: document.location.href, |
+ referrer: document.referrer, |
+ isTopLevel: isTopLevel, |
+ isPrerendered: isPrerendered |
+ } |
+ ); |
+ |
+ if (isTopLevel && isPrerendered) |
{ |
- MessageEventTarget.call(this, safari.self); |
- }; |
- ContentMessageEventTarget.prototype = { |
- __proto__: MessageEventTarget.prototype, |
- _getResponseDispatcher: function(event) |
+ var onVisibilitychange = function() |
{ |
- return event.target.tab; |
- }, |
- _getSenderDetails: function(event) |
+ safari.self.tab.dispatchMessage("replaced", {pageId: documentInfo.pageId}); |
+ document.removeEventListener("visibilitychange", onVisibilitychange); |
+ }; |
+ document.addEventListener("visibilitychange", onVisibilitychange); |
+ } |
+ |
+ |
+ /* Web requests */ |
+ |
+ document.addEventListener("beforeload", function(event) |
+ { |
+ // we don't block non-HTTP requests anyway, so we can bail out |
+ // without asking the background page. This is even necessary |
+ // because passing large data (like a photo encoded as data: URL) |
+ // to the background page, freezes Safari. |
+ if (!/^https?:/.test(event.url)) |
+ return; |
+ |
+ var type; |
+ switch(event.target.localName) |
{ |
- return {}; |
+ case "frame": |
+ case "iframe": |
+ type = "sub_frame"; |
+ break; |
+ case "img": |
+ type = "image"; |
+ break; |
+ case "object": |
+ case "embed": |
+ type = "object"; |
+ break; |
+ case "script": |
+ type = "script"; |
+ break; |
+ case "link": |
+ if (/\bstylesheet\b/i.test(event.target.rel)) |
+ { |
+ type = "stylesheet"; |
+ break; |
+ } |
+ default: |
+ type = "other"; |
} |
- }; |
+ if (!safari.self.tab.canLoad( |
+ event, { |
+ category: "webRequest", |
+ url: event.url, |
+ type: type, |
+ pageId: documentInfo.pageId, |
+ frameId: documentInfo.frameId |
+ } |
+ )) |
+ { |
+ event.preventDefault(); |
- /* Background page proxy */ |
- var proxy = { |
+ // Safari doesn't dispatch an "error" event when preventing an element |
+ // from loading by cancelling the "beforeload" event. So we have to |
+ // dispatch it manually. Otherwise element collapsing wouldn't work. |
+ if (type != "sub_frame") |
+ { |
+ var evt = document.createEvent("Event"); |
+ evt.initEvent("error"); |
+ event.target.dispatchEvent(evt); |
+ } |
+ } |
+ }, true); |
+ |
+ |
+ /* Context menus */ |
+ |
+ document.addEventListener("contextmenu", function(event) |
+ { |
+ var element = event.srcElement; |
+ safari.self.tab.setContextMenuEventUserInfo(event, { |
+ pageId: documentInfo.pageId, |
+ srcUrl: ("src" in element) ? element.src : null, |
+ tagName: element.localName |
+ }); |
+ }); |
+ |
+ |
+ /* Background page */ |
+ |
+ var backgroundPageProxy = { |
objects: [], |
callbacks: [], |
send: function(message) |
{ |
- var evt = document.createEvent("Event"); |
- evt.initEvent("beforeload"); |
- return safari.self.tab.canLoad(evt, {type: "proxy", payload: message}); |
+ message.category = "proxy"; |
+ message.pageId = documentInfo.pageId; |
+ |
+ return safari.self.tab.canLoad(beforeLoadEvent, message); |
}, |
checkResult: function(result) |
{ |
@@ -75,23 +161,10 @@ |
if (typeof obj == "function") |
{ |
var callbackId = this.callbacks.indexOf(obj); |
- |
if (callbackId == -1) |
- { |
callbackId = this.callbacks.push(obj) - 1; |
- safari.self.addEventListener("message", function(event) |
- { |
- if (event.name == "proxyCallback") |
- if (event.message.callbackId == callbackId) |
- obj.apply( |
- this.getObject(event.message.contextId), |
- this.deserializeSequence(event.message.args) |
- ); |
- }.bind(this)); |
- } |
- |
- return {type: "callback", callbackId: callbackId}; |
+ return {type: "callback", callbackId: callbackId, frameId: documentInfo.frameId}; |
} |
if (typeof obj == "object" && |
@@ -226,7 +299,15 @@ |
); |
}; |
}, |
- getObject: function(objectId) { |
+ handleCallback: function(message) |
+ { |
+ this.callbacks[message.callbackId].apply( |
+ this.getObject(message.contextId), |
+ this.deserializeSequence(message.args) |
+ ); |
+ }, |
+ getObject: function(objectId) |
+ { |
var objectInfo = this.send({ |
type: "inspectObject", |
objectId: objectId |
@@ -281,104 +362,44 @@ |
} |
}; |
- |
- /* Web request blocking */ |
- |
- document.addEventListener("beforeload", function(event) |
- { |
- // we don't block non-HTTP requests anyway, so we can bail out |
- // without asking the background page. This is even necessary |
- // because passing large data (like a photo encoded as data: URL) |
- // to the background page, freezes Safari. |
- if (!/^https?:/.test(event.url)) |
- return; |
- |
- var type; |
- |
- switch(event.target.localName) |
- { |
- case "frame": |
- case "iframe": |
- type = "sub_frame"; |
- break; |
- case "img": |
- type = "image"; |
- break; |
- case "object": |
- case "embed": |
- type = "object"; |
- break; |
- case "script": |
- type = "script"; |
- break; |
- case "link": |
- if (/\bstylesheet\b/i.test(event.target.rel)) |
- { |
- type = "stylesheet"; |
- break; |
- } |
- default: |
- type = "other"; |
- } |
- |
- if (!safari.self.tab.canLoad( |
- event, { |
- type: "webRequest", |
- payload: { |
- url: event.url, |
- type: type, |
- documentUrl: document.location.href, |
- isTopLevel: window == window.top |
- } |
- } |
- )) |
- { |
- event.preventDefault(); |
- |
- // Safari doesn't dispatch an "error" event when preventing an element |
- // from loading by cancelling the "beforeload" event. So we have to |
- // dispatch it manually. Otherwise element collapsing wouldn't work. |
- if (type != "sub_frame") |
- { |
- var evt = document.createEvent("Event"); |
- evt.initEvent("error"); |
- event.target.dispatchEvent(evt); |
- } |
- } |
- }, true); |
- |
- |
- /* API */ |
- |
ext.backgroundPage = { |
sendMessage: function(message, responseCallback) |
{ |
- _sendMessage( |
- message, responseCallback, |
- safari.self.tab, safari.self, |
- { |
- documentUrl: document.location.href, |
- isTopLevel: window == window.top |
- } |
- ); |
+ messageProxy.sendMessage(message, responseCallback, documentInfo); |
}, |
getWindow: function() |
{ |
- return proxy.getObject(0); |
+ return backgroundPageProxy.getObject(0); |
} |
}; |
- ext.onMessage = new ContentMessageEventTarget(); |
+ /* Message processing */ |
- // 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) |
+ var messageProxy = new ext._MessageProxy(safari.self.tab); |
+ |
+ safari.self.addEventListener("message", function(event) |
{ |
- var element = event.srcElement; |
- safari.self.tab.setContextMenuEventUserInfo(event, { |
- srcUrl: ("src" in element) ? element.src : null, |
- tagName: element.localName |
- }); |
- }, false); |
+ if (event.message.pageId == documentInfo.pageId) |
+ { |
+ if (event.name == "request") |
+ { |
+ messageProxy.handleRequest(event.message, {}); |
+ return; |
+ } |
+ |
+ if (event.message.frameId == documentInfo.frameId) |
+ { |
+ switch (event.name) |
+ { |
+ case "response": |
+ messageProxy.handleResponse(event.message); |
+ break; |
+ case "proxyCallback": |
+ backgroundPageProxy.handleCallback(event.message); |
+ break; |
+ } |
+ } |
+ } |
+ }); |
})(); |