+ fixed: if you quit firefox with tab candy present, and more than one window open, when you restart, everything will be broken

+ massaged the whole start up sequence
This commit is contained in:
Ian Gilman 2010-05-19 16:35:54 -07:00
parent 2d7ca7cd82
commit a8c8eef11b
4 changed files with 429 additions and 378 deletions

View File

@ -286,9 +286,10 @@ window.TabItems = {
var reconnected = false;
$div.each(function() {
var tab = Tabs.tab(this);
if(tab == Utils.homeTab)
if(tab == Utils.homeTab) {
$(this).hide();
else {
reconnected = true;
} else {
var item = new TabItem(this, tab);
$(this).data('tabItem', item);

View File

@ -64,10 +64,14 @@ var Tabbar = {
_hidden: false,
// ----------
get el(){ return window.Tabs[0].raw.parentNode; },
get el() {
return window.Tabs[0].raw.parentNode;
},
// ----------
height: window.Tabs[0].raw.parentNode.getBoundingClientRect().height,
get height() {
return window.Tabs[0].raw.parentNode.getBoundingClientRect().height;
},
// ----------
hide: function(animate) {
@ -448,121 +452,133 @@ ArrangeClass.prototype = {
// Class: UIClass
// Singleton top-level UI manager. TODO: Integrate with <Page>.
function UIClass(){
// 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();
if(window.Tabs)
this.init();
else {
var self = this;
TabsManager.addSubscriber(this, 'load', function() {
self.init();
});
}
// ___ Navbar
if(this.focused) {
this.tabBar.hide();
this.navBar.hide();
}
Tabs.onFocus(function() {
setTimeout(function() { // Marshal event from chrome thread to DOM thread
try{
if(this.contentWindow.location.host == "tabcandy") {
self.focused = true;
self.navBar.hide();
self.tabBar.hide();
} else {
self.focused = false;
self.navBar.show();
}
}catch(e){
Utils.log()
}
}, 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;
if(!sane)
alert('storage data is bad; starting fresh');
}
Groups.reconstitute(data.groups);
TabItems.reconstitute(data.tabs);
$(window).bind('beforeunload', function() {
if(self.initialized)
self.save();
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;
};
// ----------
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() {
setTimeout(function() { // Marshal event from chrome thread to DOM thread
try{
if(this.contentWindow.location.host == "tabcandy") {
self.focused = true;
self.navBar.hide();
self.tabBar.hide();
} else {
self.focused = false;
self.navBar.show();
}
}catch(e){
Utils.log()
}
}, 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;
if(!sane)
alert('storage data is bad; starting fresh');
}
Groups.reconstitute(data.groups);
TabItems.reconstitute(data.tabs);
$(window).bind('beforeunload', function() {
if(self.initialized)
self.save();
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;
},
// ----------
resize: function() {
/* Groups.repositionNewTabGroup(); */

View File

@ -180,14 +180,24 @@ Mirror.prototype = $.extend(new Subscribable(), {
// ##########
// Class: TabMirror
// A singleton that manages all of the <Mirror>s in the system.
var TabMirror = function( ){ this.init() }
var TabMirror = function() {
if(window.Tabs)
this.init();
else {
var self = this;
TabsManager.addSubscriber(this, 'load', function() {
self.init();
});
}
}
TabMirror.prototype = {
// ----------
// Function: init
// Set up the necessary tracking to maintain the <Mirror>s.
init: function(){
var self = this;
// When a tab is opened, create the mirror
Tabs.onOpen(function() {
var tab = this;

View File

@ -319,279 +319,303 @@ function EventListenerMixIn(options) {
});
}
// Class: Tabs
// ##########
// Class: TabsManager
// Singelton for dealing with the actual tabs in the browser.
function Tabs() {
var trackedWindows = new Dictionary();
var trackedTabs = new Dictionary();
var windows = {
get focused() {
var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var chromeWindow = wm.getMostRecentWindow("navigator:browser");
/* Utils.log( trackedWindows ) */
if (chromeWindow)
return trackedWindows.get(chromeWindow);
return null;
}
};
// TODO: AZA ADDED THIS TO MAKE IT WORK WHEN JETPACK NOT INSTALLED
// TOTALLY HACKY :(
var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var chromeWindow = wm.getMostRecentWindow("navigator:browser");
trackedWindows.set(chromeWindow,
new BrowserWindow(chromeWindow));
windows.__proto__ = trackedWindows.values;
var tabs = {
// ----------
get focused() {
var browserWindow = windows.focused;
if (browserWindow)
return browserWindow.getFocusedTab();
return null;
},
// ----------
open: function open(url, inBackground) {
if(typeof(inBackground) == 'undefined')
inBackground = false;
var browserWindow = windows.focused;
// TODO: What to do if we have no focused window?
// make a new one?
window.TabsManager = $.extend(new Subscribable(), {
// ----------
// Function: init
// Sets up the TabsManager and window.Tabs
init: function() {
var self = this;
/*
var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var chromeWindow = wm.getMostRecentWindow("navigator:browser");
*/
var chromeWindow = Utils.activeWindow;
if(!chromeWindow.getBrowser || !chromeWindow.getBrowser())
chromeWindow = null;
if(!chromeWindow) {
setTimeout(function() {
self.init();
}, 100);
var tab = browserWindow.addTab(url);
if (!inBackground)
browserWindow.selectedTab = tab; // TODO doesn't seem to be working
return;
}
var trackedWindows = new Dictionary();
var trackedTabs = new Dictionary();
return tab;
},
// ----------
tab: function tab(value) {
// assuming value is a DOM element for the time being
var result = $(value).data('tab');
if(!result)
result = $(value).find("canvas").data("link").tab;
return result;
},
// ----------
toString: function toString() {
return "[Tabs]";
}
};
var tabsMixIns = new EventListenerMixIns(tabs);
tabsMixIns.add({name: "onReady"});
tabsMixIns.add({name: "onFocus"});
tabsMixIns.add({name: "onClose"});
tabsMixIns.add({name: "onOpen"});
tabs.__proto__ = trackedTabs.values;
/* Utils.log(tabs); */
function newBrowserTab(tabbrowser, chromeTab) {
var browserTab = new BrowserTab(tabbrowser, chromeTab);
trackedTabs.set(chromeTab, browserTab);
return browserTab;
}
function unloadBrowserTab(chromeTab) {
var browserTab = trackedTabs.get(chromeTab);
trackedTabs.remove(chromeTab);
browserTab._unload();
}
function BrowserWindow(chromeWindow) {
var tabbrowser = chromeWindow.getBrowser();
for (var i = 0; i < tabbrowser.tabContainer.itemCount; i++)
newBrowserTab(tabbrowser,
tabbrowser.tabContainer.getItemAtIndex(i));
const EVENTS_TO_WATCH = ["TabOpen", "TabMove", "TabClose", "TabSelect"];
function onEvent(event) {
// TODO: For some reason, exceptions that are raised outside of this
// function get eaten, rather than logged, so we're adding our own
// error logging here.
try {
// This is a XUL <tab> element of class tabbrowser-tab.
var chromeTab = event.originalTarget;
switch (event.type) {
case "TabSelect":
tabsMixIns.bubble("onFocus",
trackedTabs.get(chromeTab),
true);
break;
case "TabOpen":
newBrowserTab(tabbrowser, chromeTab);
tabsMixIns.bubble("onOpen",
trackedTabs.get(chromeTab),
true);
break;
case "TabMove":
break;
case "TabClose":
tabsMixIns.bubble("onClose",
trackedTabs.get(chromeTab),
true);
unloadBrowserTab(chromeTab);
break;
}
} catch (e) {
Utils.log(e);
trackedWindows.set(chromeWindow,
new BrowserWindow(chromeWindow));
var windows = {
get focused() {
var wm = Cc["@mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
var chromeWindow = wm.getMostRecentWindow("navigator:browser");
/* Utils.log( trackedWindows ) */
if (chromeWindow)
return trackedWindows.get(chromeWindow);
return null;
}
};
windows.__proto__ = trackedWindows.values;
var tabs = {
// ----------
get focused() {
var browserWindow = windows.focused;
if (browserWindow)
return browserWindow.getFocusedTab();
return null;
},
// ----------
open: function open(url, inBackground) {
if(typeof(inBackground) == 'undefined')
inBackground = false;
var browserWindow = windows.focused;
// TODO: What to do if we have no focused window?
// make a new one?
var tab = browserWindow.addTab(url);
if (!inBackground)
browserWindow.selectedTab = tab; // TODO doesn't seem to be working
return tab;
},
// ----------
tab: function tab(value) {
// assuming value is a DOM element for the time being
var result = $(value).data('tab');
if(!result)
result = $(value).find("canvas").data("link").tab;
return result;
},
// ----------
toString: function toString() {
return "[Tabs]";
}
};
var tabsMixIns = new EventListenerMixIns(tabs);
tabsMixIns.add({name: "onReady"});
tabsMixIns.add({name: "onFocus"});
tabsMixIns.add({name: "onClose"});
tabsMixIns.add({name: "onOpen"});
tabs.__proto__ = trackedTabs.values;
/* Utils.log(tabs); */
function newBrowserTab(tabbrowser, chromeTab) {
var browserTab = new BrowserTab(tabbrowser, chromeTab);
trackedTabs.set(chromeTab, browserTab);
return browserTab;
}
EVENTS_TO_WATCH.forEach(
function(eventType) {
tabbrowser.tabContainer.addEventListener(eventType, onEvent, true);
function unloadBrowserTab(chromeTab) {
var browserTab = trackedTabs.get(chromeTab);
trackedTabs.remove(chromeTab);
browserTab._unload();
}
function BrowserWindow(chromeWindow) {
var tabbrowser = chromeWindow.getBrowser();
for (var i = 0; i < tabbrowser.tabContainer.itemCount; i++)
newBrowserTab(tabbrowser,
tabbrowser.tabContainer.getItemAtIndex(i));
const EVENTS_TO_WATCH = ["TabOpen", "TabMove", "TabClose", "TabSelect"];
function onEvent(event) {
// TODO: For some reason, exceptions that are raised outside of this
// function get eaten, rather than logged, so we're adding our own
// error logging here.
try {
// This is a XUL <tab> element of class tabbrowser-tab.
var chromeTab = event.originalTarget;
switch (event.type) {
case "TabSelect":
tabsMixIns.bubble("onFocus",
trackedTabs.get(chromeTab),
true);
break;
case "TabOpen":
newBrowserTab(tabbrowser, chromeTab);
tabsMixIns.bubble("onOpen",
trackedTabs.get(chromeTab),
true);
break;
case "TabMove":
break;
case "TabClose":
tabsMixIns.bubble("onClose",
trackedTabs.get(chromeTab),
true);
unloadBrowserTab(chromeTab);
break;
}
} catch (e) {
Utils.log(e);
}
}
EVENTS_TO_WATCH.forEach(
function(eventType) {
tabbrowser.tabContainer.addEventListener(eventType, onEvent, true);
});
this.addTab = function addTab(url) {
var chromeTab = tabbrowser.addTab(url);
// The TabOpen event has just been triggered, so we
// just need to fetch it from our dictionary now.
return trackedTabs.get(chromeTab);
};
this.getFocusedTab = function getFocusedTab() {
return trackedTabs.get(tabbrowser.selectedTab);
};
Extension.addUnloadMethod(
this,
function() {
EVENTS_TO_WATCH.forEach(
function(eventType) {
tabbrowser.tabContainer.removeEventListener(eventType, onEvent, true);
});
for (var i = 0; i < tabbrowser.tabContainer.itemCount; i++)
unloadBrowserTab(tabbrowser.tabContainer.getItemAtIndex(i));
});
}
function BrowserTab(tabbrowser, chromeTab) {
var browser = chromeTab.linkedBrowser;
var mixIns = new EventListenerMixIns(this);
var self = this;
mixIns.add(
{name: "onReady",
observe: browser,
eventName: "DOMContentLoaded",
useCapture: true,
bubbleTo: tabsMixIns,
filter: function(event) {
// Return the document that just loaded.
event.tab = self;
return event;
}});
mixIns.add(
{name: "onFocus",
observe: chromeTab,
eventName: "TabSelect",
useCapture: true,
bubbleTo: tabsMixIns,
filter: function(event) {
// There's not really much to report here other
// than the Tab itself, but that's already the
// 'this' variable, so just return true for now.
return true;
}});
this.__proto__ = {
get isClosed() { return (browser == null); },
get url() {
if (browser && browser.currentURI)
return browser.currentURI.spec;
return null;
},
get favicon() {
if (chromeTab && chromeTab.image) {
return chromeTab.image;
}
return null;
},
get contentWindow() {
if (browser && browser.contentWindow)
return browser.contentWindow;
return null;
},
get contentDocument() {
if (browser && browser.contentDocument)
return browser.contentDocument;
return null;
},
get raw() { return chromeTab; },
focus: function focus() {
if (browser)
tabbrowser.selectedTab = chromeTab;
},
close: function close() {
if (browser)
browser.contentWindow.close();
},
toString: function toString() {
if (!browser)
return "[Closed Browser Tab]";
else
return "[Browser Tab]";
},
_unload: function _unload() {
mixIns.unload();
mixIns = null;
tabbrowser = null;
chromeTab = null;
browser = null;
}
};
}
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.addTab = function addTab(url) {
var chromeTab = tabbrowser.addTab(url);
// The TabOpen event has just been triggered, so we
// just need to fetch it from our dictionary now.
return trackedTabs.get(chromeTab);
};
this.getFocusedTab = function getFocusedTab() {
return trackedTabs.get(tabbrowser.selectedTab);
};
this.__defineGetter__("tabs", function() { return tabs; });
Extension.addUnloadMethod(
this,
function() {
EVENTS_TO_WATCH.forEach(
function(eventType) {
tabbrowser.tabContainer.removeEventListener(eventType, onEvent, true);
});
for (var i = 0; i < tabbrowser.tabContainer.itemCount; i++)
unloadBrowserTab(tabbrowser.tabContainer.getItemAtIndex(i));
tabsMixIns.unload();
browserWatcher.unload();
});
window.Tabs = tabs;
window.Tabs.app = XULApp;
this._sendToSubscribers('load');
}
});
function BrowserTab(tabbrowser, chromeTab) {
var browser = chromeTab.linkedBrowser;
// ----------
window.TabsManager.init();
var mixIns = new EventListenerMixIns(this);
var self = this;
mixIns.add(
{name: "onReady",
observe: browser,
eventName: "DOMContentLoaded",
useCapture: true,
bubbleTo: tabsMixIns,
filter: function(event) {
// Return the document that just loaded.
event.tab = self;
return event;
}});
mixIns.add(
{name: "onFocus",
observe: chromeTab,
eventName: "TabSelect",
useCapture: true,
bubbleTo: tabsMixIns,
filter: function(event) {
// There's not really much to report here other
// than the Tab itself, but that's already the
// 'this' variable, so just return true for now.
return true;
}});
this.__proto__ = {
get isClosed() { return (browser == null); },
get url() {
if (browser && browser.currentURI)
return browser.currentURI.spec;
return null;
},
get favicon() {
if (chromeTab && chromeTab.image) {
return chromeTab.image;
}
return null;
},
get contentWindow() {
if (browser && browser.contentWindow)
return browser.contentWindow;
return null;
},
get contentDocument() {
if (browser && browser.contentDocument)
return browser.contentDocument;
return null;
},
get raw() { return chromeTab; },
focus: function focus() {
if (browser)
tabbrowser.selectedTab = chromeTab;
},
close: function close() {
if (browser)
browser.contentWindow.close();
},
toString: function toString() {
if (!browser)
return "[Closed Browser Tab]";
else
return "[Browser Tab]";
},
_unload: function _unload() {
mixIns.unload();
mixIns = null;
tabbrowser = null;
chromeTab = null;
browser = null;
}
};
}
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 = new Tabs().tabs;
window.Tabs.app = XULApp;
})();