From da7f93a5c13b84db6af00bf399a3e9d42b2dbcff Mon Sep 17 00:00:00 2001 From: metastable-void Date: Sat, 6 May 2023 19:53:10 +0900 Subject: [PATCH] popup rendering via BrowserStateDao --- .../fragments/SiteDetailsFragmentBuilder.ts | 55 ++++++-------- .../fragments/SitesFragmentBuilder.ts | 17 +++-- src/pages/popup-v2/popup-v2.ts | 76 ++++++------------- 3 files changed, 57 insertions(+), 91 deletions(-) diff --git a/src/pages/popup-v2/fragments/SiteDetailsFragmentBuilder.ts b/src/pages/popup-v2/fragments/SiteDetailsFragmentBuilder.ts index a548ff0c..6102c2a1 100644 --- a/src/pages/popup-v2/fragments/SiteDetailsFragmentBuilder.ts +++ b/src/pages/popup-v2/fragments/SiteDetailsFragmentBuilder.ts @@ -24,22 +24,27 @@ import { SetMap } from "weeg-types"; import { CompatTab } from "weeg-tabs"; import { DisplayedContainer } from "weeg-containers"; -import { AbstractFragmentBuilder } from "./AbstractFragmentBuilder"; +import { BrowserStateDao } from "../../../lib/states/BrowserStateDao"; +import { DisplayedContainerDao } from "../../../lib/states/DisplayedContainerDao"; +import { TabGroupDirectorySnapshot } from "../../../lib/tabGroups/TabGroupDirectorySnapshot"; +import { TabDao } from "../../../lib/states/TabDao"; +import { IndexTabService } from "../../../lib/tabs/IndexTabService"; + import { CtgFragmentElement } from "../../../components/ctg/ctg-fragment"; import { CtgTopBarElement } from "../../../components/ctg/ctg-top-bar"; + +import { AbstractFragmentBuilder } from "./AbstractFragmentBuilder"; import { PopupRendererService } from "../PopupRendererService"; -import { BrowserStateSnapshot } from "../../../legacy-lib/tabs/BrowserStateSnapshot"; -import { IndexTab } from "../../../legacy-lib/modules/IndexTab"; export class SiteDetailsFragmentBuilder extends AbstractFragmentBuilder { protected static override readonly suppressBottomNavigation = true; private readonly _popupRenderer = PopupRendererService.getInstance().popupRenderer; + private readonly _indexTabService = IndexTabService.getInstance(); + private _domain = '(none)'; private _tabCount = 0; - private _firstPartyStateSnapshot: ReadonlyMap> | null = null; - private _browserStateSnapshot: BrowserStateSnapshot | null = null; - private _isPrivate = false; + private _browserState: BrowserStateDao | null = null; private _definedDisplayedContainers: DisplayedContainer[] = []; private _tabs: CompatTab[] = []; @@ -75,11 +80,8 @@ export class SiteDetailsFragmentBuilder extends AbstractFragmentBuilder { topBarElement.headingText = `${this._domain || '(null)'} (${this._tabCount})`; } - public render(browserStateSnapshot: BrowserStateSnapshot, isPrivate: boolean): void { - this._browserStateSnapshot = browserStateSnapshot; - const firstPartyStateSnapshot = browserStateSnapshot.getFirstPartyStateSnapshot(isPrivate); - this._firstPartyStateSnapshot = firstPartyStateSnapshot; - this._isPrivate = isPrivate; + public render(browserState: BrowserStateDao): void { + this._browserState = browserState; this.setSite(); if (this.active) { this.renderTopBarWithGlobalItems(); @@ -89,26 +91,23 @@ export class SiteDetailsFragmentBuilder extends AbstractFragmentBuilder { } private setSite(): void { - if (this._firstPartyStateSnapshot == null || this._browserStateSnapshot == null) { + if (this._browserState == null) { return; } - const tabGroupDirectorySnapshot = this._browserStateSnapshot.getTabGroupDirectorySnapshot(); - const tabs = this._firstPartyStateSnapshot.get(this._domain) ?? new Set(); - this._tabCount = tabs.size; + const browserState = this._browserState; + const tabGroupDirectorySnapshot = new TabGroupDirectorySnapshot(browserState.supergroups); + const displayedContainers = browserState.displayedContainers.map((dao) => DisplayedContainerDao.toDisplayedContainer(dao)); + this._definedDisplayedContainers = [... displayedContainers].sort((a, b) => { + return tabGroupDirectorySnapshot.cookieStoreIdSortingCallback(a.cookieStore.id, b.cookieStore.id); + }); + const tabIds = browserState.tabIdsBySite[this._domain] ?? []; + const tabs = this._indexTabService.filterOutIndexTabs(tabIds.map((tabId) => TabDao.toCompatTab(browserState.tabs[tabId] as TabDao))); + this._tabCount = tabs.length; this._tabs = [... tabs]; - if (this._isPrivate) { - this._definedDisplayedContainers = [... this._browserStateSnapshot.getDisplayedContainers()].sort((a, b) => { - return tabGroupDirectorySnapshot.cookieStoreIdSortingCallback(a.cookieStore.id, b.cookieStore.id); - }).filter((displayedContainer) => displayedContainer.cookieStore.isPrivate == true); - } else { - this._definedDisplayedContainers = [... this._browserStateSnapshot.getDisplayedContainers()].sort((a, b) => { - return tabGroupDirectorySnapshot.cookieStoreIdSortingCallback(a.cookieStore.id, b.cookieStore.id); - }).filter((displayedContainer) => displayedContainer.cookieStore.isPrivate == false); - } } public renderSite(): void { - if (this._firstPartyStateSnapshot == null) { + if (this._browserState == null) { return; } const tabsByUserContextId = new SetMap(); @@ -134,14 +133,10 @@ export class SiteDetailsFragmentBuilder extends AbstractFragmentBuilder { if (!userContext) { continue; } - const userContextElement = this._popupRenderer.renderContainerForFirstPartyDomain(this._domain, userContext, this._isPrivate); + const userContextElement = this._popupRenderer.renderContainerForFirstPartyDomain(this._domain, userContext, userContext.cookieStore.isPrivate); fragment.appendChild(userContextElement); let tabCount = 0; for (const tab of tabs) { - if (IndexTab.isIndexTabUrl(tab.url)) { - continue; - } - const tabElement = this._popupRenderer.renderTab(tab, userContext); userContextElement.appendChild(tabElement); tabCount++; diff --git a/src/pages/popup-v2/fragments/SitesFragmentBuilder.ts b/src/pages/popup-v2/fragments/SitesFragmentBuilder.ts index 8005f791..4fa98a87 100644 --- a/src/pages/popup-v2/fragments/SitesFragmentBuilder.ts +++ b/src/pages/popup-v2/fragments/SitesFragmentBuilder.ts @@ -22,17 +22,19 @@ import browser from "webextension-polyfill"; import { EventSink } from "weeg-events"; import { HostnameService } from "weeg-domains"; -import { CompatTab } from "weeg-tabs"; import { TabQueryService } from "../../../lib/tabs/TabQueryService"; import { IndexTabService } from "../../../lib/tabs/IndexTabService"; import { CompatConsole } from "../../../lib/console/CompatConsole"; +import { BrowserStateDao } from "../../../lib/states/BrowserStateDao"; +import { TabDao } from "../../../lib/states/TabDao"; -import { AbstractFragmentBuilder } from "./AbstractFragmentBuilder"; import { CtgFragmentElement } from "../../../components/ctg/ctg-fragment"; import { CtgTopBarElement } from "../../../components/ctg/ctg-top-bar"; import { MenulistSiteElement } from "../../../components/menulist-site"; +import { AbstractFragmentBuilder } from "./AbstractFragmentBuilder"; + const console = new CompatConsole(CompatConsole.tagFromFilename(__filename)); export class SitesFragmentBuilder extends AbstractFragmentBuilder { @@ -68,17 +70,18 @@ export class SitesFragmentBuilder extends AbstractFragmentBuilder { topBarElement.headingText = browser.i18n.getMessage('sitesN', this._siteCount.toFixed(0)); } - public render(firstPartyStateSnapshot: ReadonlyMap>): void { - this._siteCount = firstPartyStateSnapshot.size; + public render(browserState: BrowserStateDao): void { + const keys = Object.keys(browserState.tabIdsBySite); + this._siteCount = keys.length; if (this.active) { this.renderTopBarWithGlobalItems(); } const fragment = this.getFragment(); fragment.textContent = ''; - const hostnames = this._hostnameService.sortDomains([... firstPartyStateSnapshot.keys()]); + const hostnames = this._hostnameService.sortDomains([... keys]); for (const domain of hostnames) { - const tabSet = firstPartyStateSnapshot.get(domain) ?? new Set(); - const tabs = this._indexTabService.filterOutIndexTabs([... tabSet]); + const tabIds = browserState.tabIdsBySite[domain] ?? []; + const tabs = this._indexTabService.filterOutIndexTabs(tabIds.map((tabId) => TabDao.toCompatTab(browserState.tabs[tabId] as TabDao))); const lastAccessedTab = tabs.reduce((a, b) => a.lastAccessed > b.lastAccessed ? a : b); const siteElement = new MenulistSiteElement(); siteElement.domain = domain || '(null)'; diff --git a/src/pages/popup-v2/popup-v2.ts b/src/pages/popup-v2/popup-v2.ts index a3b3524f..a6803d76 100644 --- a/src/pages/popup-v2/popup-v2.ts +++ b/src/pages/popup-v2/popup-v2.ts @@ -21,13 +21,10 @@ import browser from "webextension-polyfill"; import { MessagingService } from "weeg-utils"; -import { ViewRefreshHandler } from "weeg-utils"; +import { PromiseUtils } from "weeg-utils"; import { ConfigurationOption } from '../../lib/config/ConfigurationOption'; -import { TabGroupDirectory } from "../../lib/tabGroups/TabGroupDirectory"; import { ExternalServiceProvider } from "../../lib/ExternalServiceProvider"; -import { TagDirectory } from "../../lib/tabGroups/TagDirectory"; -import { TagService } from "../../lib/tabGroups/TagService"; import { FpiService } from "../../lib/config/FpiService"; import { CompatConsole } from "../../lib/console/CompatConsole"; import { BrowserStateStore } from "../../lib/states/BrowserStateStore"; @@ -54,7 +51,6 @@ import { HelpFragmentBuilder } from "./fragments/HelpFragmentBuilder"; import { ContainerDetailsFragmentBuilder } from "./fragments/ContainerDetailsFragmentBuilder"; import { SiteDetailsFragmentBuilder } from "./fragments/SiteDetailsFragmentBuilder"; -import { BrowserStateSnapshot } from "../../legacy-lib/tabs/BrowserStateSnapshot"; import { config, privacyConfig } from '../../config/config'; @@ -73,11 +69,8 @@ if (searchParams.get('popup') == '1') { } ExternalServiceProvider.getInstance(); -const tabGroupDirectory = new TabGroupDirectory(); -const tagDirectory = new TagDirectory(); const popupRenderer = PopupRendererService.getInstance().popupRenderer; const messagingService = MessagingService.getInstance(); -const tagService = TagService.getInstance(); const fpiService = FpiService.getInstance(); const extensionName = browser.runtime.getManifest().name; @@ -143,48 +136,6 @@ topBarElement.onBackButtonClicked.addListener(() => { // not used globalMenuItems.defineDrawerMenuItems(drawerElement); -const renderer = new ViewRefreshHandler(async () => { - topBarElement.beginSpinnerTransaction('popup-rendering'); - try { - const browserSnapshotStart = Date.now(); - const browserStateSnapshot = await BrowserStateSnapshot.create(); - const browserSnapshotEnd = Date.now(); - const browserSnapshotTime = browserSnapshotEnd - browserSnapshotStart; - if (browserSnapshotTime > 500) { - console.info(`Browser snapshot took ${browserSnapshotTime}ms`); - } - const currentWindowSnapshot = browserStateSnapshot.getWindowStateSnapshot(browserStateSnapshot.currentWindowId); - sitesBuilder.render(browserStateSnapshot.getFirstPartyStateSnapshot(currentWindowSnapshot.isPrivate)); - - siteDetailsBuilder.render(browserStateSnapshot, currentWindowSnapshot.isPrivate); - } finally { - topBarElement.endSpinnerTransaction('popup-rendering'); - // console.debug('rendering done'); - } -}); - -const renderInBackground = renderer.renderInBackground.bind(renderer); - -browser.tabs.onActivated.addListener(renderInBackground); -browser.tabs.onUpdated.addListener(renderInBackground); -browser.tabs.onCreated.addListener(renderInBackground); -browser.tabs.onRemoved.addListener(renderInBackground); -browser.tabs.onMoved.addListener(renderInBackground); -browser.tabs.onAttached.addListener(renderInBackground); -browser.tabs.onDetached.addListener(renderInBackground); -browser.tabs.onReplaced.addListener(renderInBackground); // this is not necessary in Firefox -browser.windows.onCreated.addListener(renderInBackground); -browser.windows.onRemoved.addListener(renderInBackground); -browser.contextualIdentities.onCreated.addListener(renderInBackground); -browser.contextualIdentities.onUpdated.addListener(renderInBackground); -browser.contextualIdentities.onRemoved.addListener(renderInBackground); - -tabGroupDirectory.onChanged.addListener(renderInBackground); -tagDirectory.onChanged.addListener(renderInBackground); -tagService.onChanged.addListener(renderInBackground); - -renderer.renderInBackground(); - // Containers view containersBuilder.onContainerSelected.addListener((cookieStoreId) => { @@ -299,9 +250,26 @@ messagingService.addListener('tab-sorting-ended', () => { }); const store = new BrowserStateStore(); + +let rendering = false; +const render = async () => { + if (rendering) return; + rendering = true; + try { + await PromiseUtils.sleep(200); + const value = store.value; + windowsBuilder.render(value); + containersBuilder.render(value); + containerDetailsBuilder.render(value); + sitesBuilder.render(value); + siteDetailsBuilder.render(value); + } finally { + rendering = false; + } +}; + store.onChanged.addListener(() => { - windowsBuilder.render(store.value); - containersBuilder.render(store.value); - containerDetailsBuilder.render(store.value); - console.debug('BrowserStateStore changed:', store.value); + render().catch((e) => { + console.error('Rendering errored:', e); + }); });