Index: safari/ext/background.js |
=================================================================== |
--- a/safari/ext/background.js |
+++ b/safari/ext/background.js |
@@ -17,7 +17,7 @@ |
(function() |
{ |
- /* Tabs */ |
+ /* Events */ |
var TabEventTarget = function() |
{ |
@@ -51,16 +51,40 @@ |
} |
}; |
+ var BackgroundMessageEventTarget = function() |
+ { |
+ MessageEventTarget.call(this, safari.application); |
+ }; |
+ BackgroundMessageEventTarget.prototype = { |
+ __proto__: MessageEventTarget.prototype, |
+ _getResponseDispatcher: function(event) |
+ { |
+ return event.target.page; |
+ }, |
+ _getSenderDetails: function(event) |
+ { |
+ return { |
+ tab: new Tab(event.target), |
+ frame: new Frame( |
+ event.message.documentUrl, |
+ event.message.isTopLevel, |
+ event.target |
+ ) |
+ }; |
+ } |
+ }; |
+ |
+ |
+ /* Tabs */ |
+ |
Tab = function(tab) |
{ |
this._tab = tab; |
- this._eventTarget = tab; |
- this._messageDispatcher = tab.page; |
- |
this.browserAction = new BrowserAction(this); |
this.onLoading = new LoadingTabEventTarget(tab); |
+ this.onBeforeNavigate = new TabEventTarget(tab, "beforeNavigate", false); |
this.onCompleted = new TabEventTarget(tab, "navigate", false); |
this.onActivated = new TabEventTarget(tab, "activate", false); |
this.onRemoved = new TabEventTarget(tab, "close", false); |
@@ -78,15 +102,22 @@ |
{ |
this._tab.activate(); |
}, |
- sendMessage: sendMessage |
+ sendMessage: function(message, responseCallback) |
+ { |
+ _sendMessage( |
+ message, responseCallback, |
+ this._tab.page, this._tab |
+ ); |
+ } |
}; |
- TabMap = function() |
+ TabMap = function(deleteTabOnBeforeNavigate) |
{ |
this._tabs = []; |
this._values = []; |
- this._onClosed = this._onClosed.bind(this); |
+ this._deleteOnEvent = this._deleteOnEvent.bind(this); |
+ this._deleteTabOnBeforeNavigate = deleteTabOnBeforeNavigate; |
}; |
TabMap.prototype = |
{ |
@@ -109,7 +140,9 @@ |
this._tabs.push(tab._tab); |
this._values.push(value); |
- tab._tab.addEventListener("close", this._onClosed, false); |
+ tab._tab.addEventListener("close", this._deleteOnEvent, false); |
+ if (this._deleteTabOnBeforeNavigate) |
+ tab._tab.addEventListener("beforeNavigate", this._deleteOnEvent, false); |
} |
}, |
has: function(tab) |
@@ -130,12 +163,14 @@ |
this._tabs.splice(idx, 1); |
this._values.splice(idx, 1); |
- tab.removeEventListener("close", this._onClosed, false); |
+ tab.removeEventListener("close", this._deleteOnEvent, false); |
+ tab.removeEventListener("beforeNavigate", this._deleteOnEvent, false); |
} |
}, |
- _onClosed: function(event) |
+ _deleteOnEvent: function(event) |
{ |
- this._delete(event.target); |
+ // delay so that other event handlers can still look this tab up |
+ setTimeout(this._delete.bind(this, event.target), 0); |
} |
}; |
TabMap.prototype["delete"] = function(tab) |
@@ -145,6 +180,7 @@ |
ext.tabs = { |
onLoading: new LoadingTabEventTarget(safari.application), |
+ onBeforeNavigate: new TabEventTarget(safari.application, "beforeNavigate", true), |
onCompleted: new TabEventTarget(safari.application, "navigate", true), |
onActivated: new TabEventTarget(safari.application, "activate", true), |
onRemoved: new TabEventTarget(safari.application, "close", true) |
@@ -268,6 +304,22 @@ |
}; |
+ /* Frames */ |
+ |
+ Frame = function(url, isTopLevel, tab) |
+ { |
+ this.url = url; |
+ |
+ // there is no way to discover frames with Safari's API. |
+ // so if this isn't the top level frame, assume that the parent is. |
+ // this is the best we can do for Safari. :( |
+ if (!isTopLevel) |
+ this.parent = new Frame(tab.url, true); |
+ else |
+ this.parent = null; |
+ }; |
+ |
+ |
/* Background page proxy */ |
var proxy = { |
@@ -498,47 +550,29 @@ |
ext.webRequest = { |
onBeforeRequest: { |
_listeners: [], |
- _urlPatterns: [], |
- _handleMessage: function(message, tab) |
+ _handleMessage: function(message, rawTab) |
{ |
- tab = new Tab(tab); |
+ var tab = new Tab(rawTab); |
+ var frame = new Frame(message.documentUrl, message.isTopLevel, rawTab); |
for (var i = 0; i < this._listeners.length; i++) |
{ |
- var regex = this._urlPatterns[i]; |
- |
- if ((!regex || regex.test(message.url)) && this._listeners[i](message.url, message.type, tab, 0, -1) === false) |
+ if (this._listeners[i](message.url, message.type, tab, frame) === false) |
return false; |
} |
return true; |
}, |
- addListener: function(listener, urls) |
+ addListener: function(listener) |
{ |
- var regex; |
- |
- if (urls) |
- regex = new RegExp("^(?:" + urls.map(function(url) |
- { |
- return url.split("*").map(function(s) |
- { |
- return s.replace(/([.?+^$[\]\\(){}|-])/g, "\\$1"); |
- }).join(".*"); |
- }).join("|") + ")($|[?#])"); |
- |
this._listeners.push(listener); |
- this._urlPatterns.push(regex); |
}, |
removeListener: function(listener) |
{ |
var idx = this._listeners.indexOf(listener); |
- |
if (idx != -1) |
- { |
this._listeners.splice(idx, 1); |
- this._urlPatterns.splice(idx, 1); |
- } |
} |
}, |
handlerBehaviorChanged: function() {} |
@@ -591,7 +625,7 @@ |
} |
}; |
- ext.onMessage = new MessageEventTarget(safari.application); |
+ ext.onMessage = new BackgroundMessageEventTarget(); |
var contextMenu = []; |
var isContextMenuHidden = false; |