Skip to content

Commit adcb1d7

Browse files
committed
Maintain tree structure to match to native tab groups on startup
1 parent 0d889da commit adcb1d7

2 files changed

Lines changed: 98 additions & 51 deletions

File tree

webextensions/background/background.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ export async function init() {
186186

187187
Sync.init();
188188

189+
await Tree.startToMaintainTreeForNativeTabGroups();
190+
189191
await MetricsData.addAsync('init: exporting tabs to sidebars', notifyReadyToSidebars());
190192

191193
log(`Startup metrics for ${TabsStore.tabs.size} tabs: `, MetricsData.toString());

webextensions/background/tree.js

Lines changed: 96 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,21 +2144,6 @@ export function detectTabActionFromNewPosition(tab, moveInfo = {}) {
21442144
}
21452145

21462146

2147-
function maintainTreeForUpdatedNativeTabGroups({ windowId, groupId }, options = {}) {
2148-
let timer = maintainTreeForUpdatedNativeTabGroups.delayed.get(groupId);
2149-
if (timer)
2150-
clearTimeout(timer);
2151-
if (options.justNow || !shouldApplyAnimation()) {
2152-
return maintainTreeForNativeTabGroups({ windowId, groupId });
2153-
}
2154-
timer = setTimeout(() => {
2155-
maintainTreeForUpdatedNativeTabGroups.delayed.delete(groupId);
2156-
maintainTreeForNativeTabGroups({ windowId, groupId });
2157-
}, 100);
2158-
maintainTreeForUpdatedNativeTabGroups.delayed.set(groupId, timer);
2159-
}
2160-
maintainTreeForUpdatedNativeTabGroups.delayed = new Map();
2161-
21622147
/*
21632148
*************************************************************************
21642149
Logic to maintain tree structure based on modified native tab groups
@@ -2183,7 +2168,8 @@ Firefox's native tab groups feature's basics:
21832168
* When some of already grouped tabs are moved to another group, they will
21842169
be moved AFTER existing members of the destination group.
21852170
2186-
TST should imitate Firefox's behavior, and should do more:
2171+
TST should imitate Firefox's behavior, and should do more with the method
2172+
maintainTreeForNativeTabGroup():
21872173
21882174
* A tree should not be separated with multiple groups.
21892175
All member tabs in a tree should be grouped with a same tab group,
@@ -2197,14 +2183,22 @@ TST should imitate Firefox's behavior, and should do more:
21972183
they will be moved by Firefox. TST should DO NOTHING EXTRA ON THIS
21982184
CASE, because tree structure of moved tabs are automatically
21992185
maintained.
2200-
* When TST is activated and detects a tree is separated to multiple
2201-
groups,
2202-
TST should separate the tree to multiple parts for each group.
2203-
This operation will be done based on native tab groups.
2204-
Member tabs of each group will be detached from the source tree, and
2205-
this operation will be done for all groups repeatedly.
2186+
2187+
And, what we should do when TST is activated and detects a tree is
2188+
separated to multiple groups? For example:
2189+
2190+
* tab1
2191+
* tab2 [group1]
2192+
* tab3 [group1]
2193+
* tab4
2194+
* tab5 [group2]
2195+
* tab6 [group2]
2196+
2197+
On this case we need to move only tab1 and tab4, otherwise moving of
2198+
already grouped tabs will break those groups. The method
2199+
maintainTreeForNativeTabGroup() does that too.
22062200
*/
2207-
async function maintainTreeForNativeTabGroups({ windowId, groupId }) {
2201+
async function maintainTreeForNativeTabGroup({ windowId, groupId }) {
22082202
const win = TabsStore.windows.get(windowId);
22092203

22102204
const members = Tab.getNativeGroupMemberTabs({ windowId, groupId });
@@ -2214,63 +2208,114 @@ async function maintainTreeForNativeTabGroups({ windowId, groupId }) {
22142208
return;
22152209
}
22162210

2217-
log('maintainTreeForNativeTabGroups: wholeTree = ', wholeTree);
2211+
log(`maintainTreeForNativeTabGroup: groupId = ${groupId}, wholeTree = `, () => wholeTree.map(tab => `#${tab.id}(@${tab.index})[${tab.groupId}]`));
22182212

2219-
const membersStructure = TreeBehavior.getTreeStructureFromTabs(members);
2220-
await detachTabsFromTree(members, {
2221-
partial: true,
2222-
});
2213+
const membersAndStructures = new Map();
2214+
const groupedTabs = new Set();
2215+
const others = [];
2216+
let lastMember = null;
2217+
for (const tab of wholeTree) {
2218+
if (tab.groupId == -1) {
2219+
others.push(tab);
2220+
}
2221+
else {
2222+
groupedTabs.add(tab);
2223+
const membersAndStructure = membersAndStructures.get(tab.groupId) || { members: [] };
2224+
membersAndStructure.members.push(tab);
2225+
membersAndStructures.set(tab.groupId, membersAndStructure);
2226+
lastMember = tab;
2227+
}
2228+
}
2229+
for (const [groupId, membersAndStructure] of membersAndStructures.entries()) {
2230+
log(`maintainTreeForNativeTabGroup: groupId = ${groupId}, members = `, () => membersAndStructure.members.map(tab => `#${tab.id}(@${tab.index})[${tab.groupId}]`));
2231+
membersAndStructure.structure = TreeBehavior.getTreeStructureFromTabs(membersAndStructure.members),
2232+
await detachTabsFromTree(members, {
2233+
partial: true,
2234+
});
2235+
}
22232236

2224-
const membersSet = new Set(members);
2225-
const others = wholeTree.filter(tab => !membersSet.has(tab));
2226-
log('maintainTreeForNativeTabGroups: others = ', others);
2227-
if (others.some(other => other.groupId != -1)) {
2228-
log('maintainTreeForNativeTabGroups: others are already grouped and moved by Firefox, so we need to do nothing anymore.');
2237+
log('maintainTreeForNativeTabGroup: others = ', () => others.map(tab => `#${tab.id}(@${tab.index})[${tab.groupId}]`), `, lastMember = #${lastMember.id}(@${lastMember.index})`);
2238+
2239+
if (others.length == 0) {
2240+
log('maintainTreeForNativeTabGroup: there is no other tabs need to be moved, so we do nothing.');
22292241
return;
22302242
}
22312243

22322244
for (const other of others) {
22332245
win.internallyMovingTabsForUpdatedNativeTabGroups.add(other.id);
22342246
}
22352247
const othersStructure = TreeBehavior.getTreeStructureFromTabs(others);
2248+
log('maintainTreeForNativeTabGroup: othersStructure = ', othersStructure);
2249+
await detachTabsFromTree(others);
22362250
await moveTabs(others, {
2237-
insertAfter: members[members.length - 1],
2251+
insertAfter: lastMember,
22382252
insertBefore: others[others.length - 1].unsafeNextTab,
22392253
});
2254+
log('maintainTreeForNativeTabGroup: moved others = ', others.map(tab => `#${tab.id}(@${tab.index})[${tab.groupId}]`));
22402255

22412256
await Promise.race([
22422257
new Promise((resolve, _reject) => {
2243-
const resolvers = maintainTreeForNativeTabGroups.resolversForWindow.get(windowId) || [];
2258+
const resolvers = maintainTreeForNativeTabGroup.resolversForWindow.get(windowId) || [];
22442259
resolvers.push(resolve);
2245-
maintainTreeForNativeTabGroups.resolversForWindow.set(windowId, resolvers);
2260+
maintainTreeForNativeTabGroup.resolversForWindow.set(windowId, resolvers);
22462261
}),
22472262
wait(500),
22482263
]);
2249-
maintainTreeForNativeTabGroups.resolversForWindow.delete(windowId);
2264+
maintainTreeForNativeTabGroup.resolversForWindow.delete(windowId);
22502265

22512266
for (const other of others) {
22522267
win.internallyMovingTabsForUpdatedNativeTabGroups.delete(other.id);
22532268
}
2254-
applyTreeStructureToTabs(members, membersStructure);
2269+
for (const { members, structure } of membersAndStructures.values()) {
2270+
applyTreeStructureToTabs(members, structure);
2271+
}
22552272
applyTreeStructureToTabs(others, othersStructure);
22562273
browser.tabs.ungroup(others.map(tab => tab.id));
22572274
}
2258-
maintainTreeForNativeTabGroups.resolversForWindow = new Map();
2275+
maintainTreeForNativeTabGroup.resolversForWindow = new Map();
22592276

2260-
Tab.onNativeGroupModified.addListener(tab => {
2261-
const win = TabsStore.windows.get(tab.windowId);
2262-
if (win.internallyMovingTabsForUpdatedNativeTabGroups.has(tab.id)) {
2263-
window.requestAnimationFrame(() => {
2264-
const resolvers = maintainTreeForNativeTabGroups.resolversForWindow.get(tab.windowId) || [];
2265-
maintainTreeForNativeTabGroups.resolversForWindow.delete(tab.windowId);
2266-
for (const resolver of resolvers) {
2267-
resolver();
2268-
}
2277+
function reserveToMaintainTreeForUpdatedNativeTabGroup({ windowId, groupId }, options = {}) {
2278+
let timer = reserveToMaintainTreeForUpdatedNativeTabGroup.delayed.get(groupId);
2279+
if (timer)
2280+
clearTimeout(timer);
2281+
if (options.justNow || !shouldApplyAnimation()) {
2282+
return maintainTreeForNativeTabGroup({ windowId, groupId });
2283+
}
2284+
timer = setTimeout(() => {
2285+
reserveToMaintainTreeForUpdatedNativeTabGroup.delayed.delete(groupId);
2286+
maintainTreeForNativeTabGroup({ windowId, groupId });
2287+
}, 100);
2288+
reserveToMaintainTreeForUpdatedNativeTabGroup.delayed.set(groupId, timer);
2289+
}
2290+
reserveToMaintainTreeForUpdatedNativeTabGroup.delayed = new Map();
2291+
2292+
export async function startToMaintainTreeForNativeTabGroups() {
2293+
// fixup mismatched tree structure and tab groups constructed while TST is disabled
2294+
const groups = await browser.tabGroups.query({});
2295+
for (const group of groups) {
2296+
await maintainTreeForNativeTabGroup({
2297+
windowId: group.windowId,
2298+
groupId: group.id,
22692299
});
2270-
return;
22712300
}
2272-
maintainTreeForUpdatedNativeTabGroups(tab);
2273-
});
2301+
2302+
// after all we start tracking of dynamic changes of tab groups
2303+
Tab.onNativeGroupModified.addListener(tab => {
2304+
const win = TabsStore.windows.get(tab.windowId);
2305+
if (win.internallyMovingTabsForUpdatedNativeTabGroups.has(tab.id)) {
2306+
window.requestAnimationFrame(() => {
2307+
const resolvers = maintainTreeForNativeTabGroup.resolversForWindow.get(tab.windowId) || [];
2308+
maintainTreeForNativeTabGroup.resolversForWindow.delete(tab.windowId);
2309+
for (const resolver of resolvers) {
2310+
resolver();
2311+
}
2312+
});
2313+
return;
2314+
}
2315+
reserveToMaintainTreeForUpdatedNativeTabGroup(tab);
2316+
});
2317+
}
2318+
22742319

22752320

22762321
//===================================================================

0 commit comments

Comments
 (0)