Index: chrome/background.js |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/chrome/background.js |
@@ -0,0 +1,286 @@ |
+(function() { |
Felix Dahlke
2013/10/24 16:30:34
Opening braces should go on their own line, likewi
|
+ /* Events */ |
+ |
+ var TabEventTarget = function() { |
+ WrappedEventTarget.apply(this, arguments); |
+ |
+ this._tabs = {}; |
+ |
+ this._sharedListener = this._sharedListener.bind(this); |
+ this._removeTab = this._removeTab.bind(this); |
+ }; |
+ TabEventTarget.prototype = { |
+ __proto__: WrappedEventTarget.prototype, |
+ _bindToTab: function(tab) { |
+ return { |
+ addListener: function(listener) { |
+ var listeners = this._tabs[tab._id]; |
+ |
+ if (!listeners) { |
+ this._tabs[tab._id] = listeners = []; |
+ |
+ if (Object.keys(this._tabs).length == 1) |
+ this.addListener(this._sharedListener); |
+ |
+ tab.onRemoved.addListener(this._removeTab); |
+ } |
+ |
+ listeners.push(listener); |
+ }.bind(this), |
+ removeListener: function(listener) { |
+ var listeners = this._tabs[tab._id]; |
+ if (!listeners) |
+ return; |
+ |
+ var idx = listeners.indexOf(listener); |
+ if (idx == -1) |
+ return; |
+ |
+ listeners.splice(idx, 1); |
+ |
+ if (listeners.length == 0) |
+ tab.onRemoved.removeListener(this._removeTab); |
+ else { |
+ if (listeners.length > 1) |
+ return; |
+ if (listeners[0] != this._removeTab) |
+ return; |
+ } |
+ |
+ this._removeTab(tab); |
+ }.bind(this) |
+ }; |
+ }, |
+ _sharedListener: function(tab) { |
+ var listeners = this._tabs[tab._id]; |
+ |
+ if (!listeners) |
+ return; |
+ |
+ // copy listeners before calling them, because of they |
Felix Dahlke
2013/10/24 16:30:34
s/because of/because/
|
+ // might add or remove other listeners, which must not be |
+ // taken into account before the next occurrance of the event |
Felix Dahlke
2013/10/24 16:30:34
s/occureace/occurence/
|
+ listeners = listeners.slice(0); |
+ |
+ for (var i = 0; i < listeners.length; i++) |
+ listeners[i](tab); |
+ }, |
+ _removeTab: function(tab) { |
+ delete this._tabs[tab._id]; |
+ |
+ if (Object.keys(this._tabs).length == 0) |
+ this.removeListener(this._sharedListener); |
+ } |
+ }; |
+ |
+ var BeforeNavigateTabEventTarget = function() { |
+ TabEventTarget.call(this, chrome.tabs.onUpdated); |
+ }; |
+ BeforeNavigateTabEventTarget.prototype = { |
+ __proto__: TabEventTarget.prototype, |
+ _wrapListener: function(listener) { |
+ return function(id, info, tab) { |
+ if ("url" in info) |
+ listener(new Tab(tab)); |
+ }; |
+ } |
+ }; |
+ |
+ var CompletedTabEventTarget = function() { |
+ TabEventTarget.call(this, chrome.tabs.onUpdated); |
+ }; |
+ CompletedTabEventTarget.prototype = { |
+ __proto__: TabEventTarget.prototype, |
+ _wrapListener: function(listener) { |
+ return function(id, info, tab) { |
+ if (info.status == "complete") |
+ listener(new Tab(tab)); |
+ }; |
+ } |
+ }; |
+ |
+ var ActivatedTabEventTarget = function() { |
+ TabEventTarget.call(this, chrome.tabs.onActivated); |
+ }; |
+ ActivatedTabEventTarget.prototype = { |
+ __proto__: TabEventTarget.prototype, |
+ _wrapListener: function(listener) { |
+ return function(info) { |
+ chrome.tabs.get(info.tabId, function(tab) { |
+ listener(new Tab(tab)); |
+ }); |
+ }; |
+ } |
+ } |
+ |
+ var RemovedTabEventTarget = function() { |
+ TabEventTarget.call(this, chrome.tabs.onRemoved); |
+ }; |
+ RemovedTabEventTarget.prototype = { |
+ __proto__: TabEventTarget.prototype, |
+ _wrapListener: function(listener) { |
+ return function(id) { |
+ listener(new Tab({id: id})); |
+ }; |
+ } |
+ }; |
+ |
+ var BeforeRequestEventTarget = function() { |
+ WrappedEventTarget.call(this, chrome.webRequest.onBeforeRequest); |
+ }; |
+ BeforeRequestEventTarget.prototype = { |
+ __proto__: WrappedEventTarget.prototype, |
+ _wrapListener: function(listener) { |
+ return function(details) { |
+ var tab = null; |
+ |
+ if (details.tabId != -1) |
+ tab = new Tab({id: details.tabId}); |
+ |
+ return {cancel: listener( |
+ details.url, |
+ details.type, |
+ tab, |
+ details.frameId, |
+ details.parentFrameId |
+ ) === false}; |
+ }; |
+ }, |
+ _prepareExtraArguments: function(urls) { |
+ return [urls ? {urls: urls} : {}, ["blocking"]]; |
+ } |
+ }; |
+ |
+ |
+ /* Tabs */ |
+ |
+ var PageAction = function(tabId) { |
+ this._tabId = tabId; |
+ }; |
+ PageAction.prototype = { |
+ setIcon: function(path) { |
+ chrome.pageAction.setIcon({tabId: this._tabId, path: path}); |
+ }, |
+ setTitle: function(title) { |
+ chrome.pageAction.setTitle({tabId: this._tabId, title: title}); |
+ }, |
+ hide: function() { |
+ chrome.pageAction.hide(this._tabId); |
+ }, |
+ show: function() { |
+ chrome.pageAction.show(this._tabId); |
+ } |
+ }; |
+ |
+ Tab = function(tab) { |
+ this._id = tab.id; |
+ |
+ this.url = tab.url; |
+ this.pageAction = new PageAction(tab.id); |
+ |
+ this.onBeforeNavigate = ext.tabs.onBeforeNavigate._bindToTab(this); |
+ this.onCompleted = ext.tabs.onCompleted._bindToTab(this); |
+ this.onActivated = ext.tabs.onActivated._bindToTab(this); |
+ this.onRemoved = ext.tabs.onRemoved._bindToTab(this); |
+ }; |
+ Tab.prototype = { |
+ close: function() { |
+ chrome.tabs.remove(this._id); |
+ }, |
+ activate: function() { |
+ chrome.tabs.update(this._id, {selected: true}); |
+ }, |
+ sendMessage: function(message, responseCallback) { |
+ chrome.tabs.sendMessage(this._id, message, responseCallback); |
+ } |
+ }; |
+ |
+ TabMap = function() { |
+ this._map = {}; |
+ this.delete = this.delete.bind(this); |
+ }; |
+ TabMap.prototype = { |
+ get: function(tab) { |
+ return (this._map[tab._id] || {}).value; |
+ }, |
+ set: function(tab, value) { |
+ if (!(tab._id in this._map)) |
+ tab.onRemoved.addListener(this.delete); |
+ |
+ this._map[tab._id] = {tab: tab, value: value}; |
+ }, |
+ has: function(tab) { |
+ return tab._id in this._map; |
+ }, |
+ clear: function() { |
+ for (var id in this._map) |
+ this.delete(this._map[id].tab); |
+ } |
+ }; |
+ TabMap.prototype["delete"] = function(tab) { |
+ delete this._map[tab._id]; |
+ tab.onRemoved.removeListener(this.delete); |
+ }; |
+ |
+ |
+ /* Windows */ |
+ |
+ Window = function(win) { |
+ this._id = win.id; |
+ this.visible = win.status != "minimized"; |
+ }; |
+ Window.prototype = { |
+ getAllTabs: function(callback) { |
+ chrome.tabs.query({windowId: this._id}, function(tabs) { |
+ callback(tabs.map(function(tab) { |
+ return new Tab(tab); |
+ })); |
+ }); |
+ }, |
+ getActiveTab: function(callback) { |
+ chrome.tabs.query({windowId: this._id, active: true}, function(tabs) { |
+ callback(new Tab(tabs[0])); |
+ }); |
+ }, |
+ openTab: function(url, callback) { |
+ var props = {windowId: this._id, url: url}; |
+ |
+ if (!callback) |
+ chrome.tabs.create(props); |
+ else |
+ chrome.tabs.create(props, function(tab) { |
+ callback(new Tab(tab)); |
+ }); |
+ } |
+ }; |
+ |
+ |
+ /* API */ |
+ |
+ ext.windows = { |
+ getAll: function(callback) { |
+ chrome.windows.getAll(function(windows) { |
+ callback(windows.map(function(win) { |
+ return new Window(win); |
+ })); |
+ }); |
+ }, |
+ getLastFocused: function(callback) { |
+ chrome.windows.getLastFocused(function(win) { |
+ callback(new Window(win)); |
+ }); |
+ } |
+ }; |
+ |
+ ext.tabs = { |
+ onBeforeNavigate: new BeforeNavigateTabEventTarget(), |
+ onCompleted: new CompletedTabEventTarget(), |
+ onActivated: new ActivatedTabEventTarget(), |
+ onRemoved: new RemovedTabEventTarget() |
+ }; |
+ |
+ ext.webRequest = { |
+ onBeforeRequest: new BeforeRequestEventTarget(), |
+ handlerBehaviorChanged: chrome.webRequest.handlerBehaviorChanged |
+ }; |
+})(); |