зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1863783 - Ensure fxview page content only listens/observes and renders updates when visible. r=fxview-reviewers,sclements,jsudiaman
* Add a type=page to the top-level ViewPage instances * Rename viewTabVisibleCallback and viewTabHiddenCallback to view*Callback and call each when selectedness or visiblity changes * Ensure active view/pages are always properly initialized during page load and category switching * Add a test to verify no mutations happen when tabs change while firefox view is inactive * Fix tests to better account for loading and readiness sequence when activating firefox view Differential Revision: https://phabricator.services.mozilla.com/D193744
This commit is contained in:
Родитель
80de3c062f
Коммит
2cd97ee94c
|
@ -92,7 +92,7 @@
|
|||
<main id="pages" role="application" data-l10n-id="firefoxview-page-label">
|
||||
<div class="main-container">
|
||||
<named-deck>
|
||||
<view-recentbrowsing name="recentbrowsing">
|
||||
<view-recentbrowsing name="recentbrowsing" type="page">
|
||||
<div>
|
||||
<view-opentabs slot="opentabs"></view-opentabs>
|
||||
</div>
|
||||
|
@ -103,10 +103,13 @@
|
|||
<view-syncedtabs slot="syncedtabs"></view-syncedtabs>
|
||||
</div>
|
||||
</view-recentbrowsing>
|
||||
<view-history name="history"></view-history>
|
||||
<view-opentabs name="opentabs"></view-opentabs>
|
||||
<view-recentlyclosed name="recentlyclosed"></view-recentlyclosed>
|
||||
<view-syncedtabs name="syncedtabs"></view-syncedtabs>
|
||||
<view-history name="history" type="page"></view-history>
|
||||
<view-opentabs name="opentabs" type="page"></view-opentabs>
|
||||
<view-recentlyclosed
|
||||
name="recentlyclosed"
|
||||
type="page"
|
||||
></view-recentlyclosed>
|
||||
<view-syncedtabs name="syncedtabs" type="page"></view-syncedtabs>
|
||||
</named-deck>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
@ -3,25 +3,37 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let pageList = [];
|
||||
let categoryPagesDeck = null;
|
||||
let categoryNavigation = null;
|
||||
|
||||
const { topChromeWindow } = window.browsingContext;
|
||||
|
||||
function onHashChange() {
|
||||
changePage(document.location.hash.substring(1));
|
||||
let page = document.location?.hash.substring(1);
|
||||
if (!page || !pageList.includes(page)) {
|
||||
page = "recentbrowsing";
|
||||
}
|
||||
changePage(page);
|
||||
}
|
||||
|
||||
function changePage(page) {
|
||||
let navigation = document.querySelector("fxview-category-navigation");
|
||||
if (page && !pageList.includes(page)) {
|
||||
page = "recentbrowsing";
|
||||
document.location.hash = "recentbrowsing";
|
||||
}
|
||||
document.querySelector("named-deck").selectedViewName = page || pageList[0];
|
||||
navigation.currentCategory = page || pageList[0];
|
||||
if (navigation.categoryButtons.includes(document.activeElement)) {
|
||||
let currentCategoryButton = navigation.categoryButtons.find(
|
||||
categoryPagesDeck.selectedViewName = page;
|
||||
categoryNavigation.currentCategory = page;
|
||||
if (categoryNavigation.categoryButtons.includes(document.activeElement)) {
|
||||
let currentCategoryButton = categoryNavigation.categoryButtons.find(
|
||||
categoryButton => categoryButton.name === page
|
||||
);
|
||||
(currentCategoryButton || navigation.categoryButtons[0]).focus();
|
||||
(currentCategoryButton || categoryNavigation.categoryButtons[0]).focus();
|
||||
}
|
||||
}
|
||||
|
||||
function onPagesDeckViewChange() {
|
||||
for (const child of categoryPagesDeck.children) {
|
||||
if (child.getAttribute("name") == categoryPagesDeck.selectedViewName) {
|
||||
child.enter();
|
||||
} else {
|
||||
child.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,8 +59,11 @@ function recordNavigationTelemetry(source, eventTarget) {
|
|||
|
||||
window.addEventListener("DOMContentLoaded", async () => {
|
||||
recordEnteredTelemetry();
|
||||
let navigation = document.querySelector("fxview-category-navigation");
|
||||
for (const item of navigation.categoryButtons) {
|
||||
|
||||
categoryNavigation = document.querySelector("fxview-category-navigation");
|
||||
categoryPagesDeck = document.querySelector("named-deck");
|
||||
|
||||
for (const item of categoryNavigation.categoryButtons) {
|
||||
pageList.push(item.getAttribute("name"));
|
||||
}
|
||||
window.addEventListener("hashchange", onHashChange);
|
||||
|
@ -60,31 +75,26 @@ window.addEventListener("DOMContentLoaded", async () => {
|
|||
window.addEventListener("card-container-view-all", function (event) {
|
||||
recordNavigationTelemetry("view-all", event.originalTarget);
|
||||
});
|
||||
if (document.location.hash) {
|
||||
changePage(document.location.hash.substring(1));
|
||||
}
|
||||
|
||||
categoryPagesDeck.addEventListener("view-changed", onPagesDeckViewChange);
|
||||
|
||||
// set the initial state
|
||||
onHashChange();
|
||||
onPagesDeckViewChange();
|
||||
|
||||
if (Cu.isInAutomation) {
|
||||
Services.obs.notifyObservers(null, "firefoxview-entered");
|
||||
}
|
||||
});
|
||||
|
||||
document
|
||||
.querySelector("named-deck")
|
||||
.addEventListener("view-changed", async event => {
|
||||
for (const child of event.target.children) {
|
||||
if (child.getAttribute("name") == event.target.selectedViewName) {
|
||||
child.enter();
|
||||
} else {
|
||||
child.exit();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.visibilityState === "visible") {
|
||||
recordEnteredTelemetry();
|
||||
if (Cu.isInAutomation) {
|
||||
Services.obs.notifyObservers(null, "firefoxview-entered");
|
||||
// allow all the component visibilitychange handlers to execute before notifying
|
||||
requestAnimationFrame(() => {
|
||||
Services.obs.notifyObservers(null, "firefoxview-entered");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -127,7 +137,7 @@ function onCommand(e) {
|
|||
null,
|
||||
{
|
||||
menu_action: item.id,
|
||||
page: location.hash.substring(1) || "recentbrowsing",
|
||||
page: location.hash?.substring(1) || "recentbrowsing",
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ const SEARCH_DEBOUNCE_TIMEOUT_MS = 1000;
|
|||
class HistoryInView extends ViewPage {
|
||||
constructor() {
|
||||
super();
|
||||
this._started = false;
|
||||
this.allHistoryItems = new Map();
|
||||
this.historyMapByDate = [];
|
||||
this.historyMapBySite = [];
|
||||
|
@ -58,10 +59,25 @@ class HistoryInView extends ViewPage {
|
|||
this.fullyUpdated = false;
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = true;
|
||||
|
||||
this.#updateAllHistoryItems();
|
||||
this.placesQuery.observeHistory(data => this.#updateAllHistoryItems(data));
|
||||
|
||||
this.searchTask = new lazy.DeferredTask(
|
||||
() => this.#updateSearchResults(),
|
||||
SEARCH_DEBOUNCE_RATE_MS,
|
||||
SEARCH_DEBOUNCE_TIMEOUT_MS
|
||||
);
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
super.connectedCallback();
|
||||
await this.updateHistoryData();
|
||||
this.placesQuery.observeHistory(data => this.#updateAllHistoryItems(data));
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this,
|
||||
"importHistoryDismissedPref",
|
||||
|
@ -88,23 +104,26 @@ class HistoryInView extends ViewPage {
|
|||
// Convert milliseconds to days
|
||||
this.profileAge = profileAge / 1000 / 60 / 60 / 24;
|
||||
}
|
||||
this.searchTask = new lazy.DeferredTask(
|
||||
() => this.#updateSearchResults(),
|
||||
SEARCH_DEBOUNCE_RATE_MS,
|
||||
SEARCH_DEBOUNCE_TIMEOUT_MS
|
||||
);
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (!this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = false;
|
||||
this.placesQuery.close();
|
||||
if (!this.searchTask.isFinalized) {
|
||||
this.searchTask.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.placesQuery.close();
|
||||
this.stop();
|
||||
this.migrationWizardDialog?.removeEventListener(
|
||||
"MigrationWizard:Close",
|
||||
this.migrationWizardDialog
|
||||
);
|
||||
if (!this.searchTask.isFinalized) {
|
||||
this.searchTask.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
async #updateAllHistoryItems(allHistoryItems) {
|
||||
|
@ -133,13 +152,12 @@ class HistoryInView extends ViewPage {
|
|||
}
|
||||
}
|
||||
|
||||
viewTabVisibleCallback() {
|
||||
this.#updateAllHistoryItems();
|
||||
this.placesQuery.observeHistory(data => this.#updateAllHistoryItems(data));
|
||||
viewVisibleCallback() {
|
||||
this.start();
|
||||
}
|
||||
|
||||
viewTabHiddenCallback() {
|
||||
this.placesQuery.close();
|
||||
viewHiddenCallback() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
static queries = {
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
map,
|
||||
when,
|
||||
} from "chrome://global/content/vendor/lit.all.mjs";
|
||||
import { ViewPage } from "./viewpage.mjs";
|
||||
import { ViewPage, ViewPageContent } from "./viewpage.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
|
@ -39,10 +39,15 @@ class OpenTabsInView extends ViewPage {
|
|||
static properties = {
|
||||
windows: { type: Map },
|
||||
};
|
||||
static queries = {
|
||||
viewCards: { all: "view-opentabs-card" },
|
||||
};
|
||||
|
||||
static TAB_ATTRS_TO_WATCH = Object.freeze(["image", "label"]);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._started = false;
|
||||
this.everyWindowCallbackId = `firefoxview-${Services.uuid.generateUUID()}`;
|
||||
this.windows = new Map();
|
||||
this.currentWindow = this.getWindow();
|
||||
|
@ -53,13 +58,22 @@ class OpenTabsInView extends ViewPage {
|
|||
this.devices = [];
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
start() {
|
||||
if (this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = true;
|
||||
|
||||
Services.obs.addObserver(this.boundObserve, lazy.UIState.ON_UPDATE);
|
||||
Services.obs.addObserver(this.boundObserve, TOPIC_DEVICELIST_UPDATED);
|
||||
Services.obs.addObserver(this.boundObserve, TOPIC_CURRENT_BROWSER_CHANGED);
|
||||
|
||||
lazy.EveryWindow.registerCallback(
|
||||
this.everyWindowCallbackId,
|
||||
win => {
|
||||
if (win.gBrowser && this._shouldShowOpenTabs(win) && !win.closed) {
|
||||
const { tabContainer } = win.gBrowser;
|
||||
tabContainer.addEventListener("TabSelect", this);
|
||||
tabContainer.addEventListener("TabAttrModified", this);
|
||||
tabContainer.addEventListener("TabClose", this);
|
||||
tabContainer.addEventListener("TabMove", this);
|
||||
|
@ -75,6 +89,7 @@ class OpenTabsInView extends ViewPage {
|
|||
win => {
|
||||
if (win.gBrowser && this._shouldShowOpenTabs(win)) {
|
||||
const { tabContainer } = win.gBrowser;
|
||||
tabContainer.removeEventListener("TabSelect", this);
|
||||
tabContainer.removeEventListener("TabAttrModified", this);
|
||||
tabContainer.removeEventListener("TabClose", this);
|
||||
tabContainer.removeEventListener("TabMove", this);
|
||||
|
@ -86,41 +101,52 @@ class OpenTabsInView extends ViewPage {
|
|||
}
|
||||
}
|
||||
);
|
||||
this._updateOpenTabsList();
|
||||
this.addObserversIfNeeded();
|
||||
// EveryWindow will invoke the callback for existing windows - including this one
|
||||
// So this._updateOpenTabsList will get called for the already-open window
|
||||
|
||||
if (this.currentWindow.gSync) {
|
||||
this.devices = this.currentWindow.gSync.getSendTabTargets();
|
||||
}
|
||||
|
||||
for (let card of this.viewCards) {
|
||||
card.paused = false;
|
||||
card.viewVisibleCallback?.();
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.stop();
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (!this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = false;
|
||||
this.paused = true;
|
||||
|
||||
lazy.EveryWindow.unregisterCallback(this.everyWindowCallbackId);
|
||||
this.removeObserversIfNeeded();
|
||||
}
|
||||
|
||||
addObserversIfNeeded() {
|
||||
if (!this.observerAdded) {
|
||||
Services.obs.addObserver(this.boundObserve, lazy.UIState.ON_UPDATE);
|
||||
Services.obs.addObserver(this.boundObserve, TOPIC_DEVICELIST_UPDATED);
|
||||
Services.obs.addObserver(
|
||||
this.boundObserve,
|
||||
TOPIC_CURRENT_BROWSER_CHANGED
|
||||
);
|
||||
this.observerAdded = true;
|
||||
Services.obs.removeObserver(this.boundObserve, lazy.UIState.ON_UPDATE);
|
||||
Services.obs.removeObserver(this.boundObserve, TOPIC_DEVICELIST_UPDATED);
|
||||
Services.obs.removeObserver(
|
||||
this.boundObserve,
|
||||
TOPIC_CURRENT_BROWSER_CHANGED
|
||||
);
|
||||
|
||||
for (let card of this.viewCards) {
|
||||
card.paused = true;
|
||||
card.viewHiddenCallback?.();
|
||||
}
|
||||
}
|
||||
|
||||
removeObserversIfNeeded() {
|
||||
if (this.observerAdded) {
|
||||
Services.obs.removeObserver(this.boundObserve, lazy.UIState.ON_UPDATE);
|
||||
Services.obs.removeObserver(this.boundObserve, TOPIC_DEVICELIST_UPDATED);
|
||||
Services.obs.removeObserver(
|
||||
this.boundObserve,
|
||||
TOPIC_CURRENT_BROWSER_CHANGED
|
||||
);
|
||||
this.observerAdded = false;
|
||||
}
|
||||
viewVisibleCallback() {
|
||||
this.start();
|
||||
}
|
||||
|
||||
viewHiddenCallback() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
async observe(subject, topic, data) {
|
||||
|
@ -144,9 +170,6 @@ class OpenTabsInView extends ViewPage {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (!this.selectedTab && !this.recentBrowsing) {
|
||||
return null;
|
||||
}
|
||||
if (this.recentBrowsing) {
|
||||
return this.getRecentBrowsingTemplate();
|
||||
}
|
||||
|
@ -200,6 +223,7 @@ class OpenTabsInView extends ViewPage {
|
|||
<view-opentabs-card
|
||||
class=${cardClasses}
|
||||
.tabs=${currentWindowTabs}
|
||||
.paused=${this.paused}
|
||||
data-inner-id="${this.currentWindow.windowGlobalChild
|
||||
.innerWindowId}"
|
||||
data-l10n-id="firefoxview-opentabs-current-window-header"
|
||||
|
@ -216,6 +240,7 @@ class OpenTabsInView extends ViewPage {
|
|||
<view-opentabs-card
|
||||
class=${cardClasses}
|
||||
.tabs=${tabs}
|
||||
.paused=${this.paused}
|
||||
data-inner-id="${win.windowGlobalChild.innerWindowId}"
|
||||
data-l10n-id="firefoxview-opentabs-window-header"
|
||||
data-l10n-args="${JSON.stringify({ winID })}"
|
||||
|
@ -251,6 +276,7 @@ class OpenTabsInView extends ViewPage {
|
|||
return html`<view-opentabs-card
|
||||
.tabs=${tabs}
|
||||
.recentBrowsing=${true}
|
||||
.paused=${this.paused}
|
||||
.devices=${this.devices}
|
||||
></view-opentabs-card>`;
|
||||
}
|
||||
|
@ -259,6 +285,13 @@ class OpenTabsInView extends ViewPage {
|
|||
const win = target.ownerGlobal;
|
||||
const tabs = this.windows.get(win);
|
||||
switch (type) {
|
||||
case "TabSelect": {
|
||||
// if we're switching away from our tab, we can halt any updates immediately
|
||||
if (detail.previousTab == this.getBrowserTab()) {
|
||||
this.stop();
|
||||
}
|
||||
return;
|
||||
}
|
||||
case "TabAttrModified":
|
||||
if (
|
||||
!detail.changed.some(attr =>
|
||||
|
@ -328,7 +361,7 @@ customElements.define("view-opentabs", OpenTabsInView);
|
|||
* @property {string} title
|
||||
* The window title.
|
||||
*/
|
||||
class OpenTabsInViewCard extends ViewPage {
|
||||
class OpenTabsInViewCard extends ViewPageContent {
|
||||
static properties = {
|
||||
showMore: { type: Boolean },
|
||||
tabs: { type: Array },
|
||||
|
@ -512,6 +545,18 @@ class OpenTabsInViewCard extends ViewPage {
|
|||
);
|
||||
}
|
||||
|
||||
viewVisibleCallback() {
|
||||
if (this.tabList) {
|
||||
this.tabList.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
viewHiddenCallback() {
|
||||
if (this.tabList) {
|
||||
this.tabList.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link
|
||||
|
|
|
@ -11,11 +11,21 @@ class RecentBrowsingInView extends ViewPage {
|
|||
this.pageType = "recentbrowsing";
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
viewVisibleCallback() {
|
||||
for (let child of this.children) {
|
||||
let childView = child.firstElementChild;
|
||||
childView.paused = false;
|
||||
childView.viewVisibleCallback();
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {}
|
||||
viewHiddenCallback() {
|
||||
for (let child of this.children) {
|
||||
let childView = child.firstElementChild;
|
||||
childView.paused = true;
|
||||
childView.viewHiddenCallback();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
|
|
|
@ -33,6 +33,7 @@ function getWindow() {
|
|||
class RecentlyClosedTabsInView extends ViewPage {
|
||||
constructor() {
|
||||
super();
|
||||
this._started = false;
|
||||
this.boundObserve = (...args) => this.observe(...args);
|
||||
this.fullyUpdated = false;
|
||||
this.maxTabsLength = this.recentBrowsing ? 5 : 25;
|
||||
|
@ -62,55 +63,54 @@ class RecentlyClosedTabsInView extends ViewPage {
|
|||
}
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
start() {
|
||||
if (this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = true;
|
||||
this.paused = false;
|
||||
this.updateRecentlyClosedTabs();
|
||||
this.addObserversIfNeeded();
|
||||
|
||||
Services.obs.addObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
|
||||
);
|
||||
Services.obs.addObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH
|
||||
);
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (!this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = false;
|
||||
|
||||
Services.obs.removeObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
|
||||
);
|
||||
Services.obs.removeObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH
|
||||
);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.removeObserversIfNeeded();
|
||||
this.stop();
|
||||
}
|
||||
|
||||
addObserversIfNeeded() {
|
||||
if (!this.observerAdded) {
|
||||
Services.obs.addObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
|
||||
);
|
||||
Services.obs.addObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH
|
||||
);
|
||||
this.observerAdded = true;
|
||||
}
|
||||
// We remove all the observers when the instance is not visible to the user
|
||||
viewHiddenCallback() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
removeObserversIfNeeded() {
|
||||
if (this.observerAdded) {
|
||||
Services.obs.removeObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_CLOSED_OBJECTS_CHANGED
|
||||
);
|
||||
Services.obs.removeObserver(
|
||||
this.boundObserve,
|
||||
SS_NOTIFY_BROWSER_SHUTDOWN_FLUSH
|
||||
);
|
||||
this.observerAdded = false;
|
||||
}
|
||||
}
|
||||
|
||||
// we observe when a tab closes but since this notification fires more frequently and on
|
||||
// all windows, we remove the observer when another tab is selected
|
||||
viewTabHiddenCallback() {
|
||||
this.removeObserversIfNeeded();
|
||||
}
|
||||
|
||||
// we check for changes to the session store once the user return to this tab.
|
||||
viewTabVisibleCallback() {
|
||||
this.addObserversIfNeeded();
|
||||
this.updateRecentlyClosedTabs();
|
||||
// We add observers and check for changes to the session store once the user return to this tab.
|
||||
// or the instance becomes visible to the user
|
||||
viewVisibleCallback() {
|
||||
this.start();
|
||||
}
|
||||
|
||||
getTabStateValue(tab, key) {
|
||||
|
@ -282,9 +282,6 @@ class RecentlyClosedTabsInView extends ViewPage {
|
|||
}
|
||||
|
||||
render() {
|
||||
if (!this.selectedTab && !this.recentBrowsing) {
|
||||
return null;
|
||||
}
|
||||
return html`
|
||||
<link
|
||||
rel="stylesheet"
|
||||
|
|
|
@ -25,6 +25,7 @@ const UI_OPEN_STATE = "browser.tabs.firefox-view.ui-state.tab-pickup.open";
|
|||
class SyncedTabsInView extends ViewPage {
|
||||
constructor() {
|
||||
super();
|
||||
this._started = false;
|
||||
this.boundObserve = (...args) => this.observe(...args);
|
||||
this._currentSetupStateIndex = -1;
|
||||
this.errorState = null;
|
||||
|
@ -56,7 +57,13 @@ class SyncedTabsInView extends ViewPage {
|
|||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.addEventListener("click", this);
|
||||
this.ownerDocument.addEventListener("visibilitychange", this);
|
||||
}
|
||||
|
||||
start() {
|
||||
if (this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = true;
|
||||
Services.obs.addObserver(this.boundObserve, TOPIC_SETUPSTATE_CHANGED);
|
||||
Services.obs.addObserver(this.boundObserve, SYNCED_TABS_CHANGED);
|
||||
|
||||
|
@ -64,15 +71,21 @@ class SyncedTabsInView extends ViewPage {
|
|||
this.onVisibilityChange();
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
stop() {
|
||||
if (!this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = false;
|
||||
TabsSetupFlowManager.updateViewVisibility(this._id, "unloaded");
|
||||
this.ownerDocument?.removeEventListener("visibilitychange", this);
|
||||
this.onVisibilityChange();
|
||||
|
||||
Services.obs.removeObserver(this.boundObserve, TOPIC_SETUPSTATE_CHANGED);
|
||||
Services.obs.removeObserver(this.boundObserve, SYNCED_TABS_CHANGED);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.cleanup();
|
||||
super.disconnectedCallback();
|
||||
this.stop();
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
|
@ -114,15 +127,19 @@ class SyncedTabsInView extends ViewPage {
|
|||
if (event.type == "change") {
|
||||
TabsSetupFlowManager.syncOpenTabs(event.target);
|
||||
}
|
||||
|
||||
// Returning to fxview seems like a likely time for a device check
|
||||
if (event.type == "visibilitychange") {
|
||||
this.onVisibilityChange();
|
||||
}
|
||||
}
|
||||
|
||||
viewVisibleCallback() {
|
||||
this.start();
|
||||
}
|
||||
|
||||
viewHiddenCallback() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
onVisibilityChange() {
|
||||
const isVisible = document.visibilityState == "visible";
|
||||
const isOpen = this.open;
|
||||
const isVisible = this.isVisible;
|
||||
if (isVisible && isOpen) {
|
||||
this.update();
|
||||
TabsSetupFlowManager.updateViewVisibility(this._id, "visible");
|
||||
|
|
|
@ -51,35 +51,45 @@ async function openFirefoxViewTab(win) {
|
|||
);
|
||||
}
|
||||
await testScope.SimpleTest.promiseFocus(win);
|
||||
let fxviewTab = win.FirefoxViewHandler.tab;
|
||||
let alreadyLoaded =
|
||||
fxviewTab?.linkedBrowser.currentURI.spec.includes(getFirefoxViewURL()) &&
|
||||
fxviewTab?.linkedBrowser?.contentDocument?.readyState == "complete";
|
||||
let enteredPromise = alreadyLoaded
|
||||
? Promise.resolve()
|
||||
: TestUtils.topicObserved("firefoxview-entered");
|
||||
|
||||
const fxViewTab = win.FirefoxViewHandler.tab;
|
||||
const alreadyLoaded =
|
||||
fxViewTab?.linkedBrowser?.currentURI.spec.split("#")[0] ==
|
||||
getFirefoxViewURL();
|
||||
const enteredPromise =
|
||||
alreadyLoaded &&
|
||||
fxViewTab.linkedBrowser.contentDocument.visibilityState == "visible"
|
||||
? Promise.resolve()
|
||||
: TestUtils.topicObserved("firefoxview-entered");
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#firefox-view-button",
|
||||
{ type: "mousedown" },
|
||||
win.browsingContext
|
||||
);
|
||||
if (!fxviewTab?.selected) {
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#firefox-view-button",
|
||||
{ type: "mousedown" },
|
||||
win.browsingContext
|
||||
);
|
||||
await TestUtils.waitForTick();
|
||||
}
|
||||
|
||||
fxviewTab = win.FirefoxViewHandler.tab;
|
||||
assertFirefoxViewTab(win);
|
||||
Assert.ok(
|
||||
win.FirefoxViewHandler.tab.selected,
|
||||
"Firefox View tab is selected"
|
||||
);
|
||||
|
||||
if (!alreadyLoaded) {
|
||||
testScope.info("Not already loaded, waiting for browserLoaded");
|
||||
await BrowserTestUtils.browserLoaded(
|
||||
win.FirefoxViewHandler.tab.linkedBrowser
|
||||
);
|
||||
}
|
||||
await enteredPromise;
|
||||
return win.FirefoxViewHandler.tab;
|
||||
testScope.info(
|
||||
"openFirefoxViewTab, waiting for complete readyState, visible and firefoxview-entered"
|
||||
);
|
||||
await Promise.all([
|
||||
TestUtils.waitForCondition(() => {
|
||||
const document = fxviewTab.linkedBrowser.contentDocument;
|
||||
return (
|
||||
document.readyState == "complete" &&
|
||||
document.visibilityState == "visible"
|
||||
);
|
||||
}),
|
||||
enteredPromise,
|
||||
]);
|
||||
testScope.info("openFirefoxViewTab, ready resolved");
|
||||
return fxviewTab;
|
||||
}
|
||||
|
||||
function closeFirefoxViewTab(win) {
|
||||
|
|
|
@ -3,6 +3,8 @@ support-files = [ "../head.js"]
|
|||
|
||||
["browser_firefoxview_next.js"]
|
||||
|
||||
["browser_firefoxview_paused.js"]
|
||||
|
||||
["browser_firefoxview_next_general_telemetry.js"]
|
||||
fail-if = ["a11y_checks"] # Bug 1854625 clicked button and fxview-tab-row-secondary-button may not be focusable
|
||||
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* import-globals-from ../head.js */
|
||||
|
||||
const tabURL1 = "data:,Tab1";
|
||||
const tabURL2 = "data:,Tab2";
|
||||
const tabURL3 = "data:,Tab3";
|
||||
|
||||
const TestTabs = {};
|
||||
|
||||
function getTopLevelViewElements(document) {
|
||||
return {
|
||||
recentBrowsingView: document.querySelector(
|
||||
"named-deck > view-recentbrowsing"
|
||||
),
|
||||
recentlyClosedView: document.querySelector(
|
||||
"named-deck > view-recentlyclosed"
|
||||
),
|
||||
openTabsView: document.querySelector("named-deck > view-opentabs"),
|
||||
};
|
||||
}
|
||||
|
||||
async function getElements(document) {
|
||||
let { recentBrowsingView, recentlyClosedView, openTabsView } =
|
||||
getTopLevelViewElements(document);
|
||||
let recentBrowsingOpenTabsView =
|
||||
recentBrowsingView.querySelector("view-opentabs");
|
||||
let recentBrowsingOpenTabsList =
|
||||
recentBrowsingOpenTabsView?.viewCards[0]?.tabList;
|
||||
let recentBrowsingRecentlyClosedTabsView = recentBrowsingView.querySelector(
|
||||
"view-recentlyclosed"
|
||||
);
|
||||
let recentBrowsingRecentlyClosedTabsList =
|
||||
recentBrowsingRecentlyClosedTabsView?.tabList;
|
||||
|
||||
let recentlyClosedList = recentlyClosedView.tabList;
|
||||
let openTabsList =
|
||||
openTabsView.shadowRoot.querySelector("view-opentabs-card")?.tabList;
|
||||
|
||||
return {
|
||||
// recentbrowsing
|
||||
recentBrowsingView,
|
||||
recentBrowsingOpenTabsView,
|
||||
recentBrowsingOpenTabsList,
|
||||
recentBrowsingRecentlyClosedTabsView,
|
||||
recentBrowsingRecentlyClosedTabsList,
|
||||
|
||||
// recentlyclosed
|
||||
recentlyClosedView,
|
||||
recentlyClosedList,
|
||||
|
||||
// opentabs
|
||||
openTabsView,
|
||||
openTabsList,
|
||||
};
|
||||
}
|
||||
|
||||
async function nextFrame(global = window) {
|
||||
await new Promise(resolve => {
|
||||
global.requestAnimationFrame(() => {
|
||||
global.requestAnimationFrame(resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function setupOpenAndClosedTabs() {
|
||||
TestTabs.tab1 = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
tabURL1
|
||||
);
|
||||
TestTabs.tab2 = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
tabURL2
|
||||
);
|
||||
TestTabs.tab3 = await BrowserTestUtils.openNewForegroundTab(
|
||||
gBrowser,
|
||||
tabURL3
|
||||
);
|
||||
// close a tab so we have recently-closed tabs content
|
||||
await SessionStoreTestUtils.closeTab(TestTabs.tab3);
|
||||
}
|
||||
|
||||
async function checkFxRenderCalls(browser, elements, selectedView) {
|
||||
const sandbox = sinon.createSandbox();
|
||||
const topLevelViews = getTopLevelViewElements(browser.contentDocument);
|
||||
|
||||
// sanity-check the selectedView we were given
|
||||
ok(
|
||||
Object.values(topLevelViews).find(view => view == selectedView),
|
||||
`The selected view is in the topLevelViews`
|
||||
);
|
||||
|
||||
const elementSpies = new Map();
|
||||
const viewSpies = new Map();
|
||||
|
||||
for (let [elemName, elem] of Object.entries(topLevelViews)) {
|
||||
let spy;
|
||||
if (elem.render.isSinonProxy) {
|
||||
spy = elem.render;
|
||||
} else {
|
||||
info(`Creating spy for render on element: ${elemName}`);
|
||||
spy = sandbox.spy(elem, "render");
|
||||
}
|
||||
viewSpies.set(elem, spy);
|
||||
}
|
||||
for (let [elemName, elem] of Object.entries(elements)) {
|
||||
let spy;
|
||||
if (elem.render.isSinonProxy) {
|
||||
spy = elem.render;
|
||||
} else {
|
||||
info(`Creating spy for render on element: ${elemName}`);
|
||||
spy = sandbox.spy(elem, "render");
|
||||
}
|
||||
elementSpies.set(elem, spy);
|
||||
}
|
||||
|
||||
info("test switches to tab2");
|
||||
await BrowserTestUtils.switchTab(gBrowser, TestTabs.tab2);
|
||||
|
||||
// check all the top-level views are paused
|
||||
ok(
|
||||
topLevelViews.recentBrowsingView.paused,
|
||||
"The recent-browsing view is paused"
|
||||
);
|
||||
ok(
|
||||
topLevelViews.recentlyClosedView.paused,
|
||||
"The recently-closed tabs view is paused"
|
||||
);
|
||||
ok(topLevelViews.openTabsView.paused, "The open tabs view is paused");
|
||||
|
||||
function assertSpiesCalled(spiesMap, expectCalled) {
|
||||
let message = expectCalled ? "to be called" : "to not be called";
|
||||
for (let [elem, renderSpy] of spiesMap.entries()) {
|
||||
is(
|
||||
expectCalled,
|
||||
renderSpy.called,
|
||||
`Expected the render method spy on element ${elem.localName} ${message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await nextFrame();
|
||||
info("test removes tab1");
|
||||
await BrowserTestUtils.removeTab(TestTabs.tab1);
|
||||
await nextFrame();
|
||||
|
||||
assertSpiesCalled(viewSpies, false);
|
||||
assertSpiesCalled(elementSpies, false);
|
||||
|
||||
for (let renderSpy of [...viewSpies.values(), ...elementSpies.values()]) {
|
||||
renderSpy.resetHistory();
|
||||
}
|
||||
|
||||
info("test will re-open fxview");
|
||||
await openFirefoxViewTab(window);
|
||||
await nextFrame();
|
||||
|
||||
assertSpiesCalled(elementSpies, true);
|
||||
ok(
|
||||
selectedView.render.called,
|
||||
`Render was called on the selected top-level view: ${selectedView.localName}`
|
||||
);
|
||||
|
||||
// check all the other views did not render
|
||||
viewSpies.delete(selectedView);
|
||||
assertSpiesCalled(viewSpies, false);
|
||||
|
||||
sandbox.restore();
|
||||
}
|
||||
|
||||
add_task(async function test_recentbrowsing() {
|
||||
await setupOpenAndClosedTabs();
|
||||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const document = browser.contentDocument;
|
||||
is(document.querySelector("named-deck").selectedViewName, "recentbrowsing");
|
||||
|
||||
const {
|
||||
recentBrowsingView,
|
||||
recentBrowsingOpenTabsView,
|
||||
recentBrowsingOpenTabsList,
|
||||
recentBrowsingRecentlyClosedTabsView,
|
||||
recentBrowsingRecentlyClosedTabsList,
|
||||
} = await getElements(document);
|
||||
|
||||
ok(recentBrowsingView, "Found the recent-browsing view");
|
||||
ok(recentBrowsingOpenTabsView, "Found the recent-browsing open tabs view");
|
||||
ok(recentBrowsingOpenTabsList, "Found the recent-browsing open tabs list");
|
||||
ok(
|
||||
recentBrowsingRecentlyClosedTabsView,
|
||||
"Found the recent-browsing recently-closed tabs view"
|
||||
);
|
||||
ok(
|
||||
recentBrowsingRecentlyClosedTabsList,
|
||||
"Found the recent-browsing recently-closed tabs list"
|
||||
);
|
||||
|
||||
await checkFxRenderCalls(
|
||||
browser,
|
||||
{
|
||||
recentBrowsingView,
|
||||
recentBrowsingOpenTabsView,
|
||||
recentBrowsingOpenTabsList,
|
||||
recentBrowsingRecentlyClosedTabsView,
|
||||
recentBrowsingRecentlyClosedTabsList,
|
||||
},
|
||||
recentBrowsingView
|
||||
);
|
||||
});
|
||||
await BrowserTestUtils.removeTab(TestTabs.tab2);
|
||||
});
|
||||
|
||||
add_task(async function test_opentabs() {
|
||||
await setupOpenAndClosedTabs();
|
||||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const document = browser.contentDocument;
|
||||
const { openTabsView } = getTopLevelViewElements(document);
|
||||
|
||||
await navigateToCategoryAndWait(document, "opentabs");
|
||||
|
||||
const { openTabsList } = await getElements(document);
|
||||
ok(openTabsView, "Found the open tabs view");
|
||||
ok(openTabsList, "Found the first open tabs list");
|
||||
ok(!openTabsView.paused, "The open tabs view is un-paused");
|
||||
is(openTabsView.slot, "selected", "The open tabs view is selected");
|
||||
|
||||
await checkFxRenderCalls(
|
||||
browser,
|
||||
{
|
||||
openTabsView,
|
||||
openTabsList,
|
||||
},
|
||||
openTabsView
|
||||
);
|
||||
});
|
||||
await BrowserTestUtils.removeTab(TestTabs.tab2);
|
||||
});
|
||||
|
||||
add_task(async function test_recentlyclosed() {
|
||||
await setupOpenAndClosedTabs();
|
||||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const document = browser.contentDocument;
|
||||
const { recentlyClosedView } = getTopLevelViewElements(document);
|
||||
await navigateToCategoryAndWait(document, "recentlyclosed");
|
||||
|
||||
const { recentlyClosedList } = await getElements(document);
|
||||
ok(recentlyClosedView, "Found the recently-closed view");
|
||||
ok(recentlyClosedList, "Found the recently-closed list");
|
||||
ok(!recentlyClosedView.paused, "The recently-closed view is un-paused");
|
||||
|
||||
await checkFxRenderCalls(
|
||||
browser,
|
||||
{
|
||||
recentlyClosedView,
|
||||
recentlyClosedList,
|
||||
},
|
||||
recentlyClosedView
|
||||
);
|
||||
});
|
||||
await BrowserTestUtils.removeTab(TestTabs.tab2);
|
||||
});
|
|
@ -58,14 +58,6 @@ async function historyComponentReady(historyComponent) {
|
|||
is(expected, actual, `Total number of cards should be ${expected}`);
|
||||
}
|
||||
|
||||
async function openFirefoxView(win) {
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"#firefox-view-button",
|
||||
{ type: "mousedown" },
|
||||
win.browsingContext
|
||||
);
|
||||
}
|
||||
|
||||
async function historyTelemetry() {
|
||||
await TestUtils.waitForCondition(
|
||||
() => {
|
||||
|
@ -177,7 +169,7 @@ add_task(async function test_list_ordering() {
|
|||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, "about:firefoxview-next");
|
||||
|
||||
navigateToCategory(document, "history");
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
|
@ -201,9 +193,14 @@ add_task(async function test_list_ordering() {
|
|||
return historyComponent.lists[0].rowEls.length;
|
||||
});
|
||||
let firstHistoryLink = historyComponent.lists[0].rowEls[0].mainEl;
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
document,
|
||||
"visibilitychange"
|
||||
);
|
||||
await EventUtils.synthesizeMouseAtCenter(firstHistoryLink, {}, content);
|
||||
await historyTelemetry();
|
||||
await switchToFxViewTab(browser.ownerGlobal);
|
||||
await promiseHidden;
|
||||
await openFirefoxViewTab(browser.ownerGlobal);
|
||||
|
||||
// Test number of cards when sorted by site/domain
|
||||
await clearAllParentTelemetryEvents();
|
||||
|
@ -266,7 +263,7 @@ add_task(async function test_empty_states() {
|
|||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, "about:firefoxview-next");
|
||||
|
||||
navigateToCategory(document, "history");
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
|
@ -354,7 +351,7 @@ add_task(async function test_observers_removed_when_view_is_hidden() {
|
|||
);
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "history");
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
const historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
let visitList = await TestUtils.waitForCondition(() =>
|
||||
|
@ -363,7 +360,12 @@ add_task(async function test_observers_removed_when_view_is_hidden() {
|
|||
info("The list should show a visit from the new tab.");
|
||||
await TestUtils.waitForCondition(() => visitList.rowEls.length === 1);
|
||||
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
document,
|
||||
"visibilitychange"
|
||||
);
|
||||
await BrowserTestUtils.switchTab(gBrowser, tab);
|
||||
await promiseHidden;
|
||||
const { date } = await PlacesUtils.history
|
||||
.fetch(NEW_TAB_URL, {
|
||||
includeVisits: true,
|
||||
|
@ -377,7 +379,7 @@ add_task(async function test_observers_removed_when_view_is_hidden() {
|
|||
);
|
||||
|
||||
info("The list should update when Firefox View is visible.");
|
||||
await switchToFxViewTab(browser.ownerGlobal);
|
||||
await openFirefoxViewTab(browser.ownerGlobal);
|
||||
visitList = await TestUtils.waitForCondition(() =>
|
||||
historyComponent.cards?.[0]?.querySelector("fxview-tab-list")
|
||||
);
|
||||
|
@ -399,7 +401,7 @@ add_task(async function test_show_all_history_telemetry() {
|
|||
const { document } = browser.contentWindow;
|
||||
is(document.location.href, "about:firefoxview-next");
|
||||
|
||||
navigateToCategory(document, "history");
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
|
||||
let historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
|
@ -424,7 +426,7 @@ add_task(async function test_show_all_history_telemetry() {
|
|||
add_task(async function test_search_history() {
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "history");
|
||||
await navigateToCategoryAndWait(document, "history");
|
||||
const historyComponent = document.querySelector("view-history");
|
||||
historyComponent.profileAge = 8;
|
||||
await historyComponentReady(historyComponent);
|
||||
|
|
|
@ -20,9 +20,8 @@ add_setup(function () {
|
|||
async function navigateToOpenTabs(browser) {
|
||||
const document = browser.contentDocument;
|
||||
if (document.querySelector("named-deck").selectedViewName != "opentabs") {
|
||||
navigateToCategory(document, "opentabs");
|
||||
await navigateToCategoryAndWait(browser.contentDocument, "opentabs");
|
||||
}
|
||||
await TestUtils.waitForTick();
|
||||
}
|
||||
|
||||
function getOpenTabsComponent(browser) {
|
||||
|
@ -79,8 +78,14 @@ add_task(async function open_tab_same_window() {
|
|||
gBrowser.visibleTabs[0].linkedBrowser.currentURI.spec,
|
||||
"The first item represents the first visible tab"
|
||||
);
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
browser.contentDocument,
|
||||
"visibilitychange"
|
||||
);
|
||||
await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
await promiseHidden;
|
||||
});
|
||||
await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
|
||||
|
||||
const [originalTab, newTab] = gBrowser.visibleTabs;
|
||||
|
||||
await openFirefoxViewTab(window).then(async viewTab => {
|
||||
|
@ -92,7 +97,12 @@ add_task(async function open_tab_same_window() {
|
|||
is(tabItems.length, 2, "There are two items.");
|
||||
is(tabItems[1].url, TEST_URL, "The newly opened tab appears last.");
|
||||
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
browser.contentDocument,
|
||||
"visibilitychange"
|
||||
);
|
||||
tabItems[0].mainEl.click();
|
||||
await promiseHidden;
|
||||
});
|
||||
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
|
@ -104,7 +114,14 @@ add_task(async function open_tab_same_window() {
|
|||
const browser = viewTab.linkedBrowser;
|
||||
const cards = getCards(browser);
|
||||
let tabItems = await getRowsForCard(cards[0]);
|
||||
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
browser.contentDocument,
|
||||
"visibilitychange"
|
||||
);
|
||||
|
||||
tabItems[1].mainEl.click();
|
||||
await promiseHidden;
|
||||
});
|
||||
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
|
@ -128,11 +145,8 @@ add_task(async function open_tab_same_window() {
|
|||
return tabItems[0].url === TEST_URL;
|
||||
}
|
||||
);
|
||||
});
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
|
||||
await BrowserTestUtils.removeTab(newTab);
|
||||
await openFirefoxViewTab(window).then(async viewTab => {
|
||||
const browser = viewTab.linkedBrowser;
|
||||
const [card] = getCards(browser);
|
||||
await TestUtils.waitForCondition(
|
||||
async () => (await getRowsForCard(card)).length === 1,
|
||||
|
@ -300,11 +314,11 @@ add_task(async function toggle_show_more_link() {
|
|||
is(cards.length, NUMBER_OF_WINDOWS, "There are four windows.");
|
||||
lastCard = cards[NUMBER_OF_WINDOWS - 1];
|
||||
lastWindow = windows[NUMBER_OF_WINDOWS - 2];
|
||||
});
|
||||
|
||||
for (let i = 0; i < NUMBER_OF_TABS - 1; i++) {
|
||||
await BrowserTestUtils.openNewForegroundTab(lastWindow.gBrowser);
|
||||
}
|
||||
for (let i = 0; i < NUMBER_OF_TABS - 1; i++) {
|
||||
await BrowserTestUtils.openNewForegroundTab(lastWindow.gBrowser);
|
||||
}
|
||||
});
|
||||
|
||||
await openFirefoxViewTab(window).then(async viewTab => {
|
||||
const browser = viewTab.linkedBrowser;
|
||||
|
|
|
@ -159,10 +159,16 @@ add_task(async function test_single_window_tabs() {
|
|||
await openFirefoxViewTab(window).then(async viewTab => {
|
||||
const browser = viewTab.linkedBrowser;
|
||||
await checkTabList(browser, [tabURL2, tabURL1]);
|
||||
|
||||
// switch to the first tab
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
browser.contentDocument,
|
||||
"visibilitychange"
|
||||
);
|
||||
await BrowserTestUtils.switchTab(gBrowser, gBrowser.visibleTabs[0]);
|
||||
await promiseHidden;
|
||||
});
|
||||
|
||||
// switch to the first tab
|
||||
await BrowserTestUtils.switchTab(gBrowser, gBrowser.visibleTabs[0]);
|
||||
// and check the results in the open tabs section of Recent Browsing
|
||||
await openFirefoxViewTab(window).then(async viewTab => {
|
||||
const browser = viewTab.linkedBrowser;
|
||||
|
@ -184,20 +190,29 @@ add_task(async function test_multiple_window_tabs() {
|
|||
await openFirefoxViewTab(win2).then(async viewTab => {
|
||||
const browser = viewTab.linkedBrowser;
|
||||
await checkTabList(browser, [tabURL4, tabURL3, tabURL2, tabURL1]);
|
||||
});
|
||||
Assert.equal(
|
||||
tabUrl(win2.gBrowser.selectedTab),
|
||||
fxViewURL,
|
||||
`The selected tab in window 2 is ${fxViewURL}`
|
||||
);
|
||||
|
||||
info("Switching to first tab (tab3) in win2");
|
||||
await BrowserTestUtils.switchTab(win2.gBrowser, win2.gBrowser.visibleTabs[0]);
|
||||
Assert.equal(
|
||||
tabUrl(win2.gBrowser.selectedTab),
|
||||
tabURL3,
|
||||
`The selected tab in window 2 is ${tabURL3}`
|
||||
);
|
||||
Assert.equal(
|
||||
tabUrl(win2.gBrowser.selectedTab),
|
||||
fxViewURL,
|
||||
`The selected tab in window 2 is ${fxViewURL}`
|
||||
);
|
||||
|
||||
info("Switching to first tab (tab3) in win2");
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
browser.contentDocument,
|
||||
"visibilitychange"
|
||||
);
|
||||
await BrowserTestUtils.switchTab(
|
||||
win2.gBrowser,
|
||||
win2.gBrowser.visibleTabs[0]
|
||||
);
|
||||
Assert.equal(
|
||||
tabUrl(win2.gBrowser.selectedTab),
|
||||
tabURL3,
|
||||
`The selected tab in window 2 is ${tabURL3}`
|
||||
);
|
||||
await promiseHidden;
|
||||
});
|
||||
|
||||
info("Opening fxview in win2 to confirm tab3 is most recent");
|
||||
await openFirefoxViewTab(win2).then(async viewTab => {
|
||||
|
@ -221,10 +236,18 @@ add_task(async function test_multiple_window_tabs() {
|
|||
"In fxview, check result of activating window 1, where tab 2 is selected"
|
||||
);
|
||||
await checkTabList(browser, [tabURL2, tabURL3, tabURL4, tabURL1]);
|
||||
});
|
||||
|
||||
info("Switching to first visible tab (tab1) in win1");
|
||||
await BrowserTestUtils.switchTab(win1.gBrowser, win1.gBrowser.visibleTabs[0]);
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
browser.contentDocument,
|
||||
"visibilitychange"
|
||||
);
|
||||
info("Switching to first visible tab (tab1) in win1");
|
||||
await BrowserTestUtils.switchTab(
|
||||
win1.gBrowser,
|
||||
win1.gBrowser.visibleTabs[0]
|
||||
);
|
||||
await promiseHidden;
|
||||
});
|
||||
|
||||
// check result in the fxview in the 1st window
|
||||
info("Opening fxview in win1 to confirm tab1 is most recent");
|
||||
|
@ -272,10 +295,18 @@ add_task(async function test_minimize_restore_windows() {
|
|||
await openFirefoxViewTab(win2).then(async viewTab => {
|
||||
const browser = viewTab.linkedBrowser;
|
||||
await checkTabList(browser, [tabURL4, tabURL3, tabURL2, tabURL1]);
|
||||
|
||||
let promiseHidden = BrowserTestUtils.waitForEvent(
|
||||
browser.contentDocument,
|
||||
"visibilitychange"
|
||||
);
|
||||
info("Switching to the first tab (tab3) in 2nd window");
|
||||
await BrowserTestUtils.switchTab(
|
||||
win2.gBrowser,
|
||||
win2.gBrowser.visibleTabs[0]
|
||||
);
|
||||
await promiseHidden;
|
||||
});
|
||||
//
|
||||
info("Switching to the first tab (tab3) in 2nd window");
|
||||
await BrowserTestUtils.switchTab(win2.gBrowser, win2.gBrowser.visibleTabs[0]);
|
||||
|
||||
// then minimize the window, focusing the 1st window
|
||||
info("Minimizing win2, leaving tab 3 selected");
|
||||
|
|
|
@ -59,7 +59,7 @@ add_task(async function test_network_offline() {
|
|||
sandbox.spy(TabsSetupFlowManager, "tryToClearError");
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
Services.obs.notifyObservers(
|
||||
|
@ -115,7 +115,7 @@ add_task(async function test_sync_error() {
|
|||
const sandbox = await setupWithDesktopDevices();
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
Services.obs.notifyObservers(null, "weave:service:sync:error");
|
||||
|
|
|
@ -27,7 +27,7 @@ add_task(async function test_unconfigured_initial_state() {
|
|||
});
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
@ -46,7 +46,7 @@ add_task(async function test_unconfigured_initial_state() {
|
|||
EventUtils.synthesizeMouseAtCenter(
|
||||
emptyState.querySelector(`button[data-action="sign-in"]`),
|
||||
{},
|
||||
content
|
||||
browser.contentWindow
|
||||
);
|
||||
await TestUtils.waitForCondition(
|
||||
() =>
|
||||
|
@ -83,7 +83,7 @@ add_task(async function test_signed_in() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
@ -102,7 +102,7 @@ add_task(async function test_signed_in() {
|
|||
EventUtils.synthesizeMouseAtCenter(
|
||||
emptyState.querySelector(`button[data-action="add-device"]`),
|
||||
{},
|
||||
content
|
||||
browser.contentWindow
|
||||
);
|
||||
await TestUtils.waitForCondition(
|
||||
() =>
|
||||
|
@ -146,7 +146,7 @@ add_task(async function test_no_synced_tabs() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
@ -186,7 +186,7 @@ add_task(async function test_no_error_for_two_desktop() {
|
|||
|
||||
await withFirefoxView({}, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
@ -230,7 +230,7 @@ add_task(async function test_empty_state() {
|
|||
|
||||
await withFirefoxView({ openNewWindow: true }, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
@ -275,7 +275,7 @@ add_task(async function test_tabs() {
|
|||
|
||||
await withFirefoxView({ openNewWindow: true }, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
@ -363,7 +363,7 @@ add_task(async function test_empty_desktop_same_name() {
|
|||
|
||||
await withFirefoxView({ openNewWindow: true }, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
@ -411,7 +411,7 @@ add_task(async function test_empty_desktop_same_name_three() {
|
|||
|
||||
await withFirefoxView({ openNewWindow: true }, async browser => {
|
||||
const { document } = browser.contentWindow;
|
||||
navigateToCategory(document, "syncedtabs");
|
||||
await navigateToCategoryAndWait(document, "syncedtabs");
|
||||
Services.obs.notifyObservers(null, UIState.ON_UPDATE);
|
||||
|
||||
let syncedTabsComponent = document.querySelector(
|
||||
|
|
|
@ -597,6 +597,36 @@ function navigateToCategory(document, category) {
|
|||
navButton.buttonEl.click();
|
||||
}
|
||||
|
||||
async function navigateToCategoryAndWait(document, category) {
|
||||
info(`navigateToCategoryAndWait, for ${category}`);
|
||||
const navigation = document.querySelector("fxview-category-navigation");
|
||||
const win = document.ownerGlobal;
|
||||
SimpleTest.promiseFocus(win);
|
||||
let navButton = Array.from(navigation.categoryButtons).find(
|
||||
categoryButton => {
|
||||
return categoryButton.name === category;
|
||||
}
|
||||
);
|
||||
const namedDeck = document.querySelector("named-deck");
|
||||
|
||||
await BrowserTestUtils.waitForCondition(
|
||||
() => navButton.getBoundingClientRect().height,
|
||||
`Waiting for ${category} button to be clickable`
|
||||
);
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(navButton, {}, win);
|
||||
|
||||
await BrowserTestUtils.waitForCondition(() => {
|
||||
let selectedView = Array.from(namedDeck.children).find(
|
||||
child => child.slot == "selected"
|
||||
);
|
||||
return (
|
||||
namedDeck.selectedViewName == category &&
|
||||
selectedView?.getBoundingClientRect().height
|
||||
);
|
||||
}, `Waiting for ${category} to be visible`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to the Firefox View tab.
|
||||
*
|
||||
|
|
|
@ -15,64 +15,58 @@ import "chrome://browser/content/firefoxview/fxview-tab-list.mjs";
|
|||
|
||||
import { placeLinkOnClipboard } from "./helpers.mjs";
|
||||
|
||||
export class ViewPage extends MozLitElement {
|
||||
/**
|
||||
* A base class for content container views displayed on firefox-view.
|
||||
*
|
||||
* @property {boolean} recentBrowsing
|
||||
* Is part of the recentbrowsing page view
|
||||
* @property {boolean} paused
|
||||
* No content will be updated and rendered while paused
|
||||
*/
|
||||
export class ViewPageContent extends MozLitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
selectedTab: { type: Boolean },
|
||||
recentBrowsing: { type: Boolean },
|
||||
paused: { type: Boolean },
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.selectedTab = false;
|
||||
this.recentBrowsing = Boolean(this.closest("VIEW-RECENTBROWSING"));
|
||||
// don't update or render until explicitly un-paused
|
||||
this.paused = true;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.ownerDocument.addEventListener("visibilitychange", this);
|
||||
get ownerViewPage() {
|
||||
return this.closest("[type='page']") || this;
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.ownerDocument.removeEventListener("visibilitychange", this);
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "visibilitychange":
|
||||
if (this.ownerDocument.visibilityState === "visible") {
|
||||
this.viewTabVisibleCallback();
|
||||
} else {
|
||||
this.viewTabHiddenCallback();
|
||||
}
|
||||
break;
|
||||
get isVisible() {
|
||||
if (!this.isConnected || this.ownerDocument.visibilityState != "visible") {
|
||||
return false;
|
||||
}
|
||||
return this.ownerViewPage.selectedTab;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this function to run a callback whenever Firefox View is visible.
|
||||
* Override this function to run a callback whenever this content is visible.
|
||||
*/
|
||||
viewTabVisibleCallback() {}
|
||||
viewVisibleCallback() {}
|
||||
|
||||
/**
|
||||
* Override this function to run a callback whenever Firefox View is hidden.
|
||||
* Override this function to run a callback whenever this content is hidden.
|
||||
*/
|
||||
viewTabHiddenCallback() {}
|
||||
|
||||
enter() {
|
||||
this.selectedTab = true;
|
||||
}
|
||||
|
||||
exit() {
|
||||
this.selectedTab = false;
|
||||
}
|
||||
viewHiddenCallback() {}
|
||||
|
||||
getWindow() {
|
||||
return window.browsingContext.embedderWindowGlobal.browsingContext.window;
|
||||
}
|
||||
|
||||
getBrowserTab() {
|
||||
return this.getWindow().gBrowser.getTabForBrowser(
|
||||
window.browsingContext.embedderElement
|
||||
);
|
||||
}
|
||||
|
||||
copyLink(e) {
|
||||
placeLinkOnClipboard(this.triggerNode.title, this.triggerNode.url);
|
||||
this.recordContextMenuTelemetry("copy-link", e);
|
||||
|
@ -104,4 +98,73 @@ export class ViewPage extends MozLitElement {
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
shouldUpdate(changedProperties) {
|
||||
return !this.paused && super.shouldUpdate(changedProperties);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A "page" in firefox view, which may be hidden or shown by the named-deck container or
|
||||
* via the owner document's visibility
|
||||
*
|
||||
* @property {boolean} selectedTab
|
||||
* Is this page the selected view in the named-deck container
|
||||
*/
|
||||
export class ViewPage extends ViewPageContent {
|
||||
static get properties() {
|
||||
return {
|
||||
selectedTab: { type: Boolean },
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.selectedTab = false;
|
||||
this.recentBrowsing = Boolean(this.closest("VIEW-RECENTBROWSING"));
|
||||
this.onVisibilityChange = this.onVisibilityChange.bind(this);
|
||||
}
|
||||
|
||||
onVisibilityChange(event) {
|
||||
if (this.isVisible) {
|
||||
this.paused = false;
|
||||
this.viewVisibleCallback();
|
||||
} else if (
|
||||
this.ownerViewPage.selectedTab &&
|
||||
this.ownerDocument.visibilityState == "hidden"
|
||||
) {
|
||||
this.paused = true;
|
||||
this.viewHiddenCallback();
|
||||
}
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.ownerDocument.addEventListener(
|
||||
"visibilitychange",
|
||||
this.onVisibilityChange
|
||||
);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
this.ownerDocument.removeEventListener(
|
||||
"visibilitychange",
|
||||
this.onVisibilityChange
|
||||
);
|
||||
}
|
||||
|
||||
enter() {
|
||||
this.selectedTab = true;
|
||||
if (this.isVisible) {
|
||||
this.paused = false;
|
||||
this.viewVisibleCallback();
|
||||
}
|
||||
}
|
||||
|
||||
exit() {
|
||||
this.selectedTab = false;
|
||||
this.paused = true;
|
||||
this.viewHiddenCallback();
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче