Files
tubestation/waterfox/browser/components/sidebar/background/handle-tree-changes.js
2025-11-06 14:13:52 +00:00

141 lines
4.8 KiB
JavaScript

/*
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
'use strict';
import {
log as internalLogger,
configs
} from '/common/common.js';
import * as Constants from '/common/constants.js';
import * as TabsStore from '/common/tabs-store.js';
import * as TreeBehavior from '/common/tree-behavior.js';
import { Tab, TreeItem } from '/common/TreeItem.js';
import * as Background from './background.js';
import * as BackgroundCache from './background-cache.js';
import * as Tree from './tree.js';
import * as TreeStructure from './tree-structure.js';
function log(...args) {
internalLogger('background/handle-tree-changes', ...args);
}
let mInitialized = false;
function reserveDetachHiddenTab(tab) {
reserveDetachHiddenTab.tabs.add(tab);
if (reserveDetachHiddenTab.reserved)
clearTimeout(reserveDetachHiddenTab.reserved);
reserveDetachHiddenTab.reserved = setTimeout(async () => {
delete reserveDetachHiddenTab.reserved;
const tabs = new Set(TreeItem.sort(Array.from(reserveDetachHiddenTab.tabs)));
reserveDetachHiddenTab.tabs.clear();
log('try to detach hidden tabs: ', tabs);
for (const tab of tabs) {
if (!TabsStore.ensureLivingItem(tab))
continue;
for (const descendant of tab.$TST.descendants) {
if (descendant.hidden)
continue;
const nearestVisibleAncestor = descendant.$TST.ancestors.find(ancestor => !ancestor.hidden && !tabs.has(ancestor));
if (nearestVisibleAncestor &&
nearestVisibleAncestor == descendant.$TST.parent)
continue;
for (const ancestor of descendant.$TST.ancestors) {
if (!ancestor.hidden &&
!ancestor.$TST.collapsed)
break;
if (!ancestor.$TST.subtreeCollapsed)
continue;
await Tree.collapseExpandSubtree(ancestor, {
collapsed: false,
broadcast: true
});
}
if (nearestVisibleAncestor) {
log(` => reattach descendant ${descendant.id} to ${nearestVisibleAncestor.id}`);
await Tree.attachTabTo(descendant, nearestVisibleAncestor, {
dontMove: true,
broadcast: true
});
}
else {
log(` => detach descendant ${descendant.id}`);
await Tree.detachTab(descendant, {
broadcast: true
});
}
}
if (tab.$TST.hasParent &&
!tab.$TST.parent.hidden) {
log(` => detach hidden tab ${tab.id}`);
await Tree.detachTab(tab, {
broadcast: true
});
}
}
}, 100);
}
reserveDetachHiddenTab.tabs = new Set();
Tab.onHidden.addListener(tab => {
if (configs.fixupTreeOnTabVisibilityChanged)
reserveDetachHiddenTab(tab);
});
function reserveAttachShownTab(tab) {
tab.$TST.addState(Constants.kTAB_STATE_SHOWING);
reserveAttachShownTab.tabs.add(tab);
if (reserveAttachShownTab.reserved)
clearTimeout(reserveAttachShownTab.reserved);
reserveAttachShownTab.reserved = setTimeout(async () => {
delete reserveAttachShownTab.reserved;
const tabs = new Set(TreeItem.sort(Array.from(reserveAttachShownTab.tabs)));
reserveAttachShownTab.tabs.clear();
log('try to attach shown tabs: ', tabs);
for (const tab of tabs) {
if (!TabsStore.ensureLivingItem(tab) ||
tab.$TST.hasParent) {
tab.$TST.removeState(Constants.kTAB_STATE_SHOWING);
continue;
}
const referenceTabs = TreeBehavior.calculateReferenceItemsFromInsertionPosition(tab, {
context: Constants.kINSERTION_CONTEXT_SHOWN,
insertAfter: tab.$TST.nearestVisiblePrecedingTab,
// Instead of nearestFollowingForeignerTab, to avoid placing the tab
// after hidden tabs (too far from the target)
insertBefore: tab.$TST.unsafeNearestFollowingForeignerTab
});
if (referenceTabs.parent) {
log(` => attach shown tab ${tab.id} to ${referenceTabs.parent.id}`);
await Tree.attachTabTo(tab, referenceTabs.parent, {
insertBefore: referenceTabs.insertBefore,
insertAfter: referenceTabs.insertAfter,
broadcast: true
});
}
tab.$TST.removeState(Constants.kTAB_STATE_SHOWING);
}
}, 100);
}
reserveAttachShownTab.tabs = new Set();
Tab.onShown.addListener(tab => {
if (configs.fixupTreeOnTabVisibilityChanged)
reserveAttachShownTab(tab);
});
Background.onReady.addListener(() => {
mInitialized = true;
});
Tree.onSubtreeCollapsedStateChanged.addListener((tab, _info) => {
if (mInitialized)
TreeStructure.reserveToSaveTreeStructure(tab.windowId);
BackgroundCache.markWindowCacheDirtyFromTab(tab, Constants.kWINDOW_STATE_CACHED_SIDEBAR_COLLAPSED_DIRTY);
});