+ Applied Ehsan's sessionstore patch

+ Various related fixes
+ Added Utils.log2, which logs directly to the window
This commit is contained in:
Ian Gilman 2010-05-21 15:44:15 -07:00
parent 744b1edc64
commit eba43368a6
5 changed files with 352 additions and 175 deletions

View File

@ -49,9 +49,13 @@ function dropAcceptFunction(el) { // ".tab", //".tab, .group",
// A single group in the tab candy window. Descended from <Item>.
// Note that it implements the <Subscribable> interface.
window.Group = function(listOfEls, options) {
try {
/* Utils.log("in Group ctor"); */
/* Utils.log("options: " + options.toSource()); */
if(typeof(options) == 'undefined')
options = {};
this._inited = false;
this._children = []; // an array of Items
this.defaultSize = new Point(TabItems.tabWidth * 1.5, TabItems.tabHeight * 1.5);
this.isAGroup = true;
@ -239,6 +243,12 @@ window.Group = function(listOfEls, options) {
// ___ Push other objects away
if(!options.dontPush)
this.pushAway();
this._inited = true;
this.save();
} catch(e){
Utils.log("Error in Group(): " + e);
}
};
// ----------
@ -261,6 +271,15 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
return data;
},
// ----------
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);
},
// ----------
getTitle: function() {
@ -271,6 +290,7 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
// ----------
setTitle: function(value) {
this.$title.val(value);
this.save();
},
// ----------
@ -280,6 +300,7 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
var css = {width: w};
this.$title.css(css);
this.$titleShield.css(css);
this.save();
},
// ----------
@ -393,6 +414,8 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
if(!isRect(this.bounds))
Utils.trace('Group.setBounds: this.bounds is not a real rectangle!', this.bounds);
this.save();
},
// ----------
@ -427,6 +450,8 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
$(this).remove();
Items.unsquish();
});
Storage.deleteGroup(Utils.getCurrentWindow(), this.id);
},
// ----------
@ -536,6 +561,8 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
if(item.tab == Utils.activeTab)
Groups.setActiveGroup(this);
item.save();
}
if(!options.dontArrange)
@ -545,7 +572,6 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
this._nextNewTabCallback.apply(this, [item])
this._nextNewTabCallback = null;
}
},
// ----------
@ -601,6 +627,7 @@ 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();
});
},
@ -828,6 +855,8 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
this.arrange({z: z + 2});
}
this.save();
},
// ----------
@ -896,6 +925,8 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
this.$resizer.fadeOut();
$(this.container).resizable('disable');
}
this.save();
},
// ----------
@ -950,6 +981,8 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
// actions is necessary for a good user experience.
self.onNextNewTab(doNextTab);
this.save();
},
// ----------
@ -976,6 +1009,7 @@ window.Group.prototype = $.extend(new Item(), new Subscribable(), {
});
this.arrange({animate: false});
// this.arrange calls this.save for us
},
// ----------
@ -1242,6 +1276,7 @@ window.Groups = {
getNextID: function() {
var result = this.nextID;
this.nextID++;
Storage.saveGroupsData(Utils.getCurrentWindow(), {nextID:this.nextID});
return result;
},
@ -1256,12 +1291,26 @@ window.Groups = {
},
// ----------
reconstitute: function(data) {
if(data && data.nextID)
this.nextID = data.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(data && data.groups) {
$.each(data.groups, function(index, group) {
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 options = {
locked: {
@ -1272,7 +1321,7 @@ window.Groups = {
};
new Group([], $.extend({}, group, options));
});
}
}
var group = this.getNewTabGroup();
@ -1290,6 +1339,9 @@ window.Groups = {
new Group([], options);
}
}catch(e){
Utils.log("error in recons: "+e);
}
},
// ----------
@ -1342,8 +1394,11 @@ window.Groups = {
// ----------
register: function(group) {
Utils.assert('group', group);
/* Utils.log("registering", group); */
Utils.assert('only register once per group', $.inArray(group, this.groups) == -1);
this.groups.push(group);
/* Utils.log("groups: "+this.groups.length); */
},
// ----------
@ -1521,4 +1576,4 @@ $(".tab").data('isDragging', false)
.draggable(window.Groups.dragOptions)
.droppable(window.Groups.dropOptions);
})();
})();

