├── .eslintrc.json ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── other_requests.md └── workflows │ └── codeql.yml ├── .gitignore ├── .lgtm.yml ├── .nomedia ├── .travis.yml ├── .vscode ├── settings.json └── tasks.json ├── LICENSE.txt ├── PRIVACY-POLICY.md ├── README-zh.md ├── README.md ├── RELEASE-NOTES.md ├── _locales ├── en │ └── messages.json ├── fr │ └── messages.json ├── sp │ └── messages.json ├── zh │ └── messages.json ├── zh_CN │ └── messages.json └── zh_TW │ └── messages.json ├── background ├── action_icon.ts ├── all_commands.ts ├── browser.ts ├── browsing_data_manager.ts ├── clipboard.ts ├── completion.ts ├── completion_utils.ts ├── define.ts ├── eval_urls.ts ├── exclusions.ts ├── filter_tabs.ts ├── frame_commands.ts ├── help_dialog.ts ├── i18n.ts ├── index.d.ts ├── key_mappings.ts ├── main.ts ├── normalize_urls.ts ├── open_urls.ts ├── others.ts ├── page_handlers.ts ├── page_messages.d.ts ├── parse_urls.ts ├── ports.ts ├── request_handlers.ts ├── run_commands.ts ├── run_keys.ts ├── settings.ts ├── store.ts ├── sync.ts ├── tab_commands.ts ├── tools.ts ├── tsconfig.json ├── typed_commands.d.ts ├── ui_css.ts ├── utils.ts └── worker.js ├── content ├── async_dispatcher.ts ├── commands.ts ├── dom_ui.ts ├── extend_click.ts ├── extend_click_ff.ts ├── extend_click_vc.ts ├── frontend.ts ├── hint_filters.ts ├── hud.ts ├── injected_end.ts ├── insert.ts ├── key_handler.ts ├── link_actions.ts ├── link_hints.ts ├── local_links.ts ├── marks.ts ├── mode_find.ts ├── omni.ts ├── pagination.ts ├── port.ts ├── request_handlers.ts ├── scroller.ts ├── tsconfig.json └── visual.ts ├── front ├── help_dialog.html ├── offscreen.html ├── tee.ts ├── tsconfig.json ├── vimium-c.css ├── vomnibar-tee.html ├── vomnibar.html ├── vomnibar.ts └── words.txt ├── gulpfile.js ├── i18n ├── en │ ├── action.json │ ├── background.json │ ├── help_dialog.json │ └── options.json ├── fr │ ├── action.json │ ├── background.json │ ├── help_dialog.json │ └── options.json ├── zh │ ├── action.json │ ├── background.json │ ├── help_dialog.json │ └── options.json └── zh_TW │ ├── action.json │ ├── background.json │ ├── help_dialog.json │ └── options.json ├── icons ├── .nomedia ├── disabled_19.png ├── disabled_38.png ├── enabled_19.png ├── enabled_38.png ├── icon128.png ├── icon16.png ├── icon32.png ├── icon48.png ├── partial_19.png └── partial_38.png ├── lib ├── base.d.ts ├── base.omni.d.ts ├── dom_utils.ts ├── env.ts ├── injector.ts ├── keyboard_utils.ts ├── math_parser.d.ts ├── math_parser.js ├── polyfill.ts ├── rect.ts ├── simple_eval.ts ├── utils.ts ├── viewer.css └── viewer.js ├── manifest.json ├── manifest.v2.json ├── npm-shrinkwrap.json ├── package.json ├── pages ├── action.html ├── async_bg.ts ├── blank.html ├── define.ts ├── loader.ts ├── options.css ├── options.html ├── options_action.ts ├── options_base.ts ├── options_checker.ts ├── options_defs.ts ├── options_ext.ts ├── options_permissions.ts ├── options_wnd.ts ├── show.css ├── show.html ├── show.ts └── tsconfig.json ├── scripts ├── chrome2.sh ├── dependencies.js ├── eslint.js ├── firefox2.sh ├── gulp-utils.js ├── gulp.tsconfig.json ├── icons-to-blob.js ├── linguist.rb ├── make.sh ├── parse_tlds.js ├── rollup.config.js ├── tsc.js ├── uglifyjs-mangle.js ├── uglifyjs.dist.json ├── uglifyjs.local.json └── words-collect.js ├── settings-template.json ├── tests ├── dom │ ├── doc-zoom.html │ ├── editable-in-shadow-of-body.html │ ├── firefox-position_fixed-in-dialog.html │ ├── handleevent.html │ ├── input-select-show-picker.html │ ├── microtasks-during-events.html │ ├── move-node-across-worlds.html │ ├── named-property.html │ ├── named_property_getter.md │ ├── pointer-events-test.html │ ├── script_execution_order.md │ └── word-break.html └── unit │ ├── align-key-chars-on-mac.js │ ├── first-letter.md │ ├── keyboard-layouts.js │ ├── links-across-iframes.html │ ├── null_proto.js │ ├── order-when-resolve-promise.html │ ├── performance-now.md │ ├── proxy-tostring.js │ ├── simple-js-eval.html │ └── storage-serialize.js ├── tsconfig.base.json ├── tsconfig.json └── typings ├── base ├── base.d.ts ├── chrome.d.ts ├── es.d.ts ├── index.d.ts ├── lib.dom.d.ts ├── lib.es2015.collection.d.ts ├── lib.es2015.core.d.ts ├── lib.es2015.promise.d.ts ├── lib.es2015.symbol.d.ts ├── lib.es5.d.ts ├── lib.es6.d.ts └── terser.d.ts ├── build └── index.d.ts ├── compatibility.d.ts ├── lib ├── index.d.ts └── window.d.ts ├── messages.d.ts └── vimium_c.d.ts /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { "browser": true, "es6": true }, 3 | "extends": [ 4 | "plugin:@typescript-eslint/recommended", 5 | "plugin:@typescript-eslint/recommended-requiring-type-checking" 6 | ], 7 | "ignorePatterns": ["dist/", "helpers/", "node_modules/", "test*/", "typings/base/", "weidu/", "*.js"], 8 | "parser": "@typescript-eslint/parser", 9 | "parserOptions": { "project": "tsconfig.json", "sourceType": "script" }, 10 | "plugins": [ "@typescript-eslint" ], 11 | "rules": { 12 | "@typescript-eslint/adjacent-overload-signatures": "error", 13 | "@typescript-eslint/array-type": "off", 14 | "@typescript-eslint/ban-types": "off", 15 | "@typescript-eslint/camelcase": "off", 16 | "@typescript-eslint/class-name-casing": "off", 17 | "@typescript-eslint/comma-dangle": ["error", { 18 | "arrays": "never", "functions": "never", 19 | "objects": "only-multiline", "imports": "only-multiline", "exports": "only-multiline", 20 | "enums": "only-multiline", "generics": "never", "tuples": "never" 21 | }], 22 | "@typescript-eslint/consistent-type-assertions": "off", 23 | "@typescript-eslint/consistent-type-definitions": "error", 24 | "@typescript-eslint/explicit-function-return-type": [ "error", { 25 | "allowExpressions": true, "allowTypedFunctionExpressions": true 26 | } ], 27 | "@typescript-eslint/explicit-member-accessibility": [ "off", { "accessibility": "explicit" } ], 28 | "@typescript-eslint/explicit-module-boundary-types": "error", 29 | "@typescript-eslint/indent": "off", 30 | "@typescript-eslint/interface-name-prefix": "off", 31 | "@typescript-eslint/member-delimiter-style": "off", 32 | "@typescript-eslint/member-ordering": "off", 33 | "@typescript-eslint/no-base-to-string": [ "error", { "ignoredTypeNames": ["Function"] }], 34 | "@typescript-eslint/no-empty-function": "error", 35 | "@typescript-eslint/no-empty-interface": [ "error", { "allowSingleExtends": true }], 36 | "@typescript-eslint/no-floating-promises": "warn", 37 | "@typescript-eslint/no-explicit-any": "off", 38 | "@typescript-eslint/no-implied-eval": "off", 39 | "@typescript-eslint/no-misused-new": "error", 40 | "@typescript-eslint/no-namespace": "off", 41 | "@typescript-eslint/no-non-null-assertion": "off", 42 | "@typescript-eslint/no-parameter-properties": "off", 43 | "@typescript-eslint/no-shadow": [ "error", {} ], 44 | "@typescript-eslint/no-this-alias": "off", 45 | "@typescript-eslint/no-unsafe-assignment": "off", 46 | "@typescript-eslint/no-unsafe-member-access": "off", 47 | "@typescript-eslint/no-unsafe-return": "off", 48 | "@typescript-eslint/no-use-before-define": "off", 49 | "@typescript-eslint/no-var-requires": "error", 50 | "@typescript-eslint/no-unused-vars": [ "error", { "vars": "local", "argsIgnorePattern": "^_" } ], 51 | "@typescript-eslint/prefer-for-of": "error", 52 | "@typescript-eslint/prefer-function-type": "off", 53 | "@typescript-eslint/prefer-namespace-keyword": "error", 54 | "@typescript-eslint/prefer-regexp-exec": "off", 55 | "@typescript-eslint/prefer-string-starts-ends-with": "off", 56 | "@typescript-eslint/quotes": [ "error", "double", { "avoidEscape": true } ], 57 | "@typescript-eslint/restrict-plus-operands": "off", 58 | "@typescript-eslint/restrict-template-expressions": [ "error", { 59 | "allowNumber": true, "allowNullish": true 60 | }], 61 | "@typescript-eslint/semi": "off", 62 | "@typescript-eslint/triple-slash-reference": "off", 63 | "@typescript-eslint/type-annotation-spacing": "error", 64 | "@typescript-eslint/unbound-method": "off", 65 | "@typescript-eslint/unified-signatures": "off", 66 | "arrow-body-style": "off", 67 | "arrow-parens": [ "error", "as-needed" ], 68 | "camelcase": "off", 69 | "comma-dangle": "off", 70 | "complexity": "off", 71 | "constructor-super": "error", 72 | "curly": "error", 73 | "dot-notation": "error", 74 | "eol-last": "error", 75 | "eqeqeq": [ "error", "smart" ], 76 | "guard-for-in": "off", 77 | "id-denylist": [ "error", "any", "number", "boolean" ], 78 | "id-match": "error", 79 | "import/order": "off", 80 | "max-classes-per-file": "off", 81 | "max-len": [ "error", { "ignorePattern": "^(//|/\\*| \\*)", "code": 120 } ], 82 | "new-parens": "off", 83 | "no-bitwise": "off", 84 | "no-caller": "error", 85 | "no-cond-assign": "off", 86 | "no-console": "off", 87 | "no-debugger": "error", 88 | "no-empty": [ "error", { "allowEmptyCatch": true } ], 89 | "no-eval": "error", 90 | "no-fallthrough": "off", 91 | "no-invalid-this": "off", 92 | "no-multiple-empty-lines": "error", 93 | "no-new-wrappers": "error", 94 | "no-shadow": "off", 95 | "no-throw-literal": "error", 96 | "no-trailing-spaces": "error", 97 | "no-undef-init": "error", 98 | "no-underscore-dangle": "off", 99 | "no-unsafe-finally": "error", 100 | "no-unused-expressions": "off", 101 | "no-unused-labels": "error", 102 | "no-var": "error", 103 | "object-shorthand": "error", 104 | "one-var": "off", 105 | "prefer-arrow/prefer-arrow-functions": "off", 106 | "prefer-const": "off", 107 | "prefer-spread": "off", 108 | "prefer-rest-params": "off", 109 | "quote-props": [ "error", "as-needed" ], 110 | "radix": "off", 111 | "space-before-function-paren": "off", 112 | "spaced-comment": [ "error", "always", { 113 | "line": { "markers": [ "/", "#region" ], "exceptions": ["#endregion"] }, 114 | "block": { "exceptions": [ "#__NOINLINE__", "#__INLINE__", "#__PURE__" ], "balanced": false } 115 | } ], 116 | "use-isnan": "error", 117 | "valid-typeof": "off" 118 | }, 119 | "settings": { 120 | "import/parsers": { 121 | "@typescript-eslint/parser": [".ts"] 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * crlf=input 2 | *.bin binary 3 | *.bmp binary 4 | *.crx binary 5 | *.enc binary 6 | *.gif binary 7 | *.ico binary 8 | *.jpeg binary 9 | *.jpg binary 10 | *.png binary 11 | *.xpi binary 12 | *.js linguist-detectable=false linguist-language=JavaScript 13 | *.json linguist-language=jsonc 14 | *.rb linguist-vendored 15 | *.py linguist-detectable=false linguist-language=Python 16 | *.sh linguist-detectable=false linguist-language=Shell 17 | *.yml linguist-detectable=false linguist-vendored=YAML 18 | .github/**/*.md linguist-detectable=false linguist-language=Markdown 19 | lib/math_parser.js linguist-vendored 20 | lib/viewer.css linguist-generated=true linguist-vendored=true linguist-language=CSS 21 | lib/viewer.js linguist-generated=true linguist-vendored=true linguist-language=JavaScript 22 | package-lock.json linguist-generated=true linguist-vendored=true linguist-language=JSON 23 | npm-shrinkwrap.json linguist-generated=true linguist-vendored=true linguist-language=JSON 24 | tests/*.html linguist-detectable=false linguist-language=HTML 25 | tests/**/*.html linguist-detectable=false linguist-language=HTML 26 | typings/base/* linguist-vendored 27 | typings/lib/* linguist-detectable=false linguist-language=TypeScript 28 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: vimium-c 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ["https://github.com/gdh1995/vimium-c#donate", "https://www.paypal.me/gdh1995"] 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: File a bug 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 12 | 13 | 14 | **To Reproduce** 15 | 16 | Steps to reproduce the behavior: 17 | 18 | 20 | 21 | 22 | 23 | **Browser, OS and Vimium C versions** 24 | 25 | - Browser name: 26 | - Browser version: 27 | - Vimium C version: 28 | - OS name and version: 29 | 30 | 33 | 34 | 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other_requests.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other requests 3 | about: Request features or usage documents 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **What command or commands** 11 | 12 | 13 | 14 | **How should a feature do** 15 | 16 | When a user has done such steps: 17 | 18 | 21 | 22 | then Vimium C should do: 23 | 24 | 25 | 26 | **Browser and OS** 27 | 28 | - Browser name: 29 | - OS name: 30 | 31 | 34 | 35 | 37 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '38 0 * * *' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'javascript' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : 53 | # https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 54 | # queries: security-extended,security-and-quality 55 | 56 | - name: Perform CodeQL Analysis 57 | uses: github/codeql-action/analyze@v2 58 | with: 59 | category: "/language:${{matrix.language}}" 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*/ 2 | !.git* 3 | ~* 4 | *~ 5 | *.bak 6 | *.coffee 7 | *.crx 8 | *.gz 9 | *.js 10 | *.log 11 | *.mp4 12 | *.old 13 | *.psd 14 | *.swp 15 | *.tar 16 | *.tgz 17 | *.tsbuild* 18 | .ts* 19 | *.wmv 20 | *.zip 21 | dist 22 | dist/ 23 | front/*.png 24 | node_modules* 25 | test.* 26 | !/tests/** 27 | todo* 28 | !gulpfile.js 29 | !tsc.js 30 | !/background/worker.js 31 | !/lib/math_parser.js 32 | !/lib/viewer.* 33 | !/scripts/*.js 34 | weidu/ 35 | helpers/ 36 | scripts/*public_suffix* 37 | scripts/*releases* 38 | icons/*.bin 39 | package-lock.json 40 | # the below are just to make vscode ignore those on finding 41 | /.eslintrc.json 42 | typings/base/ 43 | !typings/base/base.d.ts 44 | scripts/*.pem.enc 45 | scripts/build*.sh 46 | .env* 47 | /dist-* 48 | -------------------------------------------------------------------------------- /.lgtm.yml: -------------------------------------------------------------------------------- 1 | path_classifiers: 2 | docs: 3 | - scripts/parse_tlds.py 4 | library: 5 | - "lib/*.min.*" 6 | - lib/math_parser.js 7 | - "typings/base*" 8 | queries: 9 | - exclude: js/automatic-semicolon-insertion 10 | - exclude: js/incomplete-url-scheme-check 11 | - exclude: js/insecure-randomness 12 | - exclude: js/regex/always-matches 13 | - exclude: js/trivial-conditional 14 | - exclude: js/useless-expression 15 | -------------------------------------------------------------------------------- /.nomedia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/.nomedia -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: node 3 | install: 4 | - npm install --no-optional 5 | - npm install -g gulp-cli 6 | script: 7 | - npm run chrome 8 | - npm run firefox 9 | cache: 10 | directories: 11 | - node_modules 12 | notifications: 13 | email: true 14 | branches: 15 | only: 16 | - master 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.rulers": [ 3 | 120 4 | ], 5 | "files.associations": { 6 | "*.json": "jsonc" 7 | }, 8 | "files.eol": "\n", 9 | "files.exclude": { 10 | "**/.git": true, 11 | "**/*.idea": true, 12 | "**/.venv": true, 13 | "**/node_modules": true, 14 | "**/dist": true, 15 | "**/.hg": true, 16 | "**/*.build": true, 17 | "[bcflp]*/*.js": true, 18 | "*lock.json": true, 19 | "*/viewer.min.css": true, 20 | "dist": true, 21 | "weidu": true, 22 | "helpers": true, 23 | ".vscode": true, 24 | "icons": true, 25 | "**/*.py": true, 26 | "**/.DS_Store": true 27 | }, 28 | "typescript.tsdk": "node_modules/typescript/lib", 29 | "css.lint.vendorPrefix": "ignore", 30 | "css.lint.zeroUnits": "warning", 31 | "css.lint.validProperties": [ 32 | "d" 33 | ], 34 | "task.autoDetect": "off", 35 | "task.quickOpen.skip": true, 36 | "javascript.preferences.autoImportFileExcludePatterns": [ 37 | "**/node_modules" 38 | ], 39 | "typescript.preferences.includePackageJsonAutoImports": "off", 40 | "typescript.preferences.autoImportFileExcludePatterns": [ 41 | "**/node_modules" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "shell", 6 | "group": "build", 7 | "problemMatcher": ["$tsc-watch"], 8 | "label": "tsc: watch", 9 | "command": "node", 10 | "args": ["scripts/tsc.js", "--watch"], 11 | "detail": "node scripts/tsc.js --watch" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2023-present Gong Dahan 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | ======================================================================= 16 | 17 | # License for old code before 2023-07-15T00:00:00.000Z 18 | 19 | The MIT License (MIT) 20 | 21 | Copyright (c) 2014-2023 Gong Dahan, Phil Crosby, Ilya Sukhar. 22 | 23 | Permission is hereby granted, free of charge, to any person 24 | obtaining a copy of this software before 2023-07-15T00:00:00.000Z 25 | and associated documentation 26 | files (the "Software"), to deal in the Software without 27 | restriction, including without limitation the rights to use, 28 | copy, modify, merge, publish, distribute, sublicense, and/or sell 29 | copies of the Software, and to permit persons to whom the 30 | Software is furnished to do so, subject to the following 31 | conditions: 32 | 33 | The above copyright notice and this permission notice shall be 34 | included in all copies or substantial portions of the Software. 35 | 36 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 37 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 38 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 39 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 40 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 41 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 42 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 43 | OTHER DEALINGS IN THE SOFTWARE. 44 | 45 | ======================================================================= 46 | 47 | Another exception is, the translation files in 48 | [_locales/](https://github.com/gdh1995/vimium-c/tree/master/_locales) 49 | belong to [CC-BY-SA-4.0](https://creativecommons.org/licenses/by-sa/4.0/), 50 | except some of those English sentences which are the same as 51 | [philc/vimium](https://github.com/philc/vimium)'s 52 | are under Vimium's MIT license. 53 | -------------------------------------------------------------------------------- /PRIVACY-POLICY.md: -------------------------------------------------------------------------------- 1 | How Vimium C Respects Your Privacy 2 | ================================== 3 | 4 | Vimium C supports both export and cloud-based synchronization of your settings (including key mappings and search 5 | engines). None of your browsing behavior is included. 6 | 7 | By default, Vimium C only downloads synced settings during the first installation, and if no synced settings are found, 8 | it will never sync with your browser account. However, if you do wish to enable this behavior, you may enable the 9 | advanced `Synchronize settings with your current account for this browser` setting. 10 | 11 | All information handled by Vimium C, including history, bookmarks, page titles and URLs, will only be used locally. 12 | 13 | Your search history in `Link Hints` modes will be purged immediately after use, 14 | and queries in `Vomnibar` and browser omnibox will be cleared in less than 20 seconds. Your keyword searches -- 15 | entered when using `FindMode` -- are stored but never included in synced settings. Please note that your `FindMode` 16 | search keywords on Incognito windows are stored temporarily, and will remain in memory, until all of your Incognito 17 | windows have been destroyed. 18 | 19 | 20 | Permissions Required 21 | ==================== 22 | 23 | Vimium C requires these permissions: 24 | * **`bookmarks` & `history`**: to match queries and generate suggestions for Vomnibar. 25 | * **`tabs`**: to operate your tabs, including opening, removing, moving and updating. 26 | * **`webNavigation`**: to match an incoming URL, confirming whether Vimium C should be enabled or disabled on the page. 27 | * **clipboard read+write & browsing activity**: to add shortcuts for copying and pasting the page title/URL, link 28 | text/href, and the like. 29 | * **`contentSettings`**: to toggle your browser's content settings. For example, turn on/off images, or JavaScript, 30 | on selected websites. 31 | * **`notifications`**: to show a notification whenever Vimium C is upgraded. 32 | * **`sessions`**: to allow you to restore closed tabs. This functionality requires Chrome 37+ or Firefox. 33 | * **`storage`**: its syncing functionality is **not in use** by default. However, if you enable "`Sync settings with 34 | your current account for this browser`", Vimium C will require **`storage.sync`**, to sync your settings items with 35 | the browser account servers. 36 | * **`tabGroups`**: to follow and keep current tab groups during `moveTab*` commands and so on 37 | * **`cookies`**: (on Firefox only) to follow tab containers during moving tabs and opening URLs 38 | * **`downloads`**: to let your browser directly download some links 39 | * **`favicon`**: to show website icons on Vomnibar 40 | * **`scripting`**: to collect information about what items on webpages are clickable 41 | * **`search`**: to search for query words on Vomnibar using your browser's default search engine 42 | 43 | Note: 44 | * The "**browsing activity**" listed above actually includes a number of separate permissions: `tabs`, `` and 45 | `webNavigation`. 46 | * Vimium C never deletes any browser history item, except on explicit user requests (such as Shift+Delete). 47 | 48 | 49 | Other Information 50 | =================== 51 | 52 | Vimium C formerly overrode the "newtab" browser URL, to provide a better user experience on the `chrome://newtab` page. 53 | That behavior was reversed in version 1.77.0, released in Sep 2019: Vimium C no longer overrides this setting. If you'd 54 | like to restore the previous behavior, we recommend [NewTab 55 | Adapter](https://github.com/gdh1995/vimium-c-helpers/tree/master/newtab#readme). 56 | 57 | Vimium C registers a search key (`v`) for browser's [omnibox](https://developer.chrome.com/extensions/omnibox) (the 58 | address bar). This makes the box work just like Vomnibar in omni mode. 59 | 60 | You may remove certain permissions without breaking Vimium C (note that some commands might fail as a result): 61 | * `webNavigation`, `contentSettings`, `notifications`, and `storage` may be removed. 62 | * Certain manifest fields, including `commands`, `chrome_url_overrides`, and `omnibox`, can be removed safely. 63 | * In versions previous to Chrome 37, no `sessions` functionality exists, so a few of Vimium C's commands won't work. 64 | Most others will remain unchanged. 65 | -------------------------------------------------------------------------------- /_locales/fr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "createTab_s": { "message": "Créer un nouvel onglet" }, 3 | "goBack_s": { "message": "Remonter dans l'historique" }, 4 | "goForward_s": { "message": "Avancer dans l'historique" }, 5 | "previousTab_s": { "message": "Aller à gauche de un onglet" }, 6 | "quickNext_s": { "message": "Aller à droite de un onglet" }, 7 | "reloadTab_s": { "message": "Recharger l'onglet" }, 8 | "userCustomized": { "message": "Raccourci personnalisé" }, 9 | "name": { "message": "Vimium C - Tout par le clavier" }, 10 | "description": { "message": 11 | "Outil de raccourcis clavier pour une navigation de page centrée sur l'usage du clavier, des opérations sur les onglets du navigateur avec une omnibar avancée ainsi que des raccourcis globaux" 12 | }, 13 | "lang1": { "message": "fr" }, 14 | "i18n": { "message": "options=fr,en show=en" }, 15 | "noActiveState": { "message": "Tel que configuré, pas d'état actif présent" }, 16 | "Close": { "message": "Cacher" }, 17 | "Toggle_Dark": { "message": "Basculez le mode sombre" }, 18 | "Search": { "message": "Rechercher" }, 19 | "Go": { "message": "Aller" }, 20 | "4": { "message": "Le dernier élément est dé-survolé" }, 21 | "5": { "message": "Aucune cible correspondante trouvée" }, 22 | "6": { "message": "Aucune touche transmise" }, 23 | "7": { "message": "Mode normal (touches transmises désactivées)$1" }, 24 | "8": { "message": ": $1 fois" }, 25 | "9": { "message": "Envoyez les prochaines touches$1" }, 26 | "10": { "message": "Aucun lien pour aller $1" }, 27 | "11": { "message": "Rien n'a le focus" }, 28 | "12": { "message": "Le dernier élement en focus est caché" }, 29 | "13": { "message": "Il n'y a aucun champ de saisie sur lequel mettre le focus" }, 30 | "14": { "message": "Aucune URL trouvée !" }, 31 | "15": { "message": "Aucun texte trouvé !" }, 32 | "17": { "message": "Une frame fille vient juste d'être remplacé" }, 33 | "20": { "message": "Copié : $1" }, 34 | "21": { "message": "Désolé, le chargement de la Vomnibar semble échouer" }, 35 | "22": { "message": "Trop de liens à sélectionner" }, 36 | "23": { "message": "La recherche est arrivée $1" }, 37 | "24": { "message": "à la fin" }, 38 | "25": { "message": "au début" }, 39 | "26": { "message": "($1 correspondances)" }, 40 | "27": { "message": "(1 correspondance)" }, 41 | "28": { "message": "(Quelques correspondances)" }, 42 | "29": { "message": "(Pas de correspondances)" }, 43 | "30": { "message": " (IHM modale)" }, 44 | "31": { "message": "Veuillez saisir: $1" }, 45 | "39": { "message": "Désolé, Vimium C ne peut pas ouvrir de HUD sur cette page" }, 46 | "41": { "message": "Aucune ancienne requête à trouver" }, 47 | "43": { "message": "Ligne de bordure sélectionnée" }, 48 | "47": { "message": "Télécharge…" }, 49 | "55": { "message": "Créez une sélection avant d'entrer en mode visuel" }, 50 | "63": { "message": "Pressez pour continuer…" }, 51 | "68": { "message": "Visuel" }, 52 | "69": { "message": "Ligne" }, 53 | "70": { "message": "Curseur" }, 54 | "71": { "message": "Aucun lien à sélectionner" }, 55 | "72": { "message": "Les LinkHints existent parce que vous tapez au clavier" }, 56 | "73": { "message": "Le lien a été supprimé de la page" }, 57 | "74": { "message": "Pas une image" }, 58 | "75": { "message": "Survole pour dérouler" }, 59 | "76": { "message": "Désolé, Vimium C ne copiera aucun mot de passe" }, 60 | "77": { "message": "Rien de nouveau à copier" }, 61 | "78": { "message": "Télécharge: $1" }, 62 | "79": { "message": "(En pause)" }, 63 | "80": { "message": "Pour déclencher la touche: $1" }, 64 | "81": { "message": "Aucune correspondance pour \"$1\"" }, 65 | "83": { "message": "Aucune sélection utilisable, entrée en mode curseur…" }, 66 | "84": { "message": "Sélection perdue" }, 67 | "114": { "message": "précedent" }, 68 | "115": { "message": "suivant" }, 69 | "119": { "message": "Le texte en lecture seule est ciblé" }, 70 | "0": { "message": "Ouvre le lien dans l'onglet courant" }, 71 | "2": { "message": "Ouvre le lien dans un nouvel onglet" }, 72 | "3": { "message": "Ouvre le lien dans un nouvel onglet actif" }, 73 | "16": { "message": "Ouvre de multiples liens dans l'onglet courant" }, 74 | "18": { "message": "Ouvre de multiples liens dans de nouveaux onglets" }, 75 | "19": { "message": "Active le lien et attend" }, 76 | "32": { "message": "Survole le lien" }, 77 | "33": { "message": "Simule la souris qui quitte le lien" }, 78 | "34": { "message": "Met le focus sur le noeud" }, 79 | "35": { "message": "Télécharge le média" }, 80 | "36": { "message": "Copie l'image" }, 81 | "37": { "message": "Ouvre l'image" }, 82 | "38": { "message": "Cherche le texte sélectionné" }, 83 | "40": { "message": "Copie le texte du lien dans le presse-papiers" }, 84 | "42": { "message": "Copie le lien de l'URL dans le presse-papiers" }, 85 | "44": { "message": "Télécharge le lien" }, 86 | "45": { "message": "Ouvre le lien dans une fenêtre incognito" }, 87 | "46": { "message": "Ouvrir l'URL directement" }, 88 | "48": { "message": "Survole les noeuds continuellement" }, 89 | "49": { "message": "Simule la souris quittant continuellement" }, 90 | "50": { "message": "Met le focus sur les noeuds continuellement" }, 91 | "51": { "message": "Télécharge de multiples médias" }, 92 | "52": { "message": "Copie de multiples images" }, 93 | "53": { "message": "Ouvre de multiples images" }, 94 | "54": { "message": "Cherche le texte du lien un à un" }, 95 | "56": { "message": "Copie le texte du lien un à un" }, 96 | "57": { "message": "Copie la liste du texte de liens" }, 97 | "58": { "message": "Copie l'URL du lien un à un" }, 98 | "59": { "message": "Copie la liste d'URL du lien" }, 99 | "60": { "message": "Télécharge de multiples liens" }, 100 | "61": { "message": "Ouvre de multiples onglets en incognito" }, 101 | "62": { "message": "Ouvrir plusieurs URL directement" }, 102 | "64": { "message": "Edite l'URL du lien dans la Vomnibar" }, 103 | "65": { "message": "Edite le texte du lien dans la Vomnibar" }, 104 | "66": { "message": "Sélectionner du texte pour passer en mode visuel" }, 105 | "67": { "message": "Sélectionne une zone d'édition" } 106 | } 107 | -------------------------------------------------------------------------------- /_locales/zh/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "createTab_s": { "message": "打开新的标签页" }, 3 | "goBack_s": { "message": "在历史记录中后退" }, 4 | "goForward_s": { "message": "在历史记录中前进" }, 5 | "previousTab_s": { "message": "切换到左侧标签页" }, 6 | "quickNext_s": { "message": "切换到右侧标签页" }, 7 | "reloadTab_s": { "message": "刷新整个页面" }, 8 | "userCustomized": { "message": "自定义快捷键" }, 9 | "name": { "message": "Vimium C - 全键盘操作浏览器" }, 10 | "description": { "message": 11 | "一款支持全键盘操作浏览器的辅助工具,只用键盘即可点击任意链接、切换标签页和快速搜索任意内容,支持自定义快捷键和调整操作细节" 12 | }, 13 | "lang1": { "message": "zh" }, 14 | "noActiveState": { "message": "已设置不显示工作状态" }, 15 | "vblank": { "message": "空白页" }, 16 | "voptions": { "message": "设置" }, 17 | "vshow": { "message": "展示" }, 18 | "Close": { "message": "关闭" }, 19 | "Toggle_Dark": { "message": "切换深色模式" }, 20 | "Search": { "message": "搜索" }, 21 | "Go": { "message": "前往" }, 22 | "4": { "message": "模拟了鼠标从上次选中内容上移出" }, 23 | "5": { "message": "没有找到符合条件的页面对象" }, 24 | "6": { "message": "没有快捷键被停用" }, 25 | "7": { "message": "临时启用所有按键$1" }, 26 | "8": { "message": ":剩余 $1 次" }, 27 | "9": { "message": "临时停用所有快捷键$1" }, 28 | "10": { "message": "没有找到“$1”对应的按钮或链接" }, 29 | "11": { "message": "没有文本框获得过键盘焦点" }, 30 | "12": { "message": "上次有键盘焦点的文本框现在不可见" }, 31 | "13": { "message": "没有找到可以输入的文本框" }, 32 | "14": { "message": "没有发现网址!" }, 33 | "15": { "message": "没有发现文字内容!" }, 34 | "17": { "message": "某个页面刚刚被替换了" }, 35 | "20": { "message": "已复制:$1" }, 36 | "21": { "message": "很抱歉,搜索框页面在当前网页上加载失败了" }, 37 | "22": { "message": "可点击的页面对象过多" }, 38 | "23": { "message": "搜索已到达网页$1" }, 39 | "24": { "message": "开头" }, 40 | "25": { "message": "末尾" }, 41 | "26": { "message": "($1 处)" }, 42 | "27": { "message": "(1 处)" }, 43 | "28": { "message": "(一些)" }, 44 | "29": { "message": "(未找到)" }, 45 | "30": { "message": "(置顶模式)" }, 46 | "31": { "message": "请输入:$1" }, 47 | "39": { "message": "很抱歉,无法为当前网页显示页内查找浮层" }, 48 | "41": { "message": "页内查找历史为空" }, 49 | "43": { "message": "已选择到行边界" }, 50 | "47": { "message": "正在下载……" }, 51 | "55": { "message": "在进入文字选择模式之前请先给出一个选区" }, 52 | "63": { "message": "请按回车键继续……" }, 53 | "68": { "message": "自由选择" }, 54 | "69": { "message": "整行选择" }, 55 | "70": { "message": "光标" }, 56 | "71": { "message": "没有找到可点击的页面对象" }, 57 | "72": { "message": "检测到文字输入,自动退出选择模式" }, 58 | "73": { "message": "所选对象已经被网页自身移除了" }, 59 | "74": { "message": "所选对象不是一个有效的图片" }, 60 | "75": { "message": "已选中此可滚动区域" }, 61 | "76": { "message": "出错了,不应使用Vimium C复制网页中的密码" }, 62 | "77": { "message": "此内容已经在剪贴板了" }, 63 | "78": { "message": "将要下载:$1" }, 64 | "79": { "message": "(已暂停)" }, 65 | "80": { "message": "将要触发:$1" }, 66 | "81": { "message": "没有找到“$1”" }, 67 | "82": { "message": "$1模式" }, 68 | "83": { "message": "没有找到可选择的文字,自动进入光标模式" }, 69 | "84": { "message": "旧的选区已丢失,请重新选取文字" }, 70 | "113": { "message": "Vimium C 拦截了加载时的文本框自动聚焦:网址 = %s,时刻 = %o。" }, 71 | "114": { "message": "上一页" }, 72 | "115": { "message": "下一页" }, 73 | "119": { "message": "聚焦到只读文本框" }, 74 | "122": { "message": "请先选择一个词" }, 75 | "123": { "message": "请先选择一段文字" }, 76 | "0": { "message": "在当前页面打开链接" }, 77 | "2": { "message": "在新标签页中打开链接" }, 78 | "3": { "message": "切换到新标签页来打开链接" }, 79 | "16": { "message": "在当前页面打开多个链接" }, 80 | "18": { "message": "在新标签页中打开多个链接" }, 81 | "19": { "message": "切换到新标签页来打开链接(不退出选择模式)" }, 82 | "32": { "message": "模拟鼠标移动到网页内容上" }, 83 | "33": { "message": "模拟鼠标从网页内容上移出" }, 84 | "34": { "message": "设置键盘焦点到网页内容上" }, 85 | "35": { "message": "下载图片等多媒体文件" }, 86 | "36": { "message": "选择图片以复制" }, 87 | "37": { "message": "在新标签页中展示图片" }, 88 | "38": { "message": "选择文字以搜索" }, 89 | "40": { "message": "选择文字以复制到剪贴板" }, 90 | "42": { "message": "选择链接以复制网址" }, 91 | "44": { "message": "选择链接以下载" }, 92 | "45": { "message": "在无痕窗口中打开链接" }, 93 | "46": { "message": "直接打开链接" }, 94 | "48": { "message": "不断模拟鼠标移动到网页内容上" }, 95 | "49": { "message": "不断模拟鼠标从网页内容上移出" }, 96 | "50": { "message": "不断设置键盘焦点到新的网页内容上" }, 97 | "51": { "message": "下载多个多媒体文件" }, 98 | "52": { "message": "依次复制多张图片" }, 99 | "53": { "message": "选择并展示多张图片" }, 100 | "54": { "message": "依次搜索多条文字" }, 101 | "56": { "message": "依次复制选中的文字" }, 102 | "57": { "message": "依次选择文字并复制为多行文本" }, 103 | "58": { "message": "依次复制链接网址" }, 104 | "59": { "message": "依次选择链接并复制网址为多行文本" }, 105 | "60": { "message": "选择并下载多个链接" }, 106 | "61": { "message": "在无痕窗口中打开多个链接" }, 107 | "62": { "message": "直接打开多个链接" }, 108 | "64": { "message": "在搜索框中编辑链接网址" }, 109 | "65": { "message": "在搜索框中编辑链接文字" }, 110 | "66": { "message": "选择文字以进入自由选择模式" }, 111 | "67": { "message": "选择一个文本框" } 112 | } -------------------------------------------------------------------------------- /_locales/zh_CN/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": { 3 | "message": "Vimium C - 全键盘操作浏览器" 4 | }, 5 | "description": { 6 | "message": "一款支持全键盘操作浏览器的辅助工具,只用键盘即可点击任意链接、切换标签页和快速搜索任意内容,支持自定义快捷键和调整操作细节" 7 | }, 8 | "i18n": { "message": "zh" } 9 | } -------------------------------------------------------------------------------- /_locales/zh_TW/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "createTab_s": { "message": "打開新的分頁" }, 3 | "goBack_s": { "message": "在歷史記錄中後退" }, 4 | "goForward_s": { "message": "在歷史記錄中前進" }, 5 | "previousTab_s": { "message": "切換到左側分頁" }, 6 | "quickNext_s": { "message": "切換到右側分頁" }, 7 | "reloadTab_s": { "message": "刷新整個頁面" }, 8 | "userCustomized": { "message": "自訂快速鍵" }, 9 | "name": { "message": "Vimium C - 全鍵盤操作瀏覽器" }, 10 | "description": { 11 | "message": "一款支持全鍵盤操作瀏覽器的輔助工具,只用鍵盤即可點擊任意連結、切換分頁和快速搜索任意內容,支持自訂快速鍵和調整操作細節" 12 | }, 13 | "lang1": { "message": "zh" }, 14 | "noActiveState": { "message": "已設定不顯示工作狀態" }, 15 | "vblank": { "message": "空白頁" }, 16 | "voptions": { "message": "設定" }, 17 | "vshow": { "message": "展示" }, 18 | "Close": { "message": "關閉" }, 19 | "Toggle_Dark": { "message": "切換深色模式" }, 20 | "Search": { "message": "搜索" }, 21 | "Go": { "message": "前往" }, 22 | "4": { "message": "模擬了滑鼠從上次選中內容上移出" }, 23 | "5": { "message": "沒有找到符合條件的頁面對象" }, 24 | "6": { "message": "沒有快速鍵被停用" }, 25 | "7": { "message": "臨時啟用所有按鍵$1" }, 26 | "8": { "message": ":剩餘 $1 次" }, 27 | "9": { "message": "臨時停用所有快速鍵$1" }, 28 | "10": { "message": "沒有找到“$1”對應的按鈕或連結" }, 29 | "11": { "message": "沒有輸入框獲得過鍵盤焦點" }, 30 | "12": { "message": "上次有鍵盤焦點的輸入框現在不可見" }, 31 | "13": { "message": "沒有找到可以輸入的輸入框" }, 32 | "14": { "message": "沒有發現網址!" }, 33 | "15": { "message": "沒有發現文字內容!" }, 34 | "17": { "message": "某個頁面剛剛被替換了" }, 35 | "20": { "message": "已複製:$1" }, 36 | "21": { "message": "很抱歉,搜索框頁面在當前網頁上加載失敗了" }, 37 | "22": { "message": "可點擊的頁面對象過多" }, 38 | "23": { "message": "搜索已到達網頁$1" }, 39 | "24": { "message": "開頭" }, 40 | "25": { "message": "末尾" }, 41 | "26": { "message": "($1 處)" }, 42 | "27": { "message": "(1 處)" }, 43 | "28": { "message": "(一些)" }, 44 | "29": { "message": "(未找到)" }, 45 | "30": { "message": "(置頂模式)" }, 46 | "31": { "message": "請輸入:$1" }, 47 | "39": { "message": "很抱歉,無法為當前網頁顯示頁內查找浮層" }, 48 | "41": { "message": "頁內查找歷史為空" }, 49 | "43": { "message": "已選擇到行邊界" }, 50 | "47": { "message": "正在下載……" }, 51 | "55": { "message": "在進入文字選擇模式之前請先給出一個選區" }, 52 | "63": { "message": "請按 Enter 鍵繼續……" }, 53 | "68": { "message": "自由選擇" }, 54 | "69": { "message": "整行選擇" }, 55 | "70": { "message": "游標" }, 56 | "71": { "message": "沒有找到可點擊的頁面對象" }, 57 | "72": { "message": "檢測到文字輸入,自動退出選擇模式" }, 58 | "73": { "message": "所選對象已經被網頁自身移除了" }, 59 | "74": { "message": "所選對象不是一個有效的圖片" }, 60 | "75": { "message": "已選中此可滾動區域" }, 61 | "76": { "message": "出錯了,不應使用Vimium C複製網頁中的密碼" }, 62 | "77": { "message": "此內容已經在剪貼簿了" }, 63 | "78": { "message": "將要下載:$1" }, 64 | "79": { "message": "(已暫停)" }, 65 | "80": { "message": "將要觸發:$1" }, 66 | "81": { "message": "沒有找到“$1”" }, 67 | "82": { "message": "$1模式" }, 68 | "83": { "message": "沒有找到可選擇的文字,自動進入游標模式" }, 69 | "84": { "message": "舊的選區已丟失,請重新選取文字" }, 70 | "114": { "message": "上一頁" }, 71 | "115": { "message": "下一頁" }, 72 | "119": { "message": "聚焦到只讀輸入框" }, 73 | "122": { "message": "請先選擇一個詞" }, 74 | "123": { "message": "請先選擇一段文字" }, 75 | "0": { "message": "在當前頁面打開連結" }, 76 | "2": { "message": "在新分頁中打開連結" }, 77 | "3": { "message": "切換到新分頁來打開連結" }, 78 | "16": { "message": "在當前頁面打開多個連結" }, 79 | "18": { "message": "在新分頁中打開多個連結" }, 80 | "19": { "message": "切換到新分頁來打開連結(不退出選擇模式)" }, 81 | "32": { "message": "模擬滑鼠移動到網頁內容上" }, 82 | "33": { "message": "模擬滑鼠從網頁內容上移出" }, 83 | "34": { "message": "設定鍵盤焦點到網頁內容上" }, 84 | "35": { "message": "下載圖片等多媒體文件" }, 85 | "36": { "message": "選擇圖片以複製" }, 86 | "37": { "message": "在新分頁中展示圖片" }, 87 | "38": { "message": "選擇文字以搜索" }, 88 | "40": { "message": "選擇文字以複製到剪貼簿" }, 89 | "42": { "message": "選擇連結以複製網址" }, 90 | "44": { "message": "選擇連結以下載" }, 91 | "45": { "message": "在無痕窗口中打開連結" }, 92 | "46": { "message": "直接打開連結" }, 93 | "48": { "message": "不斷模擬滑鼠移動到網頁內容上" }, 94 | "49": { "message": "不斷模擬滑鼠從網頁內容上移出" }, 95 | "50": { "message": "不斷設定鍵盤焦點到新的網頁內容上" }, 96 | "51": { "message": "下載多個多媒體文件" }, 97 | "52": { "message": "依次複製多張圖片" }, 98 | "53": { "message": "選擇並展示多張圖片" }, 99 | "54": { "message": "依次搜索多條文字" }, 100 | "56": { "message": "依次複製選中的文字" }, 101 | "57": { "message": "依次選擇文字並複製為多行文本" }, 102 | "58": { "message": "依次複製連結網址" }, 103 | "59": { "message": "依次選擇連結並複製網址為多行文本" }, 104 | "60": { "message": "選擇並下載多個連結" }, 105 | "61": { "message": "在無痕窗口中打開多個連結" }, 106 | "62": { "message": "直接打開多個連結" }, 107 | "64": { "message": "在搜索框中編輯連結網址" }, 108 | "65": { "message": "在搜索框中編輯連結文字" }, 109 | "66": { "message": "選擇文字以進入自由選擇模式" }, 110 | "67": { "message": "選擇一個輸入框" }, 111 | "i18n": { "message": "zh_TW" } 112 | } 113 | -------------------------------------------------------------------------------- /background/action_icon.ts: -------------------------------------------------------------------------------- 1 | import { 2 | framesForTab_, CurCVer_, OnChrome, set_setIcon_, setIcon_, set_iconData_, iconData_, blank_, set_needIcon_, 3 | needIcon_, updateHooks_ 4 | } from "./store" 5 | import * as BgUtils_ from "./utils" 6 | import { extTrans_ } from "./i18n" 7 | import { browser_, runtimeError_ } from "./browser" 8 | import { asyncIterFrames_ } from "./ports" 9 | 10 | const knownIcons_: readonly [IconNS.BinaryPath, IconNS.BinaryPath, IconNS.BinaryPath 11 | ] | readonly [IconNS.ImagePath, IconNS.ImagePath, IconNS.ImagePath] = OnChrome ? [ 12 | "/icons/enabled.bin", "/icons/partial.bin", "/icons/disabled.bin" 13 | ] : [ 14 | { 19: "/icons/enabled_19.png", 38: "/icons/enabled_38.png" }, 15 | { 19: "/icons/partial_19.png", 38: "/icons/partial_38.png" }, 16 | { 19: "/icons/disabled_19.png", 38: "/icons/disabled_38.png" } 17 | ] 18 | 19 | export const browserAction_ = Build.MV3 ? (browser_ as any).action as never : browser_.browserAction 20 | let tabIds_cr_: Map | null 21 | 22 | const onerror = (err: any): void => { 23 | if (setIcon_ === blank_) { return } 24 | console.log("Can not access binary icon data:", err); 25 | set_setIcon_(blank_) 26 | set_needIcon_(false) 27 | updateHooks_.showActionIcon = undefined 28 | Promise.resolve(extTrans_("name")).then((name) => { 29 | browserAction_.setTitle({ title: name + "\n\nFailed in showing dynamic icons." }) 30 | }) 31 | } 32 | 33 | const loadBinaryImagesAndSetIcon_cr = (type: Frames.ValidStatus): void => { 34 | const path = knownIcons_[type] as IconNS.BinaryPath; 35 | const loadFromRawArray = (array: ArrayBuffer): void => { 36 | const uint8Array = new Uint8ClampedArray(array), firstSize = array.byteLength / 5, 37 | small = (Math.sqrt(firstSize / 4) | 0) as IconNS.ValidSizes, large = (small + small) as IconNS.ValidSizes, 38 | cache = BgUtils_.safeObj_() as IconNS.IconBuffer; 39 | cache[small] = new ImageData(uint8Array.subarray(0, firstSize), small, small); 40 | cache[large] = new ImageData(uint8Array.subarray(firstSize), large, large); 41 | iconData_![type] = cache; 42 | const arr = tabIds_cr_!.get(type)! 43 | tabIds_cr_!.delete(type) 44 | for (let w = 0, h = arr.length; w < h; w++) { 45 | framesForTab_.has(arr[w]) && setIcon_(arr[w], type, true) 46 | } 47 | }; 48 | if (Build.MinCVer >= BrowserVer.MinFetchExtensionFiles || CurCVer_ >= BrowserVer.MinFetchExtensionFiles) { 49 | const p = fetch(path).then(r => r.arrayBuffer()).then(loadFromRawArray) 50 | if (!Build.NDEBUG) { p.catch(onerror) } 51 | } else { 52 | const req = new XMLHttpRequest() as ArrayXHR 53 | req.open("GET", path, true) 54 | req.responseType = "arraybuffer" 55 | req.onload = function (this: typeof req) { loadFromRawArray(this.response) } 56 | if (!Build.NDEBUG) { req.onerror = onerror } 57 | req.send() 58 | } 59 | } 60 | 61 | export const toggleIconBuffer_ = (): void => { 62 | const enabled = needIcon_ 63 | if (enabled === !!iconData_) { return } 64 | set_setIcon_(enabled ? doSetIcon_ : blank_) 65 | const iter = ({ cur_: { s: sender }, flags_ }: Frames.Frames): void => { 66 | if (sender.status_ !== Frames.Status.enabled) { 67 | if (flags_ & Frames.Flags.ResReleased && enabled) { 68 | sender.status_ = Frames.Status.enabled 69 | return 70 | } 71 | setIcon_(sender.tabId_, enabled ? sender.status_ : Frames.Status.enabled) 72 | } 73 | } 74 | const cond = (): boolean => needIcon_ === enabled 75 | if (!enabled) { 76 | setTimeout((): void => { 77 | if (needIcon_ || iconData_ == null) { return } 78 | set_iconData_(null) 79 | if (OnChrome) { tabIds_cr_ = null } 80 | else { 81 | asyncIterFrames_(Frames.Flags.blank, iter, cond) 82 | } 83 | }, 200); 84 | return; 85 | } 86 | if (!OnChrome) { 87 | set_iconData_(1 as never) 88 | } else { 89 | set_iconData_([null, null, null]) 90 | tabIds_cr_ = new Map() 91 | } 92 | // only do partly updates: ignore "rare" cases like `sender.s` is enabled but the real icon isn't 93 | asyncIterFrames_(Frames.Flags.blank, iter, cond) 94 | } 95 | 96 | /** Firefox does not use ImageData as inner data format 97 | * * https://dxr.mozilla.org/mozilla-central/source/toolkit/components/extensions/schemas/manifest.json#577 98 | * converts ImageData objects in parameters into data:image/png,... URLs 99 | * * https://dxr.mozilla.org/mozilla-central/source/browser/components/extensions/parent/ext-browserAction.js#483 100 | * builds a css text of "--webextension-***: url(icon-url)", 101 | * and then set the style of an extension's toolbar button to it 102 | */ 103 | const doSetIcon_: typeof setIcon_ = !OnChrome ? (tabId, type): void => { 104 | tabId < 0 || browserAction_.setIcon({ tabId, path: knownIcons_[type]! }) 105 | } : (tabId: number, type: Frames.ValidStatus, isLater?: true): void => { 106 | let data: IconNS.IconBuffer | null | undefined; 107 | if (tabId < 0) { /* empty */ } 108 | else if (data = iconData_![type]) { 109 | const f = browserAction_.setIcon 110 | const args: Parameters[0] = { tabId, imageData: data } 111 | isLater ? f(args, runtimeError_) : f(args); 112 | } else if (tabIds_cr_!.has(type)) { 113 | tabIds_cr_!.get(type)!.push(tabId) 114 | } else { 115 | setTimeout(loadBinaryImagesAndSetIcon_cr, 0, type); 116 | tabIds_cr_!.set(type, [tabId]) 117 | } 118 | } 119 | 120 | toggleIconBuffer_() 121 | -------------------------------------------------------------------------------- /background/i18n.ts: -------------------------------------------------------------------------------- 1 | import { CurCVer_ } from "./store" 2 | import { fetchFile_ } from "./utils" 3 | import { browser_, Q_ } from "./browser" 4 | import type * as i18n_map from "../_locales/en/messages.json" 5 | import type * as i18n_dyn from "../i18n/zh/background.json" 6 | 7 | type ValidI18nFiles = "background" | "help_dialog" 8 | export type ExtNames = keyof typeof i18n_map 9 | export type I18nNames = keyof typeof i18n_dyn 10 | 11 | let extPayload_: Map 12 | export let i18nReadyExt_: Promise | BOOL = Build.MV3 && Build.MinCVer < BrowserVer.MinBg$i18n$$getMessage$InMV3 13 | && CurCVer_ < BrowserVer.MinBg$i18n$$getMessage$InMV3 ? 0 : 1 14 | let i18nPayload_: Map 15 | let ready_: Promise | BOOL = 0 16 | 17 | export const contentI18n_: string[] = [] 18 | 19 | export const extTrans_: (msg: ExtNames) => string | Promise 20 | = !(Build.MV3 && Build.MinCVer < BrowserVer.MinBg$i18n$$getMessage$InMV3 21 | && CurCVer_ < BrowserVer.MinBg$i18n$$getMessage$InMV3) 22 | ? (msg: string): string => browser_.i18n.getMessage(msg) 23 | : (msg: string): string | Promise => i18nReadyExt_ === 1 ? extPayload_.get(msg)! 24 | : (i18nReadyExt_ || loadExt_()).then(extTrans_.bind(0, msg as ExtNames)) 25 | 26 | export const trans_ = (name: I18nNames, args?: (string | number)[]): string | Promise => { 27 | if (ready_ === 1) { 28 | const val = i18nPayload_.get(name) 29 | return args != null && val 30 | ? val.replace(> /\$\d/g, (i): string => args[+i[1] - 1] as string) 31 | : val || "" 32 | } else { 33 | ready_ || (ready_ = getI18nJson("background").then((obj): void => { i18nPayload_ = obj, ready_ = 1 })) 34 | return ready_.then(trans_.bind(null, name, args)) 35 | } 36 | } 37 | 38 | export const transEx_ = (name: I18nNames, args: (string | [I18nNames] | number | Promise)[] 39 | ): string | Promise => { 40 | args.forEach((i, ind, arr) => { if (i instanceof Array) { 41 | const name = i[0] 42 | arr[ind] = ready_ === 1 ? i18nPayload_.get(name) || name : (trans_(name) as Promise).then(j => j || name) 43 | } }) 44 | if (!args.some(i => i instanceof Promise)) { 45 | return trans_(name, args as (string | number)[]) 46 | } else { 47 | const p = Promise.all(args as (string | number | Promise)[]) 48 | const p2 = ready_ === 1 ? p : (ready_ || trans_("NS") as Promise).then(() => p) 49 | return p2.then((newArgs) => trans_(name, newArgs) as string) 50 | } 51 | } 52 | 53 | type RawExtDict = Map 54 | const loadExt_ = (): Promise => { 55 | return i18nReadyExt_ = Promise.all([ 56 | (fetchFile_("/_locales/en/messages.json") as Promise), Q_(browser_.i18n.getAcceptLanguages) 57 | ]).then(([enDict, wanted]): Promise | RawExtDict[] => { 58 | let all = ((enDict.get("i18nAll") || {}).message || "").split(" "), i = "" 59 | for (i of wanted || []) { 60 | all.includes(i) || all.includes(i = i.split("-")[0]) || (i = "") 61 | if (i) { break } 62 | } 63 | if (!i) { return [enDict] } 64 | return Promise.all([enDict, fetchFile_(`/_locales/${i}/messages.json`)]) 65 | }).then((arr): void => { 66 | extPayload_ = new Map() 67 | i18nReadyExt_ = 1 68 | for (let i of arr) { 69 | for (let [k, v] of (i as any).entries() as [string, { message: string }][]) { 70 | extPayload_.set(k, v.message) 71 | } 72 | } 73 | }) 74 | } 75 | 76 | export const transPart_ = (msg: "t(i18n)" | "t(sugs)", child: string | null): string => { 77 | return msg && msg.split(" ").reduce((old, i) => old ? old : !i.includes("=") ? i 78 | : child && i.startsWith(child) ? i.slice(child.length + 1) : old, "") 79 | } 80 | 81 | export const i18nLang_ = (id?: ValidI18nFiles): string => { 82 | let msg2 = extTrans_("i18n") as "t(i18n)" 83 | return transPart_(msg2, id || "background") || extTrans_("lang1") as string || "en" 84 | } 85 | 86 | export const getI18nJson = (file_name: ValidI18nFiles): Promise> => { 87 | if (Build.MV3 && i18nReadyExt_ === 0) { 88 | return loadExt_().then(getI18nJson.bind(0, file_name)) 89 | } 90 | return fetchFile_(`/i18n/${i18nLang_(file_name)}/${file_name}.json`) 91 | } 92 | 93 | export let loadContentI18n_: (() => void) | null = (): void => { 94 | const arr: string[] = contentI18n_, args = ["$1", "$2", "$3", "$4"] 95 | for (let i = 0; i < kTip.INJECTED_CONTENT_END; i++) { 96 | arr.push(Build.MV3 && Build.MinCVer < BrowserVer.MinBg$i18n$$getMessage$InMV3 97 | && CurCVer_ < BrowserVer.MinBg$i18n$$getMessage$InMV3 98 | ? extPayload_.get("" + i)!.replace( /\$\$/g, "$") 99 | : browser_.i18n.getMessage(("" + i) as "0", args)) 100 | } 101 | loadContentI18n_ = null 102 | } 103 | -------------------------------------------------------------------------------- /background/page_messages.d.ts: -------------------------------------------------------------------------------- 1 | export declare const enum kPgReq { 2 | /** 0..4 */ settingsDefaults, settingsCache, setSetting, updatePayload, notifyUpdate, 3 | /** 5..9 */ settingItem, runFgOn, keyMappingErrors, parseCSS, reloadCSS, 4 | /** 10..14 */ convertToUrl, updateMediaQueries, whatsHelp, checkNewTabUrl, checkSearchUrl, 5 | /** 15..19 */ focusOrLaunch, showUrl, shownHash, substitute, checkHarmfulUrl, 6 | /** 20..24 */ actionInit, allowExt, toggleStatus, parseMatcher, initHelp, 7 | /** 25..29 */ callApi, selfTabId, getStorage, setInLocal, updateOmniPayload, 8 | /** 30..33 */ saveToSyncAtOnce, showInit, reopenTab, checkAllowingAccess, 9 | __mask = "" 10 | } 11 | 12 | interface KVPair { key: K, val: T[K] } 13 | type Values = T[K] 14 | 15 | export interface PgReq { 16 | [kPgReq.settingsDefaults]: [ void, [conf: SettingsNS.SettingsWithDefaults, os: kOS, platform: string] ] 17 | [kPgReq.settingsCache]: [ void, SettingsNS.SettingsWithDefaults ] 18 | [kPgReq.setSetting]: [ KVPair, Values | null ] 19 | [kPgReq.updatePayload]: [ 20 | { key: keyof SettingsNS.FrontendSettingsSyncingItems, 21 | val: Values | /** autoDarkMode */ boolean }, 22 | SettingsNS.FrontendSettingCache[keyof SettingsNS.FrontendSettingsSyncingItems] | null 23 | ] 24 | [kPgReq.notifyUpdate]: [ (keyof SettingsNS.FrontendSettingsSyncingItems)[], void ] 25 | [kPgReq.settingItem]: [ { key: keyof SettingsNS.SettingsWithDefaults }, Values ] 26 | [kPgReq.runFgOn]: [ number, void ] 27 | [kPgReq.keyMappingErrors]: [ void, true | string ] 28 | [kPgReq.parseCSS]: [ [string, number], SettingsNS.MergedCustomCSS ] 29 | [kPgReq.reloadCSS]: [ { hc: boolean } | null, void ] 30 | [kPgReq.convertToUrl]: [ [string, Urls.WorkEnsureString], [string, Urls.Type] ] 31 | [kPgReq.updateMediaQueries]: [ void, void ] 32 | [kPgReq.whatsHelp]: [ void, string ] 33 | [kPgReq.checkNewTabUrl]: [ string, [string, Urls.NewTabType | null] ] 34 | [kPgReq.checkSearchUrl]: [ string, [ok: boolean, url: string] | null ] 35 | [kPgReq.focusOrLaunch]: [ FgReq[kFgReq.focusOrLaunch], void ] 36 | [kPgReq.showUrl]: [ string, Urls.Url ] 37 | [kPgReq.shownHash]: [ void, string | null ] 38 | [kPgReq.substitute]: [ [string, SedContext], string ] 39 | [kPgReq.checkHarmfulUrl]: [ string, boolean ] 40 | [kPgReq.actionInit]: [ void, { ver: string, runnable: boolean, url: string, tabId: number, frameId: number, 41 | topUrl: string | undefined, frameUrl: string | null, lock: Frames.ValidStatus | null, status: Frames.ValidStatus, 42 | hasSubDomain: 0 | 1 | 2, unknownExt: string | null, 43 | exclusions: { 44 | rules: SettingsNS.SettingsWithDefaults["exclusionRules"], onlyFirst: boolean, matchers: BaseUrlMatcher[], 45 | defaults: SettingsNS.SettingsWithDefaults["exclusionRules"] 46 | } | null 47 | os: kOS, reduceMotion: boolean 48 | } ] 49 | [kPgReq.allowExt]: [ [ tabId: number, extIdToAdd: string], void ] 50 | [kPgReq.toggleStatus]: [ [cmd: string, tab: number, frame: number], [ Frames.ValidStatus, Frames.ValidStatus | null] ] 51 | [kPgReq.parseMatcher]: [ string, BaseUrlMatcher ] 52 | [kPgReq.initHelp]: [ void, void ] 53 | [kPgReq.callApi]: [ { 54 | module: "permissions", name: "contains" | "request" | "remove", args: unknown[] 55 | } | { 56 | module: "tabs", name: "update", args: Parameters 57 | }, ExtApiResult ] 58 | [kPgReq.selfTabId]: [ void, number ] 59 | [kPgReq.getStorage]: [ GlobalConsts.kIsHighContrast | null, Dict ] 60 | [kPgReq.setInLocal]: [ { key: string, val: string | null }, void ] 61 | [kPgReq.updateOmniPayload]: [ { key: keyof SettingsNS.DirectVomnibarItems, 62 | val: Values[1] }, void ] 63 | [kPgReq.saveToSyncAtOnce]: [ void, void ] 64 | [kPgReq.showInit]: [ void, { os: kOS } ] 65 | [kPgReq.reopenTab]: [ { url: string, tabId: number }, void ] 66 | [kPgReq.checkAllowingAccess]: [ void, [incognito: boolean, fileScheme: boolean] ] 67 | } 68 | 69 | export declare namespace Req2 { 70 | type OrNull = K extends void | undefined ? null : K 71 | interface pgReq { 72 | /** name */ n: K 73 | /** query body */ q: OrNull 74 | } 75 | type pgRes = PgReq[keyof PgReq][1] 76 | } 77 | -------------------------------------------------------------------------------- /background/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "outDir": "..", 5 | "rootDir": "..", 6 | "tsBuildInfoFile": "../scripts/.ts.background.build", 7 | }, 8 | "include": [ 9 | "*.ts" 10 | ], 11 | "extends": "../tsconfig.base.json" 12 | } 13 | -------------------------------------------------------------------------------- /background/worker.js: -------------------------------------------------------------------------------- 1 | import "./define.js" 2 | //#region main 3 | import "./store.js" 4 | import "./utils.js" 5 | import "./browser.js" 6 | import "./normalize_urls.js" 7 | import "./parse_urls.js" 8 | import "./settings.js" 9 | import "./ports.js" 10 | import "./exclusions.js" 11 | import "./ui_css.js" 12 | import "./i18n.js" 13 | import "./key_mappings.js" 14 | import "./run_commands.js" 15 | import "./run_keys.js" 16 | import "./tools.js" 17 | import "./clipboard.js" 18 | import "./eval_urls.js" 19 | import "./filter_tabs.js" 20 | import "./open_urls.js" 21 | import "./frame_commands.js" 22 | import "./tab_commands.js" 23 | import "./all_commands.js" 24 | import "./request_handlers.js" 25 | import "./main.js" 26 | //#endregion 27 | //#region dynamic 28 | import "./browsing_data_manager.js" 29 | import "./completion_utils.js" 30 | import "./completion.js" 31 | import * as action_icon from "./action_icon.js" 32 | __moduleMap.action_icon = __moduleMap.action_icon || action_icon 33 | import "./others.js" 34 | import "./sync.js" 35 | import * as page_handlers from "./page_handlers.js" 36 | import * as help_dialog from "./help_dialog.js" 37 | import * as math_parser from "../lib/math_parser.js" 38 | __moduleMap.page_handlers = __moduleMap.page_handlers || page_handlers 39 | __moduleMap.help_dialog = __moduleMap.help_dialog || help_dialog 40 | __moduleMap.math_parser = __moduleMap.math_parser || math_parser 41 | //#endregion 42 | -------------------------------------------------------------------------------- /content/hud.ts: -------------------------------------------------------------------------------- 1 | import { 2 | fgCache, isEnabled_, VTr, isAlive_, timeout_, clearTimeout_, interval_, isLocked_, OnChrome, getTime, esc, doc 3 | } from "../lib/utils" 4 | import { handler_stack } from "../lib/keyboard_utils" 5 | import { isHTML_, createElement_, setClassName_s, appendNode_s, setVisibility_s, toggleClass_s } from "../lib/dom_utils" 6 | import { ui_box, ensureBorder, addUIElement, adjustUI, getBoxTagName_old_cr } from "./dom_ui" 7 | import { allHints, isHintsActive, hintManager, setMode as setHintMode, hintMode_ } from "./link_hints" 8 | import { insert_global_, passAsNormal, raw_insert_lock, readonlyFocused_, set_readonlyFocused_ } from "./insert" 9 | import { visual_mode_name } from "./visual" 10 | import { find_box } from "./mode_find" 11 | import { currentKeys } from "./key_handler" 12 | 13 | let tweenId: ValidIntervalID = TimerID.None, tweenStart = 0 14 | let box: HTMLDivElement | HTMLBodyElement | null = null 15 | let $text: Text = null as never 16 | let text = "" 17 | let opacity_: 0 | 0.25 | 0.5 | 0.75 | 1 = 0, dom_opacity_: number = 1 18 | let timer: ValidTimeoutID = TimerID.None 19 | 20 | export { box as hud_box, text as hud_text, opacity_ as hud_opacity, timer as hud_tipTimer } 21 | 22 | export const hudTip = (tid: kTip | HintMode, duration?: 0 | 0.0001 | 1 | 2, args?: Array | string 23 | , embed?: 1): void => { 24 | hudShow(tid, args, embed) 25 | text && (timer = timeout_(hudHide 26 | , ((duration || (tid === kTip.copiedIs && (find_box || visual_mode_name) ? 0.5 : 1.5)) * 1000) | 0)) 27 | } 28 | export const hudShow = (tid: kTip | HintMode, args?: Array | string 29 | , embed?: boolean | BOOL | TimerType.fake | void): void => { 30 | if (!isHTML_()) { return; } 31 | text = VTr(tid, args); 32 | opacity_ = 1; 33 | clearTimeout_(timer) 34 | timer = TimerID.None 35 | embed || tweenId || (tweenId = interval_(tween, 40), tweenStart = getTime()) 36 | if (box) { 37 | toggleClass_s(box, "HL", 0) 38 | if (embed || dom_opacity_) { $text.data = text } 39 | embed && toggleOpacity(1) 40 | return 41 | } 42 | box = createElement_(OnChrome && Build.MinCVer < BrowserVer.MinForcedColorsMode ? getBoxTagName_old_cr() : "div") 43 | setClassName_s(box, "R HUD" + fgCache.d) 44 | appendNode_s(box, $text = new Text(text)) // lgtm [js/superfluous-trailing-arguments] 45 | if (!embed) { 46 | toggleOpacity(0) 47 | ui_box || ensureBorder() // safe to skip `getZoom_` 48 | } 49 | addUIElement(box, allHints ? AdjustType.NotAdjust : AdjustType.DEFAULT) 50 | } 51 | 52 | const tween = (fake?: TimerType.fake): void => { // safe-interval 53 | let opacity = OnChrome && Build.MinCVer < BrowserVer.MinNo$TimerType$$Fake && fake ? 0 : dom_opacity_ 54 | if (opacity === opacity_) { /* empty */ } 55 | else if (opacity === 0) { 56 | $text.data = text; 57 | toggleOpacity(OnChrome && Build.MinCVer < BrowserVer.MinNo$TimerType$$Fake && fake || fgCache.m ? 1 : 0.25) 58 | fake && (tweenId = 0) 59 | return adjustUI(); 60 | } else if (!fgCache.m && !doc.hidden && getTime() - tweenStart < 996) { 61 | // in "efficiency mode" of MS Edge 98, step of interval or following timeout may be increased into 1 second 62 | opacity += opacity < opacity_ ? 0.25 : -0.25; 63 | } else { 64 | opacity = opacity_; 65 | } 66 | if (opacity) { 67 | toggleOpacity(opacity) 68 | } else { 69 | hudHide(TimerType.noTimer) 70 | } 71 | if (opacity !== opacity_) { return } 72 | clearTimeout_(tweenId) 73 | tweenId = 0; 74 | } 75 | 76 | export const hudHide = (info?: TimerType.fake | TimerType.noTimer | void): void => { 77 | const n = handler_stack.length 78 | clearTimeout_(timer) 79 | timer = TimerID.None 80 | opacity_ = 0; text = "" 81 | if (n && handler_stack[n - 1] === kHandler.onTopNormal) { 82 | hudShow(kTip.onTopNormal, currentKeys) 83 | } else if (!find_box && isHintsActive && !hintManager) { 84 | setHintMode(hintMode_) 85 | } else if (!find_box && visual_mode_name) { 86 | hudShow(kTip.inVisualMode, VTr(kTip.OFFSET_VISUAL_MODE + visual_mode_name), info) 87 | } else if (!find_box && insert_global_ && insert_global_.h) { 88 | hudShow(kTip.raw, insert_global_.h) 89 | } else if (passAsNormal) { 90 | esc!(HandlerResult.RefreshPassAsNormal) 91 | } else if (readonlyFocused_ > 0 && set_readonlyFocused_(raw_insert_lock ? 1 : 0) && !fgCache.h) { 92 | hudShow(kTip.readOnly) 93 | } 94 | else if (!box) { /* empty */ } 95 | else if ((OnChrome && Build.MinCVer < BrowserVer.MinNo$TimerType$$Fake ? info === TimerType.noTimer : info) 96 | || !isEnabled_) { 97 | toggleOpacity(0) 98 | $text.data = ""; 99 | toggleClass_s(box, "HL", 0) 100 | isEnabled_ && isLocked_ < Frames.Flags.lockedAndDisabled || adjustUI(2) 101 | } 102 | else if (!tweenId && isAlive_) { 103 | tweenId = interval_(tween, 40); 104 | tweenStart = getTime() 105 | } 106 | } 107 | 108 | export const toggleOpacity = (opacity: number): void => { 109 | dom_opacity_ = opacity 110 | box!.style.opacity = opacity < 1 ? opacity as number | string as string : "" 111 | setVisibility_s(box!, !!opacity) 112 | } 113 | -------------------------------------------------------------------------------- /content/marks.ts: -------------------------------------------------------------------------------- 1 | import { loc_, locHref, OnFirefox, timeout_ } from "../lib/utils" 2 | import { 3 | createElement_, dispatchEvent_, newEvent_, scrollingEl_, textContent_s, OnDocLoaded_ 4 | } from "../lib/dom_utils" 5 | import { post_, runFallbackKey } from "./port" 6 | import { hudHide, hudShow, hudTip, hud_tipTimer } from "./hud" 7 | import { removeHandler_, getMappedKey, isEscape_, replaceOrSuppressMost_, hasShift_ff } from "../lib/keyboard_utils" 8 | import { makeElementScrollBy_ } from "./scroller" 9 | 10 | // [1..9] 11 | let previous: MarksNS.ScrollInfo[] = [] 12 | 13 | export const dispatchMark = ((mark?: MarksNS.ScrollInfo | 0 | undefined, global?: boolean 14 | ): MarksNS.ScrollInfo | MarksNS.ScrollInfo | null => { 15 | let a = createElement_("a"), oldStr: string | undefined, newStr: string, match: string[], 16 | newMark: MarksNS.ScrollInfo | 0 | null | undefined 17 | mark && textContent_s(a, oldStr = mark + "") 18 | global || (a.dataset.local = "") 19 | newMark = !dispatchEvent_(window, newEvent_("vimiumMark", 0, 0, 0 20 | , { relatedTarget: a }, FocusEvent)) ? null 21 | : (newStr = textContent_s(a)) === oldStr ? mark 22 | : (match = newStr.split(",")).length > 1 ? ([~~match[0], ~~match[1]] as const 23 | ).concat(match.slice(2) as never) as [number, number, string] 24 | : mark 25 | return mark ? newMark as NonNullable | null : newMark || [scrollX | 0, scrollY | 0] 26 | }) as { 27 | (mark: MarksNS.ScrollInfo, global?: boolean): MarksNS.ScrollInfo | null 28 | (mark?: 0, global?: boolean): MarksNS.ScrollInfo // create: return `Writable` 29 | } 30 | 31 | export const setPreviousMarkPosition = (idx: number): void => { 32 | const arr = dispatchMark() 33 | arr.length === 2 && (arr as Writable).push(loc_.hash) 34 | previous[idx] = arr 35 | } 36 | 37 | export const activate = (options: CmdOptions[kFgCmd.marks], count: number): void => { 38 | hudShow(kTip.raw, options.t) 39 | replaceOrSuppressMost_(kHandler.marks, (event): HandlerResult => { 40 | let storage: typeof localStorage, local: boolean 41 | if (event.i === kKeyCode.ime) { return HandlerResult.Nothing } 42 | let keyChar = getMappedKey(event, kModeId.Marks) 43 | let tempPos: MarksNS.ScrollInfo | undefined 44 | if (keyChar.length !== 1 && !isEscape_(keyChar)) { 45 | return HandlerResult.Suppress 46 | } 47 | removeHandler_(kHandler.marks) 48 | keyChar < kChar.minNotNum && keyChar > kChar.maxNotNum && options.n && (count = +keyChar || 10, keyChar = "'") 49 | if (isEscape_(keyChar)) { 50 | hudHide() 51 | } else if ("`'".includes(keyChar)) { 52 | if (options.a) { 53 | setPreviousMarkPosition(count) 54 | } else { 55 | tempPos = previous[count] 56 | setPreviousMarkPosition(tempPos ? 1 : count) 57 | tempPos && scrollToMark(dispatchMark(tempPos)) 58 | } 59 | post_({ H: kFgReq.didLocalMarkTask, c: options, i: count, n: !tempPos }) 60 | } else { 61 | local = (OnFirefox ? hasShift_ff!(event.e) : event.e.shiftKey) !== options.s 62 | post_({ 63 | H: kFgReq.marks, c: options, l: local, k: event.i, n: keyChar, 64 | s: options.a ? dispatchMark(0, !local) : local && (storage = localStorage) 65 | ? storage.getItem(`vimiumMark|${locHref().split("#", 1)[0]}|${keyChar}`) : 0, 66 | u: locHref() 67 | }); 68 | timeout_((): void => { hud_tipTimer || hudHide() }, 100) 69 | } 70 | return HandlerResult.Prevent 71 | }) 72 | } 73 | 74 | export const scrollToMark = (scroll: MarksNS.ScrollInfo | null | undefined): void => { 75 | if (scroll) { 76 | if (scroll[1] === 0 && scroll[2] && scroll[0] === 0) { 77 | loc_.hash = scroll[2]; 78 | } else { 79 | makeElementScrollBy_(scrollingEl_(1), scroll[0] - scrollX, scroll[1] - scrollY) 80 | } 81 | } 82 | } 83 | 84 | export const goToMark_ = (options: CmdOptions[kFgCmd.goToMark]): void => { 85 | const cb = (): void => { 86 | options.t && setPreviousMarkPosition(1) 87 | scrollToMark(dispatchMark(options.s, options.g)) 88 | hudTip(kTip.raw, (options.g satisfies boolean as boolean | number as number + 1) as 1 | 2, options.t) 89 | runFallbackKey(options.f, 0) 90 | } 91 | options.w ? OnDocLoaded_(timeout_.bind(0, cb, options.w), 1) : cb() 92 | } 93 | -------------------------------------------------------------------------------- /content/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "outDir": "..", 5 | "rootDir": "..", 6 | "tsBuildInfoFile": "../scripts/.ts.content.build", 7 | }, 8 | "include": [ "*.ts", "../lib/*.ts" ], 9 | "exclude": [ "../lib/injector.ts", "../lib/simple_eval.ts" ], 10 | "extends": "../tsconfig.base.json" 11 | } 12 | -------------------------------------------------------------------------------- /front/offscreen.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /front/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "outDir": "..", 5 | "rootDir": "..", 6 | "tsBuildInfoFile": "../scripts/.ts.front.build", 7 | }, 8 | "include": [ "*.ts", "../lib/injector.ts", "../lib/simple_eval.ts" ], 9 | "extends": "../tsconfig.base.json" 10 | } 11 | -------------------------------------------------------------------------------- /front/vimium-c.css: -------------------------------------------------------------------------------- 1 | :host{border:none!important;contain:none!important;height:0!important;margin:0!important; 2 | opacity:1!important;outline:none!important;padding:0!important;position:static!important; 3 | transform:none!important;width:0!important;display:contents!important}:host:before,:host:after{display:none!important} 4 | .R{background:none;border:none;box-sizing:content-box;cursor:auto;float:none;font-size-adjust:none; 5 | letter-spacing:normal;line-height-step:0;margin:0;opacity:1;outline:none;padding:0;text-align-last:auto;text-indent:0; 6 | text-shadow:none;text-transform:none;visibility:visible;white-space:normal;word-spacing:normal; 7 | -webkit-writing-mode:horizontal-tb;all:initial;display:block;text-align:left;unicode-bidi:normal; 8 | color:#000;contain:layout style;direction:ltr;font:12px/1 "Helvetica Neue",Arial,sans-serif; 9 | pointer-events:none;position:fixed;user-select:none;z-index:2147483647} 10 | .HM{font-weight:bold;position:absolute;white-space:nowrap}.DLG::backdrop{background:#0000} 11 | .LH{background:linear-gradient(#fff785,#ffc542);border:0.01px solid #e3be23;border-radius:3px;box-shadow:0 3px 12 | 5px #0000004d;box-sizing:border-box;contain:layout style;overflow:hidden;padding:2.5px 3px 2px;position:absolute} 13 | .IH{background:#fff7854d;border:0.01px solid #c38a22;position:absolute}.IHS{background:#f664;border-color:#933} 14 | .HUD,.TEE{bottom:-1px;font-size:14px;height:20px;line-height:16px;max-width:336px;min-width:152px;overflow:hidden; 15 | padding:4px 4px 1px;right:152px;text-overflow:ellipsis;white-space:nowrap} 16 | .HUD:after{background:#eee;border-radius:4px 4px 0 0;border:0.01px solid #bbb;content:"";position:absolute;z-index:-1} 17 | .HL{max-width:60vw;right:-4px;padding-right:8px} 18 | .HUD.UI{cursor:text;height:24px}.Find{cursor:text;position:static;width:100%} 19 | .Flash{box-shadow:0 0 4px 2px #4183c4;padding:1px}.AbsF{padding:0;position:absolute}.Sel{box-shadow:0 0 4px 2px #fa0} 20 | .Frame{border:5px solid #ff0}.Frame,.DLG,.HUD:after{box-sizing:border-box;height:100%;left:0;top:0;width:100%} 21 | .Omnibar{left:max(10vw - 12px,50vw - 972px);height:520px;top:64px;width:min(80vw + 24px,1944px)} 22 | .O2{left:max(10% - 12px,50% - 972px);width:min(80% + 24px,1944px)} 23 | .BH{color:#902809}.MC,.MH{color:#d4ac3a}.One{border-color:#fa7}.UI,.DLG{color-scheme:light;pointer-events:all} 24 | .DLG{position:fixed}.PO{all:initial;left:0;position:fixed;top:0} 25 | .D>.LH{background:linear-gradient(#cb0,#c80)}.HUD.D{color:#ccc}.HUD.D:after{background:#222} 26 | @media(forced-colors:active){.R{border-radius:0} 27 | .HM>.LH,.HUD:after{background:#000;border-radius:0}.Flash{outline:4px solid #fff}} 28 | /*#find*/ 29 | ::selection { background: #ff9632 !important; } 30 | html, body, * { user-select: auto; } 31 | *{cursor:text;font:14px/16px "Helvetica Neue",Arial,sans-serif;margin:0;outline:none;white-space:pre} 32 | .r{all:initial;background:#fff;border-radius:3px 3px 0 0;box-shadow:inset 0 0 1.5px 1px #aaa;color:#000; 33 | cursor:text;display:flex;height:21px;padding:4px 4px 0}.r.D{background:#222;color:#d4d4d4} 34 | #s{flex:0 0 4px}#i{flex:0 1 auto;height:16px;min-width:9px;margin-left:2px;overflow:hidden;padding:0 2px 0 0} 35 | #h{flex:1 0 auto}br{all:inherit!important;display:inline!important}#c{flex:0 0 auto;margin-left:2px} 36 | #s::after{content:"/"}#c::after{content:attr(data-vimium);display:inline} 37 | :host,body{background:#0000!important;margin:0!important;height:24px} 38 | -------------------------------------------------------------------------------- /front/vomnibar-tee.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /front/words.txt: -------------------------------------------------------------------------------- 1 | ^[^]* 2 | [\w\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377 3 | \u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559 4 | \u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF 5 | \u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828 6 | \u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F 7 | \u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1 8 | \u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74 9 | \u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1 10 | \u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83 11 | \u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0 12 | \u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C 13 | \u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10 14 | \u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6 15 | \u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F 16 | \u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47 17 | \u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070 18 | \u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D 19 | \u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310 20 | \u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C 21 | \u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877 22 | \u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16 23 | \u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F 24 | \u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D 25 | \u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3 26 | \u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115 27 | \u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E 28 | \u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96 29 | \u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6 30 | \u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF 31 | \u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C 32 | \uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793 33 | \uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB 34 | \uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A 35 | \uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06 36 | \uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D 37 | \uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44 38 | \uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A 39 | \uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC] -------------------------------------------------------------------------------- /i18n/en/action.json: -------------------------------------------------------------------------------- 1 | { 2 | "colon": ":", 3 | "NS": " ", 4 | "115": "No changes", 5 | "115_2": "Save changes", 6 | "115_3": "Saved", 7 | "137": "becomes to ", 8 | "138": "only hook", 9 | "139": "exclude", 10 | "140": "becomes", 11 | "141": "keeps to ", 12 | "142": "will ", 13 | "143": "be", 14 | "143_2": "be", 15 | "144": "disabled", 16 | "145": "\u00a0enabled", 17 | "147": " (on this tab, $1 for once)", 18 | "148": " if reset", 19 | "asterisk": "asterisk/*", 20 | "Once": " for once", 21 | "failInLoading": "\u00a0(fail in loading)\u00a0", 22 | "imgCopied": "$1 image copied", 23 | "s1": "This is a snapshot image:", 24 | "Close": "", 25 | "none": "\u00a0(null)\u00a0", 26 | "loading": "loading\u2026" 27 | } 28 | -------------------------------------------------------------------------------- /i18n/en/background.json: -------------------------------------------------------------------------------- 1 | { 2 | "allLocal": "Local", 3 | "bookmarksRevoked": "No access to bookmarks - please grant it first", 4 | "cannotDelSug": "This item cannot be deleted", 5 | "changeItsCS": "change its content settings", 6 | "clickForMore": "Click here for more information.", 7 | "cmdConfirm": 8 | "You have asked Vimium C to perform $1 repeats of the command:\n $2\n\nAre you sure you want to continue?", 9 | "copiedWndInfo": "(info of tabs)", 10 | "csCleared": "$1 content settings have been cleared", 11 | "delSug": "Succeed to delete a $1", 12 | "disabledUrlToOpen": "Extensions can't open such URLs on Firefox", 13 | "discardFail": "Cannot discard the tab", 14 | "downloadFail": "Failed in downloading", 15 | "downloadTimeout": "Downloading took too long", 16 | "downloadBarClosed": "The download bar has been closed", 17 | "dupTab": "duplicate such a tab", 18 | "failCopyingImg": "Failed in copying image", 19 | "fhCleared": "$1find history has been cleared", 20 | "fullyDisabled": "disabled", 21 | "fullyEnabled": "enabled", 22 | "globalInsertMode": "Insert mode$1", 23 | "halfDisabled": "partially disabled", 24 | "harmfulURL": "The URL may be harmful to your computer !!!", 25 | "has": " has", 26 | "hasIncog": "This tab has been in an incognito window", 27 | "have": "s have", 28 | "imgCopied": "$1 image copied", 29 | "incog": "incognito ", 30 | "indexOOR": "The session index provided is out of range", 31 | "invalidImg": "The selected image URL is invalid", 32 | "jsFail": "Here's not allowed to eval JS scripts", 33 | "mBeginCreate": "Create mark…", 34 | "mBeginGoto": "Go to mark…", 35 | "mCreate": "Created", 36 | "mJumpTo": "Jumped to", 37 | "mLastMark": "last", 38 | "mLocalMarkTask": "$1 local mark [ $2 ]", 39 | "mNormalMarkTask": "$1 $2 mark : ' $3 '", 40 | "markRemoved": "$1 $2 mark$3 been removed", 41 | "moveAllTabs": "Nothing happens when moving every tab of this window", 42 | "mute": "$1 audible tab(s) are muted", 43 | "muted": "Muted", 44 | "needText": "\"showTip\" needs a \"text\" option", 45 | "needVal": "need value=… for option $1", 46 | "newStat": "The page status is $1 now", 47 | "noCopied": "No text copied!", 48 | "noDiscardIfOld": "Vimium C cannot discard tabs before Chromium $1", 49 | "noEngineFound": "No search engine found!", 50 | "noKw": "This command lacks an argument \"keyword\"", 51 | "noMark": "$1 mark not set : ' $2 '", 52 | "noMute": "Vimium C cannot control mute state before Chromium $1", 53 | "noQueryFound": "No query words recognized!", 54 | "noReader": "No reader view for this page", 55 | "noSelOrCopied": "No selected nor copied text found", 56 | "noSessionItem": "The closed session may be too old", 57 | "noStyleName": "No Vomnibar style name has been given", 58 | "noTabHistory": "Cannot open a history of this tab", 59 | "noTabItem": "The target tab has disappeared!", 60 | "notAllowA": "It's not allowed to $1", 61 | "notBool": "$1 is not a boolean switch", 62 | "notDelSug": "The $1 cannot be found!", 63 | "notFgOpt": "$1 is not a valid switch", 64 | "notModify": "modify #$1 option", 65 | "notRemoveCur": "The current tab should be kept", 66 | "notRestoreIfIncog": "Cannot restore a tab in incognito mode!", 67 | "openExtSrc": "visit HTML of an extension's page", 68 | "openIncog": "open this URL in incognito mode", 69 | "quoteA": "\"$1\"", 70 | "readClipboard": "read text in clipboard", 71 | "setFileCS": "set file CSs since Chromium $1", 72 | "setFolderCS": "set CS of file folders", 73 | "setFTPCS": "set FTP pages' content settings", 74 | "setIncogCS": "change incognito settings", 75 | "unknownA": "unknown option name $1", 76 | "unknownCS": "Unknown content settings type: $1", 77 | "unlikeVal": "value of $1 should be like $2", 78 | "unmute": "$1 audible tab(s) are unmuted", 79 | "unmuted": "Unmuted", 80 | "useVal": "Now $1 uses $2", 81 | "turnOn": "Now $1 is on", 82 | "turnOff": "$1 has been turned off", 83 | "Upgrade": "Upgrade", 84 | "upgradeMsg": "Vimium C has been upgraded to v$1.\n", 85 | "upgradeMsg2": "Réparer cela pourrait casser des sites Web comme IEEE." 86 | } 87 | -------------------------------------------------------------------------------- /i18n/en/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "comma": ",", 3 | "NS": " ", 4 | "win": "Windows", 5 | "mac": "macOS", 6 | "based": " based", 7 | "3_2": "(Sorted)", 8 | "115": "No changes", 9 | "115_2": "Save changes", 10 | "115_3": "Saved", 11 | "144": "disabled", 12 | "145_2": "enabled", 13 | "49_2": "Ignore if not in ASCII", 14 | "optOfChromeUrl": " (need #extensions-on-chrome-urls)", 15 | "optNeedChromeUrlFirst": "Please enable the \"#extensions-on-chrome-urls\" browser flag first.", 16 | "opt_closeShelf": "Close the download bar and allow an alternative API to download files", 17 | "opt_bookmarks": "Search in bookmarks or add new items", 18 | "opt_downloads": "Let browser download files", 19 | "opt_chromeUrl": "Run on chrome://*/* pages", 20 | "opt_cNewtab": "Run on Chrome's native New Tab Page", 21 | "opt_contentSettings": "Modify content settings of websites like images and JavaScript", 22 | "opt_cookies": "cookies: only for contextual info to work with Multi-Account Containers", 23 | "opt_clipboardRead": "Read text in system clipboard (only during related commands)", 24 | "rec_perm": " [recommended]", 25 | "dirtyOptions": 26 | "Such options have been changed at other places:\n * $1\n\nContinue to save and override these changes?", 27 | "clickToUnmask": "# Click to unmask the content…", 28 | "beforeUnload": "You have unsaved changes to options.", 29 | "ignoredNonEN": "Keyboard layout has been ignored! Please consider disabling the option.", 30 | "onlyExtVomnibar": "Extension vomnibar pages can only work before Chromium $1.", 31 | "fileVomnibar": "A file vomnibar is limited by browser to only work on file:///* pages.", 32 | "httpVomnibar": "An HTTP vomnibar is limited by browser and doesn't work on HTTPS pages.", 33 | "requireChromium": " (need Chromium $1+)", 34 | "beforeChromium": "before Chromium $1", 35 | "lackPermission": "for lacking permission $1", 36 | "invalidOption": "This feature can not work $1", 37 | "invalidCss": "Found invalid CSS selectors:", 38 | "manageShortcut": "\"Manage Shortcuts\" in \"Tools Menu\" of ", 39 | "addons": "Firefox Add-ons", 40 | "webstore": "Chrome Web Store", 41 | "edgestore": "MS Edge Add-ons", 42 | "NewTabAdapter": "NewTab Adapter", 43 | "refusedURLs": "The URL \"$1\" is restricted to be opened by a WebExtension from Firefox browser.", 44 | "nonPlainURL": "The value \"$1\" is not a valid plain URL.", 45 | "haveToOpenManually": "On Firefox you have to visit this URL manually", 46 | "changedBeforeSync": 47 | " Error:\nSorry, but you're enabling the \"Sync settings\" option\n while some other options are also modified.\nPlease only perform one action at a time!" 48 | , 49 | "warningForSync": 50 | " Warning:\nthe current settings will be OVERRIDDEN the next time Vimium C starts!\nPlease back up your settings using the \"Export Settings\" button\n!!! RIGHT NOW !!!" 51 | , 52 | "confirmImport": 53 | "You are loading $1:\n * from $2Vimium C$3\n * for $4\n * exported $5\n\nAre you sure you want to continue?" 54 | , 55 | "backupFile": "a settings copy", 56 | "recommendedFile": "the recommended settings", 57 | "fileVCVer": "version * of ", 58 | "fileVCNewer": " (newer)", 59 | "filePlatform": "the $1 platform", 60 | "commonPlatform": "common platforms", 61 | "atTime": "at $1", 62 | "before": "before", 63 | "keepSyncing": 64 | "Do you want to keep settings synchronized with your current browser account?\n\nPress \"Cancel\" to stop it.", 65 | "notJSON": "No JSON data found!", 66 | "JSONParseError": 67 | "Sorry, Vimium C cannot parse the JSON file:\n an unexpected character at line $1, column $2", 68 | "notVCJSON": "Sorry, no Vimium C settings data found!", 69 | "exc": "Error: ", 70 | "unknown": "(unknown)", 71 | "JSONTooLarge": 72 | " Fatal Error:\n\nYour settings file \"$1\" seems too large!\n\nAs limited by your browser, the max size is only $2 KB.", 73 | "cancelImport": "You cancelled importing", 74 | "importOK": "Import settings data: OK!" 75 | } 76 | -------------------------------------------------------------------------------- /i18n/fr/action.json: -------------------------------------------------------------------------------- 1 | { 2 | "colon": ":", 3 | "NS": " ", 4 | "115": "Aucun changement", 5 | "115_2": "Sauvegarder", 6 | "115_3": "A sauvegardé", 7 | "137": "commence à ", 8 | "138": "s'enclenche seulement", 9 | "139": "exclue ", 10 | "140": "devient ", 11 | "141": "garde ", 12 | "142": "va ", 13 | "143": "être", 14 | "143_2": "être", 15 | "144": "désactivé(es)", 16 | "145": "activé(es)", 17 | "147": " (sur cet onglet, $1 une fois)", 18 | "148": " si remis à zéro", 19 | "asterisk": "Astérisque/*", 20 | "imgCopied": "A copié l'image $1", 21 | "Disable": "Désactiver", 22 | "Enable": "Activer", 23 | "Once": " une fois" 24 | } 25 | -------------------------------------------------------------------------------- /i18n/fr/background.json: -------------------------------------------------------------------------------- 1 | { 2 | "All": "Tous les", 3 | "allLocal": "Locale", 4 | "bookmarksRevoked": "Pas d'accès aux favoris - veuillez d'abord l'accorder", 5 | "cannotDelSug": "Cet élément ne peut être supprimé", 6 | "changeItsCS": "changez ses paramètres de contenu", 7 | "clickForMore": "Cliquez ici pour plus d'informations.", 8 | "cmdConfirm": "Vous avez demandé à Vimium C d'effectuer $1 répétitions de la commande:\n $2\n\nÊtes-vous certain de vouloir continuer ?", 9 | "copiedWndInfo": "(info des onglets)", 10 | "csCleared": "Les paramètres du contenu $1 ont été effacés", 11 | "delSug": "A réussi à supprimer un $1", 12 | "disabledUrlToOpen": "Firefox n'autorise pas l'extension à ouvrir cette URL", 13 | "discardFail": "Ne peut pas décharger l'onglet", 14 | "downloadFail": "Échec du téléchargement", 15 | "downloadTimeout": "Le téléchargement a pris trop de temps", 16 | "downloadBarClosed": "La barre de téléchargement a été fermée", 17 | "dupTab": "duplique un tel onglet", 18 | "failCopyingImg": "Échec de la copie de l'image", 19 | "fhCleared": "$1 historique de recherche nettoyé", 20 | "fullyDisabled": "désactivée", 21 | "fullyEnabled": "activé(es)", 22 | "Global": "globale", 23 | "globalInsertMode": "Mode insertion$1", 24 | "halfDisabled": "partiellement désactivé(es)", 25 | "harmfulURL": "L'URL peut être dangereuse pour votre ordinateur", 26 | "has": " a", 27 | "hasIncog": "Cet onglet provient d'une fenêtre incognito", 28 | "have": "s ont", 29 | "imgCopied": "A copié l'image $1", 30 | "incog": "incognito ", 31 | "indexOOR": "L'index de session fourni est hors de portée", 32 | "invalidImg": "L'URL de l'image sélectionnée est invalide", 33 | "jsFail": "Ici, il n'est pas permis d'évaluer les scripts JS", 34 | "Local": "locale", 35 | "mBeginCreate": "Crée un marqueur…", 36 | "mBeginGoto": "Va au marqueur…", 37 | "mCreate": "Créé", 38 | "mJumpTo": "Sauté vers", 39 | "mLastMark": "dernier", 40 | "mLocalMarkTask": "$1 local marque [ $2 ]", 41 | "mNormalMarkTask": "$1 $2 marque : ' $3 '", 42 | "markRemoved": "$1 marques $2$3 été supprimé", 43 | "moveAllTabs": "Rien ne se passe en déplacant les onglets de cette fenêtre", 44 | "mute": "$1 onglet(s) sont mis en sourdine", 45 | "muted": "Mis en sourdine", 46 | "needText": "\"showTip\" a besoin d'une option \"texte\"", 47 | "needVal": "besoin de valeur=... pour l'option $1", 48 | "newStat": "Désormais le statut de la page est $1", 49 | "noCopied": "Aucun texte copié !", 50 | "noDiscardIfOld": "Vimium C ne peut pas décharger les onglets avant Chromium $1", 51 | "noEngineFind": "Aucun moteur de recherche trouvé !", 52 | "noKw": "Cette commande manque l'argument \"mot-clé\"", 53 | "noMark": "$1 marque non-défini : ' $2 '", 54 | "noMute": "Vimium C ne peut pas contrôler l'état de sourdine avant Chromium $1", 55 | "noQueryFound": "Aucun mot de requête reconnu!", 56 | "noReader": "Pas de vue lecteur pour cette page", 57 | "noSelOrCopied": "Aucun texte sélectionné ou copié trouvé", 58 | "noSessionItem": "La session fermée semble être trop ancienne", 59 | "noStyleName": "Aucun nom de style de Vomnibar n'est donné", 60 | "noTabHistory": "Ne peut pas ouvrir un historique de cet onglet", 61 | "noTabItem": "L'onglet cible a disparu !", 62 | "notAllowA": "Il est interdit de $1", 63 | "notBool": "$1 n'est pas un commutateur booléen", 64 | "notDelSug": "Le $1 n'est pas trouvé !", 65 | "notFgOpt": "$1 n'est pas un changement valide", 66 | "notModify": "modifie #$1 option", 67 | "notRemoveCur": "L'onglet courant devrait être conservé", 68 | "notRestoreIfIncog": "Ne peut pas restaurer un onglet de mode incognito !", 69 | "OpenC": "Ouvrir: ", 70 | "openExtSrc": "ouvre le code-source HTML d'une page d'extension", 71 | "openIncog": "ouvre l'URL en mode incognito", 72 | "Other": "Autres", 73 | "quoteA": "\"$1\"", 74 | "readClipboard": "lire le texte dans le presse-papiers", 75 | "setFileCS": "spécifie les feuilles de style du fichier depuis Chromium $1", 76 | "setFolderCS": "spécifie la feuille de style des dossiers de fichiers", 77 | "setFTPCS": "spécifie les paramètres de contenu des pages de FTP", 78 | "setIncogCS": "changer les paramètres incognito", 79 | "unknownA": "nom d'option inconnu $1", 80 | "unknownCS": "Paramètres de contenu inconnus: $1", 81 | "unlikeVal": "la valeur de $1 devrait être comme $2", 82 | "unmute": "$1 onglet(s) ont leurs sourdines désactivées", 83 | "unmuted": "Sourdine désactivée", 84 | "useVal": "Désormais $1 utilise $2", 85 | "turnOn": "Désormais $1 est activé", 86 | "turnOff": "$1 a été désactivée", 87 | "Upgrade": "Mise à jour", 88 | "upgradeMsg": "Vimium C a été mise à jour vers la v$1.\n", 89 | "upgradeMsg2": "Basé sur les API Manifest V3." 90 | } -------------------------------------------------------------------------------- /i18n/fr/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "dirtyOptions": 3 | "De telles options ont été changées à d'autres endroits:\n * $1\n\nContinuer à sauvegarder et écraser avec ces modifications ?" 4 | , 5 | "clickToUnmask": "# Cliquez pour retirer le masque du contenu…", 6 | "beforeUnload": "Vous avez des modifications non-sauvegardées dans les options", 7 | "ignoredNonEN": "La configuration Clavier a été ignorée ! Merci de considérer de désactiver l'option.", 8 | "onlyExtVomnibar": "Seules les pages d'extension de Vomnibar peuvent fonctionner avant Chromium $1", 9 | "fileVomnibar" : 10 | "Une page de fichier de Vomnibar est limitée par le navigateur pour fonctionner uniquement sur file:///* pages.", 11 | "httpVomnibar" : 12 | "Une page HTTP de Vomnibar est limitée par le navigateur et ne fonctionne pas sur les pages HTTPS.", 13 | "beforeChromium": "sur navigateurs Chromium avant v$1", 14 | "lackPermission": "pour manquer de permissions $1", 15 | "invalidOption": "Cette fonctionnalité ne peut pas fonctionner $1", 16 | "invalidCss": "Sélecteurs CSS non valides trouvés:", 17 | "manageShortcut": "\"Paramétrer les raccourcis\" dans \"le menu Outils\" de ", 18 | "addons": "Extensions Firefox", 19 | "webstore": "Chrome Web Store", 20 | "edgestore": "Extensions MS Edge", 21 | "NewTabAdapter": "NewTab Adapter (Adaptateur de nouvel onglet)", 22 | "refusedURLs": 23 | "L'ouverture de l'URL \"$1\" par une WebExtension est refusée par le navigateur Firefox.", 24 | "nonPlainURL": "La valeur \"$1\" n'est pas une URL ordinaire valide", 25 | "haveToOpenManually": "Sur Firefox vous devez visiter cette URL manuellement", 26 | "changedBeforeSync": 27 | " Erreur:\nDésolé, mais vous êtes en train d'activer les options de \"Sync\"\n tandis que d'autres options sont également modifiées.\nMerci d'effectuer une action à la fois!" 28 | , 29 | "warningForSync": 30 | " Attention:\nles paramètres courants seront RÉÉCRIS par la prochaine que Vimium C démarre !\nMerci de sauvegarder vos paramètres en utilisant le bouton \"Exporter les paramètres\"\n!!! MAINTENANT !!!" 31 | , 32 | "confirmImport": 33 | "Vous êtes en train de charger $1:\n * depuis $2Vimium C$3\n * pour $4\n * exporté $5\n\nÊtes-vous sûr de vouloir continuer ?" 34 | , 35 | "backupFile": "une copie des paramètres", 36 | "recommendedFile": "les paramètres recommandés", 37 | "fileVCVer": "version * de ", 38 | "fileVCNewer": " (plus récent(e))", 39 | "filePlatform": "la plateforme $1", 40 | "commonPlatform": "les plateformes communes", 41 | "atTime": "à $1", 42 | "before": "avant", 43 | "keepSyncing": 44 | "Voulez-vous conserver les paramètres synchronisés avec votre compte de navigateur courant ?\n\nAppuyez sur \"Annuler\" pour arrêter." 45 | , 46 | "notJSON": "Aucune donnée JSON trouvée !", 47 | "JSONParseError": 48 | "Désolé, Vimium C ne peut analyser le fichier JSON :\n un caractère inattendu à la ligne $1, colonne $2", 49 | "notVCJSON": "Désolé, pas de données de paramètres Vimium C trouvées !", 50 | "exc": "Erreur: ", 51 | "unknown": "(inconnu(es))", 52 | "JSONTooLarge": 53 | " Erreur Fatale :\n\nVotre fichier de paramètres \"$1\" semble trop grand !\n\nTel que limité par votre navigateur, la taille maximale est de seulement $2 Ko.", 54 | "cancelImport": "Vous avez annulé l'importation", 55 | "importOK": "Import des données de paramétrage : OK!" 56 | } 57 | -------------------------------------------------------------------------------- /i18n/zh/action.json: -------------------------------------------------------------------------------- 1 | { 2 | "period": "。", 3 | "colon": ":", 4 | "NS": "", 5 | "2": "增加规则", 6 | "115": "无需保存", 7 | "115_2": "保存", 8 | "115_3": "已保存", 9 | "120": "停用一次", 10 | "121": "重置", 11 | "121_2": "更多配置", 12 | "122": "上表是适用于当前网页的规则。", 13 | "123": "可以保存修改并关闭此对话框。", 14 | "125": " ", 15 | "126": "尚未运行", 16 | "127": "在当前页面上", 17 | "127_2": "此页面上运行了支持 Vimium C 的扩展程序,但是它并不在受信任的扩展列表中。", 18 | "127_3": "信任此扩展", 19 | "128": "目前,浏览器为了保护您的隐私和运行安全,会禁止 Vimium 和 Vimium C 在某些页面上运行,比如浏览器设置页面。", 20 | "129": "因此除非未来浏览器主动放宽限制,否则您将始终不能使Vimium C(或其它任何普通的扩展程序)在这类页面上运行起来。", 21 | "129_2": "另外", 22 | "129_3": 23 | "浏览器特意增加了一项安全配置,默认阻止 Vimium C 等扩展在搜索引擎的结果页上运行。您可以前往 Opera 的扩展管理页面手动启用“允许访问搜索页面”。", 24 | "130": "如果您刚刚安装了 ", 25 | "131": ",那么请", 26 | "132": "刷新", 27 | "133": "当前页面试试。", 28 | "133_2": "重试", 29 | "134": "更多配置详见 ", 30 | "135": "Vimium C 设置", 31 | "136": " 页面。", 32 | "137": "现在", 33 | "138": "仅识别", 34 | "139": "不识别", 35 | "140": "现在处于", 36 | "141": "继续", 37 | "142": "将会", 38 | "143": "保持", 39 | "143_2": "转为", 40 | "143_3": "完全", 41 | "144": "停用", 42 | "145": "启用", 43 | "147": "(当前网页处于 临时$1 状态)", 44 | "148": "(保存时自动取消状态锁定)", 45 | "Patterns": "网址匹配规则", 46 | "Keys": "按键列表", 47 | "Remove": "删除", 48 | "onlyHook": "仅这些按键生效", 49 | "passThrough": "仅这些按键被停用(不组成快捷键)", 50 | "completelyDisabled": "完全停止工作", 51 | "asterisk": "星号*", 52 | "VerIs": "版本:", 53 | "Disable": "停用", 54 | "Enable": "启用", 55 | "Once": "一次", 56 | "failInLoading": "(图片加载失败)", 57 | "imgCopied": "已复制 $1 图片", 58 | "s1": "以下是截图:", 59 | "Close": "关闭", 60 | "none": "(空)", 61 | "loading": "正在加载……", 62 | "t_math": "计算器", 63 | "t_copy": "复制", 64 | "t_search": "搜索", 65 | "t_ERROR": "错误" 66 | } 67 | -------------------------------------------------------------------------------- /i18n/zh/background.json: -------------------------------------------------------------------------------- 1 | { 2 | "All": "所有", 3 | "allLocal": "各个网站下的", 4 | "bookmarksRevoked": "未能读取收藏列表,需要授予“收藏夹”权限", 5 | "cannotDelSug": "不可删除此条搜索建议", 6 | "changeItsCS": "改变此网页的功能权限", 7 | "clickForMore": "点击阅读详细信息。", 8 | "cmdConfirm": "您希望 Vimium C 执行 $1 次如下命令:\n $2\n\n是否确认继续?", 9 | "colon": ":", 10 | "copiedWndInfo": "(标签页信息)", 11 | "csCleared": "对各网站 $1 功能权限的修改已重置", 12 | "delSug": "成功删除所选$1", 13 | "disabledUrlToOpen": "Firefox 不允许扩展打开此网址", 14 | "discardFail": "无法丢弃此网页", 15 | "downloadFail": "无法下载此对象", 16 | "downloadTimeout": "对象下载超时", 17 | "downloadBarClosed": "下载栏已隐藏", 18 | "dupTab": "复制这个标签页", 19 | "failCopyingImg": "无法复制图片到剪贴板", 20 | "fhCleared": "页内查找历史已经清空", 21 | "fullyDisabled": "完全停用", 22 | "fullyEnabled": "全部生效", 23 | "Global": "全局", 24 | "globalInsertMode": "Vimium C 已暂停$1", 25 | "halfDisabled": "部分停用", 26 | "harmfulURL": "发现网址可能危害您的电脑!!!", 27 | "has": "已经", 28 | "hasIncog": "此标签页已经在无痕窗口中了", 29 | "have": "已经", 30 | "Images": "图片", 31 | "imgCopied": "已复制 $1 图片", 32 | "incog": "无痕窗口的", 33 | "indexOOR": "标签页序号超出了范围", 34 | "invalidImg": "所选网址不是一个有效的图片", 35 | "jsFail": "抱歉,无法在当前网页上执行 JS 脚本", 36 | "Local": "当前网页下的", 37 | "mBeginCreate": "将要创建标记……", 38 | "mBeginGoto": "将要跳转到……", 39 | "mCreate": "创建了", 40 | "mJumpTo": "跳转到", 41 | "mLastMark": "默认", 42 | "mLocalMarkTask": "$1 #$2 位置记录", 43 | "mNormalMarkTask": "$1 “$2标记:$3 ”", 44 | "markRemoved": "$3清理了$1个$2标记", 45 | "moveAllTabs": "不应该移动一个窗口的所有标签页", 46 | "mute": "已为$1标签页设置了静音", 47 | "muted": "已静音", 48 | "needText": "需要“text”参数为提示文字", 49 | "needVal": "需要为$1指定“value”参数", 50 | "newStat": "Vimium C 快捷键 $1", 51 | "noCopied": "剪贴板中没有文字", 52 | "noDiscardIfOld": "在 Chromium $1 之前扩展不能丢弃网页", 53 | "noEngineFound": "未能识别所属的搜索引擎", 54 | "noKw": "需要参数“keyword”为某个已定义的搜索引擎", 55 | "noMark": "$1标记中不存在“ $2 ”", 56 | "noMute": "在 Chromium $1 之前扩展程序不能设置静音", 57 | "noQueryFound": "未能识别搜索词", 58 | "noReader": "此网页无法进入阅读模式", 59 | "noSelOrCopied": "没有选中文字,剪贴板中也没有文字", 60 | "noSessionItem": "想要恢复的标签页已经关闭太久了", 61 | "noStyleName": "需要用“style”参数指定样式名称", 62 | "noTabHistory": "无法获取此网页的访问历史", 63 | "noTabItem": "目标标签页不存在", 64 | "notAllowA": "Vimium C 无法$1", 65 | "notBool": "$1不是开关类型的", 66 | "notDelSug": "未找到指定的$1", 67 | "notFgOpt": "$1不是合适的配置项", 68 | "notModify": "修改“$1”选项", 69 | "notRemoveCur": "不应该以这种方式关闭当前网页", 70 | "notRestoreIfIncog": "无法恢复已关闭的无痕标签页!", 71 | "NS": "", 72 | "OpenC": "访问:", 73 | "openExtSrc": "显示一个扩展程序网页的源代码", 74 | "openIncog": "在无痕模式下打开此网页", 75 | "Other": "其他", 76 | "quoteA": "“$1”", 77 | "readClipboard": "读取剪贴板文字", 78 | "setFileCS": "在 Chromium $1+ 设置本地文件网页的功能权限", 79 | "setFolderCS": "设置本地文件夹网页的功能权限", 80 | "setFTPCS": "设置 FTP 网页的功能权限", 81 | "setIncogCS": "设置无痕模式下网页的功能权限", 82 | "sugs": "t=标签页 h=历史记录条目 s=最近关闭的标签页", 83 | "unknownA": "$1不是一个配置项", 84 | "unknownCS": "未知的网站功能类型:$1", 85 | "unlikeVal": "$1的值应当类似于“$2”", 86 | "unmute": "已取消$1标签页的静音状态", 87 | "unmuted": "已取消静音", 88 | "useVal": "临时设置 $1 为 $2", 89 | "turnOn": "已临时启用 $1", 90 | "turnOff": "已临时停用 $1", 91 | "Upgrade": "已更新", 92 | "upgradeMsg": "您的 Vimium C 已经更新至 $1 版。\n", 93 | "upgradeMsg2": "修复了可能影响 IEEE 等网站显示内容的错误。" 94 | } 95 | -------------------------------------------------------------------------------- /i18n/zh_TW/action.json: -------------------------------------------------------------------------------- 1 | { 2 | "period": "。", 3 | "colon": ":", 4 | "NS": "", 5 | "2": "增加規則", 6 | "115": "無需保存", 7 | "115_2": "保存", 8 | "115_3": "已保存", 9 | "120": "停用一次", 10 | "121": "重置", 11 | "121_2": "更多設定", 12 | "122": "上表是適用於當前網頁的規則。", 13 | "123": "可以保存修改並關閉此對話框。", 14 | "125": " ", 15 | "126": "尚未運行", 16 | "127": "在當前頁面上", 17 | "127_2": "此頁面上運行了支持 Vimium C 的擴充程式,但是它並不在受信任的擴充列表中。", 18 | "127_3": "信任此擴充", 19 | "128": "目前,瀏覽器為了保護您的隱私和運行安全,會禁止 Vimium 和 Vimium C 在某些頁面上運行,比如瀏覽器設定頁面。", 20 | "129": "因此除非未來瀏覽器主動放寬限制,否則您將始終不能使 Vimium C(或其它任何普通的擴充程式)在這類頁面上運行起來。", 21 | "129_2": "另外", 22 | "129_3": "瀏覽器特意增加了一項安全設定,默認阻止 Vimium C 等擴充在搜尋引擎的結果頁上運行。您可以前往 Opera 的擴充管理頁面手動啟用“允許訪問搜尋頁面”。", 23 | "130": "如果您剛剛安裝了 ", 24 | "131": ",那麼請", 25 | "132": "刷新", 26 | "133": "當前頁面試試。", 27 | "133_2": "重試", 28 | "134": "更多設定詳見 ", 29 | "135": "Vimium C 設定", 30 | "136": " 頁面。", 31 | "137": "現在", 32 | "138": "僅識別", 33 | "139": "不識別", 34 | "140": "現在處於", 35 | "141": "繼續", 36 | "142": "將會", 37 | "143": "保持", 38 | "143_2": "轉為", 39 | "143_3": "完全", 40 | "144": "停用", 41 | "145": "啟用", 42 | "147": "(當前網頁處於 臨時$1 狀態)", 43 | "148": "(保存時自動取消狀態鎖定)", 44 | "Patterns": "網址匹配規則", 45 | "Keys": "按鍵列表", 46 | "Remove": "刪除", 47 | "onlyHook": "僅這些按鍵生效", 48 | "passThrough": "僅這些按鍵被停用(不組成快速鍵)", 49 | "completelyDisabled": "完全停止工作", 50 | "asterisk": "星號*", 51 | "VerIs": "版本:", 52 | "Disable": "停用", 53 | "Enable": "啟用", 54 | "Once": "一次", 55 | "failInLoading": "(圖片加載失敗)", 56 | "imgCopied": "已複製 $1 圖片", 57 | "s1": "以下是截圖:", 58 | "Close": "關閉", 59 | "none": "(空)", 60 | "loading": "正在加載……", 61 | "t_math": "計算器", 62 | "t_copy": "複製", 63 | "t_search": "搜尋", 64 | "t_ERROR": "錯誤" 65 | } 66 | -------------------------------------------------------------------------------- /i18n/zh_TW/background.json: -------------------------------------------------------------------------------- 1 | { 2 | "All": "所有", 3 | "allLocal": "各個網站下的", 4 | "bookmarksRevoked": "未能讀取書籤列表,需要授予“書籤”權限", 5 | "cannotDelSug": "不可刪除此條搜索建議", 6 | "changeItsCS": "改變此網頁的功能權限", 7 | "clickForMore": "點擊閱讀詳細信息。", 8 | "cmdConfirm": "您希望 Vimium C 執行 $1 次如下命令:\n $2\n\n是否確認繼續?", 9 | "colon": ":", 10 | "copiedWndInfo": "(分頁信息)", 11 | "csCleared": "對各網站 $1 功能權限的修改已重置", 12 | "delSug": "成功刪除所選$1", 13 | "disabledUrlToOpen": "Firefox 不允許擴展存取此網址", 14 | "discardFail": "無法丟棄此網頁", 15 | "downloadFail": "無法下載此對象", 16 | "downloadTimeout": "對象下載超時", 17 | "downloadBarClosed": "下載欄已隱藏", 18 | "dupTab": "複製這個分頁", 19 | "failCopyingImg": "無法複製圖片到剪貼簿", 20 | "fhCleared": "頁內查找歷史已經清空", 21 | "fullyDisabled": "完全停用", 22 | "fullyEnabled": "全部生效", 23 | "Global": "全局", 24 | "globalInsertMode": "Vimium C 已暫停$1", 25 | "halfDisabled": "部分停用", 26 | "harmfulURL": "發現網址可能危害您的電腦!!!", 27 | "has": "已經", 28 | "hasIncog": "此分頁已經在無痕視窗中了", 29 | "have": "已經", 30 | "Images": "圖片", 31 | "imgCopied": "已複製 $1 圖片", 32 | "incog": "無痕視窗的", 33 | "indexOOR": "分頁序號超出了範圍", 34 | "invalidImg": "所選網址不是一個有效的圖片", 35 | "jsFail": "抱歉,無法在當前網頁上執行 JS 腳本", 36 | "Local": "當前網頁下的", 37 | "mBeginCreate": "將要創建標記……", 38 | "mBeginGoto": "將要跳轉到……", 39 | "mCreate": "創建了", 40 | "mJumpTo": "跳轉到", 41 | "mLastMark": "默認", 42 | "mLocalMarkTask": "$1 #$2 位置記錄", 43 | "mNormalMarkTask": "$1 “$2標記:$3 ”", 44 | "markRemoved": "$3清理了$1個$2標記", 45 | "moveAllTabs": "不應該移動一個視窗的所有分頁", 46 | "mute": "已為$1分頁設置了靜音", 47 | "muted": "已靜音", 48 | "needText": "需要“text”參數為提示文字", 49 | "needVal": "需要為$1指定“value”參數", 50 | "newStat": "Vimium C 快速鍵 $1", 51 | "noCopied": "剪貼簿中沒有文字", 52 | "noDiscardIfOld": "在 Chromium $1 之前擴展不能丟棄網頁", 53 | "noEngineFound": "未能識別所屬的搜索引擎", 54 | "noKw": "需要參數“keyword”為某個已定義的搜索引擎", 55 | "noMark": "$1標記中不存在“ $2 ”", 56 | "noMute": "在 Chromium $1 之前擴展程序不能設置靜音", 57 | "noQueryFound": "未能識別搜索詞", 58 | "noReader": "此網頁無法進入閱讀模式", 59 | "noSelOrCopied": "沒有選中文字,剪貼簿中也沒有文字", 60 | "noSessionItem": "想要恢復的分頁已經關閉太久了", 61 | "noStyleName": "需要用“style”參數指定樣式名稱", 62 | "noTabHistory": "無法獲取此網頁的訪問歷史", 63 | "noTabItem": "目標分頁不存在", 64 | "notAllowA": "Vimium C 無法$1", 65 | "notBool": "$1不是開關類型的", 66 | "notDelSug": "未找到指定的$1", 67 | "notFgOpt": "$1不是合適的設定項", 68 | "notModify": "修改“$1”選項", 69 | "notRemoveCur": "不應該以這種方式關閉當前網頁", 70 | "notRestoreIfIncog": "無法恢復已關閉的無痕分頁!", 71 | "NS": "", 72 | "OpenC": "訪問:", 73 | "openExtSrc": "顯示一個擴展程序網頁的原始碼", 74 | "openIncog": "在無痕模式下打開此網頁", 75 | "Other": "其他", 76 | "quoteA": "“$1”", 77 | "readClipboard": "讀取剪貼簿文字", 78 | "setFileCS": "在 Chromium $1+ 設置本地文件網頁的功能權限", 79 | "setFolderCS": "設置本地文件夾網頁的功能權限", 80 | "setFTPCS": "設置 FTP 網頁的功能權限", 81 | "setIncogCS": "設置無痕模式下網頁的功能權限", 82 | "sugs": "t=分頁 h=歷史記錄條目 s=最近關閉的分頁", 83 | "unknownA": "$1不是一個設定項", 84 | "unknownCS": "未知的網站功能類型:$1", 85 | "unlikeVal": "$1的值應當類似於“$2”", 86 | "unmute": "已取消$1分頁的靜音狀態", 87 | "unmuted": "已取消靜音", 88 | "useVal": "臨時設置 $1 為 $2", 89 | "turnOn": "已臨時啟用 $1", 90 | "turnOff": "已臨時停用 $1", 91 | "Upgrade": "已更新", 92 | "upgradeMsg": "您的 Vimium C 已經更新至 $1 版。\n", 93 | "upgradeMsg2": "修正了可能影響 IEEE 等網站顯示內容的錯誤。" 94 | } 95 | -------------------------------------------------------------------------------- /icons/.nomedia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/.nomedia -------------------------------------------------------------------------------- /icons/disabled_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/disabled_19.png -------------------------------------------------------------------------------- /icons/disabled_38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/disabled_38.png -------------------------------------------------------------------------------- /icons/enabled_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/enabled_19.png -------------------------------------------------------------------------------- /icons/enabled_38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/enabled_38.png -------------------------------------------------------------------------------- /icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/icon128.png -------------------------------------------------------------------------------- /icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/icon16.png -------------------------------------------------------------------------------- /icons/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/icon32.png -------------------------------------------------------------------------------- /icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/icon48.png -------------------------------------------------------------------------------- /icons/partial_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/partial_19.png -------------------------------------------------------------------------------- /icons/partial_38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gdh1995/vimium-c/31885269460a3f0635c7dd69176ed387ad138e96/icons/partial_38.png -------------------------------------------------------------------------------- /lib/base.omni.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare namespace VomnibarNS { 3 | interface BaseFgOptions extends Pick { 4 | w: [hostInnerWidth: number, hostInnerHeight: number, devicePixelRatio: number] 5 | p: "" | FgRes[kFgReq.parseSearchUrl]; 6 | } 7 | interface FgOptions extends BaseFgOptions, Partial { 8 | url?: string | null; 9 | } 10 | type MessageData = [id: "VimiumC", secret: string, options: FgOptions | null] 11 | interface Msg { N: T } 12 | 13 | const enum kCReq { 14 | activate, hide, focus, 15 | _mask = "", 16 | } 17 | const enum kFReq { 18 | hide, focus, style, iframeIsAlive, hud, 19 | evalJS, scroll, scrollGoing, stopScroll, broken, 20 | unload, scaled_old_cr, 21 | _mask = "", 22 | } 23 | interface CReq { 24 | [kCReq.activate]: FgOptions & Msg; 25 | [kCReq.hide]: kCReq.hide; 26 | [kCReq.focus]: kCReq.focus; 27 | } 28 | interface FReq { 29 | [kFReq.hide]: { 30 | }; 31 | [kFReq.scroll]: { 32 | /** key */ k: string; 33 | /** keybody */ b: kChar; 34 | }; 35 | [kFReq.style]: { 36 | // unit: physical pixel (if C<52) 37 | /** current height */ h: number 38 | }; 39 | [kFReq.hud]: { k: kTip }; 40 | [kFReq.focus]: { 41 | /** lastKey */ l: kKeyCode; 42 | }; 43 | [kFReq.evalJS]: { 44 | u: string; 45 | }; 46 | [kFReq.broken]: {}; 47 | [kFReq.stopScroll]: {} 48 | [kFReq.scrollGoing]: {}; 49 | [kFReq.unload]: {}; 50 | [kFReq.iframeIsAlive]: { /** hasOptionsPassed */ o: BOOL }; 51 | [kFReq.scaled_old_cr]: { t: string } 52 | } 53 | interface IframePort { 54 | postMessage (this: IframePort, msg: FReq[K] & Msg): void | 1; 55 | onmessage (this: void, msg: { data: CReq[keyof CReq] }): void | 1; 56 | } 57 | type FgOptionsToFront = CReq[kCReq.activate]; 58 | 59 | interface ContentOptions extends GlobalOptions { 60 | /** on Firefox and has filter (maybe from dark reader's Filter(+) mode) */ d?: boolean | 0 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/env.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-var, @typescript-eslint/no-unused-vars */ 2 | if (Build.BTypes & BrowserType.Chrome && Build.BTypes !== BrowserType.Chrome as number) { 3 | var browser: unknown; 4 | } 5 | if (Build.BTypes & BrowserType.Chrome && Build.MinCVer < BrowserVer.MinEnsuredES6WeakMapAndWeakSet) { 6 | var WeakSet: WeakSetConstructor | undefined; 7 | var WeakMap: WeakMapConstructor | undefined; 8 | } 9 | if (Build.BTypes & BrowserType.Chrome && Build.MinCVer < BrowserVer.MinEnsuredES6$ForOf$Map$SetAnd$Symbol) { 10 | var Set: SetConstructor | undefined; 11 | } 12 | if (Build.BTypes & BrowserType.Chrome && Build.MinCVer < BrowserVer.MinEnsured$InputDeviceCapabilities) { 13 | var InputDeviceCapabilities: InputDeviceCapabilitiesVar | undefined; 14 | } 15 | interface VisualViewport { width?: number; height: number; offsetLeft: number; offsetTop: number; 16 | pageLeft: number; pageTop: number; scale: number; } 17 | if (Build.BTypes & BrowserType.Chrome && Build.MinCVer < BrowserVer.MinEnsured$visualViewport$ 18 | || Build.BTypes & BrowserType.Edge) { 19 | var visualViewport: VisualViewport | undefined; 20 | } 21 | if (Build.BTypes & BrowserType.Edge 22 | || Build.BTypes & BrowserType.Chrome && Build.MinCVer < BrowserVer.Min$queueMicrotask) { 23 | var queueMicrotask: (callback: (this: void) => void) => void 24 | } 25 | if (Build.BTypes & BrowserType.Chrome && Build.MinCVer < BrowserVer.MinEnsured$WeakRef 26 | || Build.BTypes & BrowserType.Firefox) { 27 | var WeakRef: unknown; 28 | } 29 | 30 | var VApi: VApiTy | undefined, VimiumInjector: VimiumInjectorTy | undefined | null 31 | 32 | declare var define: any, __filename: string | null | undefined 33 | 34 | Build.Inline || (function (): void { 35 | type ModuleTy = Dict & { __esModule: boolean } 36 | interface DefineTy { 37 | (deps: string[], factory: (asyncRequire: () => never, exports: ModuleTy, ...resolved: ModuleTy[]) => any): void 38 | amd?: boolean 39 | noConflict (): void 40 | } 41 | const oldDefine: DefineTy = typeof define !== "undefined" ? define : void 0 42 | const modules: Dict = {} 43 | const getName = (name: string): string => name.slice(name.lastIndexOf("/") + 1).replace(".js", "") 44 | const myDefine: DefineTy = function (this: any, deps, factory): void { 45 | let filename = __filename 46 | if (!filename || filename.lastIndexOf("content/", 0) === -1 && filename.lastIndexOf("lib/", 0) === -1) { 47 | if (!oldDefine) { 48 | const name = (document.currentScript as HTMLScriptElement).src.split("/") 49 | const fileName = name[name.length - 1].replace( /\.js|\.min/g, "") 50 | .replace(> /\b[a-z]/g, i => i.toUpperCase()); 51 | (window as any)[fileName] = ((factory || deps) as () => any)() 52 | return 53 | } 54 | return oldDefine.apply(this, arguments) // eslint-disable-line @typescript-eslint/no-unsafe-argument 55 | } 56 | __filename = null 57 | const exports = myRequire(filename) 58 | ; (myDefine as any)[getName(filename)] = exports 59 | return factory.bind(null, throwOnDynamicImport, exports).apply(null, deps.slice(2).map(myRequire)) 60 | } 61 | const throwOnDynamicImport = (): never => { 62 | throw new Error("Must avoid dynamic import in content scripts") 63 | } 64 | const myRequire = function (target: string): ModuleTy { 65 | target = getName(target) 66 | return modules[target] || (modules[target] = {} as ModuleTy) 67 | } 68 | myDefine.amd = true; 69 | myDefine.noConflict = (): void => { 70 | if ((window as PartialOf).define !== myDefine) { return } 71 | (window as PartialOf).define = oldDefine 72 | if (!oldDefine) { return } 73 | if (VimiumInjector === null) { 74 | if (Build.BTypes & BrowserType.Chrome && Build.MinCVer < BrowserVer.MinEnsured$Object$$assign) { 75 | for (let key in modules) { (oldDefine as any)[key] = modules[key] } 76 | } else { 77 | Object.assign(oldDefine, modules) 78 | } 79 | } 80 | } 81 | (window as PartialOf).__filename = undefined; 82 | (window as PartialOf).define = myDefine 83 | })() 84 | -------------------------------------------------------------------------------- /lib/math_parser.d.ts: -------------------------------------------------------------------------------- 1 | 2 | export declare const MathParser: { 3 | evaluate (expr: string): number | Function 4 | clean (): void 5 | errormsg: string 6 | } 7 | -------------------------------------------------------------------------------- /lib/polyfill.ts: -------------------------------------------------------------------------------- 1 | // DO NOT USE `no-default-lib` - tsc.js will not output into the corresponding JS file if with it 2 | 3 | (function (): void { 4 | type primitiveObject = boolean | number | string; 5 | type primitive = primitiveObject | null | undefined; 6 | type ObjectCoercible = primitiveObject | { 7 | toString (): primitive; 8 | } | { 9 | valueOf (): primitive; 10 | }; 11 | type anyNotSymbol = ObjectCoercible | null | undefined; 12 | interface StandardString { 13 | endsWith? (this: string, searchString: string, pos?: number | undefined): boolean; 14 | endsWith? (this: ObjectCoercible, searchString?: anyNotSymbol, pos?: anyNotSymbol): boolean; 15 | includes? (this: string, searchString: string, pos?: number | undefined): boolean; 16 | includes? (this: ObjectCoercible, searchString?: anyNotSymbol, pos?: anyNotSymbol): boolean; 17 | startsWith? (this: string, searchString: string, pos?: number | undefined): boolean; 18 | startsWith? (this: ObjectCoercible, searchString?: anyNotSymbol, pos?: anyNotSymbol): boolean; 19 | } 20 | 21 | const symMatch = typeof Symbol === "function" && typeof Symbol.match === "symbol" && 22 | (Symbol.match as symbol | string as "Symbol(Symbol.match)"), 23 | // eslint-disable-next-line id-denylist 24 | StrCls = String as StringConstructor & { readonly prototype: StandardString }, TECls = TypeError, 25 | StrProto = StrCls.prototype, 26 | toStr = Object.prototype.toString; 27 | 28 | "".startsWith || Object.defineProperty(StrProto, "startsWith", { enumerable: false, value: 29 | function startsWith(this: ObjectCoercible, searchString: anyNotSymbol): boolean { 30 | const err = check(this, searchString), a = !isLooselyNull(this) && err !== 1 ? StrCls(this) : ""; 31 | if (err !== 0) { 32 | if (err === 1 || err === 2) { return !((err < 2 ? this : searchString) + ""); } 33 | throw new TECls(err.replace("${func}", "startsWith")); 34 | } 35 | let b = StrCls(searchString), args = arguments, c = args.length > 1 ? +args[1] : 0; 36 | c = c > 0 ? c | 0 : 0; 37 | c > a.length && (c = a.length); 38 | return a.lastIndexOf(b, c) === c; 39 | } }) 40 | 41 | "".endsWith || Object.defineProperty(StrProto, "endsWith", { enumerable: false, value: 42 | function endsWith(this: ObjectCoercible, searchString: anyNotSymbol): boolean { 43 | const err = check(this, searchString), a = !isLooselyNull(this) && err !== 1 ? StrCls(this) : ""; 44 | if (err !== 0) { 45 | if (err === 1 || err === 2) { return !((err < 2 ? this : searchString) + ""); } 46 | throw new TECls(err.replace("${func}", "endsWith")); 47 | } 48 | let b = StrCls(searchString), args = arguments, u: undefined, c: number 49 | , p: primitive | object = args.length > 1 ? args[1] : u, l = a.length; 50 | c = (p === u ? l : (c = + p) > 0 ? c | 0 : 0) - b.length; 51 | c > l && (c = l); 52 | return c >= 0 && a.indexOf(b, c) === c; 53 | } }) 54 | 55 | "".includes || Object.defineProperty(StrProto, "includes", { enumerable: false, value: 56 | function includes(this: ObjectCoercible, searchString: anyNotSymbol): boolean { 57 | const err = check(this, searchString), a = !isLooselyNull(this) && err !== 1 ? StrCls(this) : ""; 58 | if (err !== 0) { 59 | if (err === 1 || err === 2) { return !((err < 2 ? this : searchString) + ""); } 60 | throw new TECls(err.replace("${func}", "includes")); 61 | } 62 | let b = StrCls(searchString), args = arguments, c = args.length > 1 ? +args[1] : 0; 63 | c = c > 0 ? c | 0 : 0; 64 | c > a.length && (c = a.length); 65 | // eslint-disable-next-line @typescript-eslint/prefer-includes 66 | return a.indexOf(b, c) >= 0; 67 | } }) 68 | 69 | function check(a: primitive | object, b: primitive | object): 0 | string | 1 | 2 { 70 | /** note: should never call `valueOf` or `toString` on a / b; `document.all` should pass this */ 71 | if (isLooselyNull(a)) { return "String.prototype.${func} called on null or undefined"; } 72 | if (!b) { return 0; } 73 | let t: 0 | 1 | 2 = typeof a === "symbol" ? 1 : typeof b === "symbol" ? 2 : 0; 74 | if (t) { return t; } 75 | interface PossibleTypeOfB { 76 | [key: string]: ((this: string, re: RegExp) => boolean) | primitive; 77 | } 78 | let f: PossibleTypeOfB[string], u: undefined 79 | , i = symMatch && (f = (b as PossibleTypeOfB)[symMatch]) !== u ? f 80 | : toStr.call(b) === "[object RegExp]"; 81 | return i ? "First argument to String.prototype.${func} must not be a regular expression" : 0; 82 | } 83 | function isLooselyNull (object: unknown): object is null | undefined { 84 | return object === null || object === undefined 85 | } 86 | })(); 87 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "gdh1995@qq.com", 3 | "action": { 4 | "default_icon": { 5 | "19": "icons/enabled_19.png", 6 | "38": "icons/enabled_38.png" 7 | }, 8 | "default_popup": "pages/action.html", 9 | "default_title": "__MSG_name__\n\n__MSG_noActiveState__" 10 | }, 11 | "background": { 12 | "service_worker": "background/worker.js", 13 | "type": "module" 14 | }, 15 | "commands": { 16 | "createTab": { "description": "__MSG_createTab_s__" }, 17 | "goBack": { "description": "__MSG_goBack_s__" }, 18 | "goForward": { "description": "__MSG_goForward_s__" }, 19 | "previousTab": { "description": "__MSG_previousTab_s__" }, 20 | "quickNext": { "description": "__MSG_quickNext_s__" }, 21 | "reloadTab": { "description": "__MSG_reloadTab_s__" }, 22 | "userCustomized1": { "description": "__MSG_userCustomized__ 1" }, 23 | "userCustomized2": { "description": "__MSG_userCustomized__ 2" } 24 | }, 25 | "content_scripts": [ { 26 | "all_frames": true, 27 | "js": [ 28 | "lib/env.js", 29 | "lib/utils.js", 30 | "lib/keyboard_utils.js", 31 | "lib/dom_utils.js", 32 | "lib/rect.js", 33 | "content/dom_ui.js", 34 | "content/async_dispatcher.js", 35 | "content/hud.js", 36 | "content/insert.js", 37 | "content/key_handler.js", 38 | "content/port.js", 39 | "content/pagination.js", 40 | "content/marks.js", 41 | "content/local_links.js", 42 | "content/hint_filters.js", 43 | "content/link_actions.js", 44 | "content/link_hints.js", 45 | "content/omni.js", 46 | "content/mode_find.js", 47 | "content/visual.js", 48 | "content/scroller.js", 49 | "content/request_handlers.js", 50 | "content/commands.js", 51 | "content/extend_click.js", 52 | "content/extend_click_ff.js", 53 | "content/frontend.js" 54 | ], 55 | "match_about_blank": true, 56 | "match_origin_as_fallback": true, 57 | "matches": [ "" ], 58 | "run_at": "document_start" 59 | }, { 60 | "all_frames": true, 61 | "js": [ 62 | "content/extend_click_vc.js" 63 | ], 64 | "match_about_blank": true, 65 | "match_origin_as_fallback": true, 66 | "matches": [ "" ], 67 | "run_at": "document_start", 68 | "world": "MAIN" 69 | } ], 70 | "content_security_policy": { 71 | "extension_pages": "script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'" 72 | }, 73 | "default_locale": "en", 74 | "description": "__MSG_description__", 75 | "homepage_url": "https://github.com/gdh1995/vimium-c", 76 | "host_permissions": [ 77 | "" 78 | ], 79 | "icons": { 80 | "128": "icons/icon128.png", 81 | "16": "icons/icon16.png", 82 | "32": "icons/icon32.png", 83 | "48": "icons/icon48.png" 84 | }, 85 | "incognito": "spanning", 86 | "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnaZQnkvrHvNlwXPnSfavEya+9sPtVgBR2hWLsNvwdv6+zHRGr/qhqEbtof1v7gDXzpC5+Dr4kOay6aflw5CaFzA1zyVJySAjpb0RkVh774kp9msRpjyb39obSIWAIB76PTwawEAE+K1f26jXDbTC2gEBe4OWk5KW9u9+KoTmeKLHo73ScR1jFcFaOTbrhwHtIDqj00vpIfrSAofpLIq1gP//Og+FoOT4dF107fwlVfDJuu171svuT68wmcKStl6OANxcnXCXE6OWbeZcjMehcQH41wzTtDugmt0o8w+hJ2WVugwWWmGmE8qSiwo9QYZTJW+5EvMcmNZWUmPbTLGkGQIDAQAB", 87 | "manifest_version": 3, 88 | "minimum_chrome_version": "121", 89 | "name": "__MSG_name__", 90 | "offline_enabled": true, 91 | "omnibox": { 92 | "keyword": "v" 93 | }, 94 | "optional_host_permissions": [ 95 | "chrome://*/*", 96 | "chrome://new-tab-page/*" 97 | ], 98 | "optional_permissions": [ 99 | "bookmarks", 100 | "downloads", 101 | "downloads.shelf", 102 | "cookies", 103 | "contentSettings" 104 | ], 105 | "options_ui": { 106 | "page": "pages/options.html", 107 | "open_in_tab": true 108 | }, 109 | "permissions": [ 110 | "clipboardRead", 111 | "clipboardWrite", 112 | "favicon", 113 | "history", 114 | "notifications", 115 | "offscreen", 116 | "scripting", 117 | "search", 118 | "sessions", 119 | "storage", 120 | "tabGroups", 121 | "tabs", 122 | "webNavigation" 123 | ], 124 | "short_name": "Vimium C", 125 | "update_url": "https://clients2.google.com/service/update2/crx", 126 | "version": "2.12.3", 127 | "version_name": "2.12.2", 128 | "web_accessible_resources": [ { 129 | "resources": [ "content/*", "front/vomnibar*", "lib/*" ], 130 | "matches": [ "", "chrome-extension://*/*" ], 131 | "use_dynamic_url": false 132 | } ] 133 | } -------------------------------------------------------------------------------- /manifest.v2.json: -------------------------------------------------------------------------------- 1 | { 2 | "action": null, 3 | "background": { 4 | "persistent": true, 5 | "scripts": [ 6 | "background/define.js", 7 | "background/store.js", 8 | "background/utils.js", 9 | "background/browser.js", 10 | "background/normalize_urls.js", 11 | "background/parse_urls.js", 12 | "background/settings.js", 13 | "background/ports.js", 14 | "background/exclusions.js", 15 | "background/ui_css.js", 16 | "background/i18n.js", 17 | "background/key_mappings.js", 18 | "background/run_commands.js", 19 | "background/run_keys.js", 20 | "background/tools.js", 21 | "background/clipboard.js", 22 | "background/eval_urls.js", 23 | "background/filter_tabs.js", 24 | "background/open_urls.js", 25 | "background/frame_commands.js", 26 | "background/tab_commands.js", 27 | "background/all_commands.js", 28 | "background/request_handlers.js", 29 | "background/main.js" 30 | ] 31 | }, 32 | "browser_action": { 33 | "default_icon": { 34 | "19": "icons/enabled_19.png", 35 | "38": "icons/enabled_38.png" 36 | }, 37 | "default_popup": "pages/action.html", 38 | "default_title": "__MSG_name__\n\n__MSG_noActiveState__" 39 | }, 40 | "content_security_policy": "script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'", 41 | "host_permissions": null, 42 | "manifest_version": 2, 43 | "optional_host_permissions": null, 44 | "optional_permissions[]": [ 45 | "chrome://*/*", 46 | "chrome://new-tab-page/*" 47 | ], 48 | "options_page": "pages/options.html", 49 | "options_ui": { 50 | "page": "pages/options.html", 51 | "chrome_style": false, 52 | "open_in_tab": true 53 | }, 54 | "permissions[]": [ 55 | "", 56 | "-favicon", 57 | "-offscreen", 58 | "-scripting", 59 | "-tabGroups" 60 | ], 61 | "web_accessible_resources": [ 62 | "content/*", 63 | "front/vomnibar*", 64 | "lib/*" 65 | ] 66 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vimium-c", 3 | "description": "A keyboard shortcut tool for keyboard-based page navigation and browser tab operations with an advanced omnibar and global shortcuts", 4 | "author": "gdh1995", 5 | "bugs": "https://github.com/gdh1995/vimium-c/issues", 6 | "private": true, 7 | "dependencies": { 8 | "pngjs": "^7.0.0", 9 | "terser": "^5.19.0", 10 | "typescript": "^4.9.1" 11 | }, 12 | "devDependencies": { 13 | "clean-css": "^5.3.2", 14 | "deepcopy": "^2.1.0", 15 | "gulp": "^4.0.2", 16 | "gulp-changed": "^4.0.3", 17 | "gulp-concat": "*", 18 | "gulp-newer": "*", 19 | "gulp-print": "^5.0.2", 20 | "gulp-some": "*", 21 | "gulp-typescript": "^6.0.0-alpha.1", 22 | "html-minifier": "^4.0.0", 23 | "rimraf": "^5.0.1", 24 | "rollup": "^3.26.2", 25 | "seedrandom": "^3.0.5" 26 | }, 27 | "engines": { 28 | "node": ">=14", 29 | "npm": ">=7" 30 | }, 31 | "homepage": "https://github.com/gdh1995/vimium-c#readme", 32 | "license": "Apache-2.0", 33 | "keywords": [ 34 | "vimium", 35 | "webextension", 36 | "chrome-extension", 37 | "web-extension", 38 | "all-in-keyboard", 39 | "all-by-keyboard", 40 | "keyboard", 41 | "shortcut", 42 | "chrome", 43 | "firefox", 44 | "edge" 45 | ], 46 | "optionalDependencies": { 47 | "@typescript-eslint/eslint-plugin": "^4.26.0", 48 | "@typescript-eslint/parser": "^4.26.0", 49 | "eslint": "^7.29.0" 50 | }, 51 | "repository": { 52 | "type": "git", 53 | "url": "https://github.com/gdh1995/vimium-c.git" 54 | }, 55 | "scripts": { 56 | "tsc": "node scripts/tsc.js", 57 | "watch": "node scripts/tsc.js --watch", 58 | "dev": "node scripts/tsc.js --watch", 59 | "all": "gulp tsc", 60 | "prepare": "node scripts/icons-to-blob.js -q", 61 | "lint": "node ./scripts/eslint.js", 62 | "eslint": "node ./scripts/eslint.js", 63 | "build": "gulp build", 64 | "dist": "npm run chrome-latest", 65 | "rebuild": "gulp rebuild", 66 | "edge": "bash -c \"BUILD_BTypes=1 BUILD_MinCVer=${BUILD_MinCVer:-102} BUILD_NeedCommit=1 BUILD_EdgeC=1 gulp dist && IN_DIST=1 BUILD_EdgeC=1 ./scripts/make.sh\"", 67 | "chrome": "bash -c \"BUILD_BTypes=1 BUILD_MinCVer=${BUILD_MinCVer:-102} BUILD_NeedCommit=1 gulp dist && IN_DIST=1 ./scripts/make.sh\"", 68 | "csize": "bash -c \"BUILD_BTypes=${BUILD_BTypes:-1} BUILD_MinCVer=${BUILD_MinCVer:-102} BUILD_NeedCommit=0 gulp content/size\"", 69 | "mv3-firefox": "bash -c \"BUILD_BTypes=2 BUILD_MinFFVer=${BUILD_MinFFVer:-101} BUILD_NeedCommit=1 gulp dist && IN_DIST=1 ./scripts/make.sh\"", 70 | "debug": "bash -c \"BUILD_MinCVer=${BUILD_MinCVer:-89} gulp local2\"", 71 | "legacy": "bash -c \"BUILD_BTypes=3 BUILD_MinCVer=${BUILD_MinCVer:-93} BUILD_MinFFVer=${BUILD_MinFFVer:-89} gulp local2\"", 72 | "mv2-edge": "bash -c \"BUILD_MV3=0 BUILD_BTypes=1 BUILD_MinCVer=${BUILD_MinCVer:-89} BUILD_NeedCommit=1 BUILD_EdgeC=1 gulp dist && IN_DIST=1 BUILD_EdgeC=1 ./scripts/make.sh\"", 73 | "mv2-cr": "bash -c \"BUILD_MV3=0 BUILD_BTypes=1 BUILD_MinCVer=${BUILD_MinCVer:-47} BUILD_NeedCommit=1 gulp dist && IN_DIST=1 ./scripts/make.sh\"", 74 | "mv2-ff": "bash -c \"BUILD_MV3=0 BUILD_BTypes=2 BUILD_MinFFVer=${BUILD_MinFFVer:-101} BUILD_NeedCommit=1 gulp dist && IN_DIST=1 ./scripts/make.sh\"", 75 | "mv2-edge-e": "bash -c \"BUILD_MV3=0 BUILD_BTypes=4 BUILD_MinCVer=${BUILD_MinCVer:-53} BUILD_NeedCommit=1 gulp dist && IN_DIST=1 ./scripts/make.sh\"", 76 | "mv2-debug": "bash -c \"BUILD_MV3=0 BUILD_MinCVer=${BUILD_MinCVer:-32} BUILD_BTypes=1 gulp local2\"", 77 | "mv2-debug-ff": "bash -c \"BUILD_MV3=0 BUILD_BTypes=2 BUILD_MinCVer=${BUILD_MinCVer:-57} BUILD_MinFFVer=${BUILD_MinFFVer:-63} gulp local2\"", 78 | "chromium": "npm run chrome", 79 | "clean": "gulp clean", 80 | "clear": "gulp clean", 81 | "nclean": "bash -c \"gulp clean\"", 82 | "local": "gulp local", 83 | "start": "gulp local", 84 | "test": "gulp test" 85 | }, 86 | "version": "2.0.0" 87 | } 88 | -------------------------------------------------------------------------------- /pages/blank.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Blank Page 5 | 6 | 7 | -------------------------------------------------------------------------------- /pages/show.css: -------------------------------------------------------------------------------- 1 | 2 | body.filled, body.pixel { 3 | font-size: 0; 4 | margin: 0; 5 | } 6 | body { 7 | min-width: 96vw; 8 | min-height: 96vh; 9 | } 10 | #shownImage { 11 | min-width: 32px; 12 | min-height: 32px; 13 | } 14 | #shownImage.svg { 15 | min-width: 256px; 16 | min-height: 256px; 17 | } 18 | .filled > img { 19 | outline: none; 20 | } 21 | .zoom-in { 22 | cursor: zoom-in; 23 | } 24 | .filled.viewer-open .zoom-in { 25 | max-width: 100%; 26 | max-height: 100%; 27 | } 28 | body.pixel .viewer-container img { 29 | image-rendering: pixelated; 30 | } 31 | 32 | #bgLink { 33 | position: absolute; 34 | z-index: -1; 35 | } 36 | 37 | #snapshot-banner { 38 | height: 32px; 39 | } 40 | .banner-box { 41 | background: lightyellow; 42 | border-bottom: 1px solid #ebeb00; 43 | box-sizing: border-box; 44 | font-size: 16px; 45 | height: 32px; 46 | left: 0; 47 | line-height: 32px; 48 | padding-left: 2em; 49 | position: fixed; 50 | right: 0; 51 | -webkit-user-select: none; 52 | user-select: none; 53 | } 54 | .banner-close { 55 | color: #777; 56 | fill: #777; 57 | font-size: 0; 58 | height: 20px; 59 | line-height: 0; 60 | position: absolute; 61 | right: 32px; 62 | top: 6px; 63 | width: 20px; 64 | } 65 | .btn_svg { 66 | stroke: currentColor; 67 | stroke-linecap: round; 68 | stroke-linejoin: round; 69 | stroke-width: 2.4; 70 | } 71 | 72 | #shownText { 73 | display: inline-block; 74 | font-size: large; 75 | } 76 | 77 | #textBox { 78 | display: inline-block; 79 | margin: 2px 0 0 20px; 80 | } 81 | 82 | #textTip::after { 83 | color: gray; 84 | content: attr(data-text); 85 | display: inline-block; 86 | font-style: italic; 87 | width: 55px; 88 | } 89 | 90 | .colon::after { 91 | color: gray; 92 | content: attr(data-colon); 93 | font-style: italic; 94 | } 95 | 96 | #textBody { 97 | display: inline-block; 98 | padding-left: 4px; 99 | } 100 | 101 | #textBody::after { 102 | color: transparent; 103 | content: "."; 104 | } 105 | 106 | .null::after { 107 | color: lightgray; 108 | content: "(null)"; 109 | } 110 | 111 | img.invert { 112 | background: white; 113 | outline: 4px solid #a06d0d; 114 | -webkit-filter: invert(100%); 115 | filter: invert(100%); 116 | } 117 | 118 | img.invert::selection { 119 | background: none; 120 | } 121 | 122 | img.broken { 123 | border: 1px solid silver; 124 | padding: 1px 1px 3px 1px; 125 | } 126 | 127 | .viewer-title { 128 | font-size: 16px; 129 | height: 20px; 130 | } 131 | 132 | .viewer-next, .viewer-prev { 133 | visibility: hidden; 134 | } 135 | .viewer-player { 136 | cursor: default !important; 137 | } 138 | 139 | @media (display-mode: fullscreen) { 140 | .filled .viewer-footer:not(:hover), .filled .viewer-close:not(:hover) { opacity: 0; } 141 | } 142 | -------------------------------------------------------------------------------- /pages/show.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vimium C Display 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /pages/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "incremental": true, 4 | "module": "es6", 5 | "outDir": "..", 6 | "rootDir": "..", 7 | "tsBuildInfoFile": "../scripts/.ts.pages.build", 8 | }, 9 | "include": [ 10 | "define.ts", 11 | "async_bg.ts", 12 | "loader.ts", 13 | "options*.ts", 14 | "show.ts" 15 | ], 16 | "extends": "../tsconfig.base.json" 17 | } 18 | -------------------------------------------------------------------------------- /scripts/eslint.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var node = process.argv[0], 4 | args = "eslint/bin/eslint --ext .ts".split(" "); 5 | if (process.argv.length > 2) { 6 | args = args.concat(process.argv.slice(2)); 7 | } else { 8 | args.push("."); 9 | } 10 | process.argv = [node, ...args]; 11 | require(process.argv[1]); 12 | -------------------------------------------------------------------------------- /scripts/gulp.tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "listEmittedFiles": true, 4 | "listFiles": false, 5 | "outDir": ".", 6 | "skipLibCheck": true, 7 | "target": "es5", 8 | "typeRoots": ["typings"], 9 | "typescript": "typescript/lib/typescript" 10 | }, 11 | "build": { // for [ local, dist ] 12 | "MinCVer": [ 121, 102 ], // keep dist to be 102 : the release version 13 | "MinFFVer": [ 119, 101 ], 14 | "BTypes": [ 1, 1 ], // [ Chrome, Chrome ] 15 | "OS": [ 7, 7 ], 16 | "DetectAPIOnFirefox": [1, 0], 17 | "NDEBUG": [ 0, 1 ], 18 | "Mangle": [ 0, 1 ], 19 | "Inline": [ 0, 1 ] 20 | }, 21 | "extends": "../tsconfig.base.json" 22 | } 23 | -------------------------------------------------------------------------------- /scripts/icons-to-blob.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // @ts-check 3 | "use strict"; 4 | /** 5 | * @typedef {import("./dependencies").Buffer} Buffer 6 | * @typedef { { 7 | * data: Buffer; width: number; height: number; 8 | * } } PNGImage 9 | */ 10 | 11 | var allIcons = [ 12 | { 19: "/icons/enabled_19.png", 38: "/icons/enabled_38.png" }, 13 | { 19: "/icons/partial_19.png", 38: "/icons/partial_38.png" }, 14 | { 19: "/icons/disabled_19.png", 38: "/icons/disabled_38.png" } 15 | ]; 16 | /** @type {import("./dependencies").FileSystem} */ 17 | // @ts-ignore 18 | var fs = require("fs"); 19 | /** @type {import("./dependencies").ProcessType} */ 20 | // @ts-ignore 21 | var process = require("process"); 22 | try { 23 | var lib = require("./dependencies"); 24 | } catch (ex) { lib = null; } 25 | var destRoot = +(process.env["BUILD_NDEBUG"] || 0) > 0 ? "dist/" : process.env["LOCAL_DIST"] || ""; 26 | var srcRoot = process.cwd(); 27 | { 28 | var cwd = srcRoot.split(require("path").sep).slice(-1)[0]; 29 | if ("background content front lib pages".split(" ").includes(cwd)) { 30 | srcRoot = srcRoot + "/.."; 31 | } 32 | } 33 | destRoot = require("path").resolve(destRoot || srcRoot); 34 | 35 | /** 36 | * Convert a single image 37 | * @param {string} src - path of image source file 38 | * @returns {Promise} 39 | */ 40 | function readPNGImage(src) { 41 | return new Promise((resolve, reject) => { 42 | var PNG = require("pngjs").PNG; 43 | fs.createReadStream(src).pipe(new PNG({ 44 | filterType: 6 45 | })).on('parsed', function () { 46 | resolve(this); 47 | }).on('error', function (err) { 48 | reject(err); 49 | }) 50 | }); 51 | } 52 | 53 | /** 54 | * Convert all image files 55 | * @param {((error?: any) => any) | null} [callback] 56 | * @param {Object} [options] 57 | * @param {Array<{ [size: string]: string }>} [options.icons = allIcons] 58 | * @param {(src: string) => string} [options.getDest] 59 | * @param {(src: string, dest: string) => boolean} [options.checkLatest] 60 | * @param {(message: string, ...params: any[]) => void} [options.print = console.log] 61 | * @returns {Promise} 62 | */ 63 | function main(callback = null 64 | , { 65 | icons = allIcons, 66 | getDest, 67 | checkLatest = lib && lib.compareFileTime, 68 | print = console.log 69 | } = {}) { 70 | let consumed = 0; 71 | const didFinish = () => { 72 | let local_cb = callback; 73 | callback = null; 74 | local_cb ? local_cb() 75 | // @ts-ignore 76 | : consumed <= 0 && typeof require === "function" && require.main === module 77 | && process.argv.indexOf("-q") > 0 ? void 0 78 | : print("All %d new icons in %d converted.", consumed, totalCount); 79 | }; 80 | destRoot = destRoot.replace(/\\/g, "/"); 81 | destRoot && destRoot.slice(-1) != "/" && (destRoot += "/"); 82 | const totalCount = icons.length, allPromises = []; 83 | for (let i = 0; i < totalCount; i++) { 84 | const submap = icons[i], 85 | sublist = Object.keys(submap).sort().map(i => submap[i].replace(/^\//, "")); 86 | let dest = getDest ? getDest(sublist[0]) : destRoot + sublist[0].split("_", 1)[0] + ".bin" 87 | , islatest = 0; 88 | const srcList = sublist.map(i => srcRoot + "/" + i); 89 | checkLatest && srcList.forEach(filePath => checkLatest(filePath, dest) && islatest++); 90 | if (islatest === srcList.length) { continue; } 91 | const destFolder = dest.split("/").slice(0, -1).join("/"); 92 | if (destFolder && !fs.existsSync(destFolder)) { 93 | fs.mkdirSync(destFolder, {recursive: true}); 94 | } 95 | allPromises.push(Promise.all(srcList.map(readPNGImage)).then(([img1, img2]) => { 96 | const imagesData = [img1.data, img2.data]; 97 | // @ts-ignore 98 | const allBuffer = Buffer.concat(imagesData); 99 | return new Promise((resolve, reject) => { 100 | fs.writeFile(dest, allBuffer, (err) => { 101 | if (err) { return reject(err); } 102 | consumed++; 103 | print("Write binary image data to %s", dest, ", byte[", img1.data.length, "+", img2.data.length, "]"); 104 | resolve(dest); 105 | }) 106 | }) 107 | })); 108 | } 109 | if (allPromises.length <= 0) { 110 | return Promise.resolve(didFinish()); 111 | } else { 112 | return Promise.all(allPromises).then(didFinish); 113 | } 114 | } 115 | 116 | if (typeof module !== "undefined") { 117 | module.exports = { 118 | allIcons: allIcons, 119 | readPNGImage: readPNGImage, 120 | main: main, 121 | /** 122 | * setup dest root 123 | * @param {string} newRoot - new root of dest files 124 | */ 125 | setDestRoot: function (newRoot) { 126 | destRoot = newRoot; 127 | } 128 | }; 129 | } 130 | // @ts-ignore 131 | if (typeof require === "function" && require.main === module) { 132 | main(); 133 | } 134 | -------------------------------------------------------------------------------- /scripts/linguist.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | $LOAD_PATH[0, 0] = File.join(File.dirname(__FILE__), '..', 'lib') 3 | 4 | require 'linguist' 5 | require 'rugged' 6 | require 'json' 7 | require 'optparse' 8 | 9 | path = "." 10 | breakdown = false 11 | json_breakdown = false 12 | target_languages = [] 13 | 14 | if ARGV[0] == "--breakdown" || ARGV[0] == "--json" 15 | path = Dir.pwd 16 | else 17 | path = ARGV[0] || "." 18 | ARGV.shift 19 | end 20 | 21 | if ARGV[0] == "--breakdown" 22 | breakdown = true 23 | ARGV.shift 24 | end 25 | if ARGV[0] == "--json" 26 | json_breakdown = true 27 | ARGV.shift 28 | end 29 | if ARGV[0] 30 | if ARGV[1] 31 | target_languages = ARGV 32 | else 33 | target_languages = ARGV[0].split(/[ ,]/) 34 | end 35 | target_languages.map! &:downcase 36 | end 37 | 38 | if File.directory?(path) 39 | rugged = Rugged::Repository.new(path) 40 | repo = Linguist::Repository.new(rugged, rugged.head.target_id) 41 | if !json_breakdown && target_languages.empty? 42 | repo.languages.sort_by { |_, size| size }.reverse.each do |language, size| 43 | percentage = ((size / repo.size.to_f) * 100) 44 | percentage = sprintf '%.2f' % percentage 45 | puts "%-7s %9s %s" % ["#{percentage}%", "#{size}B", language] 46 | end 47 | end 48 | if target_languages.any? 49 | repo.breakdown_by_file.each do |lang, files| 50 | if target_languages.include? lang.downcase 51 | files.each do |file| 52 | puts file 53 | end 54 | end 55 | end 56 | elsif breakdown 57 | puts 58 | file_breakdown = repo.breakdown_by_file 59 | file_breakdown.each do |lang, files| 60 | puts "#{lang}:" 61 | files.each do |file| 62 | puts file 63 | end 64 | puts 65 | end 66 | elsif json_breakdown 67 | puts JSON.dump(repo.breakdown_by_file) 68 | end 69 | elsif File.file?(path) 70 | blob = Linguist::FileBlob.new(path, Dir.pwd) 71 | type = if blob.text? 72 | 'Text' 73 | elsif blob.image? 74 | 'Image' 75 | else 76 | 'Binary' 77 | end 78 | 79 | puts "#{blob.name}: #{blob.loc} lines (#{blob.sloc} sloc)" 80 | puts " type: #{type}" 81 | puts " mime type: #{blob.mime_type}" 82 | puts " language: #{blob.language}" 83 | 84 | if blob.large? 85 | puts " blob is too large to be shown" 86 | end 87 | 88 | if blob.generated? 89 | puts " appears to be generated source code" 90 | end 91 | 92 | if blob.vendored? 93 | puts " appears to be a vendored file" 94 | end 95 | else 96 | abort <<-HELP 97 | Linguist v#{Linguist::VERSION} 98 | Detect language type for a file, or, given a repository, determine language breakdown. 99 | Usage: linguist [...target language list] 100 | linguist [--breakdown] [--json] [...target language list] 101 | linguist [--breakdown] [--json] [...target language list] 102 | HELP 103 | end 104 | -------------------------------------------------------------------------------- /scripts/parse_tlds.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | (async () => { 4 | //@ts-check 5 | const fs = require("fs") 6 | const pathModule = require("path") 7 | 8 | const FILE = "public_suffix_list.dat" 9 | const URL = "https://publicsuffix.org/list/" + FILE 10 | const DISABLED_TLDS = [ 11 | "exe", "pdf", "zip", 12 | ].filter(i => i) 13 | 14 | let curFile = [ 15 | FILE, 16 | pathModule.join("scripts", FILE), 17 | FILE + ".txt", 18 | pathModule.join("scripts", FILE + ".txt"), 19 | ].filter(i => i && fs.existsSync(i))[0] || "" 20 | 21 | /** @type { string } */ 22 | const lines = curFile && fs.existsSync(curFile) 23 | ? fs.readFileSync(curFile, {encoding: "utf-8"}) 24 | : await new Promise((resolve, reject) => { 25 | let body = "" 26 | const req = (URL[4] === "s" ? require("https") : require("http")).get(URL, res => { 27 | res.setEncoding("utf-8") 28 | .on("data", chunk => body += chunk) 29 | .on("end", () => { 30 | if (res.statusCode == 200) { 31 | resolve(body) 32 | } else { 33 | req.off("error", reject).on("abort", () => {}).abort() 34 | reject(`HTTP ${res.statusCode}: ${body}`) 35 | } 36 | }) 37 | }) 38 | req.on("error", reject) 39 | }) 40 | const tlds = [... new Set(lines.split(/\r\n?|\n/) 41 | .filter(line => line && line[0] !== "#" && line.slice(0, 2) !== "//") 42 | .map(line => line.split(".").slice(-1)[0]) 43 | )] 44 | .filter(i => ! DISABLED_TLDS.includes(i)) 45 | .map(i => `${i.length < 10 ? "0" : ""}${i.length}${i}`) 46 | .sort() 47 | .map(i => i.slice(2)) 48 | 49 | const prefix = ' , "' 50 | for (const isEn of [true, false]) { 51 | print(`BgUtils_.${isEn ? "_tlds" : "_nonENTlds"} = [""`) 52 | let [count, len_tld, line, len_line] = [0, 2, "", prefix.length] 53 | for (const i of tlds) { 54 | if (/^[\dA-Za-z]+$/.test(i) != isEn) { continue } 55 | const leni = i.length 56 | if (leni > len_tld) { 57 | let tail = count ? ` // char[${count}][${len_tld}]` : "" 58 | if (len_line > (isEn ? 115 : 80) - tail.length) { 59 | tail = '\n' + prefix.slice(0, -3) + " " + tail 60 | } 61 | print(`${prefix}${line}"${tail}`) 62 | line = "" 63 | while (len_tld + 2 < leni) { 64 | len_tld += 1 65 | line += '", "' 66 | } 67 | if (line) { 68 | print(`${prefix}${line}"`) 69 | } 70 | [count, len_tld, line, len_line] = [0, leni, "", prefix.length] 71 | } 72 | len_line += leni + 1 73 | if (len_line > (isEn ? 115 : 80)) { 74 | line += "\\\n." + i 75 | len_line = leni + 1 76 | } else { 77 | line += '.' + i 78 | } 79 | count += 1 80 | } 81 | if (count > 0) { 82 | print(`${prefix}${line}"${count ? ` // char[${count}][${len_tld}]` : ""}`) 83 | count = 0 84 | } 85 | print("];") 86 | if (isEn) { 87 | print("") 88 | } 89 | } 90 | 91 | function print() { 92 | console.log(...arguments) 93 | } 94 | })() 95 | -------------------------------------------------------------------------------- /scripts/rollup.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | input: ".", // e.g. "content/frontend.js" 5 | preserveSymlinks: true, 6 | treeshake: { 7 | moduleSideEffects: true, 8 | propertyReadSideEffects: false, 9 | tryCatchDeoptimization: false, 10 | }, 11 | output: { 12 | file: "dist/", // e.g. "dist/content/vimium-c.js" 13 | freeze: false, 14 | format: "iife", 15 | esModule: false, 16 | exports: "none", 17 | extend: false, 18 | externalLiveBindings: false, 19 | strict: true, 20 | }, 21 | onwarn (warning, rollupWarn) { 22 | if (warning.code !== 'CIRCULAR_DEPENDENCY') { 23 | rollupWarn(warning); 24 | } 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /scripts/uglifyjs.dist.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": { 3 | "ascii_only": true, // if `false` by default, '\xa0' is replaced with '\xc2\xa0', which is wrong 4 | "comments": true, 5 | "keep_quoted_props": false, 6 | "preserve_annotations": true, 7 | "wrap_iife": true, 8 | "wrap_func_args": false 9 | }, 10 | "compress": { 11 | "booleans": true, 12 | "collapse_vars": true, 13 | "comparisons": true, 14 | "conditionals": true, 15 | "dead_code": true, 16 | "drop_debugger": true, 17 | "evaluate": true, 18 | "global_defs": { 19 | "define.amd": true, 20 | "@exports": "undefined", 21 | "@__importStar": "function(i){return i}", 22 | "@module": "undefined" 23 | }, 24 | "hoist_funs": true, 25 | "hoist_props": true, 26 | "hoist_vars": true, 27 | "if_return": true, 28 | "inline": true, 29 | "join_vars": true, 30 | "keep_fargs": false, 31 | "keep_fnames": "/^(startsWith|endsWith|includes|addEventListener|toString|VC)$/", 32 | "keep_infinity": false, 33 | "lhs_constants": false, 34 | "loops": true, 35 | "negate_iife": false, 36 | "passes": 2, 37 | "properties": true, 38 | "pure_funcs": ["startsWith", "endsWith", "contains", "indexOf", "lastIndexOf", 39 | "substring", "substr", "slice", "charAt", "charCodeAt", "repeat", "concat", 40 | "toUpperCase", "toLowerCase", "trim", "trimLeft", "trimRight", 41 | "match", "split", 42 | "bind", "createElement", "Object.create", 43 | "getAttribute", "hasAttribute", "querySelector", "querySelectorAll", 44 | "max", "max_", "min", "min_", "isTY", "Lower", "includes_", "floor", "round", "abs", "abs_", "random", 45 | "Math.max", "Math.min", "Math.floor", "Math.round", "Math.abs", "Math.random", 46 | "safeObj", "safeObj_", "weakRef_", "deref_", 47 | "docEl_unsafe_", 48 | "__importStar", "sent", "_a.sent", "_b.sent", "_c.sent", 49 | "trans_", "pTrans_", "VTr", "contains_s", "attr_s", "getMediaTag", 50 | "readyState", "binarySearch_", 51 | "getMatchRanges", 52 | "activeEl_unsafe_", "querySelector_unsafe_", "querySelectorAll_unsafe_", 53 | "getSelected", "getComputedStyle_", 54 | "isHTML_", "htmlTag_", "hasTag_", "isInTouchMode_cr_", "_getter_unsafeOnly_not_ff_", "isSafeEl_", 55 | "SafeEl_not_ff_", "GetShadowRoot_", "GetParent_unsafe_", 56 | "scrollingEl_", "fullscreenEl_unsafe_", "frameElement_", "getEditableType_", 57 | "IsAInB_", "getSelectionBoundingBox_", 58 | "center_", "isContaining_", "padClientRect_", "boundingRect_", "getVisibleBoundingRect_", "SubtractSequence_", 59 | "BgUtils_.escapeText_", "escapeText_", "highlight", "cutTitle", "BgUtils_.unicodeSubstring_", "unicodeSubstring_", 60 | "safeObj_", "BgUtils_.safeObj_", 61 | "isEscape_", "getKeyStat_", "keybody_", "_getKeyCharUsingKeyIdentifier" 62 | ], 63 | "pure_getters": true, 64 | "reduce_funcs": false, 65 | "sequences": 30, 66 | "side_effects": true, 67 | "toplevel": false, 68 | "unsafe_arrows": true, 69 | "unsafe_comps": false, 70 | "unsafe_math": true, 71 | "unsafe_methods": true, 72 | "unsafe_proto": true, 73 | "unsafe_regexp": true, 74 | "unsafe": false, 75 | "unused": true, 76 | "warnings": false 77 | // `true` will remove `/a?/.test("")` 78 | // so "unsafe" is not what Vimium C needs 79 | }, 80 | "mangle": { 81 | "properties": { 82 | "regex": "/^_[^_]|_$/", // not uglify /^__.*$/ like module.__default 83 | "builtins": true, 84 | "reserved": [ "__proto__", "$_", "_", "_get" ], 85 | "undeclared": true 86 | }, 87 | "reserved": [ 88 | // # globals 89 | "browser", "chrome", "define", "__filename", "__importStar", 90 | // # expected built-in global variables in content 91 | "WeakMap", "WeakSet", "Set", "InputDeviceCapabilities", "visualViewport", "queueMicrotask", "WeakRef", 92 | // # content global names: 93 | "VimiumInjector", "VApi", 94 | // # front/vomnibar 95 | "VCID", 96 | // # lib/ 97 | "MathParser", 98 | // # pages/ 99 | "VData" 100 | ], 101 | "toplevel": true 102 | } //*/ 103 | } 104 | -------------------------------------------------------------------------------- /scripts/uglifyjs.local.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": { 3 | "ascii_only": true, // if `false` by default, '\xa0' is replaced with '\xc2\xa0', which is wrong 4 | "beautify": true, 5 | "braces": true, 6 | "comments": "/^(?!\\/) ?(?!eslint-(dis|en)able)\\S+[ .]\\S|\\n|^[!#]/", 7 | "indent_level": 2, 8 | "keep_quoted_props": false, 9 | "semicolons": true, 10 | "wrap_func_args": false, 11 | "wrap_iife": true 12 | }, 13 | "compress": { 14 | "booleans": true, 15 | "conditionals": true, 16 | "dead_code": true, 17 | "defaults": false, 18 | "evaluate": true, 19 | "hoist_vars": false, 20 | "join_vars": false, 21 | "keep_fargs": false, 22 | "keep_fnames": true, 23 | "lhs_constants": false, 24 | "passes": 2, 25 | "properties": true, 26 | "pure_getters": true, 27 | "pure_funcs": null, 28 | "side_effects": true, 29 | "unsafe_arrows": true, 30 | "unsafe_comps": false, 31 | "unsafe_math": true, 32 | "unsafe_methods": true, 33 | "unsafe_proto": true, 34 | "unsafe_regexp": true, 35 | "unsafe": false, 36 | "unused": true, 37 | "warnings": true 38 | // `true` will remove `/a?/.test("")` 39 | // so "unsafe" is not what Vimium C needs 40 | }, 41 | "ecma": 2017, 42 | "mangle": false //*/ 43 | } -------------------------------------------------------------------------------- /scripts/words-collect.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // @ts-check 3 | "use strict"; 4 | /** @type {import("./dependencies").ProcessType} */ 5 | /** @type {import("./dependencies").FileSystem} */ 6 | /** 7 | * @typedef { { word: string; occurrence: number; gain: number; es5: number; single: number } } WordItem 8 | */ 9 | // @ts-ignore 10 | var fs = require("fs"); 11 | // @ts-ignore 12 | var process = require("process"); 13 | var lib = require("./dependencies"); 14 | var logger = require("fancy-log"); 15 | var glob = require("glob"); 16 | 17 | var argv = process.argv, argi = 0; 18 | if (/\bnode\b/i.test(argv[argi])) { 19 | argi++; 20 | } 21 | if (/\b(words-collect|gulp)(\.\b|$)/i.test(argv[argi])) { 22 | argi++; 23 | } 24 | 25 | const targetFile = argv[argi] && fs.existsSync(argv[argi]) ? argv[argi++] : "dist/content/vimium-c.js"; 26 | const contentText = lib.readFile(targetFile); 27 | const minWordLen = argv[argi] && !isNaN(+argv[argi]) ? +argv[argi++] : 8; 28 | const order = argv[argi] && "occurrence".includes(argv[argi]) ? (argi++, "occ") : "gain"; 29 | const minShownVal = argv[argi] && !isNaN(+argv[argi]) ? +argv[argi++] : 24; 30 | 31 | const wordRe = /(["'])(?:\\.|)+?\1|\w+/g; 32 | const stopWords = ` 33 | function instanceof 34 | endsWith includes startsWith 35 | localName 36 | `; 37 | 38 | let allLongWords = getLongWords(contentText, minWordLen); 39 | allLongWords = allLongWords.filter(order === "occ" ? a => a.occurrence >= minShownVal : a => a.gain >= minShownVal); 40 | allLongWords.sort(order === "occ" ? (a, b) => b.occurrence - a.occurrence : (a, b) => b.gain - a.gain); 41 | logger.info("Here're potential gains in %o bytes of %o:", contentText.length, targetFile); 42 | console.table(allLongWords); 43 | 44 | const tsFiles = glob.sync("content/*.ts").concat(glob.sync("lib/*.ts")).filter(i => !i.includes("inject")); 45 | const tsContent = tsFiles.map(file => lib.readFile(file)).join("\n").replace(/\/\/[^\r\n]+|\/\*[^]*\*\//, ""); 46 | const allSimilarWords = getSimilarWords(tsContent); 47 | if (allSimilarWords.length > 0) { 48 | logger.info("Found similar words:\n", allSimilarWords) 49 | } else { 50 | logger.info("No similar property names found") 51 | } 52 | 53 | /** 54 | * @param {string} text 55 | * @param {number} min_len - minimum length of a word 56 | * @returns {WordItem[]} 57 | */ 58 | function getLongWords(text, min_len = 8) { 59 | /** @type {Map} */ 60 | const wordMap = new Map(); 61 | for (const word of text.match(wordRe)) { 62 | if (word.length >= min_len) { 63 | wordMap.set(word, (wordMap.get(word) || 0) + 1); 64 | } 65 | } 66 | for (const word of stopWords.trim().split(/\s+/)) { 67 | wordMap.delete(word); 68 | } 69 | /** @type {WordItem[]} */ 70 | const longWords = []; 71 | for (const [word, v] of wordMap) { 72 | const gain = word.length * v - (/** definition */ (word.length + 4) + /** reference */ 4 * v); 73 | if (gain > 0) { 74 | longWords.push({ word, occurrence: v, gain, es5: gain - 22, single: word.length - 4 }); 75 | } 76 | } 77 | return longWords; 78 | } 79 | 80 | /** 81 | * @param {string} text 82 | * @returns {string[]} 83 | */ 84 | function getSimilarWords(text) { 85 | /** @type {Map} */ 86 | const wordMap = new Map(); 87 | for (const word of text.match(/\w+/g)) { 88 | const type = word[0] === "_" ? 1 : word.endsWith("_") ? 2 : 0; 89 | if (type && word.length > 2) { 90 | const key = word.slice(type === 1 ? 1 : 0, type === 2 ? -1 : word.length); 91 | const old = wordMap.get(key) || 0; 92 | // @ts-ignore 93 | wordMap.set(key, old | type); 94 | } 95 | } 96 | wordMap.delete("this"); 97 | wordMap.delete("self"); 98 | return [...wordMap].filter(([_, v]) => v === 3).map(item => item[0]); 99 | } 100 | -------------------------------------------------------------------------------- /tests/dom/doc-zoom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | docEl.zoom vs. devRatio 5 | 6 | 7 |
Browser:
8 |
9 | Top frame: 10 |
11 |
Child frame:
12 | 13 | 61 | 62 | -------------------------------------------------------------------------------- /tests/dom/editable-in-shadow-of-body.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test behavior of rich text editing in shadow DOM 6 | 7 | 8 | 9 | 10 |
11 | On Firefox 91.0b7 / Firefox and Linux-Like system,
12 | a [contenteditable=true] in 14 | <body>.shadowRoot 15 | doesn't respond on Backspace / Delete. 16 |
17 |

