TabTracker now keeps track of when each tab was most recently used; tabStore wraps this data for sync, and fennec UI uses it to sort incoming tabs by most-recently-used date. This fixes 481326.

This commit is contained in:
jonathandicarlo@jonathan-dicarlos-macbook-pro.local 2009-03-17 17:57:53 -07:00
parent 6473e01c07
commit a6e05ab37a
2 changed files with 121 additions and 47 deletions

View File

@ -41,6 +41,8 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const TAB_TIME_ATTR = "weave.tabEngine.lastUsed.timeStamp";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://weave/util.js");
Cu.import("resource://weave/async.js");
@ -190,6 +192,13 @@ TabStore.prototype = {
return this._sessionStore;
},
get _windowMediator() {
let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
.getService(Ci.nsIWindowMediator);
this.__defineGetter__("_windowMediator", function() { return wm;});
return this._windowMediator;
},
get _json() {
let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
this.__defineGetter__("_json", function() {return json;});
@ -211,38 +220,46 @@ TabStore.prototype = {
},
_addFirefoxTabsToRecord: function TabStore__addFirefoxTabs(record) {
let session = this._json.decode(this._sessionStore.getBrowserState());
for (let i = 0; i < session.windows.length; i++) {
let window = session.windows[i];
/* For some reason, session store uses one-based array index
references, (f.e. in the "selectedWindow" and each tab's
"index" properties), so we convert them to and from
JavaScript's zero-based indexes as needed. */
let windowID = i + 1;
for (let j = 0; j < window.tabs.length; j++) {
let tab = window.tabs[j];
//this._sessionStore.getTabState(tab));
// Iterate through each tab of each window
let enumerator = this._windowMediator.getEnumerator("navigator:browser");
while (enumerator.hasMoreElements()) {
let window = enumerator.getNext();
let tabContainer = window.getBrowser().tabContainer;
for each (let tabChild in tabContainer.childNodes) {
if (!tabChild.QueryInterface)
continue;
let tab = tabChild.QueryInterface(Ci.nsIDOMNode);
if (!tab)
continue;
let tabState = this._json.decode(this._sessionStore.getTabState(tab));
// Skip empty (i.e. just-opened, no history yet) tabs:
if (tab.entries.length == 0)
if (tabState.entries.length == 0)
continue;
let currentPage = tab.entries[tab.entries.length - 1];
// Get the time the tab was last used
let lastUsedTimestamp = tab.getAttribute(TAB_TIME_ATTR);
// Get title of current page
let currentPage = tabState.entries[tabState.entries.length - 1];
/* TODO not always accurate -- if you've hit Back in this tab,
* then the current page might not be the last entry. Deal
* with this later.
*/
this._log.debug("Wrapping a tab with title " + currentPage.title);
* with this later. */
// Get url history
let urlHistory = [];
// Include URLs in reverse order; max out at 10, and skip nulls.
for (let i = tab.entries.length -1; i >= 0; i--) {
let entry = tab.entries[i];
for (let i = tabState.entries.length -1; i >= 0; i--) {
let entry = tabState.entries[i];
if (entry && entry.url)
urlHistory.push(entry.url);
if (urlHistory.length >= 10)
break;
}
// TODO add last-visited date for this tab... but how?
record.addTab(currentPage.title, urlHistory);
// add tab to record
this._log.debug("Wrapping a tab with title " + currentPage.title);
this._log.debug("And timestamp " + lastUsedTimestamp);
record.addTab(currentPage.title, urlHistory, lastUsedTimestamp);
}
}
},
@ -339,9 +356,6 @@ TabStore.prototype = {
};
/* TODO let's have TabTracker keep track of open/close switch events
* and maintain most-recently used date of each tab...
*/
function TabTracker() {
this._TabTracker_init();
@ -356,41 +370,96 @@ TabTracker.prototype = {
_TabTracker_init: function TabTracker__init() {
this._init();
// Register me as an observer!! Listen for tabs opening and closing:
// TODO We need to also register with any windows that are ALREDY
// open. On Fennec maybe try to get this from getBrowser(), which is
// defined differently but should still exist...
// TODO Figure out how this will work on Fennec.
// Register as an observer so we can catch windows opening and closing:
var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Ci.nsIWindowWatcher);
ww.registerNotification(this);
/* Also directly register the listeners for any browser window alread
* open: */
let wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
let enumerator = wm.getEnumerator("navigator:browser");
while (enumerator.hasMoreElements()) {
this._registerListenersForWindow(enumerator.getNext());
}
},
_registerListenersForWindow: function TabTracker__registerListen(window) {
if (! window.getBrowser) {
return;
}
let browser = window.getBrowser();
if (! browser.tabContainer) {
return;
}
//this._log.trace("Registering tab listeners in new window.\n");
let container = browser.tabContainer;
container.addEventListener("TabOpen", this.onTabOpened, false);
container.addEventListener("TabClose", this.onTabClosed, false);
container.addEventListener("TabSelect", this.onTabSelected, false);
},
_unRegisterListenersForWindow: function TabTracker__unregister(window) {
if (! window.getBrowser) {
return;
}
let browser = window.getBrowser();
if (! browser.tabContainer) {
return;
}
let container = browser.tabContainer;
container.removeEventListener("TabOpen", this.onTabOpened, false);
container.removeEventListener("TabClose", this.onTabClosed, false);
container.removeEventListener("TabSelect", this.onTabSelected, false);
},
observe: function TabTracker_observe(aSubject, aTopic, aData) {
this._log.trace("Spotted window open/close");
/* Called when a window opens or closes. Make sure that every
* window has the appropriate listeners registered. */
let window = aSubject.QueryInterface(Ci.nsIDOMWindow);
// Ignore windows that don't have tabContainers.
// TODO: Fennec windows don't have tabContainers, but we still want
// to register an observer in them.
if (! window.getBrowser)
return;
let browser = window.getBrowser();
if (! browser.tabContainer)
return;
let container = browser.tabContainer;
// TODO figure out how this will work in Fennec.
if (aTopic == "domwindowopened") {
container.addEventListener("TabOpen", this.onTabChanged, false);
container.addEventListener("TabClose", this.onTabChanged, false);
this._registerListenersForWindow(window);
} else if (aTopic == "domwindowclosed") {
container.removeEventListener("TabOpen", this.onTabChanged, false);
container.removeEventListener("TabClose", this.onTabChanged, false);
this._unRegisterListenersForWindow(window);
}
// TODO
},
onTabChanged: function TabTracker_onTabChanged(event) {
this._score += 10; // meh? meh.
onTabOpened: function TabTracker_onTabOpened(event) {
// Store a timestamp in the tab to track when it was last used
//this._log.trace("Tab opened.\n");
/*if (Cc["@mozilla.org/browser/sessionstore;1"]) {
let ss = Cc["@mozilla.org/browser/sessionstore;1"]
.getService(Ci.nsISessionStore);
ss.setTabValue(event.target, TAB_TIME_ATTR, event.timeStamp);
}*/
event.target.setAttribute(TAB_TIME_ATTR, event.timeStamp);
//this._log.debug("Tab timestamp set to " + event.target.getAttribute(TAB_TIME_ATTR) + "\n");
this._score += 50;
},
onTabClosed: function TabTracker_onTabSelected(event) {
//this._log.trace("Tab closed.\n");
this._score += 10;
},
onTabSelected: function TabTracker_onTabSelected(event) {
// Update the tab's timestamp
//this._log.trace("Tab selected.\n");
/*if (Cc["@mozilla.org/browser/sessionstore;1"]) {
let ss = Cc["@mozilla.org/browser/sessionstore;1"]
.getService(Ci.nsISessionStore);
ss.setTabValue(event.target, TAB_TIME_ATTR, event.timeStamp);
}*/
event.target.setAttribute(TAB_TIME_ATTR, event.timeStamp);
//this._log.debug("Tab timestamp set to " + event.target.getAttribute(TAB_TIME_ATTR) + "\n");
this._score += 10;
},
// TODO: Also listen for tabs loading new content?
get changedIDs() {
// The record for my own client is always the only changed record.
let obj = {};
@ -400,6 +469,9 @@ TabTracker.prototype = {
// TODO this hard-coded score is a hack; replace with maybe +25 or +35
// per tab open event.
// Actually maybe it should just stay this way? Otherwise we're missing
// something really important which is laod-page-in-tab events, which are
// a powerful motivation to sync
get score() {
return 100;
}

View File

@ -63,10 +63,12 @@ TabSetRecord.prototype = {
};
},
addTab: function TabSetRecord_addTab(title, urlHistory) {
addTab: function TabSetRecord_addTab(title, urlHistory, lastUsed) {
if (!this.cleartext.tabs)
this.cleartext.tabs = [];
this.cleartext.tabs.push( {title: title, urlHistory: urlHistory });
this.cleartext.tabs.push( {title: title,
urlHistory: urlHistory,
lastUsed: lastUsed});
},
getAllTabs: function TabSetRecord_getAllTabs() {