View File

@ -2,8 +2,90 @@
// ##########
Storage = {
GROUP_DATA_IDENTIFIER: "tabcandy-group",
GROUPS_DATA_IDENTIFIER: "tabcandy-groups",
TAB_DATA_IDENTIFIER: "tabcandy-tab",
// ----------
init: function() {
this._sessionStore = Components.classes["@mozilla.org/browser/sessionstore;1"]
.getService(Components.interfaces.nsISessionStore);
},
// ----------
saveTab: function(tab, data) {
/* Utils.log("AAAAAAAAAAAAAAAAAAAAAAAAAAA saving tab data, tab: " + tab.toSource() + ", data: "+data.toSource()); */
this._sessionStore.setTabValue(tab, this.TAB_DATA_IDENTIFIER,
JSON.stringify(data));
},
// ----------
getTabData: function(tab) {
var existingData = null;
try {
/* Utils.log("readTabData: " + this._sessionStore.getTabValue(tab, this.TAB_DATA_IDENTIFIER)); */
var tabData = this._sessionStore.getTabValue(tab, this.TAB_DATA_IDENTIFIER);
if (tabData != "") {
existingData = JSON.parse(tabData);
}
} catch (e) {
// getWindowValue will fail if the property doesn't exist
Utils.log("Error in readTabData: "+e);
}
return existingData;
},
// ----------
saveGroup: function(win, data) {
var id = data.id;
var existingData = this.readGroupData(win);
existingData[id] = data;
this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER,
JSON.stringify(existingData));
},
// ----------
deleteGroup: function(win, id) {
var existingData = this.readGroupData(win);
delete existingData[id];
this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER,
JSON.stringify(existingData));
},
// ----------
readGroupData: function(win) {
var existingData = {};
try {
/* Utils.log("readGroupData" + this._sessionStore.getWindowValue(win, this.GROUP_DATA_IDENTIFIER)); */
existingData = JSON.parse(
this._sessionStore.getWindowValue(win, this.GROUP_DATA_IDENTIFIER)
);
} catch (e) {
// getWindowValue will fail if the property doesn't exist
Utils.log("Error in readGroupData: "+e);
}
return existingData;
},
// ----------
saveGroupsData: function(win, data) {
/* Utils.log("GGGGGGGGGGGGGGGGGGGGGGGGGGGGGG " + data.toSource()); */
this._sessionStore.setWindowValue(win, this.GROUPS_DATA_IDENTIFIER,
JSON.stringify(data));
},
// ----------
readGroupsData: function(win) {
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)
);
} catch (e) {
// getWindowValue will fail if the property doesn't exist
}
return existingData;
},
// ----------

View File