18 | Current is on 19 |

20 |
21 |
<body> .shadow
22 |
23 | 24 |
25 |
26 |
27 |
body > div .shadow
28 |
29 | 30 |
31 |
32 | 85 | 86 | -------------------------------------------------------------------------------- /tests/dom/firefox-position_fixed-in-dialog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test position:fixed in dialog 7 | 8 | 9 | 10 |
11 |
12 | Test position:fixed on a child of <dialog> when there's a global filter. 13 |
14 |
15 | 16 |
17 | Here should in fixed-center

18 | 19 |
20 |
21 | 36 | 37 |
38 |
39 |
40 |
41 | 66 | 67 | -------------------------------------------------------------------------------- /tests/dom/handleevent.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Does handleEvent throw 6 | 7 | 8 |
9 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /tests/dom/input-select-show-picker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test showPicker 6 | 7 | 8 | 9 |

Test showPicker on Chromium 99+

10 | 15 |

16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |

26 |

27 | 28 | 29 |
30 |
31 |
32 |
33 |
34 |

35 | 36 | 37 | 38 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /tests/dom/microtasks-during-events.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test microtasks during dispatching events 6 | 7 | 8 |
9 |

Test how microtasks go during JavaScript code dispatching events

10 | 11 |
12 | 29 | 30 | -------------------------------------------------------------------------------- /tests/dom/move-node-across-worlds.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Wrappers in different JS worlds 6 | 7 | 8 |
before iframe
9 |
10 | 12 |
13 |
14 |
after iframe
15 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /tests/dom/named-property.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Named Property Test 6 | 7 | 8 | 9 |
10 | parentELement:
11 | parentNode:
15 |
16 | contains:
17 | getBoundingClientRect:
18 | addEventListener:
19 | dispatchEvent:
20 |
21 |
22 |   
23 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /tests/dom/named_property_getter.md: -------------------------------------------------------------------------------- 1 | # named_property_getter 2 | 3 | ## frames 4 | 5 | - **framset**: CrossContext, OverrideBuiltins 6 | - ***fixed (some are ignored)*** for Chrome 7 | - `[name] = Window` 8 | - test page: http://www.w3school.com.cn/tiy/loadtext.asp?f=html_frame_cols 9 | - removed since C70, commit 6a866d29f4314b990981119285da46540a50742c 10 | - @see https://bugs.chromium.org/p/chromium/issues/detail?id=695891 11 | - BrowserVer.MinFramesetHasNoNamedGetter 12 | - according to tests and source code, its named getter requires `.contentDocument` is valid 13 | - not on MS Edge 18.17763 14 | - not on modern versions of Firefox 15 | - according to logs of https://dxr.mozilla.org/mozilla-central/source/dom/webidl/HTMLFrameSetElement.webidl 16 | 17 | Comment: on C35 and C70, `iframe` and `frame` have no named property getters 18 | 19 | ## forms 20 | 21 | - **form**: CrossContext, OverrideBuiltins 22 | - ***fixed*** for Chrome 23 | - `[name] = HTMLFormControlElement | RadioNodeList` 24 | - CrossContext: not on Firefox 65 25 | - does cross contexts on MS Edge 18.17763 26 | - `input[form]` takes effects only if the input is connected 27 | - https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#reset-the-form-owner 28 | 29 | ## plugins 30 | 31 | - **object**, **embed**: CrossContext?, OverrideBuiltins? 32 | - ***ignored*** 33 | - `[name] = unknown (any)` 34 | - OverrideBuiltins: added since C45, commit b3afcae7d957f15d5a23c6dfd891be3bd21f3e21 35 | - CrossContext: removed since C61, commit 1e196e33687bd22a33c8b2116956754b90b29bde 36 | 37 | ### *Conclusion:* 38 | * The two are unsafe only during C45 and C60, and when installed plugins are not well designed. 39 | * In the meantime, the feature to click Flash areas are important enough. 40 | * Therefore, ignore them 41 | 42 | ### *access paths for embed/object plugins (2018/10/15)* 43 | 44 | 1. V8HTMLEmbedElement::namedPropertyGetterCustom | V8HTMLObjectElement::namedPropertyGetterCustom 45 | 2. (annoymous_namespace)::GetScriptableObjectProperty (in v8_html_plugin_element_custom.cc) 46 | 3. HTMLPlugInElement::PluginWrapper 47 | 4. WebPluginContainerImpl::ScriptableObject 48 | 5. WebPlugin::V8ScriptableObject (virtual, hidden) 49 | - WebViewPlugin::V8ScriptableObject 50 | - PluginPlaceholderBase::GetV8ScriptableObject (return empty) 51 | - **BrowserPlugin::V8ScriptableObject** (public, CONTENT_EXPORT) 52 | - MimeHandlerViewContainer::V8ScriptableObject 53 | 1. MimeHandlerViewContainerBase::GetScriptableObject 54 | 2. ScriptableObject::Create 55 | 3. ScriptableObject::GetNamedProperty (only supports "postMessage") 56 | - PepperWebPluginImpl::V8ScriptableObject 57 | 1. **PepperPluginInstanceImpl::GetMessageChannelObject** (public, CONTENT_EXPORT) 58 | 2. PepperPluginInstanceImpl::message_channel_object_ (private) 59 | 3. MessageChannel::Create (public, hidden) 60 | 4. gin::Handle::ToV8()->ToObject 61 | 5. MessageChannel::GetNamedProperty (private, hidden) 62 | - MessageChannel::internal_named_properties_ 63 | 1. MessageChannel::SetReadOnlyProperty (public, hidden) 64 | 2. **PepperPluginInstanceImpl::SetEmbedProperty** (public, CONTENT_EXPORT) 65 | - NexeLoadManager::SetReadOnlyProperty (public, hidden) 66 | 1. NexeLoadManager::set_nacl_ready_state (set "readyState") 67 | 2. NexeLoadManager::SetLastError (set "lastError") 68 | 3. NexeLoadManager::set_exit_status (set "exitStatus") 69 | - PluginObject::GetNamedProperty (public, hidden) 70 | 1. PluginObject::GetPropertyOrMethod 71 | 2. **PPP_Class_Deprecated**::HasProperty | PPP_Class_Deprecated::HasMethod (accessible) 72 | 3. PPP_Class_Deprecated::GetProperty | PluginObject::Call 73 | 4. PPP_Class_Deprecated::Call 74 | 75 | ## roots 76 | 77 | - **window**: CrossContext 78 | - ***fixed*** 79 | - `iframe[name],frame[name]`: mapped to `element.contentWindow` 80 | - `[name]` needs to be set up during loading 81 | - `[id]`: mapped to the `element` itself 82 | - dynamic; but with a lower priority than `iframe[name]` 83 | - **document**: OverrideBuiltins 84 | - ***fixed*** 85 | - those whose `Element::GetNamedItemType() != NamedItemType::kNone` 86 | - `embed, form, iframe, image, object` 87 | - doc: https://html.spec.whatwg.org/multipage/dom.html#dom-document-nameditem 88 | 89 | The overriding values may be: 90 | * `HTMLCollection` if multi matched else 91 | * `Window` if `iframe,frame` else 92 | * `Element` if `[id]` 93 | 94 | Traces: 95 | - https://cs.chromium.org/chromium/src/third_party/blink/renderer/bindings/core/v8/custom/v8_window_custom.cc?q=NamedPropertyGetterCustom&dr=CSs&l=300 96 | - https://cs.chromium.org/chromium/src/third_party/blink/renderer/bindings/core/v8/local_window_proxy.cc?q=GetNamedProperty&g=0&l=482 97 | 98 | Comment: confirmed on C35 and C70 99 | 100 | # note 101 | 102 | On Chrome, `window.Var && typeof window.Var !== "function"` is not enough, 103 | because on some old versions (< 58) of Chrome, `typeof ` is unfortunately `"function"`. 104 | Related doc: https://www.chromestatus.com/features/5715026367217664 105 | 106 | My filed issue about `
` is @see https://bugs.chromium.org/p/chromium/issues/detail?id=897399 107 | and https://github.com/whatwg/html/issues/4458 . 108 | 109 | And there's a test page: https://jsfiddle.net/bvumn4g3/ (../tests/dom/named-property.html) 110 | 111 | # Firefox: Xray vision 112 | 113 | Firefox has applied [Xray vision](https://developer.mozilla.org/en-US/docs/Mozilla/Tech/Xray_vision) 114 | to help protect privileged JavaScript code. 115 | * http://devdoc.net/web/developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts.html#DOM_access 116 | * https://dxr.mozilla.org/mozilla-central/source/dom/bindings/Codegen.py#11708 117 | -------------------------------------------------------------------------------- /tests/dom/pointer-events-test.html: -------------------------------------------------------------------------------- 1 | 2 | Pointer event tests 3 | 4 | 5 | 6 | 7 |
8 | -------------------------------------------------------------------------------- /tests/dom/script_execution_order.md: -------------------------------------------------------------------------------- 1 | # Script Execution Order 2 | 3 | * `[src][defer]:parser` 4 | * a list to execute on after-parsing and before-doc-ready 5 | * `[src]:parser` 6 | * pending parsing-blocking 7 | * `[src]${async=false}:from-code` 8 | * list to execute in order as soon as possible 9 | * `[src]:from-code`, `[src][async]:parser` 10 | * set to execute as soon as possible 11 | * `:not([src]):parser` 12 | * pending parsing-blocking + :ready 13 | * `default` 14 | * execute immediately 15 | 16 | 17 | ## Conclusion 18 | 19 | `[src]${async=false}:from-code` is not suitable for `extend_click`, 20 | and it can only use ` 189 | 190 | -------------------------------------------------------------------------------- /tests/unit/first-letter.md: -------------------------------------------------------------------------------- 1 | # The influence of `::first-letter` 2 | 3 | If using `::first-letter`, then JS code will be faster, but the below is performance data: 4 | 5 | | item | sub-item | `.MC` | `::1st-L` | unit | 6 | | :--------- | :------- | --------: | --------: | :------ | 7 | | matchHints | self | 0.26 | **0.19** | ms | 8 | | | total | 0.52 | **0.32** | ms | 9 | | recalc | affected | **27** | 61 | element | 10 | | | time | **0.66** | 2.27 | ms | 11 | | layout | affected | **0** | 122 | node | 12 | | | time | **0** | 1.62 | ms | 13 | | | root | (none) | #document | | 14 | | tree | | **0.68** | 0.92 | ms | 15 | | paint | whole | 1.26 | 1.47 | ms | 16 | | compose | | 0.31 | 0.33 | ms | 17 | | **total** | | **8.8** | 10.1 | ms | 18 | 19 | Then, `::first-letter` is bad in this case. 20 | 21 | ## Testing code 22 | 23 | ``` diff 24 | diff --git a/content/hint_filters.ts b/content/hint_filters.ts 25 | index a56d7f69..0942531e 100644 26 | --- a/content/hint_filters.ts 27 | +++ b/content/hint_filters.ts 28 | @@ -1,10 +1,10 @@ 29 | -import { chromeVer_, createRegExp, Lower, math, max_, OnChrome, OnEdge, OnFirefox } from "../lib/utils" 30 | +import { chromeVer_, createRegExp, Lower, math, max_, OnChrome, OnEdge, OnFirefox, min_ } from "../lib/utils" 31 | import { 32 | createElement_, querySelector_unsafe_, getInputType, htmlTag_, docEl_unsafe_, ElementProto, removeEl_s, ALA, attr_s, 33 | contains_s, setClassName_s, setVisibility_s, toggleClass_s, textContent_s, appendNode_s 34 | } from "../lib/dom_utils" 35 | import { 36 | - HintItem, FilteredHintItem, MarkerElement, HintText, isHC_, 37 | + HintItem, FilteredHintItem, MarkerElement, HintText, isHC_, hint_box, 38 | hintMode_, useFilter_, coreHints, hintKeyStatus, KeyStatus, hintChars, allHints, setMode, resetMode, hintOptions 39 | } from "./link_hints" 40 | import { bZoom_, padClientRect_, getBoundingClientRect_, dimSize_ } from "../lib/rect" 41 | @@ -400,8 +400,16 @@ export const renderMarkers = (hintItemArray: readonly HintItem[]): void => { 42 | toggleClass_s(marker, "TH", 1) 43 | right = ": " + right; 44 | } else { 45 | - right = hint.a.slice(-1); 46 | - for (const markerChar of hint.a.slice(0, -1)) { 47 | + right = hint.a 48 | + if (right.length > 2) { 49 | + if (OnChrome && Build.MinCVer < BrowserVer.MinEnsured$ParentNode$$appendAndPrepend && noAppend) { 50 | + appendNode_s(marker, new Text(right[0])) 51 | + } else { 52 | + marker.append!(right[0]) 53 | + } 54 | + right = right.slice(-1) 55 | + } 56 | + for (const markerChar of hint.a.slice(1, -1)) { 57 | const node = createElement_("span") 58 | node.textContent = markerChar 59 | if (Build.BTypes & BrowserType.Chrome && Build.MinCVer < BrowserVer.MinEnsured$ParentNode$$appendAndPrepend) { 60 | @@ -502,7 +510,7 @@ export const matchHintsByKey = (keyStatus: KeyStatus 61 | } else { 62 | zIndexes_ = zIndexes_ && null; 63 | keyStatus.k = sequence; 64 | - const notDoSubCheck = !keyStatus.b, limit = sequence.length - keyStatus.b, 65 | + const notDoSubCheck = !keyStatus.b, limit = sequence.length - keyStatus.b, elLimit = limit && limit - 1, 66 | fewer = doesDetectMatchSingle > 0, 67 | wantedPrefix = sequence.slice(0, limit), lastChar = notDoSubCheck ? "" : sequence[limit] 68 | hintArray = keyStatus.c = (fewer ? hintArray : allHints!).filter(hint => { 69 | @@ -511,15 +519,15 @@ export const matchHintsByKey = (keyStatus: KeyStatus 70 | return pass; 71 | }); 72 | type MarkerElementChild = Exclude; 73 | - for (const hint of hintArray) { 74 | - const ref = hint.m.childNodes, hintN = hint.i 75 | + for (const hint of maxPrefixLen_ ? hintArray : []) { 76 | + const ref = hint.m.children, hintN = hint.i 77 | // https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/dom/dom_token_list.cc?q=DOMTokenList::setValue&g=0&l=258 78 | // shows that `.classList.add()` costs more 79 | - for (let j = limit > hintN ? hintN : limit, end = limit > hintN ? limit : hintN; j < end; j++) { 80 | - ((ref[j] as MarkerElementChild).className = j < limit ? "MC" : ""); 81 | + for (let j = min_(elLimit, hintN), end = max_(elLimit, hintN); j < end; j++) { 82 | + ((ref[j] as MarkerElementChild).className = j < elLimit ? "MC" : ""); 83 | } 84 | - hint.i = limit 85 | + hint.i = elLimit 86 | } 87 | - return hintArray.length ? (isHC_ && setMode(hintMode_), 2) : 0 88 | + return hintArray.length ? (isHC_ && setMode(hintMode_), hint_box!.classList.toggle("ML", limit > 0), 2) : 0 89 | } 90 | } 91 | diff --git a/front/vimium-c.css b/front/vimium-c.css 92 | index db895c9b..f1b99aa7 100644 93 | --- a/front/vimium-c.css 94 | +++ b/front/vimium-c.css 95 | @@ -18,7 +18,7 @@ padding:4px 4px 1px;right:152px;text-overflow:ellipsis;white-space:nowrap} 96 | .Flash{box-shadow:0 0 4px 2px #4183c4;padding:1px}.AbsF{padding:0;position:absolute}.Sel{box-shadow:0 0 4px 2px #fa0} 97 | .Frame{border:5px solid #ff0}.Frame,.HUD:after{box-sizing:border-box;height:100%;left:0;top:0;width:100%} 98 | .Omnibar{left:calc(10vw - 12px);top:64px;width:calc(80vw + 24px)}.O2{left:calc(10% - 12px);width:calc(80% + 24px)} 99 | -.BH{color:#902809}.MC,.MH{color:#d4ac3a}.One{border-color:#fa7}.UI,.DHM{pointer-events:all} 100 | +.BH{color:#902809}.MC,.MH,.ML>.LH::first-letter{color:#d4ac3a}.One{border-color:#fa7}.UI,.DHM{pointer-events:all} 101 | .D>.LH{background:linear-gradient(#cb0,#c80)}.HUD.D{color:#ccc}.HUD.D:after{background:#222} 102 | @media(forced-colors:active){.R{border-radius:0} 103 | .HM>.LH,.HUD:after{background:#000;border-radius:0}.Flash{outline:4px solid #fff}} 104 | ``` 105 | -------------------------------------------------------------------------------- /tests/unit/keyboard-layouts.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | let layouts = [ 4 | `# French 5 | 6 | &é"'(-è_çà)= 7 | azertyuiop^$* 8 | qsdfghjklmù 9 | wxcvbn,;:! 10 | `, 11 | 12 | `# German (German (IBM) is the same) 13 | 14 | 1234567890ß 15 | qwertzuiopü+# 16 | asdfghjklöä 17 | yxcvbnm,.- 18 | `, 19 | 20 | `# Spanish 21 | 22 | 1234567890'¡ 23 | qwertyuiop+ç 24 | asdfghjklñ 25 | zxcvbnm,.- 26 | 27 | ## ESV 28 | 29 | 1234567890- 30 | qwertyuiop÷ 31 | asdfghjklñç 32 | zxcvbnm,.= 33 | `, 34 | 35 | `# Português 36 | 37 | 1234567890'« 38 | qwertyuiop+´~ 39 | asdfghjklçº 40 | zxcvbnm,.- 41 | 42 | ## PTB 43 | 44 | 1234567890-= 45 | qwertyuiop´[] 46 | asdfghjklç~~ 47 | zxcvbnm,.; 48 | `, 49 | 50 | `# Russian 51 | 52 | 1234567890-= 53 | йцукенгшщзхъ\ 54 | фывапролджэ 55 | ячсмитьбю. 56 | `, 57 | 58 | `# Cyrillic 59 | 60 | 1234567890'+ 61 | љњертзуиопшђж 62 | асдфгхјклчћ 63 | ѕџцвбнм,.- 64 | `, 65 | 66 | `# Arabic 67 | 68 | 1234567890-= 69 | ضصثقفغعهخحجد\ 70 | شسيبلاتنمكط 71 | ئءؤرلاىةوزظ 72 | `, 73 | 74 | `# Korean 75 | 76 | 1234567890-= 77 | qwertyuiop[]\ 78 | asdfghjkl;' 79 | zxcvbnm,./ 80 | 81 | # Hangul 82 | ㅂ ㅈ ㄷ ㄱ ㅅ ㅛ ㅕ ㅑ ㅐ ㅔ [ ] 83 | ㅁ ㄴ ㅇ ㄹ ㅎ ㅗ ㅓ ㅏ ㅣ ; ' 84 | ㅋ ㅌ ㅊ ㅍ ㅠ ㅜ ㅡ , . / 85 | 86 | # Turkish 87 | 1234567890*- 88 | qwertyuıopğü, 89 | asdfghjklşi 90 | zxcvbnmöç. 91 | 92 | !'^+%&/()=?_ 93 | QWERTYUIOPĞÜ; 94 | ASDFGHJKLŞİ 95 | ZXCVBNMÖÇ: 96 | ` 97 | ].join("\n").split("\n").filter( 98 | line => !(line[0] === "#" || line[0] === "(" && line.slice(-1) === ")") 99 | ).join(""); 100 | const getRe = name => name instanceof RegExp ? name : new RegExp(`[\\p{${name}}]`, "ug"); 101 | const filter = (text, re) => [...new Set(text.match(re, ""))].sort(); 102 | const parse = arr => [ 103 | arr, 104 | arr.map(i=>i.codePointAt()).filter(i => i >= 128), 105 | arr.length 106 | ]; 107 | const print = (category, text) => console.log(`${category}:`, ...parse(filter(text, getRe(category)))); 108 | print("Ll", layouts); 109 | print("Lo", layouts); 110 | print("Lu", layouts); 111 | print("Lt", layouts); 112 | print("Lm", layouts); 113 | 114 | const range = (start, end, conv=String.fromCharCode) => { 115 | const arr = []; 116 | for (let i = start, end2 = end || start + 1; i < end2; i++) { arr.push(i); } 117 | return conv ? conv(...arr) : arr; 118 | }; 119 | const hex = (start, end) => range(start, end, null).map(i => i.toString(16)); 120 | 121 | print("Ll", range(1070, 1120)); 122 | print("Lo", range(1569, 1614)); 123 | 124 | var hasConsole = 1; 125 | // hasConsole = 0; 126 | if (typeof require === "function" && hasConsole) { 127 | Object.assign(require('repl').start("node> ").context, { 128 | hex, range, print, filter, parse, getRe, layouts 129 | }); 130 | } 131 | -------------------------------------------------------------------------------- /tests/unit/links-across-iframes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test link detection across iframes 6 | 7 | 8 | 9 |
before iframe
10 |
11 |
12 | 13 |
14 | 1st iframe ; iframe 2nd 15 |
16 | 17 |
18 |
19 |
after iframe
20 | 21 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /tests/unit/null_proto.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var t; 4 | var arr = [ 5 | Object.create(null), 6 | {__proto__: null}, 7 | (t={}, t.__proto__ = null, t), 8 | (t={}, t["__proto__"] = null, t), 9 | {__proto__: null, ["__proto__"]: null} 10 | ]; 11 | try { 12 | arr.push(eval('({... {__proto__:null, ["__proto__"]:null}})')) 13 | } catch (_e) {} 14 | 15 | arr.forEach(function (i, ind) { console.log("[" + ind + "]", i, i.hasOwnProperty || "(null-ed)")}); 16 | 17 | arr.forEach(function (i, ind) { 18 | i.__proto__ = { a: 1 }; 19 | console.log("[" + ind + "]", i, i.a || "(still null-ed)", i.__proto__ || "(unknown error)"); 20 | }); 21 | 22 | arr.forEach(function (i, ind) { 23 | i["__proto__"] = { a: 1 }; 24 | console.log("[" + ind + "]", i, i.a || "(still null-ed)", i.__proto__ || "(unknown error)"); 25 | }); 26 | -------------------------------------------------------------------------------- /tests/unit/order-when-resolve-promise.html: -------------------------------------------------------------------------------- 1 | 2 | Test resolve(Promise) 3 | 4 |
5 |
6 |
7 |
8 | 9 | 42 | 43 | -------------------------------------------------------------------------------- /tests/unit/performance-now.md: -------------------------------------------------------------------------------- 1 | ## Firefox 2 | * macOS: `mach_absolute_time` 3 | * absolute elapsed time since system boot 4 | * https://dxr.mozilla.org/mozilla-central/source/mozglue/misc/TimeStamp_darwin.cpp#48 5 | * Posix: 6 | * `clock_gettime(CLOCK_MONOTONIC)` 7 | * https://dxr.mozilla.org/mozilla-central/source/mozglue/misc/TimeStamp_posix.cpp#73 8 | * according to https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_gettime.html, 9 | the base of mono time is unspecified while "does not change after system start-up time" 10 | * Windows: 11 | * `QueryPerformanceCounter()`, or fallback to `GetTickCount64()` 12 | * `QueryPerformanceCounter`: the total number of ticks since the Windows operating system was started, 13 | including the time when the machine was in a sleep state such as standby, hibernate, or connected standby 14 | * `GetTickCount64`: milliseconds that have elapsed since the system was started 15 | * https://dxr.mozilla.org/mozilla-central/source/mozglue/misc/TimeStamp_windows.cpp#489 16 | 17 | ## Chrome 18 | * macOS: 19 | * iOS: 20 | * `clock_gettime(CLOCK_MONOTONIC)` since iOS 10 (2016), or subtract `boottime` from `CFAbsoluteTimeGetCurrent()` 21 | * https://opensource.apple.com/source/Libc/Libc-1158.1.2/gen/clock_gettime.c.auto.html calls `_mach_boottime_usec` 22 | * it's said on macOS mojave, `mach_absolute_time` stops while the device is sleeping in line 75 23 | * according to https://www.unix.com/man-page/mojave/3/clock_getres/, 24 | mono time "will continue to increment while the system is asleep". 25 | * others: 26 | * `mach_absolute_time()`, which is safe even considering about sleeps 27 | * https://source.chromium.org/chromium/chromium/src/+/master:base/time/time_mac.cc;l=63?q=ComputeCurrentTicks 28 | * Posix: 29 | * `clock_gettime(CLOCK_MONOTONIC)` 30 | * https://source.chromium.org/chromium/chromium/src/+/master:base/time/time_now_posix.cc;l=89 31 | * Windows: 32 | * `QueryPerformanceCounter()` 33 | * https://source.chromium.org/chromium/chromium/src/+/master:base/time/time_win.cc;l=165 34 | 35 | 36 | # Related 37 | 38 | ## Chromium Bug 166153 39 | 40 | In https://bugs.chromium.org/p/chromium/issues/detail?id=166153, they want to use CLOCK_BOOTTIME or some others instead. 41 | 42 | ## Precision 43 | 44 | `.now + .timeOrigin - Date.now()` will be big if system date time gets updated (like by NTP service). 45 | 46 | Here's a page to test the difference: http://persistent.info/web-experiments/performance-now-sleep/ . 47 | 48 | ## `mach_absolute_time` 49 | * **TEST NEEDED** 50 | * stops during a sleep on a PowerPC CPU, but not on an Intel CPU, 51 | according to https://www.python.org/dev/peps/pep-0418/#mach-absolute-time 52 | * Firefox 3.6.28 is the last version of Firefox that works with Mac OS X and PowerPC 53 | * https://support.mozilla.org/en-US/kb/firefox-no-longer-works-mac-os-10-4-or-powerpc 54 | * As for Chromium, there's https://launchpad.net/ubuntu/xenial/powerpc/chromium-browser-l10n (16.04), but not on 18.04 55 | * Apple released Mac OS X v10.6 "Snow Leopard" on August 28, 2009 as Intel-only 56 | * https://en.wikipedia.org/wiki/Mac_transition_to_Intel_processors 57 | * Mac OS X v10.7 "Lion" dropped support for Rosetta (a dynamic binary translator) 58 | * It seems that Chrome doesn't support PowerPC now 59 | -------------------------------------------------------------------------------- /tests/unit/proxy-tostring.js: -------------------------------------------------------------------------------- 1 | var a = function() {}, 2 | b = new Proxy(a, { 3 | get (t,k) { console.log('get', t, k); return k ==='toString'? () => '123' : t[k] }, 4 | apply (t,s,a) { console.log('apply', t, s, a); return t.apply(s, a) } 5 | }); 6 | if (Function.prototype.toString.call(b).replace(/\s+/g, " ") !== "function () { [native code] }") { 7 | console.log("Assert error!!! Proxy behavior may have changed"); 8 | } else { 9 | console.log("Passed"); 10 | } 11 | -------------------------------------------------------------------------------- /tests/unit/storage-serialize.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | function doTest(step, encoder, repeat) { 3 | var step = step || 20; 4 | var repeat = repeat || 1; 5 | var encoder = encoder != null ? +encoder : 1; 6 | var key = 'keyMappings'; 7 | var BG_ = window.BG_ || window; 8 | var bgSettings_ = BG_.define.settings; 9 | var d = bgSettings_.get_(key, true); 10 | repeat > 1 && (d = d.repeat(repeat)); 11 | var err = []; 12 | err.push(BG_.deserializeSync(key, BG_.serializeSync(key, d, encoder)) !== d); 13 | for (let i = 0; i < d.length; i += step) { 14 | for (let j = i + step; j < d.length; j += step * 2) { 15 | err.push(BG_.deserializeSync(key, BG_.serializeSync(key, d.substring(i, j), encoder)) !== d.substring(i, j)); 16 | } 17 | } 18 | key = 'exclusionRules'; 19 | for (let i = 0; i < d.length; i += step) { 20 | for (let j = i + step; j < d.length; j += step * 2) { 21 | err.push(BG_.deserializeSync(key, BG_.serializeSync(key, d.substring(i, j), encoder)) !== d.substring(i, j)); 22 | err.push(BG_.deserializeSync(key, BG_.serializeSync(key, {d: d.substring(i, j)}, encoder)).d !== d.substring(i, j)); 23 | } 24 | } 25 | d = bgSettings_.get_("searchEngines", true); 26 | repeat > 1 && (d = d.repeat(repeat)); 27 | for (let i = 0; i < d.length; i += step) { 28 | for (let j = i + step; j < d.length; j += step * 2) { 29 | err.push(BG_.deserializeSync(key, BG_.serializeSync(key, d.substring(i, j), encoder)) !== d.substring(i, j)); 30 | err.push(BG_.deserializeSync(key, BG_.serializeSync(key, {d: d.substring(i, j)}, encoder)).d !== d.substring(i, j)); 31 | } 32 | } 33 | console.log("err", err.map(i=>+i).reduce((i,j) => i + j, 0)); 34 | return err.map((i, ind) => i ? ind : -1).filter(ind => ind >= 0); 35 | } 36 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": false, 4 | "alwaysStrict": true, 5 | "downlevelIteration": false, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "incremental": true, 9 | "keyofStringsOnly": false, 10 | "listEmittedFiles": false, 11 | "module": "amd", 12 | "moduleResolution": "node", 13 | "newLine": "lf", 14 | "noEmitHelpers": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "noImplicitAny": true, 17 | "noImplicitReturns": true, 18 | "noImplicitThis": true, 19 | "noImplicitUseStrict": false, 20 | "noLib": true, 21 | "noUncheckedIndexedAccess": false, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noPropertyAccessFromIndexSignature": true, 25 | "outDir": ".", 26 | "plugins": [ 27 | ], 28 | "preserveConstEnums": false, 29 | "removeComments": false, 30 | "resolveJsonModule": true, 31 | "rootDir": ".", 32 | "skipDefaultLibCheck": true, 33 | "strict": true, 34 | "strictBindCallApply": true, 35 | "strictFunctionTypes": true, 36 | "strictNullChecks": true, 37 | "strictPropertyInitialization": false, 38 | "target": "es2017", 39 | "tsBuildInfoFile": "scripts/.ts.build", 40 | "typeRoots": ["typings"], 41 | "types": ["base", "lib", "build"] 42 | }, 43 | "exclude": [ 44 | ".*", 45 | "**/*.json", 46 | "dist", 47 | "**/node_modules", 48 | "**/.*/", 49 | "**/scripts", 50 | "test*", 51 | "weidu" 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "references": [ 3 | { "path": "background" }, 4 | { "path": "content" }, 5 | { "path": "front" }, 6 | { "path": "pages" }, 7 | ], 8 | "exclude": [ 9 | "**/*" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /typings/base/es.d.ts: -------------------------------------------------------------------------------- 1 | /*! ***************************************************************************** 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 9 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 10 | MERCHANTABLITY OR NON-INFRINGEMENT. 11 | 12 | See the Apache Version 2.0 License for specific language governing permissions 13 | and limitations under the License. 14 | 15 | Modified by gdh1995 (github). 16 | ***************************************************************************** */ 17 | 18 | 19 | /// 20 | 21 | 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | -------------------------------------------------------------------------------- /typings/base/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | /// 5 | 6 | type Exclude = T extends U ? never : T; // Remove types from T that are assignable to U 7 | type Extract = T extends U ? T : never; // Remove types from T that are not assignable to U 8 | type Writable = { -readonly [P in keyof T]-?: T[P] }; 9 | type NonNullable = T extends null | undefined | void ? never : T; // Remove null and undefined from T 10 | type TypeName = 11 | T extends string ? "string" : 12 | T extends number ? "number" : 13 | T extends boolean ? "boolean" : 14 | T extends undefined ? "undefined" : 15 | T extends Function ? "function" : 16 | "object"; 17 | type ToString = T extends null ? "null" : T extends undefined ? "undefined" : 18 | T extends boolean ? T extends true ? "true" : "false" : 19 | T extends string ? string extends T ? string : T : 20 | T extends number ? number extends T ? string : `${T}` : 21 | unknown 22 | type Parameters = F extends (...args: infer A) => any ? A : never; 23 | type ConstructorParameters any> = T extends new (...args: infer P) => any ? P : never; 24 | type ReturnType = T extends (...args: any[]) => infer R ? R : never; 25 | type InstanceType any> = T extends new (...args: any[]) => infer R ? R : any; 26 | type PromiseOr = T | Promise 27 | type Unpacked = 28 | T extends (infer U)[] ? U : 29 | T extends (...args: any[]) => infer U ? U : 30 | T extends Promise ? U : 31 | T; 32 | 33 | // not write `{ [k extends Exclude<...>]: ... }` to avoid losing property tracking 34 | type Omit = T extends object ? Pick> : never 35 | 36 | type TypeToAssert = 37 | { readonly [P in Others]: unknown; } & { readonly [key in Key]?: Child[key]; }; 38 | type TypeToPick = 39 | { readonly [key in Key]?: Child[key]; }; 40 | 41 | type UnionToIntersection = (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never; 42 | 43 | interface WeakRef { deref (): V | undefined } 44 | interface WeakRefConstructor { 45 | new (entries: V): WeakRef 46 | readonly prototype: WeakRef 47 | } 48 | /** WeakRefConstructor | undefined */ 49 | declare var WeakRef: unknown 50 | 51 | interface FinalizationRegistry { 52 | /** 53 | * Registers an object with the registry. 54 | * @param target The target object to register. 55 | * @param heldValue The value to pass to the finalizer for this object. This cannot be the 56 | * target object. 57 | * @param unregisterToken The token to pass to the unregister method to unregister the target 58 | * object. If provided (and not undefined), this must be an object. If not provided, the target 59 | * cannot be unregistered. 60 | */ 61 | register(target: object, heldValue: T, unregisterToken?: object): void; 62 | 63 | /** 64 | * Unregisters an object from the registry. 65 | * @param unregisterToken The token that was used as the unregisterToken argument when calling 66 | * register to register the target object. 67 | */ 68 | unregister(unregisterToken: object): void; 69 | } 70 | 71 | interface FinalizationRegistryConstructor { 72 | readonly prototype: FinalizationRegistry; 73 | 74 | /** 75 | * Creates a finalization registry with an associated cleanup callback 76 | * @param cleanupCallback The callback to call after an object in the registry has been reclaimed. 77 | */ 78 | new(cleanupCallback: (heldValue: T) => void): FinalizationRegistry; 79 | } 80 | 81 | declare var FinalizationRegistry: FinalizationRegistryConstructor; 82 | 83 | type ReplaceStrOnce 84 | = string extends S ? string : string extends A ? string : A extends `${infer x}${S}${infer y}` ? `${x}${T}${y}` : A 85 | type ReplaceStrAll 86 | = string extends S ? string : string extends A ? string 87 | : A extends `${infer x}${S}${infer y}` ? ReplaceStrAll<`${x}${T}${y}`, S, T> : A 88 | type Lowercase = intrinsic; 89 | type Uppercase = intrinsic; 90 | interface String { 91 | replace ( 92 | this: Self, searchValue: RegExpG & { source: S }, replaceValue: T): ReplaceStrAll 93 | replace ( 94 | this: Self, searchValue: S, replaceValue: T 95 | ): T extends `${"" | "$&"}${number}` ? string : string extends T ? string | symbol : ReplaceStrOnce 96 | replace(searchValue: RegExpSearchable<5>, replacer: (this: void, substring: string, 97 | a: string, b: string, c: string, d: string, e: string, index: number, source: string 98 | ) => string): string 99 | toLowerCase (this: Self): string extends Self ? string : `${Lowercase}` 100 | toUpperCase (this: Self): string extends Self ? string : `${Uppercase}` 101 | } 102 | 103 | type NormalizeKeywords = K extends `${infer x}-${infer y}` 104 | ? `${Lowercase}${NormalizeKeywords}` : Lowercase 105 | 106 | type DeepKeys = T extends [infer T1, ...infer T2] ? DeepKeys | DeepKeys 107 | : T extends { [keys in infer K]: infer V } ? K & string | DeepKeys : never 108 | type StringWithOneEnd 109 | = string extends S ? never : string extends A ? never : A extends `${string}${S}` | `${S}${string}` ? A : never 110 | -------------------------------------------------------------------------------- /typings/base/lib.es2015.collection.d.ts: -------------------------------------------------------------------------------- 1 | /*! ***************************************************************************** 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 9 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 10 | MERCHANTABLITY OR NON-INFRINGEMENT. 11 | 12 | See the Apache Version 2.0 License for specific language governing permissions 13 | and limitations under the License. 14 | ***************************************************************************** */ 15 | 16 | 17 | 18 | /// 19 | 20 | 21 | interface Map { 22 | clear(): void; 23 | delete(key: K): boolean; 24 | forEach(callbackfn: K extends string ? (value: V, key: K) => void : (value: V, key: K | string) => void 25 | , thisArg?: any): void; 26 | get(key: K): V | undefined; 27 | has(key: K): boolean; 28 | set(key: K, value: V): Map; 29 | readonly size: number; 30 | } 31 | 32 | interface MapConstructor { 33 | new(): Map; 34 | new(entries?: readonly (readonly [K, V])[] | null): Map; 35 | readonly prototype: Map; 36 | } 37 | declare var Map: MapConstructor; 38 | 39 | interface ReadonlyMap { 40 | forEach(callbackfn: (value: V, key: K) => void, thisArg?: any): void; 41 | get(key: K): V | undefined; 42 | has(key: K): boolean; 43 | // readonly size: number; 44 | } 45 | 46 | interface WeakMap { 47 | delete(key: K): boolean; 48 | get(key: K): V | undefined; 49 | has(key: K): boolean; 50 | set(key: K, value: V): unknown; // this; 51 | } 52 | 53 | interface WeakMapConstructor { 54 | new (entries?: readonly [K, V][] | null): WeakMap; 55 | readonly prototype: WeakMap; 56 | } 57 | declare var WeakMap: WeakMapConstructor | undefined; 58 | 59 | interface Set { 60 | add(value: T): Set; 61 | clear(): void; 62 | delete(value: T): boolean; 63 | forEach(callbackfn: (value: T) => void, thisArg?: any): void; 64 | has(value: T): boolean; 65 | readonly size: number; 66 | } 67 | 68 | interface SetConstructor { 69 | new (values?: readonly T[] | null): Set; 70 | readonly prototype: Set; 71 | } 72 | declare var Set: SetConstructor | undefined; 73 | 74 | interface ReadonlySet { 75 | forEach(callbackfn: (value: T) => void, thisArg?: any): void; 76 | has(value: T): boolean; 77 | // readonly size: number; 78 | } 79 | 80 | interface WeakSet { 81 | add(value: T): unknown; // this; 82 | delete(value: T): boolean; 83 | has(value: T): boolean; 84 | } 85 | 86 | interface WeakSetConstructor { 87 | new (values?: readonly T[] | null): WeakSet; 88 | readonly prototype: WeakSet; 89 | } 90 | declare var WeakSet: WeakSetConstructor | undefined; 91 | interface Window { 92 | WeakSet?: WeakSetConstructor; 93 | } 94 | -------------------------------------------------------------------------------- /typings/base/lib.es2015.symbol.d.ts: -------------------------------------------------------------------------------- 1 | /*! ***************************************************************************** 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 9 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 10 | MERCHANTABLITY OR NON-INFRINGEMENT. 11 | 12 | See the Apache Version 2.0 License for specific language governing permissions 13 | and limitations under the License. 14 | ***************************************************************************** */ 15 | 16 | 17 | 18 | /// 19 | 20 | 21 | interface Symbol { 22 | /** Returns a string representation of an object. */ 23 | toString(): string; 24 | 25 | /** Returns the primitive value of the specified object. */ 26 | valueOf(): symbol; 27 | } 28 | 29 | interface SymbolConstructor { 30 | /** 31 | * A reference to the prototype. 32 | */ 33 | readonly prototype: Symbol; 34 | 35 | /** 36 | * Returns a new unique Symbol value. 37 | * @param description Description of the new Symbol object. 38 | */ 39 | (description?: string | number): symbol; 40 | 41 | /** 42 | * Returns a Symbol object from the global symbol registry matching the given key if found. 43 | * Otherwise, returns a new symbol with this key. 44 | * @param key key to search for. 45 | */ 46 | for(key: string): symbol; 47 | 48 | /** 49 | * Returns a key from the global symbol registry matching the given Symbol if found. 50 | * Otherwise, returns a undefined. 51 | * @param sym Symbol to find the key for. 52 | */ 53 | keyFor(sym: symbol): string | undefined; 54 | 55 | /** 56 | * A method that returns the default iterator for an object. Called by the semantics of the 57 | * for-of statement. 58 | */ 59 | readonly iterator: symbol; 60 | 61 | /** 62 | * A regular expression method that matches the regular expression against a string. Called 63 | * by the String.prototype.match method. 64 | */ 65 | readonly match: symbol; 66 | } 67 | 68 | declare var Symbol: SymbolConstructor; -------------------------------------------------------------------------------- /typings/base/lib.es6.d.ts: -------------------------------------------------------------------------------- 1 | /*! ***************************************************************************** 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 9 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 10 | MERCHANTABLITY OR NON-INFRINGEMENT. 11 | 12 | See the Apache Version 2.0 License for specific language governing permissions 13 | and limitations under the License. 14 | ***************************************************************************** */ 15 | 16 | 17 | 18 | /// 19 | 20 | 21 | ///////////////////////////// 22 | /// ECMAScript APIs 23 | ///////////////////////////// 24 | 25 | /// 26 | 27 | interface IteratorYieldResult { 28 | done?: false; 29 | value: TYield; 30 | } 31 | 32 | interface IteratorReturnResult { 33 | done: true; 34 | value: TReturn; 35 | } 36 | 37 | type IteratorResult = IteratorYieldResult | IteratorReturnResult; 38 | 39 | interface Iterator { 40 | // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. 41 | next(...args: [] | [TNext]): IteratorResult; 42 | return?(value?: TReturn): IteratorResult; 43 | throw?(e?: any): IteratorResult; 44 | } 45 | 46 | interface Generator extends Iterator { 47 | // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places. 48 | next(...args: [] | [TNext?]): IteratorResult; 49 | return(value: TReturn): IteratorResult; 50 | throw(e: any): IteratorResult; 51 | [Symbol.iterator](): Generator; 52 | } 53 | 54 | interface Iterable { 55 | [Symbol.iterator](): Iterator; 56 | } 57 | 58 | interface IterableIterator extends Iterator { 59 | [Symbol.iterator](): IterableIterator; 60 | } 61 | 62 | interface Array { 63 | /** Iterator */ 64 | [Symbol.iterator](): IterableIterator; 65 | includes (valueToFind: T): boolean; 66 | } 67 | 68 | interface ReadonlyArrayWithIncludes extends ReadonlyArray { 69 | includes (valueToFind: T): boolean; 70 | } 71 | 72 | interface ReadonlyArray { 73 | /** Iterator of values in the array. */ 74 | [Symbol.iterator](): IterableIterator; 75 | } 76 | 77 | interface String { 78 | /** Iterator */ 79 | [Symbol.iterator](): IterableIterator; 80 | } 81 | -------------------------------------------------------------------------------- /typings/build/index.d.ts: -------------------------------------------------------------------------------- 1 | declare const enum Build { 2 | MinCVer = 121, // minimum Chrome version 3 | MinFFVer = 119, // minimum Firefox version 4 | BTypes = 1, // default browser types: only BrowserType.Chrome - MV3 of Firefox 115 doesn't support service worker yet 5 | OS = 7, // mac = 1 << 0, linux = 1 << 1, win = 1 << 2 6 | MV3 = 1, 7 | OnBrowserNativePages = 1, 8 | NDEBUG = 0, 9 | Mangle = 0, 10 | Inline = 0, 11 | NativeWordMoveOnFirefox = 1, 12 | MayAndroidOnFirefox = 1, 13 | DetectAPIOnFirefox = 1, 14 | /** used by {@link ../../content/extend_click.ts} */ 15 | RandomClick = 2021831, 16 | /** used by {@link ../../content/frontend.ts} */ 17 | RandomReq = 2019070, 18 | } 19 | declare const enum BuildStr { 20 | Commit = "dev", 21 | CoreGetterFuncName = "__VimiumC_priv__", 22 | FirefoxID = "vimium-c@gdh1995.cn", 23 | } 24 | -------------------------------------------------------------------------------- /typings/lib/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | -------------------------------------------------------------------------------- /typings/lib/window.d.ts: -------------------------------------------------------------------------------- 1 | interface WindowWithTop extends Window { top: Window } 2 | 3 | declare var window: Window, document: Document // eslint-disable-line no-var 4 | 5 | /** Warning on Firefox: 6 | * Even when `frameElement` is valid, `parent.innerWidth` may still throw. 7 | * 8 | * Common cases: 9 | * * on QQMail desktop version, the inbox is an `