| 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; |
| + } |
| + } |
| + } |
| + }); |
| })(); |