From 1a0668ad891e747fea99f9c371773d594a605623 Mon Sep 17 00:00:00 2001 From: Alex Kontos Date: Tue, 11 Jun 2024 17:38:50 +0100 Subject: [PATCH] Update Sidebar to 1.0.2 --- .../browser/components/WaterfoxGlue.sys.mjs | 2 +- .../sidebar/_locales/en/messages.json | 64 +++--- .../sidebar/_locales/ja/messages.json | 60 +++--- .../sidebar/background/handle-misc.js | 186 ++++++++++++++++-- .../sidebar/background/tabs-group.js | 3 + .../components/sidebar/background/tree.js | 42 +++- .../browser/components/sidebar/common/Tab.js | 16 +- .../components/sidebar/common/constants.js | 5 + .../sidebar/common/tabs-internal-operation.js | 26 +++ .../browser/components/sidebar/manifest.json | 90 +++++++-- .../sidebar/sidebar/collapse-expand.js | 15 +- .../components/sidebar/sidebar/indent.js | 4 +- .../components/sidebar/sidebar/pinned-tabs.js | 112 ++++++++++- .../components/sidebar/sidebar/scroll.js | 39 ++-- .../components/sidebar/sidebar/sidebar.js | 5 + .../components/sidebar/sidebar/size.js | 20 +- 16 files changed, 572 insertions(+), 117 deletions(-) diff --git a/waterfox/browser/components/WaterfoxGlue.sys.mjs b/waterfox/browser/components/WaterfoxGlue.sys.mjs index a05fbd5820e41..c81e7a390a75b 100644 --- a/waterfox/browser/components/WaterfoxGlue.sys.mjs +++ b/waterfox/browser/components/WaterfoxGlue.sys.mjs @@ -316,7 +316,7 @@ export const WaterfoxGlue = { addon = (await lazy.AddonManager.maybeInstallBuiltinAddon( ID, - "1.0.1", + "1.0.2", "resource://builtin-addons/sidebar/" )) || addon; diff --git a/waterfox/browser/components/sidebar/_locales/en/messages.json b/waterfox/browser/components/sidebar/_locales/en/messages.json index 3b45040c2e25e..c902600bec85d 100644 --- a/waterfox/browser/components/sidebar/_locales/en/messages.json +++ b/waterfox/browser/components/sidebar/_locales/en/messages.json @@ -14,10 +14,16 @@ "command_focusNext": { "message": "Focus to Next Tab (expand tree)" }, "command_focusNextSilently": { "message": "Focus to Next Tab (don't expand tree)" }, "command_focusParent": { "message": "Focus to Parent Tab" }, + "command_focusParentOrCollapse": { "message": "Collapse Tree or Focus to Parent Tab" }, "command_focusFirstChild": { "message": "Focus to First Child Tab" }, + "command_focusFirstChildOrExpand": { "message": "Expand Tree or Focus to First Child Tab" }, "command_focusLastChild": { "message": "Focus to Last Child Tab" }, "command_focusPreviousSibling": { "message": "Focus to Previous Sibling Tab" }, "command_focusNextSibling": { "message": "Focus to Next Sibling Tab" }, + "command_simulateUpOnTree": { "message": "Simulate Up Key on Tree" }, + "command_simulateDownOnTree": { "message": "Simulate Down Key on Tree" }, + "command_simulateLeftOnTree": { "message": "Simulate Left Key on Tree" }, + "command_simulateRightOnTree": { "message": "Simulate Right Key on Tree" }, "command_tabbarUp": { "message": "Scroll Tabs Up by Lines" }, "command_tabbarPageUp": { "message": "Scroll Tabs Up by Page" }, "command_tabbarHome": { "message": "Scroll Tabs to Top" }, @@ -135,7 +141,7 @@ "progress": { "content": "$1", "example": "50" } }}, - "blank_allUrlsPermissionRequiredMessage": { "message": "This message is here due to Firefox tried to restore a dialog which is provided by Tree Style Tab addon. Please grant the \"Access your data for all websites\" permission via the \"Permissions\" tab of Tree Style Tab's details page in the Add-on Manager, if this message is not wanted to appear anymore." }, + "blank_allUrlsPermissionRequiredMessage": { "message": "This message is here due to Firefox tried to restore a dialog which is provided by Tree Style Tab extension. Please grant the \"Access your data for all websites\" permission via the \"Permissions\" tab of Tree Style Tab's details page in the Add-on Manager, if you don't want this message to appear anymore." }, "warnOnCloseTabs_title": { "message": "Close tabs?" }, "warnOnCloseTabs_message": { "message": "You are about to close $NUMBER$ tabs. Are you sure you want to continue?", @@ -269,12 +275,12 @@ "message_startup_userChromeCss_description_3": { "message": "." }, "api_requestedPermissions_title": { "message": "Request for Extra Permissions" }, - "api_requestedPermissions_message": { "message": "⚠The addon \"$NAME$\" is requesting to do following operation:\n\n$PERMISSIONS$\n\nvia Tabs Sidebar's API. Click here if you grant that.", + "api_requestedPermissions_message": { "message": "⚠The extension \"$NAME$\" is requesting to do following operation:\n\n$PERMISSIONS$\n\nvia Tabs Sidebar's API. Click here if you grant that.", "placeholders": { "NAME": { "content": "$1", "example": "foobar" }, "PERMISSIONS": { "content": "$2", "example": "tabs" } }}, - "api_requestedPermissions_message_linux": { "message": "⚠The addon \"$NAME$\" is requesting to do following operation:\n\n$PERMISSIONS$\n\nvia Tabs Sidebar's API. Click the button if you grant that.", + "api_requestedPermissions_message_linux": { "message": "⚠The extension \"$NAME$\" is requesting to do following operation:\n\n$PERMISSIONS$\n\nvia Tabs Sidebar's API. Click the button if you grant that.", "placeholders": { "NAME": { "content": "$1", "example": "foobar" }, "PERMISSIONS": { "content": "$2", "example": "tabs" } @@ -409,7 +415,7 @@ "config_showDialogInSidebar_label": { "message": "Show dialogs in the sidebar if possible" }, "config_outOfScreenTabsRenderingPages_label": { "message": "Number of pages to pre-render tabs:" }, "config_outOfScreenTabsRenderingPages_description": { "message": "* \"-1\" will pre-render all tabs for better scrolling performance, and as a trade off you may need to wait long time on initialization." }, - "config_renderHiddenTabs_label": { "message": "Show hidden tabs which are hidden by other addons" }, + "config_renderHiddenTabs_label": { "message": "Show hidden tabs which are hidden by other extensions" }, "config_context_caption": { "message": "Context Menu" }, @@ -460,7 +466,7 @@ "config_autoAttachOnOpenedWithOwner_after": { "message": "\u200b" }, "config_insertNewTabFromFirefoxViewAt_caption": { "message": "Insertion position of new child tabs from Firefox View (will appear as root tabs)" }, - "config_insertNewTabFromFirefoxViewAt_noControl": { "message": "No control (respect the decision by the browser or other tab addons)" }, + "config_insertNewTabFromFirefoxViewAt_noControl": { "message": "No control (respect the decision by the browser or other tab extensions)" }, "config_insertNewTabFromFirefoxViewAt_nextToLastRelatedTab": { "message": "Next to the recently opened child, or near the opener" }, "config_insertNewTabFromFirefoxViewAt_top": { "message": "The top of the tree (near the opener)" }, "config_insertNewTabFromFirefoxViewAt_end": { "message": "The end of the tree" }, @@ -469,7 +475,7 @@ "config_groupTabTemporaryStateForChildrenOfFirefoxView_label": { "message": "Default state of group tabs for tabs opened from Firefox View:" }, "config_insertNewTabFromPinnedTabAt_caption": { "message": "Insertion position of new child tabs from pinned tabs (will appear as root tabs)" }, - "config_insertNewTabFromPinnedTabAt_noControl": { "message": "No control (respect the decision by the browser or other tab addons)" }, + "config_insertNewTabFromPinnedTabAt_noControl": { "message": "No control (respect the decision by the browser or other tab extensions)" }, "config_insertNewTabFromPinnedTabAt_nextToLastRelatedTab": { "message": "Next to the recently opened child, or near the opener" }, "config_insertNewTabFromPinnedTabAt_top": { "message": "The top of the tree (near the opener)" }, "config_insertNewTabFromPinnedTabAt_end": { "message": "The end of the tree" }, @@ -574,13 +580,13 @@ "config_autoAttachOnAnyOtherTrigger_sibling": { "message": "Sibling of the current tab" }, "config_autoAttachOnAnyOtherTrigger_nextSibling": { "message": "Next Sibling of the current tab" }, "config_autoAttachOnAnyOtherTrigger_after": { "message": "\u200b" }, - "config_autoAttachOnAnyOtherTrigger_caution": { "message": "*CAUTION: This option has a risk breaking behavior of other addons." }, + "config_autoAttachOnAnyOtherTrigger_caution": { "message": "*CAUTION: This option has a risk breaking behavior of other extensions." }, "config_inheritContextualIdentityToTabsFromAnyOtherTriggerMode_label": { "message": "Container:" }, "config_inheritContextualIdentityToTabsFromAnyOtherTriggerMode_default": { "message": "(no control)" }, "config_inheritContextualIdentityToTabsFromAnyOtherTriggerMode_parent": { "message": "Inherit from the parent tab on the tree" }, "config_inheritContextualIdentityToTabsFromAnyOtherTriggerMode_lastActive": { "message": "Inherit from the current tab" }, - "config_inheritContextualIdentityToUnopenableURLTabs_label": { "message": "Reopen tab with inherited container anyway, even if it has a URL unable to open by addon's permission" }, + "config_inheritContextualIdentityToUnopenableURLTabs_label": { "message": "Reopen tab with inherited container anyway, even if it has a URL unable to open by extension's permission" }, "config_groupTab_caption": { "message": "Auto-grouping of tabs" }, @@ -620,13 +626,13 @@ "config_successorTabControlLevel_caption": { "message": "When the current tab is closed as a last child" }, "config_successorTabControlLevel_inTree": { "message": "Focus to the previous tab in the tree" }, "config_successorTabControlLevel_simulateDefault": { "message": "Focus to the next tab always (the browser's default)" }, - "config_successorTabControlLevel_never": { "message": "Never control focus (respect the browser or other addon's control)" }, + "config_successorTabControlLevel_never": { "message": "Never control focus (respect the browser or other extension's control)" }, "config_successorTabControlLevel_legacyDescription": { "message": "*The browser's built-in behavior for \"browser.tabs.selectOwnerOnClose\"=\"true\" works prior to this config." }, "config_simulateSelectOwnerOnClose_label": { "message": "Move focus back to the opener tab if possible, when the current tab is closed (*simulation of the browser's built-in behavior for \"browser.tabs.selectOwnerOnClose\"=\"true\", prior to the config above)" }, - "config_fixupTreeOnTabVisibilityChanged_caption": { "message": "When visibility of tabs are changed by other addons" }, - "config_fixupTreeOnTabVisibilityChanged_fix": { "message": "Fix up tree structure with visible tabs automatically (*Recommended if you use another addon to switch tab groups)" }, - "config_fixupTreeOnTabVisibilityChanged_keep": { "message": "Keep tree structure including hidden tabs (*Recommended if you use another addon which changes temporary visibility of tabs)" }, + "config_fixupTreeOnTabVisibilityChanged_caption": { "message": "When visibility of tabs are changed by other extensions" }, + "config_fixupTreeOnTabVisibilityChanged_fix": { "message": "Fix up tree structure with visible tabs automatically (*Recommended if you use another extension to switch tab groups)" }, + "config_fixupTreeOnTabVisibilityChanged_keep": { "message": "Keep tree structure including hidden tabs (*Recommended if you use another extension which changes temporary visibility of tabs)" }, "config_autoCollapseExpandSubtreeOnAttach_label": { "message": "When a new tree appears, collapse others automatically" }, "config_autoCollapseExpandSubtreeOnSelect_label": { "message": "When a tab gets focus, expand its tree and collapse others automatically" }, @@ -637,7 +643,7 @@ "config_treeDoubleClickBehavior_caption": { "message": "Double-click on a tab" }, "config_treeDoubleClickBehavior_toggleCollapsed": { "message": "Collapse/expand tree" }, - "config_treeDoubleClickBehavior_toggleSticky": { "message": "Stick to tab bar adges / Unstick from tab bar edges" }, + "config_treeDoubleClickBehavior_toggleSticky": { "message": "Stick to tab bar edges / Unstick from tab bar edges" }, "config_treeDoubleClickBehavior_close": { "message": "Close tab" }, "config_treeDoubleClickBehavior_close_note": { "message": " (*simulation of the browser's built-in behavior for \"browser.tabs.closeTabByDblclick\"=\"true\")" }, "config_treeDoubleClickBehavior_none": { "message": "Do nothing" }, @@ -647,21 +653,21 @@ "config_parentTabOperationBehaviorMode_parallel": { "message": "Recommended preset using the browser's native tab bar as the Solo Tab Operation UI" }, "config_closeParentBehavior_insideSidebar": { "message": "When a parent tab with Expanded Tree is Closed via the sidebar," }, - "config_closeParentBehavior_outsideSidebar": { "message": "When a parent tab (whether its Tree is Expanded or Collapsed) is Closed via the browser's native tab bar, keyboard shortcuts or other addons," }, - "config_moveParentBehavior_outsideSidebar": { "message": "When a parent tab (whether its Tree is Expanded or Collapsed) is Moved via the browser's native tab bar, keyboard shortcuts or other addons," }, + "config_closeParentBehavior_outsideSidebar": { "message": "When a parent tab (whether its Tree is Expanded or Collapsed) is Closed via the browser's native tab bar, keyboard shortcuts or other extensions," }, + "config_moveParentBehavior_outsideSidebar": { "message": "When a parent tab (whether its Tree is Expanded or Collapsed) is Moved via the browser's native tab bar, keyboard shortcuts or other extensions," }, "config_parentTabOperationBehaviorMode_consistent": { "message": "Recommended preset using Consistent Tab Behaviors controlled under TST" }, "config_parentTabOperationBehaviorMode_consistent_caption": { "message": "When a parent tab with Expanded Tree is closed/moved," }, - "config_parentTabOperationBehaviorMode_consistent_notes": { "message": "Tabs behave as configured above consistently, even via the browser's native tab bar, keyboard shortcuts or other addons." }, + "config_parentTabOperationBehaviorMode_consistent_notes": { "message": "Tabs behave as configured above consistently, even via the browser's native tab bar, keyboard shortcuts or other extensions." }, "config_parentTabOperationBehaviorMode_custom": { "message": "Custom" }, "config_closeParentBehavior_insideSidebar_expanded_caption": { "message": "Close: When a parent tab with Expanded Tree is Closed via the sidebar," }, - "config_closeParentBehavior_outsideSidebar_collapsed_caption": { "message": "Close: When a parent tab with Collapsed Tree is Closed via the browser's native tab bar, keyboard shortcuts or other addons," }, - "config_closeParentBehavior_outsideSidebar_expanded_caption": { "message": "Close: When a parent tab with Expanded Tree is Closed via the browser's native tab bar, keyboard shortcuts or other addons," }, + "config_closeParentBehavior_outsideSidebar_collapsed_caption": { "message": "Close: When a parent tab with Collapsed Tree is Closed via the browser's native tab bar, keyboard shortcuts or other extensions," }, + "config_closeParentBehavior_outsideSidebar_expanded_caption": { "message": "Close: When a parent tab with Expanded Tree is Closed via the browser's native tab bar, keyboard shortcuts or other extensions," }, "config_closeParentBehavior_noSidebar_collapsed_caption": { "message": "if the sidebar is closed," }, "config_closeParentBehavior_noSidebar_expanded_caption": { "message": "if the sidebar is closed," }, - "config_moveParentBehavior_outsideSidebar_collapsed_caption": { "message": "Move: When a parent tab with Collapsed Tree is Moved via the browser's native tab bar, keyboard shortcuts or other addons," }, - "config_moveParentBehavior_outsideSidebar_expanded_caption": { "message": "Move: When a parent tab with Expanded Tree is Moved via the browser's native tab bar, keyboard shortcuts or other addons," }, + "config_moveParentBehavior_outsideSidebar_collapsed_caption": { "message": "Move: When a parent tab with Collapsed Tree is Moved via the browser's native tab bar, keyboard shortcuts or other extensions," }, + "config_moveParentBehavior_outsideSidebar_expanded_caption": { "message": "Move: When a parent tab with Expanded Tree is Moved via the browser's native tab bar, keyboard shortcuts or other extensions," }, "config_moveParentBehavior_noSidebar_collapsed_caption": { "message": "if the sidebar is closed," }, "config_moveParentBehavior_noSidebar_expanded_caption": { "message": "if the sidebar is closed," }, @@ -689,7 +695,7 @@ "config_warnOnCloseTabsWithListing_label": { "message": "List closing tabs in the confirmation dialog" }, "config_insertNewChildAt_caption": { "message": "Default insertion position of new child tabs on un-controlled cases" }, - "config_insertNewChildAt_noControl": { "message": "No control (respect the decision by the browser or other tab addons)" }, + "config_insertNewChildAt_noControl": { "message": "No control (respect the decision by the browser or other tab extensions)" }, "config_insertNewChildAt_nextToLastRelatedTab": { "message": "Next to the recently opened child, or next to the parent" }, "config_insertNewChildAt_top": { "message": "The top of the tree (next to the parent)" }, "config_insertNewChildAt_end": { "message": "The end of the tree" }, @@ -751,7 +757,7 @@ "config_bookmarkTreeFolderName_before": { "message": "Folder name for \"Bookmark this Tree\":" }, "config_bookmarkTreeFolderName_after": { "message": "\u200b" }, - "config_bookmarkTreeFolderName_description": { "message": "Available placeholders: %TITLE% (title of the first tab), %URL% (URL of the first tab), %YEAR% (year, four digits), %SHORT_YEAR% (year, two digits), %MONTH% (month, two digits), %DATE% (date, two digits), %HOURS% (hours, two digits), %MINUTES% (minutes, two digits), %SECONDS% (seconds, two digits), %MILLISECONDS% (milliseconds, trhee digits), %GROUP% (title of the first tab if it is group tab - otherwise blank), %ANY(value1, value2, ...)% (the first effective value from the given list)" }, + "config_bookmarkTreeFolderName_description": { "message": "Available placeholders: %TITLE% (title of the first tab), %URL% (URL of the first tab), %YEAR% (year, four digits), %SHORT_YEAR% (year, two digits), %MONTH% (month, two digits), %DATE% (date, two digits), %HOURS% (hours, two digits), %MINUTES% (minutes, two digits), %SECONDS% (seconds, two digits), %MILLISECONDS% (milliseconds, three digits), %GROUP% (title of the first tab if it is group tab - otherwise blank), %ANY(value1, value2, ...)% (the first effective value from the given list)" }, "config_defaultBookmarkParentId_label_before": { "message": "Create new bookmarks under" }, "config_defaultBookmarkParentId_label_after": { "message": "\u200b" }, @@ -783,10 +789,10 @@ "config_userStyleRulesTheme_default": { "message": "Default" }, - "config_addons_caption": { "message": "Extra Features via Other Addons" }, + "config_addons_caption": { "message": "Extra Features via Other Extensions" }, "config_addons_description_before": { "message": "There are " }, - "config_addons_description_link_label": { "message": "addons which extend TST itself" }, + "config_addons_description_link_label": { "message": "extensions which extend TST itself" }, "config_addons_description_after": { "message": ". Check them out if you need more features on TST's sidebar." }, "helper_addons_list_link_uri": { "message": "https://github.com/piroor/treestyletab/wiki/Helper-addons-extending-functionality-of-TST" }, @@ -795,15 +801,15 @@ "config_theme_description_after": { "message": " for examples." }, "helper_theme_list_link_uri": { "message": "https://github.com/piroor/treestyletab/wiki/Code-snippets-for-custom-style-rules#restore-old-built-in-themes" }, - "config_externaladdonpermissions_label": { "message": "Permissions for API Call from Other Addons" }, - "config_externaladdonpermissions_description": { "message": "Addons allowed here may access detailed tab data or tabs in private windows, through Tabs Sidebar's permissions, even if the addon ifself is forbidden by the browser to access them. Please don't give permission for addons if you cannot trust them." }, - "config_externalAddonPermissions_header_name": { "message": "Addon Name" }, + "config_externaladdonpermissions_label": { "message": "Permissions for API Call from Other Extensions" }, + "config_externaladdonpermissions_description": { "message": "Extensions allowed here may access detailed tab data or tabs in private windows, through Tabs Sidebar's permissions, even if the extension ifself is forbidden by the browser to access them. Please don't give permission for extensions if you cannot trust them." }, + "config_externalAddonPermissions_header_name": { "message": "Extension Name" }, "config_externalAddonPermissions_header_permissions": { "message": "Allowed Special Operations" }, "config_externalAddonPermissions_header_incognito": { "message": "Notify Messages from Private Windows" }, "addon_containerBookmarks_label": { "message": "Container Bookmarks" }, "addon_containerBookmarks_uri": { "message": "https://addons.mozilla.org/firefox/addon/container-bookmarks/" }, - "config_containerRedirectKey_label": { "message": "Redirect Key (*Please set a value same to the one in the addon's options page):" }, + "config_containerRedirectKey_label": { "message": "Redirect Key (*Please set a value same to the one in the extension's options page):" }, "config_debug_caption": { "message": "Development" }, @@ -919,7 +925,7 @@ "tryConfirmUsingTST_title": { "message": "Conflicting Features Detected" }, - "tryConfirmUsingTST_message": { "message": "\"Tree Style Tab\" addon has been detected: it may conflict with Waterfox's Tabs Sidebar. Please choose which one to enable." }, + "tryConfirmUsingTST_message": { "message": "\"Tree Style Tab\" extension has been detected: it may conflict with Waterfox's Tabs Sidebar. Please choose which one to enable." }, "tryConfirmUsingTST_WS": { "message": "Waterfox's Tabs Sidebar" }, "tryConfirmUsingTST_TST": { "message": "Tree Style Tab" }, "tryConfirmUsingTST_both": { "message": "Activate Both" }, diff --git a/waterfox/browser/components/sidebar/_locales/ja/messages.json b/waterfox/browser/components/sidebar/_locales/ja/messages.json index 1fafdd302ad6c..af98063c97a27 100644 --- a/waterfox/browser/components/sidebar/_locales/ja/messages.json +++ b/waterfox/browser/components/sidebar/_locales/ja/messages.json @@ -14,10 +14,16 @@ "command_focusNext": { "message": "次のタブにフォーカスを移す(ツリーを展開する)" }, "command_focusNextSilently": { "message": "次のタブにフォーカスを移す(ツリーを展開しない)" }, "command_focusParent": { "message": "親のタブにフォーカスを移す" }, + "command_focusParentOrCollapse": { "message": "ツリーを折りたたむか、親のタブにフォーカスを移す" }, "command_focusFirstChild": { "message": "最初の子タブにフォーカスを移す" }, + "command_focusFirstChildOrExpand": { "message": "ツリーを展開するか、最初の子タブにフォーカスを移す" }, "command_focusLastChild": { "message": "最後の子タブにフォーカスを移す" }, "command_focusPreviousSibling": { "message": "同階層の前のタブにフォーカスを移す" }, "command_focusNextSibling": { "message": "同階層の次のタブにフォーカスを移す" }, + "command_simulateUpOnTree": { "message": "ツリー上での↑キーを模倣" }, + "command_simulateDownOnTree": { "message": "ツリー上での↓キーを模倣" }, + "command_simulateLeftOnTree": { "message": "ツリー上での←キーを模倣" }, + "command_simulateRightOnTree": { "message": "ツリー上での→キーを模倣" }, "command_tabbarUp": { "message": "タブの一覧を数行上にスクロール" }, "command_tabbarPageUp": { "message": "タブの一覧を1ページ上にスクロール" }, "command_tabbarHome": { "message": "タブの一覧を先頭までスクロール" }, @@ -135,7 +141,7 @@ "progress": { "content": "$1", "example": "50" } }}, - "blank_allUrlsPermissionRequiredMessage": { "message": "このメッセージはTree Style Tabが提供するダイアログをFirefoxが開き直そうとしたために表示されています。このメッセージを表示したくない場合は、アドオン管理画面のTree Style Tabの詳細ページの「権限」タブで「すべてのウェブサイトの保存されたデータへのアクセス」の権限を許可してください。" }, + "blank_allUrlsPermissionRequiredMessage": { "message": "このメッセージはTree Style Tabが提供するダイアログをFirefoxが開き直そうとしたために表示されています。このメッセージを表示したくない場合は、拡張機能管理画面のTree Style Tabの詳細ページの「権限」タブで「すべてのウェブサイトの保存されたデータへのアクセス」の権限を許可してください。" }, "warnOnCloseTabs_title": { "message": "タブを閉じますか?" }, "warnOnCloseTabs_message": { "message": "複数 ($NUMBER$) のタブを閉じようとしています。すべてのタブを閉じてよろしいですか?", @@ -269,12 +275,12 @@ "message_startup_userChromeCss_description_3": { "message": "\u200b" }, "api_requestedPermissions_title": { "message": "アクセス権の要求" }, - "api_requestedPermissions_message": { "message": "⚠アドオン「$NAME$」がTabs SidebarのAPIを経由して以下のことをしようとしています。\n\n$PERMISSIONS$\n\n許可する場合はこの通知をクリックしてアドオンに許可を与えてください。", + "api_requestedPermissions_message": { "message": "⚠拡張機能「$NAME$」がTabs SidebarのAPIを経由して以下のことをしようとしています。\n\n$PERMISSIONS$\n\n許可する場合はこの通知をクリックして拡張機能に許可を与えてください。", "placeholders": { "NAME": { "content": "$1", "example": "foobar" }, "PERMISSIONS": { "content": "$2", "example": "tabs" } }}, - "api_requestedPermissions_message_linux": { "message": "⚠アドオン「$NAME$」がTabs SidebarのAPIを経由して以下のことをしようとしています。\n\n$PERMISSIONS$\n\n許可する場合はボタンをクリックしてアドオンに許可を与えてください。", + "api_requestedPermissions_message_linux": { "message": "⚠拡張機能「$NAME$」がTabs SidebarのAPIを経由して以下のことをしようとしています。\n\n$PERMISSIONS$\n\n許可する場合はボタンをクリックして拡張機能に許可を与えてください。", "placeholders": { "NAME": { "content": "$1", "example": "foobar" }, "PERMISSIONS": { "content": "$2", "example": "tabs" } @@ -406,7 +412,7 @@ "config_showDialogInSidebar_label": { "message": "可能であればサイドバー内でダイアログを表示する" }, "config_outOfScreenTabsRenderingPages_label": { "message": "タブを事前にレンダリングしておくページ数:" }, "config_outOfScreenTabsRenderingPages_description": { "message": "※「-1」を指定するとすべてのタブを事前にレンダリングするようになり、スクロールの応答性が向上しますが、引き替えとして、初期化処理に長い時間を要するようになります" }, - "config_renderHiddenTabs_label": { "message": "他のアドオンが隠したタブを表示する" }, + "config_renderHiddenTabs_label": { "message": "他の拡張機能が隠したタブを表示する" }, "config_context_caption": { "message": "コンテキストメニュー" }, @@ -457,7 +463,7 @@ "config_autoAttachOnOpenedWithOwner_after": { "message": "開く" }, "config_insertNewTabFromFirefoxViewAt_caption": { "message": "Firefox Viewから(最上位のタブとして表れる)新しい子タブを開く位置" }, - "config_insertNewTabFromFirefoxViewAt_noControl": { "message": "制御しない(ブラウザーや他のアドオンの判断に任せる)" }, + "config_insertNewTabFromFirefoxViewAt_noControl": { "message": "制御しない(ブラウザーや他の拡張機能の判断に任せる)" }, "config_insertNewTabFromFirefoxViewAt_nextToLastRelatedTab": { "message": "直近に開いた子タブの隣、もしくは親タブの近く" }, "config_insertNewTabFromFirefoxViewAt_top": { "message": "ツリーの先頭(親タブの近く)" }, "config_insertNewTabFromFirefoxViewAt_end": { "message": "ツリーの末尾" }, @@ -466,7 +472,7 @@ "config_groupTabTemporaryStateForChildrenOfFirefoxView_label": { "message": "Firefox Viewから開かれたタブをまとめるグループ化用のタブの初期状態:" }, "config_insertNewTabFromPinnedTabAt_caption": { "message": "ピン留めされたタブから(最上位のタブとして表れる)新しい子タブを開く位置" }, - "config_insertNewTabFromPinnedTabAt_noControl": { "message": "制御しない(ブラウザーや他のアドオンの判断に任せる)" }, + "config_insertNewTabFromPinnedTabAt_noControl": { "message": "制御しない(ブラウザーや他の拡張機能の判断に任せる)" }, "config_insertNewTabFromPinnedTabAt_nextToLastRelatedTab": { "message": "直近に開いた子タブの隣、もしくは親タブの近く" }, "config_insertNewTabFromPinnedTabAt_top": { "message": "ツリーの先頭(親タブの近く)" }, "config_insertNewTabFromPinnedTabAt_end": { "message": "ツリーの末尾" }, @@ -571,13 +577,13 @@ "config_autoAttachOnAnyOtherTrigger_sibling": { "message": "現在のタブと同階層に" }, "config_autoAttachOnAnyOtherTrigger_nextSibling": { "message": "現在のタブと同階層で隣に" }, "config_autoAttachOnAnyOtherTrigger_after": { "message": "開く" }, - "config_autoAttachOnAnyOtherTrigger_caution": { "message": "※警告:このオプションは他のアドオンの動作を妨げる危険性があります" }, + "config_autoAttachOnAnyOtherTrigger_caution": { "message": "※警告:このオプションは他の拡張機能の動作を妨げる危険性があります" }, "config_inheritContextualIdentityToTabsFromAnyOtherTriggerMode_label": { "message": "コンテナー:" }, "config_inheritContextualIdentityToTabsFromAnyOtherTriggerMode_default": { "message": "(制御しない)" }, "config_inheritContextualIdentityToTabsFromAnyOtherTriggerMode_parent": { "message": "ツリー上での親から継承" }, "config_inheritContextualIdentityToTabsFromAnyOtherTriggerMode_lastActive": { "message": "現在のタブから継承" }, - "config_inheritContextualIdentityToUnopenableURLTabs_label": { "message": "アドオンの権限で開けないURLのタブを、コンテナーを継承するために安全なURLで強制的に開き直す" }, + "config_inheritContextualIdentityToUnopenableURLTabs_label": { "message": "拡張機能の権限で開けないURLのタブを、コンテナーを継承するために安全なURLで強制的に開き直す" }, "config_groupTab_caption": { "message": "タブを自動的にグループ化する" }, @@ -617,13 +623,13 @@ "config_successorTabControlLevel_caption": { "message": "現在のタブが最後の子タブだった時に、現在のタブを閉じた後は" }, "config_successorTabControlLevel_inTree": { "message": "ツリー内の1つ前のタブにフォーカスを移す" }, "config_successorTabControlLevel_simulateDefault": { "message": "次のタブにフォーカスを移す(ブラウザー既定の動作)" }, - "config_successorTabControlLevel_never": { "message": "フォーカスを制御しない(ブラウザーまたは他のアドオンによる制御を尊重)" }, + "config_successorTabControlLevel_never": { "message": "フォーカスを制御しない(ブラウザーまたは他の拡張機能による制御を尊重)" }, "config_successorTabControlLevel_legacyDescription": { "message": "※ブラウザー組み込みの機能である\"browser.tabs.selectOwnerOnClose\"=\"true\"の挙動の方が、この設定よりも優先的に反映されます。" }, "config_simulateSelectOwnerOnClose_label": { "message": "現在のタブが閉じられた時、可能であればそのタブを開いた元のタブにフォーカスを戻す(※ブラウザー組み込みの機能である\"browser.tabs.selectOwnerOnClose\"=\"true\"の挙動の再現。上記の設定よりも優先的に反映されます。)" }, - "config_fixupTreeOnTabVisibilityChanged_caption": { "message": "タブの表示・非表示が他のアドオンによって切り替えられた場合" }, - "config_fixupTreeOnTabVisibilityChanged_fix": { "message": "表示中のタブに基づいてツリー構造を自動的に修正する(※タブのグループを切り替えるアドオンとの併用時に推奨)" }, - "config_fixupTreeOnTabVisibilityChanged_keep": { "message": "非表示のタブも含めてツリー構造を維持する(※一時的にタブを非表示にするアドオンとの併用時に推奨)" }, + "config_fixupTreeOnTabVisibilityChanged_caption": { "message": "タブの表示・非表示が他の拡張機能によって切り替えられた場合" }, + "config_fixupTreeOnTabVisibilityChanged_fix": { "message": "表示中のタブに基づいてツリー構造を自動的に修正する(※タブのグループを切り替える拡張機能との併用時に推奨)" }, + "config_fixupTreeOnTabVisibilityChanged_keep": { "message": "非表示のタブも含めてツリー構造を維持する(※一時的にタブを非表示にする拡張機能との併用時に推奨)" }, "config_autoCollapseExpandSubtreeOnAttach_label": { "message": "新しいツリーが作られた時は、自動的に他のツリーを折りたたむ" }, "config_autoCollapseExpandSubtreeOnSelect_label": { "message": "タブを切り替えた時は、フォーカスされたタブのツリーを自動的に展開して、他のツリーを折りたたむ" }, @@ -644,22 +650,22 @@ "config_parentTabOperationBehaviorMode_parallel": { "message": "タブを個別に操作するUIとしてブラウザー自身のタブバーを使う場合の推奨動作" }, "config_closeParentBehavior_insideSidebar": { "message": "展開状態のツリーの親タブをサイドバー内で閉じた時は、" }, - "config_closeParentBehavior_outsideSidebar": { "message": "ツリーの開閉状態に関わらず、ブラウザー自身のタブバー、キーボードショートカット、他のアドオンでツリーの親タブを閉じた時は、" }, - "config_moveParentBehavior_outsideSidebar": { "message": "ツリーの開閉状態に関わらず、ブラウザー自身のタブバー、キーボードショートカット、他のアドオンでツリーの親タブを移動した時は、" }, + "config_closeParentBehavior_outsideSidebar": { "message": "ツリーの開閉状態に関わらず、ブラウザー自身のタブバー、キーボードショートカット、他の拡張機能でツリーの親タブを閉じた時は、" }, + "config_moveParentBehavior_outsideSidebar": { "message": "ツリーの開閉状態に関わらず、ブラウザー自身のタブバー、キーボードショートカット、他の拡張機能でツリーの親タブを移動した時は、" }, "config_parentTabOperationBehaviorMode_consistent": { "message": "タブの振る舞いを全面的にTSTで制御する場合の推奨動作" }, "config_parentTabOperationBehaviorMode_consistent_caption": { "message": "展開状態のツリーの親タブを閉じた/移動した時は、" }, - "config_parentTabOperationBehaviorMode_consistent_notes": { "message": "ブラウザー自身のタブバー、キーボードショートカット、他のアドオンでの操作に対しても、常にこの通り動作します。" }, + "config_parentTabOperationBehaviorMode_consistent_notes": { "message": "ブラウザー自身のタブバー、キーボードショートカット、他の拡張機能での操作に対しても、常にこの通り動作します。" }, "config_parentTabOperationBehaviorMode_custom": { "message": "カスタム" }, "config_closeParentBehavior_insideSidebar_expanded_caption": { "message": "閉じる:展開状態のツリーの親タブを、サイドバー内で閉じた場合は、" }, - "config_closeParentBehavior_outsideSidebar_collapsed_caption": { "message": "閉じる:折りたたまれたツリーの親タブを、ブラウザー自身のタブバー、キーボードショートカット、他のアドオンで閉じた場合は、" }, + "config_closeParentBehavior_outsideSidebar_collapsed_caption": { "message": "閉じる:折りたたまれたツリーの親タブを、ブラウザー自身のタブバー、キーボードショートカット、他の拡張機能で閉じた場合は、" }, "config_closeParentBehavior_noSidebar_collapsed_caption": { "message": "サイドバーが非表示の時は、" }, - "config_closeParentBehavior_outsideSidebar_expanded_caption": { "message": "閉じる:展開状態のツリーの親タブを、ブラウザー自身のタブバー、キーボードショートカット、他のアドオンで閉じた場合は、" }, + "config_closeParentBehavior_outsideSidebar_expanded_caption": { "message": "閉じる:展開状態のツリーの親タブを、ブラウザー自身のタブバー、キーボードショートカット、他の拡張機能で閉じた場合は、" }, "config_closeParentBehavior_noSidebar_expanded_caption": { "message": "サイドバーが非表示の時は、" }, - "config_moveParentBehavior_outsideSidebar_collapsed_caption": { "message": "移動:折りたたまれたツリーの親タブを、ブラウザー自身のタブバー、キーボードショートカット、他のアドオンで移動した場合は、" }, + "config_moveParentBehavior_outsideSidebar_collapsed_caption": { "message": "移動:折りたたまれたツリーの親タブを、ブラウザー自身のタブバー、キーボードショートカット、他の拡張機能で移動した場合は、" }, "config_moveParentBehavior_noSidebar_collapsed_caption": { "message": "サイドバーが非表示の時は、" }, - "config_moveParentBehavior_outsideSidebar_expanded_caption": { "message": "移動:展開状態のツリーの親タブを、ブラウザー自身のタブバー、キーボードショートカット、他のアドオンで移動した場合は、" }, + "config_moveParentBehavior_outsideSidebar_expanded_caption": { "message": "移動:展開状態のツリーの親タブを、ブラウザー自身のタブバー、キーボードショートカット、他の拡張機能で移動した場合は、" }, "config_moveParentBehavior_noSidebar_expanded_caption": { "message": "サイドバーが非表示の時は、" }, "config_parentTabOperationBehavior_entireTree": { "message": "ツリー全体を閉じる/移動する" }, @@ -686,7 +692,7 @@ "config_warnOnCloseTabsWithListing_label": { "message": "閉じようとしているタブの一覧を確認のダイアログに表示する" }, "config_insertNewChildAt_caption": { "message": "子タブの挿入位置が未制御の場合の既定の挿入位置" }, - "config_insertNewChildAt_noControl": { "message": "制御しない(ブラウザーや他のアドオンの判断に任せる)" }, + "config_insertNewChildAt_noControl": { "message": "制御しない(ブラウザーや他の拡張機能の判断に任せる)" }, "config_insertNewChildAt_nextToLastRelatedTab": { "message": "直近に開いた子タブの隣、もしくは親タブの隣" }, "config_insertNewChildAt_top": { "message": "ツリーの先頭(親タブの隣)" }, "config_insertNewChildAt_end": { "message": "ツリーの末尾" }, @@ -780,10 +786,10 @@ "config_userStyleRulesTheme_default": { "message": "既定" }, - "config_addons_caption": { "message": "他のアドオンによる機能追加" }, + "config_addons_caption": { "message": "他の拡張機能による機能追加" }, "config_addons_description_before": { "message": "\u200b" }, - "config_addons_description_link_label": { "message": "TST自体を拡張する他のアドオン" }, + "config_addons_description_link_label": { "message": "TST自体を拡張する他の拡張機能" }, "config_addons_description_after": { "message": "を組み合わせると、TSTのサイドバーにさらに機能を追加できます。" }, "helper_addons_list_link_uri": { "message": "https://github.com/piroor/treestyletab/wiki/TST%E3%81%AE%E6%A9%9F%E8%83%BD%E3%82%92%E6%8B%A1%E5%BC%B5%E3%81%99%E3%82%8B%E3%82%A2%E3%83%89%E3%82%AA%E3%83%B3" }, @@ -792,9 +798,9 @@ "config_theme_description_after": { "message": "にある例を参照して下さい。" }, "helper_theme_list_link_uri": { "message": "https://github.com/piroor/treestyletab/wiki/Code-snippets-for-custom-style-rules#restore-old-built-in-themes" }, - "config_externaladdonpermissions_label": { "message": "他のアドオンからのAPI呼び出しに与える許可" }, - "config_externaladdonpermissions_description": { "message": "ここでチェックを入れたアドオンは、ブラウザー自体がそのアドオンにタブの詳細情報やプライベートウィンドウのタブへのアクセスを禁止していても、Tabs Sidebarの権限でそれらにアクセスできるようになります。信用できないアドオンに対しては絶対に権限を与えないで下さい。" }, - "config_externalAddonPermissions_header_name": { "message": "アドオン名" }, + "config_externaladdonpermissions_label": { "message": "他の拡張機能からのAPI呼び出しに与える許可" }, + "config_externaladdonpermissions_description": { "message": "ここでチェックを入れた拡張機能は、ブラウザー自体がその拡張機能にタブの詳細情報やプライベートウィンドウのタブへのアクセスを禁止していても、Tabs Sidebarの権限でそれらにアクセスできるようになります。信用できない拡張機能に対しては絶対に権限を与えないで下さい。" }, + "config_externalAddonPermissions_header_name": { "message": "拡張機能名" }, "config_externalAddonPermissions_header_permissions": { "message": "許可する特別な操作" }, "config_externalAddonPermissions_header_incognito": { "message": "プライベートウィンドウからのメッセージを通知する" }, @@ -830,7 +836,7 @@ "config_useCachedTree_label": { "message": "キャッシュを使ってツリーの初期化を高速化する" }, "config_useCachedTree_description": { "message": "※キャッシュの不整合で動作が不安定になっている場合、このチェックをOFFにして再度ONにすることでキャッシュが刷新され状況が改善するかもしれません。" }, "config_persistCachedTree_label": { "message": "キャッシュを永続化する" }, - "config_persistCachedTree_description": { "message": "※ブラウザー起動時やアドオン更新時などの初期化処理を短縮できますが、ブラウザーのセッションファイルが肥大化しディスクI/Oが増加します" }, + "config_persistCachedTree_description": { "message": "※ブラウザー起動時や拡張機能更新時などの初期化処理を短縮できますが、ブラウザーのセッションファイルが肥大化しディスクI/Oが増加します" }, "config_acceleratedTabCreation_label": { "message": "新しいタブが開かれた時の処理を高速化する(※動作が不安定になります)" }, "config_maximumAcceptableDelayForTabDuplication_before": { "message": "\u200b" }, "config_maximumAcceptableDelayForTabDuplication_after": { "message": "ミリ秒以内にタブの複製を完了できなかった場合は処理を中断する" }, @@ -915,7 +921,7 @@ "tryConfirmUsingTST_title": { "message": "競合の検出" }, - "tryConfirmUsingTST_message": { "message": "「Tree Style Tab」アドオンが検出されました:Waterfoxのタブサイドバーと競合する恐れがあります。どちらの機能を有効にするか選択してください。" }, + "tryConfirmUsingTST_message": { "message": "「Tree Style Tab」拡張機能が検出されました:Waterfoxのタブサイドバーと競合する恐れがあります。どちらの機能を有効にするか選択してください。" }, "tryConfirmUsingTST_WS": { "message": "Waterfoxのタブサイドバー" }, "tryConfirmUsingTST_TST": { "message": "Tree Style Tab" }, "tryConfirmUsingTST_both": { "message": "両方有効にする" }, diff --git a/waterfox/browser/components/sidebar/background/handle-misc.js b/waterfox/browser/components/sidebar/background/handle-misc.js index b94b218f5e594..77e8f8eff7ab9 100644 --- a/waterfox/browser/components/sidebar/background/handle-misc.js +++ b/waterfox/browser/components/sidebar/background/handle-misc.js @@ -224,27 +224,29 @@ async function onShortcutCommand(command) { return; case 'focusPrevious': - case 'focusPreviousSilently': { - const nextActive = activeTab.$TST.nearestVisiblePrecedingTab || - Tab.getLastVisibleTab(activeTab.windowId); - TabsInternalOperation.activateTab(nextActive, { - silently: /Silently/.test(command) - }); - }; return; + focusPrevious(activeTab); + return; + case 'focusPreviousSilently': + focusPreviousSilently(activeTab); + return; case 'focusNext': - case 'focusNextSilently': { - const nextActive = activeTab.$TST.nearestVisibleFollowingTab || - Tab.getFirstVisibleTab(activeTab.windowId); - TabsInternalOperation.activateTab(nextActive, { - silently: /Silently/.test(command) - }); - }; return; + focusNext(activeTab); + return; + case 'focusNextSilently': + focusNextSilently(activeTab); + return; case 'focusParent': TabsInternalOperation.activateTab(activeTab.$TST.parent); return; + case 'focusParentOrCollapse': + collapseOrFocusToParent(activeTab); + return; case 'focusFirstChild': TabsInternalOperation.activateTab(activeTab.$TST.firstChild); return; + case 'focusFirstChildOrExpand': + expandOrFocusToFirstChild(activeTab); + return; case 'focusLastChild': TabsInternalOperation.activateTab(activeTab.$TST.lastChild); return; @@ -261,6 +263,110 @@ async function onShortcutCommand(command) { ); return; + case 'simulateUpOnTree': + if (SidebarConnection.isSidebarOpen(activeTab.windowId)) { + if (configs.faviconizePinnedTabs && + (activeTab.pinned || + activeTab == Tab.getFirstNormalTab(activeTab.windowId))) { + const nextActiveId = await browser.runtime.sendMessage({ + type: Constants.kCOMMAND_GET_ABOVE_TAB, + windowId: activeTab.windowId, + tabId: activeTab.id, + }); + log(`simulateUpOnTree: nextActiveId = ${nextActiveId}`); + const nextActive = ( + nextActiveId && Tab.get(nextActiveId) || + Tab.getLastVisibleTab(activeTab.windowId) + ); + TabsInternalOperation.activateTab(nextActive, { + silently: true, + }); + } + else { + focusPreviousSilently(activeTab); + } + } + else { + focusPrevious(activeTab); + } + return; + case 'simulateDownOnTree': + if (SidebarConnection.isSidebarOpen(activeTab.windowId)) { + if (configs.faviconizePinnedTabs && + activeTab.pinned) { + const nextActiveId = await browser.runtime.sendMessage({ + type: Constants.kCOMMAND_GET_BELOW_TAB, + windowId: activeTab.windowId, + tabId: activeTab.id, + }); + log(`simulateDownOnTree: nextActiveId = ${nextActiveId}`); + const nextActive = ( + nextActiveId && Tab.get(nextActiveId) || + Tab.getFirstNormalTab(activeTab.windowId) + ); + TabsInternalOperation.activateTab(nextActive, { + silently: true, + }); + } + else { + focusNextSilently(activeTab); + } + } + else { + focusNext(activeTab); + } + return; + case 'simulateLeftOnTree': + if (SidebarConnection.isSidebarOpen(activeTab.windowId)) { + if (configs.faviconizePinnedTabs && + activeTab.pinned) { + const nextActiveId = await browser.runtime.sendMessage({ + type: Constants.kCOMMAND_GET_LEFT_TAB, + windowId: activeTab.windowId, + tabId: activeTab.id, + }); + log(`simulateLeftOnTree: nextActiveId = ${nextActiveId}`); + TabsInternalOperation.activateTab(Tab.get(nextActiveId), { + silently: true, + }); + } + else if (await isSidebarRightSide(activeTab.windowId)) { + expandOrFocusToFirstChild(activeTab); + } + else { + collapseOrFocusToParent(activeTab); + } + } + else { + focusPrevious(activeTab); + } + return; + case 'simulateRightOnTree': + if (SidebarConnection.isSidebarOpen(activeTab.windowId)) { + if (configs.faviconizePinnedTabs && + activeTab.pinned) { + const nextActiveId = await browser.runtime.sendMessage({ + type: Constants.kCOMMAND_GET_RIGHT_TAB, + windowId: activeTab.windowId, + tabId: activeTab.id, + }); + log(`simulateRightOnTree: nextActiveId = ${nextActiveId}`); + TabsInternalOperation.activateTab(Tab.get(nextActiveId), { + silently: true, + }); + } + else if (await isSidebarRightSide(activeTab.windowId)) { + collapseOrFocusToParent(activeTab); + } + else { + expandOrFocusToFirstChild(activeTab); + } + } + else { + focusNext(activeTab); + } + return; + case 'tabbarUp': SidebarConnection.sendMessage({ type: Constants.kCOMMAND_SCROLL_TABBAR, @@ -345,6 +451,58 @@ async function onShortcutCommand(command) { } } +function focusPrevious(activeTab) { + const nextActive = activeTab.$TST.nearestVisiblePrecedingTab || + Tab.getLastVisibleTab(activeTab.windowId); + TabsInternalOperation.activateTab(nextActive); +} + +function focusPreviousSilently(activeTab) { + const nextActive = activeTab.$TST.nearestVisiblePrecedingTab || + Tab.getLastVisibleTab(activeTab.windowId); + TabsInternalOperation.activateTab(nextActive, { + silently: true, + }); +} + +function focusNext(activeTab) { + const nextActive = activeTab.$TST.nextVisibleTab || + Tab.getFirstVisibleTab(activeTab.windowId); + TabsInternalOperation.activateTab(nextActive); +} + +function focusNextSilently(activeTab) { + const nextActive = activeTab.$TST.nearestVisibleFollowingTab || + Tab.getFirstVisibleTab(activeTab.windowId); + TabsInternalOperation.activateTab(nextActive, { + silently: true, + }); +} + +async function isSidebarRightSide(windowId) { + const position = await browser.runtime.sendMessage({ + type: Constants.kCOMMAND_GET_SIDEBAR_POSITION, + windowId, + }); + return position == Constants.kTABBAR_POSITION_RIGHT; +} + +function collapseOrFocusToParent(activeTab) { + if (!activeTab.$TST.subtreeCollapsed && activeTab.$TST.hasChild) + Commands.collapseTree(activeTab); + else + TabsInternalOperation.activateTab(activeTab.$TST.parent); +} + +function expandOrFocusToFirstChild(activeTab) { + if (activeTab.$TST.subtreeCollapsed && activeTab.$TST.hasChild) + Commands.expandTree(activeTab); + else + TabsInternalOperation.activateTab(activeTab.$TST.firstChild, { + silently: true, + }); +} + // This must be synchronous and return Promise on demando, to avoid // blocking to other listeners. function onMessage(message, sender) { diff --git a/waterfox/browser/components/sidebar/background/tabs-group.js b/waterfox/browser/components/sidebar/background/tabs-group.js index 0031a1a1bd50e..977594b7075db 100644 --- a/waterfox/browser/components/sidebar/background/tabs-group.js +++ b/waterfox/browser/components/sidebar/background/tabs-group.js @@ -120,6 +120,9 @@ function cleanupNeedlssGroupTab(tabs) { log('trying to clanup needless temporary group tabs from ', () => tabs.map(dumpTab)); const tabsToBeRemoved = []; for (const tab of tabs) { + if (tab.$TST.temporaryMetadata.has('movingAcrossWindows')) + continue; + if (tab.$TST.isTemporaryGroupTab) { if (tab.$TST.childIds.length > 1) break; diff --git a/waterfox/browser/components/sidebar/background/tree.js b/waterfox/browser/components/sidebar/background/tree.js index bb93abb29a2df..fd12819d6c10e 100644 --- a/waterfox/browser/components/sidebar/background/tree.js +++ b/waterfox/browser/components/sidebar/background/tree.js @@ -1434,6 +1434,15 @@ export async function moveTabs(tabs, options = {}) { const structure = TreeBehavior.getTreeStructureFromTabs(tabs); log('original tree structure: ', structure); + let hasActive = false; + for (const tab of movedTabs) { + if (tab.active) + hasActive = true; + if (isAcrossWindows && + !options.duplicate) + tab.$TST.temporaryMetadata.set('movingAcrossWindows', true); + } + if (!options.duplicate) await detachTabsFromTree(tabs, options); @@ -1504,9 +1513,11 @@ export async function moveTabs(tabs, options = {}) { movedTabIds = movedTabs.map(tab => tab.id); } else { + const movedTabIdsSet = new Set(movedTabIds); for (const tab of movedTabs) { - if (tab.$TST.parent && - !movedTabs.includes(tab.$TST.parent)) + tab.$TST.temporaryMetadata.set('movingAcrossWindows', true); + if (tab.$TST.parentId && + !movedTabIdsSet.has(tab.$TST.parentId)) detachTab(tab, { broadcast: true, toBeDetached: true @@ -1543,13 +1554,38 @@ export async function moveTabs(tabs, options = {}) { toIndex--; log(' => ', toIndex); if (isAcrossWindows) { + let temporaryFocusHolderTab = null; + if (hasActive) { + // Blur to-be-moved tab, otherwise tabs.move() will activate them for each + // while the moving process and all dicarded tabs are unexpectedly restored. + const nextActiveTab = await TabsInternalOperation.blurTab(movedTabs, { + silently: true, + }); + if (!nextActiveTab) { + // There is no focusible left tab, so we move focus to a tmeporary tab. + // It will be removed automatically after tabs are moved. + temporaryFocusHolderTab = await browser.tabs.create({ + url: 'about:blank', + active: true, + windowId + }); + } + } movedTabs = await browser.tabs.move(movedTabIds, { windowId: destinationWindowId, index: toIndex }); + if (temporaryFocusHolderTab) { + const leftTabsInSourceWindow = await browser.tabs.query({ windowId }); + if (leftTabsInSourceWindow.length == 1) + browser.windows.remove(windowId); + else + browser.tabs.remove(temporaryFocusHolderTab.id); + } movedTabs = movedTabs.map(tab => Tab.get(tab.id)); movedTabIds = movedTabs.map(tab => tab.id); for (const tab of movedTabs) { + tab.$TST.temporaryMetadata.delete('movingAcrossWindows'); tab.windowId = destinationWindowId; } log('moved across windows: ', movedTabIds); @@ -2132,8 +2168,6 @@ SidebarConnection.onMessage.addListener(async (windowId, message) => { log('new window requested: ', message); await Tab.waitUntilTracked(message.tabIds); const tabs = message.tabIds.map(id => TabsStore.tabs.get(id)); - if (!message.duplicate) - await detachTabsFromTree(tabs); openNewWindowFromTabs(tabs, message); }; break; } diff --git a/waterfox/browser/components/sidebar/common/Tab.js b/waterfox/browser/components/sidebar/common/Tab.js index 1524305b43ce4..4c9a484c9c4f2 100644 --- a/waterfox/browser/components/sidebar/common/Tab.js +++ b/waterfox/browser/components/sidebar/common/Tab.js @@ -2261,7 +2261,10 @@ function destroyWaitingTabTask(task) { } function onWaitingTabTracked(tab) { - const tasks = mWaitingTasks.get(tab?.id); + if (!tab) + return; + + const tasks = mWaitingTasks.get(tab.id); if (!tasks) return; @@ -2279,7 +2282,10 @@ Tab.onElementBound.addListener(onWaitingTabTracked); Tab.onTracked.addListener(onWaitingTabTracked); function onWaitingTabDestroyed(tab) { - const tasks = mWaitingTasks.get(tab?.id); + if (!tab) + return; + + const tasks = mWaitingTasks.get(tab.id); if (!tasks) return; @@ -2322,6 +2328,7 @@ async function waitUntilTracked(tabId, options = {}) { const stack = configs.debug && new Error().stack; const tab = Tab.get(tabId); if (tab) { + onWaitingTabTracked(tab); if (options.element) return tab.$TST.promisedElement; return tab; @@ -2343,8 +2350,11 @@ async function waitUntilTracked(tabId, options = {}) { } }, configs.maximumDelayUntilTabIsTracked); // Tabs.moveTabs() between windows may take much time browser.tabs.get(tabId).catch(_error => null).then(tab => { - if (tab) + if (tab) { + if (Tab.get(tabId)) + onWaitingTabTracked(tab); return; + } const { resolve } = destroyWaitingTabTask(task); if (resolve) { log('waitUntilTracked was called for unexisting tab'); diff --git a/waterfox/browser/components/sidebar/common/constants.js b/waterfox/browser/components/sidebar/common/constants.js index e20d05bd53f19..8a728934f27ee 100644 --- a/waterfox/browser/components/sidebar/common/constants.js +++ b/waterfox/browser/components/sidebar/common/constants.js @@ -91,6 +91,11 @@ export const kCOMMAND_NOTIFY_CONTEXT_OVERRIDDEN = 'ws:notify-context-overri export const kCOMMAND_AUTODETECT_DUPLICATED_TAB_DETECTION_DELAY = 'ws:autodetect-duplicated-tab-detection-delay'; export const kCOMMAND_TEST_DUPLICATED_TAB_DETECTION = 'ws:test-duplicated-tab-detection'; export const kCOMMAND_WAIT_UNTIL_SUCCESSORS_UPDATED = 'ws:wait-until-successors-updated'; +export const kCOMMAND_GET_SIDEBAR_POSITION = 'ws:get-sidebar-position'; +export const kCOMMAND_GET_ABOVE_TAB = 'ws:get-above-tab'; +export const kCOMMAND_GET_BELOW_TAB = 'ws:get-below-tab'; +export const kCOMMAND_GET_LEFT_TAB = 'ws:get-left-tab'; +export const kCOMMAND_GET_RIGHT_TAB = 'ws:get-right-tab'; export const kCOMMAND_GET_BOUNDING_CLIENT_RECT = 'ws:get-bounding-client-rect'; export const kCOMMAND_ACTIVATE_TAB = 'ws:activate-tab'; diff --git a/waterfox/browser/components/sidebar/common/tabs-internal-operation.js b/waterfox/browser/components/sidebar/common/tabs-internal-operation.js index c61f8979ba919..3d47da4683a1f 100644 --- a/waterfox/browser/components/sidebar/common/tabs-internal-operation.js +++ b/waterfox/browser/components/sidebar/common/tabs-internal-operation.js @@ -70,6 +70,32 @@ export async function activateTab(tab, { byMouseOperation, keepMultiselection, s } } +export async function blurTab(bluredTabs, { windowId, silently } = {}) { + if (bluredTabs && + !Array.isArray(bluredTabs)) + bluredTabs = [bluredTabs]; + + const bluredTabIds = new Set(Array.from(bluredTabs || [], tab => tab.id || tab)); + + let bluredTabsFound = false; + let nextActiveTab = null; + for (const tab of Tab.getVisibleTabs(windowId || bluredTabs[0].windowId)) { + const blured = bluredTabIds.has(tab.id); + if (blured) + bluredTabsFound = true; + if (!bluredTabsFound) + nextActiveTab = tab; + if (bluredTabsFound && + !blured) { + nextActiveTab = tab; + break; + } + } + if (nextActiveTab) + await activateTab(nextActiveTab, { silently }); + return nextActiveTab; +} + export function removeTab(tab) { return removeTabs([tab]); } diff --git a/waterfox/browser/components/sidebar/manifest.json b/waterfox/browser/components/sidebar/manifest.json index 4d7d3c1407766..30b7b1a06b40c 100644 --- a/waterfox/browser/components/sidebar/manifest.json +++ b/waterfox/browser/components/sidebar/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "__MSG_extensionName__", - "version": "1.0.1", + "version": "1.0.2", "author": "Waterfox", "hidden": true, "description": "__MSG_extensionDescription__", @@ -133,9 +133,15 @@ "focusParent": { "description": "__MSG_command_focusParent__" }, + "focusParentOrCollapse": { + "description": "__MSG_command_focusParentOrCollapse__" + }, "focusFirstChild": { "description": "__MSG_command_focusFirstChild__" }, + "focusFirstChildOrExpand": { + "description": "__MSG_command_focusFirstChildOrExpand__" + }, "focusLastChild": { "description": "__MSG_command_focusLastChild__" }, @@ -145,16 +151,40 @@ "focusNextSibling": { "description": "__MSG_command_focusNextSibling__" }, + "simulateUpOnTree": { + "description": "__MSG_command_simulateUpOnTree__", + "suggested_key": { + "default": "Alt+Shift+Up" + } + }, + "simulateDownOnTree": { + "description": "__MSG_command_simulateDownOnTree__", + "suggested_key": { + "default": "Alt+Shift+Down" + } + }, + "simulateLeftOnTree": { + "description": "__MSG_command_simulateLeftOnTree__", + "suggested_key": { + "default": "Alt+Shift+Left" + } + }, + "simulateRightOnTree": { + "description": "__MSG_command_simulateRightOnTree__", + "suggested_key": { + "default": "Alt+Shift+Right" + } + }, "tabbarUp": { "description": "__MSG_command_tabbarUp__", "suggested_key": { - "default": "Alt+Shift+Up" + "default": "Alt+Up" } }, "tabbarPageUp": { "description": "__MSG_command_tabbarPageUp__", "suggested_key": { - "default": "Alt+Shift+PageUp" + "default": "Alt+PageUp" } }, "tabbarHome": { @@ -166,19 +196,19 @@ "tabbarDown": { "description": "__MSG_command_tabbarDown__", "suggested_key": { - "default": "Alt+Shift+Down" + "default": "Alt+Down" } }, "tabbarPageDown": { "description": "__MSG_command_tabbarPageDown__", "suggested_key": { - "default": "Alt+Shift+PageDown" + "default": "Alt+PageDown" } }, "tabbarEnd": { "description": "__MSG_command_tabbarEnd__", "suggested_key": { - "default": "Alt+Shift+End" + "default": "Alt+End" } }, "toggleSubPanel": { @@ -198,36 +228,62 @@ } }, "web_accessible_resources": [ - { "resources": ["/resources/group-tab.html*"], - "matches": [""] } + { + "resources": [ + "/resources/group-tab.html*" + ], + "matches": [ + "" + ] + } ], "protocol_handlers": [ - { "protocol": "ext+ws", - "name": "Waterfox", - "uriTemplate": "/resources/protocol-handler.html?%s" } + { + "protocol": "ext+ws", + "name": "Waterfox", + "uriTemplate": "/resources/protocol-handler.html?%s" + } ], "experiment_apis": { "prefs": { "schema": "experiments/prefs.json", "parent": { - "scopes": ["addon_parent"], - "paths": [["prefs"]], + "scopes": [ + "addon_parent" + ], + "paths": [ + [ + "prefs" + ] + ], "script": "experiments/prefs.js" } }, "syncPrefs": { "schema": "experiments/syncPrefs.json", "child": { - "scopes": ["addon_child"], - "paths": [["syncPrefs"]], + "scopes": [ + "addon_child" + ], + "paths": [ + [ + "syncPrefs" + ] + ], "script": "experiments/syncPrefs.js" } }, "waterfoxBridge": { "schema": "experiments/waterfoxBridge.json", "parent": { - "scopes": ["addon_parent"], - "paths": [["waterfoxBridge"]], + "scopes": [ + "addon_parent" + ], + "paths": [ + [ + "waterfoxBridge" + ] + ], "script": "experiments/waterfoxBridge.js" } } diff --git a/waterfox/browser/components/sidebar/sidebar/collapse-expand.js b/waterfox/browser/components/sidebar/sidebar/collapse-expand.js index b4f04a5ee9e19..ae6aeaee17044 100644 --- a/waterfox/browser/components/sidebar/sidebar/collapse-expand.js +++ b/waterfox/browser/components/sidebar/sidebar/collapse-expand.js @@ -55,6 +55,8 @@ export async function setCollapsed(tab, info = {}) { if (!TabsStore.ensureLivingTab(tab)) // do nothing for closed tab! return; + const changed = info.collapsed != tab.$TST.collapsed; + tab.$TST.shouldExpandLater = false; // clear flag if (info.collapsed) { @@ -104,12 +106,18 @@ export async function setCollapsed(tab, info = {}) { if (shouldApplyAnimation() && !info.justNow && - configs.collapseDuration > 0) + configs.collapseDuration > 0 && + changed) return; // force completion is required only for non-animation case //log('=> skip animation'); - if (tab.$TST.collapsed) + if (tab.$TST.collapsed) { + tab.$TST.removeState(Constants.kTAB_STATE_COLLAPSING); tab.$TST.addState(Constants.kTAB_STATE_COLLAPSED_DONE); + } + else { + tab.$TST.removeState(Constants.kTAB_STATE_EXPANDING); + } TabsStore.updateVirtualScrollRenderabilityIndexForTab(tab); onUpdated.dispatch(tab, { @@ -122,7 +130,8 @@ export async function setCollapsed(tab, info = {}) { if (!shouldApplyAnimation() || info.justNow || - configs.collapseDuration < 1) { + configs.collapseDuration < 1 || + !changed) { //log('=> skip animation'); onCompleted(tab, info); return; diff --git a/waterfox/browser/components/sidebar/sidebar/indent.js b/waterfox/browser/components/sidebar/sidebar/indent.js index f3ce2391dcb8f..92bd1f6abe253 100644 --- a/waterfox/browser/components/sidebar/sidebar/indent.js +++ b/waterfox/browser/components/sidebar/sidebar/indent.js @@ -263,8 +263,10 @@ BackgroundConnection.onMessage.addListener(async message => { if (!tab || !lastMessage) return; - if (tab.$TST.getAttribute(Constants.kLEVEL) != lastMessage.level) + if (tab.$TST.getAttribute(Constants.kLEVEL) != lastMessage.level) { tab.$TST.setAttribute(Constants.kLEVEL, lastMessage.level); + tryUpdateVisualMaxTreeLevel(); + } reserveToUpdateIndent(); }; break; diff --git a/waterfox/browser/components/sidebar/sidebar/pinned-tabs.js b/waterfox/browser/components/sidebar/sidebar/pinned-tabs.js index ce3790db55b68..99e2e7c19bc46 100644 --- a/waterfox/browser/components/sidebar/sidebar/pinned-tabs.js +++ b/waterfox/browser/components/sidebar/sidebar/pinned-tabs.js @@ -48,9 +48,14 @@ function log(...args) { let mTargetWindow; let mAreaHeight = 0; let mMaxVisibleRows = 0; +let mMaxCol = 0; +let mMaxColLastRow = 0; +let mMaxRow = 0; +const mTabsMatrix = new Map(); export function init() { mTargetWindow = TabsStore.getCurrentWindowId(); + browser.runtime.onMessage.addListener(onMessage); } function getTabHeight() { @@ -97,10 +102,18 @@ export function reposition(options = {}) { document.documentElement.style.removeProperty('--pinned-tabs-max-column'); Size.updateContainers(); + mTabsMatrix.clear(); let count = 0; let row = 0; + let col = 0; + mMaxCol = 0; + mMaxColLastRow = 0; + mMaxRow = 0; for (const tab of pinnedTabs) { + mMaxCol = Math.max(col, mMaxCol); + mMaxRow = row; + count++; if (options.justNow) tab.$TST.removeState(Constants.kTAB_STATE_ANIMATION_READY); @@ -108,9 +121,14 @@ export function reposition(options = {}) { tab.$TST.toggleState(Constants.kTAB_STATE_FAVICONIZED, faviconized); tab.$TST.toggleState(Constants.kTAB_STATE_LAST_ROW, row == maxRow - 1); + if (row == maxRow - 1) + mMaxColLastRow = col; + if (options.justNow) tab.$TST.addState(Constants.kTAB_STATE_ANIMATION_READY); + mTabsMatrix.set(`${col}:${row}`, tab.id); + /* log('pinned tab: ', { tab: dumpTab(tab), @@ -120,9 +138,11 @@ export function reposition(options = {}) { }); */ + col++; if (count > 0 && - count / maxCol == 0) { + count % maxCol == 0) { row++; + col = 0; //log('=> new row'); } } @@ -147,6 +167,10 @@ function reset() { } mAreaHeight = 0; mMaxVisibleRows = 0; + mMaxCol = 0; + mMaxColLastRow = 0; + mMaxRow = 0; + mTabsMatrix.clear(); Size.updateContainers(); } @@ -155,6 +179,92 @@ function clearStyle(tab) { tab.$TST.removeState(Constants.kTAB_STATE_LAST_ROW); } +function getTabPosition(tab) { + if (!tab) + throw new Error('missing tab'); + + log('getTabPosition from ', [...mTabsMatrix.keys()]); + for (const [position, tabId] of mTabsMatrix.entries()) { + if (tabId != tab.id) + continue; + const [col, row] = position.split(':'); + log(` => ${col}:${row}`); + return { + col: parseInt(col), + row: parseInt(row), + }; + } + + throw new Error(`no pinned tab with id ${tab.id}`); +} + +// This must be synchronous and return Promise on demando, to avoid +// blocking to other listeners. +function onMessage(message, _sender, _respond) { + if (!message || + typeof message.type != 'string' || + message.type.indexOf('ws:') != 0) + return; + + if (message.windowId && + message.windowId != mTargetWindow) + return; + + //log('onMessage: ', message, sender); + switch (message.type) { + case Constants.kCOMMAND_GET_ABOVE_TAB: { + try { + const { col, row } = getTabPosition(Tab.get(message.tabId)); + const nextRow = row - 1; + log(`above tab: ${col}:${row} => ${col}:${nextRow}`); + return Promise.resolve( + mTabsMatrix.get(`${col}:${nextRow}`) || + null + ); + } + catch(_error) { + return Promise.resolve( + mTabsMatrix.get(`0:${mMaxRow}`) || + null + ); + } + }; break; + + case Constants.kCOMMAND_GET_BELOW_TAB: { + const { col, row } = getTabPosition(Tab.get(message.tabId)); + const nextRow = row + 1; + log(`below tab: ${col}:${row} => ${col}:${nextRow}`); + return Promise.resolve( + mTabsMatrix.get(`${col}:${nextRow}`) || + mTabsMatrix.get(`${mMaxColLastRow}:${nextRow}`) || + null + ); + }; break; + + case Constants.kCOMMAND_GET_LEFT_TAB: { + const { col, row } = getTabPosition(Tab.get(message.tabId)); + const maxCol = row == mMaxRow ? mMaxColLastRow : mMaxCol; + const nextCol = col == 0 ? maxCol : col - 1; + log(`left tab: ${col}:${row} => ${nextCol}:${row}`); + return Promise.resolve( + mTabsMatrix.get(`${nextCol}:${row}`) || + null + ); + }; break; + + case Constants.kCOMMAND_GET_RIGHT_TAB: { + const { col, row } = getTabPosition(Tab.get(message.tabId)); + const maxCol = row == mMaxRow ? mMaxColLastRow : mMaxCol; + const nextCol = col == maxCol ? 0 : col + 1; + log(`right tab: ${col}:${row} => ${nextCol}:${row}`); + return Promise.resolve( + mTabsMatrix.get(`${nextCol}:${row}`) || + null + ); + }; break; + } +} + const BUFFER_KEY_PREFIX = 'pinned-tabs-'; BackgroundConnection.onMessage.addListener(async message => { diff --git a/waterfox/browser/components/sidebar/sidebar/scroll.js b/waterfox/browser/components/sidebar/sidebar/scroll.js index 05026fb8b49a4..b3a5a342bc9c9 100644 --- a/waterfox/browser/components/sidebar/sidebar/scroll.js +++ b/waterfox/browser/components/sidebar/sidebar/scroll.js @@ -528,9 +528,14 @@ function updateStickyTabs(renderableTabs, { staticRendering, skipRefreshTabs } = return stickyTabs; } -function getScrollBoxFor(tab) { +function getScrollBoxFor(tab, { allowFallback } = {}) { if (!tab || !tab.pinned) return mNormalScrollBox; // the default + if (allowFallback && + mPinnedScrollBox.$scrollTopMax == 0) { + log('pinned tabs are not scrollable, fallback to normal tabs'); + return mNormalScrollBox; + } return mPinnedScrollBox; } @@ -597,7 +602,7 @@ function scrollTo(params = {}) { return smoothScrollTo(params); //cancelPerformingAutoScroll(); - const scrollBox = getScrollBoxFor(params.tab); + const scrollBox = getScrollBoxFor(params.tab, { allowFallback: true }); const scrollTop = params.tab ? scrollBox.$scrollTop + calculateScrollDeltaForTab(params.tab) : typeof params.position == 'number' ? @@ -635,7 +640,7 @@ function calculateScrollDeltaForTab(tab, { over } = {}) { tab = tab.$TST.collapsed && tab.$TST.nearestVisibleAncestorOrSelf || tab; const tabRect = getTabRect(tab); - const scrollBoxRect = Size.getScrollBoxRect(getScrollBoxFor(tab)); + const scrollBoxRect = Size.getScrollBoxRect(getScrollBoxFor(tab, { allowFallback: true })); const overScrollOffset = over === false ? 0 : Math.ceil(tabRect.height / 2); @@ -695,7 +700,7 @@ async function smoothScrollTo(params = {}) { smoothScrollTo.stopped = false; - const scrollBox = getScrollBoxFor(params.tab); + const scrollBox = params.scrollBox || getScrollBoxFor(params.tab, { allowFallback: true }); let delta, startPosition, endPosition; if (params.tab) { @@ -758,8 +763,13 @@ async function smoothScrollTo(params = {}) { smoothScrollTo.currentOffset= 0; async function smoothScrollBy(delta) { + const scrollBox = getScrollBoxFor( + Tab.getActiveTab(TabsStore.getCurrentWindowId()), + { allowFallback: true } + ); return smoothScrollTo({ - position: getScrollBoxFor(Tab.getActiveTab(TabsStore.getCurrentWindowId())).$scrollTop + delta, + position: scrollBox.$scrollTop + delta, + scrollBox, }); } @@ -1002,23 +1012,24 @@ async function onWheel(event) { return; } - if (EventUtils.getElementTarget(event).closest('.sticky-tabs-container')) { - event.stopImmediatePropagation(); - event.preventDefault(); - scrollTo({ delta: event.deltaY }); - return; - } + const tab = EventUtils.getTabFromEvent(event); + const scrollBox = getScrollBoxFor(tab, { allowFallback: true }); if (!TSTAPI.isScrollLocked()) { cancelRunningScroll(); + if (EventUtils.getElementTarget(event).closest('.sticky-tabs-container') || + (tab?.pinned && + scrollBox != mPinnedScrollBox)) { + event.stopImmediatePropagation(); + event.preventDefault(); + scrollTo({ delta: event.deltaY, scrollBox }); + } return; } event.stopImmediatePropagation(); event.preventDefault(); - const tab = EventUtils.getTabFromEvent(event); - const scrollBox = getScrollBoxFor(tab); TSTAPI.notifyScrolled({ tab, scrollContainer: scrollBox, @@ -1169,7 +1180,7 @@ async function onBackgroundMessage(message) { case Constants.kCOMMAND_SCROLL_TABBAR: { const activeTab = Tab.getActiveTab(TabsStore.getCurrentWindowId()); - const scrollBox = getScrollBoxFor(activeTab); + const scrollBox = getScrollBoxFor(activeTab, { allowFallback: true }); switch (String(message.by).toLowerCase()) { case 'lineup': smoothScrollBy(-Size.getRenderedTabHeight() * configs.scrollLines); diff --git a/waterfox/browser/components/sidebar/sidebar/sidebar.js b/waterfox/browser/components/sidebar/sidebar/sidebar.js index 9cfec4a39cbff..c08d8f826c69b 100644 --- a/waterfox/browser/components/sidebar/sidebar/sidebar.js +++ b/waterfox/browser/components/sidebar/sidebar/sidebar.js @@ -1074,6 +1074,11 @@ function onMessage(message, _sender, _respond) { } }); + case Constants.kCOMMAND_GET_SIDEBAR_POSITION: + return Promise.resolve(document.documentElement.classList.contains('right') ? + Constants.kTABBAR_POSITION_RIGHT : + Constants.kTABBAR_POSITION_LEFT); + // for automated tests case Constants.kCOMMAND_GET_BOUNDING_CLIENT_RECT: { const range = document.createRange(); diff --git a/waterfox/browser/components/sidebar/sidebar/size.js b/waterfox/browser/components/sidebar/sidebar/size.js index b49257bf233f9..bade0a2d6867c 100644 --- a/waterfox/browser/components/sidebar/sidebar/size.js +++ b/waterfox/browser/components/sidebar/sidebar/size.js @@ -220,13 +220,18 @@ export function updateTabs() { } export function updateContainers() { + let modifiedCount = 0; + mPinnedTabsScrollBoxRect = mPinnedScrollBox.getBoundingClientRect(); mNormalTabsScrollBoxRect = mNormalScrollBox.getBoundingClientRect(); const pinnedContainerBox = mPinnedScrollBox.querySelector('.tabs'); const pinnedContainerBoxRect = pinnedContainerBox.getBoundingClientRect(); const pinnedContainerStyle = window.getComputedStyle(pinnedContainerBox, null); - mPinnedTabsContainerWidth = pinnedContainerBoxRect.width - parseFloat(pinnedContainerStyle.paddingLeft) - parseFloat(pinnedContainerStyle.borderLeftWidth) - parseFloat(pinnedContainerStyle.paddingRight) - parseFloat(pinnedContainerStyle.borderRightWidth); + const newPinnedTabsContainerWidth = pinnedContainerBoxRect.width - parseFloat(pinnedContainerStyle.paddingLeft) - parseFloat(pinnedContainerStyle.borderLeftWidth) - parseFloat(pinnedContainerStyle.paddingRight) - parseFloat(pinnedContainerStyle.borderRightWidth); + if (newPinnedTabsContainerWidth != mPinnedTabsContainerWidth) + modifiedCount++; + mPinnedTabsContainerWidth = newPinnedTabsContainerWidth; const range = document.createRange(); //range.selectNodeContents(mTabBar); @@ -235,10 +240,19 @@ export function updateContainers() { range.selectNodeContents(mTabBar); range.setStartAfter(mNormalScrollBox); const normalTabsViewPortFollowingAreaSize = range.getBoundingClientRect().height; - mNormalTabsViewPortSize = mTabBar.offsetHeight - normalTabsViewPortPrecedingAreaSize - normalTabsViewPortFollowingAreaSize; + const newNormalTabsViewportSize = mTabBar.offsetHeight - normalTabsViewPortPrecedingAreaSize - normalTabsViewPortFollowingAreaSize; range.detach(); + if (newNormalTabsViewportSize != mNormalTabsViewPortSize) + modifiedCount++; + mNormalTabsViewPortSize = newNormalTabsViewportSize; + + const newAllTabsAreaSize = mTabBar.parentNode.offsetHeight; + if (newAllTabsAreaSize != mAllTabsAreaSize) + modifiedCount++; + mAllTabsAreaSize = newAllTabsAreaSize; - mAllTabsAreaSize = mTabBar.parentNode.offsetHeight; + if (modifiedCount > 0) + onUpdated.dispatch(); } export function calc(expression) {