From 5720cafc9c1de045fa4f465ca30cd986263bdcd8 Mon Sep 17 00:00:00 2001
From: "YUKI \"Piro\" Hiroshi"
Date: Thu, 28 Nov 2024 18:30:48 +0900
Subject: [PATCH] Implement simulated tab preview panel (#3671)
* Prepare frame contents to render tab preview tooltip #3412
* Add comments to describe the design of the module
* Implement events to track mouseenter/leave on tab substances
* Send show/hide preview message to the active tab #3412
* Send message to show tab preview with preview URL
* Show tab preview
* Remove needless parameter
* Simulate behavior of the native tab preview panel #3412
* Fix indent
* Apply appearance same to the native tab preview panel as possible as we can #3412
* Cleanup old subframes #3412
* Don't try to capture discarded tabs #3412
* Cleanup cached tab object to avoid mismatched relation
* Make more safe for invalid tab case
* Show tab preview panel with fixed width
* Use internal name same to Firefox's one
* Suppress error about sending message to special tabs
* Clear preview when active tab is switched
* Re-prepare frame to show tab preview tooltip automatically
* Hide tab preview panel when the sidebar is closed
* Cleanup needless iframe
* Hide tab preview tooltip when the tab is detached
* Destroy and reconstruct frame more safely
* Show tab preview panel with expected scale in zoomed tabs
* Apply platform specific appearance correctly
* Don't apply opacity animation while updating
* Position tab preview in better coordinates even if the sidebar header is shown.
* Fix mispositioning in zoomed contents
* Ignore delayed messages
* Suppress regular tooltip when in-content tab previw tooltip is not available
* Calculate offset more correctly
* Show tab preview panel near the sidebar
* Deactivate tab preview tooltip by default
* Show tab preview tooltip in the sidebar if the tab contents is privileged
* Calculate size and position of the tab preview tooltip more correctly
* Don't try to embed tab preview tooltip in other addon's contents
* Show tab preview tooltip as soon as possible
* Fallback to in-sidebar tab preview tooltip if the active tab is privileged
* Add an option UI for tab preview panel
---
webextensions/_locales/en/messages.json | 1 +
webextensions/_locales/ja/messages.json | 1 +
webextensions/background/api-tabs-listener.js | 8 +
.../background/browser-action-menu.js | 7 +
webextensions/common/common.js | 1 +
webextensions/common/constants.js | 1 +
webextensions/common/permissions.js | 4 +-
webextensions/common/sidebar-connection.js | 29 +-
webextensions/manifest.json | 1 +
webextensions/options/init.js | 9 +
webextensions/options/options.html | 15 +-
.../resources/tab-preview-frame.html | 11 +
webextensions/resources/tab-preview-frame.js | 294 ++++++++++++++
.../sidebar/components/TabElement.js | 47 ++-
webextensions/sidebar/index.js | 1 +
webextensions/sidebar/mouse-event-listener.js | 1 +
webextensions/sidebar/sidebar.html | 1 +
webextensions/sidebar/styles/base.css | 20 +-
webextensions/sidebar/tab-preview-tooltip.js | 365 ++++++++++++++++++
19 files changed, 807 insertions(+), 10 deletions(-)
create mode 100644 webextensions/resources/tab-preview-frame.html
create mode 100644 webextensions/resources/tab-preview-frame.js
create mode 100644 webextensions/sidebar/tab-preview-tooltip.js
diff --git a/webextensions/_locales/en/messages.json b/webextensions/_locales/en/messages.json
index e7e6c8b82..7e9de3edf 100644
--- a/webextensions/_locales/en/messages.json
+++ b/webextensions/_locales/en/messages.json
@@ -409,6 +409,7 @@
"config_maxPinnedTabsRowsAreaPercentage_label_after": { "message": "% of the sidebar" },
"config_animation_label": { "message": "Enable animation effects" },
"config_animationForce_label": { "message": "Enable animations regardless \"reduce animations\" platform settings" },
+ "config_tabPreviewTooltip_label": { "message": "Show tab preview image instead of tooltip" },
"config_showCollapsedDescendantsByTooltip_label": { "message": "Show collapsed descendants in the tooltip on a tab" },
"config_shiftTabsForScrollbarDistance_label_before": { "message": "Shift tabs aside " },
"config_shiftTabsForScrollbarDistance_label_after": { "message": " to keep in-tab buttons touchable avoiding covered with the auto-shown scrollbar" },
diff --git a/webextensions/_locales/ja/messages.json b/webextensions/_locales/ja/messages.json
index f7e7661ea..0a67038fb 100644
--- a/webextensions/_locales/ja/messages.json
+++ b/webextensions/_locales/ja/messages.json
@@ -406,6 +406,7 @@
"config_maxPinnedTabsRowsAreaPercentage_label_after": { "message": "%まで" },
"config_animation_label": { "message": "アニメーション効果を有効にする" },
"config_animationForce_label": { "message": "アニメーションを抑制するプラットフォームの設定を無視して有効にする" },
+ "config_tabPreviewTooltip_label": { "message": "ツールチップの代わりにタブのプレビュー画像を表示する" },
"config_showCollapsedDescendantsByTooltip_label": { "message": "タブのツールチップに折りたたまれた子孫タブの情報を含める" },
"config_shiftTabsForScrollbarDistance_label_before": { "message": "自動的に表示されるスクロールバーに覆われてタブ内のボタンに触れなくならないよう、" },
"config_shiftTabsForScrollbarDistance_label_after": { "message": "ぶんだけタブをずらして表示する" },
diff --git a/webextensions/background/api-tabs-listener.js b/webextensions/background/api-tabs-listener.js
index b7a539d52..8094874b5 100644
--- a/webextensions/background/api-tabs-listener.js
+++ b/webextensions/background/api-tabs-listener.js
@@ -1220,6 +1220,14 @@ async function onDetached(tabId, detachInfo) {
tabId,
wasPinned: oldTab.pinned
});
+ // We need to notify this to some conetnt scripts, to destroy themselves.
+ try {
+ browser.tabs.sendMessage(tabId, {
+ type: Constants.kCOMMAND_NOTIFY_TAB_DETACHED_FROM_WINDOW,
+ }).catch(_error => {});
+ }
+ catch (_error) {
+ }
TabsStore.addRemovedTab(oldTab);
oldWindow.detachTab(oldTab.id, {
diff --git a/webextensions/background/browser-action-menu.js b/webextensions/background/browser-action-menu.js
index a457410cc..2e7320b4a 100644
--- a/webextensions/background/browser-action-menu.js
+++ b/webextensions/background/browser-action-menu.js
@@ -125,6 +125,12 @@ const mItems = [
type: 'checkbox',
expert: true
},
+ {
+ title: browser.i18n.getMessage('config_tabPreviewTooltip_label'),
+ key: 'tabPreviewTooltip',
+ type: 'checkbox',
+ permissions: Permissions.ALL_URLS
+ },
{
title: browser.i18n.getMessage('config_showCollapsedDescendantsByTooltip_label'),
key: 'showCollapsedDescendantsByTooltip',
@@ -1372,6 +1378,7 @@ const mItems = [
},
{
title: browser.i18n.getMessage('config_requestPermissions_allUrls_ctrlTabTracking'),
+ key: 'skipCollapsedTabsForTabSwitchingShortcuts',
type: 'checkbox',
permissions: Permissions.ALL_URLS
},
diff --git a/webextensions/common/common.js b/webextensions/common/common.js
index e3843788b..5def3aeef 100644
--- a/webextensions/common/common.js
+++ b/webextensions/common/common.js
@@ -171,6 +171,7 @@ export const configs = new Configs({
showNewTabActionSelector: true,
longPressOnNewTabButton: Constants.kCONTEXTUAL_IDENTITY_SELECTOR,
zoomable: false,
+ tabPreviewTooltip: false,
showOverflowTitleByTooltip: true,
showCollapsedDescendantsByTooltip: true,
diff --git a/webextensions/common/constants.js b/webextensions/common/constants.js
index a64c103b6..70091ea4b 100644
--- a/webextensions/common/constants.js
+++ b/webextensions/common/constants.js
@@ -34,6 +34,7 @@ export const kCOMMAND_CONFIRM_TO_CLOSE_TABS = 'treestyletab:confirm-to-
export const kCOMMAND_SHOW_DIALOG = 'treestyletab:show-dialog';
export const kCOMMAND_NOTIFY_BACKGROUND_READY = 'treestyletab:notify-background-ready';
export const kCOMMAND_NOTIFY_CONNECTION_READY = 'treestyletab:notify-connection-ready';
+export const kCOMMAND_NOTIFY_SIDEBAR_CLOSED = 'treestyletab:notify-sidebar-closed';
export const kCOMMAND_NOTIFY_TAB_CREATING = 'treestyletab:notify-tab-creating';
export const kCOMMAND_NOTIFY_TAB_CREATED = 'treestyletab:notify-tab-created';
export const kCOMMAND_NOTIFY_TAB_UPDATED = 'treestyletab:notify-tab-updated';
diff --git a/webextensions/common/permissions.js b/webextensions/common/permissions.js
index c7f2396e3..9fa853ad7 100644
--- a/webextensions/common/permissions.js
+++ b/webextensions/common/permissions.js
@@ -117,7 +117,9 @@ export function bindToCheckbox(permissions, checkbox, options = {}) {
.then(granted => {
const checked = options.onInitialized ?
options.onInitialized(granted) :
- undefined;
+ checkbox.dataset.relatedConfigKey ?
+ configs[checkbox.dataset.relatedConfigKey] :
+ undefined;
checkbox.checked = checked !== undefined ? !!checked : granted;
})
.catch(_error => {
diff --git a/webextensions/common/sidebar-connection.js b/webextensions/common/sidebar-connection.js
index cb5a05fc8..80b8adfa0 100644
--- a/webextensions/common/sidebar-connection.js
+++ b/webextensions/common/sidebar-connection.js
@@ -10,7 +10,8 @@ import EventListenerManager from '/extlib/EventListenerManager.js';
import {
log as internalLogger,
mapAndFilterUniq,
- configs
+ configs,
+ wait,
} from './common.js';
import * as Constants from './constants.js';
import * as TabsStore from './tabs-store.js';
@@ -223,6 +224,32 @@ if (Constants.IS_BACKGROUND) {
mReceivers.delete(windowId);
mFocusState.delete(windowId);
onDisconnected.dispatch(windowId, connections.size);
+
+ // We need to notify this to some conetnt scripts, to destroy themselves.
+ /*
+ browser.runtime.sendMessage({
+ type: Constants.kCOMMAND_NOTIFY_SIDEBAR_CLOSED,
+ windowId,
+ });
+ */
+ browser.windows.get(windowId, { populate: true }).then(async win => {
+ let count = 0;
+ for (const tab of win.tabs) {
+ count++;
+ if (count >= 20) {
+ // We should not block too long seconds on too much tabs case.
+ await wait(10);
+ count = 0;
+ }
+ try {
+ browser.tabs.sendMessage(tab.id, {
+ type: Constants.kCOMMAND_NOTIFY_SIDEBAR_CLOSED,
+ });
+ }
+ catch (_error) {
+ }
+ }
+ });
};
const receiver = message => {
if (Array.isArray(message))
diff --git a/webextensions/manifest.json b/webextensions/manifest.json
index 65d6fd85e..00579ead2 100644
--- a/webextensions/manifest.json
+++ b/webextensions/manifest.json
@@ -261,6 +261,7 @@
"browser_style": false
},
"web_accessible_resources": [
+ "/resources/blank.html",
"/resources/group-tab.html*",
"/resources/icons/*",
"/sidebar/styles/icons/*"
diff --git a/webextensions/options/init.js b/webextensions/options/init.js
index b666661e1..facdb96f8 100644
--- a/webextensions/options/init.js
+++ b/webextensions/options/init.js
@@ -781,6 +781,15 @@ function initPermissionOptions() {
Permissions.isGranted(Permissions.BOOKMARKS).then(granted => updateBookmarksUI(granted));
Permissions.isGranted(Permissions.ALL_URLS).then(granted => updateCtrlTabSubItems(granted));
+ Permissions.bindToCheckbox(
+ Permissions.ALL_URLS,
+ document.querySelector('#allUrlsPermissionGranted_tabPreviewTooltip'),
+ {
+ onChanged: (granted) => {
+ configs.tabPreviewTooltip = granted;
+ }
+ }
+ );
Permissions.bindToCheckbox(
Permissions.ALL_URLS,
document.querySelector('#allUrlsPermissionGranted_ctrlTabTracking'),
diff --git a/webextensions/options/options.html b/webextensions/options/options.html
index 5867210e2..15cb17dd5 100644
--- a/webextensions/options/options.html
+++ b/webextensions/options/options.html
@@ -115,7 +115,14 @@ __MSG_config_appearance_caption__
size="5"
min="0"
max="100">
- __MSG_config_maxPinnedTabsRowsAreaPercentage_label_after__
+ __MSG_config_maxPinnedTabsRowsAreaPercentage_label_after__[
+
+