diff --git a/browser/base/content/tabcandy/app/browserwatcher.js b/browser/base/content/tabcandy/app/browserwatcher.js new file mode 100644 index 000000000000..d4c48f308edb --- /dev/null +++ b/browser/base/content/tabcandy/app/browserwatcher.js @@ -0,0 +1,117 @@ +// Title: browserwatcher.js +(function(){ + +Utils.log('warning! browserwatcher.js has not been tested since the code was taken from tabs.js!'); + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +// ########## +// Class: WindowWatcher +function WindowWatcher() { + var self = this; + + var observer = { + observe: function(window, event) { + if (event == "domwindowopened") { + if (self.onWindowOpened) + self.onWindowOpened(window); + } else + if (self.onWindowClosed) + self.onWindowClosed(window); + } + }; + + var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"] + .getService(Ci.nsIWindowWatcher); + ww.registerNotification(observer); + + Extension.addUnloadMethod( + this, + function() { + ww.unregisterNotification(observer); + }); +} + +// ########## +// Class: BrowserWatcher +// When this object is instantiated, the given onLoad() is called for +// all browser windows, and subsequently for all newly-opened browser +// windows. When a browser window closes, onUnload() is called. +// onUnload() is also called once for each browser window when the +// extension is unloaded. +function BrowserWatcher(options) { + var pendingHandlers = []; + + function makeSafeFunc(func) { + function safeFunc(window) { + try { + func(window); + } catch (e) { + Utils.log(e); + } + }; + return safeFunc; + } + + function addUnloader(chromeWindow, func) { + function onUnload() { + chromeWindow.removeEventListener("unload", onUnload, false); + pendingHandlers.splice(pendingHandlers.indexOf(onUnload), 1); + func(chromeWindow); + } + pendingHandlers.push(onUnload); + chromeWindow.addEventListener("unload", onUnload, false); + } + + function loadAndBind(chromeWindow) { + if (options.onLoad) + (makeSafeFunc(options.onLoad))(chromeWindow); + if (options.onUnload) + addUnloader(chromeWindow, makeSafeFunc(options.onUnload)); + } + + var wm = Cc["@mozilla.org/appshell/window-mediator;1"] + .getService(Ci.nsIWindowMediator); + + var enumerator = wm.getEnumerator(XULApp.appWindowType); + + while (enumerator.hasMoreElements()) { + var chromeWindow = enumerator.getNext(); + if (chromeWindow.gIsDoneLoading) + loadAndBind(chromeWindow); + else + onWindowOpened(chromeWindow); + } + + function onWindowOpened(chromeWindow) { + function removeListener() { + chromeWindow.removeEventListener("load", onLoad, false); + pendingHandlers.splice(pendingHandlers.indexOf(removeListener), 1); + } + function onLoad() { + removeListener(); + var type = chromeWindow.document.documentElement + .getAttribute("windowtype"); + if (type == XULApp.appWindowType) + loadAndBind(chromeWindow); + } + chromeWindow.addEventListener("load", onLoad, false); + pendingHandlers.push(removeListener); + } + + var ww = new WindowWatcher(); + ww.onWindowOpened = onWindowOpened; + + Extension.addUnloadMethod( + this, + function() { + ww.unload(); + var handlers = pendingHandlers.slice(); + handlers.forEach(function(handler) { handler(); }); + }); +} + +})(); \ No newline at end of file diff --git a/browser/base/content/tabcandy/app/groups.js b/browser/base/content/tabcandy/app/groups.js index 51852a47ca6a..7f621bdb2d15 100644 --- a/browser/base/content/tabcandy/app/groups.js +++ b/browser/base/content/tabcandy/app/groups.js @@ -162,6 +162,8 @@ window.Group = function(listOfEls, options) { }); } else self.adjustTitleSize(); + + self.save(); } this.$title @@ -276,9 +278,10 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), { save: function() { if (!this._inited) // too soon to save now return; + var data = this.getStorageData(); -/* Utils.log("data to save: " + data.toSource()); */ - Storage.saveGroup(Utils.getCurrentWindow(), data); + if(Groups.groupStorageSanity(data)) + Storage.saveGroup(Utils.getCurrentWindow(), data); }, // ---------- @@ -300,7 +303,6 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), { var css = {width: w}; this.$title.css(css); this.$titleShield.css(css); - this.save(); }, // ---------- @@ -554,15 +556,13 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), { self.remove($el); }); - item.parent = this; + item.setParent(this); if(typeof(item.setResizable) == 'function') item.setResizable(false); if(item.tab == Utils.activeTab) Groups.setActiveGroup(this); - - item.save(); } if(!options.dontArrange) @@ -601,7 +601,7 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), { if(index != -1) this._children.splice(index, 1); - item.parent = null; + item.setParent(null); item.removeClass("tabInGroup"); item.removeClass("stacked"); item.removeClass("stack-trayed"); @@ -627,7 +627,6 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), { var toRemove = $.merge([], this._children); $.each(toRemove, function(index, child) { self.remove(child, {dontArrange: true}); - child.save(); }); }, @@ -861,8 +860,6 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), { this.arrange({z: z + 2}); } - - this.save(); }, // ---------- @@ -931,8 +928,6 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), { this.$resizer.fadeOut(); $(this.container).resizable('disable'); } - - this.save(); }, // ---------- @@ -994,8 +989,6 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), { // actions is necessary for a good user experience. self.onNextNewTab(doNextTab); - - this.save(); }, // ---------- @@ -1283,13 +1276,14 @@ window.Groups = { init: function() { this.groups = []; this.nextID = 1; + this._inited = false; }, // ---------- getNextID: function() { var result = this.nextID; this.nextID++; - Storage.saveGroupsData(Utils.getCurrentWindow(), {nextID:this.nextID}); + this.save(); return result; }, @@ -1303,73 +1297,77 @@ window.Groups = { return data; }, + // ---------- + saveAll: function() { + this.save(); + $.each(this.groups, function(index, group) { + group.save(); + }); + }, + + // ---------- + save: function() { + if (!this._inited) // too soon to save now + return; + + Storage.saveGroupsData(Utils.getCurrentWindow(), {nextID:this.nextID}); + }, + // ---------- reconstitute: function(groupsData, groupData) { try { -/* Utils.log("in reconst"); */ - if(groupsData && groupsData.nextID) - this.nextID = groupsData.nextID; - else { - // Decrement and increment. Increment will trigger a save, and that is - // actually what we want here. - --this.nextID; - this.getNextID(); - } + if(groupsData && groupsData.nextID) + this.nextID = groupsData.nextID; + + if(groupData) { + for (var id in groupData) { + var group = groupData[id]; + if(this.groupStorageSanity(group)) { + var isNewTabsGroup = (group.title == 'New Tabs'); + var options = { + locked: { + close: isNewTabsGroup, + title: isNewTabsGroup + }, + dontPush: true + }; + + new Group([], $.extend({}, group, options)); + } + } + } - if(groupData) { - for (var id in groupData) { -/* - Utils.log("id: " + id); - Utils.log("groupData[id]: "+ groupData[id].toSource()); -*/ - var group = groupData[id]; -/* Utils.log("src: " +group.toSource()); */ - var isNewTabsGroup = (group.title == 'New Tabs'); + var group = this.getNewTabGroup(); + if(!group) { + var box = this.getBoundsForNewTabGroup(); var options = { locked: { - close: isNewTabsGroup, - title: isNewTabsGroup + close: true, + title: true }, - dontPush: true + dontPush: true, + bounds: box, + title: 'New Tabs' }; - - new Group([], $.extend({}, group, options)); - } - } - - var group = this.getNewTabGroup(); - if(!group) { - var box = this.getBoundsForNewTabGroup(); - var options = { - locked: { - close: true, - title: true - }, - dontPush: true, - bounds: box, - title: 'New Tabs' - }; - - new Group([], options); - } + + new Group([], options); + } + + this._inited = true; + this.save(); // for nextID }catch(e){ Utils.log("error in recons: "+e); } }, // ---------- - storageSanity: function(data) { + groupStorageSanity: function(groupData) { // TODO: check everything - if(!data.groups) - return false; - var sane = true; - $.each(data.groups, function(index, group) { - if(!isRect(group.bounds)) { - Utils.log('Groups.storageSanity: bad bounds', group.bounds); - sane = false; - } - }); + if(!isRect(groupData.bounds)) { + Utils.log('Groups.groupStorageSanity: bad bounds', groupData.bounds); + sane = false; + } return sane; }, diff --git a/browser/base/content/tabcandy/app/items.js b/browser/base/content/tabcandy/app/items.js index e351d61a7a4e..a371abf98209 100644 --- a/browser/base/content/tabcandy/app/items.js +++ b/browser/base/content/tabcandy/app/items.js @@ -11,6 +11,7 @@ // close - function() // addOnClose - function(referenceObject, callback) // removeOnClose - function(referenceObject) +// save - function() // // ... and this property: // defaultSize - a Point @@ -73,6 +74,7 @@ window.Item.prototype = { Utils.assert('Subclass must provide close', typeof(this.close) == 'function'); Utils.assert('Subclass must provide addOnClose', typeof(this.addOnClose) == 'function'); Utils.assert('Subclass must provide removeOnClose', typeof(this.removeOnClose) == 'function'); + Utils.assert('Subclass must provide save', typeof(this.save) == 'function'); Utils.assert('Subclass must provide defaultSize', isPoint(this.defaultSize)); Utils.assert('Subclass must provide locked', this.locked); @@ -136,6 +138,7 @@ window.Item.prototype = { setUserSize: function() { Utils.assert('this.bounds', isRect(this.bounds)); this.userSize = new Point(this.bounds.width, this.bounds.height); + this.save(); }, // ---------- @@ -153,6 +156,14 @@ window.Item.prototype = { $(this.container).css({"-moz-transform": value}); }, + // ---------- + // Function: setParent + // + setParent: function(parent) { + this.parent = parent; + this.save(); + }, + // ---------- // Function: pushAway // Pushes all other items away so none overlap this Item. diff --git a/browser/base/content/tabcandy/app/storage.js b/browser/base/content/tabcandy/app/storage.js index 2b0126a30db1..a0df983ecc9a 100644 --- a/browser/base/content/tabcandy/app/storage.js +++ b/browser/base/content/tabcandy/app/storage.js @@ -5,6 +5,7 @@ Storage = { GROUP_DATA_IDENTIFIER: "tabcandy-group", GROUPS_DATA_IDENTIFIER: "tabcandy-groups", TAB_DATA_IDENTIFIER: "tabcandy-tab", + UI_DATA_IDENTIFIER: "tabcandy-ui", // ---------- init: function() { @@ -12,9 +13,31 @@ Storage = { .getService(Components.interfaces.nsISessionStore); }, + // ---------- + wipe: function() { + try { + var win = Utils.getCurrentWindow(); + var self = this; + + // ___ Tabs + Tabs.forEach(function(tab) { + self.saveTab(tab.raw, null); + }); + + // ___ Other + this.saveGroupsData(win, {}); + this.saveUIData(win, {}); + + this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER, + JSON.stringify({})); + } catch (e) { + Utils.log("Error in wipe: "+e); + } + }, + // ---------- saveTab: function(tab, data) { -/* Utils.log("AAAAAAAAAAAAAAAAAAAAAAAAAAA saving tab data, tab: " + tab.toSource() + ", data: "+data.toSource()); */ +/* Utils.log("AAAAAAAAAAAAAAAAAAAAAAAAAAA saving tab data"); */ this._sessionStore.setTabValue(tab, this.TAB_DATA_IDENTIFIER, JSON.stringify(data)); }, @@ -32,6 +55,8 @@ Storage = { // getWindowValue will fail if the property doesn't exist Utils.log("Error in readTabData: "+e); } + +/* Utils.log('tab', existingData); */ return existingData; }, @@ -69,71 +94,48 @@ Storage = { // ---------- saveGroupsData: function(win, data) { -/* Utils.log("GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG " + data.toSource()); */ - this._sessionStore.setWindowValue(win, this.GROUPS_DATA_IDENTIFIER, - JSON.stringify(data)); + this.saveData(win, this.GROUPS_DATA_IDENTIFIER, data); }, // ---------- readGroupsData: function(win) { + return this.readData(win, this.GROUPS_DATA_IDENTIFIER); + }, + + // ---------- + saveUIData: function(win, data) { + this.saveData(win, this.UI_DATA_IDENTIFIER, data); + }, + + // ---------- + readUIData: function(win) { + return this.readData(win, this.UI_DATA_IDENTIFIER); + }, + + // ---------- + saveData: function(win, id, data) { + try { + this._sessionStore.setWindowValue(win, id, JSON.stringify(data)); + } catch (e) { + Utils.log("Error in saveData: "+e); + } + +/* Utils.log('save data', id, data); */ + }, + + // ---------- + readData: function(win, id) { var existingData = {}; try { -/* Utils.log("readGroupsData: "+this._sessionStore.getWindowValue(win, this.GROUPS_DATA_IDENTIFIER)); */ - existingData = JSON.parse( - this._sessionStore.getWindowValue(win, this.GROUPS_DATA_IDENTIFIER) - ); + var data = this._sessionStore.getWindowValue(win, id); + if(data) + existingData = JSON.parse(data); } catch (e) { - // getWindowValue will fail if the property doesn't exist + Utils.log("Error in readData: "+e); } + +/* Utils.log('read data', id, existingData); */ return existingData; - }, - - // ---------- - read: function() { - var data = {}; - var file = this.getFile(); - if(file.exists()) { - var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]. - createInstance(Components.interfaces.nsIFileInputStream); - var cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. - createInstance(Components.interfaces.nsIConverterInputStream); - fstream.init(file, -1, 0, 0); - cstream.init(fstream, "UTF-8", 0, 0); // you can use another encoding here if you wish - - let (str = {}) { - cstream.readString(-1, str); // read the whole file and put it in str.value - if(str.value) - data = JSON.parse(str.value); - } - cstream.close(); // this closes fstream - } - - return data; - }, - - // ---------- - write: function(data) { - var file = this.getFile(); - var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]. - createInstance(Components.interfaces.nsIFileOutputStream); - foStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0); - var str = JSON.stringify(data); - foStream.write(str, str.length); - foStream.close(); - }, - - // ---------- - getFile: function() { - var file = Components.classes["@mozilla.org/file/directory_service;1"]. - getService(Components.interfaces.nsIProperties). - get("ProfD", Components.interfaces.nsIFile); - - file.append('tabcandy'); - if(!file.exists()) - file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0777); - - file.append(Switch.name + '.json'); - return file; } }; diff --git a/browser/base/content/tabcandy/app/tabitems.js b/browser/base/content/tabcandy/app/tabitems.js index f5097c6896c1..623b7a72912c 100644 --- a/browser/base/content/tabcandy/app/tabitems.js +++ b/browser/base/content/tabcandy/app/tabitems.js @@ -13,6 +13,7 @@ window.TabItem = function(container, tab) { this._init(container); + this.reconnected = false; this._hasBeenDrawn = false; this.tab = tab; this.setResizable(true); @@ -21,7 +22,14 @@ window.TabItem = function(container, tab) { var self = this; this.tab.mirror.addOnClose(this, function(who, info) { TabItems.unregister(self); - }); + }); + + this.tab.mirror.addSubscriber(this, 'urlChanged', function(who, info) { + if(!self.reconnected && (info.oldURL == 'about:blank' || !info.oldURL)) + TabItems.reconnect(self); + + self.save(); + }); }; window.TabItem.prototype = $.extend(new Item(), { @@ -37,13 +45,13 @@ window.TabItem.prototype = $.extend(new Item(), { // ---------- save: function() { -/* Utils.log((this.tab ? this.tab.url : ''), this.reconnected); */ try{ if (!("tab" in this) || !("raw" in this.tab) || !this.reconnected) // too soon to save return; + var data = this.getStorageData(); -/* Utils.log("data to save", data); */ - Storage.saveTab(this.tab.raw, data); + if(TabItems.storageSanity(data)) + Storage.saveTab(this.tab.raw, data); }catch(e){ Utils.log("Error in saving tab value: "+e); } @@ -320,20 +328,19 @@ window.TabItems = { if(TabItems.reconnect(item)) reconnected = true; - else if(!tab.url || tab.url == 'about:blank') { - tab.mirror.addSubscriber(item, 'urlChanged', function(who, info) { - Utils.assert('changing away from blank', info.oldURL == 'about:blank' || !info.oldURL); - TabItems.reconnect(item); - who.removeSubscriber(item); - }); - } + else + Groups.newTab(item); } }); /* Utils.log("reconnected: "+reconnected); */ +/* + Utils.log(reconnected, $div.length, !!Groups); if(!reconnected && $div.length == 1 && Groups){ + Utils.log('new tab'); Groups.newTab($div.data('tabItem')); } +*/ // TODO: Figure out this really weird bug? @@ -448,6 +455,14 @@ window.TabItems = { return $(tab).data("tabItem"); }, + // ---------- + saveAll: function() { + var items = this.getItems(); + $.each(items, function(index, item) { + item.save(); + }); + }, + // ---------- reconstitute: function() { var items = this.getItems(); @@ -461,34 +476,26 @@ window.TabItems = { // ---------- storageSanity: function(data) { // TODO: check everything - if(!data.tabs) - return false; - var sane = true; - $.each(data.tabs, function(index, tab) { - if(!isRect(tab.bounds)) { - Utils.log('TabItems.storageSanity: bad bounds', tab.bounds); - sane = false; - } - }); + if(!isRect(data.bounds)) { + Utils.log('TabItems.storageSanity: bad bounds', data.bounds); + sane = false; + } return sane; }, // ---------- reconnect: function(item) { + var found = false; + try{ -/* Utils.log("trying to reconnect"); */ if(item.reconnected) { -/* Utils.log("already done"); */ return true; } - var found = false; - var tab = Storage.getTabData(item.tab.raw); -/* Utils.log("this is our tab", tab, item.tab.url); */ - if (tab) { + if (tab && this.storageSanity(tab)) { if(item.parent) item.parent.remove(item); @@ -499,11 +506,12 @@ window.TabItems = { if(tab.groupID) { var group = Groups.group(tab.groupID); -/* Utils.log("group found: " + group); */ - group.add(item); + if(group) { + group.add(item); - if(item.tab == Utils.activeTab) - Groups.setActiveGroup(item.parent); + if(item.tab == Utils.activeTab) + Groups.setActiveGroup(item.parent); + } } Groups.updateTabBarForActiveGroup(); diff --git a/browser/base/content/tabcandy/app/ui.js b/browser/base/content/tabcandy/app/ui.js index 2bffeb4fb80d..8d5da136e5e9 100644 --- a/browser/base/content/tabcandy/app/ui.js +++ b/browser/base/content/tabcandy/app/ui.js @@ -8,7 +8,7 @@ window.Keys = {meta: false}; Navbar = { // ---------- get el(){ - var win = Utils.activeWindow; + var win = Utils.getCurrentWindow(); if(win) { var navbar = win.gBrowser.ownerDocument.getElementById("navigator-toolbox"); return navbar; @@ -18,7 +18,7 @@ Navbar = { }, get urlBar(){ - var win = Utils.activeWindow; + var win = Utils.getCurrentWindow(); if(win) { var navbar = win.gBrowser.ownerDocument.getElementById("urlbar"); return navbar; @@ -535,7 +535,7 @@ UIClass.prototype = { var me = this; setTimeout(function() { // Marshal event from chrome thread to DOM thread try{ - if(me.contentWindow.location.host == "tabcandy") { + if(me.contentWindow == window) { self.focused = true; Page.hideChrome(); } else { @@ -554,47 +554,30 @@ UIClass.prototype = { }, 1); }); - // ___ Page - Page.init(); - - // ___ Storage - var data = Storage.read(); - var sane = this.storageSanity(data); - if(!sane || data.dataVersion < 2) { - data.groups = null; - data.tabs = null; - data.pageBounds = null; - - if(!sane) - alert('storage data is bad; starting fresh'); - } - - var groupsData = Storage.readGroupsData(Utils.activeWindow); - var groupData = Storage.readGroupData(Utils.activeWindow); - -/* - Utils.log("in UI init"); - Utils.log(data.toSource()); - Utils.log(groupsData.toSource()); - Utils.log(groupData.toSource()); -*/ - Groups.reconstitute(groupsData, groupData); - TabItems.init(); - TabItems.reconstitute(); - $(window).bind('beforeunload', function() { - if(self.initialized) - self.save(); - self.showChrome(); self.tabBar.showAllTabs(); }); + // ___ Page + Page.init(); + + // ___ Storage + var currentWindow = Utils.getCurrentWindow(); + var data = Storage.readUIData(currentWindow); + this.storageSanity(data); + + var groupsData = Storage.readGroupsData(currentWindow); + var groupData = Storage.readGroupData(currentWindow); + Groups.reconstitute(groupsData, groupData); + + TabItems.init(); + TabItems.reconstitute(); + // ___ resizing - data.pageBounds = null; if(data.pageBounds) { this.pageBounds = data.pageBounds; - this.resize(); + this.resize(true); } else this.pageBounds = Items.getPageBounds(); @@ -607,6 +590,7 @@ UIClass.prototype = { // ___ Done this.initialized = true; + this.save(); // for this.pageBounds }catch(e) { Utils.log("Error in UIClass(): " + e); Utils.log(e.fileName); @@ -692,6 +676,7 @@ UIClass.prototype = { }); this.pageBounds = Items.getPageBounds(); + this.save(); }, // ---------- @@ -716,10 +701,21 @@ UIClass.prototype = { code: function() { location.href = '../../index.html'; } + }, { + name: 'code docs', + code: function() { + location.href = '../../doc/index.html'; + } }, { name: 'save', code: function() { - self.save(); + self.saveAll(); + } + }, { + name: 'reset', + code: function() { + Storage.wipe(); + location.href = ''; } }]; @@ -736,21 +732,24 @@ UIClass.prototype = { } }, + // ---------- + saveAll: function() { + this.save(); + Groups.saveAll(); + TabItems.saveAll(); + }, + // ---------- save: function() { - return; + if(!this.initialized) + return; + var data = { - dataVersion: 2, - groups: Groups.getStorageData(), - tabs: TabItems.getStorageData(), - pageBounds: Items.getPageBounds() + pageBounds: this.pageBounds }; -/* Utils.error(this.pageBounds, data.pageBounds); */ if(this.storageSanity(data)) - Storage.write(data); - else - alert('storage data is bad; reverting to previous version'); + Storage.saveUIData(Utils.getCurrentWindow(), data); }, // ---------- @@ -758,17 +757,13 @@ UIClass.prototype = { if($.isEmptyObject(data)) return true; - var sane = true; - sane = sane && typeof(data.dataVersion) == 'number'; - sane = sane && isRect(data.pageBounds); - - if(data.tabs) - sane = sane && TabItems.storageSanity(data.tabs); + if(!isRect(data.pageBounds)) { + Utils.log('UI.storageSanity: bad pageBounds', data.pageBounds); + data.pageBounds = null; + return false; + } - if(data.groups) - sane = sane && Groups.storageSanity(data.groups); - - return sane; + return true; }, // ---------- diff --git a/browser/base/content/tabcandy/core/tabs.js b/browser/base/content/tabcandy/core/tabs.js index 7ed194ab38a2..fba76b30ebd8 100644 --- a/browser/base/content/tabcandy/core/tabs.js +++ b/browser/base/content/tabcandy/core/tabs.js @@ -1,13 +1,14 @@ // Title: tabs.js (function(){ - const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; - +// ########## +// Class: XULApp +// Singelton var XULApp = { appWindowType: "navigator:browser", tabStripForWindow: function(aWindow) { @@ -30,7 +31,8 @@ var XULApp = { } }; - +// ########## +// Class: Dictionary function Dictionary() { var keys = []; var values = []; @@ -69,6 +71,8 @@ function Dictionary() { this.__defineGetter__("length", function() { return keys.length; }); } +// ########## +// Class: ImmutableArray function ImmutableArray(baseArray) { var self = this; var UNSUPPORTED_MUTATOR_METHODS = ["pop", "push", "reverse", "shift", @@ -86,117 +90,9 @@ function ImmutableArray(baseArray) { self.__proto__ = baseArray; } -function WindowWatcher() { - var self = this; - - var observer = { - observe: function(window, event) { - if (event == "domwindowopened") { - if (self.onWindowOpened) - self.onWindowOpened(window); - } else - if (self.onWindowClosed) - self.onWindowClosed(window); - } - }; - - var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"] - .getService(Ci.nsIWindowWatcher); - ww.registerNotification(observer); - - Extension.addUnloadMethod( - this, - function() { - ww.unregisterNotification(observer); - }); -} - -// When this object is instantiated, the given onLoad() is called for -// all browser windows, and subsequently for all newly-opened browser -// windows. When a browser window closes, onUnload() is called. -// onUnload() is also called once for each browser window when the -// extension is unloaded. - -function BrowserWatcher(options) { - var pendingHandlers = []; - - function makeSafeFunc(func) { - function safeFunc(window) { - try { - func(window); - } catch (e) { - Utils.log(e); - } - }; - return safeFunc; - } - - function addUnloader(chromeWindow, func) { - function onUnload() { - chromeWindow.removeEventListener("unload", onUnload, false); - pendingHandlers.splice(pendingHandlers.indexOf(onUnload), 1); - func(chromeWindow); - } - pendingHandlers.push(onUnload); - chromeWindow.addEventListener("unload", onUnload, false); - } - - function loadAndBind(chromeWindow) { - if (options.onLoad) - (makeSafeFunc(options.onLoad))(chromeWindow); - if (options.onUnload) - addUnloader(chromeWindow, makeSafeFunc(options.onUnload)); - } - - var wm = Cc["@mozilla.org/appshell/window-mediator;1"] - .getService(Ci.nsIWindowMediator); - - var enumerator = wm.getEnumerator(XULApp.appWindowType); - - while (enumerator.hasMoreElements()) { - var chromeWindow = enumerator.getNext(); - if (chromeWindow.gIsDoneLoading) - loadAndBind(chromeWindow); - else - onWindowOpened(chromeWindow); - } - - function onWindowOpened(chromeWindow) { - function removeListener() { - chromeWindow.removeEventListener("load", onLoad, false); - pendingHandlers.splice(pendingHandlers.indexOf(removeListener), 1); - } - function onLoad() { - removeListener(); - var type = chromeWindow.document.documentElement - .getAttribute("windowtype"); - if (type == XULApp.appWindowType) - loadAndBind(chromeWindow); - } - chromeWindow.addEventListener("load", onLoad, false); - pendingHandlers.push(removeListener); - } - - var ww = new WindowWatcher(); - ww.onWindowOpened = onWindowOpened; - - Extension.addUnloadMethod( - this, - function() { - ww.unload(); - var handlers = pendingHandlers.slice(); - handlers.forEach(function(handler) { handler(); }); - }); -} - - - - - - - - - +// ########## +// Class: Extension +// Singleton var Extension = { // === {{{Extension.addUnloadMethod()}}} === // @@ -220,14 +116,8 @@ var Extension = { } }; - - - - - - - - +// ########## +// Class: EventListenerMixIns function EventListenerMixIns(mixInto) { var mixIns = {}; @@ -256,6 +146,8 @@ function EventListenerMixIns(mixInto) { }); } +// ########## +// Class: EventListenerMixIn function EventListenerMixIn(options) { var listeners = []; @@ -577,30 +469,13 @@ window.TabsManager = $.extend(new Subscribable(), { } }; } - -/* - var browserWatcher = new BrowserWatcher( - {onLoad: function(chromeWindow) { - var trackedWindow = trackedWindows.get(chromeWindow); - if (!trackedWindow) - trackedWindows.set(chromeWindow, - new BrowserWindow(chromeWindow)); - }, - onUnload: function(chromeWindow) { - var browserWindow = trackedWindows.get(chromeWindow); - trackedWindows.remove(chromeWindow); - browserWindow.unload(); - } - }); -*/ - + this.__defineGetter__("tabs", function() { return tabs; }); Extension.addUnloadMethod( this, function() { tabsMixIns.unload(); -/* browserWatcher.unload(); */ }); window.Tabs = tabs; diff --git a/content/candies/revision-a/index.html b/content/candies/revision-a/index.html index e728c3fcdd6d..395d56622372 100644 --- a/content/candies/revision-a/index.html +++ b/content/candies/revision-a/index.html @@ -23,7 +23,7 @@