141 lines
4.8 KiB
JavaScript
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);
|
|
});
|