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

506 lines
18 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,
saveUserStyleRules,
notify,
wait,
isLinux,
isMacOS,
} from '/common/common.js';
import * as Constants from '/common/constants.js';
import * as Permissions from '/common/permissions.js';
// eslint-disable-next-line no-unused-vars
function log(...args) {
internalLogger('background/migration', ...args);
}
const kCONFIGS_VERSION = 33;
const kFEATURES_VERSION = 9;
export function migrateConfigs() {
switch (configs.configsVersion) {
case 0:
case 1:
if (configs.startDragTimeout !== null)
configs.longPressDuration = configs.startDragTimeout;
if (configs.emulateDefaultContextMenu !== null)
configs.emulateDefaultContextMenu = configs.emulateDefaultContextMenu;
case 2:
if (configs.simulateSelectOwnerOnClose !== null &&
!configs.simulateSelectOwnerOnClose)
configs.successorTabControlLevel = Constants.kSUCCESSOR_TAB_CONTROL_NEVER;
case 3:
if (!(configs.tabDragBehavior & Constants.kDRAG_BEHAVIOR_ALLOW_BOOKMARK))
configs.tabDragBehavior |= Constants.kDRAG_BEHAVIOR_TEAR_OFF;
if (!(configs.tabDragBehaviorShift & Constants.kDRAG_BEHAVIOR_ALLOW_BOOKMARK))
configs.tabDragBehaviorShift |= Constants.kDRAG_BEHAVIOR_TEAR_OFF;
case 4:
if (configs.fakeContextMenu !== null)
configs.emulateDefaultContextMenu = configs.fakeContextMenu;
if (configs.context_closeTabOptions_closeTree !== null)
configs.context_topLevel_closeTree = configs.context_closeTabOptions_closeTree;
if (configs.context_closeTabOptions_closeDescendants !== null)
configs.context_topLevel_closeDescendants = configs.context_closeTabOptions_closeDescendants;
if (configs.context_closeTabOptions_closeOthers !== null)
configs.context_topLevel_closeOthers = configs.context_closeTabOptions_closeOthers;
case 5:
if (configs.scrollbarMode !== null) {
switch (configs.scrollbarMode < 0 ? (isMacOS() ? 3 : 1) : configs.scrollbarMode) {
case 0: // default, refular width
configs.userStyleRules += `
/* regular width scrollbar */
#tabbar { scrollbar-width: auto; }`;
break;
case 1: // narrow width
break;
case 2: // hide
configs.userStyleRules += `
/* hide scrollbar */
#tabbar { scrollbar-width: none; }
/* cancel spaces for macOS overlay scrollbar */
:root.platform-mac #tabbar:dir(rtl).overflow .tab:not(.pinned) {
padding-inline-start: 0;
}
:root.platform-mac #tabbar:dir(ltr).overflow .tab:not(.pinned) {
padding-inline-end: 0;
}`;
break;
case 3: // overlay (macOS)
break;
}
}
if (configs.sidebarScrollbarPosition !== null) {
switch (configs.sidebarScrollbarPosition) {
default:
case 0: // auto
case 1: // left
break;
break;
case 2: // right
configs.userStyleRules += `
/* put scrollbar rightside */
:root.left #tabbar { direction: ltr; }`;
break;
}
}
case 6:
if (configs.promoteFirstChildForClosedRoot != null &&
configs.promoteFirstChildForClosedRoot &&
configs.closeParentBehavior == Constants.kPARENT_TAB_OPERATION_BEHAVIOR_PROMOTE_ALL_CHILDREN)
configs.closeParentBehavior = Constants.kPARENT_TAB_OPERATION_BEHAVIOR_PROMOTE_INTELLIGENTLY;
if (configs.parentTabBehaviorForChanges !== null) {
switch (configs.parentTabBehaviorForChanges) {
case Constants.kPARENT_TAB_BEHAVIOR_ALWAYS:
configs.parentTabOperationBehaviorMode = Constants.kPARENT_TAB_OPERATION_BEHAVIOR_MODE_CONSISTENT;
break;
default:
case Constants.kPARENT_TAB_BEHAVIOR_ONLY_WHEN_VISIBLE:
configs.parentTabOperationBehaviorMode = Constants.kPARENT_TAB_OPERATION_BEHAVIOR_MODE_PARALLEL;
break;
case Constants.kPARENT_TAB_BEHAVIOR_ONLY_ON_SIDEBAR:
configs.parentTabOperationBehaviorMode = Constants.kPARENT_TAB_OPERATION_BEHAVIOR_MODE_CUSTOM;
configs.closeParentBehavior_outsideSidebar_expanded = configs.closeParentBehavior_noSidebar_expanded = configs. closeParentBehavior;
break;
}
}
case 7:
if (configs.collapseExpandSubtreeByDblClick !== null &&
configs.collapseExpandSubtreeByDblClick)
configs.treeDoubleClickBehavior = Constants.kTREE_DOUBLE_CLICK_BEHAVIOR_TOGGLE_COLLAPSED;
case 8:
if (configs.autoExpandOnCollapsedChildActive !== null)
configs.unfocusableCollapsedTab = configs.autoExpandOnCollapsedChildActive;
case 9:
if (configs.simulateCloseTabByDblclick !== null &&
configs.simulateCloseTabByDblclick)
configs.treeDoubleClickBehavior = Constants.kTREE_DOUBLE_CLICK_BEHAVIOR_CLOSE;
case 10:
if (configs.style == 'plain-dark' ||
configs.style == 'metal')
configs.style = configs.$default.style;
case 11:
if (configs.userStyleRules) {
configs.userStyleRules0 = configs.userStyleRules;
configs.userStyleRules = '';
}
case 12:
try {
saveUserStyleRules(Array.from(new Uint8Array(8), (_, index) => {
const key = `userStyleRules${index}`;
if (key in configs) {
const chunk = configs[key];
configs[key] = '';
return chunk || '';
}
else {
return '';
}
}).join(''));
}
catch(error) {
console.error(error);
}
case 13:
if (configs.style == 'mixed' ||
configs.style == 'vertigo')
configs.style = 'photon';
case 14:
if (configs.inheritContextualIdentityToNewChildTab !== null)
configs.inheritContextualIdentityToChildTabMode = configs.inheritContextualIdentityToNewChildTab ? Constants.kCONTEXTUAL_IDENTITY_FROM_PARENT : Constants.kCONTEXTUAL_IDENTITY_DEFAULT;
if (configs.inheritContextualIdentityToSameSiteOrphan !== null)
configs.inheritContextualIdentityToSameSiteOrphanMode = configs.inheritContextualIdentityToSameSiteOrphan ? Constants.kCONTEXTUAL_IDENTITY_FROM_LAST_ACTIVE : Constants.kCONTEXTUAL_IDENTITY_DEFAULT;
if (configs.inheritContextualIdentityToTabsFromExternal !== null)
configs.inheritContextualIdentityToTabsFromExternalMode = configs.inheritContextualIdentityToTabsFromExternal ? Constants.kCONTEXTUAL_IDENTITY_FROM_PARENT : Constants.kCONTEXTUAL_IDENTITY_DEFAULT;
case 15:
if (configs.moveDroppedTabToNewWindowForUnhandledDragEvent !== null &&
!configs.moveDroppedTabToNewWindowForUnhandledDragEvent) {
if (configs.tabDragBehavior & Constants.kDRAG_BEHAVIOR_TEAR_OFF)
configs.tabDragBehavior = configs.tabDragBehavior ^ Constants.kDRAG_BEHAVIOR_TEAR_OFF;
else if (configs.tabDragBehavior & Constants.kDRAG_BEHAVIOR_ALLOW_BOOKMARK)
configs.tabDragBehavior = configs.tabDragBehavior ^ Constants.kDRAG_BEHAVIOR_ALLOW_BOOKMARK;
if (configs.tabDragBehaviorShift & Constants.kDRAG_BEHAVIOR_TEAR_OFF)
configs.tabDragBehaviorShift = configs.tabDragBehaviorShift ^ Constants.kDRAG_BEHAVIOR_TEAR_OFF;
else if (configs.tabDragBehaviorShift & Constants.kDRAG_BEHAVIOR_ALLOW_BOOKMARK)
configs.tabDragBehaviorShift = configs.tabDragBehaviorShift ^ Constants.kDRAG_BEHAVIOR_ALLOW_BOOKMARK;
}
case 16:
configs.maximumDelayForBug1561879 = Math.max(configs.$default.maximumDelayForBug1561879, configs.maximumDelayForBug1561879);
case 17:
configs.tabDragBehavior |= Constants.kDRAG_BEHAVIOR_MOVE;
configs.tabDragBehaviorShift |= Constants.kDRAG_BEHAVIOR_MOVE;
case 18:
if (configs.connectionTimeoutDelay == 5000)
configs.connectionTimeoutDelay = configs.$default.connectionTimeoutDelay;
case 19:
if (configs.suppressGapFromShownOrHiddenToolbar !== null) {
configs.suppressGapFromShownOrHiddenToolbarOnNewTab =
configs.suppressGapFromShownOrHiddenToolbarOnFullScreen = configs.suppressGapFromShownOrHiddenToolbar;
}
case 20:
if (configs.treatTreeAsExpandedOnClosedWithNoSidebar !== null) {
configs.treatTreeAsExpandedOnClosed_noSidebar = configs.treatTreeAsExpandedOnClosedWithNoSidebar;
}
case 21:
if (configs.style == 'plain')
configs.style = 'photon';
case 22:
case 23:
if (configs.closeParentBehaviorMode !== null) {
configs.parentTabOperationBehaviorMode = configs.closeParentBehaviorMode;
}
if (configs.closeParentBehavior !== null) {
configs.closeParentBehavior_insideSidebar_expanded =
configs.closeParentBehavior;
}
if (configs.closeParentBehavior_outsideSidebar !== null) {
configs.closeParentBehavior_outsideSidebar_expanded =
configs.moveParentBehavior_outsideSidebar_expanded =
configs.closeParentBehavior_outsideSidebar;
}
if (configs.closeParentBehavior_noSidebar !== null) {
configs.closeParentBehavior_noSidebar_expanded =
configs.closeParentBehavior_noSidebar_expanded =
configs.closeParentBehavior_noSidebar;
}
if (configs.treatTreeAsExpandedOnClosed_outsideSidebar === true) {
configs.closeParentBehavior_outsideSidebar_collapsed =
configs.moveParentBehavior_outsideSidebar_collapsed =
configs.moveParentBehavior_outsideSidebar_expanded;
}
if (configs.treatTreeAsExpandedOnClosed_noSidebar === false) {
configs.closeParentBehavior_noSidebar_collapsed =
configs.moveParentBehavior_noSidebar_collapsed =
Constants.kPARENT_TAB_OPERATION_BEHAVIOR_ENTIRE_TREE;
}
if (configs.treatTreeAsExpandedOnClosed_outsideSidebar === true ||
configs.treatTreeAsExpandedOnClosed_noSidebar === false) {
configs.parentTabOperationBehaviorMode = Constants.kPARENT_TAB_OPERATION_BEHAVIOR_MODE_CUSTOM;
}
case 24:
if (configs.autoGroupNewTabsTimeout !== null)
configs.tabBunchesDetectionTimeout = configs.autoGroupNewTabsTimeout;
if (configs.autoGroupNewTabsDelayOnNewWindow !== null)
configs.tabBunchesDetectionDelayOnNewWindow = configs.autoGroupNewTabsDelayOnNewWindow;
case 25:
if (configs.autoHiddenScrollbarPlaceholderSize !== null)
configs.shiftTabsForScrollbarDistance = configs.autoHiddenScrollbarPlaceholderSize;
case 26:
if (!configs.guessNewOrphanTabAsOpenedByNewTabCommandUrl.includes('about:privatebrowsing'))
configs.guessNewOrphanTabAsOpenedByNewTabCommandUrl = `${configs.guessNewOrphanTabAsOpenedByNewTabCommandUrl.trim().replace(/\|$/, '')}|about:privatebrowsing`;
case 27:
if (configs.openAllBookmarksWithGroupAlways !== null)
configs.suppressGroupTabForStructuredTabsFromBookmarks = !configs.openAllBookmarksWithGroupAlways;
case 28:
if (configs.heartbeatInterval == 1000)
configs.heartbeatInterval = configs.$default.heartbeatInterval;
case 29:
const autoAttachKeys = [
'autoAttachOnOpenedWithOwner',
'autoAttachOnNewTabCommand',
'autoAttachOnContextNewTabCommand',
'autoAttachOnNewTabButtonMiddleClick',
'autoAttachOnNewTabButtonAccelClick',
'autoAttachOnDuplicated',
'autoAttachSameSiteOrphan',
'autoAttachOnOpenedFromExternal',
'autoAttachOnAnyOtherTrigger',
];
if (configs.insertNewChildAt != configs.$default.insertNewChildAt &&
(configs.insertNewChildAt == Constants.kINSERT_TOP ||
configs.insertNewChildAt == Constants.kINSERT_END)) {
for (const key of autoAttachKeys) {
if (configs[key] != configs.$default[key] &&
configs[key] != Constants.kNEWTAB_OPEN_AS_CHILD)
continue;
if (configs.insertNewChildAt == Constants.kINSERT_TOP)
configs[key] = Constants.kNEWTAB_OPEN_AS_CHILD_TOP;
else
configs[key] = Constants.kNEWTAB_OPEN_AS_CHILD_END;
}
}
else {
for (const key of autoAttachKeys) {
if (configs[key] == Constants.kNEWTAB_OPEN_AS_CHILD)
configs[key] = Constants.kNEWTAB_OPEN_AS_CHILD_TOP;
}
}
case 30:
browser.windows.getAll().then(windows => {
for (const win of windows) {
browser.sessions.removeWindowValue(win.id, Constants.kWINDOW_STATE_CACHED_SIDEBAR);
browser.sessions.removeWindowValue(win.id, Constants.kWINDOW_STATE_CACHED_SIDEBAR_TABS_DIRTY);
browser.sessions.removeWindowValue(win.id, Constants.kWINDOW_STATE_CACHED_SIDEBAR_COLLAPSED_DIRTY);
}
});
case 31:
if (configs.tabPreviewTooltipInSidebar !== null)
configs.tabPreviewTooltipRenderIn = configs.tabPreviewTooltipInSidebar ?
Constants.kIN_CONTENT_PANEL_RENDER_IN_ANYWHERE :
Constants.kIN_CONTENT_PANEL_RENDER_IN_CONTENT;
case 32:
if (configs.tabPreviewTooltipOffsetTop !== null)
configs.inContentUIOffsetTop = configs.tabPreviewTooltipOffsetTop;
}
configs.configsVersion = kCONFIGS_VERSION;
}
let mShouldShowInitialStartupPage = false;
export function tryNotifyNewFeatures() {
/*
let featuresVersionOffset = 0;
const browserInfo = await browser.runtime.getBrowserInfo().catch(ApiTabs.createErrorHandler());
// "search" permission becomes available!
if (parseInt(browserInfo.version.split('.')[0]) >= 63)
featuresVersionOffset++;
// "menus.overrideContext" permission becomes available!
if (parseInt(browserInfo.version.split('.')[0]) >= 64)
featuresVersionOffset++;
*/
const featuresVersion = kFEATURES_VERSION /*+ featuresVersionOffset*/;
const isInitialInstall = configs.notifiedFeaturesVersion == 0;
if (configs.notifiedFeaturesVersion >= featuresVersion)
return;
configs.notifiedFeaturesVersion = featuresVersion;
if (isInitialInstall &&
!configs.syncOtherDevicesDetected &&
Object.keys(configs.syncDevices).length > 1) {
configs.syncAvailableNotified = true;
configs.syncOtherDevicesDetected = true;
}
const typeSuffix = isInitialInstall ? 'installed' : 'updated';
const platformSuffix = isLinux() ? '_linux' : '';
const url = isInitialInstall ? Constants.kSHORTHAND_URIS.startup : browser.i18n.getMessage('message_startup_history_uri');
notify({
url,
title: browser.i18n.getMessage(`startup_notification_title_${typeSuffix}`),
message: browser.i18n.getMessage(`startup_notification_message_${typeSuffix}${platformSuffix}`),
timeout: 90 * 1000
});
if (isInitialInstall) {
mShouldShowInitialStartupPage = true;
browser.browserAction.setBadgeText({
text: '!',
});
}
}
export function isInitialStartup() {
return !!mShouldShowInitialStartupPage;
}
export function openInitialStartupPage() {
mShouldShowInitialStartupPage = false;
browser.browserAction.setBadgeText({
text: null,
});
browser.tabs.create({
url: Constants.kSHORTHAND_URIS.startup,
active: true,
});
}
// Auto-migration of bookmarked internal URLs
//
// Internal URLs like "moz-extension://(UUID)/..." are runtime environment
// dependent and unavailable when such bookmarks are loaded in different
// runtime environment, for example they are synchronized from other devices.
// Thus we should migrate such internal URLs to universal shorthand URIs like
// "ext+ws:(name)".
export async function migrateBookmarkUrls() {
const granted = await Permissions.isGranted(Permissions.BOOKMARKS);
if (!granted)
return;
startBookmarksUrlAutoMigration();
const urls = new Set(configs.migratedBookmarkUrls);
const migrations = [];
const updates = [];
for (const key in Constants.kSHORTHAND_URIS) {
const url = Constants.kSHORTHAND_URIS[key].split('?')[0];
if (urls.has(url))
continue;
const shorthand = `ext+ws:${key.toLowerCase()}`;
migrations.push(browser.bookmarks.search({ query: url })
.then(bookmarks => {
for (const bookmark of bookmarks) {
updates.push(browser.bookmarks.update(bookmark.id, {
url: bookmark.url.replace(url, shorthand)
}));
}
}));
urls.add(url);
}
if (migrations.length > 0)
await Promise.all(migrations);
if (updates.length > 0)
await Promise.all(updates);
if (urls.size > configs.migratedBookmarkUrls.length)
configs.migratedBookmarkUrls = Array.from(urls);
}
async function migrateBookmarkUrl(bookmark) {
for (const key in Constants.kSHORTHAND_URIS) {
const url = Constants.kSHORTHAND_URIS[key].split('?')[0];
if (!bookmark.url.startsWith(url))
continue;
const shorthand = `ext+ws:${key.toLowerCase()}`;
return browser.bookmarks.update(bookmark.id, {
url: bookmark.url.replace(url, shorthand)
});
}
}
let mObservingBookmarks = false;
let mBookmarksListenersRegistered = false;
function onBookmarkCreated(id, bookmark) {
if (!mObservingBookmarks)
return;
if (bookmark.url)
migrateBookmarkUrl(bookmark);
}
async function onBookmarkChanged(id, changeInfo) {
if (!mObservingBookmarks)
return;
if (changeInfo.url &&
changeInfo.url.startsWith(browser.runtime.getURL(''))) {
const bookmark = await browser.bookmarks.get(id);
if (Array.isArray(bookmark))
bookmark.forEach(migrateBookmarkUrl);
else
migrateBookmarkUrl(bookmark);
}
}
if (browser.bookmarks &&
browser.bookmarks.onCreated) {
browser.bookmarks.onCreated.addListener(onBookmarkCreated);
browser.bookmarks.onChanged.addListener(onBookmarkChanged);
mBookmarksListenersRegistered = true;
}
async function startBookmarksUrlAutoMigration() {
if (mObservingBookmarks)
return;
mObservingBookmarks = true;
if (mBookmarksListenersRegistered ||
!browser.bookmarks ||
!browser.bookmarks.onCreated)
return;
browser.bookmarks.onCreated.addListener(onBookmarkCreated);
browser.bookmarks.onChanged.addListener(onBookmarkChanged);
mBookmarksListenersRegistered = true;
}
configs.$loaded.then(() => {
configs.$addObserver(async key => {
// This may be triggered not only "reset all", but while importing of configs also.
// We should try initial migration after all configs are successfully imported.
await wait(100);
if (key == 'configsVersion' &&
configs.configsVersion == 0)
migrateConfigs();
});
});