mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 13:51:41 +00:00
Bug 1873025 - Fix the displayed relative time for open tabs listing to match the recency sorting.r=jsudiaman,fxview-reviewers,tabbrowser-reviewers,dao
* Carry over lastAccessed times from tabs that haven't been seen/active this session. * Use the lastSeenActive rather than lastAccessed timestamp for labeling open tabs in firefox view. Differential Revision: https://phabricator.services.mozilla.com/D198892
This commit is contained in:
parent
12228dff42
commit
4765d125ef
@ -259,6 +259,14 @@
|
||||
return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a timestamp which attempts to represent the last time the user saw this tab.
|
||||
* If the tab has not been active in this session, any lastAccessed is used. We
|
||||
* differentiate between selected and explicitly visible; a selected tab in a hidden
|
||||
* window is last seen when that window and tab were last visible.
|
||||
* We use the application start time as a fallback value when no other suitable value
|
||||
* is available.
|
||||
*/
|
||||
get lastSeenActive() {
|
||||
const isForegroundWindow =
|
||||
this.ownerGlobal ==
|
||||
@ -270,8 +278,16 @@
|
||||
if (this._lastSeenActive) {
|
||||
return this._lastSeenActive;
|
||||
}
|
||||
// Use the application start time as the fallback value
|
||||
return Services.startup.getStartupInfo().start.getTime();
|
||||
|
||||
const appStartTime = Services.startup.getStartupInfo().start.getTime();
|
||||
if (!this._lastAccessed || this._lastAccessed >= appStartTime) {
|
||||
// When the tab was created this session but hasn't been seen by the user,
|
||||
// default to the application start time.
|
||||
return appStartTime;
|
||||
}
|
||||
// The tab was restored from a previous session but never seen.
|
||||
// Use the lastAccessed as the best proxy for when the user might have seen it.
|
||||
return this._lastAccessed;
|
||||
}
|
||||
|
||||
get _overPlayingIcon() {
|
||||
|
@ -76,6 +76,8 @@ support-files = ["tab_that_closes.html"]
|
||||
|
||||
["browser_hiddentab_contextmenu.js"]
|
||||
|
||||
["browser_lastSeenActive.js"]
|
||||
|
||||
["browser_lazy_tab_browser_events.js"]
|
||||
|
||||
["browser_link_in_tab_title_and_url_prefilled_blank_page.js"]
|
||||
|
260
browser/base/content/test/tabs/browser_lastSeenActive.js
Normal file
260
browser/base/content/test/tabs/browser_lastSeenActive.js
Normal file
@ -0,0 +1,260 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const { SessionStoreTestUtils } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/SessionStoreTestUtils.sys.mjs"
|
||||
);
|
||||
|
||||
const triggeringPrincipal_base64 = E10SUtils.SERIALIZED_SYSTEMPRINCIPAL;
|
||||
|
||||
SessionStoreTestUtils.init(this, window);
|
||||
// take a state snapshot we can restore to after each test
|
||||
const ORIG_STATE = SessionStore.getBrowserState();
|
||||
|
||||
const SECOND_MS = 1000;
|
||||
const DAY_MS = 24 * 60 * 60 * 1000;
|
||||
const today = new Date().getTime();
|
||||
const yesterday = new Date(Date.now() - DAY_MS).getTime();
|
||||
|
||||
function tabEntry(url, lastAccessed) {
|
||||
return {
|
||||
entries: [{ url, triggeringPrincipal_base64 }],
|
||||
lastAccessed,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the given window focused and active
|
||||
*/
|
||||
async function switchToWindow(win) {
|
||||
info("switchToWindow, waiting for promiseFocus");
|
||||
await SimpleTest.promiseFocus(win);
|
||||
info("switchToWindow, waiting for correct Services.focus.activeWindow");
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() => Services.focus.activeWindow == win
|
||||
);
|
||||
}
|
||||
|
||||
async function changeSizeMode(win, mode) {
|
||||
let promise = BrowserTestUtils.waitForEvent(win, "sizemodechange");
|
||||
win[mode]();
|
||||
await promise;
|
||||
}
|
||||
|
||||
async function cleanup() {
|
||||
await switchToWindow(window);
|
||||
await SessionStoreTestUtils.promiseBrowserState(ORIG_STATE);
|
||||
is(
|
||||
BrowserWindowTracker.orderedWindows.length,
|
||||
1,
|
||||
"One window at the end of test cleanup"
|
||||
);
|
||||
info("cleanup, browser state restored");
|
||||
}
|
||||
|
||||
function deltaTime(time, expectedTime) {
|
||||
return Math.abs(expectedTime - time);
|
||||
}
|
||||
|
||||
function getWindowUrl(win) {
|
||||
return win.gBrowser.selectedBrowser?.currentURI?.spec;
|
||||
}
|
||||
|
||||
function getWindowByTabUrl(url) {
|
||||
return BrowserWindowTracker.orderedWindows.find(
|
||||
win => getWindowUrl(win) == url
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function restoredTabs() {
|
||||
const now = Date.now();
|
||||
await SessionStoreTestUtils.promiseBrowserState({
|
||||
windows: [
|
||||
{
|
||||
tabs: [
|
||||
tabEntry("data:,Window0-Tab0", yesterday),
|
||||
tabEntry("data:,Window0-Tab1", yesterday),
|
||||
],
|
||||
selected: 2,
|
||||
},
|
||||
],
|
||||
});
|
||||
is(
|
||||
gBrowser.visibleTabs[1],
|
||||
gBrowser.selectedTab,
|
||||
"The selected tab is the 2nd visible tab"
|
||||
);
|
||||
is(
|
||||
getWindowUrl(window),
|
||||
"data:,Window0-Tab1",
|
||||
"The expected tab is selected"
|
||||
);
|
||||
Assert.greaterOrEqual(
|
||||
gBrowser.selectedTab.lastSeenActive,
|
||||
now,
|
||||
"The selected tab's lastSeenActive is now"
|
||||
);
|
||||
Assert.greaterOrEqual(
|
||||
gBrowser.selectedTab.lastAccessed,
|
||||
now,
|
||||
"The selected tab's lastAccessed is now"
|
||||
);
|
||||
|
||||
// tab restored from session but never seen or active
|
||||
is(
|
||||
gBrowser.visibleTabs[0].lastSeenActive,
|
||||
yesterday,
|
||||
"The restored tab's lastSeenActive is yesterday"
|
||||
);
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
add_task(async function switchingTabs() {
|
||||
let now = Date.now();
|
||||
let initialTab = gBrowser.selectedTab;
|
||||
let applicationStart = Services.startup.getStartupInfo().start.getTime();
|
||||
let openedTab = BrowserTestUtils.addTab(gBrowser, "data:,Tab1");
|
||||
await BrowserTestUtils.browserLoaded(openedTab.linkedBrowser);
|
||||
|
||||
ok(!openedTab.selected, "The background tab we opened isn't selected");
|
||||
Assert.greaterOrEqual(
|
||||
initialTab.selected && initialTab.lastSeenActive,
|
||||
now,
|
||||
"The initial tab is selected and last seen now"
|
||||
);
|
||||
|
||||
is(
|
||||
openedTab.lastSeenActive,
|
||||
applicationStart,
|
||||
`Background tab got default lastSeenActive value, delta: ${deltaTime(
|
||||
openedTab.lastSeenActive,
|
||||
applicationStart
|
||||
)}`
|
||||
);
|
||||
|
||||
now = Date.now();
|
||||
await BrowserTestUtils.switchTab(gBrowser, openedTab);
|
||||
Assert.greaterOrEqual(
|
||||
openedTab.lastSeenActive,
|
||||
now,
|
||||
"The tab we switched to is last seen now"
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
add_task(async function switchingWindows() {
|
||||
info("Restoring to the test browser state");
|
||||
await SessionStoreTestUtils.promiseBrowserState({
|
||||
windows: [
|
||||
{
|
||||
tabs: [tabEntry("data:,Window1-Tab0", yesterday)],
|
||||
selected: 1,
|
||||
sizemodeBeforeMinimized: "normal",
|
||||
sizemode: "maximized",
|
||||
zIndex: 1, // this will be the selected window
|
||||
},
|
||||
{
|
||||
tabs: [tabEntry("data:,Window2-Tab0", yesterday)],
|
||||
selected: 1,
|
||||
sizemodeBeforeMinimized: "normal",
|
||||
sizemode: "maximized",
|
||||
zIndex: 2,
|
||||
},
|
||||
],
|
||||
});
|
||||
info("promiseBrowserState resolved");
|
||||
info(
|
||||
`BrowserWindowTracker.pendingWindows: ${BrowserWindowTracker.pendingWindows.size}`
|
||||
);
|
||||
await Promise.all(
|
||||
Array.from(BrowserWindowTracker.pendingWindows.values()).map(
|
||||
win => win.deferred.promise
|
||||
)
|
||||
);
|
||||
info("All the pending windows are resolved");
|
||||
info("Waiting for the firstBrowserLoaded in each of the windows");
|
||||
await Promise.all(
|
||||
BrowserWindowTracker.orderedWindows.map(win => {
|
||||
const selectedUrl = getWindowUrl(win);
|
||||
if (selectedUrl && selectedUrl !== "about:blank") {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return BrowserTestUtils.firstBrowserLoaded(win, false);
|
||||
})
|
||||
);
|
||||
let expectedTabURLs = ["data:,Window1-Tab0", "data:,Window2-Tab0"];
|
||||
let [win1, win2] = expectedTabURLs.map(url => getWindowByTabUrl(url));
|
||||
if (BrowserWindowTracker.getTopWindow() !== win1) {
|
||||
info("Switch to win1 which isn't active/top after restoring session");
|
||||
// In theory the zIndex values in the session state should make win1 active
|
||||
// But in practice that isn't always true. To ensure we're testing from a known state,
|
||||
// ensure the first window is active before proceeding with the test
|
||||
await switchToWindow(win1);
|
||||
[win1, win2] = expectedTabURLs.map(url => getWindowByTabUrl(url));
|
||||
}
|
||||
|
||||
let actualTabURLs = Array.from(BrowserWindowTracker.orderedWindows).map(win =>
|
||||
getWindowUrl(win)
|
||||
);
|
||||
Assert.deepEqual(
|
||||
actualTabURLs,
|
||||
expectedTabURLs,
|
||||
"Both windows are open with selected tab URLs in the expected order"
|
||||
);
|
||||
|
||||
let lastSeenTimes = [win1, win2].map(
|
||||
win => win.gBrowser.selectedTab.lastSeenActive
|
||||
);
|
||||
|
||||
info("Focusing the other window");
|
||||
await switchToWindow(win2);
|
||||
// wait a little so the timestamps will differ and then check again
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(res => setTimeout(res, 100));
|
||||
Assert.greater(
|
||||
win2.gBrowser.selectedTab.lastSeenActive,
|
||||
lastSeenTimes[1],
|
||||
"The foreground window selected tab is last seen more recently than it was before being focused"
|
||||
);
|
||||
Assert.greater(
|
||||
win2.gBrowser.selectedTab.lastSeenActive,
|
||||
win1.gBrowser.selectedTab.lastSeenActive,
|
||||
"The foreground window selected tab is last seen more recently than the backgrounded one"
|
||||
);
|
||||
|
||||
lastSeenTimes = [win1, win2].map(
|
||||
win => win.gBrowser.selectedTab.lastSeenActive
|
||||
);
|
||||
// minimize the foreground window and focus the other
|
||||
let promiseSizeModeChange = BrowserTestUtils.waitForEvent(
|
||||
win2,
|
||||
"sizemodechange"
|
||||
);
|
||||
win2.minimize();
|
||||
info("Waiting for the sizemodechange on minimized window");
|
||||
await promiseSizeModeChange;
|
||||
await switchToWindow(win1);
|
||||
|
||||
ok(
|
||||
!win2.gBrowser.selectedTab.linkedBrowser.docShellIsActive,
|
||||
"Docshell should be Inactive"
|
||||
);
|
||||
ok(win2.document.hidden, "Minimized windows's document should be hidden");
|
||||
|
||||
// wait a little so the timestamps will differ and then check again
|
||||
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||
await new Promise(res => setTimeout(res, 100));
|
||||
Assert.greater(
|
||||
win1.gBrowser.selectedTab.lastSeenActive,
|
||||
win2.gBrowser.selectedTab.lastSeenActive,
|
||||
"The foreground window selected tab is last seen more recently than the minimized one"
|
||||
);
|
||||
Assert.greater(
|
||||
win1.gBrowser.selectedTab.lastSeenActive,
|
||||
lastSeenTimes[0],
|
||||
"The foreground window selected tab is last seen more recently than it was before being focused"
|
||||
);
|
||||
|
||||
await cleanup();
|
||||
});
|
@ -1024,7 +1024,7 @@ function getTabListItems(tabs, isRecentBrowsing) {
|
||||
? JSON.stringify({ tabTitle: tab.label })
|
||||
: null,
|
||||
tabElement: tab,
|
||||
time: tab.lastAccessed,
|
||||
time: tab.lastSeenActive,
|
||||
title: tab.label,
|
||||
url,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user