Skip to content

Commit f28799f

Browse files
committed
Maintain tree structure based on native tab groups even if they were already grouped
1 parent b7d6734 commit f28799f

5 files changed

Lines changed: 78 additions & 18 deletions

File tree

webextensions/background/tree.js

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2149,17 +2149,62 @@ function maintainTreeForUpdatedNativeTabGroups({ windowId, groupId }, options =
21492149
if (timer)
21502150
clearTimeout(timer);
21512151
if (options.justNow || !shouldApplyAnimation()) {
2152-
return maintainTreeForUpdatedNativeTabGroupsNow({ windowId, groupId });
2152+
return maintainTreeForNativeTabGroups({ windowId, groupId });
21532153
}
21542154
timer = setTimeout(() => {
21552155
maintainTreeForUpdatedNativeTabGroups.delayed.delete(groupId);
2156-
maintainTreeForUpdatedNativeTabGroupsNow({ windowId, groupId });
2156+
maintainTreeForNativeTabGroups({ windowId, groupId });
21572157
}, 100);
21582158
maintainTreeForUpdatedNativeTabGroups.delayed.set(groupId, timer);
21592159
}
21602160
maintainTreeForUpdatedNativeTabGroups.delayed = new Map();
21612161

2162-
async function maintainTreeForUpdatedNativeTabGroupsNow({ windowId, groupId }) {
2162+
/*
2163+
*************************************************************************
2164+
Logic to maintain tree structure based on modified native tab groups
2165+
*************************************************************************
2166+
2167+
Firefox's native tab groups feature's basics:
2168+
2169+
* When tabs are newly grouped, they are gathered to THE PLACE OF THE TAB
2170+
YOU OPENED THE CONTEXT MENU ON.
2171+
* When some of already grouped tabs are grouped with another new group,
2172+
a new group will be placed BEFORE THE SOURCE GROUP OF THE CONTEXT TAB
2173+
and all member tabs will be gathered there.
2174+
* When tabs in different groups are grouped with another new group, the
2175+
new group will be placed BEFORE THE SOURCE GROUP OF THE CONTEXT TAB.
2176+
* When some of already grouped tabs are ungrouped, they will be moved
2177+
AFTER the source group.
2178+
* When all member tabs are ungrouped, they will be there and just
2179+
ungrouped.
2180+
* When tabs in different groups are ungrouped, ungrouped tabs will be
2181+
placed AFTER EACH SOURCE GROUP. In other words, ungrouped tabs won't
2182+
be gathered.
2183+
* When some of already grouped tabs are moved to another group, they will
2184+
be moved AFTER existing members of the destination group.
2185+
2186+
TST should imitate Firefox's behavior, and should do more:
2187+
2188+
* A tree should not be separated with multiple groups.
2189+
All member tabs in a tree should be grouped with a same tab group,
2190+
otherwise all members are ungrouped.
2191+
* When some member tabs in a tree are grouped, they need to be DETACHED
2192+
FROM THE ORIGINAL TREE and PLACED BEFORE THE SOURCE TREE.
2193+
Thus TST need to REARRANGE INVOLVED TABS.
2194+
* However, moving grouped tabs may break the native tab group,
2195+
so TST need to move OTHER TABS.
2196+
* When already grouped tabs are newly grouped to another new group,
2197+
they will be moved by Firefox. TST should DO NOTHING EXTRA ON THIS
2198+
CASE, because tree structure of moved tabs are automatically
2199+
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.
2206+
*/
2207+
async function maintainTreeForNativeTabGroups({ windowId, groupId }) {
21632208
const win = TabsStore.windows.get(windowId);
21642209

21652210
const members = Tab.getNativeGroupMemberTabs({ windowId, groupId });
@@ -2169,7 +2214,7 @@ async function maintainTreeForUpdatedNativeTabGroupsNow({ windowId, groupId }) {
21692214
return;
21702215
}
21712216

2172-
log('maintainTreeForUpdatedNativeTabGroupsNow: wholeTree = ', wholeTree);
2217+
log('maintainTreeForNativeTabGroups: wholeTree = ', wholeTree);
21732218

21742219
const membersStructure = TreeBehavior.getTreeStructureFromTabs(members);
21752220
await detachTabsFromTree(members, {
@@ -2178,7 +2223,12 @@ async function maintainTreeForUpdatedNativeTabGroupsNow({ windowId, groupId }) {
21782223

21792224
const membersSet = new Set(members);
21802225
const others = wholeTree.filter(tab => !membersSet.has(tab));
2181-
log('maintainTreeForUpdatedNativeTabGroupsNow: others = ', others);
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.');
2229+
return;
2230+
}
2231+
21822232
for (const other of others) {
21832233
win.internallyMovingTabsForUpdatedNativeTabGroups.add(other.id);
21842234
}
@@ -2190,13 +2240,13 @@ async function maintainTreeForUpdatedNativeTabGroupsNow({ windowId, groupId }) {
21902240

21912241
await Promise.race([
21922242
new Promise((resolve, _reject) => {
2193-
const resolvers = maintainTreeForUpdatedNativeTabGroupsNow.resolversForWindow.get(windowId) || [];
2243+
const resolvers = maintainTreeForNativeTabGroups.resolversForWindow.get(windowId) || [];
21942244
resolvers.push(resolve);
2195-
maintainTreeForUpdatedNativeTabGroupsNow.resolversForWindow.set(windowId, resolvers);
2245+
maintainTreeForNativeTabGroups.resolversForWindow.set(windowId, resolvers);
21962246
}),
21972247
wait(500),
21982248
]);
2199-
maintainTreeForUpdatedNativeTabGroupsNow.resolversForWindow.delete(windowId);
2249+
maintainTreeForNativeTabGroups.resolversForWindow.delete(windowId);
22002250

22012251
for (const other of others) {
22022252
win.internallyMovingTabsForUpdatedNativeTabGroups.delete(other.id);
@@ -2205,14 +2255,14 @@ async function maintainTreeForUpdatedNativeTabGroupsNow({ windowId, groupId }) {
22052255
applyTreeStructureToTabs(others, othersStructure);
22062256
browser.tabs.ungroup(others.map(tab => tab.id));
22072257
}
2208-
maintainTreeForUpdatedNativeTabGroupsNow.resolversForWindow = new Map();
2258+
maintainTreeForNativeTabGroups.resolversForWindow = new Map();
22092259

22102260
Tab.onNativeGroupModified.addListener(tab => {
22112261
const win = TabsStore.windows.get(tab.windowId);
22122262
if (win.internallyMovingTabsForUpdatedNativeTabGroups.has(tab.id)) {
22132263
window.requestAnimationFrame(() => {
2214-
const resolvers = maintainTreeForUpdatedNativeTabGroupsNow.resolversForWindow.get(tab.windowId) || [];
2215-
maintainTreeForUpdatedNativeTabGroupsNow.resolversForWindow.delete(tab.windowId);
2264+
const resolvers = maintainTreeForNativeTabGroups.resolversForWindow.get(tab.windowId) || [];
2265+
maintainTreeForNativeTabGroups.resolversForWindow.delete(tab.windowId);
22162266
for (const resolver of resolvers) {
22172267
resolver();
22182268
}

webextensions/common/Tab.js

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,16 +1968,25 @@ export default class Tab {
19681968
}
19691969

19701970

1971-
onNativeGroupModified() {
1971+
onNativeGroupModified(oldGroupId) {
19721972
if (this.tab.groupId == -1) {
19731973
TabsStore.removeNativelyGroupedTab(this.tab);
19741974
}
19751975
else {
19761976
TabsStore.addNativelyGroupedTab(this.tab);
1977-
const group = TabsStore.windows.get(this.tab.windowId).tabGroups.get(this.tab.groupId);
1978-
const firstMember = Tab.getFirstNativeGroupMemberTab(this.tab);
1977+
}
1978+
for (const groupId of [oldGroupId, this.tab.groupId]) {
1979+
if (groupId == -1) {
1980+
continue;
1981+
}
1982+
const windowId = this.tab.windowId;
1983+
const group = TabsStore.windows.get(this.tab.windowId).tabGroups.get(groupId);
1984+
if (!group) {
1985+
continue;
1986+
}
1987+
const firstMember = Tab.getFirstNativeGroupMemberTab({ windowId, groupId });
19791988
group.index = firstMember ? firstMember.index : -1;
1980-
//console.log('first member of ', group.id, ' is ', firstMember?.id, ', all = ', Tab.getNativeGroupMemberTabs(this.tab).map(tab => tab.id));
1989+
log('onNativeGroupModified: first member of ', group.id, ' is ', firstMember?.id, ', all = ', Tab.getNativeGroupMemberTabs({ windowId, groupId }).map(tab => tab.id));
19811990
}
19821991

19831992
this.setAttribute(Constants.kGROUP_ID, this.tab.groupId);

webextensions/common/tabs-update.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ export function updateTab(tab, newState = {}, options = {}) {
369369

370370
if (options.forceApply ||
371371
'groupId' in newState) {
372-
tab.$TST.onNativeGroupModified();
372+
tab.$TST.onNativeGroupModified(oldState.groupId);
373373
update.attributes.added[Constants.kGROUP_ID] = newState.groupId;
374374
}
375375

webextensions/sidebar/sidebar-tabs.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,14 +576,15 @@ function tryApplyUpdate(update) {
576576
const highlightedChanged = update.updatedProperties && 'highlighted' in update.updatedProperties;
577577

578578
if (update.updatedProperties) {
579+
const oldGroupId = tab.groupId;
579580
for (const [key, value] of Object.entries(update.updatedProperties)) {
580581
if (Tab.UNSYNCHRONIZABLE_PROPERTIES.has(key))
581582
continue;
582583
tab[key] = value;
583584
}
584585

585586
if ('groupId' in update.updatedProperties) {
586-
tab.$TST.onNativeGroupModified();
587+
tab.$TST.onNativeGroupModified(oldGroupId);
587588
tab.$TST.updateElement(TabUpdateTarget.TabProperties);
588589
}
589590
}

webextensions/sidebar/sidebar.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,7 @@ const mImportedWindow = new Promise((resolve, _reject) => {
624624
message?.windowId != windowId)
625625
return;
626626
browser.runtime.onMessage.removeListener(onBackgroundIsReady);
627-
log(`mImportedWindow is resolved with ${message.tabs.length} tabs`);
627+
log(`mImportedWindow is resolved with ${message.exported.tabs.length} tabs`);
628628
resolve(message.exported);
629629
});
630630
};

0 commit comments

Comments
 (0)