@ -34,6 +34,20 @@ window.TabItem.prototype = $.extend(new Item(), {
groupID: (this.parent ? this.parent.id : 0)
};
},
// ----------
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);
}catch(e){
Utils.log("Error in saving tab value: "+e);
}
},
// ----------
getURL: function() {
@ -73,6 +87,8 @@ window.TabItem.prototype = $.extend(new Item(), {
this.bounds = newBounds;
this._updateDebugBounds();
/* } */
this.save();
},
// ----------
@ -154,6 +170,8 @@ window.TabItem.prototype = $.extend(new Item(), {
if(!isRect(this.bounds))
Utils.trace('TabItem.setBounds: this.bounds is not a real rectangle!', this.bounds);
this.save();
},
// ----------
@ -169,6 +187,9 @@ window.TabItem.prototype = $.extend(new Item(), {
// ----------
close: function() {
this.tab.close();
// No need to explicitly delete the tab data, becasue sessionstore data
// associated with the tab will automatically go away
},
// ----------
@ -308,7 +329,8 @@ window.TabItems = {
}
}
});
/* Utils.log("reconnected: "+reconnected); */
if(!reconnected && $div.length == 1 && Groups){
Groups.newTab($div.data('tabItem'));
}
@ -427,32 +449,13 @@ window.TabItems = {
},
// ----------
getStorageData: function() {
var data = {tabs: []};
reconstitute: function() {
var items = this.getItems();
var self = this;
$.each(items, function(index, item) {
data.tabs.push(item.getStorageData());
if(!self.reconnect(item))
Groups.newTab(item);
});
return data;
},
// ----------
reconstitute: function(data) {
this.storageData = data;
var items = this.getItems();
if(data && data.tabs) {
var self = this;
$.each(items, function(index, item) {
if(!self.reconnect(item))
Groups.newTab(item);
});
} else {
var box = Items.getPageBounds();
box.inset(20, 20);
Items.arrange(items, box, {padding: 10, animate:false});
}
},
// ----------
@ -474,45 +477,48 @@ window.TabItems = {
// ----------
reconnect: function(item) {
if(item.reconnected)
return true;
var found = false;
if(this.storageData && this.storageData.tabs) {
var self = this;
$.each(this.storageData.tabs, function(index, tab) {
if(tab.url == 'about:blank')
return;
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(item.parent)
item.parent.remove(item);
if(item.getURL() == tab.url) {
if(item.parent)
item.parent.remove(item);
item.setBounds(tab.bounds, true);
item.setBounds(tab.bounds, true);
if(isPoint(tab.userSize))
item.userSize = new Point(tab.userSize);
if(isPoint(tab.userSize))
item.userSize = new Point(tab.userSize);
if(tab.groupID) {
var group = Groups.group(tab.groupID);
group.add(item);
if(item.tab == Utils.activeTab)
Groups.setActiveGroup(item.parent);
}
if(tab.groupID) {
var group = Groups.group(tab.groupID);
/* Utils.log("group found: " + group); */
group.add(item);
Groups.updateTabBarForActiveGroup();
self.storageData.tabs.splice(index, 1);
item.reconnected = true;
found = true;
return false;
}
});
}
if(item.tab == Utils.activeTab)
Groups.setActiveGroup(item.parent);
}
Groups.updateTabBarForActiveGroup();
item.reconnected = true;
found = true;
} else
item.reconnected = (item.tab.url != 'about:blank');
item.save();
}catch(e){
Utils.log("Error in TabItems.reconnect: "+e + " at " + e.fileName + "(" + e.lineNumber + ")");
}
return found;
}
};
TabItems.init();

View File

@ -476,118 +476,136 @@ function UIClass(){
UIClass.prototype = {
// ----------
init: function() {
// Variable: navBar
// A reference to the <Navbar>, for manipulating the browser's nav bar.
this.navBar = Navbar;
// Variable: tabBar
// A reference to the <Tabbar>, for manipulating the browser's tab bar.
this.tabBar = Tabbar;
// Variable: devMode
// If true (set by an url parameter), adds extra features to the screen.
// TODO: Integrate with the dev menu
this.devMode = false;
// Variable: currentTab
// Keeps track of which <Tabs> tab we are currently on.
// Used to facilitate zooming down from a previous tab.
this.currentTab = Utils.activeTab;
// Variable: focused
// Keeps track of whether Tab Candy is focused.
this.focused = (Utils.activeTab == Utils.homeTab);
var self = this;
// ___ URL Params
var params = document.location.search.replace('?', '').split('&');
$.each(params, function(index, param) {
var parts = param.split('=');
if(parts[0] == 'dev' && parts[1] == '1')
self.devMode = true;
});
// ___ Dev Mode
if(this.devMode) {
Switch.insert('body', '');
$('<br><br>').appendTo("#actions");
this._addArrangements();
}
// ___ Navbar
if(this.focused) {
this.tabBar.hide();
this.navBar.hide();
}
Tabs.onFocus(function() {
var me = this;
setTimeout(function() { // Marshal event from chrome thread to DOM thread
try{
if(me.contentWindow.location.host == "tabcandy") {
self.focused = true;
self.navBar.hide();
self.tabBar.hide();
} else {
self.focused = false;
self.navBar.show();
}
}catch(e){
Utils.log(e)
}
}, 1);
});
Tabs.onOpen(function(a, b) {
setTimeout(function() { // Marshal event from chrome thread to DOM thread
self.navBar.show();
}, 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;
try {
// Variable: navBar
// A reference to the <Navbar>, for manipulating the browser's nav bar.
this.navBar = Navbar;
if(!sane)
alert('storage data is bad; starting fresh');
}
Groups.reconstitute(data.groups);
TabItems.reconstitute(data.tabs);
// Variable: tabBar
// A reference to the <Tabbar>, for manipulating the browser's tab bar.
this.tabBar = Tabbar;
// Variable: devMode
// If true (set by an url parameter), adds extra features to the screen.
// TODO: Integrate with the dev menu
this.devMode = false;
// Variable: currentTab
// Keeps track of which <Tabs> tab we are currently on.
// Used to facilitate zooming down from a previous tab.
this.currentTab = Utils.activeTab;
// Variable: focused
// Keeps track of whether Tab Candy is focused.
this.focused = (Utils.activeTab == Utils.homeTab);
var self = this;
// ___ URL Params
var params = document.location.search.replace('?', '').split('&');
$.each(params, function(index, param) {
var parts = param.split('=');
if(parts[0] == 'dev' && parts[1] == '1')
self.devMode = true;
});
// ___ Dev Mode
if(this.devMode) {
Switch.insert('body', '');
$('<br><br>').appendTo("#actions");
this._addArrangements();
}
// ___ Navbar
if(this.focused) {
this.tabBar.hide();
this.navBar.hide();
}
Tabs.onFocus(function() {
var me = this;
setTimeout(function() { // Marshal event from chrome thread to DOM thread
try{
if(me.contentWindow.location.host == "tabcandy") {
self.focused = true;
self.navBar.hide();
self.tabBar.hide();
} else {
self.focused = false;
self.navBar.show();
}
}catch(e){
Utils.log(e)
}
}, 1);
});
$(window).bind('beforeunload', function() {
if(self.initialized)
self.save();
Tabs.onOpen(function(a, b) {
setTimeout(function() { // Marshal event from chrome thread to DOM thread
self.navBar.show();
}, 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;
self.navBar.show();
self.tabBar.show(false);
self.tabBar.showAllTabs();
});
// ___ resizing
if(data.pageBounds) {
this.pageBounds = data.pageBounds;
this.resize();
} else
this.pageBounds = Items.getPageBounds();
$(window).resize(function() {
self.resize();
});
// ___ Dev Menu
this.addDevMenu();
// ___ Done
this.initialized = true;
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.navBar.show();
self.tabBar.show(false);
self.tabBar.showAllTabs();
});
// ___ resizing
data.pageBounds = null;
if(data.pageBounds) {
this.pageBounds = data.pageBounds;
this.resize();
} else
this.pageBounds = Items.getPageBounds();
$(window).resize(function() {
self.resize();
});
// ___ Dev Menu
this.addDevMenu();
// ___ Done
this.initialized = true;
}catch(e) {
Utils.log("Error in UIClass(): " + e);
Utils.log(e.fileName);
Utils.log(e.lineNumber);
Utils.log(e.stack);
}
},
// ----------
@ -609,6 +627,10 @@ UIClass.prototype = {
var oldPageBounds = new Rect(this.pageBounds);
var newPageBounds = Items.getPageBounds();
if(newPageBounds.equals(oldPageBounds))
return;
/* Utils.log2('resize', newPageBounds, oldPageBounds); */
if(newPageBounds.width < this.pageBounds.width && newPageBounds.width > itemBounds.width)
newPageBounds.width = this.pageBounds.width;
@ -702,6 +724,7 @@ UIClass.prototype = {
// ----------
save: function() {
return;
var data = {
dataVersion: 2,
groups: Groups.getStorageData(),
@ -709,6 +732,7 @@ UIClass.prototype = {
pageBounds: Items.getPageBounds()
};
/* Utils.error(this.pageBounds, data.pageBounds); */
if(this.storageSanity(data))
Storage.write(data);
else

View File

@ -471,6 +471,16 @@ var Utils = {
consoleService.logStringMessage(text);
},
log2: function() { // pass as many arguments as you want, it'll print them all
var text = this.expandArgumentsForLog(arguments);
var html =
'<div style="position: relative; z-index: -9999">'
+ text
+ '</div>';
$(html).prependTo('body');
},
error: function() { // pass as many arguments as you want, it'll print them all
var text = this.expandArgumentsForLog(arguments);
Cu.reportError('tabcandy error: ' + text);