mirror of
https://github.com/zen-browser/desktop.git
synced 2026-05-27 23:35:09 +00:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8274718d0d | ||
|
|
5511defb1e | ||
|
|
894fbd6a4f | ||
|
|
5a26cb6601 | ||
|
|
2eb09f092c | ||
|
|
66081e97f6 | ||
|
|
08873b0ce7 | ||
|
|
6dce6d60e9 | ||
|
|
302e321da6 | ||
|
|
5d5d51c6f3 | ||
|
|
262209f5a3 | ||
|
|
aa4aae9e26 | ||
|
|
827b89d4e8 | ||
|
|
a1e7af81cc | ||
|
|
1db7e19037 | ||
|
|
c7db15fe9d | ||
|
|
502d3f7d94 | ||
|
|
bd0651165c | ||
|
|
af4f336bf5 | ||
|
|
bffc16ba29 | ||
|
|
7c53549076 | ||
|
|
0fe3a4af10 | ||
|
|
4d1d4ab1b6 | ||
|
|
2990de8366 | ||
|
|
76966ee1c3 | ||
|
|
4f9a932e77 | ||
|
|
f2603521e5 | ||
|
|
24f695c452 | ||
|
|
f5effd4dcd | ||
|
|
b5bb7d7c8b | ||
|
|
604365dd38 | ||
|
|
466d089fc0 | ||
|
|
e28a10f6a9 | ||
|
|
102be6cd90 | ||
|
|
6e1e1d061b | ||
|
|
43250d6d37 | ||
|
|
12f0c455d1 | ||
|
|
049c3c6f54 | ||
|
|
658ac94334 | ||
|
|
09a90099c7 | ||
|
|
051470f139 | ||
|
|
8f1cb88c11 | ||
|
|
3ef233a4c2 | ||
|
|
38fcd7e872 | ||
|
|
76f17c3a57 | ||
|
|
b5c2451525 | ||
|
|
ba2a854784 |
1
.github/workflows/linux-release-build.yml
vendored
1
.github/workflows/linux-release-build.yml
vendored
@@ -92,7 +92,6 @@ jobs:
|
||||
|
||||
- name: Fix Rust version
|
||||
run: |
|
||||
# Install a rust version compatible with LLVM 18
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain $(cat .rust-toolchain)
|
||||
source $HOME/.cargo/env
|
||||
|
||||
|
||||
17
.github/workflows/sync-upstream.yml
vendored
17
.github/workflows/sync-upstream.yml
vendored
@@ -73,15 +73,13 @@ jobs:
|
||||
npm run sync
|
||||
fi
|
||||
|
||||
- name: Run formatter
|
||||
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
|
||||
run: |
|
||||
sudo apt install python3-autopep8
|
||||
npm run pretty
|
||||
- name: Install autopep8
|
||||
run: sudo apt install python3-autopep8
|
||||
|
||||
- name: Check if any files changed
|
||||
id: git-check
|
||||
run: |
|
||||
npm run pretty
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "files_changed=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
@@ -111,6 +109,15 @@ jobs:
|
||||
if: steps.git-check.outputs.files_changed == 'true'
|
||||
run: python3 scripts/import_external_tests.py || true
|
||||
|
||||
- name: Import external patches
|
||||
if: steps.git-check.outputs.files_changed == 'true'
|
||||
run: python3 scripts/import_external_patches.py || true
|
||||
|
||||
- name: Run formatter
|
||||
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
|
||||
run: |
|
||||
npm run pretty
|
||||
|
||||
- name: Create pull request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
if: steps.git-check.outputs.files_changed == 'true'
|
||||
|
||||
@@ -37,5 +37,6 @@ src/zen/common/ZenEmojis.mjs
|
||||
|
||||
src/zen/split-view/zen-decks.css
|
||||
src/zen/workspaces/zen-workspaces.css
|
||||
src/zen/common/styles/zen-toolbar.css
|
||||
|
||||
*.inc
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.83
|
||||
1.89
|
||||
@@ -9,9 +9,6 @@ ac_add_options --with-app-basename=Zen
|
||||
# Localization (Must be an absolute path)
|
||||
ac_add_options --with-l10n-base="${topsrcdir}/browser/locales"
|
||||
|
||||
# See https://github.com/zen-browser/desktop/issues/11917 for future plans.
|
||||
# We should be removing this at some point and start supporting XDG dirs.
|
||||
ac_add_options --with-user-appdir=".${binName}"
|
||||
export MOZ_APP_BASENAME=Zen
|
||||
export MOZ_BRANDING_DIRECTORY=${brandingDir}
|
||||
export MOZ_OFFICIAL_BRANDING_DIRECTORY=${brandingDir}
|
||||
@@ -85,6 +82,11 @@ if test "$ZEN_RELEASE"; then
|
||||
export MOZ_PACKAGE_JSSHELL=1
|
||||
|
||||
ac_add_options --disable-crashreporter
|
||||
|
||||
# Experimental flag, enabled only on nightly for Firefox.
|
||||
# Should bring in some nice performance improvements,
|
||||
# but may cause stability issues.
|
||||
ac_add_options --enable-replace-malloc
|
||||
fi
|
||||
|
||||
ac_add_options --enable-unverified-updates
|
||||
|
||||
@@ -30,17 +30,11 @@ if test "$ZEN_RELEASE"; then
|
||||
fi
|
||||
|
||||
# Disable DMD and ELF hacks, enable linker lld
|
||||
ac_add_options --disable-dmd
|
||||
ac_add_options --enable-linker=lld
|
||||
ac_add_options --disable-elf-hack
|
||||
ac_add_options --disable-necko-wifi
|
||||
|
||||
# Stripping options for release builds
|
||||
ac_add_options --enable-install-strip
|
||||
ac_add_options --enable-strip
|
||||
export STRIP_FLAGS="--strip-debug --strip-unneeded"
|
||||
fi
|
||||
|
||||
# Common options
|
||||
ac_add_options --enable-alsa
|
||||
ac_add_options --enable-pulseaudio
|
||||
|
||||
@@ -6,7 +6,11 @@ zen-panel-ui-current-profile-text = current profile
|
||||
|
||||
unified-extensions-description = Extensions are used to bring more extra functionality into { -brand-short-name }.
|
||||
tab-context-zen-reset-pinned-tab =
|
||||
.label = Reset Pinned Tab
|
||||
.label =
|
||||
{ $isEssential ->
|
||||
[true] Reset Essential Tab
|
||||
*[false] Reset Pinned Tab
|
||||
}
|
||||
.accesskey = R
|
||||
tab-context-zen-add-essential =
|
||||
.label = Add to Essentials
|
||||
@@ -16,7 +20,11 @@ tab-context-zen-remove-essential =
|
||||
.label = Remove from Essentials
|
||||
.accesskey = R
|
||||
tab-context-zen-replace-pinned-url-with-current =
|
||||
.label = Replace Pinned URL with Current
|
||||
.label =
|
||||
{ $isEssential ->
|
||||
[true] Replace Essential URL with Current
|
||||
*[false] Replace Pinned URL with Current
|
||||
}
|
||||
.accesskey = C
|
||||
tab-context-zen-edit-title =
|
||||
.label = Change Label...
|
||||
@@ -137,3 +145,7 @@ zen-window-sync-migration-dialog-title = Keep Your Windows in Sync
|
||||
zen-window-sync-migration-dialog-message = Zen now syncs windows on the same device, so changes in one window are reflected across the others instantly.
|
||||
zen-window-sync-migration-dialog-learn-more = Learn More
|
||||
zen-window-sync-migration-dialog-accept = Got It
|
||||
|
||||
zen-appmenu-new-blank-window =
|
||||
.label = New blank window
|
||||
|
||||
|
||||
@@ -22,5 +22,5 @@ zen-menubar-appearance-light =
|
||||
zen-menubar-appearance-dark =
|
||||
.label = Dark
|
||||
|
||||
zen-menubar-new-unsynced-window =
|
||||
.label = New blank window
|
||||
zen-menubar-new-blank-window =
|
||||
.label = New Blank Window
|
||||
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -5610,9 +5610,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"version": "4.17.23",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
|
||||
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
|
||||
@@ -54,3 +54,6 @@
|
||||
|
||||
- name: zen.view.draggable-sidebar
|
||||
value: true
|
||||
|
||||
- name: zen.view.overflow-webext-toolbar
|
||||
value: "@IS_TWILIGHT@"
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
- name: zen.urlbar.show-pip-button
|
||||
value: false
|
||||
|
||||
- name: zen.urlbar.open-on-startup
|
||||
value: true
|
||||
|
||||
# Mark: Zen suggestions controls
|
||||
|
||||
- name: zen.urlbar.suggestions.quick-actions
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
# 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/.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import requests
|
||||
|
||||
BASE_URI = "https://phabricator.services.mozilla.com"
|
||||
OUTPUT_DIR = os.path.join(os.getcwd(), "src", "firefox-patches")
|
||||
|
||||
|
||||
def download_phab_patch(phab_id, output_file):
|
||||
"""Download a Phabricator patch by its ID and save it to output_file."""
|
||||
patch_url = f"{BASE_URI}/{phab_id}?download=true"
|
||||
try:
|
||||
print(f"Downloading patch from {patch_url}")
|
||||
response = requests.get(patch_url)
|
||||
response.raise_for_status() # Raise an error for bad responses
|
||||
with open(output_file, 'wb') as f:
|
||||
f.write(response.content)
|
||||
print(f"Patch saved to {output_file}")
|
||||
except requests.RequestException as e:
|
||||
print(f"Error downloading patch: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: python download_phab_patch.py <PHABRICATOR_ID> [output_file]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
phab_id = sys.argv[1]
|
||||
output_file = sys.argv[2] if len(sys.argv) > 2 else f"phab_{phab_id}"
|
||||
output_file = os.path.join(OUTPUT_DIR, output_file + ".patch")
|
||||
|
||||
download_phab_patch(phab_id, output_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -18,10 +18,18 @@ FILE_PREFIX = """
|
||||
# This file is autogenerated by scripts/import_external_tests.py
|
||||
# Do not edit manually.
|
||||
|
||||
"""
|
||||
|
||||
BROWSER_MANIFEST_LIST_PREFIX = """
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
"""
|
||||
|
||||
XPCSHELL_MANIFESTS_LIST_PREFIX = """
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
"""
|
||||
|
||||
FILE_SUFFIX = "]"
|
||||
VALID_MANIFEST_FILES = ["browser.toml", "xpcshell.toml"]
|
||||
|
||||
|
||||
def get_tests_manifest():
|
||||
@@ -38,12 +46,17 @@ def validate_tests_path(path, files, ignore_list):
|
||||
for ignore in ignore_list:
|
||||
if ignore not in files:
|
||||
die_with_error(f"Ignore file '{ignore}' not found in tests folder '{path}'")
|
||||
if "browser.toml" not in files or "browser.js" in ignore_list:
|
||||
die_with_error(f"'browser.toml' not found in tests folder '{path}'")
|
||||
if not any(manifest_file in files for manifest_file in VALID_MANIFEST_FILES):
|
||||
die_with_error(f"None of the valid manifest files {VALID_MANIFEST_FILES} found in tests folder '{path}'")
|
||||
|
||||
|
||||
def disable_and_replace_manifest(manifest, output_path):
|
||||
toml_file = os.path.join(output_path, "browser.toml")
|
||||
toml_file = None
|
||||
for manifest_file in VALID_MANIFEST_FILES:
|
||||
candidate = os.path.join(output_path, manifest_file)
|
||||
if os.path.exists(candidate):
|
||||
toml_file = candidate
|
||||
break
|
||||
disabled_tests = manifest.get("disable", [])
|
||||
with open(toml_file, "r") as f:
|
||||
data = f.read()
|
||||
@@ -90,8 +103,17 @@ def write_moz_build_file(manifest):
|
||||
print(f"Writing moz.build file to '{moz_build_path}'")
|
||||
with open(moz_build_path, "w") as f:
|
||||
f.write(FILE_PREFIX)
|
||||
f.write(BROWSER_MANIFEST_LIST_PREFIX)
|
||||
for test_suite in manifest.keys():
|
||||
f.write(f'\t"{test_suite}/browser.toml",\n')
|
||||
# add for browser.toml first
|
||||
if not manifest[test_suite].get("xpcshell", False):
|
||||
f.write(f'\t"{test_suite}/browser.toml",\n')
|
||||
f.write(FILE_SUFFIX)
|
||||
f.write(XPCSHELL_MANIFESTS_LIST_PREFIX)
|
||||
for test_suite in manifest.keys():
|
||||
# add for xpcshell.toml
|
||||
if manifest[test_suite].get("xpcshell", False):
|
||||
f.write(f'\t"{test_suite}/xpcshell.toml",\n')
|
||||
f.write(FILE_SUFFIX)
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ npm run import
|
||||
IGNORE_FILES=(
|
||||
"shared.nsh"
|
||||
"ignorePrefs.json"
|
||||
"moz.configure"
|
||||
)
|
||||
|
||||
# Recursively find all .patch files in the current directory and its subdirectories
|
||||
|
||||
@@ -56,7 +56,7 @@ def main():
|
||||
os.chdir(engine_dir)
|
||||
|
||||
def run_mach_with_paths(test_paths):
|
||||
command = ['./mach', 'mochitest'] + other_args + test_paths
|
||||
command = ['./mach', 'test'] + other_args + test_paths
|
||||
# Replace the current process with the mach command
|
||||
os.execvp(command[0], command)
|
||||
|
||||
|
||||
99
scripts/update_external_patches.py
Normal file
99
scripts/update_external_patches.py
Normal file
@@ -0,0 +1,99 @@
|
||||
# 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/.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import requests
|
||||
from json_with_comments import JSONWithCommentsDecoder
|
||||
|
||||
BASE_URI = "https://phabricator.services.mozilla.com"
|
||||
OUTPUT_DIR = os.path.join(os.getcwd(), "src", "external-patches")
|
||||
|
||||
|
||||
def die(message):
|
||||
print(f"Error: {message}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def download_phab_patch(phab_id, output_file):
|
||||
"""Download a Phabricator patch by its ID and save it to output_file."""
|
||||
patch_url = f"{BASE_URI}/{phab_id}?download=true"
|
||||
try:
|
||||
print(f"Downloading patch from {patch_url}")
|
||||
response = requests.get(patch_url)
|
||||
response.raise_for_status() # Raise an error for bad responses
|
||||
with open(output_file, 'wb') as f:
|
||||
f.write(response.content)
|
||||
print(f"Patch saved to {output_file}")
|
||||
except requests.RequestException as e:
|
||||
die(f"Failed to download patch {phab_id}: {e}")
|
||||
|
||||
|
||||
def download_patch_from_url(url, output_file):
|
||||
"""Download a patch from a given URL and save it to output_file."""
|
||||
try:
|
||||
print(f"Downloading patch from {url}")
|
||||
response = requests.get(url)
|
||||
response.raise_for_status() # Raise an error for bad responses
|
||||
with open(output_file, 'wb') as f:
|
||||
f.write(response.content)
|
||||
print(f"Patch saved to {output_file}")
|
||||
except requests.RequestException as e:
|
||||
die(f"Failed to download patch from {url}: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
with open(os.path.join(OUTPUT_DIR, "manifest.json"), 'r') as f:
|
||||
manifest = json.load(f, cls=JSONWithCommentsDecoder)
|
||||
|
||||
expected_files = set()
|
||||
for patch in manifest:
|
||||
if patch.get("type") == "phabricator":
|
||||
phab_id = patch.get("id")
|
||||
name = patch.get("name")
|
||||
if not phab_id or not name:
|
||||
die(f"Patch entry missing 'id' or 'name': {patch}")
|
||||
name = name.replace(" ", "_").lower()
|
||||
output_file = os.path.join(OUTPUT_DIR, "firefox", f"{name}.patch")
|
||||
print(f"Processing Phabricator patch: {phab_id} -> {output_file}")
|
||||
download_phab_patch(phab_id, output_file)
|
||||
expected_files.add(output_file)
|
||||
elif patch.get("type") == "local":
|
||||
print(f"Local patch: {patch.get('path')}")
|
||||
expected_files.add(os.path.join(OUTPUT_DIR, patch.get("path")))
|
||||
elif patch.get("type") == "patch":
|
||||
url = patch.get("url")
|
||||
dest = patch.get("dest")
|
||||
if not url or not dest:
|
||||
die(f"Patch entry missing 'url' or 'dest': {patch}")
|
||||
filename = url.split("/")[-1]
|
||||
output_file = os.path.join(OUTPUT_DIR, dest, filename)
|
||||
download_patch_from_url(url, output_file)
|
||||
replaces = patch.get("replaces", {})
|
||||
for replace in replaces.keys():
|
||||
value = replaces[replace]
|
||||
with open(output_file, 'r') as f:
|
||||
content = f.read()
|
||||
if replace not in content:
|
||||
die(f"Replace string '{replace}' not found in {output_file}")
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(content.replace(replace, value))
|
||||
expected_files.add(output_file)
|
||||
else:
|
||||
die(f"Unknown patch type: {patch.get('type')}")
|
||||
|
||||
# Check for unexpected files in the output directory
|
||||
# and remove them if they are not in the expected_files set.
|
||||
for root, dirs, files in os.walk(OUTPUT_DIR):
|
||||
for file in files:
|
||||
if file.endswith(".patch"):
|
||||
file_path = os.path.join(root, file)
|
||||
if file_path not in expected_files:
|
||||
print(f"Removing unexpected patch file: {file_path}")
|
||||
os.remove(file_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/browser-sets.js b/browser/base/content/browser-sets.js
|
||||
index 94a05a510204c027e482fda33deaf3dc0d7471c9..79d37a7c7f29739d3ad2d9f6a3b3b8d638836b10 100644
|
||||
index 94a05a510204c027e482fda33deaf3dc0d7471c9..da7cda8e2708a29cf6cc3bf667240d37ef66ecfb 100644
|
||||
--- a/browser/base/content/browser-sets.js
|
||||
+++ b/browser/base/content/browser-sets.js
|
||||
@@ -266,7 +266,7 @@ document.addEventListener(
|
||||
@@ -11,3 +11,12 @@ index 94a05a510204c027e482fda33deaf3dc0d7471c9..79d37a7c7f29739d3ad2d9f6a3b3b8d6
|
||||
const SIDEBAR_REVAMP_PREF = "sidebar.revamp";
|
||||
const SIDEBAR_REVAMP_ENABLED = Services.prefs.getBoolPref(
|
||||
SIDEBAR_REVAMP_PREF,
|
||||
@@ -304,6 +304,8 @@ document.addEventListener(
|
||||
panel: SidebarController.currentID,
|
||||
opened: SidebarController._state.launcherExpanded,
|
||||
});
|
||||
+ } else {
|
||||
+ SidebarController.toggle();
|
||||
}
|
||||
break;
|
||||
case "key_gotoHistory":
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
|
||||
index 68c24f730d56f548cf1e286198a04f8363529378..71418c93ce7216d71412f2fa67295322bb73abad 100644
|
||||
index 68c24f730d56f548cf1e286198a04f8363529378..eb9aa5e77cf549062d8d3770f8057ceafe67c317 100644
|
||||
--- a/browser/base/content/navigator-toolbox.inc.xhtml
|
||||
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
|
||||
@@ -2,7 +2,7 @@
|
||||
@@ -22,16 +22,17 @@ index 68c24f730d56f548cf1e286198a04f8363529378..71418c93ce7216d71412f2fa67295322
|
||||
<toolbar id="TabsToolbar"
|
||||
class="browser-toolbar browser-titlebar"
|
||||
fullscreentoolbar="true"
|
||||
@@ -62,6 +61,8 @@
|
||||
@@ -62,6 +61,9 @@
|
||||
<html:sidebar-pins-promo id="drag-to-pin-promo-card"></html:sidebar-pins-promo>
|
||||
<arrowscrollbox id="pinned-tabs-container" orient="horizontal" clicktoscroll=""></arrowscrollbox>
|
||||
<splitter orient="vertical" id="vertical-pinned-tabs-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
|
||||
+<html:div id="zen-overflow-extensions-list" skipintoolbarset="true"></html:div>
|
||||
+<html:div id="zen-essentials" skipintoolbarset="true"></html:div>
|
||||
+<html:div id="zen-tabs-wrapper">
|
||||
<hbox class="tab-drop-indicator" hidden="true"/>
|
||||
<arrowscrollbox id="tabbrowser-arrowscrollbox" orient="horizontal" flex="1" clicktoscroll="" scrolledtostart="" scrolledtoend="">
|
||||
<tab is="tabbrowser-tab" class="tabbrowser-tab" selected="true" visuallyselected="" fadein=""/>
|
||||
@@ -81,6 +82,7 @@
|
||||
@@ -81,6 +83,7 @@
|
||||
tooltip="dynamic-shortcut-tooltip"
|
||||
data-l10n-id="tabs-toolbar-new-tab"/>
|
||||
<html:span id="tabbrowser-tab-a11y-desc" hidden="true"/>
|
||||
@@ -39,7 +40,7 @@ index 68c24f730d56f548cf1e286198a04f8363529378..71418c93ce7216d71412f2fa67295322
|
||||
</tabs>
|
||||
|
||||
<toolbarbutton id="new-tab-button"
|
||||
@@ -106,9 +108,10 @@
|
||||
@@ -106,9 +109,10 @@
|
||||
#include private-browsing-indicator.inc.xhtml
|
||||
<toolbarbutton class="content-analysis-indicator toolbarbutton-1 content-analysis-indicator-icon"/>
|
||||
|
||||
|
||||
41
src/browser/components/BrowserContentHandler-sys-mjs.patch
Normal file
41
src/browser/components/BrowserContentHandler-sys-mjs.patch
Normal file
@@ -0,0 +1,41 @@
|
||||
diff --git a/browser/components/BrowserContentHandler.sys.mjs b/browser/components/BrowserContentHandler.sys.mjs
|
||||
index 57a37b5c0d1982ecfff8d94e05fea89895713a58..062dcb0d1de00982bd777c3dc5d62a88a304bc68 100644
|
||||
--- a/browser/components/BrowserContentHandler.sys.mjs
|
||||
+++ b/browser/components/BrowserContentHandler.sys.mjs
|
||||
@@ -601,6 +601,28 @@ nsBrowserContentHandler.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
+ try {
|
||||
+ let blankWindowParam = cmdLine.handleFlagWithParam("blank-window", false);
|
||||
+ if (blankWindowParam !== null) {
|
||||
+ let { uri, principal } = resolveURIInternal(cmdLine, blankWindowParam);
|
||||
+ if (shouldLoadURI(uri)) {
|
||||
+ let win = openBrowserWindow(cmdLine, principal, uri.spec);
|
||||
+ win._zenStartupSyncFlag = 'unsynced';
|
||||
+ cmdLine.preventDefault = true;
|
||||
+ }
|
||||
+ }
|
||||
+ } catch (e) {
|
||||
+ if (e.result != Cr.NS_ERROR_INVALID_ARG) {
|
||||
+ throw e;
|
||||
+ }
|
||||
+ if (cmdLine.handleFlag("blank-window", false)) {
|
||||
+ let win = openBrowserWindow(cmdLine, null, null);
|
||||
+ win._zenStartupSyncFlag = 'unsynced';
|
||||
+ cmdLine.preventDefault = true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
var searchParam = cmdLine.handleFlagWithParam("search", false);
|
||||
if (searchParam) {
|
||||
doSearch(searchParam, cmdLine);
|
||||
@@ -670,6 +692,7 @@ nsBrowserContentHandler.prototype = {
|
||||
" --new-window <url> Open <url> in a new window.\n" +
|
||||
" --new-tab <url> Open <url> in a new tab.\n" +
|
||||
" --private-window [<url>] Open <url> in a new private window.\n";
|
||||
+ info += " --blank-window [<url>] Open the new blank window.\n";
|
||||
if (AppConstants.platform == "win") {
|
||||
info += " --preferences Open Options dialog.\n";
|
||||
} else {
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/customizableui/CustomizableUI.sys.mjs b/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b46db12e2 100644
|
||||
index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..c50781a1e8fd1a71baf497ba64d85292fa1347f4 100644
|
||||
--- a/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
+++ b/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
@@ -14,6 +14,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
@@ -153,7 +153,17 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
|
||||
/**
|
||||
* Add a widget to an area.
|
||||
* If the area to which you try to add is not known to CustomizableUI,
|
||||
@@ -7858,7 +7858,7 @@ class OverflowableToolbar {
|
||||
@@ -7798,7 +7798,9 @@ class OverflowableToolbar {
|
||||
);
|
||||
|
||||
if (webExtList && CustomizableUI.isWebExtensionWidget(child.id)) {
|
||||
+ if (webExtList.id !== "zen-overflow-extensions-list") {
|
||||
child.setAttribute("cui-anchorid", webExtButtonID);
|
||||
+ }
|
||||
webExtList.insertBefore(child, webExtList.firstElementChild);
|
||||
} else {
|
||||
child.setAttribute("cui-anchorid", this.#defaultListButton.id);
|
||||
@@ -7858,7 +7860,7 @@ class OverflowableToolbar {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -162,7 +172,7 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
|
||||
if (child != aExceptChild) {
|
||||
sum += getInlineSize(child);
|
||||
}
|
||||
@@ -7882,11 +7882,11 @@ class OverflowableToolbar {
|
||||
@@ -7882,11 +7884,11 @@ class OverflowableToolbar {
|
||||
parseFloat(style.paddingLeft) -
|
||||
parseFloat(style.paddingRight) -
|
||||
toolbarChildrenWidth;
|
||||
@@ -176,7 +186,7 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
|
||||
});
|
||||
|
||||
lazy.log.debug(
|
||||
@@ -7901,7 +7901,14 @@ class OverflowableToolbar {
|
||||
@@ -7901,7 +7903,14 @@ class OverflowableToolbar {
|
||||
Math.max(targetWidth, targetChildrenWidth)
|
||||
);
|
||||
totalAvailWidth = Math.ceil(totalAvailWidth);
|
||||
@@ -192,7 +202,7 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
|
||||
return { isOverflowing, targetContentWidth, totalAvailWidth };
|
||||
}
|
||||
|
||||
@@ -7962,7 +7969,11 @@ class OverflowableToolbar {
|
||||
@@ -7962,7 +7971,11 @@ class OverflowableToolbar {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -205,7 +215,7 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
|
||||
lazy.log.debug(
|
||||
`Need ${minSize} but width is ${totalAvailWidth} so bailing`
|
||||
);
|
||||
@@ -7995,7 +8006,7 @@ class OverflowableToolbar {
|
||||
@@ -7995,7 +8008,7 @@ class OverflowableToolbar {
|
||||
}
|
||||
}
|
||||
if (!inserted) {
|
||||
@@ -214,7 +224,27 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
|
||||
}
|
||||
child.removeAttribute("cui-anchorid");
|
||||
child.removeAttribute("overflowedItem");
|
||||
@@ -8340,7 +8351,7 @@ class OverflowableToolbar {
|
||||
@@ -8121,6 +8134,9 @@ class OverflowableToolbar {
|
||||
* if no such list exists.
|
||||
*/
|
||||
get #webExtList() {
|
||||
+ if (this.#toolbar.getAttribute("addon-webext-overflowtarget") !== this.#webExtListRef?.id) {
|
||||
+ this.#webExtListRef = null;
|
||||
+ }
|
||||
if (!this.#webExtListRef) {
|
||||
let targetID = this.#toolbar.getAttribute("addon-webext-overflowtarget");
|
||||
if (!targetID) {
|
||||
@@ -8132,6 +8148,9 @@ class OverflowableToolbar {
|
||||
let win = this.#toolbar.ownerGlobal;
|
||||
let { panel } = win.gUnifiedExtensions;
|
||||
this.#webExtListRef = panel.querySelector(`#${targetID}`);
|
||||
+ if (!this.#webExtListRef) {
|
||||
+ this.#webExtListRef = win.document.getElementById(targetID);
|
||||
+ }
|
||||
}
|
||||
return this.#webExtListRef;
|
||||
}
|
||||
@@ -8340,7 +8359,7 @@ class OverflowableToolbar {
|
||||
break;
|
||||
}
|
||||
case "mousedown": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/sessionstore/SessionStartup.sys.mjs b/browser/components/sessionstore/SessionStartup.sys.mjs
|
||||
index 86600ffb5178599ab23270a964064ca657a3283f..e7014e5bce547d37ec92377a95bad5be4d52152e 100644
|
||||
index 86600ffb5178599ab23270a964064ca657a3283f..70d7b5c7d69d3700475d663b595052264c67d351 100644
|
||||
--- a/browser/components/sessionstore/SessionStartup.sys.mjs
|
||||
+++ b/browser/components/sessionstore/SessionStartup.sys.mjs
|
||||
@@ -40,6 +40,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
@@ -28,7 +28,16 @@ index 86600ffb5178599ab23270a964064ca657a3283f..e7014e5bce547d37ec92377a95bad5be
|
||||
if (this._initialState == null) {
|
||||
// No valid session found.
|
||||
this._sessionType = this.NO_SESSION;
|
||||
@@ -336,12 +339,7 @@ export var SessionStartup = {
|
||||
@@ -276,6 +279,8 @@ export var SessionStartup = {
|
||||
`Previous shutdown ok? ${this._previousSessionCrashed}, reason: ${previousSessionCrashedReason}`
|
||||
);
|
||||
|
||||
+ lazy.ZenSessionStore.onCrashCheckpoints(this._initialState);
|
||||
+
|
||||
Services.obs.addObserver(this, "sessionstore-windows-restored", true);
|
||||
|
||||
if (this.sessionType == this.NO_SESSION) {
|
||||
@@ -336,12 +341,7 @@ export var SessionStartup = {
|
||||
isAutomaticRestoreEnabled() {
|
||||
if (this._resumeSessionEnabled === null) {
|
||||
this._resumeSessionEnabled =
|
||||
@@ -42,3 +51,13 @@ index 86600ffb5178599ab23270a964064ca657a3283f..e7014e5bce547d37ec92377a95bad5be
|
||||
}
|
||||
|
||||
return this._resumeSessionEnabled;
|
||||
@@ -354,8 +354,7 @@ export var SessionStartup = {
|
||||
*/
|
||||
willRestore() {
|
||||
return (
|
||||
- this.sessionType == this.RECOVER_SESSION ||
|
||||
- this.sessionType == this.RESUME_SESSION
|
||||
+ true
|
||||
);
|
||||
},
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96faedf6f0b 100644
|
||||
index 2a055f0c5f34f0a2667f659185120c07d38f4e41..53465df6060e6765e1e4564d340bba5fbefff5ce 100644
|
||||
--- a/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
@@ -127,6 +127,9 @@ const TAB_EVENTS = [
|
||||
@@ -63,6 +63,15 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
|
||||
// perform additional initialization when the first window is loading
|
||||
if (lazy.RunState.isStopped) {
|
||||
@@ -2088,7 +2097,7 @@ var SessionStoreInternal = {
|
||||
// to disk to NOW() to enforce a full interval before the next write.
|
||||
lazy.SessionSaver.updateLastSaveTime();
|
||||
|
||||
- if (isPrivateWindow || isTaskbarTab) {
|
||||
+ if (isPrivateWindow || isTaskbarTab || aWindow.document.documentElement.hasAttribute("zen-unsynced-window")) {
|
||||
this._log.debug(
|
||||
"initializeWindow, the window is private or a web app. Saving SessionStartup.state for possibly restoring later"
|
||||
);
|
||||
@@ -2131,6 +2140,7 @@ var SessionStoreInternal = {
|
||||
null,
|
||||
"sessionstore-one-or-no-tab-restored"
|
||||
@@ -79,7 +88,15 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
!lazy.SessionStartup.willRestore()
|
||||
) {
|
||||
// We want to split the window up into pinned tabs and unpinned tabs.
|
||||
@@ -2239,6 +2248,15 @@ var SessionStoreInternal = {
|
||||
@@ -2211,6 +2220,7 @@ var SessionStoreInternal = {
|
||||
}
|
||||
|
||||
if (newWindowState) {
|
||||
+ lazy.ZenSessionStore.onRestoringClosedWindow(newWindowState);
|
||||
// Ensure that the window state isn't hidden
|
||||
this._restoreCount = 1;
|
||||
let state = { windows: [newWindowState] };
|
||||
@@ -2239,6 +2249,15 @@ var SessionStoreInternal = {
|
||||
});
|
||||
this._shouldRestoreLastSession = false;
|
||||
}
|
||||
@@ -95,7 +112,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
|
||||
if (this._restoreLastWindow && aWindow.toolbar.visible) {
|
||||
// always reset (if not a popup window)
|
||||
@@ -2383,7 +2401,7 @@ var SessionStoreInternal = {
|
||||
@@ -2383,7 +2402,7 @@ var SessionStoreInternal = {
|
||||
|
||||
var tabbrowser = aWindow.gBrowser;
|
||||
|
||||
@@ -104,7 +121,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
|
||||
TAB_EVENTS.forEach(function (aEvent) {
|
||||
tabbrowser.tabContainer.removeEventListener(aEvent, this, true);
|
||||
@@ -2434,7 +2452,7 @@ var SessionStoreInternal = {
|
||||
@@ -2434,7 +2453,7 @@ var SessionStoreInternal = {
|
||||
|
||||
let isLastRegularWindow =
|
||||
Object.values(this._windows).filter(
|
||||
@@ -113,7 +130,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
).length == 1;
|
||||
this._log.debug(
|
||||
`onClose, closing window isLastRegularWindow? ${isLastRegularWindow}`
|
||||
@@ -2491,8 +2509,8 @@ var SessionStoreInternal = {
|
||||
@@ -2491,8 +2510,8 @@ var SessionStoreInternal = {
|
||||
// 2) Flush the window.
|
||||
// 3) When the flush is complete, revisit our decision to store the window
|
||||
// in _closedWindows, and add/remove as necessary.
|
||||
@@ -124,7 +141,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
}
|
||||
|
||||
completionPromise = lazy.TabStateFlusher.flushWindow(aWindow).then(() => {
|
||||
@@ -2512,8 +2530,9 @@ var SessionStoreInternal = {
|
||||
@@ -2512,8 +2531,9 @@ var SessionStoreInternal = {
|
||||
|
||||
// Save non-private windows if they have at
|
||||
// least one saveable tab or are the last window.
|
||||
@@ -136,7 +153,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
|
||||
if (!isLastWindow && winData.closedId > -1) {
|
||||
this._addClosedAction(
|
||||
@@ -2589,7 +2608,7 @@ var SessionStoreInternal = {
|
||||
@@ -2589,7 +2609,7 @@ var SessionStoreInternal = {
|
||||
* to call this method again asynchronously (for example, after
|
||||
* a window flush).
|
||||
*/
|
||||
@@ -145,7 +162,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
// Make sure SessionStore is still running, and make sure that we
|
||||
// haven't chosen to forget this window.
|
||||
if (
|
||||
@@ -2606,6 +2625,7 @@ var SessionStoreInternal = {
|
||||
@@ -2606,6 +2626,7 @@ var SessionStoreInternal = {
|
||||
// _closedWindows from a previous call to this function.
|
||||
let winIndex = this._closedWindows.indexOf(winData);
|
||||
let alreadyStored = winIndex != -1;
|
||||
@@ -153,7 +170,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
// If sidebar command is truthy, i.e. sidebar is open, store sidebar settings
|
||||
let shouldStore = hasSaveableTabs || isLastWindow;
|
||||
|
||||
@@ -3408,7 +3428,7 @@ var SessionStoreInternal = {
|
||||
@@ -3408,7 +3429,7 @@ var SessionStoreInternal = {
|
||||
if (!isPrivateWindow && tabState.isPrivate) {
|
||||
return;
|
||||
}
|
||||
@@ -162,7 +179,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4129,6 +4149,12 @@ var SessionStoreInternal = {
|
||||
@@ -4129,6 +4150,12 @@ var SessionStoreInternal = {
|
||||
Math.min(tabState.index, tabState.entries.length)
|
||||
);
|
||||
tabState.pinned = false;
|
||||
@@ -175,7 +192,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
|
||||
if (inBackground === false) {
|
||||
aWindow.gBrowser.selectedTab = newTab;
|
||||
@@ -4565,6 +4591,8 @@ var SessionStoreInternal = {
|
||||
@@ -4565,6 +4592,8 @@ var SessionStoreInternal = {
|
||||
// Append the tab if we're opening into a different window,
|
||||
tabIndex: aSource == aTargetWindow ? pos : Infinity,
|
||||
pinned: state.pinned,
|
||||
@@ -184,7 +201,19 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
userContextId: state.userContextId,
|
||||
skipLoad: true,
|
||||
preferredRemoteType,
|
||||
@@ -5414,7 +5442,7 @@ var SessionStoreInternal = {
|
||||
@@ -5055,9 +5084,10 @@ var SessionStoreInternal = {
|
||||
if (activePageData.title && activePageData.title != activePageData.url) {
|
||||
win.gBrowser.setInitialTabTitle(tab, activePageData.title, {
|
||||
isContentTitle: true,
|
||||
+ _zenChangeLabelFlag: true,
|
||||
});
|
||||
} else {
|
||||
- win.gBrowser.setInitialTabTitle(tab, activePageData.url);
|
||||
+ win.gBrowser.setInitialTabTitle(tab, activePageData.url, { _zenChangeLabelFlag: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5414,7 +5444,7 @@ var SessionStoreInternal = {
|
||||
|
||||
for (let i = tabbrowser.pinnedTabCount; i < tabbrowser.tabs.length; i++) {
|
||||
let tab = tabbrowser.tabs[i];
|
||||
@@ -193,7 +222,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
removableTabs.push(tab);
|
||||
}
|
||||
}
|
||||
@@ -5525,7 +5553,7 @@ var SessionStoreInternal = {
|
||||
@@ -5525,7 +5555,7 @@ var SessionStoreInternal = {
|
||||
|
||||
// collect the data for all windows
|
||||
for (ix in this._windows) {
|
||||
@@ -202,7 +231,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
// window data is still in _statesToRestore
|
||||
continue;
|
||||
}
|
||||
@@ -5668,11 +5696,12 @@ var SessionStoreInternal = {
|
||||
@@ -5668,11 +5698,12 @@ var SessionStoreInternal = {
|
||||
}
|
||||
|
||||
let tabbrowser = aWindow.gBrowser;
|
||||
@@ -216,7 +245,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
// update the internal state data for this window
|
||||
for (let tab of tabs) {
|
||||
if (tab == aWindow.FirefoxViewHandler.tab) {
|
||||
@@ -5683,6 +5712,9 @@ var SessionStoreInternal = {
|
||||
@@ -5683,6 +5714,9 @@ var SessionStoreInternal = {
|
||||
tabsData.push(tabData);
|
||||
}
|
||||
|
||||
@@ -226,7 +255,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
// update tab group state for this window
|
||||
winData.groups = [];
|
||||
for (let tabGroup of aWindow.gBrowser.tabGroups) {
|
||||
@@ -5695,7 +5727,7 @@ var SessionStoreInternal = {
|
||||
@@ -5695,7 +5729,7 @@ var SessionStoreInternal = {
|
||||
// a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab,
|
||||
// since it's only inserted into the tab strip after it's selected).
|
||||
if (aWindow.FirefoxViewHandler.tab?.selected) {
|
||||
@@ -235,7 +264,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
winData.title = tabbrowser.tabs[0].label;
|
||||
}
|
||||
winData.selected = selectedIndex;
|
||||
@@ -5810,8 +5842,8 @@ var SessionStoreInternal = {
|
||||
@@ -5810,8 +5844,8 @@ var SessionStoreInternal = {
|
||||
// selectTab represents.
|
||||
let selectTab = 0;
|
||||
if (overwriteTabs) {
|
||||
@@ -246,7 +275,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
selectTab = Math.min(selectTab, winData.tabs.length);
|
||||
}
|
||||
|
||||
@@ -5833,6 +5865,7 @@ var SessionStoreInternal = {
|
||||
@@ -5833,6 +5867,7 @@ var SessionStoreInternal = {
|
||||
if (overwriteTabs) {
|
||||
for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) {
|
||||
if (!tabbrowser.tabs[i].selected) {
|
||||
@@ -254,7 +283,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
tabbrowser.removeTab(tabbrowser.tabs[i]);
|
||||
}
|
||||
}
|
||||
@@ -5866,6 +5899,12 @@ var SessionStoreInternal = {
|
||||
@@ -5866,6 +5901,12 @@ var SessionStoreInternal = {
|
||||
savedTabGroup => !openTabGroupIdsInWindow.has(savedTabGroup.id)
|
||||
);
|
||||
}
|
||||
@@ -267,7 +296,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
|
||||
// Move the originally open tabs to the end.
|
||||
if (initialTabs) {
|
||||
@@ -6419,6 +6458,25 @@ var SessionStoreInternal = {
|
||||
@@ -6419,6 +6460,25 @@ var SessionStoreInternal = {
|
||||
|
||||
// Most of tabData has been restored, now continue with restoring
|
||||
// attributes that may trigger external events.
|
||||
@@ -293,7 +322,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
|
||||
if (tabData.pinned) {
|
||||
tabbrowser.pinTab(tab);
|
||||
@@ -6567,6 +6625,9 @@ var SessionStoreInternal = {
|
||||
@@ -6567,6 +6627,9 @@ var SessionStoreInternal = {
|
||||
aWindow.gURLBar.readOnly = false;
|
||||
}
|
||||
}
|
||||
@@ -303,7 +332,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
|
||||
let promiseParts = Promise.withResolvers();
|
||||
aWindow.setTimeout(() => {
|
||||
@@ -7343,7 +7404,7 @@ var SessionStoreInternal = {
|
||||
@@ -7343,7 +7406,7 @@ var SessionStoreInternal = {
|
||||
|
||||
let groupsToSave = new Map();
|
||||
for (let tIndex = 0; tIndex < window.tabs.length; ) {
|
||||
@@ -312,7 +341,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
|
||||
// Adjust window.selected
|
||||
if (tIndex + 1 < window.selected) {
|
||||
window.selected -= 1;
|
||||
@@ -7358,7 +7419,7 @@ var SessionStoreInternal = {
|
||||
@@ -7358,7 +7421,7 @@ var SessionStoreInternal = {
|
||||
);
|
||||
// We don't want to increment tIndex here.
|
||||
continue;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..d21c461c4e05fa5b28bc0806b07f9256ca3c9175 100644
|
||||
index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..db1c57c8eb4b3d614b5e5aa820871d66b00ec9a4 100644
|
||||
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
@@ -398,6 +398,7 @@
|
||||
@@ -186,11 +186,13 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..d21c461c4e05fa5b28bc0806b07f9256
|
||||
// If focus is on the old tab, move it to the new tab.
|
||||
if (activeEl == oldTab) {
|
||||
newTab.focus();
|
||||
@@ -1959,6 +2039,11 @@
|
||||
@@ -1958,7 +2038,12 @@
|
||||
return this._setTabLabel(aTab, aLabel);
|
||||
}
|
||||
|
||||
_setTabLabel(aTab, aLabel, { beforeTabOpen, isContentTitle, isURL } = {}) {
|
||||
+ if (!aTab._zenContentsVisible && !aTab._zenChangeLabelFlag && !aTab._labelIsInitialTitle && !gZenWorkspaces.privateWindowOrDisabled) {
|
||||
- _setTabLabel(aTab, aLabel, { beforeTabOpen, isContentTitle, isURL } = {}) {
|
||||
+ _setTabLabel(aTab, aLabel, { beforeTabOpen, isContentTitle, isURL, _zenChangeLabelFlag } = {}) {
|
||||
+ if (!aTab._zenContentsVisible && !aTab._zenChangeLabelFlag && !aTab._labelIsInitialTitle && !gZenWorkspaces.privateWindowOrDisabled && !_zenChangeLabelFlag) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ aLabel = (typeof aTab.zenStaticLabel === "string" && aTab.zenStaticLabel) ? aTab.zenStaticLabel : aLabel;
|
||||
@@ -777,7 +779,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..d21c461c4e05fa5b28bc0806b07f9256
|
||||
options,
|
||||
aTab
|
||||
);
|
||||
+ win._zenStartupSyncFlag = zenForceSync ? 'synced' : 'unsynced';
|
||||
+ win._zenStartupSyncFlag = (zenForceSync || !Services.prefs.getBoolPref("zen.tabs.dnd-open-blank-window", true)) ? 'synced' : 'unsynced';
|
||||
+ return win;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
diff --git a/browser/components/urlbar/UrlbarMuxerStandard.sys.mjs b/browser/components/urlbar/UrlbarMuxerStandard.sys.mjs
|
||||
index c12d875172650dddfe7de623a776149517c83302..66e923ed2c84e350dd7ba7a20e362666cec8f307 100644
|
||||
index c12d875172650dddfe7de623a776149517c83302..de24df54f510c44acda8c64584bf483a2cb5650a 100644
|
||||
--- a/browser/components/urlbar/UrlbarMuxerStandard.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarMuxerStandard.sys.mjs
|
||||
@@ -854,6 +854,7 @@ class MuxerUnifiedComplete extends UrlbarMuxer {
|
||||
@@ -849,11 +849,16 @@ class MuxerUnifiedComplete extends UrlbarMuxer {
|
||||
result.providerName == "UrlbarProviderHeuristicFallback" &&
|
||||
state.context.heuristicResult?.providerName !=
|
||||
"UrlbarProviderHeuristicFallback"
|
||||
+ && !(
|
||||
+ result.type == UrlbarUtils.RESULT_TYPE.SEARCH &&
|
||||
+ state.context.heuristicResult?.type == UrlbarUtils.RESULT_TYPE.URL
|
||||
+ )
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.providerName == "UrlbarProviderTabToSearch") {
|
||||
@@ -10,7 +19,7 @@ index c12d875172650dddfe7de623a776149517c83302..66e923ed2c84e350dd7ba7a20e362666
|
||||
// Discard the result if a tab-to-search result was added already.
|
||||
if (!state.canAddTabToSearch) {
|
||||
return false;
|
||||
@@ -1500,7 +1501,9 @@ class MuxerUnifiedComplete extends UrlbarMuxer {
|
||||
@@ -1500,7 +1505,9 @@ class MuxerUnifiedComplete extends UrlbarMuxer {
|
||||
usedLimits.maxResultCount++;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
diff --git a/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs b/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs
|
||||
index 7a60a871022df44c0d26745f0035fce988cc5f92..0b89f9b66573d4c5ffb67983d728bb593e5cb524 100644
|
||||
--- a/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs
|
||||
@@ -75,22 +75,26 @@ export class UrlbarProviderHeuristicFallback extends UrlbarProvider {
|
||||
// Since we can't tell if this is a real URL and whether the user wants
|
||||
// to visit or search for it, we provide an alternative searchengine
|
||||
// match if the string looks like an alphanumeric origin or an e-mail.
|
||||
- let str = queryContext.searchString;
|
||||
- if (!URL.canParse(str)) {
|
||||
- if (
|
||||
- lazy.UrlbarPrefs.get("keyword.enabled") &&
|
||||
- (lazy.UrlUtils.looksLikeOrigin(str, {
|
||||
- noIp: true,
|
||||
- noPort: true,
|
||||
- }) ||
|
||||
- lazy.UrlUtils.REGEXP_COMMON_EMAIL.test(str))
|
||||
- ) {
|
||||
+ let trimmedSearchString = queryContext.trimmedSearchString;
|
||||
+ let [searchCandidate] = UrlbarUtils.stripPrefixAndTrim(
|
||||
+ trimmedSearchString,
|
||||
+ {
|
||||
+ trimSlash: true,
|
||||
+ }
|
||||
+ );
|
||||
+ if (
|
||||
+ lazy.UrlbarPrefs.get("keyword.enabled") &&
|
||||
+ (lazy.UrlUtils.looksLikeUrl(searchCandidate) ||
|
||||
+ lazy.UrlUtils.looksLikeOrigin(searchCandidate, {
|
||||
+ allowPartialNumericalTLDs: true,
|
||||
+ }) !== lazy.UrlUtils.LOOKS_LIKE_ORIGIN.NONE ||
|
||||
+ lazy.UrlUtils.REGEXP_COMMON_EMAIL.test(trimmedSearchString))
|
||||
+ ) {
|
||||
let searchResult = await this._engineSearchResult({ queryContext });
|
||||
if (instance != this.queryInstance) {
|
||||
return;
|
||||
}
|
||||
addCallback(this, searchResult);
|
||||
- }
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/urlbar/UrlbarUtils.sys.mjs b/browser/components/urlbar/UrlbarUtils.sys.mjs
|
||||
index b97fa81cef276e4bb22414b7c182e00b4bb835dc..1f489eceead0aa331ace59281cb777db5e635366 100644
|
||||
index a4ed35032c0c60f230d95f451b3f615232784cad..2c92860acb2ddc9829eef006d93431e871241c3a 100644
|
||||
--- a/browser/components/urlbar/UrlbarUtils.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarUtils.sys.mjs
|
||||
@@ -81,6 +81,7 @@ export var UrlbarUtils = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/urlbar/content/UrlbarInput.mjs b/browser/components/urlbar/content/UrlbarInput.mjs
|
||||
index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e56a72eb3 100644
|
||||
index edb0482f0bfb22c70a585b0770e5b0437983779e..109e9eb18b90c650caff1287caef04dce857e122 100644
|
||||
--- a/browser/components/urlbar/content/UrlbarInput.mjs
|
||||
+++ b/browser/components/urlbar/content/UrlbarInput.mjs
|
||||
@@ -68,6 +68,13 @@ const lazy = XPCOMUtils.declareLazy({
|
||||
@@ -122,14 +122,17 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
// Enable the animation only after the first extend call to ensure it
|
||||
// doesn't run when opening a new window.
|
||||
if (!this.hasAttribute("breakout-extend-animate")) {
|
||||
@@ -2562,6 +2627,24 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -2562,6 +2627,27 @@ export class UrlbarInput extends HTMLElement {
|
||||
return;
|
||||
}
|
||||
|
||||
+ if (this._zenHandleUrlbarClose) {
|
||||
+ this._zenHandleUrlbarClose();
|
||||
+ } else if (!this._untrimmedValue) {
|
||||
+ // Restore the current page URL when the urlbar is empty on blur
|
||||
+ this.handleRevert();
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ // Arc like URLbar: Blur the input on exit
|
||||
+ const zenToolbox = this.document.getElementById("navigator-toolbox");
|
||||
+ this.window.document.documentElement.setAttribute("supress-primary-adjustment", !(
|
||||
@@ -147,7 +150,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
this.removeAttribute("breakout-extend");
|
||||
this.#updateTextboxPosition();
|
||||
}
|
||||
@@ -2592,7 +2675,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -2592,7 +2678,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
forceUnifiedSearchButtonAvailable = false
|
||||
) {
|
||||
let prevState = this.getAttribute("pageproxystate");
|
||||
@@ -156,7 +159,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
this.setAttribute("pageproxystate", state);
|
||||
this._inputContainer.setAttribute("pageproxystate", state);
|
||||
this._identityBox?.setAttribute("pageproxystate", state);
|
||||
@@ -2846,10 +2929,12 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -2846,10 +2932,12 @@ export class UrlbarInput extends HTMLElement {
|
||||
return;
|
||||
}
|
||||
this.style.top = px(
|
||||
@@ -169,7 +172,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2908,9 +2993,10 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -2908,9 +2996,10 @@ export class UrlbarInput extends HTMLElement {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -181,7 +184,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
);
|
||||
this.style.setProperty(
|
||||
"--urlbar-height",
|
||||
@@ -3344,6 +3430,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -3344,6 +3433,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
}
|
||||
|
||||
_toggleActionOverride(event) {
|
||||
@@ -189,7 +192,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
if (
|
||||
event.keyCode == KeyEvent.DOM_VK_SHIFT ||
|
||||
event.keyCode == KeyEvent.DOM_VK_ALT ||
|
||||
@@ -3447,8 +3534,8 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -3447,8 +3537,8 @@ export class UrlbarInput extends HTMLElement {
|
||||
if (!this.#isAddressbar) {
|
||||
return val;
|
||||
}
|
||||
@@ -200,7 +203,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
: val;
|
||||
// Only trim value if the directionality doesn't change to RTL and we're not
|
||||
// showing a strikeout https protocol.
|
||||
@@ -3754,6 +3841,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -3754,6 +3844,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
resultDetails = null,
|
||||
browser = this.window.gBrowser.selectedBrowser
|
||||
) {
|
||||
@@ -208,7 +211,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
if (this.#isAddressbar) {
|
||||
this.#prepareAddressbarLoad(
|
||||
url,
|
||||
@@ -3861,6 +3949,10 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -3861,6 +3952,10 @@ export class UrlbarInput extends HTMLElement {
|
||||
}
|
||||
reuseEmpty = true;
|
||||
}
|
||||
@@ -219,7 +222,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
if (
|
||||
where == "tab" &&
|
||||
reuseEmpty &&
|
||||
@@ -3868,6 +3960,9 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -3868,6 +3963,9 @@ export class UrlbarInput extends HTMLElement {
|
||||
) {
|
||||
where = "current";
|
||||
}
|
||||
@@ -229,7 +232,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
return where;
|
||||
}
|
||||
|
||||
@@ -4122,6 +4217,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -4122,6 +4220,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
this.setResultForCurrentValue(null);
|
||||
this.handleCommand();
|
||||
this.controller.clearLastQueryContextCache();
|
||||
@@ -237,7 +240,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
|
||||
this._suppressStartQuery = false;
|
||||
});
|
||||
@@ -4129,7 +4225,6 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -4129,7 +4228,6 @@ export class UrlbarInput extends HTMLElement {
|
||||
contextMenu.addEventListener("popupshowing", () => {
|
||||
// Close the results pane when the input field contextual menu is open,
|
||||
// because paste and go doesn't want a result selection.
|
||||
@@ -245,7 +248,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
|
||||
let controller =
|
||||
this.document.commandDispatcher.getControllerForCommand("cmd_paste");
|
||||
@@ -4239,7 +4334,11 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -4239,7 +4337,11 @@ export class UrlbarInput extends HTMLElement {
|
||||
if (!engineName && !source && !this.hasAttribute("searchmode")) {
|
||||
return;
|
||||
}
|
||||
@@ -258,7 +261,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
if (this._searchModeIndicatorTitle) {
|
||||
this._searchModeIndicatorTitle.textContent = "";
|
||||
this._searchModeIndicatorTitle.removeAttribute("data-l10n-id");
|
||||
@@ -4549,6 +4648,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -4549,6 +4651,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
|
||||
this.document.l10n.setAttributes(
|
||||
this.inputField,
|
||||
@@ -266,7 +269,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
l10nId,
|
||||
l10nId == "urlbar-placeholder-with-name"
|
||||
? { name: engineName }
|
||||
@@ -4662,6 +4762,11 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -4662,6 +4765,11 @@ export class UrlbarInput extends HTMLElement {
|
||||
}
|
||||
|
||||
_on_click(event) {
|
||||
@@ -278,7 +281,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
switch (event.target) {
|
||||
case this.inputField:
|
||||
case this._inputContainer:
|
||||
@@ -4740,7 +4845,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -4740,7 +4848,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +290,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
this.view.autoOpen({ event });
|
||||
} else {
|
||||
if (this._untrimOnFocusAfterKeydown) {
|
||||
@@ -4780,9 +4885,16 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -4780,9 +4888,16 @@ export class UrlbarInput extends HTMLElement {
|
||||
}
|
||||
|
||||
_on_mousedown(event) {
|
||||
@@ -305,7 +308,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
if (
|
||||
event.composedTarget != this.inputField &&
|
||||
event.composedTarget != this._inputContainer
|
||||
@@ -4792,6 +4904,10 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -4792,6 +4907,10 @@ export class UrlbarInput extends HTMLElement {
|
||||
|
||||
this.focusedViaMousedown = !this.focused;
|
||||
this._preventClickSelectsAll = this.focused;
|
||||
@@ -316,7 +319,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
|
||||
// Keep the focus status, since the attribute may be changed
|
||||
// upon calling this.focus().
|
||||
@@ -4827,7 +4943,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -4827,7 +4946,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
}
|
||||
// Don't close the view when clicking on a tab; we may want to keep the
|
||||
// view open on tab switch, and the TabSelect event arrived earlier.
|
||||
@@ -325,7 +328,7 @@ index edb0482f0bfb22c70a585b0770e5b0437983779e..545f65db4fe8b807b388d5552842cd9e
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5155,7 +5271,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
@@ -5155,7 +5274,7 @@ export class UrlbarInput extends HTMLElement {
|
||||
// When we are in actions search mode we can show more results so
|
||||
// increase the limit.
|
||||
let maxResults =
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/extensions/newtab/lib/ActivityStream.sys.mjs b/browser/extensions/newtab/lib/ActivityStream.sys.mjs
|
||||
index ae3c01650b3023a692cd4c50e33e3c3c39770c47..27a5fb0a630b9b79566be08cd5f7c9466ca30381 100644
|
||||
index 0f73c33c3237413131a2f4d2d1f3be857e533437..d7e9fcadc7744651fbe14952589df4603e7db592 100644
|
||||
--- a/browser/extensions/newtab/lib/ActivityStream.sys.mjs
|
||||
+++ b/browser/extensions/newtab/lib/ActivityStream.sys.mjs
|
||||
@@ -295,7 +295,7 @@ export const PREFS_CONFIG = new Map([
|
||||
|
||||
@@ -485,7 +485,7 @@
|
||||
}
|
||||
|
||||
#zen-site-data-icon-button {
|
||||
padding: 0 6px !important;
|
||||
padding: 0 4px !important;
|
||||
border-radius: var(--urlbar-icon-border-radius) !important;
|
||||
|
||||
& image {
|
||||
|
||||
3
src/external-patches/librewolf/README.md
Normal file
3
src/external-patches/librewolf/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Patches imported from LibreWolf
|
||||
|
||||
Firefox sometimes makes changes without considering the impact on forks. This is the case of the patches that are imported from LibreWolf, which are already present in LibreWolf but not yet (or never) present in Firefox. Thanks to LibreWolf for making these patches available and for their work in maintaining them. We will keep these patches as long as they are needed, and we will remove them once they are no longer necessary (for example, when they are merged into Firefox or when the issue they solve is no longer present).
|
||||
12
src/external-patches/librewolf/firefox-in-ua.patch
Normal file
12
src/external-patches/librewolf/firefox-in-ua.patch
Normal file
@@ -0,0 +1,12 @@
|
||||
diff --git a/toolkit/moz.configure b/toolkit/moz.configure
|
||||
--- a/toolkit/moz.configure
|
||||
+++ b/toolkit/moz.configure
|
||||
@@ -28,7 +28,7 @@ project_flag(
|
||||
|
||||
project_flag(
|
||||
env="MOZ_APP_UA_NAME",
|
||||
- default="",
|
||||
+ default="Firefox",
|
||||
nargs=1,
|
||||
help="Application name in the User Agent string",
|
||||
)
|
||||
24
src/external-patches/manifest.json
Normal file
24
src/external-patches/manifest.json
Normal file
@@ -0,0 +1,24 @@
|
||||
// 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/.
|
||||
[
|
||||
{
|
||||
"type": "phabricator",
|
||||
"id": "D279007",
|
||||
"name": "Fix MacOS Crash on Shutdown Firefox 149"
|
||||
},
|
||||
{
|
||||
"type": "local",
|
||||
"path": "firefox/no_liquid_glass_icon.patch"
|
||||
},
|
||||
{
|
||||
"type": "patch",
|
||||
"url": "https://codeberg.org/librewolf/source/raw/branch/main/patches/firefox-in-ua.patch",
|
||||
"dest": "librewolf",
|
||||
"replaces": {
|
||||
// They don't correctly export this patch, so we need to replace
|
||||
// the parameter's help description with the correct one.
|
||||
"application": "Application"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp
|
||||
index c0ca05afc1e8eb5cb7115a22c39dcf45285fdfb7..84b3fec2c7b2bc671e7004d8571dc428b8ee011d 100644
|
||||
index 2c4aca4a5984fc3201a2c643f8d26b8c4f4ef3bd..4c21cee90e5442d45cdb2bcf3f91200f0441218f 100644
|
||||
--- a/layout/generic/nsIFrame.cpp
|
||||
+++ b/layout/generic/nsIFrame.cpp
|
||||
@@ -11958,6 +11958,11 @@ gfx::Matrix nsIFrame::ComputeWidgetTransform() const {
|
||||
@@ -11956,6 +11956,11 @@ gfx::Matrix nsIFrame::ComputeWidgetTransform() const {
|
||||
gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
|
||||
uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel));
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp
|
||||
index 3c84ea049b01c20c6a7e9ff717e19d1d9e86248b..ed6b15dffa571348d1c13bf31f3b2e1638151e8f 100644
|
||||
index 25357c6a506b969c5049190c29e4e411ee5f4c31..bef909ddfc28d084cf87cae3fbd050bbde21468d 100644
|
||||
--- a/layout/style/nsStyleStruct.cpp
|
||||
+++ b/layout/style/nsStyleStruct.cpp
|
||||
@@ -3217,6 +3217,9 @@ nsStyleUIReset::nsStyleUIReset()
|
||||
@@ -3270,6 +3270,9 @@ nsStyleUIReset::nsStyleUIReset()
|
||||
mWindowShadow(StyleWindowShadow::Auto),
|
||||
mWindowOpacity(1.0),
|
||||
mMozWindowInputRegionMargin(StyleLength::Zero()),
|
||||
@@ -12,7 +12,7 @@ index 3c84ea049b01c20c6a7e9ff717e19d1d9e86248b..ed6b15dffa571348d1c13bf31f3b2e16
|
||||
mTransitions(
|
||||
nsStyleAutoArray<StyleTransition>::WITH_SINGLE_INITIAL_ELEMENT),
|
||||
mTransitionTimingFunctionCount(1),
|
||||
@@ -3261,6 +3264,7 @@ nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
|
||||
@@ -3314,6 +3317,7 @@ nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
|
||||
mWindowOpacity(aSource.mWindowOpacity),
|
||||
mMozWindowInputRegionMargin(aSource.mMozWindowInputRegionMargin),
|
||||
mMozWindowTransform(aSource.mMozWindowTransform),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h
|
||||
index 20d0dc30020ee7fcd27f689cb6c0754224b003c5..dcca0efd2c9822937c994dffeb63d2263fbb6331 100644
|
||||
index ca11ab168761a6da23811d802a87f6c31af6cca9..cb74679626620a12d37fb1671c373ea9793ae004 100644
|
||||
--- a/layout/style/nsStyleStruct.h
|
||||
+++ b/layout/style/nsStyleStruct.h
|
||||
@@ -2080,6 +2080,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset {
|
||||
@@ -2094,6 +2094,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset {
|
||||
// The margin of the window region that should be transparent to events.
|
||||
mozilla::StyleLength mMozWindowInputRegionMargin;
|
||||
mozilla::StyleTransform mMozWindowTransform;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
|
||||
index cc7fd419035a972bfab1b0e7631b8856e846ce81..b4e6d699f99e7164e62ce20792919420a8e73810 100644
|
||||
index 0986e53121fdb821bca4a7c30ff707bfdf2dbad7..2a1865098a00282aa1839df245cb472cdd64fdec 100644
|
||||
--- a/modules/libpref/init/StaticPrefList.yaml
|
||||
+++ b/modules/libpref/init/StaticPrefList.yaml
|
||||
@@ -19738,6 +19738,7 @@
|
||||
@@ -19748,6 +19748,7 @@
|
||||
mirror: always
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git a/netwerk/protocol/http/moz.build b/netwerk/protocol/http/moz.build
|
||||
index d2330003caf35c43d6831fb0d356ece513906f78..76c2faf822ddaf645eb03d93380c58b4e3b510c0 100644
|
||||
--- a/netwerk/protocol/http/moz.build
|
||||
+++ b/netwerk/protocol/http/moz.build
|
||||
@@ -227,7 +227,7 @@ LOCAL_INCLUDES += [
|
||||
"/netwerk/url-classifier",
|
||||
]
|
||||
|
||||
-DEFINES["MOZ_APP_UA_NAME"] = f'"{CONFIG["MOZ_APP_UA_NAME"]}"'
|
||||
+DEFINES["MOZ_APP_UA_NAME"] = f'"Firefox"'
|
||||
|
||||
if CONFIG["MOZ_AUTH_EXTENSION"]:
|
||||
LOCAL_INCLUDES += [
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/toolkit/components/extensions/parent/ext-runtime.js b/toolkit/components/extensions/parent/ext-runtime.js
|
||||
index 0d7a3e505b6bd30548c6dda1504dd343a517b083..54400def5e02e886765fab68c3854a6b3c24ef2b 100644
|
||||
index 0d7a3e505b6bd30548c6dda1504dd343a517b083..fb6c6b4ef3eae24995a02f708ec41afd31d812ef 100644
|
||||
--- a/toolkit/components/extensions/parent/ext-runtime.js
|
||||
+++ b/toolkit/components/extensions/parent/ext-runtime.js
|
||||
@@ -333,7 +333,7 @@ this.runtime = class extends ExtensionAPIPersistent {
|
||||
@@ -7,7 +7,7 @@ index 0d7a3e505b6bd30548c6dda1504dd343a517b083..54400def5e02e886765fab68c3854a6b
|
||||
getBrowserInfo: function () {
|
||||
const { name, vendor, version, appBuildID } = Services.appinfo;
|
||||
- const info = { name, vendor, version, buildID: appBuildID };
|
||||
+ const info = { name: 'firefox', vendor, version: AppConstants.ZEN_FIREFOX_VERSION, buildID: appBuildID };
|
||||
+ const info = { name: 'Firefox', vendor, version: AppConstants.ZEN_FIREFOX_VERSION, buildID: appBuildID, zen: {version: AppConstants.MOZ_APP_VERSION_DISPLAY} };
|
||||
return Promise.resolve(info);
|
||||
},
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/toolkit/modules/AppConstants.sys.mjs b/toolkit/modules/AppConstants.sys.mjs
|
||||
index 5465ed3fb01e4214029b44f338d5e1e1580c3aab..a4d6b42916014d5e0f643b35a7b4feb281f59023 100644
|
||||
index e2a22ddd6c6ba36491f37ed22bc2e1fbc149790c..920d8649ef48d685891223a834110ad9a059001a 100644
|
||||
--- a/toolkit/modules/AppConstants.sys.mjs
|
||||
+++ b/toolkit/modules/AppConstants.sys.mjs
|
||||
@@ -172,6 +172,8 @@ export var AppConstants = Object.freeze({
|
||||
@@ -170,6 +170,8 @@ export var AppConstants = Object.freeze({
|
||||
MOZ_UPDATE_CHANNEL: "@MOZ_UPDATE_CHANNEL@",
|
||||
MOZ_WIDGET_TOOLKIT: "@MOZ_WIDGET_TOOLKIT@",
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build
|
||||
index abc630955c18de629d3ffff3e8f9aa29f57950be..97d6d5f25d0859684c405c659e0eef26007027c4 100644
|
||||
index 114a8c3e2e36e57cb2334a893a11f1619831a6e7..9c9ae37b6462d00c93d8f3319cea2960b3cd9816 100644
|
||||
--- a/toolkit/modules/moz.build
|
||||
+++ b/toolkit/modules/moz.build
|
||||
@@ -281,6 +281,7 @@ for var in (
|
||||
|
||||
40
src/toolkit/xre/nsXREDirProvider-cpp.patch
Normal file
40
src/toolkit/xre/nsXREDirProvider-cpp.patch
Normal file
@@ -0,0 +1,40 @@
|
||||
diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp
|
||||
index 5eae0bca990fea9e995bd2edf5e879361f89391c..3f8bfedb83ddae07b1e82715fc7f6b2109a39e33 100644
|
||||
--- a/toolkit/xre/nsXREDirProvider.cpp
|
||||
+++ b/toolkit/xre/nsXREDirProvider.cpp
|
||||
@@ -1322,9 +1322,11 @@ nsresult nsXREDirProvider::AppendFromAppData(nsIFile* aFile, bool aIsDotted) {
|
||||
// Similar to nsXREDirProvider::AppendProfilePath.
|
||||
// TODO: Bug 1990407 - Evaluate if refactoring might be required there in the
|
||||
// future?
|
||||
- if (gAppData->profile) {
|
||||
+ // Use aIsDotted for a different purpose here, will probably break in the future
|
||||
+ if (gAppData->profile && aIsDotted) {
|
||||
nsAutoCString profile;
|
||||
profile = gAppData->profile;
|
||||
+ profile = "."_ns + nsDependentCString(gAppData->profile);
|
||||
MOZ_TRY(aFile->AppendRelativeNativePath(profile));
|
||||
} else {
|
||||
nsAutoCString vendor;
|
||||
@@ -1334,8 +1336,6 @@ nsresult nsXREDirProvider::AppendFromAppData(nsIFile* aFile, bool aIsDotted) {
|
||||
ToLowerCase(vendor);
|
||||
ToLowerCase(appName);
|
||||
|
||||
- MOZ_TRY(aFile->AppendRelativeNativePath(aIsDotted ? ("."_ns + vendor)
|
||||
- : vendor));
|
||||
MOZ_TRY(aFile->AppendRelativeNativePath(appName));
|
||||
}
|
||||
|
||||
@@ -1503,13 +1503,8 @@ nsresult nsXREDirProvider::GetLegacyOrXDGHomePath(const char* aHomeDir,
|
||||
|
||||
// If the build was made against a specific profile name, MOZ_APP_PROFILE=
|
||||
// then make sure we respect this and dont move to XDG directory
|
||||
- if (gAppData->profile) {
|
||||
- MOZ_TRY(NS_NewNativeLocalFile(nsDependentCString(aHomeDir),
|
||||
- getter_AddRefs(localDir)));
|
||||
- } else {
|
||||
MOZ_TRY(GetLegacyOrXDGConfigHome(aHomeDir, getter_AddRefs(localDir)));
|
||||
MOZ_TRY(localDir->Clone(getter_AddRefs(parentDir)));
|
||||
- }
|
||||
|
||||
MOZ_TRY(AppendFromAppData(localDir, false));
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
content/browser/zen-styles/zen-panel-ui.css (../../zen/common/styles/zen-panel-ui.css)
|
||||
content/browser/zen-styles/zen-single-components.css (../../zen/common/styles/zen-single-components.css)
|
||||
content/browser/zen-styles/zen-sidebar.css (../../zen/common/styles/zen-sidebar.css)
|
||||
content/browser/zen-styles/zen-toolbar.css (../../zen/common/styles/zen-toolbar.css)
|
||||
* content/browser/zen-styles/zen-toolbar.css (../../zen/common/styles/zen-toolbar.css)
|
||||
content/browser/zen-styles/zen-browser-container.css (../../zen/common/styles/zen-browser-container.css)
|
||||
content/browser/zen-styles/zen-omnibox.css (../../zen/common/styles/zen-omnibox.css)
|
||||
content/browser/zen-styles/zen-popup.css (../../zen/common/styles/zen-popup.css)
|
||||
|
||||
@@ -112,7 +112,7 @@ export class nsZenMenuBar {
|
||||
const openUnsyncedWindowItem = window.MozXULElement.parseXULToFragment(
|
||||
`<toolbarbutton id="appMenu-new-zen-unsynced-window-button"
|
||||
class="subviewbutton"
|
||||
data-l10n-id="zen-menubar-new-unsynced-window"
|
||||
data-l10n-id="zen-appmenu-new-blank-window"
|
||||
key="zen-new-unsynced-window"
|
||||
command="cmd_zenNewNavigatorUnsynced"/>`
|
||||
).querySelector("toolbarbutton");
|
||||
@@ -123,7 +123,7 @@ export class nsZenMenuBar {
|
||||
window.MozXULElement.parseXULToFragment(`
|
||||
<menuitem id="menu_new_zen_unsynced_window"
|
||||
class="subviewbutton"
|
||||
data-l10n-id="zen-menubar-new-unsynced-window"
|
||||
data-l10n-id="zen-menubar-new-blank-window"
|
||||
key="zen-new-unsynced-window"
|
||||
command="cmd_zenNewNavigatorUnsynced"/>`)
|
||||
);
|
||||
|
||||
@@ -1055,7 +1055,7 @@ window.gZenVerticalTabsManager = {
|
||||
if (!this._hasSetSingleToolbar) {
|
||||
height = AppConstants.platform == "macosx" ? 34 : 32;
|
||||
} else if (gURLBar.getAttribute("breakout-extend") !== "true") {
|
||||
height = 40;
|
||||
height = 38;
|
||||
}
|
||||
if (typeof height !== "undefined") {
|
||||
gURLBar.style.setProperty("--urlbar-height", `${height}px`);
|
||||
@@ -1275,6 +1275,15 @@ window.gZenVerticalTabsManager = {
|
||||
appContentNavbarContaienr.append(windowButtons);
|
||||
}
|
||||
|
||||
if (
|
||||
this._hasSetSingleToolbar &&
|
||||
Services.prefs.getBoolPref("zen.view.overflow-webext-toolbar", true)
|
||||
) {
|
||||
topButtons.setAttribute("addon-webext-overflowtarget", "zen-overflow-extensions-list");
|
||||
} else {
|
||||
topButtons.setAttribute("addon-webext-overflowtarget", "overflowed-extensions-list");
|
||||
}
|
||||
|
||||
gZenCompactModeManager.updateCompactModeContext(isSingleToolbar);
|
||||
|
||||
// Always move the splitter next to the sidebar
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
#urlbar:not([breakout-extend="true"]) {
|
||||
&:hover .urlbar-background {
|
||||
background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.2)) !important;
|
||||
background-color: var(--zen-toolbar-element-bg-hover) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@
|
||||
margin: 0;
|
||||
|
||||
:root[zen-single-toolbar="true"] & {
|
||||
padding: 6px !important;
|
||||
padding: 5px !important;
|
||||
width: unset !important;
|
||||
height: unset !important;
|
||||
}
|
||||
@@ -396,7 +396,7 @@
|
||||
#urlbar #tracking-protection-icon-container,
|
||||
#urlbar:not([breakout-extend="true"]) #identity-box:is(:not(.chromeUI), [pageproxystate="invalid"]) #identity-icon-box {
|
||||
border-radius: var(--urlbar-icon-border-radius) !important;
|
||||
min-width: 28px;
|
||||
min-width: 26px;
|
||||
}
|
||||
|
||||
/* Notification Stack */
|
||||
@@ -649,7 +649,7 @@
|
||||
}
|
||||
|
||||
&[selected] {
|
||||
--zen-selected-bg: color-mix(in srgb, var(--zen-primary-color) 50%, light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.2)) 50%);
|
||||
--zen-selected-bg: color-mix(in srgb, var(--zen-primary-color) 55%, light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.2)) 50%);
|
||||
--zen-selected-color: color-mix(in srgb, var(--zen-selected-bg), black 30%);
|
||||
background-color: var(--zen-selected-bg) !important;
|
||||
|
||||
@@ -667,7 +667,7 @@
|
||||
color: var(--zen-selected-color) !important;
|
||||
}
|
||||
|
||||
&:is([type="switchtab"], [type="dynamic"]) .urlbarView-favicon,
|
||||
&:is([type="switchtab"], [type="dynamic"], [type="history_serp"], [type="autofill_origin"], [type="top_site"]) .urlbarView-favicon,
|
||||
& .urlbarView-shortcutContent {
|
||||
fill: var(--zen-selected-color) !important;
|
||||
background-color: rgba(255, 255, 255, 0.9) !important;
|
||||
|
||||
48
src/zen/common/styles/zen-overflowing-addons.css
Normal file
48
src/zen/common/styles/zen-overflowing-addons.css
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
#zen-overflow-extensions-list:not(:empty) {
|
||||
--uei-icon-size: 14px;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
padding: 8px 2px;
|
||||
padding-bottom: 0px;
|
||||
|
||||
& .unified-extensions-item {
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& .toolbarbutton-badge-stack {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 8px 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
& .unified-extensions-item-action-button {
|
||||
background-color: var(--zen-toolbar-element-bg);
|
||||
height: 30px;
|
||||
margin: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: var(--border-radius-medium);
|
||||
overflow: clip;
|
||||
padding: 0;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--zen-toolbar-element-bg-hover);
|
||||
}
|
||||
}
|
||||
|
||||
.unified-extensions-item-contents,
|
||||
.unified-extensions-item-menu-button,
|
||||
unified-extensions-item-messagebar-wrapper {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -172,11 +172,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.toolbarbutton-1:not(#tabs-newtab-button) {
|
||||
.toolbarbutton-1:not(#tabs-newtab-button),
|
||||
.urlbar-page-action,
|
||||
.identity-box-button {
|
||||
--tab-border-radius: 6px;
|
||||
--toolbarbutton-border-radius: var(--tab-border-radius);
|
||||
--toolbarbutton-inner-padding: 6px;
|
||||
--toolbarbutton-outer-padding: 2px;
|
||||
--toolbarbutton-outer-padding: 1px;
|
||||
--toolbarbutton-icon-fill: color-mix(in srgb, currentColor 70%, transparent);
|
||||
|
||||
transition:
|
||||
background-color 0.1s,
|
||||
@@ -381,6 +384,7 @@
|
||||
&:hover {
|
||||
background-color: color-mix(in srgb, currentColor 10%, transparent);
|
||||
}
|
||||
|
||||
.zen-site-data-section:hover & {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -536,7 +540,7 @@
|
||||
0px 2px 4px rgba(0, 0, 0, 0.1),
|
||||
inset 0px 1px 0px light-dark(transparent, rgba(255, 255, 255, 0.15));
|
||||
border-radius: 6px;
|
||||
--base-border-color: light-dark(rgba(0, 0, 0, 0.175), rgba(255, 255, 255, 0.1));
|
||||
--base-border-color: light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.1));
|
||||
border: 1px solid light-dark(var(--base-border-color), rgb(21, 21, 21));
|
||||
}
|
||||
|
||||
|
||||
@@ -141,14 +141,11 @@
|
||||
--zen-button-padding: 0.6rem 1.2rem;
|
||||
|
||||
--zen-toolbar-element-bg: light-dark(
|
||||
color-mix(in oklch, var(--toolbox-textcolor) 10%, transparent),
|
||||
color-mix(in oklch, var(--toolbox-textcolor) 8%, transparent),
|
||||
color-mix(in oklch, var(--toolbox-textcolor) 15%, transparent)
|
||||
);
|
||||
|
||||
--zen-toolbar-element-bg-hover: light-dark(
|
||||
color-mix(in srgb, var(--zen-toolbar-element-bg) 75%, transparent),
|
||||
color-mix(in srgb, var(--zen-toolbar-element-bg) 60%, transparent)
|
||||
);
|
||||
--zen-toolbar-element-bg-hover: light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.1));
|
||||
|
||||
/* Toolbar */
|
||||
--tab-selected-color-scheme: inherit !important;
|
||||
@@ -222,7 +219,7 @@
|
||||
--input-border-color: var(--zen-input-border-color) !important;
|
||||
--zen-themed-toolbar-bg-transparent: light-dark(var(--zen-branding-bg), #171717);
|
||||
|
||||
--zen-workspace-indicator-height: 46px;
|
||||
--zen-workspace-indicator-height: 44px;
|
||||
|
||||
&:not([zen-sidebar-expanded='true']) {
|
||||
--zen-workspace-indicator-height: 38px;
|
||||
@@ -231,10 +228,7 @@
|
||||
--toolbar-field-color: var(--toolbox-textcolor) !important;
|
||||
|
||||
&[zen-private-window='true'] {
|
||||
--zen-main-browser-background: linear-gradient(130deg,
|
||||
color-mix(in srgb, rgb(10, 6, 11) 80%, var(--zen-themed-toolbar-bg-transparent)) 0%,
|
||||
color-mix(in srgb, rgb(19, 7, 22) 80%, var(--zen-themed-toolbar-bg-transparent)) 100%
|
||||
);
|
||||
--zen-main-browser-background: color-mix(in srgb, rgb(11, 10, 11) 90%, var(--zen-themed-toolbar-bg-transparent));
|
||||
--zen-main-browser-background-toolbar: var(--zen-main-browser-background);
|
||||
--zen-primary-color: light-dark(rgb(93, 42, 107), rgb(110, 48, 125)) !important;
|
||||
--toolbox-textcolor: color-mix(in srgb, currentColor 95%, transparent) !important;
|
||||
@@ -294,7 +288,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
#main-window:not([chromehidden~='toolbar']) {
|
||||
#main-window[windowtype="navigator:browser"]:not([chromehidden~='toolbar']) {
|
||||
min-height: 495px !important;
|
||||
|
||||
@media (-moz-windows-mica) or (-moz-platform: macos) or ((-moz-platform: linux) and
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
#nav-bar,
|
||||
#zen-sidebar-top-buttons {
|
||||
background: transparent;
|
||||
@@ -11,3 +12,5 @@
|
||||
:root[inDOMFullscreen="true"] #zen-appcontent-navbar-wrapper {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
%include zen-overflowing-addons.css
|
||||
|
||||
@@ -121,8 +121,11 @@ export const ZenCustomizableUI = new (class {
|
||||
|
||||
#initCreateNewButton(window) {
|
||||
const button = window.document.getElementById("zen-create-new-button");
|
||||
button.addEventListener("command", (event) => {
|
||||
if (window.gZenWorkspaces.privateWindowOrDisabled) {
|
||||
// If we use "mousedown" event for private windows (which open a new tab on "click"), we might end up with
|
||||
// the urlbar flicking and therefore we use "command" event to avoid that.
|
||||
let isPrivateMode = window.gZenWorkspaces.privateWindowOrDisabled;
|
||||
button.addEventListener(isPrivateMode ? "command" : "mousedown", (event) => {
|
||||
if (isPrivateMode) {
|
||||
window.document.getElementById("cmd_newNavigatorTab").doCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -962,7 +962,8 @@
|
||||
dropElement = dragData.dropElement;
|
||||
dropBefore = dragData.dropBefore;
|
||||
}
|
||||
if (!dropElement) {
|
||||
// Essentials should be properly handled by ::animateVerticalPinnedGridDragOver
|
||||
if (!dropElement || dropElement.hasAttribute("zen-essential")) {
|
||||
this.clearDragOverVisuals();
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
|
||||
|
||||
static rawIcon = new DOMParser().parseFromString(
|
||||
`
|
||||
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg width="28" height="28" viewBox="0 0 27 27" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient gradientUnits="userSpaceOnUse" x1="14" y1="5.625" x2="14" y2="22.375" id="gradient-0">
|
||||
<stop offset="0" style="stop-color: rgb(255, 255, 255)"/>
|
||||
@@ -242,7 +242,7 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
|
||||
}
|
||||
|
||||
get resetButton() {
|
||||
return this.labelElement.parentElement.querySelector(".tab-reset-button");
|
||||
return this.labelElement.parentElement?.querySelector(".tab-reset-button") ?? null;
|
||||
}
|
||||
|
||||
unloadAllTabs(event) {
|
||||
|
||||
@@ -98,6 +98,7 @@ zen-folder {
|
||||
|
||||
& svg {
|
||||
filter: contrast(125%);
|
||||
transform: translate(-1px, -1px);
|
||||
|
||||
:root:not([zen-sidebar-expanded]) & {
|
||||
transition: transform 0.1s ease;
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
box-shadow: 0 0 12px 1px rgba(0, 0, 0, 0.07);
|
||||
padding: 8px;
|
||||
opacity: 1 !important;
|
||||
color: var(--zen-primary-color);
|
||||
color: var(--zen-primary-color) !important;
|
||||
|
||||
&:hover {
|
||||
background: light-dark(rgb(41, 41, 41), rgb(204, 204, 204));
|
||||
|
||||
@@ -10,6 +10,7 @@ const lazy = {};
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
|
||||
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
|
||||
SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs",
|
||||
gWindowSyncEnabled: "resource:///modules/zen/ZenWindowSync.sys.mjs",
|
||||
gSyncOnlyPinnedTabs: "resource:///modules/zen/ZenWindowSync.sys.mjs",
|
||||
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
|
||||
@@ -29,6 +30,10 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
3
|
||||
);
|
||||
|
||||
const SHOULD_LOG_TAB_ENTRIES = Services.prefs.getBoolPref(
|
||||
"zen.session-store.log-tab-entries",
|
||||
false
|
||||
);
|
||||
const SHOULD_BACKUP_FILE = Services.prefs.getBoolPref("zen.session-store.backup-file", true);
|
||||
const FILE_NAME = "zen-sessions.jsonlz4";
|
||||
|
||||
@@ -52,6 +57,9 @@ class nsZenSidebarObject {
|
||||
}
|
||||
|
||||
set data(data) {
|
||||
if (typeof data !== "object") {
|
||||
throw new Error("Sidebar data must be an object");
|
||||
}
|
||||
this.#sidebar = data;
|
||||
}
|
||||
}
|
||||
@@ -76,14 +84,14 @@ export class nsZenSessionManager {
|
||||
|
||||
init() {
|
||||
this.log("Initializing session manager");
|
||||
let backupFile = null;
|
||||
let backupTo = null;
|
||||
if (SHOULD_BACKUP_FILE) {
|
||||
backupFile = PathUtils.join(this.#backupFolderPath, FILE_NAME);
|
||||
backupTo = PathUtils.join(this.#backupFolderPath, "recovery.baklz4");
|
||||
}
|
||||
this.#file = new JSONFile({
|
||||
path: this.#storeFilePath,
|
||||
compression: "lz4",
|
||||
backupFile,
|
||||
backupTo,
|
||||
});
|
||||
this.log("Session file path:", this.#file.path);
|
||||
this.#deferredBackupTask = new lazy.DeferredTask(async () => {
|
||||
@@ -94,18 +102,16 @@ export class nsZenSessionManager {
|
||||
log(...args) {
|
||||
if (lazy.gShouldLog) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("ZenSessionManager:", ...args);
|
||||
console.debug("ZenSessionManager:", ...args);
|
||||
}
|
||||
}
|
||||
|
||||
get #storeFilePath() {
|
||||
let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
|
||||
return PathUtils.join(profileDir, FILE_NAME);
|
||||
return PathUtils.join(PathUtils.profileDir, FILE_NAME);
|
||||
}
|
||||
|
||||
get #backupFolderPath() {
|
||||
let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
|
||||
return PathUtils.join(profileDir, "zen-sessions-backup");
|
||||
return PathUtils.join(PathUtils.profileDir, "zen-sessions-backup");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,6 +225,11 @@ export class nsZenSessionManager {
|
||||
this._shouldRunMigration = true;
|
||||
await this.#getDataFromDBForMigration();
|
||||
}
|
||||
if (SHOULD_LOG_TAB_ENTRIES) {
|
||||
for (const tab of this.#sidebar.tabs || []) {
|
||||
this.log("Tab entry in session file:", tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get #shouldRestoreOnlyPinned() {
|
||||
@@ -228,6 +239,13 @@ export class nsZenSessionManager {
|
||||
);
|
||||
}
|
||||
|
||||
get #shouldRestoreFromCrash() {
|
||||
return (
|
||||
lazy.SessionStartup.previousSessionCrashed &&
|
||||
Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash")
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the session file is read. Restores the sidebar data
|
||||
* into all windows.
|
||||
@@ -253,23 +271,53 @@ export class nsZenSessionManager {
|
||||
}
|
||||
return initialState;
|
||||
}
|
||||
const allowRestoreUnsynced = Services.prefs.getBoolPref(
|
||||
"zen.session-store.restore-unsynced-windows",
|
||||
true
|
||||
);
|
||||
if (initialState?.windows?.length && !allowRestoreUnsynced) {
|
||||
initialState.windows = initialState.windows.filter((win) => {
|
||||
if (win.isZenUnsynced) {
|
||||
this.log("Skipping unsynced window during restore");
|
||||
}
|
||||
return !win.isZenUnsynced;
|
||||
});
|
||||
}
|
||||
// If there are no windows, we create an empty one. By default,
|
||||
// firefox would create simply a new empty window, but we want
|
||||
// to make sure that the sidebar object is properly initialized.
|
||||
// This would happen on first run after having a single private window
|
||||
// open when quitting the app, for example.
|
||||
if (!initialState?.windows?.length) {
|
||||
let normalWindowsExist = initialState?.windows?.some(
|
||||
(win) => !win.isPrivate && !win.isPopup && !win.isTaskbarTab && !win.isZenUnsynced
|
||||
);
|
||||
if (!initialState?.windows?.length || !normalWindowsExist) {
|
||||
this.log("No windows found in initial state, creating an empty one");
|
||||
initialState ||= {};
|
||||
initialState.windows = [
|
||||
{
|
||||
tabs: [],
|
||||
},
|
||||
];
|
||||
initialState.windows ||= [];
|
||||
initialState.windows.push({
|
||||
tabs: [],
|
||||
});
|
||||
}
|
||||
return initialState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after @onFileRead, when session startup has crash checkpoint information available.
|
||||
* Restores the sidebar data into all windows, and runs any crash checkpoint related logic,
|
||||
* such as restoring only pinned tabs if the previous session was not crashed and the user
|
||||
* preference is set to do so.
|
||||
*
|
||||
* @param {object} initialState
|
||||
* The initial session state read from the session file, possibly modified by onFileRead.
|
||||
*/
|
||||
onCrashCheckpoints(initialState) {
|
||||
if (!lazy.gWindowSyncEnabled) {
|
||||
return;
|
||||
}
|
||||
// When we don't have browser.startup.page set to resume session,
|
||||
// we only want to restore the pinned tabs into the new windows.
|
||||
if (this.#shouldRestoreOnlyPinned && this.#sidebar?.tabs) {
|
||||
if (this.#shouldRestoreOnlyPinned && !this.#shouldRestoreFromCrash && this.#sidebar?.tabs) {
|
||||
this.log("Restoring only pinned tabs into windows");
|
||||
const sidebar = this.#sidebar;
|
||||
sidebar.tabs = (sidebar.tabs || []).filter((tab) => tab.pinned);
|
||||
@@ -278,30 +326,20 @@ export class nsZenSessionManager {
|
||||
// Restore all windows with the same sidebar object, this will
|
||||
// guarantee that all tabs, groups, folders and split view data
|
||||
// are properly synced across all windows.
|
||||
const allowRestoreUnsynced = Services.prefs.getBoolPref(
|
||||
"zen.session-store.restore-unsynced-windows",
|
||||
true
|
||||
);
|
||||
if (!this._shouldRunMigration) {
|
||||
this.log(`Restoring Zen session data into ${initialState.windows?.length || 0} windows`);
|
||||
for (let i = 0; i < initialState.windows.length; i++) {
|
||||
let winData = initialState.windows[i];
|
||||
if (winData.isZenUnsynced) {
|
||||
if (!allowRestoreUnsynced) {
|
||||
// We don't wan't to restore any unsynced windows with the sidebar data.
|
||||
this.log("Skipping restore of unsynced window");
|
||||
delete initialState.windows[i];
|
||||
}
|
||||
if (winData.isZenUnsynced || winData.isPrivate || winData.isPopup || winData.isTaskbarTab) {
|
||||
continue;
|
||||
}
|
||||
this.#restoreWindowData(winData);
|
||||
}
|
||||
} else if (initialState) {
|
||||
this.log("Saving windata state after migration");
|
||||
this.saveState(Cu.cloneInto(initialState, {}));
|
||||
this.saveState(Cu.cloneInto(initialState, {}), true);
|
||||
}
|
||||
delete this._shouldRunMigration;
|
||||
return initialState;
|
||||
}
|
||||
|
||||
get #sidebar() {
|
||||
@@ -384,6 +422,25 @@ export class nsZenSessionManager {
|
||||
return initialState;
|
||||
}
|
||||
|
||||
onRestoringClosedWindow(aWinData) {
|
||||
// We only want to save all pinned tabs if the user preference allows it.
|
||||
// See https://github.com/zen-browser/desktop/issues/12307
|
||||
if (this.#shouldRestoreOnlyPinned && aWinData?.tabs?.length) {
|
||||
this.log("Restoring only pinned tabs for closed window");
|
||||
this.#filterUnpinnedTabs(aWinData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out all unpinned tabs and groups from the given window data object.
|
||||
*
|
||||
* @param {object} aWindow - The window data object to filter.
|
||||
*/
|
||||
#filterUnpinnedTabs(aWindow) {
|
||||
aWindow.tabs = aWindow.tabs.filter((tab) => tab.pinned);
|
||||
aWindow.groups = aWindow.groups?.filter((group) => group.pinned);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a given window data object is saveable.
|
||||
*
|
||||
@@ -398,8 +455,11 @@ export class nsZenSessionManager {
|
||||
* Saves the current session state. Collects data and writes to disk.
|
||||
*
|
||||
* @param {object} state The current session state.
|
||||
* @param {boolean} soon Whether to save the file soon or immediately.
|
||||
* If true, the file will be saved asynchronously or when quitting
|
||||
* the app. If false, the file will be saved immediately.
|
||||
*/
|
||||
saveState(state) {
|
||||
saveState(state, soon = false) {
|
||||
let windows = state?.windows || [];
|
||||
windows = windows.filter((win) => this.#isWindowSaveable(win));
|
||||
if (!windows.length) {
|
||||
@@ -408,12 +468,16 @@ export class nsZenSessionManager {
|
||||
return;
|
||||
}
|
||||
this.#collectWindowData(windows);
|
||||
// This would save the data to disk asynchronously or when
|
||||
// quitting the app.
|
||||
this.#file.data = this.#sidebar;
|
||||
this.#file.saveSoon();
|
||||
// This would save the data to disk asynchronously or when quitting the app.
|
||||
let sidebar = this.#sidebar;
|
||||
this.#file.data = sidebar;
|
||||
if (soon) {
|
||||
this.#file.saveSoon();
|
||||
} else {
|
||||
this.#file._save();
|
||||
}
|
||||
this.#debounceRegeneration();
|
||||
this.log(`Saving Zen session data with ${this.#sidebar.tabs?.length || 0} tabs`);
|
||||
this.log(`Saving Zen session data with ${sidebar.tabs?.length || 0} tabs`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,12 +529,12 @@ export class nsZenSessionManager {
|
||||
// Now we need to check if we have exceeded the maximum
|
||||
// number of backups allowed, and delete the oldest ones
|
||||
// if needed.
|
||||
let prefix = PathUtils.join(backupFolder, "zen-sessions-");
|
||||
let files = await IOUtils.getChildren(backupFolder);
|
||||
files = files.filter((file) => file.startsWith("zen-sessions-")).sort();
|
||||
files = files.filter((file) => file.startsWith(prefix)).sort();
|
||||
for (let i = 0; i < files.length - lazy.gMaxSessionBackups; i++) {
|
||||
const fileToDelete = PathUtils.join(backupFolder, files[i].name);
|
||||
this.log(`Deleting old backup file ${files[i].name}`);
|
||||
await IOUtils.remove(fileToDelete);
|
||||
this.log(`Deleting old backup file ${files[i]}`);
|
||||
await IOUtils.remove(files[i]);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("ZenSessionManager: Failed to create session file backups", e);
|
||||
@@ -492,7 +556,7 @@ export class nsZenSessionManager {
|
||||
return;
|
||||
}
|
||||
this.log("Saving closed window session data into Zen session store");
|
||||
this.saveState({ windows: [aWinData] });
|
||||
this.saveState({ windows: [aWinData] }, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -514,6 +578,13 @@ export class nsZenSessionManager {
|
||||
this.#sidebar = sidebarData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out tabs that are not useful to restore, such as empty tabs with no group association.
|
||||
* If removeUnpinnedTabs is true, it also filters out unpinned tabs.
|
||||
*
|
||||
* @param {Array} tabs - The array of tab data objects to filter.
|
||||
* @returns {Array} The filtered array of tab data objects.
|
||||
*/
|
||||
#filterUnusedTabs(tabs) {
|
||||
return tabs.filter((tab) => {
|
||||
// We need to ignore empty tabs with no group association
|
||||
@@ -568,7 +639,10 @@ export class nsZenSessionManager {
|
||||
// as they should be the same as the ones in the sidebar.
|
||||
if (lazy.gSyncOnlyPinnedTabs) {
|
||||
let pinnedTabs = (sidebar.tabs || []).filter((tab) => tab.pinned);
|
||||
let unpinedWindowTabs = (aWindowData.tabs || []).filter((tab) => !tab.pinned);
|
||||
let unpinedWindowTabs = [];
|
||||
if (!this.#shouldRestoreOnlyPinned) {
|
||||
unpinedWindowTabs = (aWindowData.tabs || []).filter((tab) => !tab.pinned);
|
||||
}
|
||||
aWindowData.tabs = [...pinnedTabs, ...unpinedWindowTabs];
|
||||
|
||||
// We restore ALL the split view data in the sidebar, if the group doesn't exist in the window,
|
||||
@@ -613,18 +687,19 @@ export class nsZenSessionManager {
|
||||
);
|
||||
let windowToClone = windows[0] || {};
|
||||
let newWindow = Cu.cloneInto(windowToClone, {});
|
||||
let shouldRestoreOnlyPinned = !lazy.gWindowSyncEnabled || lazy.gSyncOnlyPinnedTabs;
|
||||
if (windows.length < 2) {
|
||||
// We only want to restore the sidebar object if we found
|
||||
// only one normal window to clone from (which is the one
|
||||
// we are opening).
|
||||
this.log("Restoring sidebar data into new window");
|
||||
this.#restoreWindowData(newWindow);
|
||||
shouldRestoreOnlyPinned ||= this.#shouldRestoreOnlyPinned;
|
||||
}
|
||||
newWindow.tabs = this.#filterUnusedTabs(newWindow.tabs || []);
|
||||
if (!lazy.gWindowSyncEnabled || lazy.gSyncOnlyPinnedTabs) {
|
||||
if (shouldRestoreOnlyPinned) {
|
||||
// Don't bring over any unpinned tabs if window sync is disabled or if syncing only pinned tabs.
|
||||
newWindow.tabs = newWindow.tabs.filter((tab) => tab.pinned);
|
||||
newWindow.groups = newWindow.groups?.filter((group) => group.pinned);
|
||||
this.#filterUnpinnedTabs(newWindow);
|
||||
}
|
||||
|
||||
// These are window-specific from the previous window state that
|
||||
|
||||
@@ -28,7 +28,7 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gShouldLog", "zen.window-sync.log", true);
|
||||
|
||||
const OBSERVING = ["browser-window-before-show", "sessionstore-windows-restored"];
|
||||
const INSTANT_EVENTS = ["SSWindowClosing"];
|
||||
const INSTANT_EVENTS = ["SSWindowClosing", "TabSelect", "focus"];
|
||||
const UNSYNCED_WINDOW_EVENTS = ["TabOpen"];
|
||||
const EVENTS = [
|
||||
"TabClose",
|
||||
@@ -50,9 +50,6 @@ const EVENTS = [
|
||||
"ZenTabRemovedFromSplit",
|
||||
"ZenSplitViewTabsSplit",
|
||||
|
||||
"TabSelect",
|
||||
|
||||
"focus",
|
||||
...INSTANT_EVENTS,
|
||||
...UNSYNCED_WINDOW_EVENTS,
|
||||
];
|
||||
@@ -81,6 +78,13 @@ class nsZenWindowSync {
|
||||
lastHandlerPromise: Promise.resolve(),
|
||||
};
|
||||
|
||||
/**
|
||||
* Promise that resolves when the current docshell swap operation is finished.
|
||||
* Used to avoid multiple simultaneous swap operations that could interfere with each other.
|
||||
* For example, when focusing a window AND selecting a tab at the same time.
|
||||
*/
|
||||
#docShellSwitchPromise = Promise.resolve();
|
||||
|
||||
/**
|
||||
* Map of sync handlers for different event types.
|
||||
* Each handler is a function that takes the event as an argument.
|
||||
@@ -169,7 +173,7 @@ class nsZenWindowSync {
|
||||
log(...args) {
|
||||
if (lazy.gShouldLog) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info("ZenWindowSync:", ...args);
|
||||
console.debug("ZenWindowSync:", ...args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +331,7 @@ class nsZenWindowSync {
|
||||
return;
|
||||
}
|
||||
if (INSTANT_EVENTS.includes(aEvent.type)) {
|
||||
this.#handleNextEvent(aEvent);
|
||||
this.#handleNextEventInternal(aEvent);
|
||||
return;
|
||||
}
|
||||
if (this.#eventHandlingContext.window && this.#eventHandlingContext.window !== window) {
|
||||
@@ -374,30 +378,31 @@ class nsZenWindowSync {
|
||||
this.#syncHandlers.delete(aHandler);
|
||||
}
|
||||
|
||||
#handleNextEventInternal(aEvent) {
|
||||
const handler = `on_${aEvent.type}`;
|
||||
if (typeof this[handler] !== "function") {
|
||||
throw new Error(`No handler for event type: ${aEvent.type}`);
|
||||
}
|
||||
return this[handler](aEvent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the next event by calling the appropriate handler method.
|
||||
*
|
||||
* @param {Event} aEvent - The event to handle.
|
||||
*/
|
||||
#handleNextEvent(aEvent) {
|
||||
const handler = `on_${aEvent.type}`;
|
||||
async #handleNextEvent(aEvent) {
|
||||
try {
|
||||
if (typeof this[handler] === "function") {
|
||||
let promise = this[handler](aEvent) || Promise.resolve();
|
||||
promise.then(() => {
|
||||
for (let syncHandler of this.#syncHandlers) {
|
||||
try {
|
||||
syncHandler(aEvent);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
throw new Error(`No handler for event type: ${aEvent.type}`);
|
||||
await this.#handleNextEventInternal(aEvent);
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
console.error(e);
|
||||
}
|
||||
for (let syncHandler of this.#syncHandlers) {
|
||||
try {
|
||||
syncHandler(aEvent);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,39 +795,43 @@ class nsZenWindowSync {
|
||||
#styleSwapedBrowsers(aOurTab, aOtherTab, callback = undefined, promiseToWait = null) {
|
||||
const ourBrowser = aOurTab.linkedBrowser;
|
||||
const otherBrowser = aOtherTab.linkedBrowser;
|
||||
return new Promise((resolve) => {
|
||||
aOurTab.ownerGlobal.requestAnimationFrame(async () => {
|
||||
if (callback) {
|
||||
const browserBlob = await aOtherTab.ownerGlobal.PageThumbs.captureToBlob(
|
||||
aOtherTab.linkedBrowser,
|
||||
{
|
||||
fullScale: true,
|
||||
fullViewport: true,
|
||||
}
|
||||
);
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async (resolve) => {
|
||||
if (callback) {
|
||||
const browserBlob = await aOtherTab.ownerGlobal.PageThumbs.captureToBlob(
|
||||
aOtherTab.linkedBrowser,
|
||||
{
|
||||
fullScale: true,
|
||||
fullViewport: true,
|
||||
}
|
||||
);
|
||||
|
||||
let mySrc = await new Promise((r, re) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(browserBlob);
|
||||
reader.onloadend = function () {
|
||||
// result includes identifier 'data:image/png;base64,' plus the base64 data
|
||||
r(reader.result);
|
||||
};
|
||||
reader.onerror = function () {
|
||||
re(new Error("Failed to read blob as data URL"));
|
||||
};
|
||||
});
|
||||
let mySrc = await new Promise((r, re) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(browserBlob);
|
||||
reader.onloadend = function () {
|
||||
// result includes identifier 'data:image/png;base64,' plus the base64 data
|
||||
r(reader.result);
|
||||
};
|
||||
reader.onerror = function () {
|
||||
re(new Error("Failed to read blob as data URL"));
|
||||
};
|
||||
});
|
||||
|
||||
this.#createPseudoImageForBrowser(otherBrowser, mySrc);
|
||||
let promise = this.#createPseudoImageForBrowser(otherBrowser, mySrc);
|
||||
await Promise.all([promiseToWait, promise]);
|
||||
aOurTab.ownerGlobal.requestAnimationFrame(() => {
|
||||
otherBrowser.setAttribute("zen-pseudo-hidden", "true");
|
||||
await promiseToWait;
|
||||
callback();
|
||||
}
|
||||
|
||||
this.#maybeRemovePseudoImageForBrowser(ourBrowser);
|
||||
ourBrowser.removeAttribute("zen-pseudo-hidden");
|
||||
this.#maybeRemovePseudoImageForBrowser(ourBrowser);
|
||||
});
|
||||
callback();
|
||||
} else {
|
||||
ourBrowser.removeAttribute("zen-pseudo-hidden");
|
||||
resolve();
|
||||
});
|
||||
this.#maybeRemovePseudoImageForBrowser(ourBrowser);
|
||||
}
|
||||
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -834,10 +843,25 @@ class nsZenWindowSync {
|
||||
*/
|
||||
#createPseudoImageForBrowser(aBrowser, aSrc) {
|
||||
const doc = aBrowser.ownerDocument;
|
||||
const win = aBrowser.ownerGlobal;
|
||||
const img = doc.createElement("img");
|
||||
img.className = "zen-pseudo-browser-image";
|
||||
img.src = aSrc;
|
||||
let promise = new Promise((resolve) => {
|
||||
if (img.complete) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
let finish = () => {
|
||||
win.requestAnimationFrame(() => {
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
img.onload = finish;
|
||||
img.onerror = finish;
|
||||
});
|
||||
aBrowser.after(img);
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -911,20 +935,16 @@ class nsZenWindowSync {
|
||||
*
|
||||
* @param {Window} aWindow - The window that triggered the event.
|
||||
* @param {object} aPreviousTab - The previously selected tab.
|
||||
* @param {boolean} ignoreSameTab - Indicates if the same tab should be ignored.
|
||||
*/
|
||||
async #onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null, ignoreSameTab = false) {
|
||||
// On some occasions, such as when closing a window, this
|
||||
// function might be called multiple times for the same tab.
|
||||
if (aWindow.gBrowser.selectedTab === this.#lastSelectedTab && !ignoreSameTab) {
|
||||
return;
|
||||
}
|
||||
async #onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null) {
|
||||
let activeBrowsers = aWindow.gBrowser.selectedBrowsers;
|
||||
let activeTabs = activeBrowsers.map((browser) => aWindow.gBrowser.getTabForBrowser(browser));
|
||||
// Ignore previous tabs that are still "active". These scenarios could happen for example,
|
||||
// when selecting on a split view tab that was already active.
|
||||
if (aPreviousTab?._zenContentsVisible && !activeTabs.includes(aPreviousTab)) {
|
||||
let tabsToSwap = aPreviousTab.splitView ? aPreviousTab.group.tabs : [aPreviousTab];
|
||||
let tabsToSwap = aPreviousTab.group?.hasAttribute("split-view-group")
|
||||
? aPreviousTab.group.tabs
|
||||
: [aPreviousTab];
|
||||
for (const tab of tabsToSwap) {
|
||||
const otherTabToShow = this.#getActiveTabFromOtherWindows(aWindow, tab.id, (t) =>
|
||||
t?.splitView ? t.group.tabs.some((st) => st.selected) : t?.selected
|
||||
@@ -1209,11 +1229,12 @@ class nsZenWindowSync {
|
||||
});
|
||||
}
|
||||
|
||||
on_focus(aEvent) {
|
||||
async on_focus(aEvent) {
|
||||
if (typeof aEvent.target !== "object") {
|
||||
return;
|
||||
}
|
||||
const { ownerGlobal: window } = aEvent.target;
|
||||
await this.#docShellSwitchPromise;
|
||||
const window = Services.focus.activeWindow;
|
||||
if (
|
||||
!window?.gBrowser ||
|
||||
this.#lastFocusedWindow?.deref() === window ||
|
||||
@@ -1224,17 +1245,21 @@ class nsZenWindowSync {
|
||||
}
|
||||
this.#lastFocusedWindow = new WeakRef(window);
|
||||
this.#lastSelectedTab = new WeakRef(window.gBrowser.selectedTab);
|
||||
return this.#onTabSwitchOrWindowFocus(window);
|
||||
return (this.#docShellSwitchPromise = this.#onTabSwitchOrWindowFocus(window));
|
||||
}
|
||||
|
||||
on_TabSelect(aEvent) {
|
||||
async on_TabSelect(aEvent) {
|
||||
await this.#docShellSwitchPromise;
|
||||
const tab = aEvent.target;
|
||||
if (this.#lastSelectedTab?.deref() === tab) {
|
||||
return;
|
||||
}
|
||||
this.#lastSelectedTab = new WeakRef(tab);
|
||||
const previousTab = aEvent.detail.previousTab;
|
||||
return this.#onTabSwitchOrWindowFocus(aEvent.target.ownerGlobal, previousTab);
|
||||
return (this.#docShellSwitchPromise = this.#onTabSwitchOrWindowFocus(
|
||||
aEvent.target.ownerGlobal,
|
||||
previousTab
|
||||
));
|
||||
}
|
||||
|
||||
on_SSWindowClosing(aEvent) {
|
||||
@@ -1254,21 +1279,26 @@ class nsZenWindowSync {
|
||||
for (let browser of aBrowsers) {
|
||||
const tab = this.#swapedTabsEntriesForWC.get(browser.permanentKey);
|
||||
if (tab) {
|
||||
let win = tab.ownerGlobal;
|
||||
this.log(`Finalizing swap for tab ${tab.id} on window close`);
|
||||
lazy.TabStateCache.update(
|
||||
tab.linkedBrowser.permanentKey,
|
||||
lazy.TabStateCache.get(browser.permanentKey)
|
||||
);
|
||||
let tabData = this.#getTabEntriesFromCache(tab);
|
||||
let activePageData = tabData.entries[tabData.index - 1] || null;
|
||||
try {
|
||||
let win = tab.ownerGlobal;
|
||||
this.log(`Finalizing swap for tab ${tab.id} on window close`);
|
||||
lazy.TabStateCache.update(
|
||||
tab.linkedBrowser.permanentKey,
|
||||
lazy.TabStateCache.get(browser.permanentKey)
|
||||
);
|
||||
let tabData = this.#getTabEntriesFromCache(tab);
|
||||
let activePageData = tabData.entries[tabData.index - 1] || null;
|
||||
|
||||
// If the page has a title, set it. When doing a swap and we still didn't
|
||||
// flush the tab state, the title might not be correct.
|
||||
if (activePageData) {
|
||||
win.gBrowser.setInitialTabTitle(tab, activePageData.title, {
|
||||
isContentTitle: activePageData.title && activePageData.title != activePageData.url,
|
||||
});
|
||||
// If the page has a title, set it. When doing a swap and we still didn't
|
||||
// flush the tab state, the title might not be correct.
|
||||
if (activePageData && win?.gBrowser) {
|
||||
win.gBrowser.setInitialTabTitle(tab, activePageData.title, {
|
||||
isContentTitle: activePageData.title && activePageData.title != activePageData.url,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// We might have already closed the window at this point, so just ignore any error.
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1370,7 +1400,7 @@ class nsZenWindowSync {
|
||||
|
||||
return new Promise((resolve) => {
|
||||
lazy.setTimeout(() => {
|
||||
this.#onTabSwitchOrWindowFocus(window, null, /* ignoreSameTab = */ true).finally(resolve);
|
||||
this.#onTabSwitchOrWindowFocus(window, null).finally(resolve);
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -208,6 +208,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
if (typeof groupIndex === "undefined") {
|
||||
groupIndex = this._data.findIndex((group) => group.tabs.includes(tab));
|
||||
}
|
||||
// If groupIndex === -1, so `this._data.findIndex` couldn't find the split group
|
||||
if (groupIndex < 0) {
|
||||
return;
|
||||
}
|
||||
const group = this._data[groupIndex];
|
||||
const tabIndex = group.tabs.indexOf(tab);
|
||||
group.tabs.splice(tabIndex, 1);
|
||||
@@ -259,6 +263,33 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
return element;
|
||||
}
|
||||
|
||||
_calculateDropSide(event, panelsRect) {
|
||||
const { width, height } = panelsRect;
|
||||
const { clientX, clientY } = event;
|
||||
// TODO(octaviusz): Maybe we should add this as preference
|
||||
// `zen.splitView.tab-drop-treshold`
|
||||
const quarterWidth = width / 4;
|
||||
const quarterHeight = height / 4;
|
||||
|
||||
const edges = [
|
||||
{ side: "left", dist: clientX - panelsRect.left, threshold: quarterWidth },
|
||||
{ side: "right", dist: panelsRect.right - clientX, threshold: quarterWidth },
|
||||
{ side: "top", dist: clientY - panelsRect.top, threshold: quarterHeight },
|
||||
{ side: "bottom", dist: panelsRect.bottom - clientY, threshold: quarterHeight },
|
||||
];
|
||||
|
||||
let closestEdge = null;
|
||||
let minDist = Infinity;
|
||||
for (const edge of edges) {
|
||||
if (edge.dist < edge.threshold && edge.dist < minDist) {
|
||||
minDist = edge.dist;
|
||||
closestEdge = edge;
|
||||
}
|
||||
}
|
||||
|
||||
return closestEdge ? closestEdge.side : null;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
onBrowserDragOverToSplit(event) {
|
||||
gBrowser.tabContainer.tabDragAndDrop.clearSpaceSwitchTimer();
|
||||
@@ -303,6 +334,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
const panelsRect = gBrowser.tabbox.getBoundingClientRect();
|
||||
const panelsWidth = panelsRect.width;
|
||||
const panelsHeight = panelsRect.height;
|
||||
if (
|
||||
event.clientX > panelsRect.left + panelsWidth - 10 ||
|
||||
event.clientX < panelsRect.left + 10 ||
|
||||
@@ -311,11 +343,17 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const dropSide = this._calculateDropSide(event, panelsRect);
|
||||
if (!dropSide) {
|
||||
return;
|
||||
}
|
||||
// first quarter or last quarter of the screen, but not the middle
|
||||
if (
|
||||
!(
|
||||
event.clientX < panelsRect.left + panelsWidth / 4 ||
|
||||
event.clientX > panelsRect.left + (panelsWidth / 4) * 3
|
||||
event.clientX > panelsRect.left + (panelsWidth / 4) * 3 ||
|
||||
event.clientY < panelsRect.top + panelsHeight / 4 ||
|
||||
event.clientY > panelsRect.top + (panelsHeight / 4) * 3
|
||||
)
|
||||
) {
|
||||
return;
|
||||
@@ -336,93 +374,113 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
this._canDrop = true;
|
||||
// eslint-disable-next-line mozilla/valid-services
|
||||
Services.zen.playHapticFeedback();
|
||||
{
|
||||
this._draggingTab = draggedTab;
|
||||
gBrowser.selectedTab = oldTab;
|
||||
this._hasAnimated = true;
|
||||
this.tabBrowserPanel.setAttribute("dragging-split", "true");
|
||||
// Add a min width to all the browser elements to prevent them from resizing
|
||||
// eslint-disable-next-line no-shadow
|
||||
const panelsWidth = gBrowser.tabbox.getBoundingClientRect().width;
|
||||
let numOfTabsToDivide = 2;
|
||||
if (currentView) {
|
||||
numOfTabsToDivide = currentView.tabs.length + 1;
|
||||
this._draggingTab = draggedTab;
|
||||
gBrowser.selectedTab = oldTab;
|
||||
this._hasAnimated = true;
|
||||
this.tabBrowserPanel.setAttribute("dragging-split", "true");
|
||||
this._animateDropEdge(dropSide, currentView, draggedTab, oldTab);
|
||||
}
|
||||
|
||||
_animateDropEdge(dropSide, currentView, draggedTab, oldTab) {
|
||||
// Add a min width to all the browser elements to prevent them from resizing
|
||||
// eslint-disable-next-line no-shadow
|
||||
const { height, width } = gBrowser.tabbox.getBoundingClientRect();
|
||||
let numOfTabsToDivide = 2;
|
||||
if (currentView) {
|
||||
numOfTabsToDivide = currentView.tabs.length + 1;
|
||||
}
|
||||
const halfWidth = width / numOfTabsToDivide;
|
||||
const halfHeight = height / numOfTabsToDivide;
|
||||
const side = dropSide;
|
||||
for (const browser of gBrowser.browsers) {
|
||||
if (!browser) {
|
||||
continue;
|
||||
}
|
||||
const halfWidth = panelsWidth / numOfTabsToDivide;
|
||||
let threshold =
|
||||
gNavToolbox.getBoundingClientRect().width *
|
||||
(gZenVerticalTabsManager._prefsRightSide ? 0 : 1);
|
||||
if (gZenCompactModeManager.preference) {
|
||||
threshold = 0;
|
||||
const { width: browserWidth, height: browserHeight } = browser.getBoundingClientRect();
|
||||
// Only apply it to the left side because if we add it to the right side,
|
||||
// we wont be able to move the element to the left.
|
||||
// FIXME: This is a workaround, we should find a better way to do this
|
||||
switch (side) {
|
||||
case "left":
|
||||
browser.style.minWidth = `${browserWidth}px`;
|
||||
break;
|
||||
case "top":
|
||||
browser.style.minHeight = `${browserHeight}px`;
|
||||
break;
|
||||
}
|
||||
const side = event.clientX - threshold > halfWidth ? "right" : "left";
|
||||
for (const browser of gBrowser.browsers) {
|
||||
if (!browser) {
|
||||
continue;
|
||||
}
|
||||
this.fakeBrowser = document.createXULElement("vbox");
|
||||
window.addEventListener("dragend", this.onBrowserDragEndToSplit, { once: true });
|
||||
const padding = ZenThemeModifier.elementSeparation;
|
||||
this.fakeBrowser.setAttribute("flex", "1");
|
||||
this.fakeBrowser.id = "zen-split-view-fake-browser";
|
||||
if (oldTab.splitView) {
|
||||
this.fakeBrowser.setAttribute("has-split-view", "true");
|
||||
}
|
||||
gBrowser.tabbox.appendChild(this.fakeBrowser);
|
||||
this.fakeBrowser.setAttribute("side", side);
|
||||
let animateTabBox = null;
|
||||
let animateFakeBrowser = null;
|
||||
switch (side) {
|
||||
case "left":
|
||||
animateTabBox = {
|
||||
padding: [0, `0 0 0 ${halfWidth}px`],
|
||||
};
|
||||
animateFakeBrowser = {
|
||||
width: [0, `${halfWidth - padding}px`],
|
||||
margin: [0, `0 0 0 ${-halfWidth}px`],
|
||||
};
|
||||
break;
|
||||
case "right":
|
||||
animateTabBox = {
|
||||
padding: [0, `0 ${halfWidth}px 0 0`],
|
||||
};
|
||||
animateFakeBrowser = {
|
||||
width: [0, `${halfWidth - padding}px`],
|
||||
};
|
||||
break;
|
||||
|
||||
case "top":
|
||||
animateTabBox = {
|
||||
padding: [0, `${halfHeight}px 0 0 0`],
|
||||
};
|
||||
animateFakeBrowser = {
|
||||
height: [0, `${halfHeight - padding}px`],
|
||||
margin: [0, `${-halfHeight}px 0 0 0`],
|
||||
};
|
||||
break;
|
||||
|
||||
case "bottom":
|
||||
animateTabBox = {
|
||||
padding: [0, `0 0 ${halfHeight}px 0`],
|
||||
};
|
||||
animateFakeBrowser = {
|
||||
height: [0, `${halfHeight - padding}px`],
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
this._finishAllAnimatingPromise = Promise.all([
|
||||
gZenUIManager.motion.animate(gBrowser.tabbox, animateTabBox, {
|
||||
duration: 0.1,
|
||||
easing: "ease-out",
|
||||
}),
|
||||
gZenUIManager.motion.animate(this.fakeBrowser, animateFakeBrowser, {
|
||||
duration: 0.1,
|
||||
easing: "ease-out",
|
||||
}),
|
||||
]);
|
||||
if (this._finishAllAnimatingPromise) {
|
||||
this._finishAllAnimatingPromise.then(() => {
|
||||
if (draggedTab !== oldTab) {
|
||||
draggedTab.linkedBrowser.docShellIsActive = false;
|
||||
draggedTab.linkedBrowser
|
||||
.closest(".browserSidebarContainer")
|
||||
.classList.remove("deck-selected");
|
||||
}
|
||||
const width = browser.getBoundingClientRect().width;
|
||||
// Only apply it to the left side because if we add it to the right side,
|
||||
// we wont be able to move the element to the left.
|
||||
// FIXME: This is a workaround, we should find a better way to do this
|
||||
if (side === "left") {
|
||||
browser.style.minWidth = `${width}px`;
|
||||
}
|
||||
}
|
||||
this.fakeBrowser = document.createXULElement("vbox");
|
||||
window.addEventListener("dragend", this.onBrowserDragEndToSplit, { once: true });
|
||||
const padding = ZenThemeModifier.elementSeparation;
|
||||
this.fakeBrowser.setAttribute("flex", "1");
|
||||
this.fakeBrowser.id = "zen-split-view-fake-browser";
|
||||
if (oldTab.splitView) {
|
||||
this.fakeBrowser.setAttribute("has-split-view", "true");
|
||||
}
|
||||
gBrowser.tabbox.appendChild(this.fakeBrowser);
|
||||
this.fakeBrowser.setAttribute("side", side);
|
||||
this._finishAllAnimatingPromise = Promise.all([
|
||||
gZenUIManager.motion.animate(
|
||||
gBrowser.tabbox,
|
||||
side === "left"
|
||||
? {
|
||||
paddingLeft: [0, `${halfWidth}px`],
|
||||
paddingRight: 0,
|
||||
}
|
||||
: {
|
||||
paddingRight: [0, `${halfWidth}px`],
|
||||
paddingLeft: 0,
|
||||
},
|
||||
{
|
||||
duration: 0.1,
|
||||
easing: "ease-out",
|
||||
}
|
||||
),
|
||||
gZenUIManager.motion.animate(
|
||||
this.fakeBrowser,
|
||||
{
|
||||
width: [0, `${halfWidth - padding}px`],
|
||||
...(side === "left"
|
||||
? {
|
||||
marginLeft: [0, `${-halfWidth}px`],
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
{
|
||||
duration: 0.1,
|
||||
easing: "ease-out",
|
||||
}
|
||||
),
|
||||
]);
|
||||
if (this._finishAllAnimatingPromise) {
|
||||
this._finishAllAnimatingPromise.then(() => {
|
||||
if (draggedTab !== oldTab) {
|
||||
draggedTab.linkedBrowser.docShellIsActive = false;
|
||||
draggedTab.linkedBrowser
|
||||
.closest(".browserSidebarContainer")
|
||||
.classList.remove("deck-selected");
|
||||
}
|
||||
this.fakeBrowser.addEventListener("dragleave", this.onBrowserDragEndToSplit);
|
||||
this._canDrop = true;
|
||||
});
|
||||
}
|
||||
this.fakeBrowser.addEventListener("dragleave", this.onBrowserDragEndToSplit);
|
||||
this._canDrop = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,12 +505,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
return;
|
||||
}
|
||||
const panelsWidth = panelsRect.width;
|
||||
const panelsHeight = panelsRect.height;
|
||||
let numOfTabsToDivide = 2;
|
||||
const currentView = this._data[this._lastOpenedTab.splitViewValue];
|
||||
if (currentView) {
|
||||
numOfTabsToDivide = currentView.tabs.length + 1;
|
||||
}
|
||||
const halfWidth = panelsWidth / numOfTabsToDivide;
|
||||
const halfHeight = panelsHeight / numOfTabsToDivide;
|
||||
const padding = ZenThemeModifier.elementSeparation;
|
||||
if (!this.fakeBrowser) {
|
||||
return;
|
||||
@@ -464,39 +524,60 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
...gBrowser.tabContainer.tabDragAndDrop.originalDragImageArgs
|
||||
);
|
||||
this._canDrop = false;
|
||||
Promise.all([
|
||||
gZenUIManager.motion.animate(
|
||||
gBrowser.tabbox,
|
||||
side === "left"
|
||||
? {
|
||||
paddingLeft: [`${halfWidth}px`, 0],
|
||||
}
|
||||
: {
|
||||
paddingRight: [`${halfWidth}px`, 0],
|
||||
},
|
||||
{
|
||||
duration: 0.1,
|
||||
easing: "ease-out",
|
||||
}
|
||||
),
|
||||
gZenUIManager.motion.animate(
|
||||
this.fakeBrowser,
|
||||
{
|
||||
width: [`${halfWidth - padding * 2}px`, 0],
|
||||
...(side === "left"
|
||||
? {
|
||||
marginLeft: [`${-halfWidth}px`, 0],
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
{
|
||||
duration: 0.1,
|
||||
easing: "ease-out",
|
||||
}
|
||||
),
|
||||
]).finally(() => {
|
||||
this._maybeRemoveFakeBrowser();
|
||||
});
|
||||
let animateTabBox = null;
|
||||
let animateFakeBrowser = null;
|
||||
switch (side) {
|
||||
case "left":
|
||||
animateTabBox = {
|
||||
padding: [`0 0 0 ${halfWidth}px`, 0],
|
||||
};
|
||||
animateFakeBrowser = {
|
||||
width: [`${halfWidth - padding}px`, 0],
|
||||
margin: [`0 0 0 ${-halfWidth}px`, 0],
|
||||
};
|
||||
break;
|
||||
case "right":
|
||||
animateTabBox = {
|
||||
padding: [`0 ${halfWidth}px 0 0`, 0],
|
||||
};
|
||||
animateFakeBrowser = {
|
||||
width: [`${halfWidth - padding}px`, 0],
|
||||
};
|
||||
break;
|
||||
case "top":
|
||||
animateTabBox = {
|
||||
padding: [`${halfHeight}px 0 0 0`, 0],
|
||||
};
|
||||
animateFakeBrowser = {
|
||||
height: [`${halfHeight - padding}px`, 0],
|
||||
margin: [`${-halfHeight}px 0 0 0`, 0],
|
||||
};
|
||||
break;
|
||||
case "bottom":
|
||||
animateTabBox = {
|
||||
padding: [`0 0 ${halfHeight}px 0`, 0],
|
||||
};
|
||||
animateFakeBrowser = {
|
||||
height: [`${halfHeight - padding}px`, 0],
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
this._finishAllAnimatingPromise = Promise.all([
|
||||
gZenUIManager.motion.animate(gBrowser.tabbox, animateTabBox, {
|
||||
duration: 0.1,
|
||||
easing: "ease-out",
|
||||
}),
|
||||
gZenUIManager.motion.animate(this.fakeBrowser, animateFakeBrowser, {
|
||||
duration: 0.1,
|
||||
easing: "ease-out",
|
||||
}),
|
||||
]);
|
||||
if (this._finishAllAnimatingPromise) {
|
||||
this._finishAllAnimatingPromise.then(() => {
|
||||
this._maybeRemoveFakeBrowser();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1845,12 +1926,24 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
const dropSide = this.fakeBrowser?.getAttribute("side");
|
||||
const containerRect = this.fakeBrowser.getBoundingClientRect();
|
||||
const padding = ZenThemeModifier.elementSeparation;
|
||||
const dropTarget = document.elementFromPoint(
|
||||
dropSide === "left"
|
||||
? containerRect.left + containerRect.width + padding + 5
|
||||
: containerRect.left - padding - 5,
|
||||
event.clientY
|
||||
);
|
||||
let targetX = event.clientX;
|
||||
let targetY = event.clientY;
|
||||
switch (dropSide) {
|
||||
case "left":
|
||||
targetX = containerRect.left + containerRect.width + padding + 5;
|
||||
break;
|
||||
case "right":
|
||||
targetX = containerRect.left - padding - 5;
|
||||
break;
|
||||
case "top":
|
||||
targetY = containerRect.top + containerRect.height + padding + 5;
|
||||
break;
|
||||
case "bottom":
|
||||
targetY = containerRect.top - padding - 5;
|
||||
break;
|
||||
}
|
||||
|
||||
const dropTarget = document.elementFromPoint(targetX, targetY);
|
||||
const browser =
|
||||
dropTarget?.closest("browser") ??
|
||||
dropTarget?.closest(".browserSidebarContainer")?.querySelector("browser");
|
||||
@@ -1862,7 +1955,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
|
||||
let droppedOnTab = gZenGlanceManager.getTabOrGlanceParent(gBrowser.getTabForBrowser(browser));
|
||||
if (droppedOnTab === this._draggingTab) {
|
||||
this.createEmptySplit(dropSide == "right");
|
||||
this.createEmptySplit(dropSide);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1919,29 +2012,21 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
|
||||
const parentNode = droppedOnSplitNode.parent;
|
||||
|
||||
// Then add the tab to the split view
|
||||
group.tabs.push(draggedTab);
|
||||
|
||||
// If dropping on a side, create a new split in that direction
|
||||
// If dropping on a side, wrap entire layout in a new split at the root level
|
||||
if (hoverSide !== "center") {
|
||||
const splitDirection = hoverSide === "left" || hoverSide === "right" ? "row" : "column";
|
||||
if (parentNode.direction !== splitDirection) {
|
||||
this.splitIntoNode(
|
||||
droppedOnSplitNode,
|
||||
new nsSplitLeafNode(draggedTab, 50),
|
||||
hoverSide,
|
||||
0.5
|
||||
);
|
||||
const rootNode = group.layoutTree;
|
||||
const prepend = hoverSide === "left" || hoverSide === "top";
|
||||
|
||||
if (rootNode.direction === splitDirection) {
|
||||
// Root has the same direction, add as a new child of the root
|
||||
this.addTabToSplit(draggedTab, rootNode, prepend);
|
||||
} else {
|
||||
this.addTabToSplit(
|
||||
draggedTab,
|
||||
parentNode,
|
||||
/* prepend = */ hoverSide === "left" || hoverSide === "top"
|
||||
);
|
||||
// Different direction, wrap root in a new split node
|
||||
this.splitIntoNode(rootNode, new nsSplitLeafNode(draggedTab, 50), hoverSide, 0.5);
|
||||
}
|
||||
} else {
|
||||
this.addTabToSplit(draggedTab, group.layoutTree);
|
||||
@@ -1951,13 +2036,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
} else {
|
||||
// Create new split view with layout based on drop position
|
||||
let gridType = "vsep";
|
||||
const gridType = dropSide === "top" || dropSide === "bottom" ? "hsep" : "vsep";
|
||||
const topOrLeft = dropSide === "top" || dropSide === "left";
|
||||
|
||||
// Put tabs always as if it was dropped from the left
|
||||
this.splitTabs(
|
||||
dropSide == "left" ? [draggedTab, droppedOnTab] : [droppedOnTab, draggedTab],
|
||||
topOrLeft ? [draggedTab, droppedOnTab] : [droppedOnTab, draggedTab],
|
||||
gridType,
|
||||
dropSide == "left" ? 0 : 1
|
||||
topOrLeft ? 0 : 1
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2192,14 +2278,16 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
}
|
||||
|
||||
createEmptySplit(rightSide = true) {
|
||||
createEmptySplit(side = "right") {
|
||||
const selectedTab = gBrowser.selectedTab;
|
||||
const emptyTab = gZenWorkspaces._emptyTab;
|
||||
let tabs = rightSide ? [selectedTab, emptyTab] : [emptyTab, selectedTab];
|
||||
const gridType = side === "top" || side === "bottom" ? "hsep" : "vsep";
|
||||
const topOrLeft = side === "top" || side === "left";
|
||||
let tabs = topOrLeft ? [emptyTab, selectedTab] : [selectedTab, emptyTab];
|
||||
const data = {
|
||||
tabs,
|
||||
gridType: "grid",
|
||||
layoutTree: this.calculateLayoutTree(tabs, "grid"),
|
||||
gridType,
|
||||
layoutTree: this.calculateLayoutTree(tabs, gridType),
|
||||
};
|
||||
this.#withoutSplitViewTransition(() => {
|
||||
this._data.push(data);
|
||||
@@ -2234,9 +2322,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
gBrowser.selectedTab = selectedTab;
|
||||
this.resetTabState(emptyTab, false);
|
||||
this.splitTabs(
|
||||
rightSide ? [selectedTab, newSelectedTab] : [newSelectedTab, selectedTab],
|
||||
"grid",
|
||||
rightSide ? 1 : 0
|
||||
topOrLeft ? [newSelectedTab, selectedTab] : [selectedTab, newSelectedTab],
|
||||
gridType,
|
||||
topOrLeft ? 0 : 1
|
||||
);
|
||||
} else {
|
||||
cleanup();
|
||||
|
||||
@@ -44,12 +44,12 @@
|
||||
#zen-splitview-dropzone {
|
||||
position: absolute !important;
|
||||
|
||||
margin: var(--zen-split-column-gap) var(--zen-split-row-gap) !important;
|
||||
margin-bottom: 0 !important;
|
||||
margin-left: 0 !important;
|
||||
margin: var(--zen-split-column-gap) var(--zen-split-row-gap);
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
|
||||
&.browserSidebarContainer:not([zen-split='true']) {
|
||||
margin-top: 0 !important;
|
||||
margin-top: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
@@ -204,6 +204,15 @@
|
||||
overflow: hidden;
|
||||
will-change: width, margin-left;
|
||||
|
||||
&[side='top'],
|
||||
&[side='bottom'] {
|
||||
width: 100%;
|
||||
|
||||
&[has-split-view='true'] {
|
||||
width: calc(100% - var(--zen-element-separation));
|
||||
}
|
||||
}
|
||||
|
||||
&[side='right'] {
|
||||
right: 0;
|
||||
|
||||
@@ -211,6 +220,10 @@
|
||||
right: var(--zen-element-separation);
|
||||
}
|
||||
}
|
||||
|
||||
&[side='bottom'] {
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
#zen-split-view-drag-image {
|
||||
|
||||
@@ -531,8 +531,16 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
const isVisible = contextTab.pinned && !contextTab.multiselected;
|
||||
const isEssential = contextTab.getAttribute("zen-essential");
|
||||
const zenAddEssential = document.getElementById("context_zen-add-essential");
|
||||
document.getElementById("context_zen-reset-pinned-tab").hidden = !isVisible;
|
||||
document.getElementById("context_zen-replace-pinned-url-with-current").hidden = !isVisible;
|
||||
const zenResetPinnedTab = document.getElementById("context_zen-reset-pinned-tab");
|
||||
const zenReplacePinnedUrl = document.getElementById(
|
||||
"context_zen-replace-pinned-url-with-current"
|
||||
);
|
||||
[zenResetPinnedTab, zenReplacePinnedUrl].forEach((element) => {
|
||||
if (element) {
|
||||
element.hidden = !isVisible;
|
||||
document.l10n.setArgs(element, { isEssential });
|
||||
}
|
||||
});
|
||||
zenAddEssential.hidden = isEssential || !!contextTab.group;
|
||||
document.l10n
|
||||
.formatValue("tab-context-zen-add-essential-badge", {
|
||||
|
||||
@@ -209,7 +209,7 @@
|
||||
}
|
||||
|
||||
:root[zen-unsynced-window="true"] & {
|
||||
transform: translateY(-4px);
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@
|
||||
|
||||
--tab-block-margin: 2px;
|
||||
--tab-selected-bgcolor: light-dark(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0.18));
|
||||
--tab-selected-shadow: 0 1px 1px 1px light-dark(rgba(0, 0, 0, 0.09), rgba(0, 0, 0, 0.05)) !important;
|
||||
--tab-selected-shadow: 0 1px 1px 0px light-dark(rgba(0, 0, 0, 0.09), rgba(0, 0, 0, 0.05)) !important;
|
||||
grid-gap: 0 !important;
|
||||
|
||||
&[overflow]::after,
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
# This file is autogenerated by scripts/import_external_tests.py
|
||||
# Do not edit manually.
|
||||
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
"safebrowsing/browser.toml",
|
||||
"sandbox/browser.toml",
|
||||
"shell/browser.toml",
|
||||
"tooltiptext/browser.toml",
|
||||
]
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
]
|
||||
@@ -682,13 +682,6 @@ async function testFileAccessWindowsOnly() {
|
||||
let tests = [];
|
||||
|
||||
let extDir = GetPerUserExtensionDir();
|
||||
// We used to unconditionally create this directory from Firefox, but that
|
||||
// was dropped in bug 2001887. The value of this directory is questionable;
|
||||
// the test was added in Firefox 56 (bug 1403744) to cover legacy add-ons,
|
||||
// but legacy add-on support was discontinued in Firefox 57, and we stopped
|
||||
// sideloading add-ons from this directory on all builds except ESR in
|
||||
// Firefox 74 (bug 1602840).
|
||||
await IOUtils.makeDirectory(extDir.path);
|
||||
tests.push({
|
||||
desc: "per-user extensions dir",
|
||||
ok: true,
|
||||
|
||||
@@ -22,13 +22,6 @@ add_setup(async function setup() {
|
||||
const xdgConfigHome = Services.env.get("XDG_CONFIG_HOME");
|
||||
Assert.greater(xdgConfigHome.length, 1, "XDG_CONFIG_HOME is defined");
|
||||
|
||||
// Verify the profile directory is inside XDG_CONFIG_HOME
|
||||
const profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
Assert.ok(
|
||||
profileDir.path.startsWith(xdgConfigHome),
|
||||
`Profile directory (${profileDir.path}) should be inside XDG_CONFIG_HOME (${xdgConfigHome})`
|
||||
);
|
||||
|
||||
// If it is there, do actual testing
|
||||
sanityChecks();
|
||||
});
|
||||
|
||||
@@ -12,14 +12,11 @@ support-files = [
|
||||
"browser_content_sandbox_utils.js",
|
||||
"browser_content_sandbox_fs_tests.js",
|
||||
]
|
||||
test-directories = [
|
||||
"/tmp/.xdg_default_test",
|
||||
"/tmp/.xdg_default_test/.config/mozilla/firefox/xdg_default_profile",
|
||||
]
|
||||
# .config needs to exists for the sandbox to properly add it
|
||||
test-directories = ["/tmp/.xdg_default_test", "/tmp/.xdg_default_test/.config"]
|
||||
environment = [
|
||||
"HOME=/tmp/.xdg_default_test",
|
||||
]
|
||||
profile-path = "/tmp/.xdg_default_test/.config/mozilla/firefox/xdg_default_profile"
|
||||
|
||||
["browser_content_sandbox_fs_xdg_default.js"]
|
||||
run-if = ["os == 'linux'"]
|
||||
|
||||
@@ -12,17 +12,12 @@ support-files = [
|
||||
"browser_content_sandbox_utils.js",
|
||||
"browser_content_sandbox_fs_tests.js",
|
||||
]
|
||||
test-directories = [
|
||||
"/tmp/.xdg_mozLegacyHome_test/.config",
|
||||
"/tmp/.xdg_config_home_test",
|
||||
"/tmp/.xdg_mozLegacyHome_test/.mozilla/firefox/xdg_mozLegacyHome_profile",
|
||||
]
|
||||
test-directories = ["/tmp/.xdg_mozLegacyHome_test/.config", "/tmp/.xdg_config_home_test"]
|
||||
environment = [
|
||||
"XDG_CONFIG_HOME=/tmp/.xdg_config_home_test",
|
||||
"HOME=/tmp/.xdg_mozLegacyHome_test",
|
||||
"MOZ_LEGACY_HOME=1",
|
||||
]
|
||||
profile-path = "/tmp/.xdg_mozLegacyHome_test/.mozilla/firefox/xdg_mozLegacyHome_profile"
|
||||
|
||||
["browser_content_sandbox_fs_xdg_mozLegacyHome.js"]
|
||||
run-if = ["os == 'linux'"]
|
||||
|
||||
@@ -11,15 +11,11 @@ support-files = [
|
||||
"browser_content_sandbox_utils.js",
|
||||
"browser_content_sandbox_fs_tests.js",
|
||||
]
|
||||
test-directories = [
|
||||
"/tmp/.xdg_config_home_test",
|
||||
"/tmp/.xdg_config_home_test/mozilla/firefox/xdg_config_home_profile",
|
||||
]
|
||||
test-directories = "/tmp/.xdg_config_home_test"
|
||||
environment = [
|
||||
"XDG_CONFIG_HOME=/tmp/.xdg_config_home_test",
|
||||
"MOZ_LEGACY_HOME=0",
|
||||
]
|
||||
profile-path = "/tmp/.xdg_config_home_test/mozilla/firefox/xdg_config_home_profile"
|
||||
|
||||
["browser_content_sandbox_fs_xdg_xdgConfigHome.js"]
|
||||
run-if = [
|
||||
|
||||
@@ -144,6 +144,10 @@ export class nsZenWorkspace extends MozXULElement {
|
||||
this.pinnedTabsContainer.scrollbox = this.scrollbox;
|
||||
this.#initialPinnedElementChildrenCount = this.pinnedTabsContainer.children.length;
|
||||
|
||||
if (document.documentElement.hasAttribute("zen-unsynced-window")) {
|
||||
this.indicator.removeAttribute("context");
|
||||
}
|
||||
|
||||
this.indicator
|
||||
.querySelector(".zen-workspaces-actions")
|
||||
.addEventListener("click", this.onActionsCommand.bind(this));
|
||||
|
||||
@@ -57,12 +57,6 @@ window.ZenWorkspaceBookmarksStorage = {
|
||||
CREATE INDEX IF NOT EXISTS idx_bookmarks_workspaces_changes
|
||||
ON zen_bookmarks_workspaces_changes(bookmark_guid, workspace_uuid)
|
||||
`);
|
||||
|
||||
// Before, workspace_uuid was a FOREIGN KEY, not anymore, so we need to drop the constraint
|
||||
// This is a no-op if the constraint doesn't exist
|
||||
await db.execute(`
|
||||
PRAGMA foreign_keys = OFF;
|
||||
`);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
@@ -231,7 +231,7 @@ class nsZenWorkspaces {
|
||||
}
|
||||
}
|
||||
|
||||
async selectEmptyTab(newTabTarget = null, selectURLBar = true) {
|
||||
async selectEmptyTab(newTabTarget = null) {
|
||||
// Validate browser state first
|
||||
if (!this._validateBrowserState()) {
|
||||
console.warn("Browser state invalid for empty tab selection");
|
||||
@@ -251,30 +251,6 @@ class nsZenWorkspaces {
|
||||
!this._emptyTab.ownerGlobal.closed &&
|
||||
gZenVerticalTabsManager._canReplaceNewTab
|
||||
) {
|
||||
// Only set up URL bar selection if we're switching to a different tab
|
||||
if (gBrowser.selectedTab !== this._emptyTab && selectURLBar) {
|
||||
const tabSelectListener = () => {
|
||||
// Remove the event listener first to prevent any chance of multiple executions
|
||||
window.removeEventListener("TabSelect", tabSelectListener);
|
||||
|
||||
// Use requestAnimationFrame to ensure DOM is updated
|
||||
requestAnimationFrame(() => {
|
||||
// Then use setTimeout to ensure browser has time to process tab switch
|
||||
setTimeout(() => {
|
||||
if (gURLBar) {
|
||||
try {
|
||||
gURLBar.select();
|
||||
} catch (e) {
|
||||
console.warn("Error selecting URL bar:", e);
|
||||
}
|
||||
}
|
||||
}, 50);
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener("TabSelect", tabSelectListener, { once: true });
|
||||
}
|
||||
|
||||
// Safely switch to the empty tab using our debounced method
|
||||
const success = await this._safelySelectTab(this._emptyTab);
|
||||
if (!success) {
|
||||
@@ -1044,6 +1020,8 @@ class nsZenWorkspaces {
|
||||
delete this._initialTab;
|
||||
}
|
||||
|
||||
showed &&= Services.prefs.getBoolPref("zen.urlbar.open-on-startup", true);
|
||||
|
||||
// Wait for the next event loop to ensure that the startup focus logic by
|
||||
// firefox has finished doing it's thing.
|
||||
setTimeout(() => {
|
||||
@@ -1130,7 +1108,7 @@ class nsZenWorkspaces {
|
||||
return (
|
||||
!window.toolbar.visible ||
|
||||
Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab") ||
|
||||
this.privateWindowOrDisabled
|
||||
(this.privateWindowOrDisabled && !this.isPrivateWindow)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2436,7 +2414,10 @@ class nsZenWorkspaces {
|
||||
if (!this.currentWindowIsSyncing) {
|
||||
containerTabId = parseInt(gBrowser.selectedTab.getAttribute("usercontextid")) || 0;
|
||||
let label = ContextualIdentityService.getUserContextLabel(containerTabId) || "Default";
|
||||
name = this.isPrivateWindow ? "Private " + name : label;
|
||||
name = this.isPrivateWindow ? "Incognito" : label;
|
||||
if (this.isPrivateWindow) {
|
||||
icon = gZenEmojiPicker.getSVGURL("eye.svg");
|
||||
}
|
||||
}
|
||||
let workspace = {
|
||||
uuid: gZenUIManager.generateUuidv4(),
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
/* Mark workspaces indicator */
|
||||
.zen-current-workspace-indicator {
|
||||
--indicator-gap: calc(var(--toolbarbutton-inner-padding) - 1px);
|
||||
padding: calc(2px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
padding: calc(3px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
max-height: var(--zen-workspace-indicator-height);
|
||||
@@ -173,14 +173,13 @@
|
||||
&::before {
|
||||
border-radius: var(--border-radius-medium);
|
||||
background: transparent;
|
||||
transition: background 0.1s;
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: var(--zen-toolbox-padding);
|
||||
top: 4px;
|
||||
left: calc(var(--zen-toolbox-padding) + 2px);
|
||||
width: calc(100% - var(--zen-toolbox-padding) * 3);
|
||||
height: calc(100% - var(--zen-toolbox-padding) * 2);
|
||||
width: calc(100% - var(--zen-toolbox-padding) * 2.5);
|
||||
height: calc(100% - 8px);
|
||||
}
|
||||
|
||||
:root[zen-private-window] & {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"brandShortName": "Zen",
|
||||
"brandFullName": "Zen Browser",
|
||||
"release": {
|
||||
"displayVersion": "1.18.5b",
|
||||
"displayVersion": "1.18.7b",
|
||||
"github": {
|
||||
"repo": "zen-browser/desktop"
|
||||
},
|
||||
@@ -54,4 +54,4 @@
|
||||
"licenseType": "MPL-2.0"
|
||||
},
|
||||
"updateHostname": "updates.zen-browser.app"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user