fix: Start working on more eslint rules, p=#11874

* fix: Start working on more eslint rules, b=no-bug, c=common, mods, workspaces

* chore: Continue migration, b=no-bug, c=workflows, windows, glance, mods, welcome, workspaces, common, compact-mode, folders, tests, kbs, media, split-view, tabs

* chore: Finish, b=no-bug, c=common, compact-mode, folders, glance, tests, kbs, media, mods, split-view, tabs, workspaces, welcome

* fix: Fix installing deps, b=no-bug, c=common

* feat: Dont initialize git on download checks, b=no-bug, c=workflows

* feat: Remove empty JS docs, b=no-bug, c=common, compact-mode, folders, glance, kbs, media, mods, split-view, tabs, tests, workspaces

* chore: Run lint, b=no-bug, c=common, folders, glance, kbs, mods, split-view, tabs, workspaces
This commit is contained in:
mr. m
2026-01-12 15:11:43 +01:00
committed by GitHub
parent fd82ad95b7
commit 37eed5fcfe
200 changed files with 15153 additions and 12664 deletions

View File

@@ -3,56 +3,56 @@ policy:
- id: [component]
label:
# Make sure it's in sync with the dropdown in the issue template
- name: 'component: sync'
keys: ['Sync']
- name: 'component: compact-mode'
keys: ['Compact Mode']
- name: 'component: workspaces'
keys: ['Workspaces']
- name: 'component: mods-themes'
keys: ['Mods / Themes']
- name: 'component: bookmarks'
keys: ['Bookmarks']
- name: 'component: glance'
keys: ['Glance']
- name: 'component: url-bar'
keys: ['URL Bar']
- name: 'component: tabs'
keys: ['Tabs']
- name: 'component: settings'
keys: ['Settings']
- name: 'component: privacy'
keys: ['Privacy']
- name: 'component: split-view'
keys: ['Split View']
- name: 'component: performance'
keys: ['Performance']
- name: 'component: media-controller'
keys: ['Media Controler']
- name: 'component: tab-unloading'
keys: ['Tab unloading']
- name: 'component: tab-folders'
keys: ['Tab Folders']
- name: 'component: keyboard-shortcuts'
keys: ['Keyboard Shortcuts']
- name: 'component: security'
keys: ['Security']
- name: 'component: extensions'
keys: ['Extensions']
- name: 'component: customizable-ui-toolbars'
keys: ['Customizable UI / Toolbars']
- name: 'component: localization'
keys: ['Localization']
- name: 'component: other'
keys: ['Other']
- name: "component: sync"
keys: ["Sync"]
- name: "component: compact-mode"
keys: ["Compact Mode"]
- name: "component: workspaces"
keys: ["Workspaces"]
- name: "component: mods-themes"
keys: ["Mods / Themes"]
- name: "component: bookmarks"
keys: ["Bookmarks"]
- name: "component: glance"
keys: ["Glance"]
- name: "component: url-bar"
keys: ["URL Bar"]
- name: "component: tabs"
keys: ["Tabs"]
- name: "component: settings"
keys: ["Settings"]
- name: "component: privacy"
keys: ["Privacy"]
- name: "component: split-view"
keys: ["Split View"]
- name: "component: performance"
keys: ["Performance"]
- name: "component: media-controller"
keys: ["Media Controler"]
- name: "component: tab-unloading"
keys: ["Tab unloading"]
- name: "component: tab-folders"
keys: ["Tab Folders"]
- name: "component: keyboard-shortcuts"
keys: ["Keyboard Shortcuts"]
- name: "component: security"
keys: ["Security"]
- name: "component: extensions"
keys: ["Extensions"]
- name: "component: customizable-ui-toolbars"
keys: ["Customizable UI / Toolbars"]
- name: "component: localization"
keys: ["Localization"]
- name: "component: other"
keys: ["Other"]
- id: [platform]
block-list: ['Other']
block-list: ["Other"]
label:
# Make sure it's in sync with the dropdown in the issue template
- name: 'platform: linux'
keys: ['Linux (AppImage)', 'Linux (Flatpak)', 'Linux (Tarball)']
- name: 'platform: macOS'
keys: ['macOS - aarch64', 'macOS - Intel']
- name: 'platform: windows'
keys: ['Windows - x64', 'Windows - aarch64']
- name: "platform: linux"
keys: ["Linux (AppImage)", "Linux (Flatpak)", "Linux (Tarball)"]
- name: "platform: macOS"
keys: ["macOS - aarch64", "macOS - Intel"]
- name: "platform: windows"
keys: ["Windows - x64", "Windows - aarch64"]

View File

@@ -4,49 +4,49 @@ on:
workflow_dispatch:
inputs:
create_release:
description: 'Create a new release for this build'
description: "Create a new release for this build"
required: false
default: false
type: 'boolean'
type: "boolean"
update_version:
description: 'Update the version number'
description: "Update the version number"
required: false
default: false
type: 'boolean'
type: "boolean"
update_branch:
description: 'Update branch with new version'
description: "Update branch with new version"
required: true
default: 'release'
type: 'choice'
default: "release"
type: "choice"
options:
- 'release'
- 'twilight'
- "release"
- "twilight"
use-sccache:
description: 'Use sccache'
description: "Use sccache"
required: true
type: 'boolean'
type: "boolean"
default: false
workflow_call:
inputs:
create_release:
description: 'Create a new release for this build'
description: "Create a new release for this build"
required: false
default: false
type: 'boolean'
type: "boolean"
update_version:
description: 'Update the version number'
description: "Update the version number"
required: false
default: false
type: 'boolean'
type: "boolean"
update_branch:
description: 'Update branch with new version'
description: "Update branch with new version"
required: true
default: 'release'
type: 'string'
default: "release"
type: "string"
use-sccache:
description: 'Use sccache'
description: "Use sccache"
required: true
type: 'boolean'
type: "boolean"
default: false
jobs:
@@ -142,7 +142,7 @@ jobs:
- name: Setup Node.js
uses: useblacksmith/setup-node@v5
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Install dependencies
run: |
@@ -202,7 +202,7 @@ jobs:
- name: Setup Node.js
uses: useblacksmith/setup-node@v5
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Install dependencies
run: |
@@ -244,7 +244,7 @@ jobs:
- name: Setup Node.js
uses: useblacksmith/setup-node@v5
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Setup Git
run: |
@@ -379,7 +379,7 @@ jobs:
- name: Setup Node.js
uses: useblacksmith/setup-node@v5
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Install dependencies
run: |
@@ -565,8 +565,8 @@ jobs:
./zen.installer.exe/*
./zen.installer-arm64.exe/*
./zen.macos-universal.dmg/*
tag_name: 'twilight'
name: 'Twilight build - ${{ needs.build-data.outputs.version }} (${{ needs.build-data.outputs.build_date }} at ${{ needs.build-data.outputs.build_time }})'
tag_name: "twilight"
name: "Twilight build - ${{ needs.build-data.outputs.version }} (${{ needs.build-data.outputs.build_date }} at ${{ needs.build-data.outputs.build_time }})"
draft: false
generate_release_notes: false
prerelease: true
@@ -584,7 +584,7 @@ jobs:
prerelease: false
fail_on_unmatched_files: false
generate_release_notes: false
name: 'Release build - ${{ needs.build-data.outputs.version }} (${{ needs.build-data.outputs.build_date }})'
name: "Release build - ${{ needs.build-data.outputs.version }} (${{ needs.build-data.outputs.build_date }})"
body_path: release_notes.md
files: |
./zen.source.tar.zst/*
@@ -656,7 +656,7 @@ jobs:
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: '[release]: Update Flatpak manifest'
commit_message: "[release]: Update Flatpak manifest"
commit_user_name: Zen Browser Robot
commit_user_email: zen-browser-auto@users.noreply.github.com
repository: ./flatpak

View File

@@ -2,7 +2,7 @@ name: Check Firefox Candidate Release
on:
schedule:
- cron: '59 4 * * 2'
- cron: "59 4 * * 2"
workflow_dispatch:
permissions:

View File

@@ -5,9 +5,6 @@ on:
branches:
- dev
workflow_call:
pull_request:
branches:
- dev
permissions:
contents: read
@@ -25,7 +22,13 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Download Firefox
env:
ZEN_DOWNLOAD_DONT_INIT_GIT: "1"
run: |
npm run download
- name: Setup and run autopep8
if: ${{ contains(join(github.event.commits.*.modified, ' '), '.py') || contains(join(github.event.commits.*.added, ' '), '.py') || contains(join(github.event.commits.*.removed, ' '), '.py') }}

View File

@@ -5,7 +5,7 @@ permissions:
on:
workflow_dispatch:
schedule:
- cron: '3 2 1 * *'
- cron: "3 2 1 * *"
jobs:
build:
@@ -47,7 +47,7 @@ jobs:
GH_TOKEN: ${{ secrets.DEPLOY_KEY }}
HIDE_AUTHOR: true
HIDE_TIME_TO_ANSWER: true
SEARCH_QUERY: 'repo:zen-browser/desktop is:issue created:${{ env.last_month }}'
SEARCH_QUERY: "repo:zen-browser/desktop is:issue created:${{ env.last_month }}"
- name: Move metrics to docs folder
run: |
@@ -62,6 +62,6 @@ jobs:
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: 'docs: Update monthly issue metrics, b=(no bug), c={docs}'
commit_message: "docs: Update monthly issue metrics, b=(no bug), c={docs}"
commit_user_name: Zen Browser Robot
commit_user_email: zen-browser-auto@users.noreply.github.com

View File

@@ -4,19 +4,19 @@ on:
workflow_call:
inputs:
build-version:
description: 'The version to build'
description: "The version to build"
required: true
type: string
release-branch:
description: 'The branch to build'
description: "The branch to build"
required: true
type: string
MOZ_BUILD_DATE:
type: string
required: true
default: ''
default: ""
use-sccache:
description: 'Use sccache'
description: "Use sccache"
required: true
type: boolean
default: false
@@ -53,7 +53,7 @@ jobs:
- name: Setup Node.js
uses: useblacksmith/setup-node@v5
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@main

View File

@@ -6,19 +6,19 @@ on:
workflow_call:
inputs:
build-version:
description: 'The version to build'
description: "The version to build"
required: true
type: string
release-branch:
description: 'The branch to build'
description: "The branch to build"
required: true
type: string
MOZ_BUILD_DATE:
type: string
required: true
default: ''
default: ""
use-sccache:
description: 'Use sccache'
description: "Use sccache"
required: true
type: boolean
default: false
@@ -47,7 +47,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@main

View File

@@ -4,18 +4,18 @@ on:
workflow_call:
inputs:
build-version:
description: 'The version to build'
description: "The version to build"
required: true
type: string
release-branch:
description: 'The branch to build'
description: "The branch to build"
required: true
type: string
jobs:
mac-build:
name: Unify macOS (Universal)
runs-on: 'macos-26'
runs-on: "macos-26"
strategy:
fail-fast: false
@@ -30,7 +30,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Setup Python
uses: actions/setup-python@v5

View File

@@ -19,13 +19,18 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Install dependencies
run: npm ci
- name: Download Firefox and dependencies
env:
ZEN_DOWNLOAD_DONT_INIT_GIT: "1"
run: npm run download
- name: Import patches
run: npm run import
- name: Run linting
run: npm run lint

View File

@@ -4,14 +4,14 @@ on:
workflow_dispatch:
inputs:
release_candidate:
description: 'Set to true to sync release candidates'
description: "Set to true to sync release candidates"
required: false
type: boolean
default: false
workflow_call:
inputs:
release_candidate:
description: 'Set to true to sync release candidates'
description: "Set to true to sync release candidates"
required: false
type: boolean
default: false
@@ -41,7 +41,7 @@ jobs:
uses: actions/setup-node@v4
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Install dependencies
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
@@ -119,9 +119,9 @@ jobs:
GIT_CURL_VERBOSE: 1
with:
token: ${{ secrets.DEPLOY_KEY }}
commit-message: 'chore: Sync upstream to `Firefox ${{ steps.build-data.outputs.version }}`'
branch: 'chore/upstream-sync'
title: 'Sync upstream Firefox to version ${{ steps.build-data.outputs.version }}'
commit-message: "chore: Sync upstream to `Firefox ${{ steps.build-data.outputs.version }}`"
branch: "chore/upstream-sync"
title: "Sync upstream Firefox to version ${{ steps.build-data.outputs.version }}"
body: |
This PR syncs the upstream Firefox to version ${{ steps.build-data.outputs.version }}.

View File

@@ -2,11 +2,11 @@ name: Zen Twilight Scheduled Releases
on:
schedule:
- cron: '0 23 * * *'
- cron: "0 23 * * *"
workflow_dispatch:
inputs:
create_release:
description: 'Whether to do a release'
description: "Whether to do a release"
required: false
type: boolean
default: true
@@ -34,4 +34,4 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"

View File

@@ -7,15 +7,15 @@ on:
workflow_call:
inputs:
build-version:
description: 'The version to build'
description: "The version to build"
required: true
type: string
profile-data-path-archive:
description: 'The path to the zip archive containing the profile data'
description: "The path to the zip archive containing the profile data"
required: false
type: string
release-branch:
description: 'The branch to build'
description: "The branch to build"
required: true
type: string
@@ -37,7 +37,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Setup Git
run: |

View File

@@ -11,22 +11,22 @@ on:
type: boolean
default: false
build-version:
description: 'The version to build'
description: "The version to build"
required: true
type: string
profile-data-path-archive:
description: 'The path to the zip archive containing the profile data'
description: "The path to the zip archive containing the profile data"
type: string
release-branch:
description: 'The branch to build'
description: "The branch to build"
required: true
type: string
MOZ_BUILD_DATE:
type: string
required: true
default: ''
default: ""
use-sccache:
description: 'Use sccache'
description: "Use sccache"
required: true
type: boolean
default: false
@@ -62,7 +62,7 @@ jobs:
- name: Setup Node.js
uses: useblacksmith/setup-node@v5
with:
node-version-file: '.nvmrc'
node-version-file: ".nvmrc"
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@main

View File

@@ -2,11 +2,19 @@
"bracketSameLine": true,
"endOfLine": "lf",
"trailingComma": "es5",
"singleQuote": true,
"tabWidth": 2,
"useTabs": false,
"jsxSingleQuote": false,
"semi": true,
"printWidth": 100,
"plugins": ["prettier-plugin-sh"]
"plugins": ["prettier-plugin-sh"],
"overrides": [
{
"files": "*.css",
"options": {
"parser": "css",
"printWidth": 160
}
}
]
}

View File

@@ -2,13 +2,13 @@
// 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/.
const fs = require('fs');
const MJS_FILES = ['src/zen/split-view/ZenViewSplitter.ts'];
const fs = require("fs");
const MJS_FILES = ["src/zen/split-view/ZenViewSplitter.ts"];
for (const file of MJS_FILES) {
const code = fs.readFileSync(file, 'utf8');
require('@babel/core').transformSync(code, {
presets: ['@babel/preset-typescript'],
const code = fs.readFileSync(file, "utf8");
require("@babel/core").transformSync(code, {
presets: ["@babel/preset-typescript"],
filename: file,
});
}

View File

@@ -1,27 +1,492 @@
// 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/.
/* 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 js from '@eslint/js';
import globals from 'globals';
import { defineConfig, globalIgnores } from 'eslint/config';
import zenGlobals from './src/zen/zen.globals.js';
import sdl from "@microsoft/eslint-plugin-sdl";
import eslintConfigPrettier from "eslint-config-prettier/flat";
import html from "eslint-plugin-html";
import importPlugin from "eslint-plugin-import";
import json from "@eslint/json";
import lit from "eslint-plugin-lit";
import mozilla from "eslint-plugin-mozilla";
import reactHooks from "eslint-plugin-react-hooks";
import zenGlobals from "./src/zen/zen.globals.mjs";
export default defineConfig([
import fs from "fs";
import globals from "globals";
import path from "path";
import globalIgnores from "./engine/eslint-ignores.config.mjs";
import testPathsConfig from "./engine/eslint-test-paths.config.mjs";
import repositoryGlobals from "./engine/eslint-file-globals.config.mjs";
import rollouts from "./engine/eslint-rollouts.config.mjs";
import subdirConfigs from "./engine/eslint-subdirs.config.mjs";
const testPaths = testPathsConfig.testPaths;
function readFile(filePath) {
return fs
.readFileSync(filePath, { encoding: "utf-8" })
.split("\n")
.filter((p) => p && !p.startsWith("#"));
}
const httpTestingPaths = [
`**/*mixedcontent*.{${mozilla.allFileExtensions.join(",")}}`,
`**/*CrossOrigin*.{${mozilla.allFileExtensions.join(",")}}`,
`**/*crossorigin*.{${mozilla.allFileExtensions.join(",")}}`,
`**/*cors*.{${mozilla.allFileExtensions.join(",")}}`,
`**/*downgrade*.{${mozilla.allFileExtensions.join(",")}}`,
`**/*Downgrade*.{${mozilla.allFileExtensions.join(",")}}`,
];
globals.browser = {
...globals.browser,
...zenGlobals.reduce((obj, key) => {
obj[key] = "readonly";
return obj;
}, {}),
};
testPaths.browser = testPaths.browser.concat("src/zen/tests/");
/**
* Takes each path in the paths array, and expands it with the list of extensions
* that ESLint is watching.
*
* @param {object} options
* @param {string[]} options.paths
* The list of paths to wrap.
* @param {string[]} [options.excludedExtensions]
* The list of extensions to be excluded from the wrapping.
*/
function wrapPaths({ paths, excludedExtensions }) {
let extensions = excludedExtensions
? mozilla.allFileExtensions.filter((f) => !excludedExtensions.includes(f))
: mozilla.allFileExtensions;
return paths.map((p) => {
if (p.endsWith("**")) {
return p + `/*.{${extensions.join(",")}}`;
}
if (p.endsWith("/")) {
return p + `**/*.{${extensions.join(",")}}`;
}
if (p.endsWith("*")) {
return p + `.{${extensions.join(",")}}`;
}
return p;
});
}
/**
* Wraps the paths listed in the files section of a configuration with the
* file extensions that ESLint is watching.
*
* @param {object} configs
*/
function wrapPathsInConfig(configs) {
for (let config of configs) {
// add "engine/" to the paths in the files section.
config.files = wrapPaths({ paths: config.files.map((p) => "engine/" + p) });
}
return configs;
}
let config = [
{
files: ['**/*.{js,mjs,cjs}'],
plugins: { js },
extends: ['js/recommended'],
languageOptions: {
globals: {
...globals.browser,
...zenGlobals.reduce((acc, global) => {
acc[global] = 'readable';
return acc;
}, {}),
name: "import-plugin-settings",
settings: {
"import/extensions": [".mjs"],
"import/resolver": {
[path.resolve(import.meta.dirname, "engine", "srcdir-resolver.js")]: {},
node: {},
},
},
ignores: ['**/vendor/**', '**/tests/**'],
},
globalIgnores(['**/mochitests/**']),
]);
{
name: "ignores",
ignores: [...globalIgnores, "src/zen/vendor/*"],
},
{
name: "all-files",
files: wrapPaths({ paths: ["**"] }),
linterOptions: {
// With this option on, if an inline comment disables a rule, and the
// rule is able to be automatically fixed, then ESLint will remove the
// inline comment and apply the fix. We don't want this because we have
// some rules that intentionally need to be turned off in specific cases,
// e.g. @microsoft/sdl/no-insecure-url.
reportUnusedDisableDirectives: "off",
},
plugins: { lit },
rules: {
"lit/quoted-expressions": ["error", "never"],
"lit/no-invalid-html": "error",
"lit/no-value-attribute": "error",
},
},
{
name: "source-type-script",
files: ["**/*.{js,json,html,sjs,xhtml}"],
languageOptions: {
sourceType: "script",
},
},
...mozilla.configs["flat/recommended"],
{
name: "json-recommended-with-comments",
files: ["**/*.json"],
language: "json/jsonc",
...json.configs.recommended,
},
{
name: "json-recommended-no-comments",
files: ["**/package.json"],
language: "json/json",
...json.configs.recommended,
},
{
name: "json-empty-keys-off-for-image_builder",
files: ["taskcluster/docker/image_builder/policy.json"],
rules: {
"json/no-empty-keys": "off",
},
},
{
name: "eslint-plugin-html",
files: ["**/*.html", "**/*.xhtml"],
plugins: { html },
},
{
name: "define-globals-for-browser-env",
// Not available for sjs files.
files: wrapPaths({ paths: ["**"], excludedExtensions: ["sjs"] }),
ignores: [
// Also not available for various other scopes and tools.
"**/*.sys.mjs",
"**/?(*.)worker.?(m)js",
"**/?(*.)serviceworker.?(m)js",
...wrapPaths({
paths: testPaths.xpcshell,
excludedExtensions: ["mjs", "sjs"],
}),
"tools/lint/eslint/**",
],
languageOptions: {
globals: globals.browser,
},
},
{
// Generally we assume that all files, except mjs ones are in our
// privileged and specific environment. mjs are handled separately by
// the recommended configuration in eslint-plugin-mozilla.
name: "define-privileged-and-specific-globals-for-most-files",
files: wrapPaths({ paths: ["**"], excludedExtensions: ["json"] }),
ignores: ["browser/components/storybook/**", "tools"],
languageOptions: {
globals: {
...mozilla.environments.privileged.globals,
...mozilla.environments.specific.globals,
},
},
},
{
name: "define-globals-for-node-files",
files: [
// All .eslintrc.mjs files are in the node environment, so turn that
// on here.
"**/.eslintrc*.mjs",
// .js files in the top-level are generally assumed to be node.
"\.*.js",
// *.config.js files are generally assumed to be configuration files
// based for node.
"**/*.config.js",
// The resolver for moz-src for eslint, vscode etc.
"engine/srcdir-resolver.js",
],
languageOptions: {
globals: { ...globals.node, ...mozilla.turnOff(globals.browser) },
},
},
{
name: "browser-no-more-globals",
files: ["browser/base/content/browser.js"],
rules: {
"mozilla/no-more-globals": "error",
},
},
{
name: "jsx-files",
files: ["**/*.jsx", "browser/components/storybook/.storybook/**/*.mjs"],
languageOptions: {
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
},
{
name: "eslint-plugin-import-rules",
files: ["**/*.mjs"],
plugins: { import: importPlugin },
rules: {
"import/default": "error",
"import/export": "error",
"import/named": "error",
"import/namespace": "error",
"import/newline-after-import": "error",
"import/no-duplicates": "error",
"import/no-absolute-path": "error",
"import/no-named-default": "error",
"import/no-named-as-default": "error",
"import/no-named-as-default-member": "error",
"import/no-self-import": "error",
"import/no-unassigned-import": "error",
"import/no-unresolved": [
"error",
// Bug 1773473 - Ignore resolver URLs for chrome and resource as we
// do not yet have a resolver for them.
{ ignore: ["chrome://", "resource://"] },
],
"import/no-useless-path-segments": "error",
},
},
{
name: "turn-off-unassigned-import-for-stories",
// Turn off no-unassigned-import for files that typically test our
// custom elements, which are imported for the side effects (ie
// the custom element being registered) rather than any particular
// export:
files: ["**/*.stories.mjs"],
plugins: { import: importPlugin },
rules: {
"import/no-unassigned-import": "off",
},
},
{
...mozilla.configs["flat/general-test"],
files: wrapPaths({ paths: ["**/test/**", "**/tests/**"] }),
},
{
...mozilla.configs["flat/xpcshell-test"],
files: wrapPaths({
paths: testPaths.xpcshell,
excludedExtensions: ["mjs", "sjs"],
}),
},
{
name: "no-unused-vars-disable-on-headjs",
// If it is an xpcshell head file, we turn off global unused variable checks, as it
// would require searching the other test files to know if they are used or not.
// This would be expensive and slow, and it isn't worth it for head files.
// We could get developers to declare as exported, but that doesn't seem worth it.
files: testPaths.xpcshell.map((filePath) => `${filePath}head*.js`),
rules: {
"no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
caughtErrors: "none",
vars: "local",
},
],
},
},
{
name: "no-unused-vars-for-xpcshell",
// This section enables errors of no-unused-vars globally for all test*.js
// files in xpcshell test paths.
// This is not done in the xpcshell-test configuration as we cannot pull
// in overrides from there. We should at some stage, aim to enable this
// for all files in xpcshell-tests.
files: testPaths.xpcshell.map((filePath) => `${filePath}test*.js`),
rules: {
// No declaring variables that are never used
"no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
caughtErrors: "none",
vars: "all",
},
],
},
},
{
...mozilla.configs["flat/browser-test"],
files: wrapPaths({
paths: testPaths.browser,
excludedExtensions: ["mjs", "sjs"],
}),
},
{
...mozilla.configs["flat/mochitest-test"],
files: wrapPaths({
paths: testPaths.mochitest,
excludedExtensions: ["mjs"],
}),
ignores: ["security/manager/ssl/tests/mochitest/browser/**"],
},
{
...mozilla.configs["flat/chrome-test"],
files: wrapPaths({
paths: testPaths.chrome,
excludedExtensions: ["mjs", "sjs"],
}),
},
{
name: "simpletest",
languageOptions: {
globals: {
...mozilla.environments.simpletest.globals,
},
},
files: [
...testPaths.mochitest.map((filePath) => `${filePath}/**/*.js`),
...testPaths.chrome.map((filePath) => `${filePath}/**/*.js`),
],
},
{
name: "multiple-test-kinds",
// Some directories have multiple kinds of tests, and some rules
// don't work well for HTML-based mochitests, so disable those.
files: testPaths.xpcshell
.concat(testPaths.browser)
.map((filePath) => [`${filePath}/**/*.html`, `${filePath}/**/*.xhtml`])
.flat(),
rules: {
// plain/chrome mochitests don't automatically include Assert, so
// autofixing `ok()` to Assert.something is bad.
"mozilla/no-comparison-or-assignment-inside-ok": "off",
},
},
{
name: "test-file-reuse",
// Some directories reuse `test_foo.js` files between mochitest-plain and
// unit tests, or use custom postMessage-based assertion propagation into
// browser tests. Ignore those too:
files: wrapPaths({
paths: [
// Reuses xpcshell unit test scripts in mochitest-plain HTML files.
"dom/indexedDB/test/**",
// Dispatches functions to the webpage in ways that are hard to detect.
"toolkit/components/antitracking/test/**",
],
}),
rules: {
"mozilla/no-comparison-or-assignment-inside-ok": "off",
},
},
{
// Rules of Hooks broadly checks for camelCase "use" identifiers, so
// enable only for paths actually using React to avoid false positives.
name: "react-hooks",
files: [
"browser/components/aboutwelcome/**",
"browser/components/asrouter/**",
"browser/extensions/newtab/**",
"devtools/**",
],
...reactHooks.configs["recommended-latest"],
plugins: { "react-hooks": reactHooks },
rules: {
// react-hooks/recommended has exhaustive-deps as a warning, we prefer
// errors, so that raised issues get addressed one way or the other.
"react-hooks/exhaustive-deps": "error",
},
},
{
name: "disable-no-insecure-url-for-http-testing",
// Exempt files with these paths since they have to use http for full coverage
files: httpTestingPaths,
plugins: { "@microsoft/sdl": sdl },
rules: {
"@microsoft/sdl/no-insecure-url": "off",
},
},
{
name: "mozilla/valid-jsdoc",
files: wrapPaths({ paths: ["**"] }),
...mozilla.configs["flat/valid-jsdoc"],
},
{
name: "mozilla/require-jsdoc",
files: wrapPaths({ paths: ["**"] }),
...mozilla.configs["flat/require-jsdoc"],
},
{
name: "rollout-no-browser-refs-in-toolkit",
files: ["toolkit/**"],
ignores: ["toolkit/**/test/**", "toolkit/**/tests/**"],
plugins: { mozilla },
rules: {
"mozilla/no-browser-refs-in-toolkit": "error",
},
},
{
name: "no-newtab-refs-outside-newtab",
files: ["**/*.mjs", "**/*.js", "**/*.sys.mjs"],
ignores: [
"tools/@types/generated/**",
"tools/lint/eslint/eslint-plugin-mozilla/lib/rules/no-newtab-refs-outside-newtab.mjs",
"tools/lint/eslint/eslint-plugin-mozilla/tests/no-newtab-refs-outside-newtab.mjs",
],
plugins: { mozilla },
rules: {
"mozilla/no-newtab-refs-outside-newtab": "error",
},
},
{
name: "jsdoc/require-jsdoc",
ignores: wrapPaths({ paths: ["**"] }),
...mozilla.configs["flat/jsdoc-require-jsdoc"],
},
...wrapPathsInConfig(subdirConfigs),
...wrapPathsInConfig(repositoryGlobals),
/**
* The items below should always be the last items in this order:
*
* - Enable eslint-config-prettier.
* - Enable curly.
* - Rollouts
*/
// Turn off rules that conflict with Prettier.
{ name: "eslint-config-prettier", ...eslintConfigPrettier },
{
name: "enable-curly",
files: wrapPaths({ paths: ["**/"] }),
rules: {
// Require braces around blocks that start a new line. This must be
// configured after eslint-config-prettier is included, as otherwise
// eslint-config-prettier disables the curly rule. Hence, we do
// not include it in
// `tools/lint/eslint/eslint-plugin-mozilla/lib/configs/recommended.js`.
curly: ["error", "all"],
},
},
...wrapPathsInConfig(rollouts),
];
// The various places we get our globals from use true/false rather than
// the strings required by ESLint, so translate those here.
config.map((entry) => {
if (entry.languageOptions?.globals) {
let newGlobals = {};
for (let [key, value] of Object.entries(entry.languageOptions.globals)) {
if (typeof entry.languageOptions.globals[key] == "boolean") {
newGlobals[key] = value ? "writable" : "readonly";
} else {
newGlobals[key] = value;
}
}
}
return entry;
});
export default config;

1098
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -51,20 +51,32 @@
"homepage": "https://github.com/zen-browser/desktop#readme",
"devDependencies": {
"@babel/preset-typescript": "^7.27.0",
"@eslint/js": "^9.32.0",
"@eslint/js": "^9.39.2",
"@eslint/json": "^0.14.0",
"@microsoft/eslint-plugin-sdl": "^1.1.0",
"@zen-browser/surfer": "^1.13.0",
"eslint": "^9.32.0",
"@zen-browser/surfer": "^1.13.1",
"eslint": "^9.39.2",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-eslint-plugin": "^7.3.0",
"eslint-plugin-html": "^8.1.3",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.12.1",
"eslint-plugin-json": "^4.0.1",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-lit": "^2.1.1",
"eslint-plugin-mozilla": "^4.3.3",
"eslint-plugin-no-unsanitized": "4.1.4",
"eslint-plugin-promise": "7.2.1",
"eslint-plugin-react": "7.37.5",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-spidermonkey-js": "file:tools/eslint-plugin-spidermonkey-js",
"formal-git": "^1.2.9",
"globals": "^16.3.0",
"husky": "^9.1.7",
"lint-staged": "^15.3.0",
"prettier": "^3.4.2",
"prettier-plugin-sh": "^0.14.0"
"prettier-plugin-sh": "^0.14.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.52.0"
}
}

View File

@@ -5,45 +5,45 @@
# Smooth scrolling and mousewheel tuning preferences
- name: general.smoothScroll.msdPhysics.enabled
condition: '!defined(XP_MACOSX)'
condition: "!defined(XP_MACOSX)"
value: true
- name: general.smoothScroll.currentVelocityWeighting
condition: '!defined(XP_MACOSX)'
value: '0.15'
condition: "!defined(XP_MACOSX)"
value: "0.15"
- name: general.smoothScroll.stopDecelerationWeighting
condition: '!defined(XP_MACOSX)'
value: '0.6'
condition: "!defined(XP_MACOSX)"
value: "0.6"
- name: mousewheel.min_line_scroll_amount
condition: '!defined(XP_MACOSX)'
condition: "!defined(XP_MACOSX)"
value: 10
- name: general.smoothScroll.mouseWheel.durationMinMS
condition: '!defined(XP_MACOSX)'
condition: "!defined(XP_MACOSX)"
value: 80
- name: general.smoothScroll.msdPhysics.continuousMotionMaxDeltaMS
condition: '!defined(XP_MACOSX)'
condition: "!defined(XP_MACOSX)"
value: 12
- name: general.smoothScroll.msdPhysics.motionBeginSpringConstant
condition: '!defined(XP_MACOSX)'
condition: "!defined(XP_MACOSX)"
value: 600
- name: general.smoothScroll.msdPhysics.regularSpringConstant
condition: '!defined(XP_MACOSX)'
condition: "!defined(XP_MACOSX)"
value: 650
- name: general.smoothScroll.msdPhysics.slowdownMinDeltaMS
condition: '!defined(XP_MACOSX)'
condition: "!defined(XP_MACOSX)"
value: 25
- name: general.smoothScroll.msdPhysics.slowdownSpringConstant
condition: '!defined(XP_MACOSX)'
condition: "!defined(XP_MACOSX)"
value: 250
- name: mousewheel.default.delta_multiplier_y
condition: '!defined(XP_MACOSX)'
condition: "!defined(XP_MACOSX)"
value: 200

View File

@@ -18,7 +18,7 @@
value: true
- name: browser.toolbars.bookmarks.visibility
value: 'never'
value: "never"
- name: widget.non-native-theme.scrollbar.style
value: 2
@@ -48,7 +48,7 @@
- name: app.update.checkInstallTime.days
value: 6
condition: 'defined(MOZILLA_OFFICIAL)'
condition: "defined(MOZILLA_OFFICIAL)"
- name: browser.profiles.enabled
value: false

View File

@@ -4,10 +4,10 @@
# Fullscreen API preferences
- name: full-screen-api.transition-duration.enter
value: '0 0'
value: "0 0"
- name: full-screen-api.transition-duration.leave
value: '0 0'
value: "0 0"
- name: full-screen-api.warning.delay
value: -1

View File

@@ -4,7 +4,7 @@
- name: browser.lowMemoryResponseMask
value: 3
condition: 'defined(XP_MACOSX)'
condition: "defined(XP_MACOSX)"
- name: network.predictor.enable-hover-on-ssl
value: true
@@ -14,6 +14,6 @@
# Make sure its in sync with:
# https://searchfox.org/firefox-main/rev/1477feb9706f4ccc5bd571c1c215832a6fbb7464/modules/libpref/init/StaticPrefList.yaml#7741-7748
- name: gfx.webrender.compositor
condition: 'defined(XP_WIN) || defined(XP_DARWIN)'
value: '@cond'
condition: "defined(XP_WIN) || defined(XP_DARWIN)"
value: "@cond"
mirror: once

View File

@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
- name: browser.privatebrowsing.vpnpromourl
value: ''
value: ""
locked: true
- name: extensions.getAddons.showPane

View File

@@ -20,7 +20,7 @@
locked: true
- name: toolkit.telemetry.server
value: 'data:,'
value: "data:,"
locked: true
- name: toolkit.telemetry.archive.enabled
@@ -56,7 +56,7 @@
locked: true
- name: toolkit.coverage.endpoint.base
value: ''
value: ""
locked: true
- name: browser.newtabpage.activity-stream.feeds.telemetry
@@ -84,7 +84,7 @@
value: false
- name: app.normandy.api_url
value: ''
value: ""
locked: true
# Crash Reports

View File

@@ -12,7 +12,7 @@
value: true
- name: zen.glance.activation-method
value: 'alt' # ctrl, alt, shift
value: "alt" # ctrl, alt, shift
- name: zen.glance.animation-duration
value: 350 # in milliseconds

View File

@@ -5,8 +5,8 @@
# GTK-specific preferences
- name: widget.gtk.rounded-bottom-corners.enabled
value: true
condition: 'defined(MOZ_WIDGET_GTK)'
condition: "defined(MOZ_WIDGET_GTK)"
- name: zen.widget.linux.transparency
value: false
condition: 'defined(MOZ_WIDGET_GTK)'
condition: "defined(MOZ_WIDGET_GTK)"

View File

@@ -5,7 +5,7 @@
# Enable transparent background for macos
- name: widget.macos.sidebar-blend-mode.behind-window
value: true
condition: 'defined(XP_MACOSX)'
condition: "defined(XP_MACOSX)"
# 1. hudWindow
# 2. fullScreenUI
@@ -16,7 +16,7 @@
# 7. underlay
- name: zen.widget.macos.window-material
value: 1
condition: 'defined(XP_MACOSX)'
condition: "defined(XP_MACOSX)"
cpptype: uint32_t
mirror: always
type: static

View File

@@ -9,8 +9,8 @@
value: 20 # In days
- name: zen.mods.auto-update
value: '@cond'
condition: 'defined(MOZILLA_OFFICIAL)'
value: "@cond"
condition: "defined(MOZILLA_OFFICIAL)"
- name: zen.rice.share.notice.accepted
value: false
@@ -18,10 +18,10 @@
# === Mark: Site Injection ===
- name: zen.injections.match-urls
value: 'https://zen-browser.app/*'
value: "https://zen-browser.app/*"
locked: true
condition: 'defined(MOZILLA_OFFICIAL)'
condition: "defined(MOZILLA_OFFICIAL)"
- name: zen.injections.match-urls
value: 'http://localhost/*'
condition: '!defined(MOZILLA_OFFICIAL)'
value: "http://localhost/*"
condition: "!defined(MOZILLA_OFFICIAL)"

View File

@@ -6,7 +6,7 @@
value: true
- name: zen.session-store.log
value: '@IS_TWILIGHT@'
value: "@IS_TWILIGHT@"
- name: zen.session-store.restore-unsynced-windows
value: true

View File

@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
- name: zen.theme.accent-color
value: 'AccentColor'
value: "AccentColor"
- name: zen.theme.content-element-separation
value: 8
@@ -30,11 +30,11 @@
value: true
- name: zen.theme.styled-status-panel
value: '@IS_TWILIGHT@'
value: "@IS_TWILIGHT@"
- name: zen.theme.styled-status-panel
value: true
condition: 'defined(XP_MACOSX)'
condition: "defined(XP_MACOSX)"
- name: zen.theme.hide-unified-extensions-button
value: true

View File

@@ -10,11 +10,11 @@
- name: zen.view.mac.show-three-dot-menu
value: false
condition: 'defined(XP_MACOSX)'
condition: "defined(XP_MACOSX)"
- name: zen.widget.mac.mono-window-controls
value: true
condition: 'defined(XP_MACOSX)'
condition: "defined(XP_MACOSX)"
- name: zen.view.use-single-toolbar
value: true
@@ -41,7 +41,7 @@
value: 2
- name: zen.view.context-menu.refresh
value: '@IS_TWILIGHT@'
value: "@IS_TWILIGHT@"
- name: zen.view.borderless-fullscreen
value: true

View File

@@ -3,6 +3,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
- name: zen.watermark.enabled
value: '@cond'
value: "@cond"
sticky: true
condition: 'defined(MOZILLA_OFFICIAL)'
condition: "defined(MOZILLA_OFFICIAL)"

View File

@@ -3,6 +3,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
- name: zen.welcome-screen.seen
value: '@cond'
value: "@cond"
sticky: true
condition: '!defined(MOZILLA_OFFICIAL)'
condition: "!defined(MOZILLA_OFFICIAL)"

View File

@@ -5,15 +5,15 @@
# Mica effect preferences for Windows
- name: widget.windows.mica
value: true
condition: 'defined(XP_WIN)'
condition: "defined(XP_WIN)"
- name: widget.windows.mica.popups
value: true
condition: 'defined(XP_WIN)'
condition: "defined(XP_WIN)"
# 1 = DWMSBT_MAINWINDOW
# 2 = DWMSBT_TRANSIENTWINDOW (default, also used for popups)
# 3 = DWMSBT_TABBEDWINDOW
- name: widget.windows.mica.toplevel-backdrop
value: 2
condition: 'defined(XP_WIN)'
condition: "defined(XP_WIN)"

View File

@@ -36,8 +36,8 @@
value: 10
- name: zen.workspaces.debug
value: '@cond'
condition: '!defined(MOZILLA_OFFICIAL)' # Section: Pinned tabs management
value: "@cond"
condition: "!defined(MOZILLA_OFFICIAL)" # Section: Pinned tabs management
- name: zen.pinned-tab-manager.wheel-close-if-pending
value: true

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
@namespace html 'http://www.w3.org/1999/xhtml';
@namespace html "http://www.w3.org/1999/xhtml";
:root {
--in-content-box-background: var(--zen-colors-tertiary) !important;
@@ -78,7 +78,7 @@ groupbox h2 {
}
#categories > .category[selected]::before {
content: '';
content: "";
display: block;
height: 70%;
width: 5px;
@@ -89,7 +89,7 @@ groupbox h2 {
}
#languagesGroup::before {
content: '';
content: "";
display: block;
height: 1px;
background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));
@@ -123,7 +123,7 @@ groupbox h2 {
}
#category-zen-looks > .category-icon {
list-style-image: url('chrome://browser/skin/customize.svg');
list-style-image: url("chrome://browser/skin/customize.svg");
}
#zenLooksAndFeelGroup > html|div:last-of-type {
@@ -176,7 +176,7 @@ groupbox h2 {
/* Workspace */
#category-zen-tabs-management > .category-icon {
list-style-image: url('chrome://browser/skin/window.svg');
list-style-image: url("chrome://browser/skin/window.svg");
}
#zenTabsUnloadDelayContainer {
@@ -191,7 +191,7 @@ groupbox h2 {
/* CKS */
#category-zen-CKS > .category-icon {
list-style-image: url('chrome://browser/skin/quickactions.svg');
list-style-image: url("chrome://browser/skin/quickactions.svg");
}
.zenCKSOption-input {
@@ -325,7 +325,7 @@ groupbox h2 {
/* THemes marketplace */
#category-zen-marketplace > .category-icon {
list-style-image: url('chrome://mozapps/skin/extensions/category-themes.svg');
list-style-image: url("chrome://mozapps/skin/extensions/category-themes.svg");
}
.zenThemeMarketplaceItem {
@@ -347,14 +347,14 @@ groupbox h2 {
cursor: pointer;
&::before {
content: '';
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 18px;
height: 18px;
background-image: url('chrome://browser/skin/home.svg');
background-image: url("chrome://browser/skin/home.svg");
background-repeat: no-repeat;
background-size: cover;
fill: currentColor;
@@ -369,14 +369,14 @@ groupbox h2 {
cursor: pointer;
&::before {
content: '';
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 15px;
height: 15px;
background-image: url('chrome://global/skin/icons/settings.svg');
background-image: url("chrome://global/skin/icons/settings.svg");
background-repeat: no-repeat;
background-size: cover;
}
@@ -493,7 +493,7 @@ groupbox h2 {
font-weight: 600;
cursor: pointer;
&[disabled='true'] {
&[disabled="true"] {
opacity: 0.7;
cursor: not-allowed;
}
@@ -532,16 +532,16 @@ groupbox h2 {
.sync-engine-workspaces .checkbox-icon,
.sync-engine-workspaces.sync-engine-image {
list-style-image: url('chrome://devtools/skin/images/tool-storage.svg');
list-style-image: url("chrome://devtools/skin/images/tool-storage.svg");
}
@media -moz-pref('zen.theme.disable-lightweight') {
html|div[data-l10n-id='preferences-web-appearance-footer'] {
@media -moz-pref("zen.theme.disable-lightweight") {
html|div[data-l10n-id="preferences-web-appearance-footer"] {
display: none;
}
}
@media -moz-pref('zen.urlbar.replace-newtab') {
@media -moz-pref("zen.urlbar.replace-newtab") {
#category-home {
display: none !important;
}

File diff suppressed because it is too large Load Diff

View File

@@ -22,7 +22,7 @@
/// <reference types="./lib.gecko.esnext.d.ts" />
/// <reference types="./lib.gecko.tweaks.d.ts" />
import type {} from './lib.gecko.augmentations.d.ts';
import type {} from "./lib.gecko.augmentations.d.ts";
declare global {
const Cc: nsXPCComponents_Classes;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@ interface CanonicalBrowsingContext extends LoadContextMixin {
}
declare namespace ChromeUtils {
type Modules = import('./generated/lib.gecko.modules').Modules;
type Modules = import("./generated/lib.gecko.modules").Modules;
function importESModule<T extends keyof Modules>(
aResourceURI: T,
@@ -29,15 +29,15 @@ interface ChromeWindow extends Window {
}
interface Document {
createXULElement(name: 'browser'): MozBrowser;
createXULElement(name: "browser"): MozBrowser;
}
type nsIGleanPingNoReason = {
[K in keyof nsIGleanPing]: K extends 'submit' ? (_?: never) => void : nsIGleanPing[K];
[K in keyof nsIGleanPing]: K extends "submit" ? (_?: never) => void : nsIGleanPing[K];
};
type nsIGleanPingWithReason<T> = {
[K in keyof nsIGleanPing]: K extends 'submit' ? (reason: T) => void : nsIGleanPing[K];
[K in keyof nsIGleanPing]: K extends "submit" ? (reason: T) => void : nsIGleanPing[K];
};
interface MessageListenerManagerMixin {

View File

@@ -18,8 +18,8 @@
/// <reference types="../lib.gecko.tweaks.d.ts" />
/// <reference types="../lib.gecko.nsresult.d.ts" />
declare var window: Window;
declare var Components: nsIXPCComponents;
declare let window: Window;
declare let Components: nsIXPCComponents;
declare var Cu: nsIXPCComponents_Utils;
declare var Ci: nsIXPCComponents_Interfaces;
declare var Services: JSServices;
@@ -72,10 +72,10 @@ declare namespace MockedExports {
* This interface teaches ChromeUtils.importESModule how to find modules.
*/
interface KnownModules {
Services: typeof import('Services');
'resource://gre/modules/AppConstants.sys.mjs': typeof import('resource://gre/modules/AppConstants.sys.mjs');
'resource:///modules/CustomizableUI.sys.mjs': typeof import('resource:///modules/CustomizableUI.sys.mjs');
'resource:///modules/CustomizableWidgets.sys.mjs': typeof import('resource:///modules/CustomizableWidgets.sys.mjs');
Services: typeof import("Services");
"resource://gre/modules/AppConstants.sys.mjs": typeof import("resource://gre/modules/AppConstants.sys.mjs");
"resource:///modules/CustomizableUI.sys.mjs": typeof import("resource:///modules/CustomizableUI.sys.mjs");
"resource:///modules/CustomizableWidgets.sys.mjs": typeof import("resource:///modules/CustomizableWidgets.sys.mjs");
}
interface ChromeUtils {
@@ -166,7 +166,7 @@ declare namespace MockedExports {
type PrefObserverFunction = (
aSubject: nsIPrefBranch,
aTopic: 'nsPref:changed',
aTopic: "nsPref:changed",
aData: string
) => unknown;
type PrefObserver = PrefObserverFunction | { observe: PrefObserverFunction };
@@ -310,7 +310,7 @@ declare namespace MockedExports {
}
interface Cc {
'@mozilla.org/filepicker;1': {
"@mozilla.org/filepicker;1": {
createInstance(instance: nsIFilePicker): FilePicker;
};
}
@@ -339,17 +339,17 @@ interface PathUtilsInterface {
isAbsolute: (path: string) => boolean;
}
declare module 'Services' {
declare module "Services" {
export = MockedExports.Services;
}
declare module 'ChromeUtils' {
declare module "ChromeUtils" {
export = ChromeUtils;
}
declare var ChromeUtils: MockedExports.ChromeUtils;
declare let ChromeUtils: MockedExports.ChromeUtils;
declare var PathUtils: PathUtilsInterface;
declare let PathUtils: PathUtilsInterface;
// These global objects can be used directly in JSM files only.
declare var Cu: MockedExports.Cu;
@@ -365,7 +365,7 @@ declare interface ChromeDocument extends Document {
* Create a XUL element of a specific type. Right now this function
* only refines iframes, but more tags could be added.
*/
createXULElement: ((type: 'iframe') => XULIframeElement) & ((type: string) => XULElement);
createXULElement: ((type: "iframe") => XULIframeElement) & ((type: string) => XULElement);
/**
* This is a fluent instance connected to this document.
@@ -400,7 +400,7 @@ declare interface Window {
browsingContext: MockedExports.BrowsingContext;
openWebLinkIn: (
url: string,
where: 'current' | 'tab' | 'tabshifted' | 'window' | 'save',
where: "current" | "tab" | "tabshifted" | "window" | "save",
options?: Partial<{
// Not all possible options are present, please add more if/when needed.
userContextId: number;
@@ -411,7 +411,7 @@ declare interface Window {
) => void;
openTrustedLinkIn: (
url: string,
where: 'current' | 'tab' | 'tabshifted' | 'window' | 'save',
where: "current" | "tab" | "tabshifted" | "window" | "save",
options?: Partial<{
// Not all possible options are present, please add more if/when needed.
userContextId: number;
@@ -435,12 +435,12 @@ declare interface XULCommandEvent extends Event {
declare interface XULElementWithCommandHandler {
addEventListener: (
type: 'command',
type: "command",
handler: (event: XULCommandEvent) => void,
isCapture?: boolean
) => void;
removeEventListener: (
type: 'command',
type: "command",
handler: (event: XULCommandEvent) => void,
isCapture?: boolean
) => void;

View File

@@ -3,7 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// prettier-ignore
// eslint-disable-next-line no-lone-blocks
{
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenWorkspaceBookmarksStorage.js", this);

View File

@@ -2,7 +2,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/.
import { nsZenDOMOperatedFeature } from 'chrome://browser/content/zen-components/ZenCommonUtils.mjs';
import { nsZenDOMOperatedFeature } from "chrome://browser/content/zen-components/ZenCommonUtils.mjs";
// prettier-ignore
const SVG_ICONS = [
@@ -41,31 +41,31 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
#currentPromiseReject = null;
init() {
this.#panel = document.getElementById('PanelUI-zen-emojis-picker');
this.#panel.addEventListener('popupshowing', this);
this.#panel.addEventListener('popuphidden', this);
this.#panel.addEventListener('command', this);
this.searchInput.addEventListener('input', this);
this.#panel = document.getElementById("PanelUI-zen-emojis-picker");
this.#panel.addEventListener("popupshowing", this);
this.#panel.addEventListener("popuphidden", this);
this.#panel.addEventListener("command", this);
this.searchInput.addEventListener("input", this);
}
handleEvent(event) {
switch (event.type) {
case 'popupshowing':
case "popupshowing":
this.#onPopupShowing(event);
break;
case 'popuphidden':
case "popuphidden":
this.#onPopupHidden(event);
break;
case 'command':
if (event.target.id === 'PanelUI-zen-emojis-picker-none') {
case "command":
if (event.target.id === "PanelUI-zen-emojis-picker-none") {
this.#selectEmoji(null);
} else if (event.target.id === 'PanelUI-zen-emojis-picker-change-emojis') {
} else if (event.target.id === "PanelUI-zen-emojis-picker-change-emojis") {
this.#changePage(false);
} else if (event.target.id === 'PanelUI-zen-emojis-picker-change-svg') {
} else if (event.target.id === "PanelUI-zen-emojis-picker-change-svg") {
this.#changePage(true);
}
break;
case 'input':
case "input":
this.#onSearchInput(event);
break;
}
@@ -77,42 +77,43 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
}
const lazy = {};
Services.scriptloader.loadSubScript(
'chrome://browser/content/zen-components/ZenEmojisData.min.mjs',
"chrome://browser/content/zen-components/ZenEmojisData.min.mjs",
lazy
);
/* eslint-disable mozilla/valid-lazy */
this._emojis = lazy.ZenEmojisData;
return this._emojis;
}
get emojiList() {
return document.getElementById('PanelUI-zen-emojis-picker-list');
return document.getElementById("PanelUI-zen-emojis-picker-list");
}
get svgList() {
return document.getElementById('PanelUI-zen-emojis-picker-svgs');
return document.getElementById("PanelUI-zen-emojis-picker-svgs");
}
get searchInput() {
return document.getElementById('PanelUI-zen-emojis-picker-search');
return document.getElementById("PanelUI-zen-emojis-picker-search");
}
#changePage(toSvg = false) {
const itemToScroll = toSvg
? this.svgList
: document.getElementById('PanelUI-zen-emojis-picker-pages').querySelector('[emojis="true"]');
: document.getElementById("PanelUI-zen-emojis-picker-pages").querySelector('[emojis="true"]');
itemToScroll.scrollIntoView({
behavior: 'smooth',
block: 'nearest',
inline: 'start',
behavior: "smooth",
block: "nearest",
inline: "start",
});
const button = document.getElementById(
`PanelUI-zen-emojis-picker-change-${toSvg ? 'svg' : 'emojis'}`
`PanelUI-zen-emojis-picker-change-${toSvg ? "svg" : "emojis"}`
);
const otherButton = document.getElementById(
`PanelUI-zen-emojis-picker-change-${toSvg ? 'emojis' : 'svg'}`
`PanelUI-zen-emojis-picker-change-${toSvg ? "emojis" : "svg"}`
);
button.classList.add('selected');
otherButton.classList.remove('selected');
button.classList.add("selected");
otherButton.classList.remove("selected");
}
#clearEmojis() {
@@ -129,7 +130,7 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
})
.sort((a, b) => a.order - b.order);
for (const button of this.emojiList.children) {
const buttonEmoji = button.getAttribute('label');
const buttonEmoji = button.getAttribute("label");
const emojiObject = filteredEmojis.find((emoji) => emoji.emoji === buttonEmoji);
if (emojiObject) {
button.hidden = !emojiObject.tags.some((tag) => tag.toLowerCase().includes(value));
@@ -142,17 +143,19 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
// note: It's async on purpose so we can render the popup before processing the emojis
async #onPopupShowing(event) {
if (event.target !== this.#panel) return;
this.searchInput.value = '';
const allowEmojis = !this.#panel.hasAttribute('only-svg-icons');
if (event.target !== this.#panel) {
return;
}
this.searchInput.value = "";
const allowEmojis = !this.#panel.hasAttribute("only-svg-icons");
if (allowEmojis) {
const emojiList = this.emojiList;
for (const emoji of this.#emojis) {
const item = document.createXULElement('toolbarbutton');
item.className = 'toolbarbutton-1 zen-emojis-picker-emoji';
item.setAttribute('label', emoji.emoji);
item.setAttribute('tooltiptext', '');
item.addEventListener('command', () => {
const item = document.createXULElement("toolbarbutton");
item.className = "toolbarbutton-1 zen-emojis-picker-emoji";
item.setAttribute("label", emoji.emoji);
item.setAttribute("tooltiptext", "");
item.addEventListener("command", () => {
this.#selectEmoji(emoji.emoji);
});
emojiList.appendChild(item);
@@ -163,13 +166,13 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
}
const svgList = this.svgList;
for (const icon of SVG_ICONS) {
const item = document.createXULElement('toolbarbutton');
item.className = 'toolbarbutton-1 zen-emojis-picker-svg';
item.setAttribute('label', icon);
item.setAttribute('tooltiptext', '');
const item = document.createXULElement("toolbarbutton");
item.className = "toolbarbutton-1 zen-emojis-picker-svg";
item.setAttribute("label", icon);
item.setAttribute("tooltiptext", "");
item.style.listStyleImage = `url(${this.getSVGURL(icon)})`;
item.setAttribute('icon', icon);
item.addEventListener('command', () => {
item.setAttribute("icon", icon);
item.addEventListener("command", () => {
this.#selectEmoji(this.getSVGURL(icon));
});
svgList.appendChild(item);
@@ -177,31 +180,33 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
}
#onPopupHidden(event) {
if (event.target !== this.#panel) return;
if (event.target !== this.#panel) {
return;
}
this.#clearEmojis();
this.#changePage(false);
const emojiList = this.emojiList;
emojiList.innerHTML = '';
emojiList.innerHTML = "";
this.svgList.innerHTML = '';
this.svgList.innerHTML = "";
if (this.#currentPromiseReject) {
this.#currentPromiseReject(new Error('Emoji picker closed without selection'));
this.#currentPromiseReject(new Error("Emoji picker closed without selection"));
}
this.#currentPromise = null;
this.#currentPromiseResolve = null;
this.#currentPromiseReject = null;
this.#anchor.removeAttribute('zen-emoji-open');
this.#anchor.parentElement.removeAttribute('zen-emoji-open');
this.#anchor.removeAttribute("zen-emoji-open");
this.#anchor.parentElement.removeAttribute("zen-emoji-open");
this.#anchor = null;
}
#selectEmoji(emoji) {
if (this.#emojiAsSVG && emoji && !emoji.startsWith('chrome://')) {
if (this.#emojiAsSVG && emoji && !emoji.startsWith("chrome://")) {
emoji = `data:image/svg+xml;base64,${btoa(
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><text y="28" font-size="28" x="0">${unescape(
encodeURIComponent(emoji)
@@ -222,14 +227,14 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
this.#currentPromiseReject = reject;
});
this.#anchor = anchor;
this.#anchor.setAttribute('zen-emoji-open', 'true');
this.#anchor.parentElement.setAttribute('zen-emoji-open', 'true');
this.#anchor.setAttribute("zen-emoji-open", "true");
this.#anchor.parentElement.setAttribute("zen-emoji-open", "true");
if (onlySvgIcons) {
this.#panel.setAttribute('only-svg-icons', 'true');
this.#panel.setAttribute("only-svg-icons", "true");
} else {
this.#panel.removeAttribute('only-svg-icons');
this.#panel.removeAttribute("only-svg-icons");
}
this.#panel.openPopup(anchor, 'after_start', 0, 0, false, false);
this.#panel.openPopup(anchor, "after_start", 0, 0, false, false);
return this.#currentPromise;
}

File diff suppressed because one or more lines are too long

View File

@@ -4,9 +4,9 @@
window.gZenOperatingSystemCommonUtils = {
kZenOSToSmallName: {
WINNT: 'windows',
Darwin: 'macos',
Linux: 'linux',
WINNT: "windows",
Darwin: "macos",
Linux: "linux",
},
get currentOperatingSystem() {
@@ -19,11 +19,11 @@ export class nsZenMultiWindowFeature {
constructor() {}
static get browsers() {
return Services.wm.getEnumerator('navigator:browser');
return Services.wm.getEnumerator("navigator:browser");
}
static get currentBrowser() {
return Services.wm.getMostRecentWindow('navigator:browser');
return Services.wm.getMostRecentWindow("navigator:browser");
}
static get isActiveWindow() {
@@ -38,13 +38,15 @@ export class nsZenMultiWindowFeature {
if (!nsZenMultiWindowFeature.isActiveWindow) {
return;
}
return this.forEachWindow(callback);
await this.forEachWindow(callback);
}
async forEachWindow(callback) {
for (const browser of nsZenMultiWindowFeature.browsers) {
try {
if (browser.closed) continue;
if (browser.closed) {
continue;
}
await callback(browser);
} catch (e) {
console.error(e);
@@ -55,7 +57,9 @@ export class nsZenMultiWindowFeature {
forEachWindowSync(callback) {
for (const browser of nsZenMultiWindowFeature.browsers) {
try {
if (browser.closed) continue;
if (browser.closed) {
continue;
}
callback(browser);
} catch (e) {
console.error(e);
@@ -67,14 +71,14 @@ export class nsZenMultiWindowFeature {
export class nsZenDOMOperatedFeature {
constructor() {
var initBound = this.init.bind(this);
document.addEventListener('DOMContentLoaded', initBound, { once: true });
document.addEventListener("DOMContentLoaded", initBound, { once: true });
}
}
export class nsZenPreloadedFeature {
constructor() {
var initBound = this.init.bind(this);
document.addEventListener('MozBeforeInitialXULLayout', initBound, { once: true });
document.addEventListener("MozBeforeInitialXULLayout", initBound, { once: true });
}
}
@@ -84,15 +88,17 @@ window.gZenCommonActions = {
const displaySpec = currentUrl.displaySpec;
ClipboardHelper.copyString(displaySpec);
let button;
if (Services.zen.canShare() && displaySpec.startsWith('http')) {
/* eslint-disable mozilla/valid-services */
if (Services.zen.canShare() && displaySpec.startsWith("http")) {
button = {
id: 'zen-copy-current-url-button',
id: "zen-copy-current-url-button",
command: (event) => {
const buttonRect = event.target.getBoundingClientRect();
/* eslint-disable mozilla/valid-services */
Services.zen.share(
currentUrl,
'',
'',
"",
"",
buttonRect.left,
window.innerHeight - buttonRect.bottom,
buttonRect.width,
@@ -101,7 +107,7 @@ window.gZenCommonActions = {
},
};
}
gZenUIManager.showToast('zen-copy-current-url-confirmation', { button, timeout: 3000 });
gZenUIManager.showToast("zen-copy-current-url-confirmation", { button, timeout: 3000 });
},
copyCurrentURLAsMarkdownToClipboard() {
@@ -109,7 +115,7 @@ window.gZenCommonActions = {
const tabTitle = gBrowser.selectedTab.label;
const markdownLink = `[${tabTitle}](${currentUrl.displaySpec})`;
ClipboardHelper.copyString(markdownLink);
gZenUIManager.showToast('zen-copy-current-url-confirmation', { timeout: 3000 });
gZenUIManager.showToast("zen-copy-current-url-confirmation", { timeout: 3000 });
},
throttle(f, delay) {
@@ -125,13 +131,13 @@ window.gZenCommonActions = {
* Only tabs with an owner that are not pinned and not empty are eligible.
* Respects the user preference zen.tabs.close-on-back-with-no-history.
*
* @return {boolean} True if the tab should be closed on back
* @returns {boolean} True if the tab should be closed on back
*/
shouldCloseTabOnBack() {
if (!Services.prefs.getBoolPref('zen.tabs.close-on-back-with-no-history', true)) {
if (!Services.prefs.getBoolPref("zen.tabs.close-on-back-with-no-history", true)) {
return false;
}
const tab = gBrowser.selectedTab;
return Boolean(tab.owner && !tab.pinned && !tab.hasAttribute('zen-empty-tab'));
return Boolean(tab.owner && !tab.pinned && !tab.hasAttribute("zen-empty-tab"));
},
};

View File

@@ -9,13 +9,16 @@ class nsHasPolyfill {
}
/**
* @param {{selector: string, exists: boolean}} descendantSelectors
* @param {HTMLElement} element
* @param {Array<{selector: string, exists: boolean}>} descendantSelectors
* @param {string} stateAttribute
* @param {Array<string>} attributeFilter
*/
observeSelectorExistence(element, descendantSelectors, stateAttribute, attributeFilter = []) {
const updateState = () => {
const exists = descendantSelectors.some(({ selector }) => {
let selected = element.querySelector(selector);
if (selected?.tagName?.toLowerCase() === 'menu') {
if (selected?.tagName?.toLowerCase() === "menu") {
return null;
}
return selected;
@@ -25,10 +28,8 @@ class nsHasPolyfill {
if (!element.hasAttribute(stateAttribute)) {
gZenCompactModeManager._setElementExpandAttribute(element, true, stateAttribute);
}
} else {
if (element.hasAttribute(stateAttribute)) {
gZenCompactModeManager._setElementExpandAttribute(element, false, stateAttribute);
}
} else if (element.hasAttribute(stateAttribute)) {
gZenCompactModeManager._setElementExpandAttribute(element, false, stateAttribute);
}
};
@@ -70,6 +71,6 @@ class nsHasPolyfill {
}
const hasPolyfillInstance = new nsHasPolyfill();
window.addEventListener('unload', () => hasPolyfillInstance.destroy(), { once: true });
window.addEventListener("unload", () => hasPolyfillInstance.destroy(), { once: true });
window.ZenHasPolyfill = hasPolyfillInstance;

View File

@@ -2,7 +2,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/.
const WINDOW_SCHEME_PREF = 'zen.view.window.scheme';
const WINDOW_SCHEME_PREF = "zen.view.window.scheme";
const WINDOW_SCHEME_MAPPING = {
dark: 0,
light: 1,
@@ -25,31 +25,33 @@ class nsZenMenuBar {
<menuitem data-l10n-id="zen-menubar-appearance-dark" data-type="dark" type="radio" />
</menupopup>
</menu>`);
const menu = appearanceMenu.querySelector('menu');
menu.addEventListener('command', (event) => {
const type = event.target.getAttribute('data-type');
const menu = appearanceMenu.querySelector("menu");
menu.addEventListener("command", (event) => {
const type = event.target.getAttribute("data-type");
const schemeValue = WINDOW_SCHEME_MAPPING[type];
Services.prefs.setIntPref(WINDOW_SCHEME_PREF, schemeValue);
});
const parent = document.getElementById('view-menu');
const parentPopup = parent.querySelector('menupopup');
parentPopup.prepend(document.createXULElement('menuseparator'));
const viewMenu = document.getElementById("view-menu");
const parentPopup = viewMenu.querySelector("menupopup");
parentPopup.prepend(document.createXULElement("menuseparator"));
parentPopup.prepend(menu);
const sibling = document.getElementById('viewSidebarMenuMenu');
const sibling = document.getElementById("viewSidebarMenuMenu");
const togglePinnedItem = window.MozXULElement.parseXULToFragment(
'<menuitem data-l10n-id="zen-menubar-toggle-pinned-tabs" />'
).querySelector('menuitem');
if (!gZenWorkspaces.privateWindowOrDisabled) sibling.after(togglePinnedItem);
).querySelector("menuitem");
if (!gZenWorkspaces.privateWindowOrDisabled) {
sibling.after(togglePinnedItem);
}
parentPopup.addEventListener('popupshowing', () => {
parentPopup.addEventListener("popupshowing", () => {
const currentScheme = Services.prefs.getIntPref(WINDOW_SCHEME_PREF);
for (const [type, value] of Object.entries(WINDOW_SCHEME_MAPPING)) {
let menuItem = menu.querySelector(`menuitem[data-type="${type}"]`);
if (value === currentScheme) {
menuItem.setAttribute('checked', 'true');
menuItem.setAttribute("checked", "true");
} else {
menuItem.removeAttribute('checked');
menuItem.removeAttribute("checked");
}
}
const pinnedAreCollapsed =
@@ -58,13 +60,13 @@ class nsZenMenuBar {
document.l10n.setArgs(togglePinnedItem, args);
});
togglePinnedItem.addEventListener('command', () => {
togglePinnedItem.addEventListener("command", () => {
gZenWorkspaces.activeWorkspaceElement?.collapsiblePins.toggle();
});
}
#initSpacesMenu() {
let menubar = window.MozXULElement.parseXULToFragment(`
let spacesMenubar = window.MozXULElement.parseXULToFragment(`
<menu id="zen-spaces-menubar" data-l10n-id="zen-panel-ui-spaces-label">
<menupopup>
<menuitem data-l10n-id="zen-panel-ui-workspaces-create" command="cmd_zenOpenWorkspaceCreation"/>
@@ -82,8 +84,8 @@ class nsZenMenuBar {
key="zen-workspace-backward"/>
</menupopup>
</menu>`);
document.getElementById('view-menu').after(menubar);
document.getElementById('zen-spaces-menubar').addEventListener('popupshowing', () => {
document.getElementById("view-menu").after(spacesMenubar);
document.getElementById("zen-spaces-menubar").addEventListener("popupshowing", () => {
gZenWorkspaces.updateWorkspacesChangeContextMenu();
});
}

View File

@@ -2,7 +2,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/.
import { nsZenPreloadedFeature } from 'chrome://browser/content/zen-components/ZenCommonUtils.mjs';
import { nsZenPreloadedFeature } from "chrome://browser/content/zen-components/ZenCommonUtils.mjs";
class ZenSessionStore extends nsZenPreloadedFeature {
init() {
@@ -15,11 +15,11 @@ class ZenSessionStore extends nsZenPreloadedFeature {
restoreInitialTabData(tab, tabData) {
if (tabData.zenWorkspace) {
tab.setAttribute('zen-workspace-id', tabData.zenWorkspace);
tab.setAttribute("zen-workspace-id", tabData.zenWorkspace);
}
// Keep for now, for backward compatibility for window sync to work.
if (tabData.zenSyncId || tabData.zenPinnedId) {
tab.setAttribute('id', tabData.zenSyncId || tabData.zenPinnedId);
tab.setAttribute("id", tabData.zenSyncId || tabData.zenPinnedId);
}
if (tabData.zenStaticLabel) {
tab.zenStaticLabel = tabData.zenStaticLabel;
@@ -28,10 +28,10 @@ class ZenSessionStore extends nsZenPreloadedFeature {
tab.zenStaticIcon = tabData.image;
}
if (tabData.zenEssential) {
tab.setAttribute('zen-essential', 'true');
tab.setAttribute("zen-essential", "true");
}
if (tabData.zenDefaultUserContextId) {
tab.setAttribute('zenDefaultUserContextId', 'true');
tab.setAttribute("zenDefaultUserContextId", "true");
}
if (tabData._zenPinnedInitialState) {
tab._zenPinnedInitialState = tabData._zenPinnedInitialState;

View File

@@ -2,14 +2,14 @@
* 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 { html } from 'chrome://global/content/vendor/lit.all.mjs';
import { MozLitElement } from 'chrome://global/content/lit-utils.mjs';
import { html } from "chrome://global/content/vendor/lit.all.mjs";
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
const lazy = {};
ChromeUtils.defineLazyGetter(lazy, 'siblingElement', () => {
ChromeUtils.defineLazyGetter(lazy, "siblingElement", () => {
// All our notifications should be attached after the media controls toolbar
return document.getElementById('zen-media-controls-toolbar');
return document.getElementById("zen-media-controls-toolbar");
});
/**
@@ -17,8 +17,6 @@ ChromeUtils.defineLazyGetter(lazy, 'siblingElement', () => {
*
* Displays and takes care of animations for notifications that
* appear in the sidebar.
*
* @properties {headingL10nId} - The L10n ID for the heading text.
*/
class ZenSidebarNotification extends MozLitElement {
static properties = {
@@ -26,7 +24,7 @@ class ZenSidebarNotification extends MozLitElement {
links: { type: Array },
};
constructor({ headingL10nId = '', links = [] } = {}) {
constructor({ headingL10nId = "", links = [] } = {}) {
super();
this.headingL10nId = headingL10nId;
this.links = links;
@@ -54,7 +52,7 @@ class ZenSidebarNotification extends MozLitElement {
<label
class="zen-sidebar-notification-heading"
flex="1"
data-l10n-id="${this.headingL10nId}"></label>
data-l10n-id=${this.headingL10nId}></label>
<div class="zen-sidebar-notification-close-button" @click=${() => this.remove()}>
<img src="chrome://browser/skin/zen-icons/close.svg" />
</div>
@@ -71,13 +69,13 @@ class ZenSidebarNotification extends MozLitElement {
link.action();
return;
}
window.openLinkIn(link.url, 'tab', {
window.openLinkIn(link.url, "tab", {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
forceForeground: true,
});
this.remove();
}}>
<img class="zen-sidebar-notification-link-icon" src="${link.icon}" />
<img class="zen-sidebar-notification-link-icon" src=${link.icon} />
<label
class="zen-sidebar-notification-link-text"
data-l10n-id="${link.l10nId}-label"></label>
@@ -89,7 +87,7 @@ class ZenSidebarNotification extends MozLitElement {
}
#animateIn() {
this.style.opacity = '0';
this.style.opacity = "0";
return gZenUIManager.motion.animate(
this,
{
@@ -121,8 +119,8 @@ export default function createSidebarNotification(args) {
const notification = new ZenSidebarNotification(args);
lazy.siblingElement.insertAdjacentElement('afterend', notification);
lazy.siblingElement.insertAdjacentElement("afterend", notification);
return notification;
}
customElements.define('zen-sidebar-notification', ZenSidebarNotification);
customElements.define("zen-sidebar-notification", ZenSidebarNotification);

View File

@@ -4,10 +4,10 @@
import checkForZenUpdates, {
createWindowUpdateAnimation,
} from 'chrome://browser/content/ZenUpdates.mjs';
} from "chrome://browser/content/ZenUpdates.mjs";
class ZenStartup {
#watermarkIgnoreElements = ['zen-toast-container'];
#watermarkIgnoreElements = ["zen-toast-container"];
#hasInitializedLayout = false;
isReady = false;
@@ -20,38 +20,42 @@ class ZenStartup {
}
#initBrowserBackground() {
const background = document.createXULElement('box');
background.id = 'zen-browser-background';
background.classList.add('zen-browser-generic-background');
const grain = document.createXULElement('box');
grain.classList.add('zen-browser-grain');
const background = document.createXULElement("box");
background.id = "zen-browser-background";
background.classList.add("zen-browser-generic-background");
const grain = document.createXULElement("box");
grain.classList.add("zen-browser-grain");
background.appendChild(grain);
document.getElementById('browser').prepend(background);
document.getElementById("browser").prepend(background);
const toolbarBackground = background.cloneNode(true);
toolbarBackground.removeAttribute('id');
toolbarBackground.classList.add('zen-toolbar-background');
document.getElementById('titlebar').prepend(toolbarBackground);
toolbarBackground.removeAttribute("id");
toolbarBackground.classList.add("zen-toolbar-background");
document.getElementById("titlebar").prepend(toolbarBackground);
}
#zenInitBrowserLayout() {
if (this.#hasInitializedLayout) return;
if (this.#hasInitializedLayout) {
return;
}
this.#hasInitializedLayout = true;
gZenKeyboardShortcutsManager.beforeInit();
try {
const kNavbarItems = ['nav-bar', 'PersonalToolbar'];
const kNewContainerId = 'zen-appcontent-navbar-container';
const kNavbarItems = ["nav-bar", "PersonalToolbar"];
const kNewContainerId = "zen-appcontent-navbar-container";
let newContainer = document.getElementById(kNewContainerId);
for (let id of kNavbarItems) {
const node = document.getElementById(id);
console.assert(node, 'Could not find node with id: ' + id);
if (!node) continue;
if (!node) {
console.error("Could not find node with id: " + id);
continue;
}
newContainer.appendChild(node);
}
// Fix notification deck
const deckTemplate = document.getElementById('tab-notification-deck-template');
const deckTemplate = document.getElementById("tab-notification-deck-template");
if (deckTemplate) {
document.getElementById('zen-appcontent-wrapper').prepend(deckTemplate);
document.getElementById("zen-appcontent-wrapper").prepend(deckTemplate);
}
gZenWorkspaces.init();
@@ -60,20 +64,20 @@ class ZenStartup {
this.#checkForWelcomePage();
}, 0);
} catch (e) {
console.error('ZenThemeModifier: Error initializing browser layout', e);
console.error("ZenThemeModifier: Error initializing browser layout", e);
}
if (gBrowserInit.delayedStartupFinished) {
this.delayedStartupFinished();
} else {
Services.obs.addObserver(this, 'browser-delayed-startup-finished');
Services.obs.addObserver(this, "browser-delayed-startup-finished");
}
}
observe(aSubject, aTopic) {
// This nsIObserver method allows us to defer initialization until after
// this window has finished painting and starting up.
if (aTopic == 'browser-delayed-startup-finished' && aSubject == window) {
Services.obs.removeObserver(this, 'browser-delayed-startup-finished');
if (aTopic == "browser-delayed-startup-finished" && aSubject == window) {
Services.obs.removeObserver(this, "browser-delayed-startup-finished");
this.delayedStartupFinished();
}
}
@@ -86,35 +90,35 @@ class ZenStartup {
this.#initSearchBar();
gZenCompactModeManager.init();
// Fix for https://github.com/zen-browser/desktop/issues/7605, specially in compact mode
if (gURLBar.hasAttribute('breakout-extend')) {
if (gURLBar.hasAttribute("breakout-extend")) {
gURLBar.focus();
}
// A bit of a hack to make sure the tabs toolbar is updated.
// Just in case we didn't get the right size.
gZenUIManager.updateTabsToolbar();
this.closeWatermark();
document.getElementById('tabbrowser-arrowscrollbox').setAttribute('orient', 'vertical');
document.getElementById("tabbrowser-arrowscrollbox").setAttribute("orient", "vertical");
this.isReady = true;
});
}
openWatermark() {
if (!Services.prefs.getBoolPref('zen.watermark.enabled', false)) {
document.documentElement.removeAttribute('zen-before-loaded');
if (!Services.prefs.getBoolPref("zen.watermark.enabled", false)) {
document.documentElement.removeAttribute("zen-before-loaded");
return;
}
for (let elem of document.querySelectorAll('#browser > *, #urlbar')) {
for (let elem of document.querySelectorAll("#browser > *, #urlbar")) {
elem.style.opacity = 0;
}
}
closeWatermark() {
document.documentElement.removeAttribute('zen-before-loaded');
if (Services.prefs.getBoolPref('zen.watermark.enabled', false)) {
let elementsToIgnore = this.#watermarkIgnoreElements.map((id) => '#' + id).join(', ');
document.documentElement.removeAttribute("zen-before-loaded");
if (Services.prefs.getBoolPref("zen.watermark.enabled", false)) {
let elementsToIgnore = this.#watermarkIgnoreElements.map((id) => "#" + id).join(", ");
gZenUIManager.motion
.animate(
'#browser > *:not(' + elementsToIgnore + '), #urlbar, #tabbrowser-tabbox > *',
"#browser > *:not(" + elementsToIgnore + "), #urlbar, #tabbrowser-tabbox > *",
{
opacity: [0, 1],
},
@@ -124,24 +128,24 @@ class ZenStartup {
)
.then(() => {
for (let elem of document.querySelectorAll(
'#browser > *, #urlbar, #tabbrowser-tabbox > *'
"#browser > *, #urlbar, #tabbrowser-tabbox > *"
)) {
elem.style.removeProperty('opacity');
elem.style.removeProperty("opacity");
}
});
}
window.requestAnimationFrame(() => {
window.dispatchEvent(new window.Event('resize')); // To recalculate the layout
window.dispatchEvent(new window.Event("resize")); // To recalculate the layout
});
}
#changeSidebarLocation() {
const kElementsToAppend = ['sidebar-splitter', 'sidebar-box'];
const kElementsToAppend = ["sidebar-splitter", "sidebar-box"];
const browser = document.getElementById('browser');
const browser = document.getElementById("browser");
browser.prepend(gNavToolbox);
const sidebarPanelWrapper = document.getElementById('tabbrowser-tabbox');
const sidebarPanelWrapper = document.getElementById("tabbrowser-tabbox");
for (let id of kElementsToAppend) {
const elem = document.getElementById(id);
if (elem) {
@@ -156,12 +160,12 @@ class ZenStartup {
}
#checkForWelcomePage() {
if (!Services.prefs.getBoolPref('zen.welcome-screen.seen', false)) {
Services.prefs.setBoolPref('zen.welcome-screen.seen', true);
Services.prefs.setStringPref('zen.updates.last-build-id', Services.appinfo.appBuildID);
Services.prefs.setStringPref('zen.updates.last-version', Services.appinfo.version);
if (!Services.prefs.getBoolPref("zen.welcome-screen.seen", false)) {
Services.prefs.setBoolPref("zen.welcome-screen.seen", true);
Services.prefs.setStringPref("zen.updates.last-build-id", Services.appinfo.appBuildID);
Services.prefs.setStringPref("zen.updates.last-version", Services.appinfo.version);
Services.scriptloader.loadSubScript(
'chrome://browser/content/zen-components/ZenWelcome.mjs',
"chrome://browser/content/zen-components/ZenWelcome.mjs",
window
);
} else {
@@ -178,7 +182,7 @@ class ZenStartup {
window.gZenStartup = new ZenStartup();
window.addEventListener(
'MozBeforeInitialXULLayout',
"MozBeforeInitialXULLayout",
() => {
gZenStartup.init();
},

File diff suppressed because it is too large Load Diff

View File

@@ -2,37 +2,37 @@
// 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 createSidebarNotification from 'chrome://browser/content/zen-components/ZenSidebarNotification.mjs';
import createSidebarNotification from "chrome://browser/content/zen-components/ZenSidebarNotification.mjs";
const ZEN_UPDATE_PREF = 'zen.updates.last-version';
const ZEN_BUILD_ID_PREF = 'zen.updates.last-build-id';
const ZEN_UPDATE_SHOW = 'zen.updates.show-update-notification';
const ZEN_UPDATE_PREF = "zen.updates.last-version";
const ZEN_BUILD_ID_PREF = "zen.updates.last-build-id";
const ZEN_UPDATE_SHOW = "zen.updates.show-update-notification";
export default function checkForZenUpdates() {
const version = Services.appinfo.version;
const lastVersion = Services.prefs.getStringPref(ZEN_UPDATE_PREF, '');
const lastVersion = Services.prefs.getStringPref(ZEN_UPDATE_PREF, "");
Services.prefs.setStringPref(ZEN_UPDATE_PREF, version);
if (
version !== lastVersion &&
!gZenUIManager.testingEnabled &&
Services.prefs.getBoolPref(ZEN_UPDATE_SHOW, true)
) {
const updateUrl = Services.prefs.getStringPref('app.releaseNotesURL.prompt', '');
const updateUrl = Services.prefs.getStringPref("app.releaseNotesURL.prompt", "");
createSidebarNotification({
headingL10nId: 'zen-sidebar-notification-updated-heading',
headingL10nId: "zen-sidebar-notification-updated-heading",
links: [
{
url: Services.urlFormatter.formatURL(updateUrl.replace('%VERSION%', version)),
l10nId: 'zen-sidebar-notification-updated',
url: Services.urlFormatter.formatURL(updateUrl.replace("%VERSION%", version)),
l10nId: "zen-sidebar-notification-updated",
special: true,
icon: 'chrome://browser/skin/zen-icons/heart-circle-fill.svg',
icon: "chrome://browser/skin/zen-icons/heart-circle-fill.svg",
},
{
action: () => {
Services.obs.notifyObservers(window, 'restart-in-safe-mode');
Services.obs.notifyObservers(window, "restart-in-safe-mode");
},
l10nId: 'zen-sidebar-notification-restart-safe-mode',
icon: 'chrome://browser/skin/zen-icons/security-broken.svg',
l10nId: "zen-sidebar-notification-restart-safe-mode",
icon: "chrome://browser/skin/zen-icons/security-broken.svg",
},
],
});
@@ -42,18 +42,18 @@ export default function checkForZenUpdates() {
export async function createWindowUpdateAnimation() {
const appID = Services.appinfo.appBuildID;
if (
Services.prefs.getStringPref(ZEN_BUILD_ID_PREF, '') === appID ||
Services.prefs.getStringPref(ZEN_BUILD_ID_PREF, "") === appID ||
gZenUIManager.testingEnabled
) {
return;
}
Services.prefs.setStringPref(ZEN_BUILD_ID_PREF, appID);
await gZenWorkspaces.promiseInitialized;
const appWrapper = document.getElementById('zen-main-app-wrapper');
const element = document.createElement('div');
element.id = 'zen-update-animation';
const elementBorder = document.createElement('div');
elementBorder.id = 'zen-update-animation-border';
const appWrapper = document.getElementById("zen-main-app-wrapper");
const element = document.createElement("div");
element.id = "zen-update-animation";
const elementBorder = document.createElement("div");
elementBorder.id = "zen-update-animation-border";
requestIdleCallback(() => {
if (gReduceMotion) {
return;
@@ -62,9 +62,9 @@ export async function createWindowUpdateAnimation() {
appWrapper.appendChild(elementBorder);
Promise.all([
gZenUIManager.motion.animate(
'#zen-update-animation',
"#zen-update-animation",
{
top: ['100%', '-50%'],
top: ["100%", "-50%"],
opacity: [0.5, 1],
},
{
@@ -72,9 +72,9 @@ export async function createWindowUpdateAnimation() {
}
),
gZenUIManager.motion.animate(
'#zen-update-animation-border',
"#zen-update-animation-border",
{
'--background-top': ['150%', '-50%'],
"--background-top": ["150%", "-50%"],
},
{
duration: 0.35,

View File

@@ -75,11 +75,7 @@
@keyframes zen-urlbar-searchmode {
0% {
box-shadow: 0 0 20px
color-mix(
in srgb,
color-mix(in srgb, var(--zen-primary-color), var(--toolbox-textcolor) 20%),
light-dark(rgba(0, 0, 0, 0.3), transparent) 50%
);
color-mix(in srgb, color-mix(in srgb, var(--zen-primary-color), var(--toolbox-textcolor) 20%), light-dark(rgba(0, 0, 0, 0.3), transparent) 50%);
}
100% {

View File

@@ -5,25 +5,25 @@
*/
@font-face {
font-family: 'Zen-Junicode';
src: url('chrome://browser/content/zen-fonts/JunicodeVF-Roman.woff2') format('woff2');
font-family: "Zen-Junicode";
src: url("chrome://browser/content/zen-fonts/JunicodeVF-Roman.woff2") format("woff2");
}
@font-face {
font-family: 'Zen-Junicode-Italic';
src: url('chrome://browser/content/zen-fonts/JunicodeVF-Italic.woff2') format('woff2');
font-family: "Zen-Junicode-Italic";
src: url("chrome://browser/content/zen-fonts/JunicodeVF-Italic.woff2") format("woff2");
}
.zen-branding-title {
font-size: 6rem;
line-height: 0.9;
margin-bottom: 0.4rem;
font-family: 'Zen-Junicode', serif;
font-family: "Zen-Junicode", serif;
font-weight: 400;
font-style: normal;
font-feature-settings: 'swsh' 1;
font-feature-settings: "swsh" 1;
& .zen-branding-title-italic {
font-family: 'Zen-Junicode-Italic', serif;
font-family: "Zen-Junicode-Italic", serif;
}
}

View File

@@ -3,33 +3,33 @@
* 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/.
*/
:root:not([inDOMFullscreen='true']):not([chromehidden~='location']):not([chromehidden~='toolbar']) {
:root:not([inDOMFullscreen="true"]):not([chromehidden~="location"]):not([chromehidden~="toolbar"]) {
& #tabbrowser-tabbox #tabbrowser-tabpanels .browserSidebarContainer {
overflow: clip;
:root:not([zen-no-padding='true']) &:not(.zen-glance-overlay) {
:root:not([zen-no-padding="true"]) &:not(.zen-glance-overlay) {
border-radius: var(--zen-native-inner-radius);
box-shadow: var(--zen-big-shadow);
}
& browser[type='content'] {
&:not([transparent='true']) {
& browser[type="content"] {
&:not([transparent="true"]) {
background: light-dark(rgb(255, 255, 255), rgb(32, 32, 32));
}
&[transparent='true'] {
&[transparent="true"] {
background: light-dark(rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.1));
}
}
@media not -moz-pref('layout.css.prefers-color-scheme.content-override', 2) {
& browser[type='content'] {
@media not -moz-pref("layout.css.prefers-color-scheme.content-override", 2) {
& browser[type="content"] {
color-scheme: env(-moz-content-preferred-color-scheme);
}
}
@media -moz-pref('zen.theme.acrylic-elements') {
& browser[type='content'] {
@media -moz-pref("zen.theme.acrylic-elements") {
& browser[type="content"] {
/* For the rendering engine to apply layering optimizations. This
* is a hacky solution, but it works for now. */
will-change: transform;
@@ -37,11 +37,11 @@
}
}
@media (not -moz-pref('zen.view.shift-down-site-on-hover')) and (not ((-moz-pref('zen.view.experimental-no-window-controls') or (not -moz-pref('zen.view.hide-window-controls'))) and -moz-pref('zen.view.use-single-toolbar'))) {
.browserSidebarContainer:is(.deck-selected, [zen-split='true']) .browserContainer {
@media (not -moz-pref("zen.view.shift-down-site-on-hover")) and (not ((-moz-pref("zen.view.experimental-no-window-controls") or (not -moz-pref("zen.view.hide-window-controls"))) and -moz-pref("zen.view.use-single-toolbar"))) {
.browserSidebarContainer:is(.deck-selected, [zen-split="true"]) .browserContainer {
transition: margin var(--zen-hidden-toolbar-transition);
:root[zen-single-toolbar='true'] & {
:root[zen-single-toolbar="true"] & {
transition-delay: 0.2s;
--zen-toolbar-height: 34px;
}
@@ -49,10 +49,8 @@
#tabbrowser-tabpanels[has-toolbar-hovered] & {
--margin-top-fix: calc(-1 * var(--zen-toolbar-height) + var(--zen-element-separation));
:root:not([zen-single-toolbar='true']) & {
--margin-top-fix: calc(
-1 * var(--zen-toolbar-height-with-bookmarks) + var(--zen-element-separation)
);
:root:not([zen-single-toolbar="true"]) & {
--margin-top-fix: calc(-1 * var(--zen-toolbar-height-with-bookmarks) + var(--zen-element-separation));
}
margin-top: var(--margin-top-fix);
@@ -69,6 +67,6 @@
z-index: 2;
}
browser[zen-pseudo-hidden='true'] {
browser[zen-pseudo-hidden="true"] {
-moz-subtree-hidden-only-visually: 1 !important;
}

View File

@@ -19,15 +19,15 @@
position: inherit;
}
:root:is([inDOMFullscreen='true'], [chromehidden~='location'], [chromehidden~='toolbar']) {
:root:is([inDOMFullscreen="true"], [chromehidden~="location"], [chromehidden~="toolbar"]) {
#navigator-toolbox,
#zen-sidebar-splitter {
visibility: collapse;
}
}
:root[zen-before-loaded='true'] #browser > *:not(#zen-toast-container),
:root[zen-before-loaded='true'] #urlbar {
:root[zen-before-loaded="true"] #browser > *:not(#zen-toast-container),
:root[zen-before-loaded="true"] #urlbar {
opacity: 0 !important;
}
@@ -47,7 +47,7 @@
&::after,
&::before {
content: '';
content: "";
position: absolute;
inset: 0;
z-index: 0;
@@ -80,7 +80,7 @@
}
}
:root:not([animating-background='true']) &::before {
:root:not([animating-background="true"]) &::before {
transition: background-color var(--inactive-window-transition);
}
@@ -88,7 +88,7 @@
display: none;
}
:root[zen-show-grainy-background='true'] & .zen-browser-grain {
:root[zen-show-grainy-background="true"] & .zen-browser-grain {
display: flex;
width: 100%;
height: 100%;
@@ -97,7 +97,7 @@
z-index: 1;
opacity: var(--zen-grainy-background-opacity, 0);
mix-blend-mode: overlay;
:root:not([swipe-gesture='true']) & {
:root:not([swipe-gesture="true"]) & {
/* note: Keep in sync with kGlobalAnimationDuration */
transition: opacity 0.2s ease-in-out;
}
@@ -127,14 +127,14 @@
border: none;
}
:root:not([zen-single-toolbar='true']) #zen-appcontent-wrapper {
:root:not([zen-single-toolbar="true"]) #zen-appcontent-wrapper {
z-index: 3;
}
#nav-bar {
border-top-color: transparent !important;
:root[zen-single-toolbar='true'] & {
:root[zen-single-toolbar="true"] & {
border-top: none !important;
--zen-toolbar-height: 37px;
}
@@ -145,7 +145,7 @@
/* See bug #8814, don't an overflow here as it causes issues
* with firefox's rendering of the tab bar */
@media (-moz-pref('zen.view.grey-out-inactive-windows')) {
@media (-moz-pref("zen.view.grey-out-inactive-windows")) {
&:-moz-window-inactive {
background: InactiveCaption;
}
@@ -155,7 +155,7 @@
z-index: 1;
}
@media (-moz-windows-accent-color-in-titlebar) and ((-moz-windows-mica) or -moz-pref('browser.theme.windows.accent-color-in-tabs.enabled')) {
@media (-moz-windows-accent-color-in-titlebar) and ((-moz-windows-mica) or -moz-pref("browser.theme.windows.accent-color-in-tabs.enabled")) {
background: ActiveCaption;
color: CaptionText;
}
@@ -169,17 +169,17 @@
min-width: 1px;
}
:root:not([inDOMFullscreen='true']):not([chromehidden~='location']):not([chromehidden~='toolbar']) {
:root:not([inDOMFullscreen="true"]):not([chromehidden~="location"]):not([chromehidden~="toolbar"]) {
& #zen-tabbox-wrapper {
margin: var(--zen-element-separation);
margin-top: 0;
}
&:not([zen-right-side='true']) #zen-tabbox-wrapper {
&:not([zen-right-side="true"]) #zen-tabbox-wrapper {
margin-left: 0;
}
&[zen-right-side='true'] #zen-tabbox-wrapper {
&[zen-right-side="true"] #zen-tabbox-wrapper {
margin-right: 0;
}
}
@@ -207,7 +207,7 @@
}
}
@media -moz-pref('zen.widget.mac.mono-window-controls') {
@media -moz-pref("zen.widget.mac.mono-window-controls") {
.titlebar-buttonbox-container {
color: var(--toolbox-textcolor);
@@ -219,11 +219,7 @@
/* Draw 3 dots as background to represent the window controls,
all with the same cololr as the titlebar */
background-image: radial-gradient(
circle,
var(--zen-toolbar-element-bg) var(--zen-traffic-light-size),
transparent 0.5px
);
background-image: radial-gradient(circle, var(--zen-toolbar-element-bg) var(--zen-traffic-light-size), transparent 0.5px);
background-size: 20px 22px;
background-position: 53% 50%;
@@ -232,7 +228,7 @@
background-position: 52% 50%;
}
&:not([zen-has-hover='true']) > .titlebar-buttonbox {
&:not([zen-has-hover="true"]) > .titlebar-buttonbox {
opacity: 0;
}
}
@@ -240,13 +236,13 @@
}
@media (-moz-platform: macos) {
:root[zen-window-buttons-reversed='true'][zen-right-side='true'] .titlebar-buttonbox-container {
:root[zen-window-buttons-reversed="true"][zen-right-side="true"] .titlebar-buttonbox-container {
margin-inline-start: max(calc(var(--zen-element-separation) - 3px), 4px);
margin-block: auto;
}
}
.zen-split-view-splitter[orient='vertical'],
.zen-split-view-splitter[orient="vertical"],
#zen-sidebar-splitter {
position: absolute;
top: 0;
@@ -258,7 +254,7 @@
cursor: ew-resize;
z-index: 3;
&:is(.zen-split-view-splitter[orient='vertical']) {
&:is(.zen-split-view-splitter[orient="vertical"]) {
/* Bit of a hacky solution, but it works */
width: var(--zen-split-row-gap);
margin-left: calc(var(--zen-element-separation) * -1 - 1px);
@@ -271,7 +267,7 @@
width: 2px;
background: var(--button-primary-bgcolor);
border-radius: 2px;
content: '';
content: "";
position: absolute;
top: 50%;
left: 50%;
@@ -284,13 +280,13 @@
opacity: 1;
}
}
.zen-split-view-splitter[orient='horizontal'] {
.zen-split-view-splitter[orient="horizontal"] {
&::before {
height: 2px;
width: 50px;
background: var(--button-primary-bgcolor);
border-radius: 2px;
content: '';
content: "";
position: absolute;
top: 40%;
left: 50%;
@@ -337,12 +333,12 @@
z-index: 1;
& #zen-sidebar-top-buttons {
max-width: fit-content;
:root[zen-right-side='true'] & {
:root[zen-right-side="true"] & {
order: 1;
}
}
}
:root[zen-right-side='true'] #zen-appcontent-navbar-wrapper #PanelUI-button {
:root[zen-right-side="true"] #zen-appcontent-navbar-wrapper #PanelUI-button {
order: 2;
}

View File

@@ -4,8 +4,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
@namespace html 'http://www.w3.org/1999/xhtml';
@namespace xul 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
@namespace html "http://www.w3.org/1999/xhtml";
@namespace xul "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
/** This file is used to override UI inside "common-shared.css" */

View File

@@ -9,8 +9,8 @@
padding-block: 0 !important;
}
:root[zen-single-toolbar='true'] {
& #urlbar:not([breakout-extend='true']) {
:root[zen-single-toolbar="true"] {
& #urlbar:not([breakout-extend="true"]) {
padding: 1px;
--toolbarbutton-border-radius: 6px;
}
@@ -27,7 +27,7 @@
--urlbar-margin-inline: 5px;
--urlbar-container-padding: 4px;
&[breakout-extend='true'] {
&[breakout-extend="true"] {
--urlbar-container-padding: 0px;
}
@@ -36,12 +36,12 @@
visibility: visible;
}
:root[zen-single-toolbar='true'] &[breakout-extend='true'],
&[zen-floating-urlbar='true'] {
:root[zen-single-toolbar="true"] &[breakout-extend="true"],
&[zen-floating-urlbar="true"] {
--urlbar-container-padding: 3px;
--urlbar-margin-inline: 11px;
&:not([zen-floating-urlbar='true']) {
&:not([zen-floating-urlbar="true"]) {
--urlbar-container-height: 52px !important;
}
}
@@ -62,7 +62,7 @@
border-radius: var(--border-radius-medium);
outline: none !important;
#urlbar:not([breakout-extend='true']) & {
#urlbar:not([breakout-extend="true"]) & {
margin: 1px;
}
@@ -70,20 +70,20 @@
box-shadow: none !important;
}
#urlbar[focused='true']:not([suppress-focus-border]) > .urlbar-background,
#urlbar[focused="true"]:not([suppress-focus-border]) > .urlbar-background,
#searchbar:focus-within {
outline: none !important;
outline-offset: none !important;
outline-color: none !important;
}
#urlbar:not([breakout-extend='true']) {
#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;
}
}
#identity-box.chromeUI:not([pageproxystate='invalid']) {
#identity-box.chromeUI:not([pageproxystate="invalid"]) {
& #identity-icon-box {
background: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)) !important;
}
@@ -101,7 +101,7 @@
padding-inline-start: 8px !important;
}
#urlbar:not([extend='true']) #identity-box #identity-icon-box {
#urlbar:not([extend="true"]) #identity-box #identity-icon-box {
position: relative;
}
@@ -112,7 +112,7 @@
margin-bottom: auto;
padding: 6px 8px !important;
:root:not([zen-single-toolbar='true']) & {
:root:not([zen-single-toolbar="true"]) & {
padding-block: 2px !important;
}
}
@@ -125,14 +125,14 @@
font-weight: 500;
}
:root[zen-single-toolbar='true'] #urlbar:not([breakout-extend='true']) {
:root[zen-single-toolbar="true"] #urlbar:not([breakout-extend="true"]) {
& #urlbar-input {
cursor: default;
}
& #identity-box {
margin-inline-end: 0 !important;
&.chromeUI:not([pageproxystate='invalid']) #identity-icon-box {
&.chromeUI:not([pageproxystate="invalid"]) #identity-icon-box {
border-radius: 10px !important;
}
}
@@ -143,25 +143,23 @@
height: 100%; /* To still be able to open popups */
visibility: collapse;
:root:not([supress-primary-adjustment='true']) & {
:root:not([supress-primary-adjustment="true"]) & {
transition:
opacity 0.15s,
visibility 0.15s;
}
#navigator-toolbox[zen-has-implicit-hover='true'] &,
#navigator-toolbox[zen-has-implicit-hover="true"] &,
&[open],
#urlbar[has-popup-open='true'] &,
:root[zen-has-empty-tab='true'] & {
#urlbar[has-popup-open="true"] &,
:root[zen-has-empty-tab="true"] & {
opacity: 1;
visibility: visible;
}
}
}
:root:not([zen-single-toolbar='true']):not([zen-has-empty-tab='true'])
#urlbar:not([breakout-extend='true'])
.urlbar-input-container {
:root:not([zen-single-toolbar="true"]):not([zen-has-empty-tab="true"]) #urlbar:not([breakout-extend="true"]) .urlbar-input-container {
padding: 2px 3px;
gap: 2px;
@@ -184,19 +182,19 @@
background: transparent !important;
}
:root:is([zen-single-toolbar='true'], [zen-has-empty-tab='true']) {
:root:is([zen-single-toolbar="true"], [zen-has-empty-tab="true"]) {
#zen-copy-url-button[disabled] {
display: none !important;
}
}
@media -moz-pref('zen.urlbar.single-toolbar-show-copy-url', false) {
:root[zen-single-toolbar='true'] #zen-copy-url-button {
@media -moz-pref("zen.urlbar.single-toolbar-show-copy-url", false) {
:root[zen-single-toolbar="true"] #zen-copy-url-button {
display: none !important;
}
}
@media not -moz-pref('zen.urlbar.show-pip-button') {
@media not -moz-pref("zen.urlbar.show-pip-button") {
#picture-in-picture-button {
display: none !important;
}
@@ -209,7 +207,7 @@
align-items: center !important;
margin: 0;
:root[zen-single-toolbar='true'] & {
:root[zen-single-toolbar="true"] & {
padding: 6px !important;
width: unset !important;
height: unset !important;
@@ -239,11 +237,11 @@
position: fixed;
}
#urlbar[breakout-extend='true'] {
#urlbar[breakout-extend="true"] {
z-index: 2;
:root[zen-single-toolbar='true'] & .urlbar-input-box,
&[zen-floating-urlbar='true'] .urlbar-input-box {
:root[zen-single-toolbar="true"] & .urlbar-input-box,
&[zen-floating-urlbar="true"] .urlbar-input-box {
font-weight: 400;
@media (-moz-platform: windows) {
font-weight: 500;
@@ -254,33 +252,21 @@
height: 100%;
}
:root:not([zen-single-toolbar='true'])
&:not([zen-floating-urlbar='true'])
#identity-box:not([pageproxystate='invalid']) {
:root:not([zen-single-toolbar="true"]) &:not([zen-floating-urlbar="true"]) #identity-box:not([pageproxystate="invalid"]) {
margin-inline-end: 0;
}
& .urlbar-background {
--zen-urlbar-background-base: light-dark(
#fbfbfb,
color-mix(in srgb, hsl(0, 0%, 6.7%), var(--zen-colors-primary) 30%)
);
@media -moz-pref('zen.theme.acrylic-elements') {
--zen-urlbar-background-transparent: color-mix(
in srgb,
var(--zen-urlbar-background-base) 70%,
transparent 30%
);
--zen-urlbar-background-base: light-dark(#fbfbfb, color-mix(in srgb, hsl(0, 0%, 6.7%), var(--zen-colors-primary) 30%));
@media -moz-pref("zen.theme.acrylic-elements") {
--zen-urlbar-background-transparent: color-mix(in srgb, var(--zen-urlbar-background-base) 70%, transparent 30%);
}
background-color: var(
--zen-urlbar-background-transparent,
var(--zen-urlbar-background-base)
) !important;
background-color: var(--zen-urlbar-background-transparent, var(--zen-urlbar-background-base)) !important;
box-shadow: 0px 30px 140px -15px light-dark(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.6)) !important;
backdrop-filter: none !important;
outline: 0.5px solid light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2)) !important;
outline-offset: var(--zen-urlbar-outline-offset) !important;
@media -moz-pref('zen.theme.acrylic-elements') {
@media -moz-pref("zen.theme.acrylic-elements") {
backdrop-filter: blur(42px) saturate(110%) brightness(0.25) contrast(100%) !important;
}
}
@@ -290,9 +276,9 @@
border-radius: 12px !important;
}
&[breakout-extend][animate-searchmode='true']::before {
&[breakout-extend][animate-searchmode="true"]::before {
position: absolute;
content: '';
content: "";
inset: 0;
width: 100%;
height: 100%;
@@ -310,7 +296,7 @@
display: none;
}
:root[zen-single-toolbar='true'] {
:root[zen-single-toolbar="true"] {
--urlbar-icon-border-radius: 8px !important;
--urlbar-inner-border-radius: var(--toolbarbutton-border-radius) !important;
@@ -322,7 +308,7 @@
display: none;
}
&:not([zen-has-empty-tab='true']) #urlbar:not([breakout-extend='true']) #identity-box {
&:not([zen-has-empty-tab="true"]) #urlbar:not([breakout-extend="true"]) #identity-box {
order: 2;
}
@@ -354,7 +340,7 @@
}
}
:root:not([zen-single-toolbar='true']) {
:root:not([zen-single-toolbar="true"]) {
--urlbar-icon-border-radius: 6px !important;
#notification-popup-box {
@@ -372,7 +358,7 @@
}
}
#urlbar[breakout-extend='true'] #notification-popup-box {
#urlbar[breakout-extend="true"] #notification-popup-box {
min-width: calc(var(--urlbar-min-height) - 4 * var(--urlbar-container-padding)) !important;
height: calc(var(--urlbar-min-height) - 4 * var(--urlbar-container-padding)) !important;
}
@@ -411,9 +397,7 @@
/* Border radius on hover */
#urlbar .urlbar-page-action,
#urlbar #tracking-protection-icon-container,
#urlbar:not([breakout-extend='true'])
#identity-box:is(:not(.chromeUI), [pageproxystate='invalid'])
#identity-icon-box {
#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;
}
@@ -423,7 +407,7 @@
.notificationbox-stack {
background: transparent;
&[notificationside='top'] {
&[notificationside="top"] {
position: fixed;
bottom: calc(var(--zen-element-separation) * 1.5);
right: calc(var(--zen-element-separation) * 1.5);
@@ -453,12 +437,12 @@
align-items: center;
}
:root:not([zen-single-toolbar='true']) {
&[zen-right-side='true']:not([zen-window-buttons-reversed='true']) #nav-bar {
:root:not([zen-single-toolbar="true"]) {
&[zen-right-side="true"]:not([zen-window-buttons-reversed="true"]) #nav-bar {
margin-inline-start: var(--zen-element-separation);
}
&:not([zen-right-side='true'])[zen-window-buttons-reversed='true'] #nav-bar {
&:not([zen-right-side="true"])[zen-window-buttons-reversed="true"] #nav-bar {
margin-inline-end: var(--zen-element-separation);
}
}
@@ -489,17 +473,17 @@
}
}
:root[zen-single-toolbar='true'] {
:root[zen-single-toolbar="true"] {
#urlbar[open] {
min-width: min(90%, 40rem);
}
&[zen-right-side='true'] #urlbar[open]:not([zen-floating-urlbar='true']) {
&[zen-right-side="true"] #urlbar[open]:not([zen-floating-urlbar="true"]) {
right: 0;
}
}
#urlbar[open][zen-floating-urlbar='true'] {
#urlbar[open][zen-floating-urlbar="true"] {
z-index: 1000;
max-width: unset;
--urlbar-container-height: 62px !important;
@@ -520,7 +504,7 @@
background: var(--toolbarbutton-hover-background);
height: var(--urlbar-height) !important;
margin-inline: 0.15rem !important;
:root:not([zen-single-toolbar='true']) & {
:root:not([zen-single-toolbar="true"]) & {
max-height: 32px !important;
min-height: unset !important;
margin-block: -1px !important;
@@ -528,7 +512,7 @@
}
}
@media not -moz-pref('zen.urlbar.show-protections-icon') {
@media not -moz-pref("zen.urlbar.show-protections-icon") {
#tracking-protection-icon-container {
display: none !important;
}
@@ -601,13 +585,13 @@
}
}
.urlbarView-row[has-action]:is([type='switchtab'], [type='remotetab'], [type='clipboard']) {
.urlbarView-row[has-action]:is([type="switchtab"], [type="remotetab"], [type="clipboard"]) {
& .urlbarView-action:last-child {
margin-left: auto !important;
margin-right: 0 !important;
}
&:is([type='switchtab'], [type='remotetab']) .urlbarView-action:last-child {
&:is([type="switchtab"], [type="remotetab"]) .urlbarView-action:last-child {
background: transparent !important;
align-items: center;
display: flex;
@@ -619,13 +603,13 @@
transform-origin: right;
&::after {
content: '';
content: "";
display: inline-block;
width: 28px;
height: 28px;
background-color: currentColor;
mask-image: url('chrome://browser/skin/zen-icons/urlbar-arrow.svg');
mask-image: url("chrome://browser/skin/zen-icons/urlbar-arrow.svg");
mask-size: contain;
mask-repeat: no-repeat;
mask-position: center;
@@ -648,11 +632,7 @@
#urlbar-label-box,
#urlbar-search-mode-indicator {
background-color: color-mix(
in srgb,
var(--zen-primary-color) 50%,
light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.2)) 50%
) !important;
background-color: color-mix(in srgb, var(--zen-primary-color) 50%, light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.2)) 50%) !important;
color: white;
padding: 4px 8px;
border-radius: 50px;
@@ -667,20 +647,12 @@
&:hover {
&,
& .urlbarView-favicon {
background-color: color-mix(
in srgb,
var(--zen-branding-bg-reverse) 5%,
transparent 95%
) !important;
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse) 5%, transparent 95%) !important;
}
}
&[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) 50%, 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;
@@ -698,7 +670,7 @@
color: var(--zen-selected-color) !important;
}
&:is([type='switchtab'], [type='dynamic']) .urlbarView-favicon,
&:is([type="switchtab"], [type="dynamic"]) .urlbarView-favicon,
& .urlbarView-shortcutContent {
fill: var(--zen-selected-color) !important;
background-color: rgba(255, 255, 255, 0.9) !important;
@@ -723,8 +695,7 @@
}
}
:root:not([zen-single-toolbar='true'])
#urlbar[breakout-extend='true']:not([zen-floating-urlbar='true']) {
:root:not([zen-single-toolbar="true"]) #urlbar[breakout-extend="true"]:not([zen-floating-urlbar="true"]) {
& .urlbar-background {
border-radius: 10px !important;
}
@@ -759,7 +730,7 @@
display: none !important;
}
@media not -moz-pref('zen.urlbar.show-contextual-id') {
@media not -moz-pref("zen.urlbar.show-contextual-id") {
#userContext-icons {
display: none !important;
}

View File

@@ -3,30 +3,30 @@
* 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/.
*/
panel[type='arrow'] {
panel[type="arrow"] {
@media (-moz-platform: macos) and (-moz-panel-animations) {
&[side='bottom'] {
&[side="bottom"] {
/* Animate from the bottom */
-zen-window-transform-origin: 0 100%;
}
:root[zen-right-side='true'] & {
:root[zen-right-side="true"] & {
/* Animate from the right */
-zen-window-transform-origin: 100% 0;
&[side='bottom'] {
&[side="bottom"] {
/* Animate from the bottom right */
-zen-window-transform-origin: 100% 100%;
}
}
-moz-window-opacity: 0;
&[animate='open'] {
&[animate="open"] {
animation: zen-jello-animation-macos 0.2s ease-in-out forwards !important;
}
}
}
menupopup,
panel[type='arrow']:not(#feature-callout) {
panel[type="arrow"]:not(#feature-callout) {
@media (-moz-windows-mica-popups) {
appearance: auto !important;
-moz-default-appearance: menupopup;

View File

@@ -3,15 +3,14 @@
* 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 url('chrome://browser/content/zen-styles/zen-panels/bookmarks.css');
@import url('chrome://browser/content/zen-styles/zen-panels/print.css');
@import url('chrome://browser/content/zen-styles/zen-panels/dialog.css');
@import url("chrome://browser/content/zen-styles/zen-panels/bookmarks.css");
@import url("chrome://browser/content/zen-styles/zen-panels/print.css");
@import url("chrome://browser/content/zen-styles/zen-panels/dialog.css");
:root {
--panel-subview-body-padding: 2px 0;
--arrowpanel-menuitem-border-radius: 5px;
--arrowpanel-menuitem-margin: var(--uc-arrowpanel-menuitem-margin-block)
var(--uc-arrowpanel-menuitem-margin-inline);
--arrowpanel-menuitem-margin: var(--uc-arrowpanel-menuitem-margin-block) var(--uc-arrowpanel-menuitem-margin-inline);
--arrowpanel-menuitem-padding-block: 8px;
--arrowpanel-menuitem-padding-inline: 14px;
--uc-arrowpanel-menuicon-margin-inline: 14px;
@@ -22,9 +21,7 @@
--uc-panel-zoom-button-padding: 8px;
--uc-panel-zoom-button-inline-padding: 9px;
--uc-panel-zoom-padding-block: calc(
var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block)
);
--uc-panel-zoom-padding-block: calc(var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block));
--uc-autocomplete-panel-menuitem-margin: 4px;
--uc-autocomplete-panel-menuicon-padding-inline: 14px;
@@ -71,9 +68,7 @@ panel {
}
.widget-overflow-list .toolbarbutton-1:not(.toolbarbutton-combined) > .toolbarbutton-text,
.subviewbutton:not(#appMenu-zoom-controls > .subviewbutton)
> .toolbarbutton-icon
+ .toolbarbutton-text,
.subviewbutton:not(#appMenu-zoom-controls > .subviewbutton) > .toolbarbutton-icon + .toolbarbutton-text,
#appMenu-fxa-label2 > vbox {
padding-inline-start: var(--uc-arrowpanel-menuicon-margin-inline);
}
@@ -87,7 +82,7 @@ panel {
/* Firefox profile avatar in appmenu */
#appMenu-fxa-label2::before {
content: '';
content: "";
display: -moz-box;
height: 16px;
width: 16px;
@@ -112,10 +107,7 @@ panel {
/* zoom controls */
#appMenu-zoom-controls {
border-top: 1px solid var(--panel-separator-color);
padding-inline: calc(
var(--arrowpanel-menuitem-padding-inline) + var(--uc-arrowpanel-menuitem-margin-inline)
)
var(--uc-arrowpanel-menuitem-margin-inline);
padding-inline: calc(var(--arrowpanel-menuitem-padding-inline) + var(--uc-arrowpanel-menuitem-margin-inline)) var(--uc-arrowpanel-menuitem-margin-inline);
padding-block: var(--uc-panel-zoom-padding-block);
margin: var(--panel-separator-margin-vertical) 0 calc(var(--panel-separator-margin-vertical) * -1);
}
@@ -131,23 +123,18 @@ panel {
/* #appMenu-zoomReduce-button2, */
#appMenu-zoom-controls > #appMenu-fullscreen-button2 {
margin-left: calc(
(var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block)) * 2 + 1px
);
margin-left: calc((var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block)) * 2 + 1px);
}
#appMenu-zoom-controls > #appMenu-fullscreen-button2::before {
content: '';
content: "";
border-inline-start: 1px solid var(--panel-separator-color);
display: block;
position: relative;
height: 32px;
margin-block: calc(var(--uc-panel-zoom-button-padding) * -1);
transform: translateX(
calc(
var(--uc-panel-zoom-button-inline-padding) * -1 -
(var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block)) - 1px
)
calc(var(--uc-panel-zoom-button-inline-padding) * -1 - (var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block)) - 1px)
);
}
@@ -319,7 +306,7 @@ menuseparator {
cursor: pointer;
}
#editBMPanel_workspaceList input[type='checkbox'] {
#editBMPanel_workspaceList input[type="checkbox"] {
margin-right: 8px;
}
@@ -333,16 +320,16 @@ menuseparator {
display: flex;
align-items: end;
:root:not([zen-right-side='true']) & {
:root:not([zen-right-side="true"]) & {
right: var(--zen-toast-spacing);
}
:root[zen-right-side='true'] & {
:root[zen-right-side="true"] & {
left: var(--zen-toast-spacing);
}
& .zen-toast {
:root[zen-right-side='true'] & {
:root[zen-right-side="true"] & {
translate: 100%;
}
@@ -357,11 +344,7 @@ menuseparator {
z-index: 1000;
padding: var(--zen-toast-padding);
border-radius: 10px;
background: linear-gradient(
to bottom,
var(--zen-primary-color) -40%,
color-mix(in srgb, var(--zen-primary-color), #0f0f0f 20%)
);
background: linear-gradient(to bottom, var(--zen-primary-color) -40%, color-mix(in srgb, var(--zen-primary-color), #0f0f0f 20%));
box-shadow: light-dark(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.4)) 0px 0px 22px 2px;
border: none;
display: flex;
@@ -408,7 +391,7 @@ menuseparator {
outline: 0.5px solid light-dark(transparent, rgba(255, 255, 255, 0.02));
outline-offset: -0.5px;
:root[zen-right-side='true'] & {
:root[zen-right-side="true"] & {
order: -1;
}

View File

@@ -92,13 +92,7 @@
opacity: 1;
--special-color: color-mix(in srgb, var(--zen-primary-color), currentColor 50%);
--specia-color-2: color-mix(in srgb, var(--zen-primary-color), currentColor 90%);
background: linear-gradient(
135deg,
var(--specia-color-2),
var(--special-color),
var(--specia-color-2),
var(--special-color)
);
background: linear-gradient(135deg, var(--specia-color-2), var(--special-color), var(--specia-color-2), var(--special-color));
background-size: 400%;
filter: saturate(3);
background-clip: text;

View File

@@ -23,7 +23,7 @@
display: none !important;
}
@media -moz-pref('zen.theme.disable-lightweight') {
@media -moz-pref("zen.theme.disable-lightweight") {
#customization-lwtheme-link {
display: none !important;
}
@@ -76,7 +76,7 @@ body > #confetti {
}
}
&[only-svg-icons='true'] {
&[only-svg-icons="true"] {
& #PanelUI-zen-emojis-picker-change-emojis {
display: none;
}
@@ -85,7 +85,7 @@ body > #confetti {
pointer-events: none;
}
& #PanelUI-zen-emojis-picker-pages > vbox[emojis='true'] {
& #PanelUI-zen-emojis-picker-pages > vbox[emojis="true"] {
display: none;
}
}
@@ -186,11 +186,11 @@ body > #confetti {
background-color 0.1s,
transform 0.2s;
&[open='true'] {
&[open="true"] {
transition: background-color 0.1s;
}
&:not([open='true']):active:hover {
&:not([open="true"]):active:hover {
transform: scale(0.95);
}
}
@@ -216,13 +216,7 @@ body > #confetti {
position: fixed;
width: 100%;
height: 100%;
background: linear-gradient(
to bottom,
transparent,
rgba(255, 255, 255, 0.5) 40%,
rgba(255, 255, 255, 0.5) 60%,
transparent 100%
);
background: linear-gradient(to bottom, transparent, rgba(255, 255, 255, 0.5) 40%, rgba(255, 255, 255, 0.5) 60%, transparent 100%);
left: 0;
z-index: 999;
opacity: 0;
@@ -238,17 +232,13 @@ body > #confetti {
}
#zen-update-animation-border::before {
content: '';
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
padding: 2px; /* thickness of border */
background: radial-gradient(
ellipse 100% 50% at center var(--background-top),
rgba(255, 255, 255, 0.5) 80%,
transparent
);
background: radial-gradient(ellipse 100% 50% at center var(--background-top), rgba(255, 255, 255, 0.5) 80%, transparent);
/* Punch out the inside once (not animated) */
mask:
@@ -264,7 +254,7 @@ body > #confetti {
/* Status panel */
@media (-moz-pref('zen.theme.styled-status-panel')) {
@media (-moz-pref("zen.theme.styled-status-panel")) {
#statuspanel {
padding: 6px;
@@ -310,7 +300,7 @@ body > #confetti {
display: none !important;
}
&[overflowing='true'] {
&[overflowing="true"] {
overflow-x: auto;
max-height: 420px;
mask-image: linear-gradient(to top, transparent, black 5%);
@@ -443,7 +433,7 @@ body > #confetti {
}
&::before {
content: '';
content: "";
position: absolute;
inset: 1px;
border-radius: 99px;
@@ -464,7 +454,7 @@ body > #confetti {
transform: scale(0.95);
}
.permission-popup-permission-item[state='allow'] &::before {
.permission-popup-permission-item[state="allow"] &::before {
opacity: 1;
}
}
@@ -491,13 +481,13 @@ body > #confetti {
}
}
@media -moz-pref('zen.theme.hide-unified-extensions-button') {
@media -moz-pref("zen.theme.hide-unified-extensions-button") {
#unified-extensions-button:not([showing]) {
display: none !important;
}
}
@media not -moz-pref('zen.theme.hide-unified-extensions-button') {
@media not -moz-pref("zen.theme.hide-unified-extensions-button") {
#zen-site-data-section-addons {
display: none;
}
@@ -509,7 +499,7 @@ body > #confetti {
padding: 10px 9px;
padding-bottom: 8px;
:root[zen-single-toolbar='true']:not([zen-right-side='true']) & {
:root[zen-single-toolbar="true"]:not([zen-right-side="true"]) & {
flex-direction: row-reverse;
}
@@ -539,16 +529,12 @@ body > #confetti {
}
&::before {
content: '';
content: "";
position: absolute;
width: 100%;
height: 100%;
@media (-moz-platform: macos) {
background: linear-gradient(
to bottom,
light-dark(rgb(255, 255, 255), rgb(34, 34, 34)),
light-dark(rgb(246, 246, 246), rgb(21, 21, 21))
);
background: linear-gradient(to bottom, light-dark(rgb(255, 255, 255), rgb(34, 34, 34)), light-dark(rgb(246, 246, 246), rgb(21, 21, 21)));
box-shadow:
0px 2px 4px rgba(0, 0, 0, 0.1),
@@ -624,6 +610,6 @@ body > #confetti {
}
/* Sidebar notification */
:root:not([zen-sidebar-expanded='true']) zen-sidebar-notification {
:root:not([zen-sidebar-expanded="true"]) zen-sidebar-notification {
display: none;
}

View File

@@ -8,6 +8,6 @@
background: transparent;
}
:root[inDOMFullscreen='true'] #zen-appcontent-navbar-wrapper {
:root[inDOMFullscreen="true"] #zen-appcontent-navbar-wrapper {
visibility: collapse;
}

View File

@@ -3,7 +3,7 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Utility to register JSWindowActors
import { ActorManagerParent } from 'resource://gre/modules/ActorManagerParent.sys.mjs';
import { ActorManagerParent } from "resource://gre/modules/ActorManagerParent.sys.mjs";
/**
* Fission-compatible JSProcess implementations.
@@ -21,25 +21,25 @@ let JSPROCESSACTORS = {};
let JSWINDOWACTORS = {
ZenModsMarketplace: {
parent: {
esModuleURI: 'resource:///actors/ZenModsMarketplaceParent.sys.mjs',
esModuleURI: "resource:///actors/ZenModsMarketplaceParent.sys.mjs",
},
child: {
esModuleURI: 'resource:///actors/ZenModsMarketplaceChild.sys.mjs',
esModuleURI: "resource:///actors/ZenModsMarketplaceChild.sys.mjs",
events: {
DOMContentLoaded: {},
},
},
matches: [
...Services.prefs.getStringPref('zen.injections.match-urls').split(','),
'about:preferences',
...Services.prefs.getStringPref("zen.injections.match-urls").split(","),
"about:preferences",
],
},
ZenGlance: {
parent: {
esModuleURI: 'resource:///actors/ZenGlanceParent.sys.mjs',
esModuleURI: "resource:///actors/ZenGlanceParent.sys.mjs",
},
child: {
esModuleURI: 'resource:///actors/ZenGlanceChild.sys.mjs',
esModuleURI: "resource:///actors/ZenGlanceChild.sys.mjs",
events: {
DOMContentLoaded: {},
mousedown: {
@@ -54,8 +54,8 @@ let JSWINDOWACTORS = {
},
},
allFrames: true,
matches: ['*://*/*'],
enablePreference: 'zen.glance.enabled',
matches: ["*://*/*"],
enablePreference: "zen.glance.enabled",
},
};

View File

@@ -2,27 +2,27 @@
// 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 { AppConstants } from 'resource://gre/modules/AppConstants.sys.mjs';
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
export const ZenCustomizableUI = new (class {
constructor() {}
TYPE_TOOLBAR = 'toolbar';
defaultSidebarIcons = ['downloads-button', 'zen-workspaces-button', 'zen-create-new-button'];
TYPE_TOOLBAR = "toolbar";
defaultSidebarIcons = ["downloads-button", "zen-workspaces-button", "zen-create-new-button"];
startup(CustomizableUIInternal) {
CustomizableUIInternal.registerArea(
'zen-sidebar-top-buttons',
"zen-sidebar-top-buttons",
{
type: this.TYPE_TOOLBAR,
defaultPlacements: ['zen-toggle-compact-mode'],
defaultPlacements: ["zen-toggle-compact-mode"],
defaultCollapsed: null,
overflowable: true,
},
true
);
CustomizableUIInternal.registerArea(
'zen-sidebar-foot-buttons',
"zen-sidebar-foot-buttons",
{
type: this.TYPE_TOOLBAR,
defaultPlacements: this.defaultSidebarIcons,
@@ -39,16 +39,16 @@ export const ZenCustomizableUI = new (class {
}
#addSidebarButtons(window) {
const kDefaultSidebarWidth = AppConstants.platform === 'macosx' ? '230px' : '186px';
const kDefaultSidebarWidth = AppConstants.platform === "macosx" ? "230px" : "186px";
const toolbox = window.gNavToolbox;
// Set a splitter to navigator-toolbox
const splitter = window.document.createXULElement('splitter');
splitter.setAttribute('id', 'zen-sidebar-splitter');
splitter.setAttribute('orient', 'horizontal');
splitter.setAttribute('resizebefore', 'sibling');
splitter.setAttribute('resizeafter', 'none');
toolbox.insertAdjacentElement('afterend', splitter);
const splitter = window.document.createXULElement("splitter");
splitter.setAttribute("id", "zen-sidebar-splitter");
splitter.setAttribute("orient", "horizontal");
splitter.setAttribute("resizebefore", "sibling");
splitter.setAttribute("resizeafter", "none");
toolbox.insertAdjacentElement("afterend", splitter);
const sidebarBox = window.MozXULElement.parseXULToFragment(`
<toolbar id="zen-sidebar-top-buttons"
@@ -82,7 +82,9 @@ export const ZenCustomizableUI = new (class {
`);
toolbox.prepend(sidebarBox);
new window.MutationObserver((e) => {
if (e[0].type !== 'attributes' || e[0].attributeName !== 'width') return;
if (e[0].type !== "attributes" || e[0].attributeName !== "width") {
return;
}
this._dispatchResizeEvent(window);
}).observe(toolbox, {
attributes: true, //configure it to listen to attribute changes
@@ -90,23 +92,27 @@ export const ZenCustomizableUI = new (class {
// remove all styles except for the width, since we are xulstoring the complet style list
const width = toolbox.style.width || kDefaultSidebarWidth;
toolbox.removeAttribute('style');
toolbox.removeAttribute("style");
toolbox.style.width = width;
toolbox.setAttribute('width', width);
toolbox.setAttribute("width", width);
splitter.addEventListener('dblclick', (e) => {
if (e.button !== 0) return;
splitter.addEventListener("dblclick", (e) => {
if (e.button !== 0) {
return;
}
toolbox.style.width = kDefaultSidebarWidth;
toolbox.setAttribute('width', kDefaultSidebarWidth);
toolbox.setAttribute("width", kDefaultSidebarWidth);
});
const newTab = window.document.getElementById('vertical-tabs-newtab-button');
newTab.classList.add('zen-sidebar-action-button');
const newTab = window.document.getElementById("vertical-tabs-newtab-button");
newTab.classList.add("zen-sidebar-action-button");
for (let id of this.defaultSidebarIcons) {
const elem = window.document.getElementById(id);
if (!elem || elem.id === 'zen-workspaces-button') continue;
elem.setAttribute('removable', 'true');
if (!elem || elem.id === "zen-workspaces-button") {
continue;
}
elem.setAttribute("removable", "true");
}
this.#initCreateNewButton(window);
@@ -114,18 +120,19 @@ export const ZenCustomizableUI = new (class {
}
#initCreateNewButton(window) {
const button = window.document.getElementById('zen-create-new-button');
button.addEventListener('command', (event) => {
const button = window.document.getElementById("zen-create-new-button");
button.addEventListener("command", (event) => {
if (window.gZenWorkspaces.privateWindowOrDisabled) {
return window.document.getElementById('cmd_newNavigatorTab').doCommand();
}
if (button.hasAttribute('open')) {
window.document.getElementById("cmd_newNavigatorTab").doCommand();
return;
}
const popup = window.document.getElementById('zenCreateNewPopup');
if (button.hasAttribute("open")) {
return;
}
const popup = window.document.getElementById("zenCreateNewPopup");
popup.openPopup(
button,
'before_start',
"before_start",
0,
0,
true /* isContextMenu */,
@@ -136,13 +143,13 @@ export const ZenCustomizableUI = new (class {
}
#moveWindowButtons(window) {
const windowControls = window.document.getElementsByClassName('titlebar-buttonbox-container');
const windowControls = window.document.getElementsByClassName("titlebar-buttonbox-container");
const toolboxIcons = window.document.getElementById(
'zen-sidebar-top-buttons-customization-target'
"zen-sidebar-top-buttons-customization-target"
);
if (
window.AppConstants.platform === 'macosx' ||
window.matchMedia('(-moz-gtk-csd-reversed-placement)').matches
window.AppConstants.platform === "macosx" ||
window.matchMedia("(-moz-gtk-csd-reversed-placement)").matches
) {
for (let i = 0; i < windowControls.length; i++) {
if (i === 0) {
@@ -155,27 +162,27 @@ export const ZenCustomizableUI = new (class {
}
#modifyToolbarButtons(window) {
const wrapper = window.document.getElementById('zen-sidebar-foot-buttons');
const elementsToHide = ['new-tab-button'];
const wrapper = window.document.getElementById("zen-sidebar-foot-buttons");
const elementsToHide = ["new-tab-button"];
for (let id of elementsToHide) {
const elem = window.document.getElementById(id);
if (elem) {
wrapper.prepend(elem);
}
}
window.document.getElementById('stop-reload-button').removeAttribute('overflows');
window.document.getElementById("stop-reload-button").removeAttribute("overflows");
}
_dispatchResizeEvent(window) {
window.dispatchEvent(new window.Event('resize'));
window.dispatchEvent(new window.Event("resize"));
}
registerToolbarNodes(window) {
window.CustomizableUI.registerToolbarNode(
window.document.getElementById('zen-sidebar-top-buttons')
window.document.getElementById("zen-sidebar-top-buttons")
);
window.CustomizableUI.registerToolbarNode(
window.document.getElementById('zen-sidebar-foot-buttons')
window.document.getElementById("zen-sidebar-foot-buttons")
);
}
})();

View File

@@ -2,10 +2,10 @@
* 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 { AppConstants } from 'resource://gre/modules/AppConstants.sys.mjs';
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
class nsZenUIMigration {
PREF_NAME = 'zen.ui.migration.version';
PREF_NAME = "zen.ui.migration.version";
MIGRATION_VERSION = 5;
init(isNewProfile) {
@@ -13,7 +13,7 @@ class nsZenUIMigration {
try {
this._migrate();
} catch (e) {
console.error('ZenUIMigration: Error during migration', e);
console.error("ZenUIMigration: Error during migration", e);
}
}
this.clearVariables();
@@ -46,48 +46,48 @@ class nsZenUIMigration {
// If there's an userChrome.css or userContent.css existing, we set
// 'toolkit.legacyUserProfileCustomizations.stylesheets' back to true
// We do this to avoid existing user stylesheets to be ignored
const profileDir = Services.dirsvc.get('ProfD', Ci.nsIFile);
const profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
const userChromeFile = profileDir.clone();
userChromeFile.append('chrome');
userChromeFile.append('userChrome.css');
userChromeFile.append("chrome");
userChromeFile.append("userChrome.css");
const userContentFile = profileDir.clone();
userContentFile.append('chrome');
userContentFile.append('userContent.css');
userContentFile.append("chrome");
userContentFile.append("userContent.css");
Services.prefs.setBoolPref(
'zen.workspaces.separate-essentials',
Services.prefs.getBoolPref('zen.workspaces.container-specific-essentials-enabled', false)
"zen.workspaces.separate-essentials",
Services.prefs.getBoolPref("zen.workspaces.container-specific-essentials-enabled", false)
);
const theme = Services.prefs.getIntPref('layout.css.prefers-color-scheme.content-override', 0);
Services.prefs.setIntPref('zen.view.window.scheme', theme);
const theme = Services.prefs.getIntPref("layout.css.prefers-color-scheme.content-override", 0);
Services.prefs.setIntPref("zen.view.window.scheme", theme);
if (userChromeFile.exists() || userContentFile.exists()) {
Services.prefs.setBoolPref('toolkit.legacyUserProfileCustomizations.stylesheets', true);
console.log('ZenUIMigration: User stylesheets detected, enabling legacy stylesheets.');
Services.prefs.setBoolPref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
console.warn("ZenUIMigration: User stylesheets detected, enabling legacy stylesheets.");
this.shouldRestart = true;
}
}
_migrateV2() {
if (AppConstants.platform !== 'linux') {
Services.prefs.setIntPref('zen.theme.gradient-legacy-version', 0);
if (AppConstants.platform !== "linux") {
Services.prefs.setIntPref("zen.theme.gradient-legacy-version", 0);
}
}
_migrateV3() {
if (Services.prefs.getStringPref('zen.theme.accent-color', '').startsWith('system')) {
Services.prefs.setStringPref('zen.theme.accent-color', 'AccentColor');
if (Services.prefs.getStringPref("zen.theme.accent-color", "").startsWith("system")) {
Services.prefs.setStringPref("zen.theme.accent-color", "AccentColor");
}
}
_migrateV4() {
// Fix spelling mistake in preference name
Services.prefs.setBoolPref(
'zen.theme.use-system-colors',
Services.prefs.getBoolPref('zen.theme.use-sysyem-colors', false)
"zen.theme.use-system-colors",
Services.prefs.getBoolPref("zen.theme.use-sysyem-colors", false)
);
}
_migrateV5() {
Services.prefs.setBoolPref('zen.site-data-panel.show-callout', true);
Services.prefs.setBoolPref("zen.site-data-panel.show-callout", true);
}
}

View File

@@ -3,114 +3,115 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
document.addEventListener(
'MozBeforeInitialXULLayout',
"MozBeforeInitialXULLayout",
() => {
// <commandset id="mainCommandSet"> defined in browser-sets.inc
document.getElementById('zenCommandSet').addEventListener('command', (event) => {
// eslint-disable-next-line complexity
document.getElementById("zenCommandSet").addEventListener("command", (event) => {
switch (event.target.id) {
case 'cmd_zenCompactModeToggle':
case "cmd_zenCompactModeToggle":
gZenCompactModeManager.toggle();
break;
case 'cmd_zenCompactModeShowSidebar':
case "cmd_zenCompactModeShowSidebar":
gZenCompactModeManager.toggleSidebar();
break;
case 'cmd_toggleCompactModeIgnoreHover':
case "cmd_toggleCompactModeIgnoreHover":
gZenCompactModeManager.toggle(true);
break;
case 'cmd_zenWorkspaceForward':
case "cmd_zenWorkspaceForward":
gZenWorkspaces.changeWorkspaceShortcut();
break;
case 'cmd_zenWorkspaceBackward':
case "cmd_zenWorkspaceBackward":
gZenWorkspaces.changeWorkspaceShortcut(-1);
break;
case 'cmd_zenSplitViewGrid':
gZenViewSplitter.toggleShortcut('grid');
case "cmd_zenSplitViewGrid":
gZenViewSplitter.toggleShortcut("grid");
break;
case 'cmd_zenSplitViewVertical':
gZenViewSplitter.toggleShortcut('vsep');
case "cmd_zenSplitViewVertical":
gZenViewSplitter.toggleShortcut("vsep");
break;
case 'cmd_zenSplitViewHorizontal':
gZenViewSplitter.toggleShortcut('hsep');
case "cmd_zenSplitViewHorizontal":
gZenViewSplitter.toggleShortcut("hsep");
break;
case 'cmd_zenSplitViewUnsplit':
gZenViewSplitter.toggleShortcut('unsplit');
case "cmd_zenSplitViewUnsplit":
gZenViewSplitter.toggleShortcut("unsplit");
break;
case 'cmd_zenSplitViewContextMenu':
case "cmd_zenSplitViewContextMenu":
gZenViewSplitter.contextSplitTabs();
break;
case 'cmd_zenCopyCurrentURLMarkdown':
case "cmd_zenCopyCurrentURLMarkdown":
gZenCommonActions.copyCurrentURLAsMarkdownToClipboard();
break;
case 'cmd_zenCopyCurrentURL':
case "cmd_zenCopyCurrentURL":
gZenCommonActions.copyCurrentURLToClipboard();
break;
case 'cmd_zenPinnedTabReset':
case "cmd_zenPinnedTabReset":
gZenPinnedTabManager.resetPinnedTab(gBrowser.selectedTab);
break;
case 'cmd_zenPinnedTabResetNoTab':
case "cmd_zenPinnedTabResetNoTab":
gZenPinnedTabManager.resetPinnedTab();
break;
case 'cmd_zenToggleSidebar':
case "cmd_zenToggleSidebar":
gZenVerticalTabsManager.toggleExpand();
break;
case 'cmd_zenOpenZenThemePicker':
case "cmd_zenOpenZenThemePicker":
gZenThemePicker.openThemePicker(event);
break;
case 'cmd_zenChangeWorkspaceTab':
case "cmd_zenChangeWorkspaceTab":
gZenWorkspaces.changeTabWorkspace(
event.sourceEvent.target.getAttribute('zen-workspace-id')
event.sourceEvent.target.getAttribute("zen-workspace-id")
);
break;
case 'cmd_zenToggleTabsOnRight':
case "cmd_zenToggleTabsOnRight":
gZenVerticalTabsManager.toggleTabsOnRight();
break;
case 'cmd_zenSplitViewLinkInNewTab':
case "cmd_zenSplitViewLinkInNewTab":
gZenViewSplitter.splitLinkInNewTab();
break;
case 'cmd_zenNewEmptySplit':
case "cmd_zenNewEmptySplit":
setTimeout(() => {
gZenViewSplitter.createEmptySplit();
}, 0);
break;
case 'cmd_zenReplacePinnedUrlWithCurrent':
case "cmd_zenReplacePinnedUrlWithCurrent":
gZenPinnedTabManager.replacePinnedUrlWithCurrent();
break;
case 'cmd_contextZenAddToEssentials':
case "cmd_contextZenAddToEssentials":
gZenPinnedTabManager.addToEssentials();
break;
case 'cmd_contextZenRemoveFromEssentials':
case "cmd_contextZenRemoveFromEssentials":
gZenPinnedTabManager.removeEssentials();
break;
case 'cmd_zenCtxDeleteWorkspace':
case "cmd_zenCtxDeleteWorkspace":
gZenWorkspaces.contextDeleteWorkspace(event);
break;
case 'cmd_zenChangeWorkspaceName':
case "cmd_zenChangeWorkspaceName":
gZenVerticalTabsManager.renameTabStart({
target: gZenWorkspaces.activeWorkspaceIndicator.querySelector(
'.zen-current-workspace-indicator-name'
".zen-current-workspace-indicator-name"
),
});
break;
case 'cmd_zenChangeWorkspaceIcon':
case "cmd_zenChangeWorkspaceIcon":
gZenWorkspaces.changeWorkspaceIcon();
break;
case 'cmd_zenReorderWorkspaces':
gZenUIManager.showToast('zen-workspaces-how-to-reorder-title', {
case "cmd_zenReorderWorkspaces":
gZenUIManager.showToast("zen-workspaces-how-to-reorder-title", {
timeout: 9000,
descriptionId: 'zen-workspaces-how-to-reorder-desc',
descriptionId: "zen-workspaces-how-to-reorder-desc",
});
break;
case 'cmd_zenOpenWorkspaceCreation':
case "cmd_zenOpenWorkspaceCreation":
gZenWorkspaces.openWorkspaceCreation(event);
break;
case 'cmd_zenOpenFolderCreation':
case "cmd_zenOpenFolderCreation":
gZenFolders.createFolder([], {
renameFolder: true,
});
break;
case 'cmd_zenTogglePinTab': {
case "cmd_zenTogglePinTab": {
const currentTab = gBrowser.selectedTab;
if (currentTab && !currentTab.hasAttribute('zen-empty-tab')) {
if (currentTab && !currentTab.hasAttribute("zen-empty-tab")) {
if (currentTab.pinned) {
gBrowser.unpinTab(currentTab);
} else {
@@ -119,20 +120,20 @@ document.addEventListener(
}
break;
}
case 'cmd_zenCloseUnpinnedTabs':
case "cmd_zenCloseUnpinnedTabs":
gZenWorkspaces.closeAllUnpinnedTabs();
break;
case 'cmd_zenUnloadWorkspace': {
case "cmd_zenUnloadWorkspace": {
gZenWorkspaces.unloadWorkspace();
break;
}
case 'cmd_zenNewNavigatorUnsynced':
case "cmd_zenNewNavigatorUnsynced":
OpenBrowserWindow({ zenSyncedWindow: false });
break;
default:
gZenGlanceManager.handleMainCommandSet(event);
if (event.target.id.startsWith('cmd_zenWorkspaceSwitch')) {
const index = parseInt(event.target.id.replace('cmd_zenWorkspaceSwitch', ''), 10) - 1;
if (event.target.id.startsWith("cmd_zenWorkspaceSwitch")) {
const index = parseInt(event.target.id.replace("cmd_zenWorkspaceSwitch", ""), 10) - 1;
gZenWorkspaces.shortcutSwitchTo(index);
}
break;

View File

@@ -2,7 +2,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/. */
'use strict';
"use strict";
/* INCLUDE THIS FILE AS:
* <script src="chrome://browser/content/zenThemeModifier.js"></script>
@@ -11,13 +11,13 @@
*/
{
const { AppConstants } = ChromeUtils.importESModule(
'resource://gre/modules/AppConstants.sys.mjs'
"resource://gre/modules/AppConstants.sys.mjs"
);
const kZenThemePrefsList = [
'zen.theme.accent-color',
'zen.theme.border-radius',
'zen.theme.content-element-separation',
"zen.theme.accent-color",
"zen.theme.border-radius",
"zen.theme.content-element-separation",
];
const kZenMaxElementSeparation = 12;
@@ -38,7 +38,7 @@
* begin listening to changes in preferred color scheme.
*/
init() {
this._inMainBrowserWindow = window.location.href == 'chrome://browser/content/browser.xhtml';
this._inMainBrowserWindow = window.location.href == "chrome://browser/content/browser.xhtml";
this.listenForEvents();
this.updateAllThemeBasics();
},
@@ -52,10 +52,10 @@
// Add fullscreen listener to update the theme when going in and out of fullscreen
const eventsForSeparation = [
'ZenViewSplitter:SplitViewDeactivated',
'ZenViewSplitter:SplitViewActivated',
'fullscreen',
'ZenCompactMode:Toggled',
"ZenViewSplitter:SplitViewDeactivated",
"ZenViewSplitter:SplitViewActivated",
"fullscreen",
"ZenCompactMode:Toggled",
];
const separationHandler = this.updateElementSeparation.bind(this);
for (let eventName of eventsForSeparation) {
@@ -63,7 +63,7 @@
}
window.addEventListener(
'unload',
"unload",
() => {
for (let pref of kZenThemePrefsList) {
Services.prefs.removeObserver(pref, handleEvent);
@@ -91,27 +91,27 @@
},
updateBorderRadius() {
const borderRadius = Services.prefs.getIntPref('zen.theme.border-radius', -1);
const borderRadius = Services.prefs.getIntPref("zen.theme.border-radius", -1);
// -1 is the default value, will use platform-native values
// otherwise, use the custom value
if (borderRadius == -1) {
if (AppConstants.platform == 'macosx') {
const targetRadius = window.matchMedia('(-moz-mac-tahoe-theme)').matches ? 15 : 10;
document.documentElement.style.setProperty('--zen-border-radius', targetRadius + 'px');
} else if (AppConstants.platform == 'linux') {
if (AppConstants.platform == "macosx") {
const targetRadius = window.matchMedia("(-moz-mac-tahoe-theme)").matches ? 15 : 10;
document.documentElement.style.setProperty("--zen-border-radius", targetRadius + "px");
} else if (AppConstants.platform == "linux") {
// Linux uses GTK CSD titlebar radius, default to 8px
document.documentElement.style.setProperty(
'--zen-border-radius',
'env(-moz-gtk-csd-titlebar-radius, 8px)'
"--zen-border-radius",
"env(-moz-gtk-csd-titlebar-radius, 8px)"
);
} else {
// Windows defaults to 8px
document.documentElement.style.setProperty('--zen-border-radius', '8px');
document.documentElement.style.setProperty("--zen-border-radius", "8px");
}
} else {
// Use the overridden value
document.documentElement.style.setProperty('--zen-border-radius', borderRadius + 'px');
document.documentElement.style.setProperty("--zen-border-radius", borderRadius + "px");
}
},
@@ -119,26 +119,26 @@
const kMinElementSeparation = 0.1; // in px
let separation = this.elementSeparation;
if (
document.documentElement.hasAttribute('inFullscreen') &&
document.documentElement.hasAttribute("inFullscreen") &&
window.gZenCompactModeManager?.preference &&
!document.getElementById('tabbrowser-tabbox')?.hasAttribute('zen-split-view') &&
Services.prefs.getBoolPref('zen.view.borderless-fullscreen', true)
!document.getElementById("tabbrowser-tabbox")?.hasAttribute("zen-split-view") &&
Services.prefs.getBoolPref("zen.view.borderless-fullscreen", true)
) {
separation = 0;
}
// In order to still use it on fullscreen, even if it's 0px, add .1px (almost invisible)
separation = Math.max(kMinElementSeparation, separation);
document.documentElement.style.setProperty('--zen-element-separation', separation + 'px');
document.documentElement.style.setProperty("--zen-element-separation", separation + "px");
if (separation == kMinElementSeparation) {
document.documentElement.setAttribute('zen-no-padding', true);
document.documentElement.setAttribute("zen-no-padding", true);
} else {
document.documentElement.removeAttribute('zen-no-padding');
document.documentElement.removeAttribute("zen-no-padding");
}
},
get elementSeparation() {
return Math.min(
Services.prefs.getIntPref('zen.theme.content-element-separation'),
Services.prefs.getIntPref("zen.theme.content-element-separation"),
kZenMaxElementSeparation
);
},
@@ -147,10 +147,12 @@
* Update the accent color.
*/
updateAccentColor() {
const accentColor = Services.prefs.getStringPref('zen.theme.accent-color');
document.documentElement.style.setProperty('--zen-primary-color', accentColor);
const accentColor = Services.prefs.getStringPref("zen.theme.accent-color");
document.documentElement.style.setProperty("--zen-primary-color", accentColor);
},
};
if (typeof Services !== 'undefined') ZenThemeModifier.init();
if (typeof Services !== "undefined") {
ZenThemeModifier.init();
}
}

View File

@@ -6,34 +6,34 @@ const lazy = {};
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'COMPACT_MODE_FLASH_DURATION',
'zen.view.compact.toolbar-flash-popup.duration',
"COMPACT_MODE_FLASH_DURATION",
"zen.view.compact.toolbar-flash-popup.duration",
800
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'COMPACT_MODE_FLASH_ENABLED',
'zen.view.compact.toolbar-flash-popup',
"COMPACT_MODE_FLASH_ENABLED",
"zen.view.compact.toolbar-flash-popup",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'COMPACT_MODE_CAN_ANIMATE_SIDEBAR',
'zen.view.compact.animate-sidebar',
"COMPACT_MODE_CAN_ANIMATE_SIDEBAR",
"zen.view.compact.animate-sidebar",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'COMPACT_MODE_SHOW_SIDEBAR_AND_TOOLBAR_ON_HOVER',
'zen.view.compact.show-sidebar-and-toolbar-on-hover',
"COMPACT_MODE_SHOW_SIDEBAR_AND_TOOLBAR_ON_HOVER",
"zen.view.compact.show-sidebar-and-toolbar-on-hover",
true
);
ChromeUtils.defineLazyGetter(lazy, 'mainAppWrapper', () =>
document.getElementById('zen-main-app-wrapper')
ChromeUtils.defineLazyGetter(lazy, "mainAppWrapper", () =>
document.getElementById("zen-main-app-wrapper")
);
window.gZenCompactModeManager = {
@@ -42,14 +42,14 @@ window.gZenCompactModeManager = {
_removeHoverFrames: {},
// Delay to avoid flickering when hovering over the sidebar
HOVER_HACK_DELAY: Services.prefs.getIntPref('zen.view.compact.hover-hack-delay', 0),
HOVER_HACK_DELAY: Services.prefs.getIntPref("zen.view.compact.hover-hack-delay", 0),
preInit() {
this._wasInCompactMode = Services.prefs.getBoolPref(
'zen.view.compact.enable-at-startup',
"zen.view.compact.enable-at-startup",
false
);
this._canDebugLog = Services.prefs.getBoolPref('zen.view.compact.debug', false);
this._canDebugLog = Services.prefs.getBoolPref("zen.view.compact.debug", false);
this.addContextMenu();
},
@@ -58,35 +58,37 @@ window.gZenCompactModeManager = {
this.addMouseActions();
const tabIsRightObserver = this._updateSidebarIsOnRight.bind(this);
Services.prefs.addObserver('zen.tabs.vertical.right-side', tabIsRightObserver);
Services.prefs.addObserver("zen.tabs.vertical.right-side", tabIsRightObserver);
window.addEventListener(
'unload',
"unload",
() => {
Services.prefs.removeObserver('zen.tabs.vertical.right-side', tabIsRightObserver);
Services.prefs.removeObserver("zen.tabs.vertical.right-side", tabIsRightObserver);
},
{ once: true }
);
gZenUIManager.addPopupTrackingAttribute(this.sidebar);
gZenUIManager.addPopupTrackingAttribute(
document.getElementById('zen-appcontent-navbar-wrapper')
document.getElementById("zen-appcontent-navbar-wrapper")
);
this.addHasPolyfillObserver();
// Clear hover states when window state changes (minimize, maximize, etc.)
window.addEventListener('sizemodechange', () => this._clearAllHoverStates());
window.addEventListener("sizemodechange", () => this._clearAllHoverStates());
this._canShowBackgroundTabToast = Services.prefs.getBoolPref(
'zen.view.compact.show-background-tab-toast',
"zen.view.compact.show-background-tab-toast",
true
);
if (AppConstants.platform == 'macosx') {
window.addEventListener('mouseover', (event) => {
if (AppConstants.platform == "macosx") {
window.addEventListener("mouseover", (event) => {
const buttons = gZenVerticalTabsManager.actualWindowButtons;
if (event.target.closest('.titlebar-buttonbox-container') === buttons) return;
if (event.target.closest(".titlebar-buttonbox-container") === buttons) {
return;
}
this._setElementExpandAttribute(buttons, false);
});
}
@@ -98,28 +100,29 @@ window.gZenCompactModeManager = {
log(...args) {
if (this._canDebugLog) {
console.log('[Zen Compact Mode]', ...args);
// eslint-disable-next-line no-console
console.debug("[Zen Compact Mode]", ...args);
}
},
get preference() {
return document.documentElement.getAttribute('zen-compact-mode') === 'true';
return document.documentElement.getAttribute("zen-compact-mode") === "true";
},
get shouldBeCompact() {
return !document.documentElement.getAttribute('chromehidden')?.includes('toolbar');
return !document.documentElement.getAttribute("chromehidden")?.includes("toolbar");
},
set preference(value) {
if (!this.shouldBeCompact) {
value = false;
}
this.log('Setting compact mode preference to', value);
this.log("Setting compact mode preference to", value);
if (
this.preference === value ||
document.documentElement.hasAttribute('zen-compact-animating')
document.documentElement.hasAttribute("zen-compact-animating")
) {
if (typeof this._wasInCompactMode !== 'undefined') {
if (typeof this._wasInCompactMode !== "undefined") {
// We wont do anything with it anyway, so we remove it
delete this._wasInCompactMode;
}
@@ -127,22 +130,22 @@ window.gZenCompactModeManager = {
// We dont want the user to be able to spam the button
return;
}
this.sidebar.removeAttribute('zen-user-show');
this.sidebar.removeAttribute("zen-user-show");
// We use this element in order to make it persis across restarts, by using the XULStore.
// main-window can't store attributes other than window sizes, so we use this instead
lazy.mainAppWrapper.setAttribute('zen-compact-mode', value);
document.documentElement.setAttribute('zen-compact-mode', value);
if (typeof this._wasInCompactMode === 'undefined') {
Services.prefs.setBoolPref('zen.view.compact.enable-at-startup', value);
lazy.mainAppWrapper.setAttribute("zen-compact-mode", value);
document.documentElement.setAttribute("zen-compact-mode", value);
if (typeof this._wasInCompactMode === "undefined") {
Services.prefs.setBoolPref("zen.view.compact.enable-at-startup", value);
}
this._updateEvent();
},
get sidebarIsOnRight() {
if (typeof this._sidebarIsOnRight !== 'undefined') {
if (typeof this._sidebarIsOnRight !== "undefined") {
return this._sidebarIsOnRight;
}
this._sidebarIsOnRight = Services.prefs.getBoolPref('zen.tabs.vertical.right-side');
this._sidebarIsOnRight = Services.prefs.getBoolPref("zen.tabs.vertical.right-side");
return this._sidebarIsOnRight;
},
@@ -151,7 +154,7 @@ window.gZenCompactModeManager = {
},
addHasPolyfillObserver() {
const attributes = ['panelopen', 'open', 'breakout-extend', 'zen-floating-urlbar'];
const attributes = ["panelopen", "open", "breakout-extend", "zen-floating-urlbar"];
this.sidebarObserverId = ZenHasPolyfill.observeSelectorExistence(
this.sidebar,
[
@@ -160,18 +163,18 @@ window.gZenCompactModeManager = {
":is([panelopen='true'], [open='true'], [breakout-extend='true']):not(#urlbar[zen-floating-urlbar='true']):not(tab):not(.zen-compact-mode-ignore)",
},
],
'zen-compact-mode-active',
"zen-compact-mode-active",
attributes
);
this.toolbarObserverId = ZenHasPolyfill.observeSelectorExistence(
document.getElementById('zen-appcontent-navbar-wrapper'),
document.getElementById("zen-appcontent-navbar-wrapper"),
[
{
selector:
":is([panelopen='true'], [open='true'], #urlbar:focus-within, [breakout-extend='true']):not(.zen-compact-mode-ignore)",
},
],
'zen-compact-mode-active',
"zen-compact-mode-active",
attributes
);
// Always connect this observer, we need it even if compact mode is disabled
@@ -208,64 +211,66 @@ window.gZenCompactModeManager = {
`);
const idToAction = {
'zen-context-menu-compact-mode-hide-sidebar': this.hideSidebar.bind(this),
'zen-context-menu-compact-mode-hide-toolbar': this.hideToolbar.bind(this),
'zen-context-menu-compact-mode-hide-both': this.hideBoth.bind(this),
"zen-context-menu-compact-mode-hide-sidebar": this.hideSidebar.bind(this),
"zen-context-menu-compact-mode-hide-toolbar": this.hideToolbar.bind(this),
"zen-context-menu-compact-mode-hide-both": this.hideBoth.bind(this),
};
for (let menuitem of fragment.querySelectorAll('menuitem')) {
for (let menuitem of fragment.querySelectorAll("menuitem")) {
if (menuitem.id in idToAction) {
menuitem.addEventListener('command', idToAction[menuitem.id]);
menuitem.addEventListener("command", idToAction[menuitem.id]);
}
}
document.getElementById('viewToolbarsMenuSeparator').before(fragment);
document.getElementById("viewToolbarsMenuSeparator").before(fragment);
this.updateContextMenu();
},
updateCompactModeContext(isSingleToolbar) {
const isIllegalState = this.checkIfIllegalState();
const menuitem = document.getElementById('zen-context-menu-compact-mode-toggle');
const menu = document.getElementById('zen-context-menu-compact-mode');
const menuitem = document.getElementById("zen-context-menu-compact-mode-toggle");
const menu = document.getElementById("zen-context-menu-compact-mode");
if (!menu) {
return;
}
if (isSingleToolbar) {
menu.setAttribute('hidden', 'true');
menu.setAttribute("hidden", "true");
menu.before(menuitem);
} else {
menu.removeAttribute('hidden');
menu.querySelector('menupopup').prepend(menuitem);
menu.removeAttribute("hidden");
menu.querySelector("menupopup").prepend(menuitem);
}
const hideToolbarMenuItem = document.getElementById(
'zen-context-menu-compact-mode-hide-toolbar'
"zen-context-menu-compact-mode-hide-toolbar"
);
if (isIllegalState) {
hideToolbarMenuItem.setAttribute('disabled', 'true');
hideToolbarMenuItem.setAttribute("disabled", "true");
} else {
hideToolbarMenuItem.removeAttribute('disabled');
hideToolbarMenuItem.removeAttribute("disabled");
}
},
hideSidebar() {
Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', true);
Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', false);
Services.prefs.setBoolPref("zen.view.compact.hide-tabbar", true);
Services.prefs.setBoolPref("zen.view.compact.hide-toolbar", false);
this.callAllEventListeners();
},
hideToolbar() {
Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', true);
Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', false);
Services.prefs.setBoolPref("zen.view.compact.hide-toolbar", true);
Services.prefs.setBoolPref("zen.view.compact.hide-tabbar", false);
this.callAllEventListeners();
},
hideBoth() {
Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', true);
Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', true);
Services.prefs.setBoolPref("zen.view.compact.hide-tabbar", true);
Services.prefs.setBoolPref("zen.view.compact.hide-toolbar", true);
this.callAllEventListeners();
},
/* Check for illegal states and fix them
/**
* Check for illegal states and fix them
*
* @returns {boolean} If the context menu should just show the "toggle" item
* instead of a submenu with hide options
*/
@@ -288,8 +293,8 @@ window.gZenCompactModeManager = {
(isLeftSideButtons && !isRightSidebar) || (!isLeftSideButtons && isRightSidebar);
if (closelyIllegalState && canHideToolbar && !canHideSidebar) {
// This state is illegal
Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', true);
Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', false);
Services.prefs.setBoolPref("zen.view.compact.hide-tabbar", true);
Services.prefs.setBoolPref("zen.view.compact.hide-toolbar", false);
this.callAllEventListeners();
return true;
}
@@ -332,7 +337,7 @@ window.gZenCompactModeManager = {
} else {
ZenHasPolyfill.disconnectObserver(this.sidebarObserverId);
}
window.dispatchEvent(new CustomEvent('ZenCompactMode:Toggled', { detail: this.preference }));
window.dispatchEvent(new CustomEvent("ZenCompactMode:Toggled", { detail: this.preference }));
},
// NOTE: Dont actually use event, it's just so we make sure
@@ -344,15 +349,15 @@ window.gZenCompactModeManager = {
}
let sidebarWidth = this.sidebar.getBoundingClientRect().width;
const shouldRecalculate =
this.preference || document.documentElement.hasAttribute('zen-creating-workspace');
const sidebarExpanded = document.documentElement.hasAttribute('zen-sidebar-expanded');
this.preference || document.documentElement.hasAttribute("zen-creating-workspace");
const sidebarExpanded = document.documentElement.hasAttribute("zen-sidebar-expanded");
if (sidebarWidth > 1) {
if (shouldRecalculate && sidebarExpanded) {
sidebarWidth = Math.max(sidebarWidth, 150);
}
// Second variable to get the genuine width of the sidebar
this.sidebar.style.setProperty('--actual-zen-sidebar-width', `${sidebarWidth}px`);
window.dispatchEvent(new window.Event('resize')); // To recalculate the layout
this.sidebar.style.setProperty("--actual-zen-sidebar-width", `${sidebarWidth}px`);
window.dispatchEvent(new window.Event("resize")); // To recalculate the layout
if (
event &&
shouldRecalculate &&
@@ -362,57 +367,56 @@ window.gZenCompactModeManager = {
return;
}
delete gZenVerticalTabsManager._hadSidebarCollapse;
this.sidebar.style.setProperty('--zen-sidebar-width', `${sidebarWidth}px`);
this.sidebar.style.setProperty("--zen-sidebar-width", `${sidebarWidth}px`);
}
return sidebarWidth;
},
get canHideSidebar() {
return (
Services.prefs.getBoolPref('zen.view.compact.hide-tabbar') ||
Services.prefs.getBoolPref("zen.view.compact.hide-tabbar") ||
gZenVerticalTabsManager._hasSetSingleToolbar
);
},
get canHideToolbar() {
return (
Services.prefs.getBoolPref('zen.view.compact.hide-toolbar') &&
Services.prefs.getBoolPref("zen.view.compact.hide-toolbar") &&
!gZenVerticalTabsManager._hasSetSingleToolbar
);
},
animateCompactMode() {
// Get the splitter width before hiding it (we need to hide it before animating on right)
document.documentElement.setAttribute('zen-compact-animating', 'true');
document.documentElement.setAttribute("zen-compact-animating", "true");
return new Promise((resolve) => {
// We need to set the splitter width before hiding it
let splitterWidth = document
.getElementById('zen-sidebar-splitter')
.getElementById("zen-sidebar-splitter")
.getBoundingClientRect().width;
const isCompactMode = this.preference;
const canHideSidebar = this.canHideSidebar;
let canAnimate = lazy.COMPACT_MODE_CAN_ANIMATE_SIDEBAR && !this.isSidebarPotentiallyOpen();
if (typeof this._wasInCompactMode !== 'undefined') {
if (typeof this._wasInCompactMode !== "undefined") {
canAnimate = false;
delete this._wasInCompactMode;
}
// Do this so we can get the correct width ONCE compact mode styled have been applied
if (canAnimate) {
this.sidebar.setAttribute('animate', 'true');
this.sidebar.setAttribute("animate", "true");
}
if (this._ignoreNextHover) {
this._setElementExpandAttribute(this.sidebar, false);
}
this.sidebar.style.removeProperty('margin-right');
this.sidebar.style.removeProperty('margin-left');
this.sidebar.style.removeProperty('transform');
this.sidebar.style.removeProperty("margin-right");
this.sidebar.style.removeProperty("margin-left");
this.sidebar.style.removeProperty("transform");
window.requestAnimationFrame(() => {
delete this._ignoreNextResize;
let sidebarWidth = this.getAndApplySidebarWidth();
const elementSeparation = ZenThemeModifier.elementSeparation;
if (!canAnimate) {
this.sidebar.removeAttribute('animate');
document.documentElement.removeAttribute('zen-compact-animating');
this.sidebar.removeAttribute("animate");
document.documentElement.removeAttribute("zen-compact-animating");
this.getAndApplySidebarWidth({});
this._ignoreNextResize = true;
@@ -422,7 +426,7 @@ window.gZenCompactModeManager = {
resolve();
return;
}
if (document.documentElement.hasAttribute('zen-sidebar-expanded')) {
if (document.documentElement.hasAttribute("zen-sidebar-expanded")) {
sidebarWidth -= 0.5 * splitterWidth;
if (elementSeparation < splitterWidth) {
// Subtract from the splitter width to end up with the correct element separation
@@ -441,20 +445,20 @@ window.gZenCompactModeManager = {
marginLeft: [0, this.sidebarIsOnRight ? 0 : `-${sidebarWidth}px`],
},
{
ease: 'easeIn',
type: 'spring',
ease: "easeIn",
type: "spring",
bounce: 0,
duration: 0.12,
}
)
.then(() => {
this.sidebar.style.transition = 'none';
this.sidebar.style.pointEvents = 'none';
const titlebar = document.getElementById('titlebar');
titlebar.style.visibility = 'hidden';
titlebar.style.transition = 'none';
this.sidebar.removeAttribute('animate');
document.documentElement.removeAttribute('zen-compact-animating');
this.sidebar.style.transition = "none";
this.sidebar.style.pointEvents = "none";
const titlebar = document.getElementById("titlebar");
titlebar.style.visibility = "hidden";
titlebar.style.transition = "none";
this.sidebar.removeAttribute("animate");
document.documentElement.removeAttribute("zen-compact-animating");
setTimeout(() => {
this.getAndApplySidebarWidth({});
@@ -467,16 +471,16 @@ window.gZenCompactModeManager = {
});
}
this.sidebar.style.removeProperty('margin-right');
this.sidebar.style.removeProperty('margin-left');
this.sidebar.style.removeProperty('transition');
this.sidebar.style.removeProperty('transform');
this.sidebar.style.removeProperty('point-events');
this.sidebar.style.removeProperty("margin-right");
this.sidebar.style.removeProperty("margin-left");
this.sidebar.style.removeProperty("transition");
this.sidebar.style.removeProperty("transform");
this.sidebar.style.removeProperty("point-events");
titlebar.style.removeProperty('visibility');
titlebar.style.removeProperty('transition');
titlebar.style.removeProperty("visibility");
titlebar.style.removeProperty("transition");
gURLBar.style.removeProperty('visibility');
gURLBar.style.removeProperty("visibility");
resolve();
});
@@ -485,7 +489,7 @@ window.gZenCompactModeManager = {
} else if (canHideSidebar && !isCompactMode) {
// Shouldn't be ever true, but just in case
delete this._ignoreNextHover;
document.getElementById('browser').style.overflow = 'clip';
document.getElementById("browser").style.overflow = "clip";
if (this.sidebarIsOnRight) {
this.sidebar.style.marginRight = `-${sidebarWidth}px`;
} else {
@@ -497,32 +501,32 @@ window.gZenCompactModeManager = {
this.sidebarIsOnRight
? {
marginRight: [`-${sidebarWidth}px`, 0],
transform: ['translateX(100%)', 'translateX(0)'],
transform: ["translateX(100%)", "translateX(0)"],
}
: { marginLeft: 0 },
{
ease: 'easeOut',
type: 'spring',
ease: "easeOut",
type: "spring",
bounce: 0,
duration: 0.12,
}
)
.then(() => {
this.sidebar.removeAttribute('animate');
document.getElementById('browser').style.removeProperty('overflow');
this.sidebar.style.transition = 'none';
this.sidebar.style.removeProperty('margin-right');
this.sidebar.style.removeProperty('margin-left');
this.sidebar.style.removeProperty('transform');
document.documentElement.removeAttribute('zen-compact-animating');
this.sidebar.removeAttribute("animate");
document.getElementById("browser").style.removeProperty("overflow");
this.sidebar.style.transition = "none";
this.sidebar.style.removeProperty("margin-right");
this.sidebar.style.removeProperty("margin-left");
this.sidebar.style.removeProperty("transform");
document.documentElement.removeAttribute("zen-compact-animating");
setTimeout(() => {
this.sidebar.style.removeProperty('transition');
this.sidebar.style.removeProperty("transition");
resolve();
});
});
} else {
this.sidebar.removeAttribute('animate'); // remove the attribute if we are not animating
document.documentElement.removeAttribute('zen-compact-animating');
this.sidebar.removeAttribute("animate"); // remove the attribute if we are not animating
document.documentElement.removeAttribute("zen-compact-animating");
delete this._ignoreNextHover;
resolve();
}
@@ -531,32 +535,32 @@ window.gZenCompactModeManager = {
},
updateContextMenu() {
const toggle = document.getElementById('zen-context-menu-compact-mode-toggle');
const toggle = document.getElementById("zen-context-menu-compact-mode-toggle");
if (!toggle) {
return;
}
toggle.setAttribute('checked', this.preference);
toggle.setAttribute("checked", this.preference);
const hideTabBar = this.canHideSidebar;
const hideToolbar = this.canHideToolbar;
const hideBoth = hideTabBar && hideToolbar;
const idName = 'zen-context-menu-compact-mode-hide-';
const sidebarItem = document.getElementById(idName + 'sidebar');
const toolbarItem = document.getElementById(idName + 'toolbar');
const bothItem = document.getElementById(idName + 'both');
sidebarItem.setAttribute('checked', !hideBoth && hideTabBar);
toolbarItem.setAttribute('checked', !hideBoth && hideToolbar);
bothItem.setAttribute('checked', hideBoth);
const idName = "zen-context-menu-compact-mode-hide-";
const sidebarItem = document.getElementById(idName + "sidebar");
const toolbarItem = document.getElementById(idName + "toolbar");
const bothItem = document.getElementById(idName + "both");
sidebarItem.setAttribute("checked", !hideBoth && hideTabBar);
toolbarItem.setAttribute("checked", !hideBoth && hideToolbar);
bothItem.setAttribute("checked", hideBoth);
},
_removeOpenStateOnUnifiedExtensions() {
// Fix for bug https://github.com/zen-browser/desktop/issues/1925
const buttons = document.querySelectorAll(
'toolbarbutton:is(#unified-extensions-button, .webextension-browser-action)'
"toolbarbutton:is(#unified-extensions-button, .webextension-browser-action)"
);
for (let button of buttons) {
button.removeAttribute('open');
button.removeAttribute("open");
}
},
@@ -567,30 +571,30 @@ window.gZenCompactModeManager = {
},
_updateSidebarIsOnRight() {
this._sidebarIsOnRight = Services.prefs.getBoolPref('zen.tabs.vertical.right-side');
this._sidebarIsOnRight = Services.prefs.getBoolPref("zen.tabs.vertical.right-side");
},
toggleSidebar() {
this.sidebar.toggleAttribute('zen-user-show');
this.sidebar.toggleAttribute("zen-user-show");
},
get hideAfterHoverDuration() {
if (this._hideAfterHoverDuration) {
return this._hideAfterHoverDuration;
}
return Services.prefs.getIntPref('zen.view.compact.toolbar-hide-after-hover.duration');
return Services.prefs.getIntPref("zen.view.compact.toolbar-hide-after-hover.duration");
},
get hoverableElements() {
return [
{
element: this.sidebar,
screenEdge: this.sidebarIsOnRight ? 'right' : 'left',
screenEdge: this.sidebarIsOnRight ? "right" : "left",
keepHoverDuration: 100,
},
{
element: document.getElementById('zen-appcontent-navbar-wrapper'),
screenEdge: 'top',
element: document.getElementById("zen-appcontent-navbar-wrapper"),
screenEdge: "top",
},
{
element: gZenVerticalTabsManager.actualWindowButtons,
@@ -599,13 +603,13 @@ window.gZenCompactModeManager = {
},
flashSidebar(duration = lazy.COMPACT_MODE_FLASH_DURATION) {
let tabPanels = document.getElementById('tabbrowser-tabpanels');
let tabPanels = document.getElementById("tabbrowser-tabpanels");
if (!tabPanels.matches("[zen-split-view='true']")) {
this.flashElement(this.sidebar, duration, this.sidebar.id);
}
},
flashElement(element, duration, id, attrName = 'flash-popup') {
flashElement(element, duration, id, attrName = "flash-popup") {
if (this._flashTimeouts[id]) {
clearTimeout(this._flashTimeouts[id]);
} else {
@@ -624,48 +628,50 @@ window.gZenCompactModeManager = {
this._flashTimeouts[id] = null;
},
_setElementExpandAttribute(element, value, attr = 'zen-has-hover') {
const kVerifiedAttributes = ['zen-has-hover', 'has-popup-menu', 'zen-compact-mode-active'];
const isToolbar = element.id === 'zen-appcontent-navbar-wrapper';
_setElementExpandAttribute(element, value, attr = "zen-has-hover") {
const kVerifiedAttributes = ["zen-has-hover", "has-popup-menu", "zen-compact-mode-active"];
const isToolbar = element.id === "zen-appcontent-navbar-wrapper";
if (value) {
if (attr === 'zen-has-hover' && element !== gZenVerticalTabsManager.actualWindowButtons) {
element.setAttribute('zen-has-implicit-hover', 'true');
if (attr === "zen-has-hover" && element !== gZenVerticalTabsManager.actualWindowButtons) {
element.setAttribute("zen-has-implicit-hover", "true");
if (!lazy.COMPACT_MODE_SHOW_SIDEBAR_AND_TOOLBAR_ON_HOVER) {
return;
}
}
element.setAttribute(attr, 'true');
element.setAttribute(attr, "true");
if (
isToolbar &&
((gZenVerticalTabsManager._hasSetSingleToolbar &&
(element.hasAttribute('should-hide') ||
document.documentElement.hasAttribute('zen-has-bookmarks'))) ||
(element.hasAttribute("should-hide") ||
document.documentElement.hasAttribute("zen-has-bookmarks"))) ||
(this.preference &&
Services.prefs.getBoolPref('zen.view.compact.hide-toolbar') &&
Services.prefs.getBoolPref("zen.view.compact.hide-toolbar") &&
!gZenVerticalTabsManager._hasSetSingleToolbar))
) {
gBrowser.tabpanels.setAttribute('has-toolbar-hovered', 'true');
gBrowser.tabpanels.setAttribute("has-toolbar-hovered", "true");
}
} else {
if (attr === 'zen-has-hover') {
element.removeAttribute('zen-has-implicit-hover');
if (attr === "zen-has-hover") {
element.removeAttribute("zen-has-implicit-hover");
}
element.removeAttribute(attr);
// Only remove if none of the verified attributes are present
if (isToolbar && !kVerifiedAttributes.some((attr) => element.hasAttribute(attr))) {
gBrowser.tabpanels.removeAttribute('has-toolbar-hovered');
if (
isToolbar &&
!kVerifiedAttributes.some((verifiedAttr) => element.hasAttribute(verifiedAttr))
) {
gBrowser.tabpanels.removeAttribute("has-toolbar-hovered");
}
}
},
addMouseActions() {
gURLBar.addEventListener('mouseenter', (event) => {
if (event.target.closest('#urlbar[zen-floating-urlbar]')) {
gURLBar.addEventListener("mouseenter", (event) => {
if (event.target.closest("#urlbar[zen-floating-urlbar]")) {
window.requestAnimationFrame(() => {
this._setElementExpandAttribute(gZenVerticalTabsManager.actualWindowButtons, false);
});
this._hasHoveredUrlbar = true;
return;
}
});
@@ -673,22 +679,26 @@ window.gZenCompactModeManager = {
let target = this.hoverableElements[i].element;
// Add the attribute on startup if the mouse is already over the element
if (target.matches(':hover')) {
if (target.matches(":hover")) {
this._setElementExpandAttribute(target, true);
}
const onEnter = (event) => {
setTimeout(() => {
if (event.type === 'mouseenter' && !event.target.matches(':hover')) return;
if (event.target.closest('panel')) return;
if (event.type === "mouseenter" && !event.target.matches(":hover")) {
return;
}
if (event.target.closest("panel")) {
return;
}
// Dont register the hover if the urlbar is floating and we are hovering over it
this.clearFlashTimeout('has-hover' + target.id);
this.clearFlashTimeout("has-hover" + target.id);
window.requestAnimationFrame(() => {
if (
document.documentElement.getAttribute('supress-primary-adjustment') === 'true' ||
document.documentElement.getAttribute("supress-primary-adjustment") === "true" ||
this._hasHoveredUrlbar ||
this._ignoreNextHover ||
target.hasAttribute('zen-has-hover')
target.hasAttribute("zen-has-hover")
) {
return;
}
@@ -698,7 +708,7 @@ window.gZenCompactModeManager = {
};
const onLeave = (event) => {
if (AppConstants.platform == 'macosx') {
if (AppConstants.platform == "macosx") {
const buttonRect = gZenVerticalTabsManager.actualWindowButtons.getBoundingClientRect();
const MAC_WINDOW_BUTTONS_X_BORDER = buttonRect.width + buttonRect.x;
const MAC_WINDOW_BUTTONS_Y_BORDER = buttonRect.height + buttonRect.y;
@@ -717,17 +727,17 @@ window.gZenCompactModeManager = {
// This is because the mouse is left to be handled natively so firefox thinks the mouse left the window for a split second.
setTimeout(() => {
// Let's double check if the mouse is still hovering over the element, see the bug above.
if (event.target.matches(':hover')) {
if (event.target.matches(":hover")) {
return;
}
if (
event.explicitOriginalTarget?.closest?.('#urlbar[zen-floating-urlbar]') ||
(document.documentElement.getAttribute('supress-primary-adjustment') === 'true' &&
event.explicitOriginalTarget?.closest?.("#urlbar[zen-floating-urlbar]") ||
(document.documentElement.getAttribute("supress-primary-adjustment") === "true" &&
gZenVerticalTabsManager._hasSetSingleToolbar) ||
this._hasHoveredUrlbar ||
this._ignoreNextHover ||
(event.type === 'dragleave' &&
(event.type === "dragleave" &&
event.explicitOriginalTarget !== target &&
target.contains?.(event.explicitOriginalTarget))
) {
@@ -738,8 +748,8 @@ window.gZenCompactModeManager = {
this.flashElement(
target,
this.hoverableElements[i].keepHoverDuration,
'has-hover' + target.id,
'zen-has-hover'
"has-hover" + target.id,
"zen-has-hover"
);
} else {
this._removeHoverFrames[target.id] = window.requestAnimationFrame(() =>
@@ -749,21 +759,25 @@ window.gZenCompactModeManager = {
}, this.HOVER_HACK_DELAY);
};
target.addEventListener('mouseover', onEnter);
target.addEventListener('dragover', onEnter);
target.addEventListener("mouseover", onEnter);
target.addEventListener("dragover", onEnter);
target.addEventListener('mouseleave', onLeave);
target.addEventListener('dragleave', onLeave);
target.addEventListener("mouseleave", onLeave);
target.addEventListener("dragleave", onLeave);
}
document.documentElement.addEventListener('mouseleave', (event) => {
document.documentElement.addEventListener("mouseleave", (event) => {
setTimeout(() => {
const screenEdgeCrossed = this._getCrossedEdge(event.pageX, event.pageY);
if (!screenEdgeCrossed) return;
if (!screenEdgeCrossed) {
return;
}
for (let entry of this.hoverableElements) {
if (screenEdgeCrossed !== entry.screenEdge) continue;
if (screenEdgeCrossed !== entry.screenEdge) {
continue;
}
const target = entry.element;
const boundAxis = entry.screenEdge === 'right' || entry.screenEdge === 'left' ? 'y' : 'x';
const boundAxis = entry.screenEdge === "right" || entry.screenEdge === "left" ? "y" : "x";
if (!this._positionInBounds(boundAxis, target, event.pageX, event.pageY, 7)) {
continue;
}
@@ -772,15 +786,17 @@ window.gZenCompactModeManager = {
this.flashElement(
target,
this.hideAfterHoverDuration,
'has-hover' + target.id,
'zen-has-hover'
"has-hover" + target.id,
"zen-has-hover"
);
document.addEventListener(
'mousemove',
"mousemove",
() => {
if (target.matches(':hover')) return;
if (target.matches(":hover")) {
return;
}
this._setElementExpandAttribute(target, false);
this.clearFlashTimeout('has-hover' + target.id);
this.clearFlashTimeout("has-hover" + target.id);
},
{ once: true }
);
@@ -788,7 +804,7 @@ window.gZenCompactModeManager = {
}, this.HOVER_HACK_DELAY);
});
gURLBar.addEventListener('mouseleave', () => {
gURLBar.addEventListener("mouseleave", () => {
setTimeout(() => {
setTimeout(() => {
requestAnimationFrame(() => {
@@ -803,25 +819,27 @@ window.gZenCompactModeManager = {
const targetBox = element.getBoundingClientRect();
posX = Math.max(targetBox.left, Math.min(posX, targetBox.right));
posY = Math.max(targetBox.top, Math.min(posY, targetBox.bottom));
return ['top', 'bottom', 'left', 'right'].find((edge, i) => {
return ["top", "bottom", "left", "right"].find((edge, i) => {
const distance = Math.abs((i < 2 ? posY : posX) - targetBox[edge]);
return distance <= maxDistance;
});
},
_positionInBounds(axis = 'x', element, x, y, error = 0) {
_positionInBounds(axis = "x", element, x, y, error = 0) {
const bBox = element.getBoundingClientRect();
if (axis === 'y') return bBox.top - error < y && y < bBox.bottom + error;
else return bBox.left - error < x && x < bBox.right + error;
if (axis === "y") {
return bBox.top - error < y && y < bBox.bottom + error;
}
return bBox.left - error < x && x < bBox.right + error;
},
_clearAllHoverStates() {
// Clear hover attributes from all hoverable elements
for (let entry of this.hoverableElements) {
const target = entry.element;
if (target && !target.matches(':hover') && target.hasAttribute('zen-has-hover')) {
if (target && !target.matches(":hover") && target.hasAttribute("zen-has-hover")) {
this._setElementExpandAttribute(target, false);
this.clearFlashTimeout('has-hover' + target.id);
this.clearFlashTimeout("has-hover" + target.id);
}
}
},
@@ -831,9 +849,9 @@ window.gZenCompactModeManager = {
this._setElementExpandAttribute(this.sidebar, false);
}
return (
this.sidebar.hasAttribute('zen-user-show') ||
this.sidebar.hasAttribute('zen-has-hover') ||
this.sidebar.hasAttribute('zen-has-empty-tab')
this.sidebar.hasAttribute("zen-user-show") ||
this.sidebar.hasAttribute("zen-has-hover") ||
this.sidebar.hasAttribute("zen-has-empty-tab")
);
},
@@ -846,9 +864,9 @@ window.gZenCompactModeManager = {
!gZenGlanceManager._animating &&
!this._nextTimeWillBeActive
) {
gZenUIManager.showToast('zen-background-tab-opened-toast', {
gZenUIManager.showToast("zen-background-tab-opened-toast", {
button: {
id: 'zen-open-background-tab-button',
id: "zen-open-background-tab-button",
command: () => {
const targetWindow = window.ownerGlobal.parent || window;
targetWindow.gBrowser.selectedTab = tab;
@@ -861,7 +879,7 @@ window.gZenCompactModeManager = {
};
document.addEventListener(
'MozBeforeInitialXULLayout',
"MozBeforeInitialXULLayout",
() => {
gZenCompactModeManager.preInit();
},

View File

@@ -5,7 +5,7 @@
import {
nsZenDOMOperatedFeature,
nsZenMultiWindowFeature,
} from 'chrome://browser/content/zen-components/ZenCommonUtils.mjs';
} from "chrome://browser/content/zen-components/ZenCommonUtils.mjs";
const CONFIG = Object.freeze({
ANIMATION: {
@@ -37,7 +37,7 @@ class nsZenDownloadAnimation extends nsZenDOMOperatedFeature {
#handleNewDownload() {
if (
!Services.prefs.getBoolPref('zen.downloads.download-animation') ||
!Services.prefs.getBoolPref("zen.downloads.download-animation") ||
!nsZenMultiWindowFeature.isActiveWindow
) {
return;
@@ -54,10 +54,10 @@ class nsZenDownloadAnimation extends nsZenDOMOperatedFeature {
}
#animateDownload(startPosition) {
let animationElement = document.querySelector('zen-download-animation');
let animationElement = document.querySelector("zen-download-animation");
if (!animationElement) {
animationElement = document.createElement('zen-download-animation');
animationElement = document.createElement("zen-download-animation");
document.body.appendChild(animationElement);
}
@@ -72,17 +72,17 @@ class nsZenDownloadAnimationElement extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.attachShadow({ mode: "open" });
this.#loadArcStyles();
}
#loadArcStyles() {
try {
const link = document.createElement('link');
link.setAttribute('rel', 'stylesheet');
const link = document.createElement("link");
link.setAttribute("rel", "stylesheet");
link.setAttribute(
'href',
'chrome://browser/content/zen-styles/zen-download-arc-animation.css'
"href",
"chrome://browser/content/zen-styles/zen-download-arc-animation.css"
);
this.shadowRoot.appendChild(link);
} catch (error) {
@@ -131,14 +131,18 @@ class nsZenDownloadAnimationElement extends HTMLElement {
}
#areTabsOnRightSide() {
const position = Services.prefs.getIntPref('zen.downloads.icon-popup-position', 0);
if (position === 1) return false;
if (position === 2) return true;
return Services.prefs.getBoolPref('zen.tabs.vertical.right-side');
const position = Services.prefs.getIntPref("zen.downloads.icon-popup-position", 0);
if (position === 1) {
return false;
}
if (position === 2) {
return true;
}
return Services.prefs.getBoolPref("zen.tabs.vertical.right-side");
}
#determineEndPosition() {
const downloadsButton = document.getElementById('downloads-button');
const downloadsButton = document.getElementById("downloads-button");
const isDownloadButtonVisible = downloadsButton && this.#isElementVisible(downloadsButton);
let endPosition = { clientX: 0, clientY: 0 };
@@ -153,7 +157,7 @@ class nsZenDownloadAnimationElement extends HTMLElement {
} else {
// Use alternative position at bottom of wrapper
const areTabsPositionedRight = this.#areTabsOnRightSide();
const wrapper = document.getElementById('zen-main-app-wrapper');
const wrapper = document.getElementById("zen-main-app-wrapper");
const wrapperRect = wrapper.getBoundingClientRect();
endPosition = {
@@ -175,12 +179,12 @@ class nsZenDownloadAnimationElement extends HTMLElement {
`;
const fragment = window.MozXULElement.parseXULToFragment(arcAnimationHTML);
const animationElement = fragment.querySelector('.zen-download-arc-animation');
const animationElement = fragment.querySelector(".zen-download-arc-animation");
Object.assign(animationElement.style, {
left: `${startPosition.clientX}px`,
top: `${startPosition.clientY}px`,
transform: 'translate(-50%, -50%)',
transform: "translate(-50%, -50%)",
});
this.shadowRoot.appendChild(animationElement);
@@ -229,14 +233,14 @@ class nsZenDownloadAnimationElement extends HTMLElement {
}
await gZenUIManager.motion.animate(arcAnimationElement, sequence, {
duration: Services.prefs.getIntPref('zen.downloads.download-animation-duration') / 1000,
easing: 'cubic-bezier(0.37, 0, 0.63, 1)',
fill: 'forwards',
duration: Services.prefs.getIntPref("zen.downloads.download-animation-duration") / 1000,
easing: "cubic-bezier(0.37, 0, 0.63, 1)",
fill: "forwards",
});
this.#cleanArcAnimation(arcAnimationElement);
} catch (error) {
console.error('[nsZenDownloadAnimationElement] Error in animation sequence:', error);
console.error("[nsZenDownloadAnimationElement] Error in animation sequence:", error);
this.#cleanArcAnimation(arcAnimationElement);
}
}
@@ -330,7 +334,7 @@ class nsZenDownloadAnimationElement extends HTMLElement {
return;
}
const wrapper = document.getElementById('zen-main-app-wrapper');
const wrapper = document.getElementById("zen-main-app-wrapper");
if (!wrapper) {
console.warn(
`[${nsZenDownloadAnimationElement.name}] Cannot start box animation, Wrapper element not found`
@@ -347,15 +351,15 @@ class nsZenDownloadAnimationElement extends HTMLElement {
</box>
`;
const sideProp = areTabsPositionedRight ? 'right' : 'left';
const sideProp = areTabsPositionedRight ? "right" : "left";
const fragment = window.MozXULElement.parseXULToFragment(boxAnimationHTML);
this.#boxAnimationElement = fragment.querySelector('.zen-download-box-animation');
this.#boxAnimationElement = fragment.querySelector(".zen-download-box-animation");
Object.assign(this.#boxAnimationElement.style, {
bottom: '24px',
transform: 'scale(0.8)',
[sideProp]: '-50px',
bottom: "24px",
transform: "scale(0.8)",
[sideProp]: "-50px",
});
wrapper.appendChild(this.#boxAnimationElement);
@@ -363,25 +367,25 @@ class nsZenDownloadAnimationElement extends HTMLElement {
await gZenUIManager.motion.animate(
this.#boxAnimationElement,
{
[sideProp]: '34px',
[sideProp]: "34px",
opacity: 1,
transform: 'scale(1.1)',
transform: "scale(1.1)",
},
{
duration: 0.35,
easing: 'ease-out',
easing: "ease-out",
}
).finished;
await gZenUIManager.motion.animate(
this.#boxAnimationElement,
{
[sideProp]: '24px',
transform: 'scale(1)',
[sideProp]: "24px",
transform: "scale(1)",
},
{
duration: 0.2,
easing: 'ease-in-out',
easing: "ease-in-out",
}
).finished;
@@ -401,7 +405,7 @@ class nsZenDownloadAnimationElement extends HTMLElement {
}
#getBoxAnimationDurationMs() {
return Services.prefs.getIntPref('zen.downloads.download-animation-duration') + 200;
return Services.prefs.getIntPref("zen.downloads.download-animation-duration") + 200;
}
async #finishBoxAnimation(areTabsPositionedRight) {
@@ -409,36 +413,38 @@ class nsZenDownloadAnimationElement extends HTMLElement {
this.#boxAnimationTimeoutId = null;
if (!this.#boxAnimationElement || this.#isBoxAnimationRunning) {
if (!this.#boxAnimationElement) this.#cleanBoxAnimationState();
if (!this.#boxAnimationElement) {
this.#cleanBoxAnimationState();
}
return;
}
this.#isBoxAnimationRunning = true;
try {
const sideProp = areTabsPositionedRight ? 'right' : 'left';
const sideProp = areTabsPositionedRight ? "right" : "left";
await gZenUIManager.motion.animate(
this.#boxAnimationElement,
{
transform: 'scale(0.9)',
transform: "scale(0.9)",
},
{
duration: 0.15,
easing: 'ease-in',
easing: "ease-in",
}
).finished;
await gZenUIManager.motion.animate(
this.#boxAnimationElement,
{
[sideProp]: '-50px',
[sideProp]: "-50px",
opacity: 0,
transform: 'scale(0.8)',
transform: "scale(0.8)",
},
{
duration: 0.3,
easing: 'cubic-bezier(0.5, 0, 0.75, 0)',
easing: "cubic-bezier(0.5, 0, 0.75, 0)",
}
).finished;
} catch (error) {
@@ -474,7 +480,9 @@ class nsZenDownloadAnimationElement extends HTMLElement {
}
#isElementVisible(element) {
if (!element) return false;
if (!element) {
return false;
}
const rect = element.getBoundingClientRect();
@@ -495,6 +503,6 @@ class nsZenDownloadAnimationElement extends HTMLElement {
}
}
customElements.define('zen-download-animation', nsZenDownloadAnimationElement);
customElements.define("zen-download-animation", nsZenDownloadAnimationElement);
new nsZenDownloadAnimation();

View File

@@ -41,6 +41,6 @@
width: 100%;
height: 100%;
background-color: var(--zen-primary-color);
mask: url('chrome://browser/content/zen-images/downloads/download.svg') no-repeat center;
mask: url("chrome://browser/content/zen-images/downloads/download.svg") no-repeat center;
mask-size: 70%;
}

View File

@@ -21,7 +21,7 @@
width: 50%;
height: 50%;
background-color: var(--zen-primary-color);
mask: url('chrome://browser/content/zen-images/downloads/archive.svg') no-repeat center;
mask: url("chrome://browser/content/zen-images/downloads/archive.svg") no-repeat center;
mask-size: contain;
display: block;
}

View File

@@ -2,7 +2,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/. */
'use strict';
"use strict";
// Wrap in a block to prevent leaking to window scope.
{
@@ -33,19 +33,19 @@
*/
const elementToMove = (element) => {
if (
element.closest('.zen-current-workspace-indicator') ||
element.hasAttribute('split-view-group')
element.closest(".zen-current-workspace-indicator") ||
element.hasAttribute("split-view-group")
) {
return element;
}
if (element.group?.hasAttribute('split-view-group')) {
if (element.group?.hasAttribute("split-view-group")) {
return element.group;
}
if (isTab(element)) {
return element;
}
if (isTabGroupLabel(element)) {
return element.closest('.tab-group-label-container');
return element.closest(".tab-group-label-container");
}
throw new Error(`Element "${element.tagName}" is not expected to move`);
};
@@ -64,15 +64,15 @@
XPCOMUtils.defineLazyServiceGetter(
this,
'ZenDragAndDropService',
'@mozilla.org/zen/drag-and-drop;1',
"ZenDragAndDropService",
"@mozilla.org/zen/drag-and-drop;1",
Ci.nsIZenDragAndDrop
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
'_dndSwitchSpaceDelay',
'zen.tabs.dnd-switch-space-delay',
"_dndSwitchSpaceDelay",
"zen.tabs.dnd-switch-space-delay",
1000
);
}
@@ -80,7 +80,7 @@
init() {
super.init();
this.handle_windowDragEnter = this.handle_windowDragEnter.bind(this);
window.addEventListener('dragleave', this.handle_windowDragLeave.bind(this), {
window.addEventListener("dragleave", this.handle_windowDragLeave.bind(this), {
capture: true,
});
}
@@ -102,28 +102,28 @@
#createDragImageForTabs(draggedTab, movingTabs) {
const periphery = gZenWorkspaces.activeWorkspaceElement.querySelector(
'#tabbrowser-arrowscrollbox-periphery'
"#tabbrowser-arrowscrollbox-periphery"
);
const dragData = draggedTab._dragData;
const tabRect = window.windowUtils.getBoundsWithoutFlushing(movingTabs[0]);
const wrapper = document.createElement('div');
wrapper.style.width = tabRect.width + 'px';
wrapper.style.height = tabRect.height * movingTabs.length + 'px';
wrapper.style.overflow = 'clip';
wrapper.style.position = 'fixed';
wrapper.style.top = '-9999px';
const wrapper = document.createElement("div");
wrapper.style.width = tabRect.width + "px";
wrapper.style.height = tabRect.height * movingTabs.length + "px";
wrapper.style.overflow = "clip";
wrapper.style.position = "fixed";
wrapper.style.top = "-9999px";
periphery.appendChild(wrapper);
for (let i = 0; i < movingTabs.length; i++) {
const tab = movingTabs[i];
const tabClone = tab.cloneNode(true);
if (tabClone.hasAttribute('zen-essential')) {
if (tabClone.hasAttribute("zen-essential")) {
const rect = tab.getBoundingClientRect();
tabClone.style.minWidth = tabClone.style.maxWidth = `${rect.width}px`;
tabClone.style.minHeight = tabClone.style.maxHeight = `${rect.height}px`;
}
if (i > 0) {
tabClone.style.transform = `translate(${i * 4}px, -${i * (tabRect.height - 4)}px)`;
tabClone.style.opacity = '0.2';
tabClone.style.opacity = "0.2";
tabClone.style.zIndex = `${-i}`;
}
// Apply a transform translate to the tab in order to center it within the drag image
@@ -131,9 +131,9 @@
if (!movingTabs.length > 1) {
tabClone.style.transform = `translate(${(tabRect.width - dragData.offsetX) / 2}px, ${(tabRect.height - dragData.offsetY) / 2}px)`;
}
tabClone.setAttribute('drag-image', 'true');
tabClone.setAttribute("drag-image", "true");
wrapper.appendChild(tabClone);
if (isTab(tabClone) && !tabClone.hasAttribute('zen-essential')) {
if (isTab(tabClone) && !tabClone.hasAttribute("zen-essential")) {
// We need to limit the label content so the drag image doesn't grow too big.
const label = tabClone.textLabel;
const tabLabelParentWidth = label.parentElement.getBoundingClientRect().width;
@@ -147,32 +147,34 @@
#maybeCreateDragImageDot(movingTabs, wrapper) {
if (movingTabs.length > 1) {
const dot = document.createElement('div');
const dot = document.createElement("div");
dot.textContent = movingTabs.length;
dot.style.position = 'absolute';
dot.style.top = '-10px';
dot.style.left = '-16px';
dot.style.background = 'red';
dot.style.borderRadius = '50%';
dot.style.fontWeight = 'bold';
dot.style.fontSize = '10px';
dot.style.lineHeight = '16px';
dot.style.justifyContent = dot.style.alignItems = 'center';
dot.style.height = dot.style.minWidth = '16px';
dot.style.textAlign = 'center';
dot.style.color = 'white';
dot.style.position = "absolute";
dot.style.top = "-10px";
dot.style.left = "-16px";
dot.style.background = "red";
dot.style.borderRadius = "50%";
dot.style.fontWeight = "bold";
dot.style.fontSize = "10px";
dot.style.lineHeight = "16px";
dot.style.justifyContent = dot.style.alignItems = "center";
dot.style.height = dot.style.minWidth = "16px";
dot.style.textAlign = "center";
dot.style.color = "white";
wrapper.appendChild(dot);
}
}
// eslint-disable-next-line complexity
_animateTabMove(event) {
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
if (event.target.closest('#zen-essentials')) {
if (event.target.closest("#zen-essentials")) {
if (!isTab(draggedTab)) {
this.clearDragOverVisuals();
return;
}
return this.#animateVerticalPinnedGridDragOver(event);
this.#animateVerticalPinnedGridDragOver(event);
return;
} else if (this._fakeEssentialTab) {
this.#makeDragImageNonEssential(event);
}
@@ -185,7 +187,7 @@
: dragData.screenX;
let allTabs = this._tabbrowserTabs.ariaFocusableItems;
let numEssentials = gBrowser._numZenEssentials;
let isEssential = draggedTab.hasAttribute('zen-essential');
let isEssential = draggedTab.hasAttribute("zen-essential");
let tabs = allTabs.slice(
isEssential ? 0 : numEssentials,
isEssential ? numEssentials : undefined
@@ -206,8 +208,8 @@
let bounds = (ele) => window.windowUtils.getBoundsWithoutFlushing(ele);
let logicalForward = screenForward != this._rtlMode;
let screenAxis = this._tabbrowserTabs.verticalMode ? 'screenY' : 'screenX';
let size = this._tabbrowserTabs.verticalMode ? 'height' : 'width';
let screenAxis = this._tabbrowserTabs.verticalMode ? "screenY" : "screenX";
let size = this._tabbrowserTabs.verticalMode ? "height" : "width";
let { width: tabWidth, height: tabHeight } = bounds(draggedTab);
let tabSize = this._tabbrowserTabs.verticalMode ? tabHeight : tabWidth;
let translateX = event.screenX - dragData.screenX;
@@ -219,7 +221,7 @@
dragData.translateY = translateY;
// Move the dragged tab based on the mouse position.
let periphery = document.getElementById('tabbrowser-arrowscrollbox-periphery');
let periphery = document.getElementById("tabbrowser-arrowscrollbox-periphery");
let lastMovingTab = movingTabs.at(-1);
let firstMovingTab = movingTabs[0];
let endEdge = (ele) => ele[screenAxis] + bounds(ele)[size];
@@ -485,7 +487,7 @@
);
moveOverThreshold = gBrowser._tabGroupsEnabled
? Services.prefs.getIntPref('browser.tabs.dragDrop.moveOverThresholdPercent') / 100
? Services.prefs.getIntPref("browser.tabs.dragDrop.moveOverThresholdPercent") / 100
: 0.5;
moveOverThreshold = Math.min(1, Math.max(0, moveOverThreshold));
let shouldMoveOver = overlapPercent > moveOverThreshold;
@@ -516,8 +518,8 @@
}
}
this._tabbrowserTabs.removeAttribute('movingtab-group');
this._resetGroupTarget(document.querySelector('[dragover-groupTarget]'));
this._tabbrowserTabs.removeAttribute("movingtab-group");
this._resetGroupTarget(document.querySelector("[dragover-groupTarget]"));
delete dragData.shouldDropIntoCollapsedTabGroup;
@@ -538,8 +540,8 @@
dropBefore = true;
}
this._setDragOverGroupColor(colorCode);
this._tabbrowserTabs.toggleAttribute('movingtab-addToGroup', colorCode);
this._tabbrowserTabs.toggleAttribute('movingtab-ungroup', !colorCode);
this._tabbrowserTabs.toggleAttribute("movingtab-addToGroup", colorCode);
this._tabbrowserTabs.toggleAttribute("movingtab-ungroup", !colorCode);
if (
newDropElementIndex == oldDropElementIndex &&
@@ -555,15 +557,15 @@
}
#isMovingTab() {
return this._tabbrowserTabs.hasAttribute('movingtab');
return this._tabbrowserTabs.hasAttribute("movingtab");
}
get #dragShiftableItems() {
const separator = gZenWorkspaces.pinnedTabsContainer.querySelector(
'.pinned-tabs-container-separator'
".pinned-tabs-container-separator"
);
// Make sure to always return the separator at the start of the array
return Services.prefs.getBoolPref('zen.view.show-newtab-button-top')
return Services.prefs.getBoolPref("zen.view.show-newtab-button-top")
? [separator, gZenWorkspaces.activeWorkspaceElement.newTabButton]
: [separator];
}
@@ -577,10 +579,10 @@
}
#shouldSwitchSpace(event) {
const padding = Services.prefs.getIntPref('zen.workspaces.dnd-switch-padding');
const padding = Services.prefs.getIntPref("zen.workspaces.dnd-switch-padding");
// If we are hovering over the edges of the gNavToolbox or the splitter, we
// can change the workspace after a short delay.
const splitter = document.getElementById('zen-sidebar-splitter');
const splitter = document.getElementById("zen-sidebar-splitter");
let rect = window.windowUtils.getBoundsWithoutFlushing(gNavToolbox);
if (!(gZenCompactModeManager.preference && gZenCompactModeManager.canHideSidebar)) {
rect.width += window.windowUtils.getBoundsWithoutFlushing(splitter).width;
@@ -601,7 +603,7 @@
#handle_sidebarDragOver(event) {
const dt = event.dataTransfer;
const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (draggedTab.hasAttribute('zen-essential')) {
if (draggedTab.hasAttribute("zen-essential")) {
this.clearSpaceSwitchTimer();
return;
}
@@ -655,12 +657,12 @@
let dragData = draggedTab._dragData;
let movingTabs = dragData.movingTabs;
if (!this._browserDragImageWrapper) {
const wrappingDiv = document.createXULElement('vbox');
canvas.style.borderRadius = '8px';
canvas.style.border = '2px solid white';
wrappingDiv.style.width = 200 + 'px';
wrappingDiv.style.height = 130 + 'px';
wrappingDiv.style.position = 'relative';
const wrappingDiv = document.createXULElement("vbox");
canvas.style.borderRadius = "8px";
canvas.style.border = "2px solid white";
wrappingDiv.style.width = 200 + "px";
wrappingDiv.style.height = 130 + "px";
wrappingDiv.style.position = "relative";
this.#maybeCreateDragImageDot(movingTabs, wrappingDiv);
wrappingDiv.appendChild(canvas);
this._browserDragImageWrapper = wrappingDiv;
@@ -671,7 +673,7 @@
this.originalDragImageArgs[1],
this.originalDragImageArgs[2]
);
window.addEventListener('dragover', this.handle_windowDragEnter, {
window.addEventListener("dragover", this.handle_windowDragEnter, {
once: true,
capture: true,
});
@@ -686,12 +688,12 @@
let draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (
isTab(draggedTab) &&
!draggedTab.hasAttribute('zen-essential') &&
draggedTab.getAttribute('zen-workspace-id') != activeWorkspace
!draggedTab.hasAttribute("zen-essential") &&
draggedTab.getAttribute("zen-workspace-id") != activeWorkspace
) {
const movingTabs = draggedTab._dragData?.movingTabs || [draggedTab];
for (let tab of movingTabs) {
tab.setAttribute('zen-workspace-id', activeWorkspace);
tab.setAttribute("zen-workspace-id", activeWorkspace);
}
gBrowser.selectedTab = draggedTab;
}
@@ -716,9 +718,9 @@
!gZenStartup.isReady ||
gReduceMotion ||
!dropElement ||
dropElement.hasAttribute('zen-essential') ||
draggedTab.hasAttribute('zen-essential') ||
draggedTab.getAttribute('zen-workspace-id') != gZenWorkspaces.activeWorkspace ||
dropElement.hasAttribute("zen-essential") ||
draggedTab.hasAttribute("zen-essential") ||
draggedTab.getAttribute("zen-workspace-id") != gZenWorkspaces.activeWorkspace ||
!dropElement.visible ||
!draggedTab.visible
) {
@@ -727,16 +729,16 @@
this.#isAnimatingTabMove = true;
for (let item of this._tabbrowserTabs.ariaFocusableItems) {
item = elementToMove(item);
item.style.transform = '';
item.style.transform = "";
}
const animateElement = (ele, translateY) => {
ele.style.transform = `translateY(${translateY}px)`;
let animateInternal = (resolve) => {
gZenUIManager
.elementAnimate(ele, { y: [translateY, 0] }, { duration: 100, easing: 'ease-out' })
.elementAnimate(ele, { y: [translateY, 0] }, { duration: 100, easing: "ease-out" })
.then(() => {
ele.style.transform = '';
ele.style.zIndex = '';
ele.style.transform = "";
ele.style.zIndex = "";
})
.finally(resolve);
};
@@ -785,7 +787,7 @@
: -rect.height * tabsInBetween.length;
draggedTabTranslateY +=
extraTranslate * (draggedTab.elementIndex > dropElement.elementIndex ? 1 : -1);
draggedTab.style.zIndex = '9';
draggedTab.style.zIndex = "9";
animateElement(draggedTab, draggedTabTranslateY);
} catch (e) {
console.error(e);
@@ -802,7 +804,7 @@
gZenPinnedTabManager.removeTabContainersDragoverClass();
this.#maybeClearVerticalPinnedGridDragOver();
this.originalDragImageArgs = [];
window.removeEventListener('dragover', this.handle_windowDragEnter, { capture: true });
window.removeEventListener("dragover", this.handle_windowDragEnter, { capture: true });
this.#isOutOfWindow = false;
if (this._browserDragImageWrapper) {
this._browserDragImageWrapper.remove();
@@ -820,8 +822,8 @@
}
const margin = 2;
const rect = window.windowUtils.getBoundsWithoutFlushing(element);
this.#dragOverBackground = document.createElement('div');
this.#dragOverBackground.id = 'zen-dragover-background';
this.#dragOverBackground = document.createElement("div");
this.#dragOverBackground.id = "zen-dragover-background";
this.#dragOverBackground.style.height = `${rect.height - margin * 2}px`;
this.#dragOverBackground.style.top = `${rect.top + margin}px`;
gNavToolbox.appendChild(this.#dragOverBackground);
@@ -842,16 +844,16 @@
gZenPinnedTabManager.removeTabContainersDragoverClass();
}
// eslint-disable-next-line complexity
#applyDragoverIndicator(event, tabs, movingTabs, draggedTab) {
const separation = 4;
const dropZoneSelector =
':is(.tabbrowser-tab, .zen-drop-target, .tab-group-label, tab-group[split-view-group])';
":is(.tabbrowser-tab, .zen-drop-target, .tab-group-label, tab-group[split-view-group])";
let shouldPlayHapticFeedback = false;
let showIndicatorUnderNewTabButton = false;
let dropBefore = false;
let dropElement = event.target.closest(dropZoneSelector);
if (!dropElement) {
if (event.target.classList.contains('zen-workspace-empty-space')) {
if (event.target.classList.contains("zen-workspace-empty-space")) {
dropElement = this._tabbrowserTabs.ariaFocusableItems.at(-1);
// Only if there are no normal tabs to drop after
showIndicatorUnderNewTabButton = !tabs.some((tab) => !(tab.group || tab).pinned);
@@ -884,14 +886,14 @@
const overlapPercent = (event.clientY - rect.top) / rect.height;
// We wan't to leave a small threshold (20% for example) so we can drag tabs below and above
// a folder label without dragging into the folder.
let threshold = Services.prefs.getIntPref('zen.tabs.folder-dragover-threshold-percent') / 100;
let threshold = Services.prefs.getIntPref("zen.tabs.folder-dragover-threshold-percent") / 100;
let dropIntoFolder =
isZenFolder && (overlapPercent < threshold || overlapPercent > 1 - threshold);
if (
isTabGroupLabel(draggedTab) &&
draggedTab.group?.isZenFolder &&
(isTab(dropElement) || dropElement.hasAttribute('split-view-group')) &&
(!dropElement.pinned || dropElement.hasAttribute('zen-essential'))
(isTab(dropElement) || dropElement.hasAttribute("split-view-group")) &&
(!dropElement.pinned || dropElement.hasAttribute("zen-essential"))
) {
this.clearDragOverVisuals();
return;
@@ -900,7 +902,7 @@
isTab(dropElement) ||
dropIntoFolder ||
showIndicatorUnderNewTabButton ||
dropElement.hasAttribute('split-view-group')
dropElement.hasAttribute("split-view-group")
) {
if (showIndicatorUnderNewTabButton) {
rect = window.windowUtils.getBoundsWithoutFlushing(this.#dragShiftableItems.at(-1));
@@ -908,42 +910,39 @@
const indicator = gZenPinnedTabManager.dragIndicator;
let top = 0;
threshold =
Services.prefs.getIntPref('browser.tabs.dragDrop.moveOverThresholdPercent') / 100;
Services.prefs.getIntPref("browser.tabs.dragDrop.moveOverThresholdPercent") / 100;
if (overlapPercent > threshold) {
top = Math.round(rect.top + rect.height) + 'px';
dropBefore = false;
top = Math.round(rect.top + rect.height) + "px";
} else {
top = Math.round(rect.top) + 'px';
dropBefore = true;
top = Math.round(rect.top) + "px";
}
if (indicator.style.top !== top) {
shouldPlayHapticFeedback = true;
}
indicator.setAttribute('orientation', 'horizontal');
indicator.style.setProperty('--indicator-left', rect.left + separation / 2 + 'px');
indicator.style.setProperty('--indicator-width', rect.width - separation + 'px');
indicator.setAttribute("orientation", "horizontal");
indicator.style.setProperty("--indicator-left", rect.left + separation / 2 + "px");
indicator.style.setProperty("--indicator-width", rect.width - separation + "px");
indicator.style.top = top;
indicator.style.removeProperty('left');
indicator.style.removeProperty("left");
this.#removeDragOverBackground();
if (!isTab(dropElement) && dropElement?.parentElement?.isZenFolder) {
dropElement = dropElement.parentElement;
}
} else if (dropElement.classList.contains('zen-drop-target') && canHightlightGroup) {
} else if (dropElement.classList.contains("zen-drop-target") && canHightlightGroup) {
shouldPlayHapticFeedback =
this.#applyDragOverBackground(dropElement) && !gZenPinnedTabManager._dragIndicator;
gZenPinnedTabManager.removeTabContainersDragoverClass();
dropElement = dropElement.parentElement?.labelElement || dropElement;
if (dropElement.classList.contains('zen-current-workspace-indicator')) {
if (dropElement.classList.contains("zen-current-workspace-indicator")) {
dropElement =
elementToMove(this._tabbrowserTabs.ariaFocusableItems.at(gBrowser._numZenEssentials)) ||
dropElement;
dropBefore = true;
}
}
if (shouldPlayHapticFeedback) {
// eslint-disable-next-line mozilla/valid-services
Services.zen.playHapticFeedback();
}
return [dropBefore, dropElement];
}
#getDragImageOffset(event, tab, draggingTabs) {
@@ -960,13 +959,14 @@
};
}
// eslint-disable-next-line complexity
#animateVerticalPinnedGridDragOver(event) {
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
let dragData = draggedTab._dragData;
let movingTabs = dragData.movingTabs;
this.clearDragOverVisuals();
if (
!draggedTab.hasAttribute('zen-essential') &&
!draggedTab.hasAttribute("zen-essential") &&
gBrowser._numZenEssentials >= gZenPinnedTabManager.maxEssentialTabs
) {
return;
@@ -975,11 +975,11 @@
if (!this._fakeEssentialTab) {
const numEssentials = gBrowser._numZenEssentials;
let pinnedTabs = this._tabbrowserTabs.ariaFocusableItems.slice(0, numEssentials);
this._fakeEssentialTab = document.createXULElement('vbox');
this._fakeEssentialTab = document.createXULElement("vbox");
this._fakeEssentialTab.elementIndex = numEssentials;
delete dragData.animDropElementIndex;
if (!draggedTab.hasAttribute('zen-essential')) {
event.target.closest('.zen-essentials-container').appendChild(this._fakeEssentialTab);
if (!draggedTab.hasAttribute("zen-essential")) {
event.target.closest(".zen-essentials-container").appendChild(this._fakeEssentialTab);
gZenWorkspaces.updateTabsContainers();
pinnedTabs.push(this._fakeEssentialTab);
}
@@ -1180,7 +1180,7 @@
for (let tab of tabs) {
if (tab != draggedTab) {
let [shiftX, shiftY] = getTabShift(tab, newIndex);
tab.style.transform = shiftX || shiftY ? `translate(${shiftX}px, ${shiftY}px)` : '';
tab.style.transform = shiftX || shiftY ? `translate(${shiftX}px, ${shiftY}px)` : "";
}
}
}
@@ -1190,7 +1190,7 @@
this._fakeEssentialTab.remove();
delete this._fakeEssentialTab;
for (let tab of this._tabbrowserTabs.visibleTabs.slice(0, gBrowser._numZenEssentials)) {
tab.style.transform = '';
tab.style.transform = "";
}
gZenWorkspaces.updateTabsContainers();
}
@@ -1202,15 +1202,15 @@
const dragData = draggedTab._dragData;
const [wrapper] = this.originalDragImageArgs;
const tab = wrapper.firstElementChild;
tab.setAttribute('zen-essential', 'true');
tab.setAttribute('pinned', 'true');
tab.setAttribute('selected', 'true');
tab.setAttribute("zen-essential", "true");
tab.setAttribute("pinned", "true");
tab.setAttribute("selected", "true");
const draggedTabRect = window.windowUtils.getBoundsWithoutFlushing(this._fakeEssentialTab);
tab.style.minWidth = tab.style.maxWidth = wrapper.style.width = draggedTabRect.width + 'px';
tab.style.minWidth = tab.style.maxWidth = wrapper.style.width = draggedTabRect.width + "px";
tab.style.minHeight =
tab.style.maxHeight =
wrapper.style.height =
draggedTabRect.height + 'px';
draggedTabRect.height + "px";
const offsetY = dragData.offsetY;
const offsetX = dragData.offsetX;
// Apply a transform translate to the tab in order to center it within the drag image
@@ -1224,17 +1224,17 @@
const draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
const wrapper = this.originalDragImageArgs[0];
const tab = wrapper.firstElementChild;
tab.style.setProperty('transition', 'none', 'important');
tab.removeAttribute('zen-essential');
tab.removeAttribute('pinned');
tab.style.minWidth = tab.style.maxWidth = '';
tab.style.minHeight = tab.style.maxHeight = '';
tab.style.transform = '';
tab.style.setProperty("transition", "none", "important");
tab.removeAttribute("zen-essential");
tab.removeAttribute("pinned");
tab.style.minWidth = tab.style.maxWidth = "";
tab.style.minHeight = tab.style.maxHeight = "";
tab.style.transform = "";
const rect = window.windowUtils.getBoundsWithoutFlushing(draggedTab);
wrapper.style.width = rect.width + 'px';
wrapper.style.height = rect.height + 'px';
wrapper.style.width = rect.width + "px";
wrapper.style.height = rect.height + "px";
setTimeout(() => {
tab.style.transition = '';
tab.style.transition = "";
dt.updateDragImage(...this.originalDragImageArgs);
}, 50);
}

View File

@@ -53,7 +53,7 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
<ellipse cx="18" cy="16" rx="1.25" ry="1.25"/>
</g>
</svg>`,
'image/svg+xml'
"image/svg+xml"
).documentElement;
constructor() {
@@ -70,29 +70,30 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
this._activeTabs = [];
this.icon.appendChild(nsZenFolder.rawIcon.cloneNode(true));
this.labelElement.parentElement.setAttribute('context', 'zenFolderActions');
this.labelElement.parentElement.setAttribute("context", "zenFolderActions");
this.labelElement.onRenameFinished = (newLabel) => {
this.name = newLabel.trim() || 'Folder';
const event = new CustomEvent('ZenFolderRenamed', {
this.name = newLabel.trim() || "Folder";
const event = new CustomEvent("ZenFolderRenamed", {
bubbles: true,
});
this.dispatchEvent(event);
};
if (this.collapsed) {
this.groupContainer.setAttribute('hidden', true);
this.groupContainer.setAttribute("hidden", true);
}
}
get icon() {
return this.querySelector('.tab-group-folder-icon');
return this.querySelector(".tab-group-folder-icon");
}
/**
* Returns the group this folder belongs to.
*
* @returns {MozTabbrowserTabGroup|null} The group this folder belongs to, or null if it is not part of a group.
**/
*/
get group() {
if (gBrowser.isTabGroup(this.parentElement?.parentElement)) {
return this.parentElement.parentElement;
@@ -107,10 +108,12 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
get activeGroups() {
let activeGroups = [];
let currentGroup = this;
if (currentGroup?.hasAttribute('has-active')) activeGroups.push(currentGroup);
if (currentGroup?.hasAttribute("has-active")) {
activeGroups.push(currentGroup);
}
while (currentGroup?.group) {
currentGroup = currentGroup?.group;
if (currentGroup?.hasAttribute('has-active')) {
if (currentGroup?.hasAttribute("has-active")) {
activeGroups.push(currentGroup);
}
}
@@ -118,14 +121,14 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
}
get childActiveGroups() {
if (this.tagName === 'zen-workspace-collapsible-pins') {
return Array.from(this.parentElement.querySelectorAll('zen-folder[has-active]'));
if (this.tagName === "zen-workspace-collapsible-pins") {
return Array.from(this.parentElement.querySelectorAll("zen-folder[has-active]"));
}
return Array.from(this.querySelectorAll('zen-folder[has-active]'));
return Array.from(this.querySelectorAll("zen-folder[has-active]"));
}
rename() {
if (!document.documentElement.hasAttribute('zen-sidebar-expanded')) {
if (!document.documentElement.hasAttribute("zen-sidebar-expanded")) {
return;
}
gZenVerticalTabsManager.renameTabStart({
@@ -143,7 +146,7 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
} while (currentFolder);
gZenFolders.createFolder([], {
renameFolder: !gZenUIManager.testingEnabled,
label: 'Subfolder',
label: "Subfolder",
insertAfter: this.groupContainer.lastElementChild,
});
}
@@ -151,8 +154,8 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
async unpackTabs() {
this.collapsed = false;
for (let tab of this.allItems.reverse()) {
tab = tab.group.hasAttribute('split-view-group') ? tab.group : tab;
if (tab.hasAttribute('zen-empty-tab')) {
tab = tab.group.hasAttribute("split-view-group") ? tab.group : tab;
if (tab.hasAttribute("zen-empty-tab")) {
gBrowser.removeTab(tab);
} else {
gBrowser.ungroupTab(tab);
@@ -162,7 +165,7 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
async delete() {
for (const tab of this.allItemsRecursive) {
if (tab.hasAttribute('zen-empty-tab')) {
if (tab.hasAttribute("zen-empty-tab")) {
// Manually remove the empty tabs as removeTabs() inside removeTabGroup
// does ignore them.
gBrowser.removeTab(tab);
@@ -187,8 +190,8 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
return [...this.groupContainer.children].filter(
(child) =>
!(
child.classList.contains('zen-tab-group-start') ||
child.classList.contains('pinned-tabs-container-separator')
child.classList.contains("zen-tab-group-start") ||
child.classList.contains("pinned-tabs-container-separator")
)
);
}
@@ -207,26 +210,26 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
set pinned(value) {}
get iconURL() {
return this.icon.querySelector('image')?.getAttribute('href') || '';
return this.icon.querySelector("image")?.getAttribute("href") || "";
}
set activeTabs(tabs) {
if (tabs.length) {
this._activeTabs = tabs;
for (let tab of tabs) {
tab.setAttribute('folder-active', 'true');
tab.setAttribute("folder-active", "true");
}
} else {
const folders = new Map();
for (let tab of this._activeTabs) {
const group = tab?.group?.hasAttribute('split-view-group') ? tab?.group?.group : tab?.group;
const group = tab?.group?.hasAttribute("split-view-group") ? tab?.group?.group : tab?.group;
if (!folders.has(group?.id)) {
folders.set(group?.id, group?.activeGroups?.at(-1));
}
let activeGroup = folders.get(group?.id);
if (!activeGroup) {
tab.removeAttribute('folder-active');
tab.style.removeProperty('--zen-folder-indent');
tab.removeAttribute("folder-active");
tab.style.removeProperty("--zen-folder-indent");
}
}
this._activeTabs = [];
@@ -239,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");
}
unloadAllTabs(event) {
@@ -266,6 +269,7 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
/**
* Get the root most collapsed folder in the tree.
*
* @returns {ZenFolder|null} The root most collapsed folder, or null if none are collapsed.
*/
get rootMostCollapsedFolder() {
@@ -281,4 +285,4 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
}
}
customElements.define('zen-folder', nsZenFolder);
customElements.define("zen-folder", nsZenFolder);

File diff suppressed because it is too large Load Diff

View File

@@ -13,23 +13,14 @@ zen-folder {
flex-direction: column;
visibility: visible;
--zen-folder-behind-bgcolor: light-dark(
color-mix(in srgb, var(--zen-primary-color) 60%, gray),
color-mix(in srgb, var(--zen-primary-color) 60%, #c1c1c1)
);
--zen-folder-front-bgcolor: light-dark(
color-mix(in srgb, var(--zen-primary-color), white 70%),
color-mix(in srgb, var(--zen-primary-color), black 40%)
);
--zen-folder-stroke: light-dark(
color-mix(in srgb, var(--zen-primary-color) 50%, black),
color-mix(in srgb, var(--zen-colors-primary) 15%, #ebebeb)
);
--zen-folder-behind-bgcolor: light-dark(color-mix(in srgb, var(--zen-primary-color) 60%, gray), color-mix(in srgb, var(--zen-primary-color) 60%, #c1c1c1));
--zen-folder-front-bgcolor: light-dark(color-mix(in srgb, var(--zen-primary-color), white 70%), color-mix(in srgb, var(--zen-primary-color), black 40%));
--zen-folder-stroke: light-dark(color-mix(in srgb, var(--zen-primary-color) 50%, black), color-mix(in srgb, var(--zen-colors-primary) 15%, #ebebeb));
-moz-window-dragging: no-drag;
transition: var(--zen-tabbox-element-indent-transition);
:root[zen-sidebar-expanded='true'] & {
:root[zen-sidebar-expanded="true"] & {
margin-inline-start: var(--zen-folder-indent) !important;
}
@@ -82,7 +73,7 @@ zen-folder {
height: calc(var(--tab-block-margin) * 2 + var(--tab-min-height));
padding-inline: var(--tab-group-label-padding);
:root[zen-sidebar-expanded='true'] & {
:root[zen-sidebar-expanded="true"] & {
padding-inline-end: calc(var(--tab-group-label-padding) * 2);
}
@@ -111,7 +102,7 @@ zen-folder {
:root:not([zen-sidebar-expanded]) & {
transition: transform 0.1s ease;
&[state='open'] {
&[state="open"] {
transform: translate(-2px);
}
}
@@ -131,11 +122,11 @@ zen-folder {
opacity 0.3s cubic-bezier(0.42, 0, 0, 1);
}
&[state='open'] .back {
&[state="open"] .back {
transform: skewX(16deg) translate(-2px, 3.4px) scale(0.85);
}
&[state='open'] :is(.front, .dots, .icon) {
&[state="open"] :is(.front, .dots, .icon) {
transform: skewX(-16deg) translate(11.1px, 3.4px) scale(0.85);
}
@@ -147,18 +138,18 @@ zen-folder {
opacity: 0;
}
&[active='true'] .icon {
&[active="true"] .icon {
opacity: 0;
}
&[active='true'] .dots {
&[active="true"] .dots {
opacity: 1;
}
}
}
&::before {
content: '';
content: "";
position: absolute;
top: 2px;
left: 2px;

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
// 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/.
export class ZenGlanceChild extends JSWindowActorChild {
#activationMethod;
@@ -10,13 +11,13 @@ export class ZenGlanceChild extends JSWindowActorChild {
async handleEvent(event) {
const handler = this[`on_${event.type}`];
if (typeof handler === 'function') {
if (typeof handler === "function") {
await handler.call(this, event);
}
}
async #initActivationMethod() {
this.#activationMethod = await this.sendQuery('ZenGlance:GetActivationMethod');
this.#activationMethod = await this.sendQuery("ZenGlance:GetActivationMethod");
}
#ensureOnlyKeyModifiers(event) {
@@ -29,7 +30,7 @@ export class ZenGlanceChild extends JSWindowActorChild {
if (!url.match(/^(?:[a-z]+:)?\/\//i)) {
url = this.contentWindow.location.origin + url;
}
this.sendAsyncMessage('ZenGlance:OpenGlance', {
this.sendAsyncMessage("ZenGlance:OpenGlance", {
url,
});
}
@@ -49,7 +50,7 @@ export class ZenGlanceChild extends JSWindowActorChild {
if (anchorRect.width * anchorRect.height > rect.width * rect.height) {
rect = anchorRect;
}
this.sendAsyncMessage('ZenGlance:RecordLinkClickData', {
this.sendAsyncMessage("ZenGlance:RecordLinkClickData", {
clientX: rect.left,
clientY: rect.top,
width: rect.width,
@@ -60,10 +61,12 @@ export class ZenGlanceChild extends JSWindowActorChild {
/**
* Returns the closest A element from the event target
* and the element to record (originalTarget or target)
*
* @param {Event} event
*/
#getTargetFromEvent(event) {
// get closest A element
const target = event.target.closest('A');
const target = event.target.closest("A");
const elementToRecord = event.originalTarget || event.target;
return {
target,
@@ -87,13 +90,13 @@ export class ZenGlanceChild extends JSWindowActorChild {
return;
}
const activationMethod = this.#activationMethod;
if (activationMethod === 'ctrl' && !event.ctrlKey) {
if (activationMethod === "ctrl" && !event.ctrlKey) {
return;
} else if (activationMethod === 'alt' && !event.altKey) {
} else if (activationMethod === "alt" && !event.altKey) {
return;
} else if (activationMethod === 'shift' && !event.shiftKey) {
} else if (activationMethod === "shift" && !event.shiftKey) {
return;
} else if (activationMethod === 'meta' && !event.metaKey) {
} else if (activationMethod === "meta" && !event.metaKey) {
return;
}
event.preventDefault();
@@ -102,10 +105,10 @@ export class ZenGlanceChild extends JSWindowActorChild {
}
on_keydown(event) {
if (event.defaultPrevented || event.key !== 'Escape') {
if (event.defaultPrevented || event.key !== "Escape") {
return;
}
this.sendAsyncMessage('ZenGlance:CloseGlance', {
this.sendAsyncMessage("ZenGlance:CloseGlance", {
hasFocused: this.contentWindow.document.activeElement !== this.contentWindow.document.body,
});
}

View File

@@ -1,6 +1,9 @@
// 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/.
/* eslint-disable consistent-return */
export class ZenGlanceParent extends JSWindowActorParent {
constructor() {
super();
@@ -8,14 +11,14 @@ export class ZenGlanceParent extends JSWindowActorParent {
async receiveMessage(message) {
switch (message.name) {
case 'ZenGlance:GetActivationMethod': {
return Services.prefs.getStringPref('zen.glance.activation-method', 'ctrl');
case "ZenGlance:GetActivationMethod": {
return Services.prefs.getStringPref("zen.glance.activation-method", "ctrl");
}
case 'ZenGlance:OpenGlance': {
case "ZenGlance:OpenGlance": {
this.openGlance(this.browsingContext.topChromeWindow, message.data);
break;
}
case 'ZenGlance:CloseGlance': {
case "ZenGlance:CloseGlance": {
const params = {
onTabClose: true,
...message.data,
@@ -23,7 +26,7 @@ export class ZenGlanceParent extends JSWindowActorParent {
this.browsingContext.topChromeWindow.gZenGlanceManager.closeGlance(params);
break;
}
case 'ZenGlance:RecordLinkClickData': {
case "ZenGlance:RecordLinkClickData": {
this.browsingContext.topChromeWindow.gZenGlanceManager.lastLinkClickData = message.data;
break;
}

View File

@@ -3,10 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
export function openGlanceOnTab(window, callback, close = true) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
window.gZenGlanceManager
.openGlance({
url: 'https://example.com',
url: "https://example.com",
clientX: 0,
clientY: 0,
width: 0,

View File

@@ -14,22 +14,18 @@
gap: 12px;
max-width: 56px;
:root:not([zen-right-side='true']) & {
:root:not([zen-right-side="true"]) & {
left: 100%;
}
:root[zen-right-side='true'] & {
:root[zen-right-side="true"] & {
right: 100%;
}
& toolbarbutton {
width: 32px;
height: 32px;
background: color-mix(
in srgb,
light-dark(rgb(24, 24, 24), rgb(231, 231, 231)) 96%,
var(--zen-primary-color)
);
background: color-mix(in srgb, light-dark(rgb(24, 24, 24), rgb(231, 231, 231)) 96%, var(--zen-primary-color));
transition:
background 0.05s ease,
scale 0.05s ease;
@@ -57,7 +53,7 @@
}
}
&[disabled='true'] {
&[disabled="true"] {
scale: 1 !important;
& image {
opacity: 0.5;
@@ -93,7 +89,7 @@
}
}
:root[zen-no-padding='true'] .browserSidebarContainer.zen-glance-background {
:root[zen-no-padding="true"] .browserSidebarContainer.zen-glance-background {
--zen-native-inner-radius: 6px;
--zen-element-separation: 6px;
}
@@ -103,7 +99,7 @@
}
.browserSidebarContainer.zen-glance-background,
.browserSidebarContainer.zen-glance-overlay .browserContainer:not([fade-out='true']) {
.browserSidebarContainer.zen-glance-overlay .browserContainer:not([fade-out="true"]) {
border-radius: var(--zen-native-inner-radius);
}
@@ -114,7 +110,7 @@
/* Hint compositor to optimize animations and scrolling */
will-change: transform, opacity, filter;
:root[zen-no-padding='true'] & {
:root[zen-no-padding="true"] & {
--zen-native-inner-radius: 0px;
}
@@ -129,11 +125,11 @@
width: 85%;
height: 100%;
&:not([has-finished-animation='true']) #statuspanel {
&:not([has-finished-animation="true"]) #statuspanel {
display: none;
}
&[has-finished-animation='true'] {
&[has-finished-animation="true"] {
position: relative !important;
transition: 0s !important;
transform: none !important;
@@ -154,12 +150,12 @@
height: 100%;
}
&[animate='true'] {
&[animate="true"] {
position: absolute;
}
}
&[fade-out='true'] {
&[fade-out="true"] {
& .browserStack {
transition: opacity 0.15s ease-in-out;
opacity: 0;

File diff suppressed because it is too large Load Diff

View File

@@ -5,11 +5,15 @@
const lazy = {};
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'RESPECT_PIP_DISABLED',
'media.videocontrols.picture-in-picture.respect-disablePictureInPicture',
"RESPECT_PIP_DISABLED",
"media.videocontrols.picture-in-picture.respect-disablePictureInPicture",
true
);
/**
* Zen Media Controller, handles the small media control bar UI and interactions
* located at the bottom of the sidebar.
*/
class nsZenMediaController {
_currentMediaController = null;
_currentBrowser = null;
@@ -24,7 +28,7 @@ class nsZenMediaController {
mediaFocusButton = null;
mediaProgressBarContainer = null;
supportedKeys = ['playpause', 'previoustrack', 'nexttrack'];
supportedKeys = ["playpause", "previoustrack", "nexttrack"];
mediaControllersMap = new Map();
_tabTimeout = null;
@@ -33,16 +37,18 @@ class nsZenMediaController {
#isSeeking = false;
init() {
if (!Services.prefs.getBoolPref('zen.mediacontrols.enabled', true)) return;
if (!Services.prefs.getBoolPref("zen.mediacontrols.enabled", true)) {
return;
}
this.mediaTitle = document.querySelector('#zen-media-title');
this.mediaArtist = document.querySelector('#zen-media-artist');
this.mediaControlBar = document.querySelector('#zen-media-controls-toolbar');
this.mediaProgressBar = document.querySelector('#zen-media-progress-bar');
this.mediaCurrentTime = document.querySelector('#zen-media-current-time');
this.mediaDuration = document.querySelector('#zen-media-duration');
this.mediaFocusButton = document.querySelector('#zen-media-focus-button');
this.mediaProgressBarContainer = document.querySelector('#zen-media-progress-hbox');
this.mediaTitle = document.querySelector("#zen-media-title");
this.mediaArtist = document.querySelector("#zen-media-artist");
this.mediaControlBar = document.querySelector("#zen-media-controls-toolbar");
this.mediaProgressBar = document.querySelector("#zen-media-progress-bar");
this.mediaCurrentTime = document.querySelector("#zen-media-current-time");
this.mediaDuration = document.querySelector("#zen-media-duration");
this.mediaFocusButton = document.querySelector("#zen-media-focus-button");
this.mediaProgressBarContainer = document.querySelector("#zen-media-progress-hbox");
this.onPositionstateChange = this._onPositionstateChange.bind(this);
this.onPlaybackstateChange = this._onPlaybackstateChange.bind(this);
@@ -55,50 +61,56 @@ class nsZenMediaController {
}
#initEventListeners() {
this.mediaControlBar.addEventListener('mousedown', (event) => {
if (event.target.closest(':is(toolbarbutton,#zen-media-progress-hbox)')) return;
else this.onMediaFocus();
this.mediaControlBar.addEventListener("mousedown", (event) => {
if (event.target.closest(":is(toolbarbutton,#zen-media-progress-hbox)")) {
return;
}
this.onMediaFocus();
});
this.mediaControlBar.addEventListener('command', (event) => {
const button = event.target.closest('toolbarbutton');
if (!button) return;
this.mediaControlBar.addEventListener("command", (event) => {
const button = event.target.closest("toolbarbutton");
if (!button) {
return;
}
switch (button.id) {
case 'zen-media-pip-button':
case "zen-media-pip-button":
this.onMediaPip();
break;
case 'zen-media-close-button':
case "zen-media-close-button":
this.onControllerClose();
break;
case 'zen-media-focus-button':
case "zen-media-focus-button":
this.onMediaFocus();
break;
case 'zen-media-mute-button':
case "zen-media-mute-button":
this.onMediaMute();
break;
case 'zen-media-previoustrack-button':
case "zen-media-previoustrack-button":
this.onMediaPlayPrev();
break;
case 'zen-media-nexttrack-button':
case "zen-media-nexttrack-button":
this.onMediaPlayNext();
break;
case 'zen-media-playpause-button':
case "zen-media-playpause-button":
this.onMediaToggle();
break;
case 'zen-media-mute-mic-button':
case "zen-media-mute-mic-button":
this.onMicrophoneMuteToggle();
break;
case 'zen-media-mute-camera-button':
case "zen-media-mute-camera-button":
this.onCameraMuteToggle();
break;
}
});
this.mediaProgressBar.addEventListener('input', this.onMediaSeekDrag.bind(this));
this.mediaProgressBar.addEventListener('change', this.onMediaSeekComplete.bind(this));
this.mediaProgressBar.addEventListener("input", this.onMediaSeekDrag.bind(this));
this.mediaProgressBar.addEventListener("change", this.onMediaSeekComplete.bind(this));
window.addEventListener('TabSelect', (event) => {
if (this.isSharing) return;
window.addEventListener("TabSelect", (event) => {
if (this.isSharing) {
return;
}
const linkedBrowser = event.target.linkedBrowser;
this.switchController();
@@ -113,8 +125,11 @@ class nsZenMediaController {
this.hideMediaControls();
} else {
this._tabTimeout = setTimeout(() => {
if (!this.mediaControlBar.hasAttribute('pip')) this.showMediaControls();
else this._tabTimeout = null;
if (!this.mediaControlBar.hasAttribute("pip")) {
this.showMediaControls();
} else {
this._tabTimeout = null;
}
}, 500);
}
}
@@ -122,15 +137,15 @@ class nsZenMediaController {
const onTabDiscardedOrClosed = this.onTabDiscardedOrClosed.bind(this);
window.addEventListener('TabClose', onTabDiscardedOrClosed);
window.addEventListener('TabBrowserDiscarded', onTabDiscardedOrClosed);
window.addEventListener("TabClose", onTabDiscardedOrClosed);
window.addEventListener("TabBrowserDiscarded", onTabDiscardedOrClosed);
window.addEventListener('DOMAudioPlaybackStarted', (event) => {
window.addEventListener("DOMAudioPlaybackStarted", (event) => {
setTimeout(() => {
if (
this._currentMediaController?.isPlaying &&
this.mediaControlBar.hasAttribute('hidden') &&
!this.mediaControlBar.hasAttribute('pip')
this.mediaControlBar.hasAttribute("hidden") &&
!this.mediaControlBar.hasAttribute("pip")
) {
const { selectedBrowser } = gBrowser;
if (selectedBrowser.browserId !== this._currentBrowser.browserId) {
@@ -142,7 +157,7 @@ class nsZenMediaController {
this.activateMediaControls(event.target.browsingContext.mediaController, event.target);
});
window.addEventListener('DOMAudioPlaybackStopped', () => this.updateMuteState());
window.addEventListener("DOMAudioPlaybackStopped", () => this.updateMuteState());
}
onTabDiscardedOrClosed(event) {
@@ -171,12 +186,12 @@ class nsZenMediaController {
shouldHide = true
) {
if (shouldForget && mediaController) {
mediaController.removeEventListener('pictureinpicturemodechange', this.onPipModeChange);
mediaController.removeEventListener('positionstatechange', this.onPositionstateChange);
mediaController.removeEventListener('playbackstatechange', this.onPlaybackstateChange);
mediaController.removeEventListener('supportedkeyschange', this.onSupportedKeysChange);
mediaController.removeEventListener('metadatachange', this.onMetadataChange);
mediaController.removeEventListener('deactivated', this.onDeactivated);
mediaController.removeEventListener("pictureinpicturemodechange", this.onPipModeChange);
mediaController.removeEventListener("positionstatechange", this.onPositionstateChange);
mediaController.removeEventListener("playbackstatechange", this.onPlaybackstateChange);
mediaController.removeEventListener("supportedkeyschange", this.onSupportedKeysChange);
mediaController.removeEventListener("metadatachange", this.onMetadataChange);
mediaController.removeEventListener("deactivated", this.onDeactivated);
this.mediaControllersMap.delete(mediaController.id);
}
@@ -190,36 +205,40 @@ class nsZenMediaController {
this._mediaUpdateInterval = null;
}
if (shouldHide) await this.hideMediaControls();
this.mediaControlBar.removeAttribute('muted');
this.mediaControlBar.classList.remove('playing');
if (shouldHide) {
await this.hideMediaControls();
}
this.mediaControlBar.removeAttribute("muted");
this.mediaControlBar.classList.remove("playing");
}
}
get isSharing() {
return this.mediaControlBar.hasAttribute('media-sharing');
return this.mediaControlBar.hasAttribute("media-sharing");
}
set isSharing(value) {
if (this._currentBrowser?.browsingContext && !value) {
const webRTC = this._currentBrowser.browsingContext.currentWindowGlobal.getActor('WebRTC');
webRTC.sendAsyncMessage('webrtc:UnmuteMicrophone');
webRTC.sendAsyncMessage('webrtc:UnmuteCamera');
const webRTC = this._currentBrowser.browsingContext.currentWindowGlobal.getActor("WebRTC");
webRTC.sendAsyncMessage("webrtc:UnmuteMicrophone");
webRTC.sendAsyncMessage("webrtc:UnmuteCamera");
}
if (!value) {
this.mediaControlBar.removeAttribute('mic-muted');
this.mediaControlBar.removeAttribute('camera-muted');
this.mediaControlBar.removeAttribute("mic-muted");
this.mediaControlBar.removeAttribute("camera-muted");
} else {
this.mediaControlBar.setAttribute('media-position-hidden', '');
this.mediaControlBar.setAttribute('media-sharing', '');
this.mediaControlBar.setAttribute("media-position-hidden", "");
this.mediaControlBar.setAttribute("media-sharing", "");
}
}
hideMediaControls() {
if (this.mediaControlBar.hasAttribute('hidden')) return;
if (this.mediaControlBar.hasAttribute("hidden")) {
return;
}
return gZenUIManager.motion
gZenUIManager.motion
.animate(
this.mediaControlBar,
{
@@ -231,32 +250,38 @@ class nsZenMediaController {
}
)
.then(() => {
this.mediaControlBar.setAttribute('hidden', 'true');
this.mediaControlBar.removeAttribute('media-sharing');
this.mediaControlBar.setAttribute("hidden", "true");
this.mediaControlBar.removeAttribute("media-sharing");
gZenUIManager.updateTabsToolbar();
});
}
showMediaControls() {
if (!this.mediaControlBar.hasAttribute('hidden')) return;
if (!this.mediaControlBar.hasAttribute("hidden")) {
return;
}
if (!this.isSharing) {
if (!this._currentMediaController) return;
if (this._currentMediaController.isBeingUsedInPIPModeOrFullscreen)
return this.hideMediaControls();
if (!this._currentMediaController) {
return;
}
if (this._currentMediaController.isBeingUsedInPIPModeOrFullscreen) {
this.hideMediaControls();
return;
}
this.updatePipButton();
}
const mediaInfoElements = [this.mediaTitle, this.mediaArtist];
for (const element of mediaInfoElements) {
element.removeAttribute('overflow'); // So we can properly recalculate the overflow
element.removeAttribute("overflow"); // So we can properly recalculate the overflow
}
this.mediaControlBar.removeAttribute('hidden');
this.mediaControlBar.removeAttribute("hidden");
window.requestAnimationFrame(() => {
this.mediaControlBar.style.height =
this.mediaControlBar.querySelector('toolbaritem').getBoundingClientRect().height + 'px';
this.mediaControlBar.querySelector("toolbaritem").getBoundingClientRect().height + "px";
this.mediaControlBar.style.opacity = 0;
gZenUIManager.updateTabsToolbar();
gZenUIManager.motion.animate(
@@ -273,11 +298,12 @@ class nsZenMediaController {
addLabelOverflows(elements) {
for (const element of elements) {
// eslint-disable-next-line no-shadow
const parent = element.parentElement;
if (element.scrollWidth > parent.clientWidth) {
element.setAttribute('overflow', '');
element.setAttribute("overflow", "");
} else {
element.removeAttribute('overflow');
element.removeAttribute("overflow");
}
}
}
@@ -293,18 +319,18 @@ class nsZenMediaController {
this.updatePipButton();
if (
!this.mediaControlBar.classList.contains('playing') &&
!this.mediaControlBar.classList.contains("playing") &&
this._currentMediaController.isPlaying
) {
this.mediaControlBar.classList.add('playing');
this.mediaControlBar.classList.add("playing");
}
const iconURL =
this._currentBrowser.mIconURL || `page-icon:${this._currentBrowser.currentURI.spec}`;
this.mediaFocusButton.style.listStyleImage = `url(${iconURL})`;
this.mediaTitle.textContent = metadata.title || '';
this.mediaArtist.textContent = metadata.artist || '';
this.mediaTitle.textContent = metadata.title || "";
this.mediaArtist.textContent = metadata.artist || "";
gZenUIManager.updateTabsToolbar();
@@ -324,7 +350,9 @@ class nsZenMediaController {
this.updateMuteState();
this.switchController();
if (!mediaController.isActive || this._currentBrowser?.browserId === browser.browserId) return;
if (!mediaController.isActive || this._currentBrowser?.browserId === browser.browserId) {
return;
}
const metadata = mediaController.getMetadata();
const positionState = mediaController.getPositionState();
@@ -342,17 +370,17 @@ class nsZenMediaController {
this.setupMediaControlUI(metadata, positionState);
}
mediaController.addEventListener('pictureinpicturemodechange', this.onPipModeChange);
mediaController.addEventListener('positionstatechange', this.onPositionstateChange);
mediaController.addEventListener('playbackstatechange', this.onPlaybackstateChange);
mediaController.addEventListener('supportedkeyschange', this.onSupportedKeysChange);
mediaController.addEventListener('metadatachange', this.onMetadataChange);
mediaController.addEventListener('deactivated', this.onDeactivated);
mediaController.addEventListener("pictureinpicturemodechange", this.onPipModeChange);
mediaController.addEventListener("positionstatechange", this.onPositionstateChange);
mediaController.addEventListener("playbackstatechange", this.onPlaybackstateChange);
mediaController.addEventListener("supportedkeyschange", this.onSupportedKeysChange);
mediaController.addEventListener("metadatachange", this.onMetadataChange);
mediaController.addEventListener("deactivated", this.onDeactivated);
}
activateMediaDeviceControls(browser) {
if (browser?.browsingContext.currentWindowGlobal.hasActivePeerConnections()) {
this.mediaControlBar.removeAttribute('can-pip');
this.mediaControlBar.removeAttribute("can-pip");
this._currentBrowser = browser;
const tab = window.gBrowser.getTabForBrowser(browser);
@@ -362,7 +390,7 @@ class nsZenMediaController {
this.mediaFocusButton.style.listStyleImage = `url(${iconURL})`;
this.mediaTitle.textContent = tab.label;
this.mediaArtist.textContent = '';
this.mediaArtist.textContent = "";
this.showMediaControls();
}
@@ -376,19 +404,25 @@ class nsZenMediaController {
const isCurrentBrowser = this._currentBrowser?.browserId === browser.browserId;
const shouldShow = showCameraIndicator || showMicrophoneIndicator;
if (!isMatch) continue;
if (!isMatch) {
continue;
}
if (shouldShow && !(isCurrentBrowser && this.isSharing)) {
const webRTC = browser.browsingContext.currentWindowGlobal.getActor('WebRTC');
webRTC.sendAsyncMessage('webrtc:UnmuteMicrophone');
webRTC.sendAsyncMessage('webrtc:UnmuteCamera');
const webRTC = browser.browsingContext.currentWindowGlobal.getActor("WebRTC");
webRTC.sendAsyncMessage("webrtc:UnmuteMicrophone");
webRTC.sendAsyncMessage("webrtc:UnmuteCamera");
if (this._currentBrowser) this.isSharing = false;
if (this._currentBrowser) {
this.isSharing = false;
}
if (this._currentMediaController) {
this._currentMediaController.pause();
this.deinitMediaController(this._currentMediaController, true, true).then(() =>
this.activateMediaDeviceControls(browser)
);
} else this.activateMediaDeviceControls(browser);
} else {
this.activateMediaDeviceControls(browser);
}
} else if (!shouldShow && isCurrentBrowser && this.isSharing) {
this.isSharing = false;
this._currentBrowser = null;
@@ -411,15 +445,17 @@ class nsZenMediaController {
_onPlaybackstateChange() {
if (this._currentMediaController?.isPlaying) {
this.mediaControlBar.classList.add('playing');
this.mediaControlBar.classList.add("playing");
} else {
this.switchController();
this.mediaControlBar.classList.remove('playing');
this.mediaControlBar.classList.remove("playing");
}
}
_onSupportedKeysChange(event) {
if (event.target.id !== this._currentMediaController?.id) return;
if (event.target.id !== this._currentMediaController?.id) {
return;
}
for (const key of this.supportedKeys) {
const button = this.mediaControlBar.querySelector(`#zen-media-${key}-button`);
button.disabled = !event.target.supportedKeys.includes(key);
@@ -436,7 +472,9 @@ class nsZenMediaController {
lastUpdated: Date.now(),
});
if (event.target.id !== this._currentMediaController?.id) return;
if (event.target.id !== this._currentMediaController?.id) {
return;
}
this._currentPosition = event.position;
this._currentDuration = event.duration;
@@ -448,15 +486,21 @@ class nsZenMediaController {
switchController(force = false) {
let timeout = 3000;
if (this.isSharing) return;
if (this.#isSeeking) return;
if (this.isSharing) {
return;
}
if (this.#isSeeking) {
return;
}
if (this._controllerSwitchTimeout) {
clearTimeout(this._controllerSwitchTimeout);
this._controllerSwitchTimeout = null;
}
if (this.mediaControllersMap.size === 1) timeout = 0;
if (this.mediaControllersMap.size === 1) {
timeout = 0;
}
this._controllerSwitchTimeout = setTimeout(() => {
if (!this._currentMediaController?.isPlaying || force) {
const nextController = Array.from(this.mediaControllersMap.values())
@@ -496,11 +540,15 @@ class nsZenMediaController {
this._mediaUpdateInterval = null;
}
if (this._currentDuration >= 900_000)
return this.mediaControlBar.setAttribute('media-position-hidden', 'true');
else this.mediaControlBar.removeAttribute('media-position-hidden');
if (this._currentDuration >= 900_000) {
this.mediaControlBar.setAttribute("media-position-hidden", "true");
return;
}
this.mediaControlBar.removeAttribute("media-position-hidden");
if (!this._currentDuration) return;
if (!this._currentDuration) {
return;
}
this.mediaCurrentTime.textContent = this.formatSecondsToTime(this._currentPosition);
this.mediaDuration.textContent = this.formatSecondsToTime(this._currentDuration);
@@ -522,7 +570,9 @@ class nsZenMediaController {
}
formatSecondsToTime(seconds) {
if (!seconds || isNaN(seconds)) return '0:00';
if (!seconds || isNaN(seconds)) {
return "0:00";
}
const totalSeconds = Math.max(0, Math.ceil(seconds));
const hours = Math.floor(totalSeconds / 3600);
@@ -530,51 +580,55 @@ class nsZenMediaController {
const secs = (totalSeconds % 60).toString();
if (hours > 0) {
return `${hours}:${minutes.padStart(2, '0')}:${secs.padStart(2, '0')}`;
return `${hours}:${minutes.padStart(2, "0")}:${secs.padStart(2, "0")}`;
}
return `${minutes}:${secs.padStart(2, '0')}`;
return `${minutes}:${secs.padStart(2, "0")}`;
}
_onMetadataChange(event) {
if (event.target.id !== this._currentMediaController?.id) return;
if (event.target.id !== this._currentMediaController?.id) {
return;
}
this.updatePipButton();
const metadata = event.target.getMetadata();
this.mediaTitle.textContent = metadata.title || '';
this.mediaArtist.textContent = metadata.artist || '';
this.mediaTitle.textContent = metadata.title || "";
this.mediaArtist.textContent = metadata.artist || "";
const mediaInfoElements = [this.mediaTitle, this.mediaArtist];
for (const element of mediaInfoElements) {
element.removeAttribute('overflow');
element.removeAttribute("overflow");
}
this.addLabelOverflows(mediaInfoElements);
}
_onPictureInPictureModeChange(event) {
if (event.target.id !== this._currentMediaController?.id) return;
if (event.target.id !== this._currentMediaController?.id) {
return;
}
if (event.target.isBeingUsedInPIPModeOrFullscreen) {
this.hideMediaControls();
this.mediaControlBar.setAttribute('pip', '');
this.mediaControlBar.setAttribute("pip", "");
} else {
const { selectedBrowser } = gBrowser;
if (selectedBrowser.browserId !== this._currentBrowser.browserId) {
this.showMediaControls();
}
this.mediaControlBar.removeAttribute('pip');
this.mediaControlBar.removeAttribute("pip");
}
}
onMediaPlayPrev() {
if (this._currentMediaController?.supportedKeys.includes('previoustrack')) {
if (this._currentMediaController?.supportedKeys.includes("previoustrack")) {
this._currentMediaController.prevTrack();
}
}
onMediaPlayNext() {
if (this._currentMediaController?.supportedKeys.includes('nexttrack')) {
if (this._currentMediaController?.supportedKeys.includes("nexttrack")) {
this._currentMediaController.nextTrack();
}
}
@@ -589,7 +643,7 @@ class nsZenMediaController {
onMediaSeekComplete(event) {
const newPosition = (event.target.value / 100) * this._currentDuration;
if (this._currentMediaController?.supportedKeys.includes('seekto')) {
if (this._currentMediaController?.supportedKeys.includes("seekto")) {
this._currentMediaController.seekTo(newPosition);
this._currentMediaController.play();
}
@@ -598,12 +652,17 @@ class nsZenMediaController {
}
onMediaFocus() {
if (!this._currentBrowser) return;
if (!this._currentBrowser) {
return;
}
if (this._currentMediaController) this._currentMediaController.focus();
else if (this._currentBrowser) {
if (this._currentMediaController) {
this._currentMediaController.focus();
} else if (this._currentBrowser) {
const tab = window.gBrowser.getTabForBrowser(this._currentBrowser);
if (tab) window.gZenWorkspaces.switchTabIfNeeded(tab);
if (tab) {
window.gZenWorkspaces.switchTabIfNeeded(tab);
}
}
}
@@ -616,7 +675,7 @@ class nsZenMediaController {
}
onMediaToggle() {
if (this.mediaControlBar.classList.contains('playing')) {
if (this.mediaControlBar.classList.contains("playing")) {
this._currentMediaController?.pause();
} else {
this._currentMediaController?.play();
@@ -627,7 +686,9 @@ class nsZenMediaController {
if (this._currentMediaController) {
this._currentMediaController.pause();
this.deinitMediaController(this._currentMediaController);
} else if (this.isSharing) this.isSharing = false;
} else if (this.isSharing) {
this.isSharing = false;
}
this.hideMediaControls();
this.switchController(true);
@@ -635,51 +696,57 @@ class nsZenMediaController {
onMediaPip() {
this._currentBrowser.browsingContext.currentWindowGlobal
.getActor('PictureInPictureLauncher')
.sendAsyncMessage('PictureInPicture:KeyToggle');
.getActor("PictureInPictureLauncher")
.sendAsyncMessage("PictureInPicture:KeyToggle");
}
onMicrophoneMuteToggle() {
if (this._currentBrowser) {
const shouldMute = this.mediaControlBar.hasAttribute('mic-muted')
? 'webrtc:UnmuteMicrophone'
: 'webrtc:MuteMicrophone';
const shouldMute = this.mediaControlBar.hasAttribute("mic-muted")
? "webrtc:UnmuteMicrophone"
: "webrtc:MuteMicrophone";
this._currentBrowser.browsingContext.currentWindowGlobal
.getActor('WebRTC')
.getActor("WebRTC")
.sendAsyncMessage(shouldMute);
this.mediaControlBar.toggleAttribute('mic-muted');
this.mediaControlBar.toggleAttribute("mic-muted");
}
}
onCameraMuteToggle() {
if (this._currentBrowser) {
const shouldMute = this.mediaControlBar.hasAttribute('camera-muted')
? 'webrtc:UnmuteCamera'
: 'webrtc:MuteCamera';
const shouldMute = this.mediaControlBar.hasAttribute("camera-muted")
? "webrtc:UnmuteCamera"
: "webrtc:MuteCamera";
this._currentBrowser.browsingContext.currentWindowGlobal
.getActor('WebRTC')
.getActor("WebRTC")
.sendAsyncMessage(shouldMute);
this.mediaControlBar.toggleAttribute('camera-muted');
this.mediaControlBar.toggleAttribute("camera-muted");
}
}
updateMuteState() {
if (!this._currentBrowser) return;
this.mediaControlBar.toggleAttribute('muted', this._currentBrowser.audioMuted);
if (!this._currentBrowser) {
return;
}
this.mediaControlBar.toggleAttribute("muted", this._currentBrowser.audioMuted);
}
updatePipButton() {
if (!this._currentBrowser) return;
if (this.isSharing) return;
if (!this._currentBrowser) {
return;
}
if (this.isSharing) {
return;
}
const { totalPipCount, totalPipDisabled } = PictureInPicture.getEligiblePipVideoCount(
this._currentBrowser
);
const canPip = totalPipCount === 1 || (totalPipDisabled > 0 && lazy.RESPECT_PIP_DISABLED);
this.mediaControlBar.toggleAttribute('can-pip', canPip);
this.mediaControlBar.toggleAttribute("can-pip", canPip);
}
}

View File

@@ -115,13 +115,13 @@
}
& #zen-media-focus-button::after {
content: '';
content: "";
position: absolute;
width: 110%;
height: 110%;
background-repeat: no-repeat;
opacity: 1;
background: url('chrome://browser/content/zen-images/note-indicator.svg') no-repeat;
background: url("chrome://browser/content/zen-images/note-indicator.svg") no-repeat;
top: -70%;
left: 50%;
transform: translateX(-50%);
@@ -238,7 +238,7 @@
min-width: 1px;
&::before {
content: '';
content: "";
position: absolute;
width: 0.6em;
background: linear-gradient(to right, var(--zen-sidebar-notification-bg) 0%, transparent 100%);
@@ -309,7 +309,7 @@
}
}
:root:not([zen-sidebar-expanded='true']) {
:root:not([zen-sidebar-expanded="true"]) {
#zen-media-controls-toolbar {
display: none;
}

View File

@@ -5,11 +5,16 @@
import {
nsZenPreloadedFeature,
nsZenMultiWindowFeature,
} from 'chrome://browser/content/zen-components/ZenCommonUtils.mjs';
} from "chrome://browser/content/zen-components/ZenCommonUtils.mjs";
/**
* Zen Mods Manager, handles downloading, updating and applying Zen Mods.
*
* @augments nsZenPreloadedFeature
*/
class nsZenMods extends nsZenPreloadedFeature {
// private properties start
#kZenStylesheetModHeader = '/* Zen Mods - Generated by ZenMods.';
#kZenStylesheetModHeader = "/* Zen Mods - Generated by ZenMods.";
#kZenStylesheetModHeaderBody = `* DO NOT EDIT THIS FILE DIRECTLY!
* Your changes will be overwritten.
* Instead, go to the preferences and edit the mods there.
@@ -19,9 +24,9 @@ class nsZenMods extends nsZenPreloadedFeature {
/* End of Zen Mods */
`;
#getCurrentDateTime = () =>
new Intl.DateTimeFormat('en-US', {
dateStyle: 'full',
timeStyle: 'full',
new Intl.DateTimeFormat("en-US", {
dateStyle: "full",
timeStyle: "full",
}).format(new Date().getTime());
constructor() {
@@ -33,13 +38,13 @@ class nsZenMods extends nsZenPreloadedFeature {
get #modsBackend() {
if (!this.#_modsBackend) {
this.#_modsBackend = Cc['@mozilla.org/zen/mods-backend;1'].getService(Ci.nsIZenModsBackend);
this.#_modsBackend = Cc["@mozilla.org/zen/mods-backend;1"].getService(Ci.nsIZenModsBackend);
}
return this.#_modsBackend;
}
get #styleSheetPath() {
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
return PathUtils.join(PathUtils.profileDir, "chrome", "zen-themes.css");
}
async #handleDisableMods() {
@@ -47,13 +52,13 @@ class nsZenMods extends nsZenPreloadedFeature {
}
#getStylesheetPathForMod(mod) {
return PathUtils.join(this.getModFolder(mod.id), 'chrome.css');
return PathUtils.join(this.getModFolder(mod.id), "chrome.css");
}
async #readStylesheet() {
const path = this.modsRootPath;
if (!(await IOUtils.exists(path))) {
return '';
return "";
}
return await IOUtils.readUTF8(this.#styleSheetPath);
}
@@ -63,7 +68,7 @@ class nsZenMods extends nsZenPreloadedFeature {
const content = await this.#readStylesheet();
this.#modsBackend.rebuildModsStyles(content);
} catch (e) {
console.warn('[ZenMods]: Error rebuilding mods styles:', e);
console.warn("[ZenMods]: Error rebuilding mods styles:", e);
}
}
@@ -91,8 +96,9 @@ class nsZenMods extends nsZenPreloadedFeature {
}
async #getEnabledMods() {
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
console.log('[ZenMods]: Mods are disabled by user preference.');
if (Services.prefs.getBoolPref("zen.themes.disable-all", false)) {
// eslint-disable-next-line no-console
console.info("[ZenMods]: Mods are disabled by user preference.");
return [];
}
const modsObject = await this.getMods();
@@ -100,14 +106,15 @@ class nsZenMods extends nsZenPreloadedFeature {
(mod) => mod.enabled === undefined || mod.enabled
);
const modList = mods.map(({ name }) => name).join(', ');
// eslint-disable-next-line no-shadow
const modList = mods.map(({ name }) => name).join(", ");
const message =
modList !== ''
modList !== ""
? `[ZenMods]: Loading enabled Zen mods: ${modList}.`
: '[ZenMods]: No enabled Zen mods.';
: "[ZenMods]: No enabled Zen mods.";
console.log(message);
console.warn(message);
return mods;
}
@@ -124,21 +131,21 @@ class nsZenMods extends nsZenPreloadedFeature {
}
const getProperty =
type === 'checkbox' ? Services.prefs.getBoolPref : Services.prefs.getStringPref;
type === "checkbox" ? Services.prefs.getBoolPref : Services.prefs.getStringPref;
const setProperty =
type === 'checkbox' ? Services.prefs.setBoolPref : Services.prefs.setStringPref;
type === "checkbox" ? Services.prefs.setBoolPref : Services.prefs.setStringPref;
try {
getProperty(property);
} catch {
console.debug(
console.warn(
`[ZenMods]: Setting default value for ${property} to ${defaultValue} (${typeof defaultValue})`
);
if (
typeof defaultValue !== 'boolean' &&
typeof defaultValue !== 'string' &&
typeof defaultValue !== 'number'
typeof defaultValue !== "boolean" &&
typeof defaultValue !== "string" &&
typeof defaultValue !== "number"
) {
console.warn(
`[ZenMods]: Warning, invalid data type received (${typeof defaultValue}), skipping.`
@@ -148,7 +155,7 @@ class nsZenMods extends nsZenPreloadedFeature {
setProperty(
property,
typeof defaultValue === 'boolean' ? defaultValue : defaultValue.toString()
typeof defaultValue === "boolean" ? defaultValue : defaultValue.toString()
);
}
}
@@ -157,6 +164,7 @@ class nsZenMods extends nsZenPreloadedFeature {
#writeToDom(modsWithPreferences) {
for (const browser of nsZenMultiWindowFeature.browsers) {
// eslint-disable-next-line no-shadow
for (const { enabled, preferences, name } of modsWithPreferences) {
const sanitizedName = this.sanitizeModName(name);
@@ -167,29 +175,29 @@ class nsZenMods extends nsZenPreloadedFeature {
element.remove();
}
for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) {
const sanitizedProperty = property?.replaceAll(/\./g, '-');
for (const { property } of preferences.filter(({ type }) => type !== "checkbox")) {
const sanitizedProperty = property?.replaceAll(/\./g, "-");
browser.document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`);
browser.document.querySelector(":root").style.removeProperty(`--${sanitizedProperty}`);
}
continue;
}
for (const { property, type } of preferences) {
const value = Services.prefs.getStringPref(property, '');
const sanitizedProperty = property?.replaceAll(/\./g, '-');
const value = Services.prefs.getStringPref(property, "");
const sanitizedProperty = property?.replaceAll(/\./g, "-");
switch (type) {
case 'dropdown': {
if (value !== '') {
case "dropdown": {
if (value !== "") {
let element = browser.document.getElementById(sanitizedName);
if (!element) {
element = browser.document.createElement('div');
element = browser.document.createElement("div");
element.style.display = 'none';
element.setAttribute('id', sanitizedName);
element.style.display = "none";
element.setAttribute("id", sanitizedName);
browser.document.body.appendChild(element);
}
@@ -199,14 +207,14 @@ class nsZenMods extends nsZenPreloadedFeature {
break;
}
case 'string': {
if (value === '') {
case "string": {
if (value === "") {
browser.document
.querySelector(':root')
.querySelector(":root")
.style.removeProperty(`--${sanitizedProperty}`);
} else {
browser.document
.querySelector(':root')
.querySelector(":root")
.style.setProperty(`--${sanitizedProperty}`, value);
}
break;
@@ -256,12 +264,12 @@ class nsZenMods extends nsZenPreloadedFeature {
#compareVersions(version1, version2) {
let result = false;
if (typeof version1 !== 'object') {
version1 = version1.toString().split('.');
if (typeof version1 !== "object") {
version1 = version1.toString().split(".");
}
if (typeof version2 !== 'object') {
version2 = version2.toString().split('.');
if (typeof version2 !== "object") {
version2 = version2.toString().split(".");
}
for (let i = 0; i < Math.max(version1.length, version2.length); i++) {
@@ -287,7 +295,6 @@ class nsZenMods extends nsZenPreloadedFeature {
return `https://zen-browser.github.io/theme-store/themes/${modId}/theme.json`;
}
/* eslint-disable no-unused-vars */
async #downloadUrlToFile(url, path, isStyleSheet = false, maxRetries = 3, retryDelayMs = 500) {
let attempt = 0;
@@ -309,7 +316,7 @@ class nsZenMods extends nsZenPreloadedFeature {
} catch (e) {
attempt++;
if (attempt >= maxRetries) {
console.error('[ZenMods]: Error downloading file after retries', url, e);
console.error("[ZenMods]: Error downloading file after retries", url, e);
} else {
console.warn(
`[ZenMods]: Download failed (attempt ${attempt} of ${maxRetries}), retrying in ${retryDelayMs}ms...`,
@@ -350,21 +357,22 @@ class nsZenMods extends nsZenPreloadedFeature {
};
}
sanitizeModName(name) {
sanitizeModName(aName) {
// Do not change to "mod-" for backwards compatibility
return `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
// eslint-disable-next-line no-shadow
return `theme-${aName?.replaceAll(/\s/g, "-")?.replaceAll(/[^A-Za-z_-]+/g, "")}`;
}
get updatePref() {
return 'zen.mods.updated-value-observer';
return "zen.mods.updated-value-observer";
}
get modsRootPath() {
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
return PathUtils.join(PathUtils.profileDir, "chrome", "zen-themes");
}
get modsDataFile() {
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
return PathUtils.join(PathUtils.profileDir, "zen-themes.json");
}
getModFolder(modId) {
@@ -383,16 +391,16 @@ class nsZenMods extends nsZenPreloadedFeature {
try {
mods = await IOUtils.readJSON(this.modsDataFile);
if (mods === null || typeof mods !== 'object') {
throw new Error('Mods data file is invalid');
if (mods === null || typeof mods !== "object") {
throw new Error("Mods data file is invalid");
}
} catch {
// If we have a corrupted file, reset it
await IOUtils.writeJSON(this.modsDataFile, {});
Services.wm
.getMostRecentWindow('navigator:browser')
.gZenUIManager.showToast('zen-themes-corrupted', {
.getMostRecentWindow("navigator:browser")
.gZenUIManager.showToast("zen-themes-corrupted", {
timeout: 8000,
});
}
@@ -401,7 +409,7 @@ class nsZenMods extends nsZenPreloadedFeature {
}
async getModPreferences(mod) {
const modPath = PathUtils.join(this.modsRootPath, mod.id, 'preferences.json');
const modPath = PathUtils.join(this.modsRootPath, mod.id, "preferences.json");
if (!(await IOUtils.exists(modPath)) || !mod.preferences) {
return [];
@@ -424,10 +432,10 @@ class nsZenMods extends nsZenPreloadedFeature {
await SessionStore.promiseInitialized;
if (
Services.prefs.getBoolPref('zen.themes.disable-all', false) ||
Services.prefs.getBoolPref("zen.themes.disable-all", false) ||
Services.appinfo.inSafeMode
) {
console.log('[ZenMods]: Mods disabled by user or in safe mode.');
console.warn("[ZenMods]: Mods disabled by user or in safe mode.");
return;
}
@@ -464,29 +472,29 @@ class nsZenMods extends nsZenPreloadedFeature {
);
}
} catch (e) {
console.error('[ZenMods]: Error loading Zen Mods:', e);
console.error("[ZenMods]: Error loading Zen Mods:", e);
}
Services.prefs.addObserver(this.updatePref, this.#rebuildModsStylesheet.bind(this), false);
Services.prefs.addObserver('zen.themes.disable-all', this.#handleDisableMods.bind(this), false);
Services.prefs.addObserver(this.updatePref, this.#rebuildModsStylesheet.bind(this));
Services.prefs.addObserver("zen.themes.disable-all", this.#handleDisableMods.bind(this));
}
#setNewMilestoneIfNeeded() {
const previousMilestone = Services.prefs.getStringPref('zen.mods.milestone', '');
const previousMilestone = Services.prefs.getStringPref("zen.mods.milestone", "");
if (previousMilestone != Services.appinfo.version) {
Services.prefs.setStringPref('zen.mods.milestone', Services.appinfo.version);
Services.prefs.clearUserPref('zen.mods.last-update');
Services.prefs.setStringPref("zen.mods.milestone", Services.appinfo.version);
Services.prefs.clearUserPref("zen.mods.last-update");
}
}
#shouldAutoUpdate() {
const daysBeforeUpdate = Services.prefs.getIntPref('zen.mods.auto-update-days');
const lastUpdatedSec = Services.prefs.getIntPref('zen.mods.last-update', -1);
const daysBeforeUpdate = Services.prefs.getIntPref("zen.mods.auto-update-days");
const lastUpdatedSec = Services.prefs.getIntPref("zen.mods.last-update", -1);
const nowSec = Math.floor(Date.now() / 1000);
const daysSinceUpdate = (nowSec - lastUpdatedSec) / (60 * 60 * 24);
return (
(Services.prefs.getBoolPref('zen.mods.auto-update', true) &&
(Services.prefs.getBoolPref("zen.mods.auto-update", true) &&
daysSinceUpdate >= daysBeforeUpdate) ||
lastUpdatedSec < 0
);
@@ -505,10 +513,10 @@ class nsZenMods extends nsZenPreloadedFeature {
}
if (
!this.#compareVersions(possibleNewModVersion.version, currentMod.version ?? '0.0.0') &&
!this.#compareVersions(possibleNewModVersion.version, currentMod.version ?? "0.0.0") &&
possibleNewModVersion.version != currentMod.version
) {
console.log(
console.warn(
`[ZenMods]: Mod update found for mod ${currentMod.name} (${currentMod.id}), current: ${currentMod.version}, new: ${possibleNewModVersion.version}`
);
@@ -523,7 +531,7 @@ class nsZenMods extends nsZenPreloadedFeature {
return null;
} catch (e) {
console.error('[ZenMods]: Error checking for mod updates', e);
console.error("[ZenMods]: Error checking for mod updates", e);
return null;
}
@@ -531,7 +539,7 @@ class nsZenMods extends nsZenPreloadedFeature {
);
await this.updateMods(mods);
Services.prefs.setIntPref('zen.mods.last-update', Math.floor(Date.now() / 1000));
Services.prefs.setIntPref("zen.mods.last-update", Math.floor(Date.now() / 1000));
return updates.filter((update) => {
return update !== null;
});
@@ -540,7 +548,7 @@ class nsZenMods extends nsZenPreloadedFeature {
async removeMod(modId, triggerUpdate = true) {
const modPath = this.getModFolder(modId);
console.log(`[ZenMods]: Removing mod ${modPath}`);
console.warn(`[ZenMods]: Removing mod ${modPath}`);
await IOUtils.remove(modPath, { recursive: true, ignoreAbsent: true });
@@ -559,7 +567,7 @@ class nsZenMods extends nsZenPreloadedFeature {
const mods = await this.getMods();
const mod = mods[modId];
console.log(`[ZenMods]: Enabling mod ${mod.name}`);
console.warn(`[ZenMods]: Enabling mod ${mod.name}`);
mod.enabled = true;
@@ -570,7 +578,7 @@ class nsZenMods extends nsZenPreloadedFeature {
const mods = await this.getMods();
const mod = mods[modId];
console.log(`[ZenMods]: Disabling mod ${mod.name}`);
console.warn(`[ZenMods]: Disabling mod ${mod.name}`);
mod.enabled = false;
@@ -595,14 +603,14 @@ class nsZenMods extends nsZenPreloadedFeature {
const modPath = PathUtils.join(this.modsRootPath, mod.id);
await IOUtils.makeDirectory(modPath, { ignoreExisting: true });
await this.#downloadUrlToFile(mod.style, PathUtils.join(modPath, 'chrome.css'), true);
await this.#downloadUrlToFile(mod.readme, PathUtils.join(modPath, 'readme.md'));
await this.#downloadUrlToFile(mod.style, PathUtils.join(modPath, "chrome.css"), true);
await this.#downloadUrlToFile(mod.readme, PathUtils.join(modPath, "readme.md"));
if (mod.preferences) {
await this.#downloadUrlToFile(mod.preferences, PathUtils.join(modPath, 'preferences.json'));
await this.#downloadUrlToFile(mod.preferences, PathUtils.join(modPath, "preferences.json"));
}
} catch (e) {
console.error('[ZenMods]: Error installing mod', mod.id, e);
console.error("[ZenMods]: Error installing mod", mod.id, e);
}
}
@@ -619,7 +627,7 @@ class nsZenMods extends nsZenPreloadedFeature {
await this.installMod(mod);
}
} catch (e) {
console.error('[ZenMods]: Error checking for mod changes', e);
console.error("[ZenMods]: Error checking for mod changes", e);
}
}
@@ -629,10 +637,10 @@ class nsZenMods extends nsZenPreloadedFeature {
async requestMod(modId) {
const url = this.#composeModApiUrl(modId);
console.debug(`[ZenMods]: Fetching mod ${modId} info from ${url}`);
console.warn(`[ZenMods]: Fetching mod ${modId} info from ${url}`);
const data = await fetch(url, {
mode: 'no-cors',
mode: "no-cors",
});
if (data.ok) {

View File

@@ -8,19 +8,19 @@ export class ZenModsMarketplaceChild extends JSWindowActorChild {
}
handleEvent(event) {
if (event.type === 'DOMContentLoaded') {
if (event.type === "DOMContentLoaded") {
const verifier = this.contentWindow.document.querySelector(
'meta[name="zen-content-verified"]'
);
if (verifier) {
verifier.setAttribute('content', 'verified');
verifier.setAttribute("content", "verified");
}
this.initiateModsMarketplace();
this.contentWindow.document.addEventListener(
'ZenCheckForModUpdates',
"ZenCheckForModUpdates",
this.checkForModUpdates.bind(this)
);
}
@@ -30,7 +30,7 @@ export class ZenModsMarketplaceChild extends JSWindowActorChild {
checkForModUpdates(event) {
event.preventDefault();
this.sendAsyncMessage('ZenModsMarketplace:CheckForUpdates');
this.sendAsyncMessage("ZenModsMarketplace:CheckForUpdates");
}
initiateModsMarketplace() {
@@ -41,20 +41,20 @@ export class ZenModsMarketplaceChild extends JSWindowActorChild {
}
get actionButton() {
return this.contentWindow.document.getElementById('install-theme');
return this.contentWindow.document.getElementById("install-theme");
}
get actionButtonUninstall() {
return this.contentWindow.document.getElementById('install-theme-uninstall');
return this.contentWindow.document.getElementById("install-theme-uninstall");
}
async isThemeInstalled(themeId) {
return await this.sendQuery('ZenModsMarketplace:IsModInstalled', { themeId });
return await this.sendQuery("ZenModsMarketplace:IsModInstalled", { themeId });
}
async receiveMessage(message) {
switch (message.name) {
case 'ZenModsMarketplace:ModChanged': {
case "ZenModsMarketplace:ModChanged": {
const modId = message.data.modId;
const actionButton = this.actionButton;
const actionButtonInstalled = this.actionButtonUninstall;
@@ -64,22 +64,22 @@ export class ZenModsMarketplaceChild extends JSWindowActorChild {
actionButtonInstalled.disabled = false;
if (await this.isThemeInstalled(modId)) {
actionButton.classList.add('hidden');
actionButtonInstalled.classList.remove('hidden');
actionButton.classList.add("hidden");
actionButtonInstalled.classList.remove("hidden");
} else {
actionButton.classList.remove('hidden');
actionButtonInstalled.classList.add('hidden');
actionButton.classList.remove("hidden");
actionButtonInstalled.classList.add("hidden");
}
}
break;
}
case 'ZenModsMarketplace:CheckForUpdatesFinished': {
case "ZenModsMarketplace:CheckForUpdatesFinished": {
const updates = message.data.updates;
this.contentWindow.document.dispatchEvent(
new CustomEvent('ZenModsMarketplace:CheckForUpdatesFinished', { detail: { updates } })
new CustomEvent("ZenModsMarketplace:CheckForUpdatesFinished", { detail: { updates } })
);
break;
@@ -89,38 +89,38 @@ export class ZenModsMarketplaceChild extends JSWindowActorChild {
injectMarketplaceAPI() {
Cu.exportFunction(this.handleModInstallationEvent.bind(this), this.contentWindow, {
defineAs: 'ZenInstallMod',
defineAs: "ZenInstallMod",
});
}
async addButtons() {
const actionButton = this.actionButton;
const actionButtonUninstall = this.actionButtonUninstall;
const errorMessage = this.contentWindow.document.getElementById('install-theme-error');
const errorMessage = this.contentWindow.document.getElementById("install-theme-error");
if (!actionButton || !actionButtonUninstall) {
return;
}
errorMessage.classList.add('hidden');
errorMessage.classList.add("hidden");
const themeId = actionButton.getAttribute('zen-theme-id');
const themeId = actionButton.getAttribute("zen-theme-id");
if (await this.isThemeInstalled(themeId)) {
actionButtonUninstall.classList.remove('hidden');
actionButtonUninstall.classList.remove("hidden");
} else {
actionButton.classList.remove('hidden');
actionButton.classList.remove("hidden");
}
actionButton.addEventListener('click', this.handleModInstallationEvent.bind(this));
actionButtonUninstall.addEventListener('click', this.handleModUninstallEvent.bind(this));
actionButton.addEventListener("click", this.handleModInstallationEvent.bind(this));
actionButtonUninstall.addEventListener("click", this.handleModUninstallEvent.bind(this));
}
async handleModUninstallEvent(event) {
const button = event.target;
button.disabled = true;
const modId = button.getAttribute('zen-theme-id');
const modId = button.getAttribute("zen-theme-id");
this.sendAsyncMessage('ZenModsMarketplace:UninstallMod', { modId });
this.sendAsyncMessage("ZenModsMarketplace:UninstallMod", { modId });
}
async handleModInstallationEvent(event) {
@@ -131,12 +131,12 @@ export class ZenModsMarketplaceChild extends JSWindowActorChild {
const button = event.target;
button.disabled = true;
modId = button.getAttribute('zen-theme-id');
modId = button.getAttribute("zen-theme-id");
} else {
// Backwards compatibility is... Interesting
modId = event.themeId ?? event.modId ?? event.id;
}
this.sendAsyncMessage('ZenModsMarketplace:InstallMod', { modId });
this.sendAsyncMessage("ZenModsMarketplace:InstallMod", { modId });
}
}

View File

@@ -13,11 +13,11 @@ export class ZenModsMarketplaceParent extends JSWindowActorParent {
async receiveMessage(message) {
switch (message.name) {
case 'ZenModsMarketplace:InstallMod': {
case "ZenModsMarketplace:InstallMod": {
const modId = message.data.modId;
const mod = await this.modsManager.requestMod(modId);
console.log(`[ZenModsMarketplaceParent]: Installing mod ${mod.id}`);
console.warn(`[ZenModsMarketplaceParent]: Installing mod ${mod.id}`);
mod.enabled = true;
@@ -29,9 +29,9 @@ export class ZenModsMarketplaceParent extends JSWindowActorParent {
break;
}
case 'ZenModsMarketplace:UninstallMod': {
case "ZenModsMarketplace:UninstallMod": {
const modId = message.data.modId;
console.log(`[ZenModsMarketplaceParent]: Uninstalling mod ${modId}`);
console.warn(`[ZenModsMarketplaceParent]: Uninstalling mod ${modId}`);
const mods = await this.modsManager.getMods();
@@ -44,22 +44,23 @@ export class ZenModsMarketplaceParent extends JSWindowActorParent {
break;
}
case 'ZenModsMarketplace:CheckForUpdates': {
case "ZenModsMarketplace:CheckForUpdates": {
const updates = await this.modsManager.checkForModsUpdates();
this.sendAsyncMessage('ZenModsMarketplace:CheckForUpdatesFinished', { updates });
this.sendAsyncMessage("ZenModsMarketplace:CheckForUpdatesFinished", { updates });
break;
}
case 'ZenModsMarketplace:IsModInstalled': {
case "ZenModsMarketplace:IsModInstalled": {
const themeId = message.data.themeId;
const themes = await this.modsManager.getMods();
return Boolean(themes?.[themeId]);
}
}
return undefined;
}
async updateChildProcesses(modId) {
this.sendAsyncMessage('ZenModsMarketplace:ModChanged', { modId });
this.sendAsyncMessage("ZenModsMarketplace:ModChanged", { modId });
}
}

View File

@@ -2,37 +2,41 @@
* 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 { JSONFile } from 'resource://gre/modules/JSONFile.sys.mjs';
import { XPCOMUtils } from 'resource://gre/modules/XPCOMUtils.sys.mjs';
import { JSONFile } from "resource://gre/modules/JSONFile.sys.mjs";
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
PrivateBrowsingUtils: 'resource://gre/modules/PrivateBrowsingUtils.sys.mjs',
BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.sys.mjs',
TabGroupState: 'resource:///modules/sessionstore/TabGroupState.sys.mjs',
SessionStore: 'resource:///modules/sessionstore/SessionStore.sys.mjs',
SessionSaver: 'resource:///modules/sessionstore/SessionSaver.sys.mjs',
setTimeout: 'resource://gre/modules/Timer.sys.mjs',
gWindowSyncEnabled: 'resource:///modules/zen/ZenWindowSync.sys.mjs',
DeferredTask: 'resource://gre/modules/DeferredTask.sys.mjs',
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
// eslint-disable-next-line mozilla/valid-lazy
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
// eslint-disable-next-line mozilla/valid-lazy
TabGroupState: "resource:///modules/sessionstore/TabGroupState.sys.mjs",
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
// eslint-disable-next-line mozilla/valid-lazy
SessionSaver: "resource:///modules/sessionstore/SessionSaver.sys.mjs",
// eslint-disable-next-line mozilla/valid-lazy
setTimeout: "resource://gre/modules/Timer.sys.mjs",
gWindowSyncEnabled: "resource:///modules/zen/ZenWindowSync.sys.mjs",
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
});
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'gShouldLog', 'zen.session-store.log', true);
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gShouldLog", "zen.session-store.log", true);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'gMaxSessionBackups',
'zen.session-store.max-backups',
"gMaxSessionBackups",
"zen.session-store.max-backups",
10
);
// Note that changing this hidden pref will make the previous session file
// unused, causing a new session file to be created on next write.
const SHOULD_COMPRESS_FILE = Services.prefs.getBoolPref('zen.session-store.compress-file', true);
const SHOULD_BACKUP_FILE = Services.prefs.getBoolPref('zen.session-store.backup-file', true);
const SHOULD_COMPRESS_FILE = Services.prefs.getBoolPref("zen.session-store.compress-file", true);
const SHOULD_BACKUP_FILE = Services.prefs.getBoolPref("zen.session-store.backup-file", true);
const FILE_NAME = SHOULD_COMPRESS_FILE ? 'zen-sessions.jsonlz4' : 'zen-sessions.json';
const MIGRATION_PREF = 'zen.ui.migration.session-manager-restore';
const FILE_NAME = SHOULD_COMPRESS_FILE ? "zen-sessions.jsonlz4" : "zen-sessions.json";
const MIGRATION_PREF = "zen.ui.migration.session-manager-restore";
// 'browser.startup.page' preference value to resume the previous session.
const BROWSER_STARTUP_RESUME_SESSION = 3;
@@ -61,11 +65,13 @@ class nsZenSidebarObject {
export class nsZenSessionManager {
/**
* The JSON file instance used to read/write session data.
*
* @type {JSONFile}
*/
#file = null;
/**
* The sidebar object holding tabs, groups, folders and split view data.
*
* @type {nsZenSidebarObject}
*/
#sidebarObject = new nsZenSidebarObject();
@@ -76,8 +82,8 @@ export class nsZenSessionManager {
// Called from SessionComponents.manifest on app-startup
init() {
this.log('Initializing session manager');
let profileDir = Services.dirsvc.get('ProfD', Ci.nsIFile).path;
this.log("Initializing session manager");
let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
let backupFile = null;
if (SHOULD_BACKUP_FILE) {
backupFile = PathUtils.join(this.#backupFolderPath, FILE_NAME);
@@ -85,7 +91,7 @@ export class nsZenSessionManager {
let filePath = PathUtils.join(profileDir, FILE_NAME);
this.#file = new JSONFile({
path: filePath,
compression: SHOULD_COMPRESS_FILE ? 'lz4' : undefined,
compression: SHOULD_COMPRESS_FILE ? "lz4" : undefined,
backupFile,
});
this.#deferredBackupTask = new lazy.DeferredTask(async () => {
@@ -95,13 +101,13 @@ export class nsZenSessionManager {
log(...args) {
if (lazy.gShouldLog) {
console.info('ZenSessionManager:', ...args);
console.warn("ZenSessionManager:", ...args);
}
}
get #backupFolderPath() {
let profileDir = Services.dirsvc.get('ProfD', Ci.nsIFile).path;
return PathUtils.join(profileDir, 'zen-sessions-backup');
let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile).path;
return PathUtils.join(profileDir, "zen-sessions-backup");
}
/**
@@ -112,24 +118,24 @@ export class nsZenSessionManager {
async #getDataFromDBForMigration() {
try {
const { PlacesUtils } = ChromeUtils.importESModule(
'resource://gre/modules/PlacesUtils.sys.mjs'
"resource://gre/modules/PlacesUtils.sys.mjs"
);
const db = await PlacesUtils.promiseDBConnection();
let data = {};
let rows = await db.execute('SELECT * FROM zen_workspaces ORDER BY created_at ASC');
let rows = await db.execute("SELECT * FROM zen_workspaces ORDER BY created_at ASC");
data.spaces = rows.map((row) => ({
uuid: row.getResultByName('uuid'),
name: row.getResultByName('name'),
icon: row.getResultByName('icon'),
containerTabId: row.getResultByName('container_id') ?? 0,
position: row.getResultByName('position'),
theme: row.getResultByName('theme_type')
uuid: row.getResultByName("uuid"),
name: row.getResultByName("name"),
icon: row.getResultByName("icon"),
containerTabId: row.getResultByName("container_id") ?? 0,
position: row.getResultByName("position"),
theme: row.getResultByName("theme_type")
? {
type: row.getResultByName('theme_type'),
gradientColors: JSON.parse(row.getResultByName('theme_colors')),
opacity: row.getResultByName('theme_opacity'),
rotation: row.getResultByName('theme_rotation'),
texture: row.getResultByName('theme_texture'),
type: row.getResultByName("theme_type"),
gradientColors: JSON.parse(row.getResultByName("theme_colors")),
opacity: row.getResultByName("theme_opacity"),
rotation: row.getResultByName("theme_rotation"),
texture: row.getResultByName("theme_texture"),
}
: null,
}));
@@ -142,11 +148,12 @@ export class nsZenSessionManager {
/**
* Reads the session file and populates the sidebar object.
* This should be only called once at startup.
*
* @see SessionFileInternal.read
*/
async readFile() {
try {
this.log('Reading Zen session file from disk');
this.log("Reading Zen session file from disk");
let promises = [];
promises.push(this.#file.load());
if (!Services.prefs.getBoolPref(MIGRATION_PREF, false)) {
@@ -154,7 +161,7 @@ export class nsZenSessionManager {
}
await Promise.all(promises);
} catch (e) {
console.error('ZenSessionManager: Failed to read session file', e);
console.error("ZenSessionManager: Failed to read session file", e);
}
this.#sidebar = this.#file.data || {};
}
@@ -163,18 +170,20 @@ export class nsZenSessionManager {
* Called when the session file is read. Restores the sidebar data
* into all windows.
*
* @param initialState
* @param {object} initialState
* The initial session state read from the session file.
*/
onFileRead(initialState) {
if (!lazy.gWindowSyncEnabled) return;
if (!lazy.gWindowSyncEnabled) {
return;
}
// For the first time after migration, we restore the tabs
// That where going to be restored by SessionStore. The sidebar
// object will always be empty after migration because we haven't
// gotten the opportunity to save the session yet.
if (!Services.prefs.getBoolPref(MIGRATION_PREF, false)) {
Services.prefs.setBoolPref(MIGRATION_PREF, true);
this.log('Restoring tabs from Places DB after migration');
this.log("Restoring tabs from Places DB after migration");
this.#sidebar = {
...this.#sidebar,
spaces: this._migrationData?.spaces || [],
@@ -189,7 +198,7 @@ export class nsZenSessionManager {
);
if (normalClosedWindow) {
initialState.windows = [Cu.cloneInto(normalClosedWindow, {})];
this.log('Restoring tabs from last closed normal window');
this.log("Restoring tabs from last closed normal window");
}
}
for (const winData of initialState?.windows || []) {
@@ -207,17 +216,17 @@ export class nsZenSessionManager {
// This would happen on first run after having a single private window
// open when quitting the app, for example.
if (!initialState?.windows?.length) {
this.log('No windows found in initial state, creating an empty one');
this.log("No windows found in initial state, creating an empty one");
initialState ||= {};
initialState.windows = [{}];
}
// When we don't have browser.startup.page set to resume session,
// we only want to restore the pinned tabs into the new windows.
const shouldRestoreOnlyPinned =
Services.prefs.getIntPref('browser.startup.page', 1) !== BROWSER_STARTUP_RESUME_SESSION ||
Services.prefs.getIntPref("browser.startup.page", 1) !== BROWSER_STARTUP_RESUME_SESSION ||
lazy.PrivateBrowsingUtils.permanentPrivateBrowsing;
if (shouldRestoreOnlyPinned && this.#sidebar?.tabs) {
this.log('Restoring only pinned tabs into windows');
this.log("Restoring only pinned tabs into windows");
const sidebar = this.#sidebar;
sidebar.tabs = (sidebar.tabs || []).filter((tab) => tab.pinned);
this.#sidebar = sidebar;
@@ -226,7 +235,7 @@ export class nsZenSessionManager {
// 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',
"zen.session-store.restore-unsynced-windows",
true
);
this.log(`Restoring Zen session data into ${initialState.windows?.length || 0} windows`);
@@ -235,7 +244,7 @@ export class nsZenSessionManager {
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');
this.log("Skipping restore of unsynced window");
delete initialState.windows[i];
}
continue;
@@ -255,7 +264,7 @@ export class nsZenSessionManager {
/**
* Saves the current session state. Collects data and writes to disk.
*
* @param state The current session state.
* @param {object} state The current session state.
*/
saveState(state) {
if (!state?.windows?.length || !lazy.gWindowSyncEnabled) {
@@ -306,8 +315,8 @@ export class nsZenSessionManager {
});
const todayFileName = `zen-sessions-${today.getFullYear()}-${(today.getMonth() + 1)
.toString()
.padStart(2, '0')}-${today.getDate().toString().padStart(2, '0')}.json${
SHOULD_COMPRESS_FILE ? 'lz4' : ''
.padStart(2, "0")}-${today.getDate().toString().padStart(2, "0")}.json${
SHOULD_COMPRESS_FILE ? "lz4" : ""
}`;
const todayFilePath = PathUtils.join(backupFolder, todayFileName);
const sessionFilePath = this.#file.path;
@@ -317,14 +326,14 @@ export class nsZenSessionManager {
// number of backups allowed, and delete the oldest ones
// if needed.
let files = await IOUtils.getChildren(backupFolder);
files = files.filter((file) => file.startsWith('zen-sessions-')).sort();
files = files.filter((file) => file.startsWith("zen-sessions-")).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);
}
} catch (e) {
console.error('ZenSessionManager: Failed to create session file backups', e);
console.error("ZenSessionManager: Failed to create session file backups", e);
}
}
@@ -332,8 +341,8 @@ export class nsZenSessionManager {
* Saves the session data for a closed window if it meets the criteria.
* See SessionStoreInternal.maybeSaveClosedWindow for more details.
*
* @param aWinData - The window data object to save.
* @param isLastWindow - Whether this is the last saveable window.
* @param {object} aWinData - The window data object to save.
* @param {boolean} isLastWindow - Whether this is the last saveable window.
*/
maybeSaveClosedWindow(aWinData, isLastWindow) {
// We only want to save the *last* normal window that is closed.
@@ -342,14 +351,14 @@ export class nsZenSessionManager {
if (aWinData.isPopup || aWinData.isTaskbarTab || aWinData.isZenUnsynced || !isLastWindow) {
return;
}
this.log('Saving closed window session data into Zen session store');
this.log("Saving closed window session data into Zen session store");
this.saveState({ windows: [aWinData] });
}
/**
* Collects session data for a given window.
*
* @param state
* @param {object} state
* The current session state.
*/
#collectWindowData(state) {
@@ -374,8 +383,8 @@ export class nsZenSessionManager {
/**
* Collects session data for all tabs in a given window.
*
* @param sidebarData The sidebar data object to populate.
* @param state The current session state.
* @param {object} sidebarData The sidebar data object to populate.
* @param {object} state The current session state.
*/
#collectTabsData(sidebarData, state) {
const tabIdRelationMap = new Map();
@@ -404,7 +413,7 @@ export class nsZenSessionManager {
* We do this in order to make sure all new window objects
* have the same sidebar data.
*
* @param aWindowData The window data object to restore into.
* @param {object} aWindowData The window data object to restore into.
*/
#restoreWindowData(aWindowData) {
const sidebar = this.#sidebar;
@@ -422,18 +431,18 @@ export class nsZenSessionManager {
* Restores a new window with Zen session data. This should be called
* not at startup, but when a new window is opened by the user.
*
* @param aWindow
* @param {Window} aWindow
* The window to restore.
* @param SessionStoreInternal
* @param {object} SessionStoreInternal
* The SessionStore module instance.
* @param fromClosedWindow
* @param {boolean} fromClosedWindow
* Whether this new window is being restored from a closed window.
*/
restoreNewWindow(aWindow, SessionStoreInternal, fromClosedWindow = false) {
if (aWindow.gZenWorkspaces?.privateWindowOrDisabled || !lazy.gWindowSyncEnabled) {
return;
}
this.log('Restoring new window with Zen session data');
this.log("Restoring new window with Zen session data");
const state = lazy.SessionStore.getCurrentState(true);
const windows = (state.windows || []).filter(
(win) => !win.isPrivate && !win.isPopup && !win.isTaskbarTab && !win.isZenUnsynced
@@ -444,7 +453,7 @@ export class nsZenSessionManager {
// 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.log("Restoring sidebar data into new window");
this.#restoreWindowData(newWindow);
}
newWindow.tabs = this.#filterUnusedTabs(newWindow.tabs || []);
@@ -477,11 +486,11 @@ export class nsZenSessionManager {
* Called when a new empty session is created. For example,
* when creating a new profile or when the user installed it for
* the first time.
* @param {*} aWindow
* @returns
*
* @param {Window} aWindow
*/
onNewEmptySession(aWindow) {
this.log('Restoring empty session with Zen session data');
this.log("Restoring empty session with Zen session data");
aWindow.gZenWorkspaces.restoreWorkspacesFromSessionStore({
spaces: this.#sidebar.spaces || [],
});

View File

@@ -2,47 +2,48 @@
* 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 { XPCOMUtils } from 'resource://gre/modules/XPCOMUtils.sys.mjs';
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
BrowserWindowTracker: 'resource:///modules/BrowserWindowTracker.sys.mjs',
SessionStore: 'resource:///modules/sessionstore/SessionStore.sys.mjs',
TabStateFlusher: 'resource:///modules/sessionstore/TabStateFlusher.sys.mjs',
ZenSessionStore: 'resource:///modules/zen/ZenSessionManager.sys.mjs',
TabStateCache: 'resource:///modules/sessionstore/TabStateCache.sys.mjs',
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs",
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.sys.mjs",
// eslint-disable-next-line mozilla/valid-lazy
ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs",
TabStateCache: "resource:///modules/sessionstore/TabStateCache.sys.mjs",
});
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'gWindowSyncEnabled', 'zen.window-sync.enabled');
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'gShouldLog', 'zen.window-sync.log', true);
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gWindowSyncEnabled", "zen.window-sync.enabled");
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gShouldLog", "zen.window-sync.log", true);
const OBSERVING = ['browser-window-before-show'];
const INSTANT_EVENTS = ['SSWindowClosing'];
const OBSERVING = ["browser-window-before-show"];
const INSTANT_EVENTS = ["SSWindowClosing"];
const EVENTS = [
'TabOpen',
'TabClose',
"TabOpen",
"TabClose",
'ZenTabIconChanged',
'ZenTabLabelChanged',
"ZenTabIconChanged",
"ZenTabLabelChanged",
'TabMove',
'TabPinned',
'TabUnpinned',
'TabAddedToEssentials',
'TabRemovedFromEssentials',
"TabMove",
"TabPinned",
"TabUnpinned",
"TabAddedToEssentials",
"TabRemovedFromEssentials",
'TabGroupUpdate',
'TabGroupCreate',
'TabGroupRemoved',
'TabGroupMoved',
"TabGroupUpdate",
"TabGroupCreate",
"TabGroupRemoved",
"TabGroupMoved",
'ZenTabRemovedFromSplit',
'ZenSplitViewTabsSplit',
"ZenTabRemovedFromSplit",
"ZenSplitViewTabsSplit",
'TabSelect',
"TabSelect",
'focus',
"focus",
...INSTANT_EVENTS,
];
@@ -124,7 +125,7 @@ class nsZenWindowSync {
log(...args) {
if (lazy.gShouldLog) {
console.info('ZenWindowSync:', ...args);
console.warn("ZenWindowSync:", ...args);
}
}
@@ -144,21 +145,21 @@ class nsZenWindowSync {
// to avoid confusing the old private window behavior.
let forcedSync = !aWindow.gZenWorkspaces?.privateWindowOrDisabled;
let hasUnsyncedArg = false;
if (aWindow._zenStartupSyncFlag === 'synced') {
if (aWindow._zenStartupSyncFlag === "synced") {
forcedSync = true;
} else if (aWindow._zenStartupSyncFlag === 'unsynced') {
} else if (aWindow._zenStartupSyncFlag === "unsynced") {
hasUnsyncedArg = true;
}
delete aWindow._zenStartupSyncFlag;
if (
!forcedSync &&
(hasUnsyncedArg ||
(typeof aWindow.arguments[0] === 'string' &&
(typeof aWindow.arguments[0] === "string" &&
aWindow.arguments.length > 1 &&
[...this.#browserWindows].length > 0))
!![...this.#browserWindows].length))
) {
this.log('Not syncing new window due to unsynced argument or existing synced windows');
aWindow.document.documentElement.setAttribute('zen-unsynced-window', 'true');
this.log("Not syncing new window due to unsynced argument or existing synced windows");
aWindow.document.documentElement.setAttribute("zen-unsynced-window", "true");
return;
}
aWindow.gZenWindowSync = this;
@@ -169,8 +170,6 @@ class nsZenWindowSync {
/**
* Called when the session store has finished initializing for a window.
*
* @param {Window} aWindow - The browser window that has initialized session store.
*/
async #onSessionStoreInitialized() {
// For every tab we have in where there's no sync ID, we need to
@@ -226,6 +225,7 @@ class nsZenWindowSync {
/**
* Runs a callback function on all browser windows except the specified one.
* This version supports asynchronous callbacks.
*
* @see #runOnAllWindows - Make sure functionality is the same.
* @param {Window} aWindow - The browser window to exclude.
* @param {Function} aCallback - The asynchronous callback function to run on each window.
@@ -240,7 +240,7 @@ class nsZenWindowSync {
observe(aSubject, aTopic) {
switch (aTopic) {
case 'browser-window-before-show': {
case "browser-window-before-show": {
this.#onWindowBeforeShow(aSubject);
break;
}
@@ -257,7 +257,8 @@ class nsZenWindowSync {
return;
}
if (INSTANT_EVENTS.includes(aEvent.type)) {
return this.#handleNextEvent(aEvent);
this.#handleNextEvent(aEvent);
return;
}
if (this.#eventHandlingContext.window && this.#eventHandlingContext.window !== window) {
// We're already handling an event for another window.
@@ -284,6 +285,7 @@ class nsZenWindowSync {
/**
* Adds a sync handler for a specific event type.
*
* @param {Function} aHandler - The sync handler function to add.
*/
addSyncHandler(aHandler) {
@@ -295,6 +297,7 @@ class nsZenWindowSync {
/**
* Removes a sync handler for a specific event type.
*
* @param {Function} aHandler - The sync handler function to remove.
*/
removeSyncHandler(aHandler) {
@@ -309,7 +312,7 @@ class nsZenWindowSync {
#handleNextEvent(aEvent) {
const handler = `on_${aEvent.type}`;
try {
if (typeof this[handler] === 'function') {
if (typeof this[handler] === "function") {
let promise = this[handler](aEvent) || Promise.resolve();
promise.then(() => {
for (let syncHandler of this.#syncHandlers) {
@@ -321,9 +324,8 @@ class nsZenWindowSync {
}
});
return promise;
} else {
throw new Error(`No handler for event type: ${aEvent.type}`);
}
throw new Error(`No handler for event type: ${aEvent.type}`);
} catch (e) {
return Promise.reject(e);
}
@@ -345,6 +347,7 @@ class nsZenWindowSync {
/**
* Synchronizes a specific attribute from the original item to the target item.
*
* @param {MozTabbrowserTab|MozTabbrowserTabGroup} aOriginalItem - The original item to copy from.
* @param {MozTabbrowserTab|MozTabbrowserTabGroup} aTargetItem - The target item to copy to.
* @param {string} aAttributeName - The name of the attribute to synchronize.
@@ -360,8 +363,8 @@ class nsZenWindowSync {
/**
* Synchronizes the icon and label of the target tab with the original tab.
*
* @param {Object} aOriginalTab - The original tab to copy from.
* @param {Object} aTargetTab - The target tab to copy to.
* @param {object} aOriginalItem - The original item to copy from.
* @param {object} aTargetItem - The target item to copy to.
* @param {Window} aWindow - The window containing the tabs.
* @param {number} flags - The sync flags indicating what to synchronize.
*/
@@ -375,7 +378,7 @@ class nsZenWindowSync {
if (gBrowser.isTab(aOriginalItem)) {
gBrowser.setIcon(
aTargetItem,
aOriginalItem.getAttribute('image') || gBrowser.getIcon(aOriginalItem)
aOriginalItem.getAttribute("image") || gBrowser.getIcon(aOriginalItem)
);
} else if (aOriginalItem.isZenFolder) {
// Icons are a zen-only feature for tab groups.
@@ -392,8 +395,8 @@ class nsZenWindowSync {
aTargetItem.label = aOriginalItem.label;
}
}
if (flags & SYNC_FLAG_MOVE && !aTargetItem.hasAttribute('zen-empty-tab')) {
this.#maybeSyncAttributeChange(aOriginalItem, aTargetItem, 'zen-workspace-id');
if (flags & SYNC_FLAG_MOVE && !aTargetItem.hasAttribute("zen-empty-tab")) {
this.#maybeSyncAttributeChange(aOriginalItem, aTargetItem, "zen-workspace-id");
this.#syncItemPosition(aOriginalItem, aTargetItem, aWindow);
}
if (gBrowser.isTab(aTargetItem)) {
@@ -410,8 +413,8 @@ class nsZenWindowSync {
*/
#syncItemPosition(aOriginalItem, aTargetItem, aWindow) {
const { gBrowser, gZenPinnedTabManager } = aWindow;
const originalIsEssential = aOriginalItem.hasAttribute('zen-essential');
const targetIsEssential = aTargetItem.hasAttribute('zen-essential');
const originalIsEssential = aOriginalItem.hasAttribute("zen-essential");
const targetIsEssential = aTargetItem.hasAttribute("zen-essential");
const originalIsPinned = aOriginalItem.pinned;
const targetIsPinned = aTargetItem.pinned;
@@ -448,7 +451,7 @@ class nsZenWindowSync {
* @param {MozTabbrowserTab|MozTabbrowserTabGroup} aOriginalItem - The original item to match.
* @param {MozTabbrowserTab|MozTabbrowserTabGroup} aTargetItem - The target item to move.
* @param {Window} aWindow - The window containing the items.
* @param {Object} options - Additional options for moving the item.
* @param {object} options - Additional options for moving the item.
* @param {boolean} options.isEssential - Indicates if the item is essential.
* @param {boolean} options.isPinned - Indicates if the item is pinned.
*/
@@ -458,15 +461,15 @@ class nsZenWindowSync {
let isFirstTab = true;
if (gBrowser.isTabGroup(originalSibling) || gBrowser.isTab(originalSibling)) {
isFirstTab =
!originalSibling.hasAttribute('id') || originalSibling.hasAttribute('zen-empty-tab');
!originalSibling.hasAttribute("id") || originalSibling.hasAttribute("zen-empty-tab");
}
gBrowser.zenHandleTabMove(aTargetItem, () => {
if (isFirstTab) {
let container;
const parentGroup = aOriginalItem.group;
if (parentGroup?.hasAttribute('id')) {
container = this.getItemFromWindow(aWindow, parentGroup.getAttribute('id'));
if (parentGroup?.hasAttribute("id")) {
container = this.getItemFromWindow(aWindow, parentGroup.getAttribute("id"));
if (container) {
if (container?.tabs?.length) {
// First tab in folders is the empty tab placeholder.
@@ -481,7 +484,7 @@ class nsZenWindowSync {
container = gZenWorkspaces.getEssentialsSection(aTargetItem);
} else {
const workspaceId =
aTargetItem.getAttribute('zen-workspace-id') ||
aTargetItem.getAttribute("zen-workspace-id") ||
aOriginalItem.ownerGlobal.gZenWorkspaces.activeWorkspace;
const workspaceElement = gZenWorkspaces.workspaceElement(workspaceId);
container = isPinned
@@ -522,8 +525,8 @@ class nsZenWindowSync {
/**
* Swaps the browser docshells between two tabs.
*
* @param {Object} aOurTab - The tab in the current window.
* @param {Object} aOtherTab - The tab in the other window.
* @param {object} aOurTab - The tab in the current window.
* @param {object} aOtherTab - The tab in the other window.
*/
async #swapBrowserDocShellsAsync(aOurTab, aOtherTab) {
lazy.TabStateFlusher.flush(aOtherTab.linkedBrowser);
@@ -535,7 +538,7 @@ class nsZenWindowSync {
/**
* Restores the tab progress listener for a given tab.
*
* @param {Object} aTab - The tab to restore the progress listener for.
* @param {object} aTab - The tab to restore the progress listener for.
* @param {Function} callback - The callback function to execute while the listener is removed.
* @param {boolean} onClose - Indicates if the swap is done during a tab close operation.
*/
@@ -573,8 +576,9 @@ class nsZenWindowSync {
/**
* Swaps the browser docshells between two tabs.
*
* @param {Object} aOurTab - The tab in the current window.
* @param {Object} aOtherTab - The tab in the other window.
* @param {object} aOurTab - The tab in the current window.
* @param {object} aOtherTab - The tab in the other window.
* @param {object} options - Options object.
* @param {boolean} options.focus - Indicates if the tab should be focused after the swap.
* @param {boolean} options.onClose - Indicates if the swap is done during a tab close operation.
*/
@@ -601,8 +605,8 @@ class nsZenWindowSync {
// and will stay in a 'busy' state forever.
// To avoid this, we manually check if the other tab is still busy after the swap,
// and if not, we remove the busy attribute from our tab.
if (!aOtherTab.hasAttribute('busy')) {
aOurTab.removeAttribute('busy');
if (!aOtherTab.hasAttribute("busy")) {
aOurTab.removeAttribute("busy");
}
// Load about:blank if by any chance we loaded the previous tab's URL.
// TODO: We should maybe start using a singular about:blank preloaded view
@@ -612,11 +616,11 @@ class nsZenWindowSync {
// around this limitation somehow.
if (
!onClose &&
(aOtherTab.linkedBrowser?.currentURI.spec !== 'about:blank' ||
aOtherTab.hasAttribute('busy'))
(aOtherTab.linkedBrowser?.currentURI.spec !== "about:blank" ||
aOtherTab.hasAttribute("busy"))
) {
this.log(`Loading about:blank in our tab ${aOtherTab.id} before swap`);
aOtherTab.linkedBrowser.loadURI(Services.io.newURI('about:blank'), {
aOtherTab.linkedBrowser.loadURI(Services.io.newURI("about:blank"), {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY,
});
@@ -624,7 +628,7 @@ class nsZenWindowSync {
},
onClose
);
const kAttributesToRemove = ['muted', 'soundplaying', 'sharing', 'pictureinpicture', 'busy'];
const kAttributesToRemove = ["muted", "soundplaying", "sharing", "pictureinpicture", "busy"];
// swapBrowsersAndCloseOther already takes care of transferring attributes like 'muted',
// but we need to manually remove some attributes from the other tab.
for (let attr of kAttributesToRemove) {
@@ -656,8 +660,8 @@ class nsZenWindowSync {
/**
* Styles the swapped browsers to ensure proper visibility and layout.
*
* @param {Object} aOurTab - The tab in the current window.
* @param {Object} aOtherTab - The tab in the other window.
* @param {object} aOurTab - The tab in the current window.
* @param {object} aOtherTab - The tab in the other window.
* @param {Function|undefined} callback - The callback function to execute after styling.
*/
async #styleSwapedBrowsers(aOurTab, aOtherTab, callback = undefined) {
@@ -681,7 +685,7 @@ class nsZenWindowSync {
resolve(reader.result);
};
reader.onerror = function () {
reject(new Error('Failed to read blob as data URL'));
reject(new Error("Failed to read blob as data URL"));
};
});
@@ -689,25 +693,25 @@ class nsZenWindowSync {
// Run a reflow to ensure the image is rendered before hiding the browser.
void img.getBoundingClientRect();
await loadPromise;
otherBrowser.setAttribute('zen-pseudo-hidden', 'true');
otherBrowser.setAttribute("zen-pseudo-hidden", "true");
callback();
}
this.#maybeRemovePseudoImageForBrowser(ourBrowser);
ourBrowser.removeAttribute('zen-pseudo-hidden');
ourBrowser.removeAttribute("zen-pseudo-hidden");
}
/**
* Create and insert a new pseudo image for a browser element.
*
* @param {Object} aBrowser - The browser element to create the pseudo image for.
* @param {object} aBrowser - The browser element to create the pseudo image for.
* @param {string} aSrc - The source URL of the image.
* @returns {Object} The created pseudo image element.
* @returns {object} The created pseudo image element.
*/
#createPseudoImageForBrowser(aBrowser, aSrc) {
const doc = aBrowser.ownerDocument;
const img = doc.createElement('img');
img.className = 'zen-pseudo-browser-image';
const img = doc.createElement("img");
img.className = "zen-pseudo-browser-image";
aBrowser.after(img);
const loadPromise = new Promise((resolve) => {
img.onload = () => resolve();
@@ -719,10 +723,10 @@ class nsZenWindowSync {
/**
* Removes the pseudo image element for a browser if it exists.
*
* @param {Object} aBrowser - The browser element to remove the pseudo image for.
* @param {object} aBrowser - The browser element to remove the pseudo image for.
*/
#maybeRemovePseudoImageForBrowser(aBrowser) {
const elements = aBrowser.parentNode?.querySelectorAll('.zen-pseudo-browser-image');
const elements = aBrowser.parentNode?.querySelectorAll(".zen-pseudo-browser-image");
if (elements) {
elements.forEach((element) => element.remove());
}
@@ -735,7 +739,7 @@ class nsZenWindowSync {
* @param {Window} aWindow - The window to exclude.
* @param {string} aTabId - The ID of the tab to retrieve.
* @param {Function} filter - A function to filter the tabs.
* @returns {Object|null} The active tab from other windows if found, otherwise null.
* @returns {object | null} The active tab from other windows if found, otherwise null.
*/
#getActiveTabFromOtherWindows(aWindow, aTabId, filter = (tab) => tab?._zenContentsVisible) {
return this.#runOnAllWindows(aWindow, (win) => {
@@ -743,6 +747,7 @@ class nsZenWindowSync {
if (filter(tab)) {
return tab;
}
return undefined;
});
}
@@ -781,7 +786,7 @@ class nsZenWindowSync {
* Handles tab switch or window focus events to synchronize tab contents visibility.
*
* @param {Window} aWindow - The window that triggered the event.
* @param {Object} aPreviousTab - The previously selected tab.
* @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) {
@@ -805,7 +810,7 @@ class nsZenWindowSync {
let promises = [];
for (const browserView of aWindow.gBrowser.selectedBrowsers) {
const selectedTab = aWindow.gBrowser.getTabForBrowser(browserView);
if (selectedTab._zenContentsVisible || selectedTab.hasAttribute('zen-empty-tab')) {
if (selectedTab._zenContentsVisible || selectedTab.hasAttribute("zen-empty-tab")) {
continue;
}
const otherSelectedTab = this.#getActiveTabFromOtherWindows(aWindow, selectedTab.id);
@@ -832,8 +837,8 @@ class nsZenWindowSync {
/**
* Retrieves the tab state for a given tab.
*
* @param {Object} tab - The tab to retrieve the state for.
* @returns {Object} The tab state.
* @param {object} tab - The tab to retrieve the state for.
* @returns {object} The tab state.
*/
#getTabState(tab) {
return JSON.parse(lazy.SessionStore.getTabState(tab));
@@ -844,14 +849,14 @@ class nsZenWindowSync {
/**
* Sets the initial pinned state for a tab across all windows.
*
* @param {Object} aTab - The tab to set the pinned state for.
* @param {object} aTab - The tab to set the pinned state for.
* @returns {Promise} A promise that resolves when the operation is complete.
*/
setPinnedTabState(aTab) {
return lazy.TabStateFlusher.flush(aTab.linkedBrowser).finally(() => {
this.log(`Setting pinned initial state for tab ${aTab.id}`);
const state = this.#getTabState(aTab);
let activeIndex = 'index' in state ? state.index : state.entries.length - 1;
let activeIndex = "index" in state ? state.index : state.entries.length - 1;
activeIndex = Math.min(activeIndex, state.entries.length - 1);
activeIndex = Math.max(activeIndex, 0);
const initialState = {
@@ -869,6 +874,7 @@ class nsZenWindowSync {
/**
* Propagates the workspaces to all windows.
*
* @param {Array} aWorkspaces - The workspaces to propagate.
*/
propagateWorkspacesToAllWindows(aWorkspaces) {
@@ -886,7 +892,7 @@ class nsZenWindowSync {
*/
moveTabsToSyncedWorkspace(aWindow, aWorkspaceId) {
const tabsToMove = aWindow.gZenWorkspaces.allStoredTabs.filter(
(tab) => !tab.hasAttribute('zen-empty-tab')
(tab) => !tab.hasAttribute("zen-empty-tab")
);
const selectedTab = aWindow.gBrowser.selectedTab;
let win = [...this.#browserWindows][0];
@@ -899,7 +905,7 @@ class nsZenWindowSync {
const newTab = gBrowser.adoptTab(tab, { tabIndex: Infinity });
if (!newTab) {
// The adoption failed. Restore "fadein" and don't increase the index.
tab.setAttribute('fadein', 'true');
tab.setAttribute("fadein", "true");
success = false;
continue;
}
@@ -913,7 +919,7 @@ class nsZenWindowSync {
}
};
if (!win) {
this.log('No synced window found, creating a new one');
this.log("No synced window found, creating a new one");
win = aWindow.gBrowser.replaceTabWithWindow(selectedTab, {}, /* zenForceSync = */ true);
win.gZenWorkspaces.promiseInitialized.then(() => {
moveAllTabsToWindow();
@@ -935,11 +941,11 @@ class nsZenWindowSync {
tab._zenContentsVisible = true;
tab.id = this.#newTabSyncId;
this.#runOnAllWindows(window, (win) => {
const newTab = win.gBrowser.addTrustedTab('about:blank', {
const newTab = win.gBrowser.addTrustedTab("about:blank", {
animate: true,
createLazyBrowser: true,
zenWorkspaceId: tab.getAttribute('zen-workspace-id') || '',
_forZenEmptyTab: tab.hasAttribute('zen-empty-tab'),
zenWorkspaceId: tab.getAttribute("zen-workspace-id") || "",
_forZenEmptyTab: tab.hasAttribute("zen-empty-tab"),
});
newTab.id = tab.id;
this.#syncItemWithOriginal(
@@ -957,7 +963,7 @@ class nsZenWindowSync {
// No need to sync icon changes for tabs that aren't active in this window.
return;
}
return this.#delegateGenericSyncEvent(aEvent, SYNC_FLAG_ICON);
this.#delegateGenericSyncEvent(aEvent, SYNC_FLAG_ICON);
}
on_ZenTabLabelChanged(aEvent) {
@@ -965,7 +971,7 @@ class nsZenWindowSync {
// No need to sync label changes for tabs that aren't active in this window.
return;
}
return this.#delegateGenericSyncEvent(aEvent, SYNC_FLAG_LABEL);
this.#delegateGenericSyncEvent(aEvent, SYNC_FLAG_LABEL);
}
on_TabMove(aEvent) {
@@ -1025,7 +1031,7 @@ class nsZenWindowSync {
}
this.#lastFocusedWindow = new WeakRef(window);
this.#lastSelectedTab = new WeakRef(window.gBrowser.selectedTab);
return this.#onTabSwitchOrWindowFocus(window);
this.#onTabSwitchOrWindowFocus(window);
}
on_TabSelect(aEvent) {
@@ -1035,7 +1041,7 @@ class nsZenWindowSync {
}
this.#lastSelectedTab = new WeakRef(tab);
const previousTab = aEvent.detail.previousTab;
return this.#onTabSwitchOrWindowFocus(aEvent.target.ownerGlobal, previousTab);
this.#onTabSwitchOrWindowFocus(aEvent.target.ownerGlobal, previousTab);
}
on_SSWindowClosing(aEvent) {
@@ -1056,7 +1062,7 @@ class nsZenWindowSync {
}
const window = tabGroup.ownerGlobal;
const isFolder = tabGroup.isZenFolder;
const isSplitView = tabGroup.hasAttribute('split-view-group');
const isSplitView = tabGroup.hasAttribute("split-view-group");
if (isSplitView) {
return; // Split view groups are synced via ZenSplitViewTabsSplit event.
}
@@ -1066,7 +1072,7 @@ class nsZenWindowSync {
const existingGroup = this.getItemFromWindow(win, tabGroup.id);
if (existingGroup) {
this.log(
`Attempted to create group ${tabGroup.id} in window ${win}, ` + `but it already exists.`
`Attempted to create group ${tabGroup.id} in window ${win}, but it already exists.`
);
return; // Do not proceed with creation.
}
@@ -1127,8 +1133,8 @@ class nsZenWindowSync {
const otherWindowTabs = tabs
.map((tab) => this.getItemFromWindow(win, tab.id))
.filter(Boolean);
if (otherWindowTabs.length > 0 && win.gZenViewSplitter) {
const group = win.gZenViewSplitter.splitTabs(otherWindowTabs, 'grid', -1);
if (otherWindowTabs.length && win.gZenViewSplitter) {
const group = win.gZenViewSplitter.splitTabs(otherWindowTabs, "grid", -1);
if (group) {
let otherTabGroup = group.tabs[0].group;
otherTabGroup.id = tabGroup.id;
@@ -1141,5 +1147,6 @@ class nsZenWindowSync {
}
}
// eslint-disable-next-line mozilla/valid-lazy
export const gWindowSyncEnabled = lazy.gWindowSyncEnabled;
export const ZenWindowSync = new nsZenWindowSync();

File diff suppressed because it is too large Load Diff

View File

@@ -2,37 +2,39 @@
// 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 { nsZenDOMOperatedFeature } from 'chrome://browser/content/zen-components/ZenCommonUtils.mjs';
import { nsZenDOMOperatedFeature } from "chrome://browser/content/zen-components/ZenCommonUtils.mjs";
const lazy = {};
class ZenPinnedTabsObserver {
static ALL_EVENTS = ['TabPinned', 'TabUnpinned'];
static ALL_EVENTS = ["TabPinned", "TabUnpinned"];
#listeners = [];
constructor() {
// eslint-disable-next-line mozilla/valid-lazy
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'zenPinnedTabRestorePinnedTabsToPinnedUrl',
'zen.pinned-tab-manager.restore-pinned-tabs-to-pinned-url',
"zenPinnedTabRestorePinnedTabsToPinnedUrl",
"zen.pinned-tab-manager.restore-pinned-tabs-to-pinned-url",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'zenPinnedTabCloseShortcutBehavior',
'zen.pinned-tab-manager.close-shortcut-behavior',
'switch'
"zenPinnedTabCloseShortcutBehavior",
"zen.pinned-tab-manager.close-shortcut-behavior",
"switch"
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
'zenTabsEssentialsMax',
'zen.tabs.essentials.max',
"zenTabsEssentialsMax",
"zen.tabs.essentials.max",
12
);
ChromeUtils.defineESModuleGetters(lazy, {
E10SUtils: 'resource://gre/modules/E10SUtils.sys.mjs',
TabStateCache: 'resource:///modules/sessionstore/TabStateCache.sys.mjs',
// eslint-disable-next-line mozilla/valid-lazy
E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
TabStateCache: "resource:///modules/sessionstore/TabStateCache.sys.mjs",
});
this.#listenPinnedTabEvents();
}
@@ -42,7 +44,7 @@ class ZenPinnedTabsObserver {
for (const event of ZenPinnedTabsObserver.ALL_EVENTS) {
window.addEventListener(event, eventListener);
}
window.addEventListener('unload', () => {
window.addEventListener("unload", () => {
for (const event of ZenPinnedTabsObserver.ALL_EVENTS) {
window.removeEventListener(event, eventListener);
}
@@ -65,7 +67,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
if (!this.enabled) {
return;
}
this._canLog = Services.prefs.getBoolPref('zen.pinned-tab-manager.debug', false);
this._canLog = Services.prefs.getBoolPref("zen.pinned-tab-manager.debug", false);
this.observer = new ZenPinnedTabsObserver();
this._initClosePinnedTabShortcut();
this._insertItemsIntoTabContextMenu();
@@ -78,20 +80,21 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
log(message) {
if (this._canLog) {
/* eslint-disable-next-line no-console */
console.log(`[ZenPinnedTabManager] ${message}`);
}
}
onTabIconChanged(tab, url = null) {
tab.dispatchEvent(new CustomEvent('ZenTabIconChanged', { bubbles: true, detail: { tab } }));
if (tab.hasAttribute('zen-essential')) {
tab.dispatchEvent(new CustomEvent("ZenTabIconChanged", { bubbles: true, detail: { tab } }));
if (tab.hasAttribute("zen-essential")) {
this.setEssentialTabIcon(tab, url);
}
}
setEssentialTabIcon(tab, url = null) {
const iconUrl = url ?? tab.getAttribute('image') ?? '';
tab.style.setProperty('--zen-essential-tab-icon', `url(${iconUrl})`);
const iconUrl = url ?? tab.getAttribute("image") ?? "";
tab.style.setProperty("--zen-essential-tab-icon", `url(${iconUrl})`);
}
_onTabResetPinButton(event, tab) {
@@ -108,26 +111,28 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
_onPinnedTabEvent(action, event) {
if (!this.enabled) return;
if (!this.enabled) {
return;
}
const tab = event.target;
if (this._ignoreNextTabPinnedEvent) {
delete this._ignoreNextTabPinnedEvent;
return;
}
switch (action) {
case 'TabPinned':
case "TabPinned":
tab._zenClickEventListener = this._zenClickEventListener;
tab.addEventListener('click', tab._zenClickEventListener);
tab.addEventListener("click", tab._zenClickEventListener);
break;
// [Fall through]
case 'TabUnpinned':
case "TabUnpinned":
if (tab._zenClickEventListener) {
tab.removeEventListener('click', tab._zenClickEventListener);
tab.removeEventListener("click", tab._zenClickEventListener);
delete tab._zenClickEventListener;
}
break;
default:
console.warn('ZenPinnedTabManager: Unhandled tab event', action);
console.warn("ZenPinnedTabManager: Unhandled tab event", action);
break;
}
}
@@ -137,10 +142,10 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
async _onTabClick(e) {
const tab = e.target?.closest('tab');
const tab = e.target?.closest("tab");
if (e.button === 1 && tab) {
await this.onCloseTabShortcut(e, tab, {
closeIfPending: Services.prefs.getBoolPref('zen.pinned-tab-manager.wheel-close-if-pending'),
closeIfPending: Services.prefs.getBoolPref("zen.pinned-tab-manager.wheel-close-if-pending"),
});
}
}
@@ -165,17 +170,18 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
window.gZenWindowSync.setPinnedTabState(tab);
this.resetPinChangedUrl(tab);
gZenUIManager.showToast('zen-pinned-tab-replaced');
gZenUIManager.showToast("zen-pinned-tab-replaced");
}
_initClosePinnedTabShortcut() {
let cmdClose = document.getElementById('cmd_close');
let cmdClose = document.getElementById("cmd_close");
if (cmdClose) {
cmdClose.addEventListener('command', this.onCloseTabShortcut.bind(this));
cmdClose.addEventListener("command", this.onCloseTabShortcut.bind(this));
}
}
// eslint-disable-next-line complexity
async onCloseTabShortcut(
event,
selectedTab = gBrowser.selectedTab,
@@ -193,7 +199,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
...new Set(
tabs
.flatMap((tab) => {
if (tab.group?.hasAttribute('split-view-group')) {
if (tab.group?.hasAttribute("split-view-group")) {
return tab.group.tabs;
}
return tab;
@@ -211,28 +217,28 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
event.stopPropagation();
event.preventDefault();
if (noClose && behavior === 'close') {
behavior = 'unload-switch';
if (noClose && behavior === "close") {
behavior = "unload-switch";
}
if (alwaysUnload && ['close', 'reset', 'switch', 'reset-switch'].includes(behavior)) {
behavior = behavior.contains('reset') ? 'reset-unload-switch' : 'unload-switch';
if (alwaysUnload && ["close", "reset", "switch", "reset-switch"].includes(behavior)) {
behavior = behavior.contains("reset") ? "reset-unload-switch" : "unload-switch";
}
switch (behavior) {
case 'close': {
case "close": {
for (const tab of pinnedTabs) {
gBrowser.removeTab(tab, { animate: true });
}
break;
}
case 'reset-unload-switch':
case 'unload-switch':
case 'reset-switch':
case 'switch':
if (behavior.includes('unload')) {
case "reset-unload-switch":
case "unload-switch":
case "reset-switch":
case "switch":
if (behavior.includes("unload")) {
for (const tab of pinnedTabs) {
if (tab.hasAttribute('glance-id')) {
if (tab.hasAttribute("glance-id")) {
// We have a glance tab inside the tab we are trying to unload,
// before we used to just ignore it but now we need to fully close
// it as well.
@@ -243,22 +249,22 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
hasRan = true;
resolve();
};
window.addEventListener('GlanceClose', onGlanceClose, { once: true });
window.addEventListener("GlanceClose", onGlanceClose, { once: true });
// Set a timeout to resolve the promise if the event doesn't fire.
// We do this to prevent any future issues where glance woudnt close such as
// glance requering to ask for permit unload.
setTimeout(() => {
if (!hasRan) {
console.warn('GlanceClose event did not fire within 3 seconds');
console.warn("GlanceClose event did not fire within 3 seconds");
resolve();
}
}, 3000);
});
return;
}
const isSpltView = tab.group?.hasAttribute('split-view-group');
const isSpltView = tab.group?.hasAttribute("split-view-group");
const group = isSpltView ? tab.group.group : tab.group;
if (!folderToUnload && tab.hasAttribute('folder-active')) {
if (!folderToUnload && tab.hasAttribute("folder-active")) {
await gZenFolders.animateUnload(group, tab);
}
}
@@ -266,37 +272,37 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
await gZenFolders.animateUnloadAll(folderToUnload);
}
const allAreUnloaded = pinnedTabs.every(
(tab) => tab.hasAttribute('pending') && !tab.hasAttribute('zen-essential')
(tab) => tab.hasAttribute("pending") && !tab.hasAttribute("zen-essential")
);
for (const tab of pinnedTabs) {
for (const tabItem of pinnedTabs) {
if (allAreUnloaded && closeIfPending) {
return await this.onCloseTabShortcut(event, tab, { behavior: 'close' });
await this.onCloseTabShortcut(event, tabItem, { behavior: "close" });
return;
}
}
await gBrowser.explicitUnloadTabs(pinnedTabs);
for (const tab of pinnedTabs) {
tab.removeAttribute('discarded');
tab.removeAttribute("discarded");
}
}
if (selectedTabs.length) {
this._handleTabSwitch(selectedTabs[0]);
}
if (behavior.includes('reset')) {
if (behavior.includes("reset")) {
for (const tab of pinnedTabs) {
this._resetTabToStoredState(tab);
}
}
break;
case 'reset':
case "reset":
for (const tab of pinnedTabs) {
this._resetTabToStoredState(tab);
}
break;
default:
return;
}
} catch (ex) {
console.error('Error handling close tab shortcut for pinned tab:', ex);
console.error("Error handling close tab shortcut for pinned tab:", ex);
}
}
@@ -349,12 +355,13 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
return faviconData.dataURI;
} catch (ex) {
console.error('Failed to get favicon:', ex);
console.error("Failed to get favicon:", ex);
return null;
}
}
addToEssentials(tab) {
// eslint-disable-next-line no-nested-ternary
const tabs = tab
? // if it's already an array, dont make it [tab]
tab?.length
@@ -365,18 +372,19 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
: [TabContextMenu.contextTab];
let movedAll = true;
for (let i = 0; i < tabs.length; i++) {
// eslint-disable-next-line no-shadow
let tab = tabs[i];
const section = gZenWorkspaces.getEssentialsSection(tab);
if (!this.canEssentialBeAdded(tab)) {
movedAll = false;
continue;
}
if (tab.hasAttribute('zen-essential')) {
if (tab.hasAttribute("zen-essential")) {
continue;
}
tab.setAttribute('zen-essential', 'true');
if (tab.hasAttribute('zen-workspace-id')) {
tab.removeAttribute('zen-workspace-id');
tab.setAttribute("zen-essential", "true");
if (tab.hasAttribute("zen-workspace-id")) {
tab.removeAttribute("zen-workspace-id");
}
if (tab.pinned) {
gBrowser.zenHandleTabMove(tab, () => {
@@ -384,7 +392,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
tab = gBrowser.adoptTab(tab, {
selectTab: tab.selected,
});
tab.setAttribute('zen-essential', 'true');
tab.setAttribute("zen-essential", "true");
}
section.appendChild(tab);
});
@@ -392,13 +400,13 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
gBrowser.pinTab(tab);
this._ignoreNextTabPinnedEvent = true;
}
tab.setAttribute('zenDefaultUserContextId', true);
tab.setAttribute("zenDefaultUserContextId", true);
if (tab.selected) {
gZenWorkspaces.switchTabIfNeeded(tab);
}
this.onTabIconChanged(tab);
// Dispatch the event to update the UI
const event = new CustomEvent('TabAddedToEssentials', {
const event = new CustomEvent("TabAddedToEssentials", {
detail: { tab },
bubbles: true,
cancelable: false,
@@ -410,16 +418,18 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
removeEssentials(tab, unpin = true) {
// eslint-disable-next-line no-nested-ternary
const tabs = tab
? [tab]
: TabContextMenu.contextTab.multiselected
? gBrowser.selectedTabs
: [TabContextMenu.contextTab];
for (let i = 0; i < tabs.length; i++) {
// eslint-disable-next-line no-shadow
const tab = tabs[i];
tab.removeAttribute('zen-essential');
tab.removeAttribute("zen-essential");
if (gZenWorkspaces.workspaceEnabled && gZenWorkspaces.getActiveWorkspaceFromCache().uuid) {
tab.setAttribute('zen-workspace-id', gZenWorkspaces.getActiveWorkspaceFromCache().uuid);
tab.setAttribute("zen-workspace-id", gZenWorkspaces.getActiveWorkspaceFromCache().uuid);
}
if (unpin) {
gBrowser.unpinTab(tab);
@@ -430,7 +440,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
});
}
// Dispatch the event to update the UI
const event = new CustomEvent('TabRemovedFromEssentials', {
const event = new CustomEvent("TabRemovedFromEssentials", {
detail: { tab },
bubbles: true,
cancelable: false,
@@ -455,7 +465,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
hidden="true"
command="cmd_zenPinnedTabResetNoTab"/>
`);
document.getElementById('tabContextMenu').appendChild(elements);
document.getElementById("tabContextMenu").appendChild(elements);
const element = window.MozXULElement.parseXULToFragment(`
<menuitem id="context_zen-add-essential"
@@ -476,11 +486,11 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
<menuseparator/>
`);
document.getElementById('context_pinTab')?.before(element);
document.getElementById('context_zen-edit-tab-title').addEventListener('command', (event) => {
document.getElementById("context_pinTab")?.before(element);
document.getElementById("context_zen-edit-tab-title").addEventListener("command", (event) => {
gZenVerticalTabsManager.renameTabStart(event);
});
document.getElementById('context_zen-edit-tab-icon').addEventListener('command', () => {
document.getElementById("context_zen-edit-tab-icon").addEventListener("command", () => {
const tab = TabContextMenu.contextTab;
gZenEmojiPicker
.open(tab.iconImage, { emojiAsSVG: true })
@@ -497,46 +507,46 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
})
.catch((err) => {
console.error(err);
return;
});
});
}
updatePinnedTabContextMenu(contextTab) {
if (!this.enabled) {
document.getElementById('context_pinTab').hidden = true;
document.getElementById("context_pinTab").hidden = true;
return;
}
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 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;
zenAddEssential.hidden = isEssential || !!contextTab.group;
document.l10n
.formatValue('tab-context-zen-add-essential-badge', {
.formatValue("tab-context-zen-add-essential-badge", {
num: gBrowser._numZenEssentials,
max: this.maxEssentialTabs,
})
.then((badgeText) => {
zenAddEssential.setAttribute('badge', badgeText);
zenAddEssential.setAttribute("badge", badgeText);
});
document
.getElementById('cmd_contextZenAddToEssentials')
.setAttribute('disabled', !this.canEssentialBeAdded(contextTab));
document.getElementById('context_closeTab').hidden = contextTab.hasAttribute('zen-essential');
document.getElementById('context_zen-remove-essential').hidden = !isEssential;
document.getElementById('context_unpinTab').hidden =
document.getElementById('context_unpinTab').hidden || isEssential;
document.getElementById('context_unpinSelectedTabs').hidden =
document.getElementById('context_unpinSelectedTabs').hidden || isEssential;
document.getElementById('context_zen-pinned-tab-separator').hidden = !isVisible;
document.getElementById('context_zen-edit-tab-title').hidden =
.getElementById("cmd_contextZenAddToEssentials")
.setAttribute("disabled", !this.canEssentialBeAdded(contextTab));
document.getElementById("context_closeTab").hidden = contextTab.hasAttribute("zen-essential");
document.getElementById("context_zen-remove-essential").hidden = !isEssential;
document.getElementById("context_unpinTab").hidden =
document.getElementById("context_unpinTab").hidden || isEssential;
document.getElementById("context_unpinSelectedTabs").hidden =
document.getElementById("context_unpinSelectedTabs").hidden || isEssential;
document.getElementById("context_zen-pinned-tab-separator").hidden = !isVisible;
document.getElementById("context_zen-edit-tab-title").hidden =
isEssential ||
!Services.prefs.getBoolPref('zen.tabs.rename-tabs') ||
!Services.prefs.getBoolPref("zen.tabs.rename-tabs") ||
!gZenVerticalTabsManager._prefsSidebarExpanded;
}
// eslint-disable-next-line complexity
moveToAnotherTabContainerIfNecessary(event, movingTabs) {
movingTabs = [...movingTabs];
if (!this.enabled) {
@@ -544,9 +554,9 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
try {
const pinnedTabsTarget = event.target.closest(
':is(.zen-current-workspace-indicator, .zen-workspace-pinned-tabs-section)'
":is(.zen-current-workspace-indicator, .zen-workspace-pinned-tabs-section)"
);
const essentialTabsTarget = event.target.closest('.zen-essentials-container');
const essentialTabsTarget = event.target.closest(".zen-essentials-container");
const tabsTarget = !pinnedTabsTarget;
// TODO: Solve the issue of adding a tab between two groups
@@ -570,7 +580,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
let isRegularTabs = false;
// Check for essentials container
if (essentialTabsTarget) {
if (!draggedTab.hasAttribute('zen-essential') && !draggedTab?.group) {
if (!draggedTab.hasAttribute("zen-essential") && !draggedTab?.group) {
moved = true;
isVertical = false;
hasActuallyMoved = this.addToEssentials(draggedTab);
@@ -580,31 +590,31 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
else if (pinnedTabsTarget) {
if (!draggedTab.pinned) {
gBrowser.pinTab(draggedTab);
} else if (draggedTab.hasAttribute('zen-essential')) {
} else if (draggedTab.hasAttribute("zen-essential")) {
this.removeEssentials(draggedTab, false);
moved = true;
}
}
// Check for normal tabs container
else if (tabsTarget || event.target.id === 'zen-tabs-wrapper') {
if (draggedTab.pinned && !draggedTab.hasAttribute('zen-essential')) {
else if (tabsTarget || event.target.id === "zen-tabs-wrapper") {
if (draggedTab.pinned && !draggedTab.hasAttribute("zen-essential")) {
gBrowser.unpinTab(draggedTab);
isRegularTabs = true;
} else if (draggedTab.hasAttribute('zen-essential')) {
} else if (draggedTab.hasAttribute("zen-essential")) {
this.removeEssentials(draggedTab);
moved = true;
isRegularTabs = true;
}
}
if (typeof hasActuallyMoved === 'undefined') {
if (typeof hasActuallyMoved === "undefined") {
hasActuallyMoved = moved;
}
// If the tab was moved, adjust its position relative to the target tab
if (hasActuallyMoved) {
const targetTab = event.target.closest('.tabbrowser-tab');
const targetFolder = event.target.closest('zen-folder');
const targetTab = event.target.closest(".tabbrowser-tab");
const targetFolder = event.target.closest("zen-folder");
let targetElem = targetTab || targetFolder?.labelElement;
if (targetElem?.group?.activeGroups?.length > 0) {
const activeGroup = targetElem.group.activeGroups.at(-1);
@@ -642,7 +652,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
return moved;
} catch (ex) {
console.error('Error moving tabs:', ex);
console.error("Error moving tabs:", ex);
return false;
}
}
@@ -652,14 +662,14 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
if (
!tab ||
!tab.pinned ||
tab.hasAttribute('zen-essential') ||
tab.hasAttribute("zen-essential") ||
!tab._zenPinnedInitialState?.entry
) {
return;
}
// Remove # and ? from the URL
const pinUrl = tab._zenPinnedInitialState.entry.url.split('#')[0];
const currentUrl = browser.currentURI.spec.split('#')[0];
const pinUrl = tab._zenPinnedInitialState.entry.url.split("#")[0];
const currentUrl = browser.currentURI.spec.split("#")[0];
// Add an indicator that the pin has been changed
if (pinUrl === currentUrl) {
this.resetPinChangedUrl(tab);
@@ -669,57 +679,58 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
resetPinChangedUrl(tab) {
if (!tab.hasAttribute('zen-pinned-changed')) {
if (!tab.hasAttribute("zen-pinned-changed")) {
return;
}
tab.removeAttribute('zen-pinned-changed');
tab.removeAttribute('had-zen-pinned-changed');
tab.style.removeProperty('--zen-original-tab-icon');
tab.removeAttribute("zen-pinned-changed");
tab.removeAttribute("had-zen-pinned-changed");
tab.style.removeProperty("--zen-original-tab-icon");
}
pinHasChangedUrl(tab) {
if (tab.hasAttribute('zen-pinned-changed')) {
if (tab.hasAttribute("zen-pinned-changed")) {
return;
}
if (tab.group?.hasAttribute('split-view-group')) {
tab.setAttribute('had-zen-pinned-changed', 'true');
if (tab.group?.hasAttribute("split-view-group")) {
tab.setAttribute("had-zen-pinned-changed", "true");
} else {
tab.setAttribute('zen-pinned-changed', 'true');
tab.setAttribute("zen-pinned-changed", "true");
}
tab.style.setProperty('--zen-original-tab-icon', `url(${tab._zenPinnedInitialState.image})`);
tab.style.setProperty("--zen-original-tab-icon", `url(${tab._zenPinnedInitialState.image})`);
}
removeTabContainersDragoverClass(hideIndicator = true) {
this.dragIndicator.remove();
this._dragIndicator = null;
if (hideIndicator) {
gZenWorkspaces.activeWorkspaceIndicator?.removeAttribute('open');
gZenWorkspaces.activeWorkspaceIndicator?.removeAttribute("open");
}
}
get dragIndicator() {
if (!this._dragIndicator) {
this._dragIndicator = document.createElement('div');
this._dragIndicator.id = 'zen-drag-indicator';
this._dragIndicator = document.createElement("div");
this._dragIndicator.id = "zen-drag-indicator";
gNavToolbox.appendChild(this._dragIndicator);
}
return this._dragIndicator;
}
get expandedSidebarMode() {
return document.documentElement.getAttribute('zen-sidebar-expanded') === 'true';
return document.documentElement.getAttribute("zen-sidebar-expanded") === "true";
}
canEssentialBeAdded(tab) {
return (
!(
(tab.getAttribute('usercontextid') || 0) !=
(tab.getAttribute("usercontextid") || 0) !=
gZenWorkspaces.getActiveWorkspaceFromCache().containerTabId &&
gZenWorkspaces.containerSpecificEssentials
) && gBrowser._numZenEssentials < this.maxEssentialTabs
);
}
// eslint-disable-next-line complexity
applyDragoverClass(event, draggedTab) {
if (!this.enabled) {
return;
@@ -727,27 +738,27 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
let isVertical = this.expandedSidebarMode;
if (
gBrowser.isTabGroupLabel(draggedTab) &&
!draggedTab?.group?.hasAttribute('split-view-group')
!draggedTab?.group?.hasAttribute("split-view-group")
) {
// If the target is a tab group label, we don't want to apply the dragover class
this.removeTabContainersDragoverClass();
return;
}
const pinnedTabsTarget = event.target.closest('.zen-workspace-pinned-tabs-section');
const essentialTabsTarget = event.target.closest('.zen-essentials-container');
const tabsTarget = event.target.closest('.zen-workspace-normal-tabs-section');
const folderTarget = event.target.closest('zen-folder');
let targetTab = event.target.closest('.tabbrowser-tab');
const pinnedTabsTarget = event.target.closest(".zen-workspace-pinned-tabs-section");
const essentialTabsTarget = event.target.closest(".zen-essentials-container");
const tabsTarget = event.target.closest(".zen-workspace-normal-tabs-section");
const folderTarget = event.target.closest("zen-folder");
let targetTab = event.target.closest(".tabbrowser-tab");
targetTab = targetTab?.group || targetTab;
draggedTab = draggedTab?.group?.hasAttribute('split-view-group')
draggedTab = draggedTab?.group?.hasAttribute("split-view-group")
? draggedTab.group
: draggedTab;
const isHoveringIndicator = !!event.target.closest('.zen-current-workspace-indicator');
const isHoveringIndicator = !!event.target.closest(".zen-current-workspace-indicator");
if (isHoveringIndicator) {
this.removeTabContainersDragoverClass(false);
gZenWorkspaces.activeWorkspaceIndicator?.setAttribute('open', true);
gZenWorkspaces.activeWorkspaceIndicator?.setAttribute("open", true);
} else {
gZenWorkspaces.activeWorkspaceIndicator?.removeAttribute('open');
gZenWorkspaces.activeWorkspaceIndicator?.removeAttribute("open");
}
if (draggedTab?._dragData?.movingTabs) {
@@ -758,16 +769,16 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
// Decide whether we should show a dragover class for the given target
if (essentialTabsTarget) {
if (!draggedTab.hasAttribute('zen-essential') && this.canEssentialBeAdded(draggedTab)) {
if (!draggedTab.hasAttribute("zen-essential") && this.canEssentialBeAdded(draggedTab)) {
shouldAddDragOverElement = true;
isVertical = false;
}
} else if (pinnedTabsTarget) {
if (draggedTab.hasAttribute('zen-essential')) {
if (draggedTab.hasAttribute("zen-essential")) {
shouldAddDragOverElement = true;
}
} else if (tabsTarget) {
if (draggedTab.hasAttribute('zen-essential')) {
if (draggedTab.hasAttribute("zen-essential")) {
shouldAddDragOverElement = true;
}
}
@@ -784,46 +795,48 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
const separation = 8;
const middleY = targetTab.screenY + rect.height / 2;
const indicator = this.dragIndicator;
// eslint-disable-next-line no-shadow
let top = 0;
if (event.screenY > middleY) {
top = Math.round(rect.top + rect.height) + 'px';
top = Math.round(rect.top + rect.height) + "px";
} else {
top = Math.round(rect.top) + 'px';
top = Math.round(rect.top) + "px";
}
if (indicator.style.top !== top) {
shouldPlayHapticFeedback = true;
}
indicator.setAttribute('orientation', 'horizontal');
indicator.style.setProperty('--indicator-left', rect.left + separation / 2 + 'px');
indicator.style.setProperty('--indicator-width', rect.width - separation + 'px');
indicator.setAttribute("orientation", "horizontal");
indicator.style.setProperty("--indicator-left", rect.left + separation / 2 + "px");
indicator.style.setProperty("--indicator-width", rect.width - separation + "px");
indicator.style.top = top;
indicator.style.removeProperty('left');
indicator.style.removeProperty("left");
} else {
const separation = 8;
const middleX = targetTab.screenX + rect.width / 2;
const indicator = this.dragIndicator;
let left = 0;
if (event.screenX > middleX) {
left = Math.round(rect.left + rect.width + 1) + 'px';
left = Math.round(rect.left + rect.width + 1) + "px";
} else {
left = Math.round(rect.left - 2) + 'px';
left = Math.round(rect.left - 2) + "px";
}
if (indicator.style.left !== left) {
shouldPlayHapticFeedback = true;
}
indicator.setAttribute('orientation', 'vertical');
indicator.style.setProperty('--indicator-top', rect.top + separation / 2 + 'px');
indicator.style.setProperty('--indicator-height', rect.height - separation + 'px');
indicator.setAttribute("orientation", "vertical");
indicator.style.setProperty("--indicator-top", rect.top + separation / 2 + "px");
indicator.style.setProperty("--indicator-height", rect.height - separation + "px");
indicator.style.left = left;
indicator.style.removeProperty('top');
indicator.style.removeProperty("top");
}
if (shouldPlayHapticFeedback) {
// eslint-disable-next-line mozilla/valid-services
Services.zen.playHapticFeedback();
}
}
onTabLabelChanged(tab) {
tab.dispatchEvent(new CustomEvent('ZenTabLabelChanged', { bubbles: true, detail: { tab } }));
tab.dispatchEvent(new CustomEvent("ZenTabLabelChanged", { bubbles: true, detail: { tab } }));
}
}

View File

@@ -5,12 +5,7 @@
*/
&:not([zen-compact-animating])
#zen-sidebar-top-buttons:not(
:has(
#zen-sidebar-top-buttons-customization-target > *:not(#zen-sidebar-top-buttons-separator),
.titlebar-buttonbox-container
)
) {
#zen-sidebar-top-buttons:not(:has(#zen-sidebar-top-buttons-customization-target > *:not(#zen-sidebar-top-buttons-separator), .titlebar-buttonbox-container)) {
max-height: 0 !important;
min-height: 0 !important;
opacity: 0;

View File

@@ -29,8 +29,8 @@
/* ==========================================================================
Single Toolbar Mode Specific Styles (`zen-single-toolbar='true'`)
========================================================================== */
:root[zen-single-toolbar='true'] {
@media (-moz-platform: macos) and (not -moz-pref('zen.view.mac.show-three-dot-menu')) {
:root[zen-single-toolbar="true"] {
@media (-moz-platform: macos) and (not -moz-pref("zen.view.mac.show-three-dot-menu")) {
&:not([customizing]) #PanelUI-button:not([open]):not([panelopen]) {
position: absolute;
opacity: 0;
@@ -51,20 +51,20 @@
/* ==========================================================================
Vertical Tabs Mode Specific Styles (`zen.tabs.vertical` pref)
========================================================================== */
@media -moz-pref('zen.tabs.vertical') {
:root:not([zen-window-buttons-reversed='true']) {
@media -moz-pref("zen.tabs.vertical") {
:root:not([zen-window-buttons-reversed="true"]) {
& .titlebar-buttonbox-container {
margin-left: auto;
width: fit-content;
}
&:root[zen-right-side='true'] #zen-sidebar-top-buttons .titlebar-buttonbox-container {
&:root[zen-right-side="true"] #zen-sidebar-top-buttons .titlebar-buttonbox-container {
margin-right: calc(-1 * var(--zen-toolbox-padding));
}
}
}
:root[zen-window-buttons-reversed='true'] .titlebar-buttonbox-container {
:root[zen-window-buttons-reversed="true"] .titlebar-buttonbox-container {
margin-right: auto;
width: fit-content;
}
@@ -89,21 +89,18 @@
--zen-min-toolbox-padding: 0.35rem;
}
--zen-toolbox-padding: max(
var(--zen-min-toolbox-padding),
calc(var(--zen-element-separation) / 1.5)
);
--zen-toolbox-padding: max(var(--zen-min-toolbox-padding), calc(var(--zen-element-separation) / 1.5));
}
/* ==========================================================================
Single Toolbar Mode - Further Layout Adjustments
========================================================================== */
:root[zen-single-toolbar='true'] {
:root[zen-single-toolbar="true"] {
#urlbar-container {
width: -moz-available !important;
}
& #urlbar-container[breakout='true']:has(#urlbar[zen-floating-urlbar='true']) {
& #urlbar-container[breakout="true"]:has(#urlbar[zen-floating-urlbar="true"]) {
--urlbar-container-height: 36px !important;
--urlbar-height: 38px !important;
}
@@ -116,7 +113,7 @@
& #zen-sidebar-top-buttons {
margin: calc(var(--zen-toolbox-padding) / 2) 0;
:root[zen-right-side='true']:not([zen-window-buttons-reversed='true']) & {
:root[zen-right-side="true"]:not([zen-window-buttons-reversed="true"]) & {
margin-top: 0;
}
}
@@ -182,7 +179,7 @@
font-weight: 500;
gap: 2px;
#navigator-toolbox[zen-has-implicit-hover='true'] & {
#navigator-toolbox[zen-has-implicit-hover="true"] & {
visibility: visible;
opacity: 0.5;
}
@@ -191,7 +188,7 @@
opacity: 1 !important;
}
:root:not([zen-sidebar-expanded='true']) & {
:root:not([zen-sidebar-expanded="true"]) & {
display: none;
}
@@ -214,7 +211,7 @@
}
}
:root[zen-unsynced-window='true'] & {
:root[zen-unsynced-window="true"] & {
transform: translateY(-4px);
}
}
@@ -246,8 +243,8 @@
}
/* Hide default titlebar spacers */
.titlebar-spacer[type='pre-tabs'],
.titlebar-spacer[type='post-tabs'] {
.titlebar-spacer[type="pre-tabs"],
.titlebar-spacer[type="post-tabs"] {
display: none;
}
@@ -260,7 +257,7 @@
gap: 0;
&::after {
content: '';
content: "";
display: block;
height: 1px;
margin: 0 auto;
@@ -320,7 +317,7 @@
var(--zen-tabbox-element-indent-transition);
}
:root[zen-sidebar-expanded='true'] &:not([zen-glance-tab]) {
:root[zen-sidebar-expanded="true"] &:not([zen-glance-tab]) {
margin-inline-start: var(--zen-folder-indent) !important;
}
@@ -344,11 +341,7 @@
&:not([src]),
&:-moz-broken {
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3C/svg%3E") !important;
background: color-mix(
in srgb,
var(--zen-primary-color) 80%,
light-dark(rgb(0, 0, 0), rgb(255, 255, 255))
);
background: color-mix(in srgb, var(--zen-primary-color) 80%, light-dark(rgb(0, 0, 0), rgb(255, 255, 255)));
}
}
@@ -408,13 +401,13 @@
}
&[soundplaying]:not([muted]) .tab-icon-stack::after {
content: '';
content: "";
position: absolute;
width: 110%;
height: 110%;
background-repeat: no-repeat;
opacity: 1;
background: url('chrome://browser/content/zen-images/note-indicator.svg') no-repeat;
background: url("chrome://browser/content/zen-images/note-indicator.svg") no-repeat;
top: -70%;
left: 50%;
transform: translateX(-50%);
@@ -427,7 +420,7 @@
/* --- Essentials Glance Tab Specifics (Floating) --- */
/* Additional styles for glance tabs in "essential" mode */
&[zen-essential='true'] .tabbrowser-tab {
&[zen-essential="true"] .tabbrowser-tab {
position: absolute;
top: 0px;
right: 0px;
@@ -472,7 +465,7 @@
overflow-y: auto;
height: 100%;
:root[zen-workspace-id][zen-sidebar-expanded='true'] & {
:root[zen-workspace-id][zen-sidebar-expanded="true"] & {
margin-left: calc(-1 * var(--zen-toolbox-padding));
width: calc(100% + var(--zen-toolbox-padding) * 2);
}
@@ -495,7 +488,7 @@
margin: 0;
}
#navigator-toolbox[zen-sidebar-expanded='true'] {
#navigator-toolbox[zen-sidebar-expanded="true"] {
--zen-toolbox-min-width: fit-content;
padding: var(--zen-toolbox-padding);
@@ -523,8 +516,8 @@
& #nav-bar {
padding-right: 0;
:root[zen-single-toolbar='true']:not([zen-has-empty-tab='true']) & {
& #urlbar:not([breakout-extend='true']) .urlbar-input-container {
:root[zen-single-toolbar="true"]:not([zen-has-empty-tab="true"]) & {
& #urlbar:not([breakout-extend="true"]) .urlbar-input-container {
padding-left: 8px;
padding-right: 4px;
}
@@ -535,12 +528,12 @@
margin-top: 0;
}
& .zen-current-workspace-indicator-icon[no-icon='true'] {
& .zen-current-workspace-indicator-icon[no-icon="true"] {
display: none;
}
& #zen-workspaces-button {
& .zen-workspace-sidebar-icon[no-icon='true'] {
& .zen-workspace-sidebar-icon[no-icon="true"] {
display: none;
}
}
@@ -557,10 +550,10 @@
--tab-min-height: 44px;
}
&[zen-right-side='true'] {
&[zen-right-side="true"] {
padding-left: 0;
}
&:not([zen-right-side='true']) {
&:not([zen-right-side="true"]) {
padding-right: 0;
padding-left: var(--zen-toolbox-padding);
}
@@ -624,10 +617,7 @@
/* --- Pinned Tab Icon Repositioning & Reset Button --- */
/* Reposition icon stack absolutely when tab is pinned (and not essential) */
&[zen-pinned-changed='true']:not([zen-essential])
> .tab-stack
> .tab-content
> .tab-icon-stack {
&[zen-pinned-changed="true"]:not([zen-essential]) > .tab-stack > .tab-content > .tab-icon-stack {
position: absolute;
top: 50%;
transform: translateY(-50%);
@@ -640,11 +630,11 @@
}
}
&[zen-pinned-changed='true']:not([zen-essential]) .tab-reset-pin-button image {
&[zen-pinned-changed="true"]:not([zen-essential]) .tab-reset-pin-button image {
opacity: 0;
}
&[zen-pinned-changed='true']:not([zen-essential]) .tab-reset-pin-button:hover {
&[zen-pinned-changed="true"]:not([zen-essential]) .tab-reset-pin-button:hover {
& ~ .tab-label-container .tab-reset-pin-label {
max-height: 10px;
opacity: 0.6;
@@ -684,13 +674,13 @@
}
}
:root:not([zen-sidebar-expanded='true']) {
:root:not([zen-sidebar-expanded="true"]) {
--tab-min-width: 48px !important;
--zen-toolbox-padding: 6px !important;
--zen-toolbox-max-width: calc(var(--tab-min-width) + var(--zen-toolbox-padding) * 2);
}
#navigator-toolbox:not([zen-sidebar-expanded='true']) {
#navigator-toolbox:not([zen-sidebar-expanded="true"]) {
max-width: var(--zen-toolbox-max-width) !important;
min-width: var(--zen-toolbox-max-width) !important;
padding-bottom: var(--zen-toolbox-padding);
@@ -713,7 +703,7 @@
height: 100%;
}
&:is([open='true'], :hover) .zen-current-workspace-indicator-icon {
&:is([open="true"], :hover) .zen-current-workspace-indicator-icon {
display: none;
}
}
@@ -857,7 +847,7 @@
}
}
:root:not([zen-sidebar-expanded='true']) #zen-sidebar-splitter {
:root:not([zen-sidebar-expanded="true"]) #zen-sidebar-splitter {
display: none !important;
}
@@ -873,7 +863,7 @@
order: 0;
}
:root[zen-right-side='true'] {
:root[zen-right-side="true"] {
& #navigator-toolbox {
order: 10 !important;
}
@@ -885,18 +875,18 @@
#tabbrowser-tabs {
& .tabbrowser-tab {
&[pinned]:not([pending='true']) .tab-close-button {
&[pinned]:not([pending="true"]) .tab-close-button {
display: none !important;
}
&[pinned]:not([pending='true']):not([zen-essential]) {
&[pinned]:not([pending="true"]):not([zen-essential]) {
&:hover .tab-reset-button,
&[visuallyselected] .tab-reset-button {
display: block;
}
}
&:not([zen-pinned-changed='true']) .tab-reset-pin-button {
&:not([zen-pinned-changed="true"]) .tab-reset-pin-button {
display: none;
}
@@ -942,8 +932,7 @@
height: calc(100% - var(--tab-block-margin) * 2);
margin-left: calc(-1 * var(--tab-inline-padding) + var(--tab-block-margin));
margin-right: 8px;
padding: 0 calc(var(--toolbarbutton-inner-padding) - 2px) 0
calc(var(--toolbarbutton-inner-padding) / 4 + var(--tab-inline-padding) - 2px);
padding: 0 calc(var(--toolbarbutton-inner-padding) - 2px) 0 calc(var(--toolbarbutton-inner-padding) / 4 + var(--tab-inline-padding) - 2px);
border-radius: 0;
border-top-left-radius: var(--border-radius-medium);
width: unset;
@@ -960,7 +949,7 @@
}
&::after {
content: '';
content: "";
display: block;
width: 2.5px;
height: 16px;
@@ -1019,11 +1008,11 @@
order: -1;
min-width: unset !important;
:root[zen-sidebar-expanded='true'] & {
:root[zen-sidebar-expanded="true"] & {
--toolbarbutton-inner-padding: var(--zen-toolbar-button-inner-padding) !important;
}
:root[zen-single-toolbar='true'] & {
:root[zen-single-toolbar="true"] & {
--zen-toolbar-height: 36px;
@media (-moz-platform: macos) {
--zen-toolbar-height: 38px;
@@ -1042,7 +1031,7 @@
padding-inline-end: var(--toolbarbutton-outer-padding);
}
:root:not([zen-sidebar-expanded='true']) & toolbarspring {
:root:not([zen-sidebar-expanded="true"]) & toolbarspring {
display: none;
}
}
@@ -1061,7 +1050,7 @@
-moz-user-focus: ignore !important;
}
@media -moz-pref('zen.tabs.show-newtab-vertical') {
@media -moz-pref("zen.tabs.show-newtab-vertical") {
#tabs-newtab-button {
max-height: var(--tab-min-height);
display: flex !important;
@@ -1091,7 +1080,7 @@
--zen-colors-border: var(--zen-colors-tertiary);
}
@media -moz-pref('zen.view.show-newtab-button-top') {
@media -moz-pref("zen.view.show-newtab-button-top") {
order: -1;
}
}
@@ -1119,24 +1108,14 @@
grid-template-columns 0.3s ease-out;
opacity: 1;
--min-essentials-width-wrap: calc(var(--tab-min-height) + 4px);
grid-template-columns: repeat(
auto-fit,
minmax(max(23.7%, var(--min-essentials-width-wrap)), 1fr)
);
&[data-hack-type='1'] {
grid-template-columns: repeat(
auto-fit,
minmax(max(30%, var(--min-essentials-width-wrap)), auto)
);
grid-template-columns: repeat(auto-fit, minmax(max(23.7%, var(--min-essentials-width-wrap)), 1fr));
&[data-hack-type="1"] {
grid-template-columns: repeat(auto-fit, minmax(max(30%, var(--min-essentials-width-wrap)), auto));
}
&[data-hack-type='2'] {
grid-template-columns: repeat(
auto-fit,
minmax(max(23%, var(--min-essentials-width-wrap)), 1fr)
minmax(max(23%, var(--min-essentials-width-wrap)), 1fr)
);
&[data-hack-type="2"] {
grid-template-columns: repeat(auto-fit, minmax(max(23%, var(--min-essentials-width-wrap)), 1fr) minmax(max(23%, var(--min-essentials-width-wrap)), 1fr));
}
&[data-hack-type='3'] {
&[data-hack-type="3"] {
grid-template-columns: repeat(auto-fit, minmax(max(25%, 48px), 1fr));
}
scrollbar-width: thin;
@@ -1147,15 +1126,13 @@
position: absolute;
:root[zen-single-toolbar='true'] &:not(:empty) {
:root[zen-single-toolbar="true"] &:not(:empty) {
padding-top: 6px;
}
&[hidden='true'] {
&[hidden="true"] {
--hidden-essentials-width: calc(var(--zen-sidebar-width) + var(--zen-toolbox-padding));
max-width: var(
--hidden-essentials-width
) !important; /* To still allow essentials to grid the tabs */
max-width: var(--hidden-essentials-width) !important; /* To still allow essentials to grid the tabs */
min-width: var(--hidden-essentials-width) !important;
visibility: hidden;
@@ -1168,7 +1145,7 @@
}
}
.tabbrowser-tab[zen-essential='true'],
.tabbrowser-tab[zen-essential="true"],
#zen-welcome-initial-essentials-browser-sidebar-essentials .tabbrowser-tab {
--toolbarbutton-inner-padding: 0;
max-width: unset;
@@ -1180,7 +1157,7 @@
--tab-selected-bgcolor: light-dark(rgba(255, 255, 255, 0.85), rgba(255, 255, 255, 0.2));
&:not([visuallyselected], [multiselected='true']) .tab-background {
&:not([visuallyselected], [multiselected="true"]) .tab-background {
background: var(--zen-toolbar-element-bg);
border: none;
}
@@ -1204,10 +1181,10 @@
background: light-dark(rgba(0, 0, 0, 0.1), var(--tab-selected-bgcolor));
}
@media -moz-pref('zen.theme.essentials-favicon-bg') {
@media -moz-pref("zen.theme.essentials-favicon-bg") {
&[visuallyselected] > .tab-stack > .tab-background {
&::after {
content: '';
content: "";
inset: -50%;
filter: blur(20px);
position: absolute;
@@ -1225,24 +1202,12 @@
position: relative;
--zen-essential-bg-margin: 2px;
--zen-essential-tab-selected-bg: light-dark(
rgba(255, 255, 255, 0.85),
color-mix(in srgb, var(--zen-primary-color), rgba(132, 132, 132, 0.85) 40%)
);
--zen-essential-tab-selected-bg-hover: light-dark(
rgba(255, 255, 255, 0.8),
color-mix(in srgb, var(--zen-primary-color), rgba(132, 132, 132, 0.85) 30%)
);
--zen-essential-tab-selected-bg: light-dark(rgba(255, 255, 255, 0.85), color-mix(in srgb, var(--zen-primary-color), rgba(132, 132, 132, 0.85) 40%));
--zen-essential-tab-selected-bg-hover: light-dark(rgba(255, 255, 255, 0.8), color-mix(in srgb, var(--zen-primary-color), rgba(132, 132, 132, 0.85) 30%));
:root[zen-default-theme='true'] & {
--zen-essential-tab-selected-bg: light-dark(
rgba(255, 255, 255, 0.85),
rgba(68, 64, 64, 0.85)
);
--zen-essential-tab-selected-bg-hover: light-dark(
rgba(255, 255, 255, 0.8),
rgba(68, 64, 64, 0.75)
);
:root[zen-default-theme="true"] & {
--zen-essential-tab-selected-bg: light-dark(rgba(255, 255, 255, 0.85), rgba(68, 64, 64, 0.85));
--zen-essential-tab-selected-bg-hover: light-dark(rgba(255, 255, 255, 0.8), rgba(68, 64, 64, 0.75));
}
&::before {
@@ -1252,7 +1217,7 @@
position: absolute;
inset: 0;
z-index: 0;
content: '';
content: "";
transition: background 0.1s ease-in-out;
}
}
@@ -1265,11 +1230,7 @@
#zen-drag-indicator {
--zen-drag-indicator-height: 2px;
--zen-drag-indicator-bg: color-mix(
in srgb,
var(--zen-primary-color) 50%,
light-dark(rgba(0, 0, 0, 0.85), rgba(255, 255, 255, 0.95)) 50%
);
--zen-drag-indicator-bg: color-mix(in srgb, var(--zen-primary-color) 50%, light-dark(rgba(0, 0, 0, 0.85), rgba(255, 255, 255, 0.95)) 50%);
position: fixed;
z-index: 1000;
@@ -1278,7 +1239,7 @@
border-radius: 5px;
&::before {
content: '';
content: "";
position: absolute;
height: calc(2 * var(--zen-drag-indicator-height));
width: calc(2 * var(--zen-drag-indicator-height));
@@ -1287,7 +1248,7 @@
background: transparent;
}
&[orientation='horizontal'] {
&[orientation="horizontal"] {
left: calc(var(--indicator-left) + 2 * var(--zen-drag-indicator-height) + 4px);
width: calc(var(--indicator-width) - 2 * var(--zen-drag-indicator-height) - 4px);
height: var(--zen-drag-indicator-height);
@@ -1303,7 +1264,7 @@
}
}
&[orientation='vertical'] {
&[orientation="vertical"] {
top: calc(var(--indicator-top) + 2 * var(--zen-drag-indicator-height) + 4px);
height: calc(var(--indicator-height) - 2 * var(--zen-drag-indicator-height) - 4px);
width: var(--zen-drag-indicator-height);
@@ -1347,11 +1308,11 @@
&:not(.zen-essentials-container) {
display: flex;
:root[zen-sidebar-expanded='true'] & {
:root[zen-sidebar-expanded="true"] & {
min-width: calc(100% - var(--zen-toolbox-padding) * 2);
}
:root:not([zen-sidebar-expanded='true']) & {
:root:not([zen-sidebar-expanded="true"]) & {
max-width: var(--zen-sidebar-width);
align-items: center;
}
@@ -1363,12 +1324,12 @@
}
}
#tabs-newtab-button:not([in-urlbar='true']) label,
#tabs-newtab-button:not([in-urlbar="true"]) label,
.zen-current-workspace-indicator-name {
opacity: 0.7;
}
@media (-moz-pref('zen.theme.hide-tab-throbber')) {
@media (-moz-pref("zen.theme.hide-tab-throbber")) {
.tab-throbber {
display: none !important;
}

View File

@@ -1,16 +1,19 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
"use strict";
function goToRightSideTabs(callback) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
await SpecialPowers.pushPrefEnv({
set: [['zen.tabs.vertical.right-side', true]],
set: [["zen.tabs.vertical.right-side", true]],
});
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(async () => {
await callback();
await SpecialPowers.popPrefEnv();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
resolve();
}, 1000); // Wait for new layout
@@ -26,20 +29,22 @@ async function testSidebarWidth() {
let hasRan = false;
const ogSize = gNavToolbox.getBoundingClientRect().width;
const onCompactChanged = (event) => {
const onCompactChanged = (_event) => {
if (hasRan) {
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
gZenCompactModeManager.removeEventListener(onCompactChanged);
resolvePromise();
}, 500);
return;
}
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
const newSize = gNavToolbox.style.getPropertyValue('--zen-sidebar-width').replace('px', '');
const newSize = gNavToolbox.style.getPropertyValue("--zen-sidebar-width").replace("px", "");
Assert.equal(
newSize,
ogSize,
'The size of the titlebar should be the same as the original size'
"The size of the titlebar should be the same as the original size"
);
hasRan = true;
gZenCompactModeManager.preference = false;
@@ -61,7 +66,7 @@ add_task(async function test_Compact_Mode_Width_Right_Side() {
});
add_task(async function test_Compact_Mode_Hover() {
gNavToolbox.setAttribute('zen-has-hover', true);
gNavToolbox.setAttribute("zen-has-hover", true);
await testSidebarWidth();
gNavToolbox.removeAttribute('zen-has-hover');
gNavToolbox.removeAttribute("zen-has-hover");
});

View File

@@ -1,34 +1,34 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
"use strict";
add_task(async function test_Container_Essentials_Auto_Swithc() {
await gZenWorkspaces.createAndSaveWorkspace('Container Profile 1', undefined, false, 1);
await gZenWorkspaces.createAndSaveWorkspace("Container Profile 1", undefined, false, 1);
const workspaces = await gZenWorkspaces._workspaces();
ok(workspaces.length === 2, 'Two workspaces should exist.');
Assert.strictEqual(workspaces.length, 2, "Two workspaces should exist.");
let newTab = BrowserTestUtils.addTab(gBrowser, 'about:blank', {
let newTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
skipAnimation: true,
userContextId: 1,
});
ok(newTab, 'New tab should be opened.');
ok(newTab, "New tab should be opened.");
gZenPinnedTabManager.addToEssentials(newTab);
ok(
newTab.hasAttribute('zen-essential') && newTab.parentNode.getAttribute('container') == '1',
'New tab should be marked as essential.'
newTab.hasAttribute("zen-essential") && newTab.parentNode.getAttribute("container") == "1",
"New tab should be marked as essential."
);
ok(
gBrowser.tabs.find(
(t) => t.hasAttribute('zen-essential') && t.getAttribute('usercontextid') == 1
(t) => t.hasAttribute("zen-essential") && t.getAttribute("usercontextid") == 1
),
'New tab should be marked as essential.'
"New tab should be marked as essential."
);
const newWorkspaceUUID = gZenWorkspaces.activeWorkspace;
Assert.equal(
gZenWorkspaces.activeWorkspace,
workspaces[1].uuid,
'The new workspace should be active.'
"The new workspace should be active."
);
// Change to the original workspace, there should be no essential tabs

View File

@@ -1,29 +1,29 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
"use strict";
add_task(async function test_Check_Creation() {
await gZenWorkspaces.createAndSaveWorkspace('Container Profile 1', undefined, false, 1);
await gZenWorkspaces.createAndSaveWorkspace("Container Profile 1", undefined, false, 1);
const workspaces = await gZenWorkspaces._workspaces();
ok(workspaces.length === 2, 'Two workspaces should exist.');
Assert.strictEqual(workspaces.length, 2, "Two workspaces should exist.");
await gZenWorkspaces.changeWorkspace(workspaces[1]);
let newTab = BrowserTestUtils.addTab(gBrowser, 'about:blank', {
let newTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {
skipAnimation: true,
userContextId: 1,
});
ok(newTab, 'New tab should be opened.');
ok(newTab, "New tab should be opened.");
gZenPinnedTabManager.addToEssentials(newTab);
ok(
newTab.hasAttribute('zen-essential') && newTab.parentNode.getAttribute('container') == '1',
'New tab should be marked as essential.'
newTab.hasAttribute("zen-essential") && newTab.parentNode.getAttribute("container") == "1",
"New tab should be marked as essential."
);
ok(
gBrowser.tabs.find(
(t) => t.hasAttribute('zen-essential') && t.getAttribute('usercontextid') == 1
(t) => t.hasAttribute("zen-essential") && t.getAttribute("usercontextid") == 1
),
'New tab should be marked as essential.'
"New tab should be marked as essential."
);
const newWorkspaceUUID = gZenWorkspaces.activeWorkspace;
@@ -31,9 +31,9 @@ add_task(async function test_Check_Creation() {
await gZenWorkspaces.changeWorkspace(workspaces[0]);
ok(
!gBrowser.tabs.find(
(t) => t.hasAttribute('zen-essential') && t.getAttribute('usercontextid') == 1
(t) => t.hasAttribute("zen-essential") && t.getAttribute("usercontextid") == 1
),
'No essential tabs should be found in the original workspace.'
"No essential tabs should be found in the original workspace."
);
await gZenWorkspaces.removeWorkspace(newWorkspaceUUID);

View File

@@ -1,21 +1,21 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
'use strict';
"use strict";
add_task(async function test_Basic_Toggle() {
const folder = await gZenFolders.createFolder([], {
renameFolder: false,
label: 'subfolder',
label: "subfolder",
});
ok(!folder.collapsed, 'Folder should not be collapsed by default');
ok(!folder.collapsed, "Folder should not be collapsed by default");
folder.labelElement.click();
ok(folder.collapsed, 'Folder should be collapsed after clicking on it');
ok(folder.collapsed, "Folder should be collapsed after clicking on it");
folder.labelElement.click();
ok(!folder.collapsed, 'Folder should be expanded after clicking on it again');
ok(!folder.collapsed, "Folder should be expanded after clicking on it again");
await removeFolder(folder);
});

Some files were not shown because too many files have changed in this diff Show More