Compare commits
No commits in common. "master" and "v4.2.67" have entirely different histories.
13
.eslintignore
Normal file
13
.eslintignore
Normal file
@ -0,0 +1,13 @@
|
||||
build/
|
||||
l10n/
|
||||
docs/
|
||||
node_modules/
|
||||
external/bcmaps/
|
||||
external/builder/fixtures/
|
||||
external/builder/fixtures_esprima/
|
||||
external/quickjs/
|
||||
external/openjpeg/
|
||||
test/tmp/
|
||||
test/pdfs/
|
||||
web/locale/
|
||||
*~/
|
||||
267
.eslintrc
Normal file
267
.eslintrc
Normal file
@ -0,0 +1,267 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2022,
|
||||
"sourceType": "module",
|
||||
},
|
||||
|
||||
"plugins": [
|
||||
"import",
|
||||
"json",
|
||||
"mozilla",
|
||||
"no-unsanitized",
|
||||
"sort-exports",
|
||||
"unicorn",
|
||||
],
|
||||
|
||||
"extends": [
|
||||
"plugin:json/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2022": true,
|
||||
"worker": true,
|
||||
},
|
||||
|
||||
"globals": {
|
||||
"PDFJSDev": "readonly",
|
||||
"__non_webpack_import__": "readonly",
|
||||
},
|
||||
|
||||
"rules": {
|
||||
// Plugins
|
||||
"import/export": "error",
|
||||
"import/exports-last": "error",
|
||||
"import/extensions": ["error", "always", { "ignorePackages": true, }],
|
||||
"import/first": "error",
|
||||
"import/named": "error",
|
||||
"import/no-cycle": "error",
|
||||
"import/no-empty-named-blocks": "error",
|
||||
"import/no-commonjs": "error",
|
||||
"import/no-mutable-exports": "error",
|
||||
"import/no-self-import": "error",
|
||||
"import/no-unresolved": ["error", {
|
||||
"ignore": ["display", "pdfjs", "pdfjs-lib", "pdfjs-web", "web", "fluent-bundle", "fluent-dom"],
|
||||
}],
|
||||
"mozilla/avoid-removeChild": "error",
|
||||
"mozilla/use-includes-instead-of-indexOf": "error",
|
||||
"no-unsanitized/method": "error",
|
||||
"no-unsanitized/property": "error",
|
||||
"sort-exports/sort-exports": ["error", {
|
||||
"ignoreCase": true,
|
||||
}],
|
||||
"unicorn/no-abusive-eslint-disable": "error",
|
||||
"unicorn/no-array-push-push": "error",
|
||||
"unicorn/no-instanceof-array": "error",
|
||||
"unicorn/no-invalid-remove-event-listener": "error",
|
||||
"unicorn/no-new-buffer": "error",
|
||||
"unicorn/no-typeof-undefined": ["error", {
|
||||
"checkGlobalVariables": false,
|
||||
}],
|
||||
"unicorn/no-useless-promise-resolve-reject": "error",
|
||||
"unicorn/no-useless-spread": "error",
|
||||
"unicorn/prefer-array-find": "error",
|
||||
"unicorn/prefer-array-flat": "error",
|
||||
"unicorn/prefer-array-flat-map": "error",
|
||||
"unicorn/prefer-array-index-of": "error",
|
||||
"unicorn/prefer-array-some": "error",
|
||||
"unicorn/prefer-at": "error",
|
||||
"unicorn/prefer-date-now": "error",
|
||||
"unicorn/prefer-dom-node-append": "error",
|
||||
"unicorn/prefer-dom-node-remove": "error",
|
||||
"unicorn/prefer-logical-operator-over-ternary": "error",
|
||||
"unicorn/prefer-modern-dom-apis": "error",
|
||||
"unicorn/prefer-modern-math-apis": "error",
|
||||
"unicorn/prefer-negative-index": "error",
|
||||
"unicorn/prefer-optional-catch-binding": "error",
|
||||
"unicorn/prefer-regexp-test": "error",
|
||||
"unicorn/prefer-string-replace-all": "error",
|
||||
"unicorn/prefer-string-starts-ends-with": "error",
|
||||
"unicorn/prefer-ternary": ["error", "only-single-line"],
|
||||
|
||||
// Possible errors
|
||||
"for-direction": "error",
|
||||
"getter-return": "error",
|
||||
"no-async-promise-executor": "error",
|
||||
"no-cond-assign": ["error", "except-parens"],
|
||||
"no-constant-condition": ["error", { "checkLoops": false, }],
|
||||
"no-debugger": "error",
|
||||
"no-dupe-args": "error",
|
||||
"no-dupe-else-if": "error",
|
||||
"no-dupe-keys": "error",
|
||||
"no-duplicate-case": "error",
|
||||
"no-empty": ["error", { "allowEmptyCatch": true, }],
|
||||
"no-empty-character-class": "error",
|
||||
"no-ex-assign": "error",
|
||||
"no-extra-boolean-cast": "error",
|
||||
"no-func-assign": "error",
|
||||
"no-inner-declarations": ["error", "functions"],
|
||||
"no-invalid-regexp": "error",
|
||||
"no-irregular-whitespace": "error",
|
||||
"no-loss-of-precision": "error",
|
||||
"no-obj-calls": "error",
|
||||
"no-promise-executor-return": "error",
|
||||
"no-regex-spaces": "error",
|
||||
"no-setter-return": "error",
|
||||
"no-sparse-arrays": "error",
|
||||
"no-template-curly-in-string": "error",
|
||||
"no-unexpected-multiline": "error",
|
||||
"no-unreachable": "error",
|
||||
"no-unsafe-finally": "error",
|
||||
"no-unsafe-negation": "error",
|
||||
"no-unsafe-optional-chaining": ["error", { "disallowArithmeticOperators": true }],
|
||||
"no-unused-private-class-members": "error",
|
||||
"use-isnan": ["error", { "enforceForIndexOf": true, }],
|
||||
"valid-typeof": ["error", { "requireStringLiterals": true, }],
|
||||
|
||||
// Best Practices
|
||||
"accessor-pairs": ["error", {
|
||||
"setWithoutGet": true,
|
||||
"enforceForClassMembers": true,
|
||||
}],
|
||||
"consistent-return": "error",
|
||||
"curly": ["error", "all"],
|
||||
"default-case-last": "error",
|
||||
"dot-notation": "error",
|
||||
"eqeqeq": ["error", "always"],
|
||||
"grouped-accessor-pairs": ["error", "getBeforeSet"],
|
||||
"no-alert": "error",
|
||||
"no-caller": "error",
|
||||
"no-else-return": "error",
|
||||
"no-empty-pattern": "error",
|
||||
"no-eval": "error",
|
||||
"no-extend-native": "error",
|
||||
"no-extra-bind": "error",
|
||||
"no-extra-label": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-floating-decimal": "error",
|
||||
"no-global-assign": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-iterator": "error",
|
||||
"no-lone-blocks": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-multi-str": "error",
|
||||
"no-new": "error",
|
||||
"no-new-func": "error",
|
||||
"no-new-symbol": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-octal-escape": "error",
|
||||
"no-octal": "error",
|
||||
"no-redeclare": "error",
|
||||
"no-return-await": "error",
|
||||
"no-self-assign": "error",
|
||||
"no-self-compare": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-unused-expressions": "error",
|
||||
"no-unused-labels": "error",
|
||||
"no-useless-call": "error",
|
||||
"no-useless-catch": "error",
|
||||
"no-useless-concat": "error",
|
||||
"no-useless-escape": "error",
|
||||
"no-useless-return": "error",
|
||||
"prefer-promise-reject-errors": "error",
|
||||
"prefer-spread": "error",
|
||||
"wrap-iife": ["error", "any"],
|
||||
"yoda": ["error", "never", {
|
||||
"exceptRange": true,
|
||||
}],
|
||||
|
||||
// Strict Mode
|
||||
"strict": ["off", "global"],
|
||||
|
||||
// Variables
|
||||
"no-delete-var": "error",
|
||||
"no-label-var": "error",
|
||||
"no-shadow": "error",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-undef": ["error", { "typeof": true, }],
|
||||
"no-unused-vars": ["error", {
|
||||
"vars": "all",
|
||||
"args": "none",
|
||||
}],
|
||||
"no-use-before-define": ["error", {
|
||||
"functions": false,
|
||||
"classes": false,
|
||||
"variables": false,
|
||||
}],
|
||||
|
||||
// Stylistic Issues
|
||||
"lines-between-class-members": ["error", "always"],
|
||||
"max-len": ["error", {
|
||||
"code": 1000,
|
||||
"comments": 80,
|
||||
"ignoreUrls": true
|
||||
}],
|
||||
"new-cap": ["error", { "newIsCap": true, "capIsNew": false, }],
|
||||
"no-array-constructor": "error",
|
||||
"no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0, "maxBOF": 1, }],
|
||||
"no-nested-ternary": "error",
|
||||
"no-new-object": "error",
|
||||
"no-restricted-syntax": ["error",
|
||||
{
|
||||
"selector": "BinaryExpression[operator='instanceof'][right.name='Object']",
|
||||
"message": "Use `typeof` rather than `instanceof Object`.",
|
||||
},
|
||||
{
|
||||
"selector": "CallExpression[callee.name='assert'][arguments.length!=2]",
|
||||
"message": "`assert()` must always be invoked with two arguments.",
|
||||
},
|
||||
{
|
||||
"selector": "CallExpression[callee.name='isCmd'][arguments.length<2]",
|
||||
"message": "Use `instanceof Cmd` rather than `isCmd()` with one argument.",
|
||||
},
|
||||
{
|
||||
"selector": "CallExpression[callee.name='isDict'][arguments.length<2]",
|
||||
"message": "Use `instanceof Dict` rather than `isDict()` with one argument.",
|
||||
},
|
||||
{
|
||||
"selector": "CallExpression[callee.name='isName'][arguments.length<2]",
|
||||
"message": "Use `instanceof Name` rather than `isName()` with one argument.",
|
||||
},
|
||||
{
|
||||
"selector": "NewExpression[callee.name='Cmd']",
|
||||
"message": "Use `Cmd.get()` rather than `new Cmd()`.",
|
||||
},
|
||||
{
|
||||
"selector": "NewExpression[callee.name='Name']",
|
||||
"message": "Use `Name.get()` rather than `new Name()`.",
|
||||
},
|
||||
{
|
||||
"selector": "NewExpression[callee.name='Ref']",
|
||||
"message": "Use `Ref.get()` rather than `new Ref()`.",
|
||||
},
|
||||
],
|
||||
"no-unneeded-ternary": "error",
|
||||
"operator-assignment": "error",
|
||||
"prefer-exponentiation-operator": "error",
|
||||
"spaced-comment": ["error", "always", {
|
||||
"block": {
|
||||
"balanced": true,
|
||||
}
|
||||
}],
|
||||
|
||||
// ECMAScript 6
|
||||
"arrow-body-style": ["error", "as-needed"],
|
||||
"constructor-super": "error",
|
||||
"no-class-assign": "error",
|
||||
"no-const-assign": "error",
|
||||
"no-dupe-class-members": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
"no-this-before-super": "error",
|
||||
"no-useless-computed-key": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-useless-rename": "error",
|
||||
"no-var": "error",
|
||||
"object-shorthand": ["error", "always", {
|
||||
"avoidQuotes": true,
|
||||
}],
|
||||
"prefer-const": "error",
|
||||
"require-yield": "error",
|
||||
"sort-imports": ["error", {
|
||||
"ignoreCase": true,
|
||||
}],
|
||||
"template-curly-spacing": ["error", "never"],
|
||||
},
|
||||
}
|
||||
17
.github/ISSUE_TEMPLATE.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
Attach (recommended) or Link to PDF file here:
|
||||
|
||||
Configuration:
|
||||
- Web browser and its version:
|
||||
- Operating system and its version:
|
||||
- PDF.js version:
|
||||
- Is a browser extension:
|
||||
|
||||
Steps to reproduce the problem:
|
||||
1.
|
||||
2.
|
||||
|
||||
What is the expected behavior? (add screenshot)
|
||||
|
||||
What went wrong? (add screenshot)
|
||||
|
||||
Link to a viewer (if hosted on a site other than mozilla.github.io/pdf.js or as Firefox/Chrome extension):
|
||||
81
.github/ISSUE_TEMPLATE/bug.yml
vendored
81
.github/ISSUE_TEMPLATE/bug.yml
vendored
@ -1,81 +0,0 @@
|
||||
name: Bug Report
|
||||
description: Report a bug in PDF.js
|
||||
title: "[Bug]: "
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Attach (recommended) or Link to PDF file
|
||||
description: Without this information the issue may be closed without comment
|
||||
placeholder: Please place only the PDF file in this field
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes: { value: "---" }
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Web browser and its version
|
||||
description: Please ensure that it's supported, refer to [the FAQ](https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#faq-support)
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Operating system and its version
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: PDF.js version
|
||||
description: Please find official releases [here](https://github.com/mozilla/pdf.js/releases)
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Is the bug present in the latest PDF.js version?
|
||||
description: Please check the [online demo](https://github.com/mozilla/pdf.js#online-demo)
|
||||
options: ["Yes", "No"]
|
||||
default: 0
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Is a browser extension
|
||||
options: ["Yes", "No"]
|
||||
default: 1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes: { value: "---" }
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce the problem
|
||||
placeholder: "1.\n2."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What is the expected behavior?
|
||||
description: Also add a screenshot
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What went wrong?
|
||||
description: Also add a screenshot
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Link to a viewer
|
||||
description: Needed if hosted on a site other than mozilla.github.io/pdf.js or as Firefox/Chrome extension
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Do you have anything to add that doesn't fit in the issue template?
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Need help?
|
||||
url: https://github.com/mozilla/pdf.js/discussions
|
||||
about: If you need help on how to use PDF.js, please open a discussion where other community members can reply
|
||||
23
.github/ISSUE_TEMPLATE/feature.yml
vendored
23
.github/ISSUE_TEMPLATE/feature.yml
vendored
@ -1,23 +0,0 @@
|
||||
name: Feature request
|
||||
description: Propose a new feature or enhancement for PDF.js
|
||||
title: "[Feature]: "
|
||||
body:
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Is the feature relevant to the Firefox PDF Viewer?
|
||||
options: ["Yes", "No"]
|
||||
default: 1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Feature description
|
||||
description: What new feature would you like PDF.js to have? Why would it be useful? What are the current workarounds?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Other PDF viewers
|
||||
description: Do other PDF viewers implement similar functionality? Add descriptions, links, and/or screenshots.
|
||||
13
.github/SECURITY.md
vendored
13
.github/SECURITY.md
vendored
@ -1,13 +0,0 @@
|
||||
# Security policy
|
||||
|
||||
Mozilla takes the security of our software seriously. If you believe you have found a security vulnerability in PDF.js, please report it to us as described below.
|
||||
|
||||
## Reporting security vulnerabilities
|
||||
|
||||
**Please don't report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report security vulnerabilities in [Bugzilla](https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=PDF%20Viewer&groups=firefox-core-security) and make sure that the checkbox in the "Security" section is checked so the required access controls are automatically configured:
|
||||
|
||||

|
||||
|
||||
The Mozilla security team will process the bug as described in [Mozilla's security bugs policy](https://www.mozilla.org/en-US/about/governance/policies/security-group/bugs).
|
||||
BIN
.github/security.png
vendored
BIN
.github/security.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [20, 22, 23]
|
||||
node-version: [18, lts/*, latest]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@ -25,7 +25,7 @@ jobs:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: npm install
|
||||
|
||||
- name: Run external tests
|
||||
run: npx gulp externaltest
|
||||
|
||||
4
.github/workflows/fluent_linter.yml
vendored
4
.github/workflows/fluent_linter.yml
vendored
@ -27,10 +27,10 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Use Python 3.13
|
||||
- name: Use Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13'
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install Fluent dependencies
|
||||
|
||||
6
.github/workflows/font_tests.yml
vendored
6
.github/workflows/font_tests.yml
vendored
@ -46,12 +46,12 @@ jobs:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: npm install
|
||||
|
||||
- name: Use Python 3.13
|
||||
- name: Use Python 3.12
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13'
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install Fonttools
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: npm install
|
||||
|
||||
- name: Run lint
|
||||
run: npx gulp lint
|
||||
|
||||
39
.github/workflows/publish_release.yml
vendored
39
.github/workflows/publish_release.yml
vendored
@ -1,39 +0,0 @@
|
||||
name: Publish release
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [lts/*]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build the `pdfjs-dist` library
|
||||
run: npx gulp dist
|
||||
|
||||
- name: Publish the `pdfjs-dist` library to NPM
|
||||
run: npm publish ./build/dist --provenance
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
2
.github/workflows/publish_website.yml
vendored
2
.github/workflows/publish_website.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: npm install
|
||||
|
||||
- name: Build the website
|
||||
run: npx gulp web
|
||||
|
||||
2
.github/workflows/types_tests.yml
vendored
2
.github/workflows/types_tests.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: npm install
|
||||
|
||||
- name: Run types tests
|
||||
run: npx gulp typestest
|
||||
|
||||
7
.gitpod.Dockerfile
vendored
Normal file
7
.gitpod.Dockerfile
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
FROM gitpod/workspace-full-vnc
|
||||
|
||||
USER gitpod
|
||||
|
||||
RUN sudo apt-get update && \
|
||||
sudo apt-get install -yq firefox && \
|
||||
sudo rm -rf /var/lib/apt/lists/*
|
||||
13
.gitpod.yml
Normal file
13
.gitpod.yml
Normal file
@ -0,0 +1,13 @@
|
||||
image:
|
||||
file: .gitpod.Dockerfile
|
||||
tasks:
|
||||
- command: |
|
||||
gp await-port 8888 && gp preview $(gp url 8888)/web/viewer.html && echo '[{"name": "Firefox","path": "/usr/bin/firefox"}]' | jq '.' > test/resources/browser_manifests/browser_manifest.json
|
||||
|
||||
- init: npm install -g gulp-cli && npm install
|
||||
command: gulp server
|
||||
ports:
|
||||
- port: 8888
|
||||
onOpen: ignore
|
||||
- port: 6080
|
||||
onOpen: ignore
|
||||
@ -4,9 +4,8 @@ docs/
|
||||
node_modules/
|
||||
external/bcmaps/
|
||||
external/builder/fixtures/
|
||||
external/builder/fixtures_babel/
|
||||
external/builder/fixtures_esprima/
|
||||
external/quickjs/
|
||||
test/stats/results/
|
||||
test/tmp/
|
||||
test/pdfs/
|
||||
web/locale/
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
{
|
||||
"chrome": {
|
||||
"skipDownload": false
|
||||
},
|
||||
"firefox": {
|
||||
"skipDownload": false,
|
||||
"version": "nightly"
|
||||
}
|
||||
}
|
||||
@ -4,9 +4,8 @@ docs/
|
||||
node_modules/
|
||||
external/bcmaps/
|
||||
external/builder/fixtures/
|
||||
external/builder/fixtures_babel/
|
||||
external/builder/fixtures_esprima/
|
||||
external/quickjs/
|
||||
test/stats/results/
|
||||
test/tmp/
|
||||
test/pdfs/
|
||||
web/locale/
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
export default {
|
||||
rules: {
|
||||
valid: true,
|
||||
|
||||
custom: [
|
||||
(reporter, $, ast, { filename }) => {
|
||||
reporter.name = "no-svg-fill-context-fill";
|
||||
|
||||
const svg = $.find("svg");
|
||||
const fill = svg.attr("fill");
|
||||
if (fill === "context-fill") {
|
||||
reporter.error(
|
||||
"Fill attribute on svg element must not be set to 'context-fill'",
|
||||
svg[0],
|
||||
ast
|
||||
);
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
ignore: [
|
||||
"build/**",
|
||||
"l10n/**",
|
||||
"docs/**",
|
||||
"node_modules/**",
|
||||
"external/bcmaps/**",
|
||||
"external/builder/fixtures/**",
|
||||
"external/builder/fixtures_babel/**",
|
||||
"external/quickjs/**",
|
||||
"test/tmp/**",
|
||||
"test/pdfs/**",
|
||||
"web/locale/**",
|
||||
"*~/**",
|
||||
],
|
||||
};
|
||||
5
AUTHORS
5
AUTHORS
@ -8,21 +8,16 @@ Andreas Gal <gal@mozilla.com>
|
||||
Artur Adib <aadib@mozilla.com>
|
||||
Brendan Dahl <bdahl@mozilla.com>
|
||||
Bill Walker <bwalker@mozilla.com>
|
||||
Calixte Denizet <calixte@mozilla.com>
|
||||
Chris G Jones <cjones@mozilla.com>
|
||||
David Quintana <gigaherz@gmail.com>
|
||||
Emily Wachowiak <ewachowiak@mozilla.com>
|
||||
Felix Kälberer <@fkaelberer>
|
||||
Jakob Miland <saebekassebil@gmail.com>
|
||||
Jonas Jenwald <jonas.jenwald@gmail.com>
|
||||
Julian Viereck
|
||||
Justin D'Arcangelo <justindarc@gmail.com>
|
||||
Kalervo Kujala
|
||||
Marco Castelluccio <mcastelluccio@mozilla.com>
|
||||
Marie-Lilas Onanga Ozavino <monangaozavino@mozilla.com>
|
||||
Michał Gołębiowski-Owczarek <m.goleb@gmail.com>
|
||||
Ophir Lojkine <@lovasoa>
|
||||
Ryan Casey <rcasey@mozilla.com>
|
||||
Rob Wu <rob@robwu.nl>
|
||||
Shaon Barman <shaon.barman@gmail.com>
|
||||
Sehyun Park <premed055515@gmail.com>
|
||||
|
||||
29
README.md
29
README.md
@ -1,4 +1,4 @@
|
||||
# PDF.js [](https://github.com/mozilla/pdf.js/actions/workflows/ci.yml?query=branch%3Amaster)
|
||||
# PDF.js [](https://github.com/mozilla/pdf.js/actions?query=workflow%3ACI+branch%3Amaster)
|
||||
|
||||
[PDF.js](https://mozilla.github.io/pdf.js/) is a Portable Document Format (PDF) viewer that is built with HTML5.
|
||||
|
||||
@ -40,7 +40,7 @@ PDF.js is built into version 19+ of Firefox.
|
||||
|
||||
+ The official extension for Chrome can be installed from the [Chrome Web Store](https://chrome.google.com/webstore/detail/pdf-viewer/oemmndcbldboiebfnladdacbdfmadadm).
|
||||
*This extension is maintained by [@Rob--W](https://github.com/Rob--W).*
|
||||
+ Build Your Own - Get the code as explained below and issue `npx gulp chromium`. Then open
|
||||
+ Build Your Own - Get the code as explained below and issue `gulp chromium`. Then open
|
||||
Chrome, go to `Tools > Extension` and load the (unpackaged) extension from the
|
||||
directory `build/chromium`.
|
||||
|
||||
@ -52,15 +52,24 @@ To get a local copy of the current code, clone it using git:
|
||||
$ cd pdf.js
|
||||
|
||||
Next, install Node.js via the [official package](https://nodejs.org) or via
|
||||
[nvm](https://github.com/creationix/nvm). If everything worked out, install
|
||||
all dependencies for PDF.js:
|
||||
[nvm](https://github.com/creationix/nvm). You need to install the gulp package
|
||||
globally (see also [gulp's getting started](https://github.com/gulpjs/gulp/tree/master/docs/getting-started)):
|
||||
|
||||
$ npm install -g gulp-cli@^2.3.0
|
||||
|
||||
If you prefer to not install `gulp-cli` globally, you have to prefix all the `gulp` commands with `npx` (for example, `npx gulp server` instead of `gulp server`).
|
||||
|
||||
If everything worked out, install all dependencies for PDF.js:
|
||||
|
||||
$ npm install
|
||||
|
||||
> [!NOTE]
|
||||
> On MacOS M1/M2 you may see some `node-gyp`-related errors when running `npm install`. This is because one of our dependencies, `"canvas"`, does not provide pre-built binaries for this platform and instead `npm` will try to build it from source. Please make sure to first install the necessary native dependencies using `brew`: https://github.com/Automattic/node-canvas#compiling.
|
||||
|
||||
Finally, you need to start a local web server as some browsers do not allow opening
|
||||
PDF files using a `file://` URL. Run:
|
||||
|
||||
$ npx gulp server
|
||||
$ gulp server
|
||||
|
||||
and then you can open:
|
||||
|
||||
@ -77,11 +86,11 @@ It is also possible to view all test PDF files on the right side by opening:
|
||||
In order to bundle all `src/` files into two production scripts and build the generic
|
||||
viewer, run:
|
||||
|
||||
$ npx gulp generic
|
||||
$ gulp generic
|
||||
|
||||
If you need to support older browsers, run:
|
||||
|
||||
$ npx gulp generic-legacy
|
||||
$ gulp generic-legacy
|
||||
|
||||
This will generate `pdf.js` and `pdf.worker.js` in the `build/generic/build/` directory (respectively `build/generic-legacy/build/`).
|
||||
Both scripts are needed but only `pdf.js` needs to be included since `pdf.worker.js` will
|
||||
@ -90,7 +99,7 @@ be loaded by `pdf.js`. The PDF.js files are large and should be minified for pro
|
||||
## Using PDF.js in a web application
|
||||
|
||||
To use PDF.js in a web application you can choose to use a pre-built version of the library
|
||||
or to build it from source. We supply pre-built versions for usage with NPM under
|
||||
or to build it from source. We supply pre-built versions for usage with NPM and Bower under
|
||||
the `pdfjs-dist` name. For more information and examples please refer to the
|
||||
[wiki page](https://github.com/mozilla/pdf.js/wiki/Setup-pdf.js-in-a-website) on this subject.
|
||||
|
||||
@ -107,7 +116,7 @@ You can play with the PDF.js API directly from your browser using the live demos
|
||||
|
||||
+ [Interactive examples](https://mozilla.github.io/pdf.js/examples/index.html#interactive-examples)
|
||||
|
||||
More examples can be found in the [examples folder](https://github.com/mozilla/pdf.js/tree/master/examples/). Some of them are using the pdfjs-dist package, which can be built and installed in this repo directory via `npx gulp dist-install` command.
|
||||
More examples can be found in the [examples folder](https://github.com/mozilla/pdf.js/tree/master/examples/). Some of them are using the pdfjs-dist package, which can be built and installed in this repo directory via `gulp dist-install` command.
|
||||
|
||||
For an introduction to the PDF.js code, check out the presentation by our
|
||||
contributor Julian Viereck:
|
||||
@ -134,4 +143,4 @@ Talk to us on Matrix:
|
||||
|
||||
File an issue:
|
||||
|
||||
+ https://github.com/mozilla/pdf.js/issues/new/choose
|
||||
+ https://github.com/mozilla/pdf.js/issues/new
|
||||
|
||||
20
docs/config.json
Normal file
20
docs/config.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"locals": {
|
||||
"url": "http://localhost:8080",
|
||||
"name": "PDF.js Documentation",
|
||||
"description": ""
|
||||
},
|
||||
"require": {
|
||||
"typogr": "typogr"
|
||||
},
|
||||
"jade": {
|
||||
"pretty": true
|
||||
},
|
||||
"markdown": {
|
||||
"smartLists": true,
|
||||
"smartypants": true
|
||||
},
|
||||
"plugins": [
|
||||
"./plugins/wintersmith-makerelative.coffee"
|
||||
]
|
||||
}
|
||||
@ -1,11 +1,8 @@
|
||||
---
|
||||
title: API
|
||||
layout: layout.njk
|
||||
slug: api
|
||||
template: layout.jade
|
||||
---
|
||||
|
||||
# API
|
||||
|
||||
The generated API documentation, from the inline comments in [api.js](https://github.com/mozilla/pdf.js/blob/master/src/display/api.js), is available below.
|
||||
|
||||
<iframe src="draft/index.html" title="PDF.js API documentation"></iframe>
|
||||
We're currently working on <a href="draft/index.html">better API docs</a>, but the API is well documented in [api.js](https://github.com/mozilla/pdf.js/blob/master/src/display/api.js).
|
||||
|
||||
@ -1,100 +0,0 @@
|
||||
/*!
|
||||
Theme: a11y-light
|
||||
Author: @ericwbailey
|
||||
Maintainer: @ericwbailey
|
||||
|
||||
Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlight.js/blob/master/src/styles/tomorrow-night-eighties.css
|
||||
|
||||
Original source: https://github.com/highlightjs/highlight.js/blob/main/src/styles/a11y-light.css
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
background: #fefefe;
|
||||
color: #545454;
|
||||
}
|
||||
|
||||
/* Comment */
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #696969;
|
||||
}
|
||||
|
||||
/* Red */
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-regexp,
|
||||
.hljs-deletion {
|
||||
color: #d91e18;
|
||||
}
|
||||
|
||||
/* Orange */
|
||||
.hljs-number,
|
||||
.hljs-built_in,
|
||||
.hljs-literal,
|
||||
.hljs-type,
|
||||
.hljs-params,
|
||||
.hljs-meta,
|
||||
.hljs-link {
|
||||
color: #aa5d00;
|
||||
}
|
||||
|
||||
/* Yellow */
|
||||
.hljs-attribute {
|
||||
color: #aa5d00;
|
||||
}
|
||||
|
||||
/* Green */
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-addition {
|
||||
color: #008000;
|
||||
}
|
||||
|
||||
/* Blue */
|
||||
.hljs-title,
|
||||
.hljs-section {
|
||||
color: #007faa;
|
||||
}
|
||||
|
||||
/* Purple */
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color: #7928a1;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@media screen and (-ms-high-contrast: active) {
|
||||
.hljs-addition,
|
||||
.hljs-attribute,
|
||||
.hljs-built_in,
|
||||
.hljs-bullet,
|
||||
.hljs-comment,
|
||||
.hljs-link,
|
||||
.hljs-literal,
|
||||
.hljs-meta,
|
||||
.hljs-number,
|
||||
.hljs-params,
|
||||
.hljs-string,
|
||||
.hljs-symbol,
|
||||
.hljs-type,
|
||||
.hljs-quote {
|
||||
color: highlight;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
10
docs/contents/css/bootstrap.min.css
vendored
10
docs/contents/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1,40 +1,119 @@
|
||||
header {
|
||||
background-color: #f8f8f8;
|
||||
border-bottom: 1px solid #e5e7e8;
|
||||
|
||||
.navbar-brand {
|
||||
padding: 0;
|
||||
|
||||
img {
|
||||
height: 42px;
|
||||
}
|
||||
}
|
||||
body {
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 50px 0;
|
||||
|
||||
.description {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid #cccccc;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: none;
|
||||
height: calc(0.55 * 100vh);
|
||||
width: 100%;
|
||||
}
|
||||
.starter-template {
|
||||
padding: 0 15px;
|
||||
}
|
||||
.navbar-brand {
|
||||
padding: 4px 15px;
|
||||
}
|
||||
.navbar-brand img {
|
||||
height: 42px;
|
||||
}
|
||||
.navbar {
|
||||
border-color: #e5e7e8;
|
||||
}
|
||||
.navbar-default .navbar-nav > .active > a,
|
||||
.navbar-default .navbar-nav > .active > a:hover,
|
||||
.navbar-default .navbar-nav > .active > a:focus {
|
||||
background-color: #fff;
|
||||
border: 1px solid #e5e7e8;
|
||||
border-width: 0 1px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
footer {
|
||||
border-top: 1px solid #e5e5e5;
|
||||
color: #777777;
|
||||
padding: 40px 0;
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
margin-top: 100px;
|
||||
color: #777;
|
||||
text-align: center;
|
||||
border-top: 1px solid #E5E5E5;
|
||||
}
|
||||
|
||||
/* code styling */
|
||||
|
||||
code {
|
||||
font-family: 'Anonymous Pro', monospace;
|
||||
font-size: 0.85em;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
pre code {
|
||||
display: block;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
p code {
|
||||
padding: 0.1em 0.3em 0.2em;
|
||||
border-radius: 0.3em;
|
||||
position: relative;
|
||||
top: -0.15em;
|
||||
background: #444;
|
||||
color: #fff;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* syntax hl stuff */
|
||||
|
||||
code.lang-markdown {
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
code.lang-markdown .header,
|
||||
code.lang-markdown .strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code.lang-markdown .emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
code.lang-markdown .horizontal_rule,
|
||||
code.lang-markdown .link_label,
|
||||
code.lang-markdown .code,
|
||||
code.lang-markdown .header,
|
||||
code.lang-markdown .link_url {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
code.lang-markdown .blockquote,
|
||||
code.lang-markdown .bullet {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
/* Tomorrow Theme */
|
||||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
|
||||
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
|
||||
.tomorrow-comment, pre .comment, pre .title {
|
||||
color: #8e908c;
|
||||
}
|
||||
|
||||
.tomorrow-red, pre .variable, pre .attribute, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo {
|
||||
color: #c82829;
|
||||
}
|
||||
|
||||
.tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .literal, pre .params, pre .constant {
|
||||
color: #f5871f;
|
||||
}
|
||||
|
||||
.tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute {
|
||||
color: #eab700;
|
||||
}
|
||||
|
||||
.tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata {
|
||||
color: #718c00;
|
||||
}
|
||||
|
||||
.tomorrow-aqua, pre .css .hexcolor {
|
||||
color: #3e999f;
|
||||
}
|
||||
|
||||
.tomorrow-blue, pre .function, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title {
|
||||
color: #4271ae;
|
||||
}
|
||||
|
||||
.tomorrow-purple, pre .keyword, pre .javascript .function {
|
||||
color: #8959a8;
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
---
|
||||
title: Examples
|
||||
layout: layout.njk
|
||||
slug: examples
|
||||
template: layout.jade
|
||||
---
|
||||
|
||||
## Hello World Walkthrough
|
||||
@ -84,17 +83,17 @@ var scaledViewport = page.getViewport({ scale: scale, });
|
||||
The example demonstrates how promises can be used to handle errors during loading.
|
||||
It also demonstrates how to wait until a page is loaded and rendered.
|
||||
|
||||
<script async src="https://jsfiddle.net/pdfjs/9engc9mw/embed/html,css,result/"></script>
|
||||
<script async src="//jsfiddle.net/pdfjs/9engc9mw/embed/html,css,result/"></script>
|
||||
|
||||
### Hello World using base64 encoded PDF
|
||||
|
||||
The PDF.js can accept any decoded base64 data as an array.
|
||||
|
||||
<script async src="https://jsfiddle.net/pdfjs/cq0asLqz/embed/html,css,result/"></script>
|
||||
<script async src="//jsfiddle.net/pdfjs/cq0asLqz/embed/html,css,result/"></script>
|
||||
|
||||
### Previous/Next example
|
||||
|
||||
The same canvas cannot be used to perform to draw two pages at the same time --
|
||||
the example demonstrates how to wait on previous operation to be complete.
|
||||
|
||||
<script async src="https://jsfiddle.net/pdfjs/wagvs9Lf/embed/html,css,result/"></script>
|
||||
<script async src="//jsfiddle.net/pdfjs/wagvs9Lf/embed/html,css,result/"></script>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
---
|
||||
title: Getting Started
|
||||
layout: layout.njk
|
||||
slug: getting_started
|
||||
template: layout.jade
|
||||
---
|
||||
|
||||
# Getting Started
|
||||
@ -77,50 +76,51 @@ Note that we only mention the most relevant files and folders.
|
||||
|
||||
### Prebuilt
|
||||
|
||||
```plaintext
|
||||
```
|
||||
├── build/
|
||||
│ ├── pdf.mjs - display layer
|
||||
│ ├── pdf.mjs.map - display layer's source map
|
||||
│ ├── pdf.worker.mjs - core layer
|
||||
│ └── pdf.worker.mjs.map - core layer's source map
|
||||
│ ├── pdf.js - display layer
|
||||
│ ├── pdf.js.map - display layer's source map
|
||||
│ ├── pdf.worker.js - core layer
|
||||
│ └── pdf.worker.js.map - core layer's source map
|
||||
├── web/
|
||||
│ ├── cmaps/ - character maps (required by core)
|
||||
│ ├── compressed.tracemonkey-pldi-09.pdf - PDF file for testing purposes
|
||||
│ ├── debugger.js - helpful debugging features
|
||||
│ ├── images/ - images for the viewer and annotation icons
|
||||
│ ├── locale/ - translation files
|
||||
│ ├── viewer.css - viewer style sheet
|
||||
│ ├── viewer.html - viewer layout
|
||||
│ ├── viewer.mjs - viewer layer
|
||||
│ └── viewer.mjs.map - viewer layer's source map
|
||||
│ ├── viewer.js - viewer layer
|
||||
│ └── viewer.js.map - viewer layer's source map
|
||||
└── LICENSE
|
||||
```
|
||||
|
||||
### Source
|
||||
|
||||
```plaintext
|
||||
```
|
||||
├── docs/ - website source code
|
||||
├── examples/ - simple usage examples
|
||||
├── extensions/ - browser extension source code
|
||||
├── external/ - third party code
|
||||
├── l10n/ - translation files
|
||||
├── src/
|
||||
│ ├── core/ - core layer
|
||||
│ ├── display/ - display layer
|
||||
│ ├── shared/ - shared code between the core and display layers
|
||||
│ ├── interfaces.js - interface definitions for the core/display layers
|
||||
│ └── pdf.*.js - wrapper files for bundling
|
||||
│ ├── core/ - core layer
|
||||
│ ├── display/ - display layer
|
||||
│ ├── shared/ - shared code between the core and display layers
|
||||
│ ├── interfaces.js - interface definitions for the core/display layers
|
||||
│ └── pdf.*.js - wrapper files for bundling
|
||||
├── test/ - unit, font, reference, and integration tests
|
||||
├── web/ - viewer layer
|
||||
├── LICENSE
|
||||
├── README.md
|
||||
├── gulpfile.mjs - build scripts/logic
|
||||
├── gulpfile.js - build scripts/logic
|
||||
├── package-lock.json - pinned dependency versions
|
||||
└── package.json - package definition and dependencies
|
||||
```
|
||||
|
||||
## Trying the Viewer
|
||||
|
||||
With the prebuilt or source version, open `web/viewer.html` in a browser and the test pdf should load. Note: the worker is not enabled for file:// urls, so use a server. If you're using the source build and have node, you can run `npx gulp server`.
|
||||
With the prebuilt or source version, open `web/viewer.html` in a browser and the test pdf should load. Note: the worker is not enabled for file:// urls, so use a server. If you're using the source build and have node, you can run `gulp server`.
|
||||
|
||||
## More Information
|
||||
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
---
|
||||
title: Home
|
||||
layout: layout.njk
|
||||
slug: home
|
||||
title: PDF.js
|
||||
template: layout.jade
|
||||
---
|
||||
|
||||
|
||||
<h1 class="text-center">PDF.js</h1>
|
||||
<p class="text-center description">A general-purpose, web standards-based platform for parsing and rendering PDFs.</p>
|
||||
<p class="text-center">
|
||||
<a type="button" class="btn btn-outline-dark" href="/getting_started/#download">Download</a>
|
||||
<a type="button" class="btn btn-outline-dark" href="https://github.com/mozilla/pdf.js#online-demo">Demo</a>
|
||||
<a type="button" class="btn btn-outline-dark" href="https://github.com/mozilla/pdf.js">GitHub Project</a>
|
||||
<p class="text-center" style="font-size: 20px">A general-purpose, web standards-based platform for parsing and rendering PDFs.
|
||||
</p>
|
||||
<p class="text-center">
|
||||
<a type="button" class="btn btn-lg btn-default" href="getting_started/#download">Download</a>
|
||||
<a type="button" class="btn btn-lg btn-default" href="https://github.com/mozilla/pdf.js#online-demo">Demo</a>
|
||||
<a type="button" class="btn btn-lg btn-default" href="https://github.com/mozilla/pdf.js">GitHub Project</a>
|
||||
</p>
|
||||
|
||||
11
docs/contents/js/bootstrap.min.js
vendored
11
docs/contents/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
4
docs/contents/js/jquery-2.1.0.min.js
vendored
Normal file
4
docs/contents/js/jquery-2.1.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
docs/contents/js/jquery-3.7.1.min.js
vendored
2
docs/contents/js/jquery-3.7.1.min.js
vendored
File diff suppressed because one or more lines are too long
15
docs/plugins/wintersmith-makerelative.coffee
Normal file
15
docs/plugins/wintersmith-makerelative.coffee
Normal file
@ -0,0 +1,15 @@
|
||||
module.exports = (env, callback) ->
|
||||
count = (string, substr) ->
|
||||
num = pos = 0
|
||||
return 1/0 unless substr.length
|
||||
num++ while pos = 1 + string.indexOf(substr, pos)
|
||||
num
|
||||
|
||||
env.helpers.makeRelative = (source, dest) ->
|
||||
return dest unless dest.indexOf("/") == 0
|
||||
depth = count(source, '/') # 1 being /
|
||||
ret = ""
|
||||
ret += "../" while depth = depth - 1
|
||||
ret + dest.substring(1)
|
||||
|
||||
callback()
|
||||
52
docs/templates/layout.jade
vendored
Normal file
52
docs/templates/layout.jade
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
- makeRelative = env.helpers.makeRelative
|
||||
doctype html
|
||||
html(lang='en')
|
||||
head
|
||||
meta(charset='utf-8')
|
||||
meta(name='viewport', content='width=device-width, initial-scale=1.0')
|
||||
meta(name='description', content='A general-purpose, web standards-based platform for parsing and rendering PDFs.')
|
||||
meta(name='author', content='')
|
||||
link(rel='shortcut icon', href=makeRelative(page.url, '/images/favicon.ico'))
|
||||
title=page.title
|
||||
// Bootstrap core CSS
|
||||
link(href=makeRelative(page.url, '/css/bootstrap.min.css'), rel='stylesheet')
|
||||
// Custom styles for this template
|
||||
link(href=makeRelative(page.url, '/css/main.css'), rel='stylesheet')
|
||||
|
||||
body
|
||||
header.navbar.navbar-default.navbar-static-top
|
||||
.container
|
||||
.navbar-header
|
||||
button.navbar-toggle(type='button', data-toggle='collapse', data-target='.navbar-collapse')
|
||||
span.icon-bar
|
||||
span.icon-bar
|
||||
span.icon-bar
|
||||
a.navbar-brand(href='#')
|
||||
img(src=makeRelative(page.url, '/images/logo.svg'))
|
||||
.collapse.navbar-collapse
|
||||
ul.nav.navbar-nav
|
||||
li(class=(page.url === '/' ? 'active' : ''))
|
||||
a(href=makeRelative(page.url, '/')) Home
|
||||
li(class=(page.url === '/getting_started/' ? 'active' : ''))
|
||||
a(href=makeRelative(page.url, '/getting_started/')) Getting Started
|
||||
li(class=(page.url === '/examples/' ? 'active' : ''))
|
||||
a(href=makeRelative(page.url, '/examples/')) Examples
|
||||
li
|
||||
a(href='https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions') FAQ
|
||||
li(class=(page.url === '/api/' ? 'active' : ''))
|
||||
a(href=makeRelative(page.url, '/api/')) API
|
||||
|
||||
.container
|
||||
.starter-template
|
||||
section.content!= typogr(page.html).typogrify()
|
||||
|
||||
.container
|
||||
footer
|
||||
p ©Mozilla and individual contributors
|
||||
:markdown-it
|
||||
PDF.js is licensed under [Apache](https://github.com/mozilla/pdf.js/blob/master/LICENSE),
|
||||
documentation is licensed under [CC BY-SA 2.5](https://creativecommons.org/licenses/by-sa/2.5/)
|
||||
|
||||
// Bootstrap core JavaScript
|
||||
script(src=makeRelative(page.url, '/js/jquery-2.1.0.min.js'))
|
||||
script(src=makeRelative(page.url, '/js/bootstrap.min.js'))
|
||||
56
docs/templates/layout.njk
vendored
56
docs/templates/layout.njk
vendored
@ -1,56 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>{{ sitename }} - {{ title }}</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="{{ description }}">
|
||||
<meta name="viewport" content="device-width, initial-scale=1.0">
|
||||
<script src="/js/jquery-3.7.1.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
<link rel="shortcut icon" href="/images/favicon.ico">
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/css/a11y-light.css">
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container">
|
||||
<div class="navbar-brand"><img src="/images/logo.svg"></div>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item {{ 'active' if slug == 'home' else '' }}">
|
||||
<a class="nav-link" href="/">Home</a>
|
||||
</li>
|
||||
<li class="nav-item {{ 'active' if slug == 'getting_started' else '' }}">
|
||||
<a class="nav-link" href="/getting_started">Getting started</a>
|
||||
</li>
|
||||
<li class="nav-item {{ 'active' if slug == 'examples' else '' }}">
|
||||
<a class="nav-link" href="/examples">Examples</a>
|
||||
</li>
|
||||
<li class="nav-item {{ 'active' if slug == 'api' else '' }}">
|
||||
<a class="nav-link" href="/api">API</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions">FAQ</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
<main class="container">
|
||||
{{ contents | safe }}
|
||||
</main>
|
||||
<footer>
|
||||
<p>© Mozilla and individual contributors</p>
|
||||
<p>
|
||||
PDF.js is licensed under <a href="https://github.com/mozilla/pdf.js/blob/master/LICENSE">Apache 2.0</a>,
|
||||
documentation is licensed under <a href="https://creativecommons.org/licenses/by-sa/2.5">CC BY-SA 2.5</a>.
|
||||
</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,452 +0,0 @@
|
||||
import globals from "globals";
|
||||
|
||||
import import_ from "eslint-plugin-import";
|
||||
import jasmine from "eslint-plugin-jasmine";
|
||||
import json from "eslint-plugin-json";
|
||||
import noUnsanitized from "eslint-plugin-no-unsanitized";
|
||||
import perfectionist from "eslint-plugin-perfectionist";
|
||||
import prettierRecommended from "eslint-plugin-prettier/recommended";
|
||||
import unicorn from "eslint-plugin-unicorn";
|
||||
|
||||
const jsFiles = folder => {
|
||||
const prefix = folder === "." ? "" : folder + "/";
|
||||
return [prefix + "**/*.js", prefix + "**/*.jsm", prefix + "**/*.mjs"];
|
||||
};
|
||||
|
||||
// Include all files referenced in extensions/chromium/background.js
|
||||
const chromiumExtensionServiceWorkerFiles = [
|
||||
"extensions/chromium/extension-router.js",
|
||||
"extensions/chromium/options/migration.js",
|
||||
"extensions/chromium/pdfHandler.js",
|
||||
"extensions/chromium/preserve-referer.js",
|
||||
"extensions/chromium/suppress-update.js",
|
||||
"extensions/chromium/telemetry.js",
|
||||
];
|
||||
|
||||
export default [
|
||||
{
|
||||
ignores: [
|
||||
"**/build/",
|
||||
"**/l10n/",
|
||||
"**/docs/",
|
||||
"**/node_modules/",
|
||||
"external/bcmaps/",
|
||||
"external/builder/fixtures/",
|
||||
"external/builder/fixtures_babel/",
|
||||
"external/quickjs/",
|
||||
"external/openjpeg/",
|
||||
"test/stats/results/",
|
||||
"test/tmp/",
|
||||
"test/pdfs/",
|
||||
"web/locale/",
|
||||
"web/wasm/",
|
||||
"**/*~/",
|
||||
],
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
Base configuration
|
||||
\* ======================================================================== */
|
||||
|
||||
prettierRecommended,
|
||||
{
|
||||
files: ["**/*.json"],
|
||||
...json.configs.recommended,
|
||||
},
|
||||
{
|
||||
files: jsFiles("."),
|
||||
ignores: chromiumExtensionServiceWorkerFiles,
|
||||
languageOptions: {
|
||||
globals: globals.browser,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: jsFiles("."),
|
||||
|
||||
plugins: {
|
||||
import: import_.flatConfigs.recommended.plugins.import,
|
||||
json,
|
||||
"no-unsanitized": noUnsanitized,
|
||||
perfectionist,
|
||||
unicorn,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.worker,
|
||||
PDFJSDev: "readonly",
|
||||
__non_webpack_import__: "readonly",
|
||||
},
|
||||
|
||||
ecmaVersion: 2025,
|
||||
sourceType: "module",
|
||||
},
|
||||
|
||||
rules: {
|
||||
"import/export": "error",
|
||||
"import/exports-last": "error",
|
||||
"import/extensions": ["error", "always", { ignorePackages: true }],
|
||||
"import/first": "error",
|
||||
"import/named": "error",
|
||||
"import/no-cycle": "error",
|
||||
"import/no-empty-named-blocks": "error",
|
||||
"import/no-commonjs": "error",
|
||||
"import/no-mutable-exports": "error",
|
||||
"import/no-restricted-paths": [
|
||||
"error",
|
||||
{
|
||||
zones: [
|
||||
{
|
||||
target: "./web",
|
||||
from: "./src",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
"import/no-self-import": "error",
|
||||
"import/no-unresolved": [
|
||||
"error",
|
||||
{
|
||||
ignore: [
|
||||
"display",
|
||||
"pdfjs",
|
||||
"pdfjs-lib",
|
||||
"pdfjs-web",
|
||||
"web",
|
||||
"fluent-bundle",
|
||||
"fluent-dom",
|
||||
// See https://github.com/firebase/firebase-admin-node/discussions/1359.
|
||||
"eslint-plugin-perfectionist",
|
||||
],
|
||||
},
|
||||
],
|
||||
"no-unsanitized/method": "error",
|
||||
"no-unsanitized/property": "error",
|
||||
"perfectionist/sort-exports": "error",
|
||||
"perfectionist/sort-named-exports": "error",
|
||||
"unicorn/no-abusive-eslint-disable": "error",
|
||||
"unicorn/no-array-push-push": "error",
|
||||
"unicorn/no-console-spaces": "error",
|
||||
"unicorn/no-instanceof-builtins": "error",
|
||||
"unicorn/no-invalid-remove-event-listener": "error",
|
||||
"unicorn/no-new-buffer": "error",
|
||||
"unicorn/no-single-promise-in-promise-methods": "error",
|
||||
"unicorn/no-typeof-undefined": ["error", { checkGlobalVariables: false }],
|
||||
"unicorn/no-useless-promise-resolve-reject": "error",
|
||||
"unicorn/no-useless-spread": "error",
|
||||
"unicorn/prefer-array-find": "error",
|
||||
"unicorn/prefer-array-flat": "error",
|
||||
"unicorn/prefer-array-flat-map": "error",
|
||||
"unicorn/prefer-array-index-of": "error",
|
||||
"unicorn/prefer-array-some": "error",
|
||||
"unicorn/prefer-at": "error",
|
||||
"unicorn/prefer-date-now": "error",
|
||||
"unicorn/prefer-dom-node-append": "error",
|
||||
"unicorn/prefer-dom-node-remove": "error",
|
||||
"unicorn/prefer-includes": "error",
|
||||
"unicorn/prefer-logical-operator-over-ternary": "error",
|
||||
"unicorn/prefer-modern-dom-apis": "error",
|
||||
"unicorn/prefer-modern-math-apis": "error",
|
||||
"unicorn/prefer-negative-index": "error",
|
||||
"unicorn/prefer-optional-catch-binding": "error",
|
||||
"unicorn/prefer-regexp-test": "error",
|
||||
"unicorn/prefer-string-replace-all": "error",
|
||||
"unicorn/prefer-string-starts-ends-with": "error",
|
||||
"unicorn/prefer-ternary": ["error", "only-single-line"],
|
||||
"unicorn/throw-new-error": "error",
|
||||
|
||||
// Possible errors
|
||||
"for-direction": "error",
|
||||
"getter-return": "error",
|
||||
"no-async-promise-executor": "error",
|
||||
"no-cond-assign": ["error", "except-parens"],
|
||||
"no-constant-condition": ["error", { checkLoops: false }],
|
||||
"no-debugger": "error",
|
||||
"no-dupe-args": "error",
|
||||
"no-dupe-else-if": "error",
|
||||
"no-dupe-keys": "error",
|
||||
"no-duplicate-case": "error",
|
||||
"no-empty": ["error", { allowEmptyCatch: true }],
|
||||
"no-empty-character-class": "error",
|
||||
"no-ex-assign": "error",
|
||||
"no-extra-boolean-cast": "error",
|
||||
"no-func-assign": "error",
|
||||
"no-inner-declarations": ["error", "functions"],
|
||||
"no-invalid-regexp": "error",
|
||||
"no-irregular-whitespace": "error",
|
||||
"no-loss-of-precision": "error",
|
||||
"no-obj-calls": "error",
|
||||
"no-promise-executor-return": "error",
|
||||
"no-regex-spaces": "error",
|
||||
"no-setter-return": "error",
|
||||
"no-sparse-arrays": "error",
|
||||
"no-template-curly-in-string": "error",
|
||||
"no-unexpected-multiline": "error",
|
||||
"no-unreachable": "error",
|
||||
"no-unsafe-finally": "error",
|
||||
"no-unsafe-negation": "error",
|
||||
"no-unsafe-optional-chaining": [
|
||||
"error",
|
||||
{ disallowArithmeticOperators: true },
|
||||
],
|
||||
"no-unused-private-class-members": "error",
|
||||
"use-isnan": ["error", { enforceForIndexOf: true }],
|
||||
"valid-typeof": ["error", { requireStringLiterals: true }],
|
||||
|
||||
// Best Practices
|
||||
"accessor-pairs": [
|
||||
"error",
|
||||
{ setWithoutGet: true, enforceForClassMembers: true },
|
||||
],
|
||||
"consistent-return": "error",
|
||||
curly: ["error", "all"],
|
||||
"default-case-last": "error",
|
||||
"dot-notation": "error",
|
||||
eqeqeq: ["error", "always"],
|
||||
"grouped-accessor-pairs": ["error", "getBeforeSet"],
|
||||
"no-alert": "error",
|
||||
"no-caller": "error",
|
||||
"no-else-return": "error",
|
||||
"no-empty-pattern": "error",
|
||||
"no-eval": "error",
|
||||
"no-extend-native": "error",
|
||||
"no-extra-bind": "error",
|
||||
"no-extra-label": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-floating-decimal": "error",
|
||||
"no-global-assign": "error",
|
||||
"no-implied-eval": "error",
|
||||
"no-iterator": "error",
|
||||
"no-lone-blocks": "error",
|
||||
"no-lonely-if": "error",
|
||||
"no-multi-str": "error",
|
||||
"no-new": "error",
|
||||
"no-new-func": "error",
|
||||
"no-new-symbol": "error",
|
||||
"no-new-wrappers": "error",
|
||||
"no-octal-escape": "error",
|
||||
"no-octal": "error",
|
||||
"no-redeclare": "error",
|
||||
"no-return-await": "error",
|
||||
"no-self-assign": "error",
|
||||
"no-self-compare": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-unused-expressions": "error",
|
||||
"no-unused-labels": "error",
|
||||
"no-useless-call": "error",
|
||||
"no-useless-catch": "error",
|
||||
"no-useless-concat": "error",
|
||||
"no-useless-escape": "error",
|
||||
"no-useless-return": "error",
|
||||
"prefer-promise-reject-errors": "error",
|
||||
"prefer-spread": "error",
|
||||
"wrap-iife": ["error", "any"],
|
||||
yoda: ["error", "never", { exceptRange: true }],
|
||||
|
||||
// Strict Mode
|
||||
strict: ["off", "global"],
|
||||
|
||||
// Variables
|
||||
"no-delete-var": "error",
|
||||
"no-label-var": "error",
|
||||
"no-shadow": "error",
|
||||
"no-shadow-restricted-names": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-undef": ["error", { typeof: true }],
|
||||
"no-unused-vars": ["error", { vars: "all", args: "none" }],
|
||||
"no-use-before-define": [
|
||||
"error",
|
||||
{ functions: false, classes: false, variables: false },
|
||||
],
|
||||
|
||||
// Stylistic Issues
|
||||
"lines-between-class-members": ["error", "always"],
|
||||
"max-len": ["error", { code: 1000, comments: 80, ignoreUrls: true }],
|
||||
"new-cap": ["error", { newIsCap: true, capIsNew: false }],
|
||||
"no-array-constructor": "error",
|
||||
"no-multiple-empty-lines": ["error", { max: 1, maxEOF: 0, maxBOF: 1 }],
|
||||
"no-nested-ternary": "error",
|
||||
"no-new-object": "error",
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector:
|
||||
"BinaryExpression[operator='instanceof'][right.name='Object']",
|
||||
message: "Use `typeof` rather than `instanceof Object`.",
|
||||
},
|
||||
{
|
||||
selector: "CallExpression[callee.name='assert'][arguments.length!=2]",
|
||||
message: "`assert()` must always be invoked with two arguments.",
|
||||
},
|
||||
{
|
||||
selector: "CallExpression[callee.name='isCmd'][arguments.length<2]",
|
||||
message:
|
||||
"Use `instanceof Cmd` rather than `isCmd()` with one argument.",
|
||||
},
|
||||
{
|
||||
selector: "CallExpression[callee.name='isDict'][arguments.length<2]",
|
||||
message:
|
||||
"Use `instanceof Dict` rather than `isDict()` with one argument.",
|
||||
},
|
||||
{
|
||||
selector: "CallExpression[callee.name='isName'][arguments.length<2]",
|
||||
message:
|
||||
"Use `instanceof Name` rather than `isName()` with one argument.",
|
||||
},
|
||||
{
|
||||
selector: "NewExpression[callee.name='Cmd']",
|
||||
message: "Use `Cmd.get()` rather than `new Cmd()`.",
|
||||
},
|
||||
{
|
||||
selector: "NewExpression[callee.name='Name']",
|
||||
message: "Use `Name.get()` rather than `new Name()`.",
|
||||
},
|
||||
{
|
||||
selector: "NewExpression[callee.name='Ref']",
|
||||
message: "Use `Ref.get()` rather than `new Ref()`.",
|
||||
},
|
||||
],
|
||||
"no-unneeded-ternary": "error",
|
||||
"operator-assignment": "error",
|
||||
"prefer-exponentiation-operator": "error",
|
||||
"spaced-comment": ["error", "always", { block: { balanced: true } }],
|
||||
|
||||
// ECMAScript 6
|
||||
"arrow-body-style": ["error", "as-needed"],
|
||||
"constructor-super": "error",
|
||||
"no-class-assign": "error",
|
||||
"no-const-assign": "error",
|
||||
"no-dupe-class-members": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
"no-this-before-super": "error",
|
||||
"no-useless-computed-key": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"no-useless-rename": "error",
|
||||
"no-var": "error",
|
||||
"object-shorthand": ["error", "always", { avoidQuotes: true }],
|
||||
"prefer-const": "error",
|
||||
"require-yield": "error",
|
||||
"sort-imports": ["error", { ignoreCase: true }],
|
||||
"template-curly-spacing": ["error", "never"],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: jsFiles("src"),
|
||||
rules: {
|
||||
"no-console": "error",
|
||||
},
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
Test-specific rules
|
||||
\* ======================================================================== */
|
||||
|
||||
{
|
||||
files: jsFiles("test"),
|
||||
|
||||
plugins: { jasmine },
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
...globals.jasmine,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
...jasmine.configs.recommended.rules,
|
||||
"jasmine/new-line-before-expect": "off",
|
||||
"jasmine/new-line-between-declarations": "off",
|
||||
"jasmine/no-focused-tests": "error",
|
||||
"jasmine/no-pending-tests": "off",
|
||||
"jasmine/no-spec-dupes": ["error", "branch"],
|
||||
"jasmine/no-suite-dupes": ["error", "branch"],
|
||||
"jasmine/prefer-jasmine-matcher": "off",
|
||||
"jasmine/prefer-toHaveBeenCalledWith": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: jsFiles("test/unit"),
|
||||
rules: {
|
||||
"import/no-unresolved": ["error", { ignore: ["pdfjs/"] }],
|
||||
"no-console": ["error", { allow: ["warn", "error"] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: jsFiles("test/integration"),
|
||||
rules: {
|
||||
"no-console": ["error", { allow: ["warn", "error"] }],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
selector: "CallExpression[callee.name='waitForTimeout']",
|
||||
message:
|
||||
"`waitForTimeout` can cause intermittent failures and should not be used (see issue #17656 for replacements).",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
External libraries
|
||||
\* ======================================================================== */
|
||||
|
||||
{
|
||||
files: jsFiles("external"),
|
||||
|
||||
languageOptions: { globals: globals.node },
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
Examples
|
||||
\* ======================================================================== */
|
||||
|
||||
{
|
||||
files: jsFiles("examples"),
|
||||
|
||||
languageOptions: {
|
||||
globals: {
|
||||
pdfjsImageDecoders: false,
|
||||
pdfjsLib: false,
|
||||
pdfjsViewer: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [...jsFiles("examples/node"), ...jsFiles("examples/webpack")],
|
||||
|
||||
languageOptions: { globals: globals.node },
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
Chromium extension
|
||||
\* ======================================================================== */
|
||||
|
||||
{
|
||||
files: jsFiles("extensions/chromium"),
|
||||
|
||||
languageOptions: {
|
||||
globals: globals.webextensions,
|
||||
sourceType: "script",
|
||||
},
|
||||
|
||||
rules: {
|
||||
"no-var": "off",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: chromiumExtensionServiceWorkerFiles,
|
||||
|
||||
languageOptions: {
|
||||
globals: globals.serviceworker,
|
||||
sourceType: "script",
|
||||
},
|
||||
},
|
||||
|
||||
/* ======================================================================== *\
|
||||
Other
|
||||
\* ======================================================================== */
|
||||
{
|
||||
files: ["gulpfile.mjs"],
|
||||
languageOptions: { globals: globals.node },
|
||||
},
|
||||
];
|
||||
11
examples/.eslintrc
Normal file
11
examples/.eslintrc
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": [
|
||||
"../.eslintrc"
|
||||
],
|
||||
|
||||
"globals": {
|
||||
"pdfjsImageDecoders": false,
|
||||
"pdfjsLib": false,
|
||||
"pdfjsViewer": false,
|
||||
},
|
||||
}
|
||||
@ -9,8 +9,8 @@
|
||||
<h1>'Previous/Next' example</h1>
|
||||
|
||||
<div>
|
||||
<button id="prev" type="button">Previous</button>
|
||||
<button id="next" type="button">Next</button>
|
||||
<button id="prev">Previous</button>
|
||||
<button id="next">Next</button>
|
||||
|
||||
<span>Page: <span id="page_num"></span> / <span id="page_count"></span></span>
|
||||
</div>
|
||||
|
||||
@ -113,8 +113,8 @@ footer {
|
||||
background-color: rgb(0 0 0 / 0);
|
||||
font-size: 1.2rem;
|
||||
color: rgb(255 255 255 / 1);
|
||||
background-image:
|
||||
url(images/div_line_left.png), url(images/div_line_right.png);
|
||||
background-image: url(images/div_line_left.png),
|
||||
url(images/div_line_right.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: left, right;
|
||||
background-size: 0.2rem, 0.2rem;
|
||||
|
||||
@ -43,13 +43,13 @@ limitations under the License.
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<button class="toolbarButton pageUp" title="Previous Page" id="previous" type="button"></button>
|
||||
<button class="toolbarButton pageDown" title="Next Page" id="next" type="button"></button>
|
||||
<button class="toolbarButton pageUp" title="Previous Page" id="previous"></button>
|
||||
<button class="toolbarButton pageDown" title="Next Page" id="next"></button>
|
||||
|
||||
<input type="number" id="pageNumber" class="toolbarField pageNumber" value="1" size="4" min="1">
|
||||
|
||||
<button class="toolbarButton zoomOut" title="Zoom Out" id="zoomOut" type="button"></button>
|
||||
<button class="toolbarButton zoomIn" title="Zoom In" id="zoomIn" type="button"></button>
|
||||
<button class="toolbarButton zoomOut" title="Zoom Out" id="zoomOut"></button>
|
||||
<button class="toolbarButton zoomIn" title="Zoom In" id="zoomIn"></button>
|
||||
</footer>
|
||||
|
||||
<script src="viewer.mjs" type="module"></script>
|
||||
|
||||
@ -91,10 +91,10 @@ const PDFViewerApplication = {
|
||||
let key = "pdfjs-loading-error";
|
||||
if (reason instanceof pdfjsLib.InvalidPDFException) {
|
||||
key = "pdfjs-invalid-file-error";
|
||||
} else if (reason instanceof pdfjsLib.ResponseException) {
|
||||
key = reason.missing
|
||||
? "pdfjs-missing-file-error"
|
||||
: "pdfjs-unexpected-response-error";
|
||||
} else if (reason instanceof pdfjsLib.MissingPDFException) {
|
||||
key = "pdfjs-missing-file-error";
|
||||
} else if (reason instanceof pdfjsLib.UnexpectedResponseException) {
|
||||
key = "pdfjs-unexpected-response-error";
|
||||
}
|
||||
self.l10n.get(key).then(msg => {
|
||||
self.error(msg, { message: reason?.message });
|
||||
|
||||
9
examples/node/.eslintrc
Normal file
9
examples/node/.eslintrc
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"../.eslintrc"
|
||||
],
|
||||
|
||||
"env": {
|
||||
"node": true,
|
||||
},
|
||||
}
|
||||
@ -9,7 +9,9 @@ Install the dependencies and build the PDF.js library:
|
||||
$ npm install
|
||||
$ gulp dist-install
|
||||
|
||||
Run the example to convert the first page of a PDF file to a PNG image:
|
||||
Install the Node canvas library and run the example to convert the first page of a
|
||||
PDF file to a PNG image:
|
||||
|
||||
$ npm install canvas
|
||||
$ cd examples/node/pdf2png
|
||||
$ node pdf2png.mjs
|
||||
$ node pdf2png.js
|
||||
|
||||
@ -13,9 +13,41 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { strict as assert } from "assert";
|
||||
import Canvas from "canvas";
|
||||
import fs from "fs";
|
||||
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
|
||||
|
||||
class NodeCanvasFactory {
|
||||
create(width, height) {
|
||||
assert(width > 0 && height > 0, "Invalid canvas size");
|
||||
const canvas = Canvas.createCanvas(width, height);
|
||||
const context = canvas.getContext("2d");
|
||||
return {
|
||||
canvas,
|
||||
context,
|
||||
};
|
||||
}
|
||||
|
||||
reset(canvasAndContext, width, height) {
|
||||
assert(canvasAndContext.canvas, "Canvas is not specified");
|
||||
assert(width > 0 && height > 0, "Invalid canvas size");
|
||||
canvasAndContext.canvas.width = width;
|
||||
canvasAndContext.canvas.height = height;
|
||||
}
|
||||
|
||||
destroy(canvasAndContext) {
|
||||
assert(canvasAndContext.canvas, "Canvas is not specified");
|
||||
|
||||
// Zeroing the width and height cause Firefox to release graphics
|
||||
// resources immediately, which can greatly reduce memory consumption.
|
||||
canvasAndContext.canvas.width = 0;
|
||||
canvasAndContext.canvas.height = 0;
|
||||
canvasAndContext.canvas = null;
|
||||
canvasAndContext.context = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Some PDFs need external cmaps.
|
||||
const CMAP_URL = "../../../node_modules/pdfjs-dist/cmaps/";
|
||||
const CMAP_PACKED = true;
|
||||
@ -24,6 +56,8 @@ const CMAP_PACKED = true;
|
||||
const STANDARD_FONT_DATA_URL =
|
||||
"../../../node_modules/pdfjs-dist/standard_fonts/";
|
||||
|
||||
const canvasFactory = new NodeCanvasFactory();
|
||||
|
||||
// Loading file from file system into typed array.
|
||||
const pdfPath =
|
||||
process.argv[2] || "../../../web/compressed.tracemonkey-pldi-09.pdf";
|
||||
@ -35,6 +69,7 @@ const loadingTask = getDocument({
|
||||
cMapUrl: CMAP_URL,
|
||||
cMapPacked: CMAP_PACKED,
|
||||
standardFontDataUrl: STANDARD_FONT_DATA_URL,
|
||||
canvasFactory,
|
||||
});
|
||||
|
||||
try {
|
||||
@ -43,7 +78,6 @@ try {
|
||||
// Get the first page.
|
||||
const page = await pdfDocument.getPage(1);
|
||||
// Render the page on a Node canvas with 100% scale.
|
||||
const canvasFactory = pdfDocument.canvasFactory;
|
||||
const viewport = page.getViewport({ scale: 1.0 });
|
||||
const canvasAndContext = canvasFactory.create(
|
||||
viewport.width,
|
||||
@ -57,7 +91,7 @@ try {
|
||||
const renderTask = page.render(renderContext);
|
||||
await renderTask.promise;
|
||||
// Convert the canvas to an image buffer.
|
||||
const image = canvasAndContext.canvas.toBuffer("image/png");
|
||||
const image = canvasAndContext.canvas.toBuffer();
|
||||
fs.writeFile("output.png", image, function (error) {
|
||||
if (error) {
|
||||
console.error("Error: " + error);
|
||||
|
||||
9
examples/webpack/.eslintrc
Normal file
9
examples/webpack/.eslintrc
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
"../.eslintrc"
|
||||
],
|
||||
|
||||
"env": {
|
||||
"node": true,
|
||||
},
|
||||
}
|
||||
22
extensions/chromium/.eslintrc
Normal file
22
extensions/chromium/.eslintrc
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"extends": [
|
||||
../../.eslintrc
|
||||
],
|
||||
|
||||
"env": {
|
||||
"webextensions": true
|
||||
},
|
||||
|
||||
"plugins": [
|
||||
"mozilla"
|
||||
],
|
||||
|
||||
"parserOptions": {
|
||||
"sourceType": "script"
|
||||
},
|
||||
|
||||
"rules": {
|
||||
"mozilla/import-globals": "error",
|
||||
"no-var": "off",
|
||||
},
|
||||
}
|
||||
@ -16,16 +16,13 @@ limitations under the License.
|
||||
|
||||
"use strict";
|
||||
|
||||
var VIEWER_URL = chrome.runtime.getURL("content/web/viewer.html");
|
||||
var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html");
|
||||
|
||||
function getViewerURL(pdf_url) {
|
||||
return VIEWER_URL + "?file=" + encodeURIComponent(pdf_url);
|
||||
}
|
||||
|
||||
document.addEventListener("animationstart", onAnimationStart, true);
|
||||
if (document.contentType === "application/pdf") {
|
||||
chrome.runtime.sendMessage({ action: "canRequestBody" }, maybeRenderPdfDoc);
|
||||
}
|
||||
|
||||
function onAnimationStart(event) {
|
||||
if (event.animationName === "pdfjs-detected-object-or-embed") {
|
||||
@ -224,38 +221,3 @@ function getEmbeddedViewerURL(path) {
|
||||
path = a.href;
|
||||
return getViewerURL(path) + fragment;
|
||||
}
|
||||
|
||||
function maybeRenderPdfDoc(isNotPOST) {
|
||||
if (!isNotPOST) {
|
||||
// The document was loaded through a POST request, but we cannot access the
|
||||
// original response body, nor safely send a new request to fetch the PDF.
|
||||
// Until #4483 is fixed, POST requests should be ignored.
|
||||
return;
|
||||
}
|
||||
|
||||
// Detected PDF that was not redirected by the declarativeNetRequest rules.
|
||||
// Maybe because this was served without Content-Type and sniffed as PDF.
|
||||
// Or because this is Chrome 127-, which does not support responseHeaders
|
||||
// condition in declarativeNetRequest (DNR), and PDF requests are therefore
|
||||
// not redirected via DNR.
|
||||
|
||||
// In any case, load the viewer.
|
||||
console.log(`Detected PDF via document, opening viewer for ${document.URL}`);
|
||||
|
||||
// Ideally we would use logic consistent with the DNR logic, like this:
|
||||
// location.href = getEmbeddedViewerURL(document.URL);
|
||||
// ... unfortunately, this causes Chrome to crash until version 129, fixed by
|
||||
// https://chromium.googlesource.com/chromium/src/+/8c42358b2cc549553d939efe7d36515d80563da7%5E%21/
|
||||
// Work around this by replacing the body with an iframe of the viewer.
|
||||
// Interestingly, Chrome's built-in PDF viewer uses a similar technique.
|
||||
const shadowRoot = document.body.attachShadow({ mode: "closed" });
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.style.position = "absolute";
|
||||
iframe.style.top = "0";
|
||||
iframe.style.left = "0";
|
||||
iframe.style.width = "100%";
|
||||
iframe.style.height = "100%";
|
||||
iframe.style.border = "0 none";
|
||||
iframe.src = getEmbeddedViewerURL(document.URL);
|
||||
shadowRoot.append(iframe);
|
||||
}
|
||||
|
||||
@ -17,12 +17,13 @@ limitations under the License.
|
||||
"use strict";
|
||||
|
||||
(function ExtensionRouterClosure() {
|
||||
var VIEWER_URL = chrome.runtime.getURL("content/web/viewer.html");
|
||||
var CRX_BASE_URL = chrome.runtime.getURL("/");
|
||||
var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html");
|
||||
var CRX_BASE_URL = chrome.extension.getURL("/");
|
||||
|
||||
var schemes = [
|
||||
"http",
|
||||
"https",
|
||||
"ftp",
|
||||
"file",
|
||||
"chrome-extension",
|
||||
"blob",
|
||||
@ -55,50 +56,73 @@ limitations under the License.
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function resolveViewerURL(originalUrl) {
|
||||
if (originalUrl.startsWith(CRX_BASE_URL)) {
|
||||
// TODO(rob): Use declarativeWebRequest once declared URL-encoding is
|
||||
// supported, see http://crbug.com/273589
|
||||
// (or rewrite the query string parser in viewer.js to get it to
|
||||
// recognize the non-URL-encoded PDF URL.)
|
||||
chrome.webRequest.onBeforeRequest.addListener(
|
||||
function (details) {
|
||||
// This listener converts chrome-extension://.../http://...pdf to
|
||||
// chrome-extension://.../content/web/viewer.html?file=http%3A%2F%2F...pdf
|
||||
var url = parseExtensionURL(originalUrl);
|
||||
var url = parseExtensionURL(details.url);
|
||||
if (url) {
|
||||
url = VIEWER_URL + "?file=" + url;
|
||||
var i = originalUrl.indexOf("#");
|
||||
var i = details.url.indexOf("#");
|
||||
if (i > 0) {
|
||||
url += originalUrl.slice(i);
|
||||
url += details.url.slice(i);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
self.addEventListener("fetch", event => {
|
||||
const req = event.request;
|
||||
if (req.destination === "document") {
|
||||
var url = resolveViewerURL(req.url);
|
||||
if (url) {
|
||||
console.log("Redirecting " + req.url + " to " + url);
|
||||
event.respondWith(Response.redirect(url));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Ctrl + F5 bypasses service worker. the pretty extension URLs will fail to
|
||||
// resolve in that case. Catch this and redirect to destination.
|
||||
chrome.webNavigation.onErrorOccurred.addListener(
|
||||
details => {
|
||||
if (details.frameId !== 0) {
|
||||
// Not a top-level frame. Cannot easily navigate a specific child frame.
|
||||
return;
|
||||
}
|
||||
const url = resolveViewerURL(details.url);
|
||||
if (url) {
|
||||
console.log(`Redirecting ${details.url} to ${url} (fallback)`);
|
||||
chrome.tabs.update(details.tabId, { url });
|
||||
console.log("Redirecting " + details.url + " to " + url);
|
||||
return { redirectUrl: url };
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
{ url: [{ urlPrefix: CRX_BASE_URL }] }
|
||||
{
|
||||
types: ["main_frame", "sub_frame"],
|
||||
urls: schemes.map(function (scheme) {
|
||||
// Format: "chrome-extension://[EXTENSIONID]/<scheme>*"
|
||||
return CRX_BASE_URL + scheme + "*";
|
||||
}),
|
||||
},
|
||||
["blocking"]
|
||||
);
|
||||
|
||||
// When session restore is used, viewer pages may be loaded before the
|
||||
// webRequest event listener is attached (= page not found).
|
||||
// Or the extension could have been crashed (OOM), leaving a sad tab behind.
|
||||
// Reload these tabs.
|
||||
chrome.tabs.query(
|
||||
{
|
||||
url: CRX_BASE_URL + "*:*",
|
||||
},
|
||||
function (tabsFromLastSession) {
|
||||
for (const { id } of tabsFromLastSession) {
|
||||
chrome.tabs.reload(id);
|
||||
}
|
||||
}
|
||||
);
|
||||
console.log("Set up extension URL router.");
|
||||
|
||||
Object.keys(localStorage).forEach(function (key) {
|
||||
// The localStorage item is set upon unload by chromecom.js.
|
||||
var parsedKey = /^unload-(\d+)-(true|false)-(.+)/.exec(key);
|
||||
if (parsedKey) {
|
||||
var timeStart = parseInt(parsedKey[1], 10);
|
||||
var isHidden = parsedKey[2] === "true";
|
||||
var url = parsedKey[3];
|
||||
if (Date.now() - timeStart < 3000) {
|
||||
// Is it a new item (younger than 3 seconds)? Assume that the extension
|
||||
// just reloaded, so restore the tab (work-around for crbug.com/511670).
|
||||
chrome.tabs.create({
|
||||
url:
|
||||
chrome.runtime.getURL("restoretab.html") +
|
||||
"?" +
|
||||
encodeURIComponent(url) +
|
||||
"#" +
|
||||
encodeURIComponent(localStorage.getItem(key)),
|
||||
active: !isHidden,
|
||||
});
|
||||
}
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
BIN
extensions/chromium/icon19.png
Normal file
BIN
extensions/chromium/icon19.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 679 B |
BIN
extensions/chromium/icon38.png
Normal file
BIN
extensions/chromium/icon38.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
@ -1,6 +1,6 @@
|
||||
{
|
||||
"minimum_chrome_version": "103",
|
||||
"manifest_version": 3,
|
||||
"minimum_chrome_version": "88",
|
||||
"manifest_version": 2,
|
||||
"name": "PDF Viewer",
|
||||
"version": "PDFJSSCRIPT_VERSION",
|
||||
"description": "Uses HTML5 to display PDF files directly in the browser.",
|
||||
@ -10,52 +10,61 @@
|
||||
"16": "icon16.png"
|
||||
},
|
||||
"permissions": [
|
||||
"alarms",
|
||||
"declarativeNetRequestWithHostAccess",
|
||||
"fileBrowserHandler",
|
||||
"webRequest",
|
||||
"webRequestBlocking",
|
||||
"<all_urls>",
|
||||
"tabs",
|
||||
"webNavigation",
|
||||
"storage"
|
||||
],
|
||||
"host_permissions": ["<all_urls>"],
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["http://*/*", "https://*/*", "file://*/*"],
|
||||
"matches": ["http://*/*", "https://*/*", "ftp://*/*", "file://*/*"],
|
||||
"run_at": "document_start",
|
||||
"all_frames": true,
|
||||
"css": ["contentstyle.css"],
|
||||
"js": ["contentscript.js"]
|
||||
}
|
||||
],
|
||||
"content_security_policy": {
|
||||
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
|
||||
},
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
|
||||
"file_browser_handlers": [
|
||||
{
|
||||
"id": "open-as-pdf",
|
||||
"default_title": "Open with PDF Viewer",
|
||||
"file_filters": ["filesystem:*.pdf"]
|
||||
}
|
||||
],
|
||||
"storage": {
|
||||
"managed_schema": "preferences_schema.json"
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "options/options.html"
|
||||
"page": "options/options.html",
|
||||
"chrome_style": true
|
||||
},
|
||||
"options_page": "options/options.html",
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
"page": "pdfHandler.html"
|
||||
},
|
||||
"page_action": {
|
||||
"default_icon": {
|
||||
"19": "icon19.png",
|
||||
"38": "icon38.png"
|
||||
},
|
||||
"default_title": "Show PDF URL",
|
||||
"default_popup": "pageActionPopup.html"
|
||||
},
|
||||
"incognito": "split",
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": [
|
||||
"content/web/viewer.html",
|
||||
"http:/*",
|
||||
"https:/*",
|
||||
"file:/*",
|
||||
"chrome-extension:/*",
|
||||
"blob:*",
|
||||
"data:*",
|
||||
"filesystem:/*",
|
||||
"drive:*"
|
||||
],
|
||||
"matches": ["<all_urls>"],
|
||||
"extension_ids": ["*"]
|
||||
}
|
||||
"content/web/viewer.html",
|
||||
"http:/*",
|
||||
"https:/*",
|
||||
"ftp:/*",
|
||||
"file:/*",
|
||||
"chrome-extension:/*",
|
||||
"blob:*",
|
||||
"data:*",
|
||||
"filesystem:/*",
|
||||
"drive:*"
|
||||
]
|
||||
}
|
||||
|
||||
@ -13,14 +13,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
"use strict";
|
||||
/* eslint strict: ["error", "function"] */
|
||||
|
||||
chrome.runtime.onInstalled.addListener(({ reason }) => {
|
||||
if (reason !== "update") {
|
||||
// We only need to run migration logic for extension updates, not for new
|
||||
// installs or browser updates.
|
||||
return;
|
||||
}
|
||||
(function () {
|
||||
"use strict";
|
||||
var storageLocal = chrome.storage.local;
|
||||
var storageSync = chrome.storage.sync;
|
||||
|
||||
@ -41,12 +37,16 @@ chrome.runtime.onInstalled.addListener(({ reason }) => {
|
||||
});
|
||||
});
|
||||
|
||||
async function getStorageNames(callback) {
|
||||
function getStorageNames(callback) {
|
||||
var x = new XMLHttpRequest();
|
||||
var schema_location = chrome.runtime.getManifest().storage.managed_schema;
|
||||
var res = await fetch(chrome.runtime.getURL(schema_location));
|
||||
var storageManifest = await res.json();
|
||||
var storageKeys = Object.keys(storageManifest.properties);
|
||||
callback(storageKeys);
|
||||
x.open("get", chrome.runtime.getURL(schema_location));
|
||||
x.onload = function () {
|
||||
var storageKeys = Object.keys(x.response.properties);
|
||||
callback(storageKeys);
|
||||
};
|
||||
x.responseType = "json";
|
||||
x.send();
|
||||
}
|
||||
|
||||
// Save |values| to storage.sync and delete the values with that key from
|
||||
@ -150,4 +150,4 @@ chrome.runtime.onInstalled.addListener(({ reason }) => {
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
@ -19,28 +19,23 @@ limitations under the License.
|
||||
<meta charset="utf-8">
|
||||
<title>PDF.js viewer options</title>
|
||||
<style>
|
||||
/* TODO: Remove as much custom CSS as possible - crbug.com/446511 */
|
||||
body {
|
||||
min-width: 400px; /* a page at the settings page is at least 400px wide */
|
||||
margin: 14px 17px; /* already added by default in Chrome 40.0.2212.0 */
|
||||
}
|
||||
.settings-row {
|
||||
margin: 1em 0;
|
||||
}
|
||||
.checkbox label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
.checkbox label input {
|
||||
flex-shrink: 0;
|
||||
margin: 0.65em 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="settings-boxes"></div>
|
||||
<button id="reset-button" type="button">Restore default settings</button>
|
||||
<button id="reset-button">Restore default settings</button>
|
||||
|
||||
<template id="checkbox-template">
|
||||
<div class="settings-row checkbox">
|
||||
<!-- Chromium's style: //src/extensions/renderer/resources/extension.css -->
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox">
|
||||
<span></span>
|
||||
|
||||
45
extensions/chromium/pageAction/background.js
Normal file
45
extensions/chromium/pageAction/background.js
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright 2014 Mozilla Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
(function PageActionClosure() {
|
||||
/**
|
||||
* @param {number} tabId - ID of tab where the page action will be shown.
|
||||
* @param {string} url - URL to be displayed in page action.
|
||||
*/
|
||||
function showPageAction(tabId, displayUrl) {
|
||||
// rewriteUrlClosure in viewer.js ensures that the URL looks like
|
||||
// chrome-extension://[extensionid]/http://example.com/file.pdf
|
||||
var url = /^chrome-extension:\/\/[a-p]{32}\/([^#]+)/.exec(displayUrl);
|
||||
if (url) {
|
||||
url = url[1];
|
||||
chrome.pageAction.setPopup({
|
||||
tabId,
|
||||
popup: "/pageAction/popup.html?file=" + encodeURIComponent(url),
|
||||
});
|
||||
chrome.pageAction.show(tabId);
|
||||
} else {
|
||||
console.log("Unable to get PDF url from " + displayUrl);
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener(function (message, sender) {
|
||||
if (message === "showPageAction" && sender.tab) {
|
||||
showPageAction(sender.tab.id, sender.tab.url);
|
||||
}
|
||||
});
|
||||
})();
|
||||
44
extensions/chromium/pageAction/popup.html
Normal file
44
extensions/chromium/pageAction/popup.html
Normal file
@ -0,0 +1,44 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
Copyright 2012 Mozilla Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<style>
|
||||
html {
|
||||
/* maximum width of popup as defined in Chromium's source code as kMaxWidth
|
||||
//src/chrome/browser/ui/views/extensions/extension_popup.cc
|
||||
//src/chrome/browser/ui/gtk/extensions/extension_popup_gtk.cc
|
||||
*/
|
||||
width: 800px;
|
||||
/* in case Chromium decides to lower the value of kMaxWidth */
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body contentEditable="plaintext-only" spellcheck="false">
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
25
extensions/chromium/pageAction/popup.js
Normal file
25
extensions/chromium/pageAction/popup.js
Normal file
@ -0,0 +1,25 @@
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var url = location.search.match(/[&?]file=([^&]+)/i);
|
||||
if (url) {
|
||||
url = decodeURIComponent(url[1]);
|
||||
document.body.textContent = url;
|
||||
// Set cursor to end of the content-editable section.
|
||||
window.getSelection().selectAllChildren(document.body);
|
||||
window.getSelection().collapseToEnd();
|
||||
}
|
||||
102
extensions/chromium/pdfHandler-vcros.js
Normal file
102
extensions/chromium/pdfHandler-vcros.js
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright 2014 Mozilla Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/* eslint strict: ["error", "function"] */
|
||||
/* import-globals-from pdfHandler.js */
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
if (!chrome.fileBrowserHandler) {
|
||||
// Not on Chromium OS, bail out
|
||||
return;
|
||||
}
|
||||
chrome.fileBrowserHandler.onExecute.addListener(onExecuteFileBrowserHandler);
|
||||
|
||||
/**
|
||||
* Invoked when "Open with PDF Viewer" is chosen in the File browser.
|
||||
*
|
||||
* @param {string} id File browser action ID as specified in
|
||||
* manifest.json
|
||||
* @param {Object} details Object of type FileHandlerExecuteEventDetails
|
||||
*/
|
||||
function onExecuteFileBrowserHandler(id, details) {
|
||||
if (id !== "open-as-pdf") {
|
||||
return;
|
||||
}
|
||||
var fileEntries = details.entries;
|
||||
// "tab_id" is the currently documented format, but it is inconsistent with
|
||||
// the other Chrome APIs that use "tabId" (http://crbug.com/179767)
|
||||
var tabId = details.tab_id || details.tabId;
|
||||
if (tabId > 0) {
|
||||
chrome.tabs.get(tabId, function (tab) {
|
||||
openViewer(tab && tab.windowId, fileEntries);
|
||||
});
|
||||
} else {
|
||||
// Re-use existing window, if available.
|
||||
chrome.windows.getLastFocused(function (chromeWindow) {
|
||||
var windowId = chromeWindow && chromeWindow.id;
|
||||
if (windowId) {
|
||||
chrome.windows.update(windowId, { focused: true });
|
||||
}
|
||||
openViewer(windowId, fileEntries);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the PDF Viewer for the given list of PDF files.
|
||||
*
|
||||
* @param {number} windowId
|
||||
* @param {Array} fileEntries List of Entry objects (HTML5 FileSystem API)
|
||||
*/
|
||||
function openViewer(windowId, fileEntries) {
|
||||
if (!fileEntries.length) {
|
||||
return;
|
||||
}
|
||||
var fileEntry = fileEntries.shift();
|
||||
var url = fileEntry.toURL();
|
||||
// Use drive: alias to get shorter (more human-readable) URLs.
|
||||
url = url.replace(
|
||||
/^filesystem:chrome-extension:\/\/[a-p]{32}\/external\//,
|
||||
"drive:"
|
||||
);
|
||||
url = getViewerURL(url);
|
||||
|
||||
if (windowId) {
|
||||
chrome.tabs.create(
|
||||
{
|
||||
windowId,
|
||||
active: true,
|
||||
url,
|
||||
},
|
||||
function () {
|
||||
openViewer(windowId, fileEntries);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
chrome.windows.create(
|
||||
{
|
||||
type: "normal",
|
||||
focused: true,
|
||||
url,
|
||||
},
|
||||
function (chromeWindow) {
|
||||
openViewer(chromeWindow.id, fileEntries);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
})();
|
||||
24
extensions/chromium/pdfHandler.html
Normal file
24
extensions/chromium/pdfHandler.html
Normal file
@ -0,0 +1,24 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
Copyright 2012 Mozilla Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<script src="options/migration.js"></script>
|
||||
<script src="preserve-referer.js"></script>
|
||||
<script src="pdfHandler.js"></script>
|
||||
<script src="extension-router.js"></script>
|
||||
<script src="pdfHandler-vcros.js"></script>
|
||||
<script src="pageAction/background.js"></script>
|
||||
<script src="suppress-update.js"></script>
|
||||
<script src="telemetry.js"></script>
|
||||
@ -13,256 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/* globals canRequestBody */ // From preserve-referer.js
|
||||
/* import-globals-from preserve-referer.js */
|
||||
|
||||
"use strict";
|
||||
|
||||
var VIEWER_URL = chrome.runtime.getURL("content/web/viewer.html");
|
||||
|
||||
// Use in-memory storage to ensure that the DNR rules have been registered at
|
||||
// least once per session. runtime.onInstalled would have been the most fitting
|
||||
// event to ensure that, except there are cases where it does not fire when
|
||||
// needed. E.g. in incognito mode: https://issues.chromium.org/issues/41029550
|
||||
chrome.storage.session.get({ hasPdfRedirector: false }, async items => {
|
||||
if (items?.hasPdfRedirector) {
|
||||
return;
|
||||
}
|
||||
const rules = await chrome.declarativeNetRequest.getDynamicRules();
|
||||
if (rules.length) {
|
||||
// Dynamic rules persist across extension updates. We don't expect other
|
||||
// dynamic rules, so just remove them all.
|
||||
await chrome.declarativeNetRequest.updateDynamicRules({
|
||||
removeRuleIds: rules.map(r => r.id),
|
||||
});
|
||||
}
|
||||
await registerPdfRedirectRule();
|
||||
|
||||
// Only set the flag in the end, so that we know for sure that all
|
||||
// asynchronous initialization logic has run. If not, then we will run the
|
||||
// logic again at the next background wakeup.
|
||||
chrome.storage.session.set({ hasPdfRedirector: true });
|
||||
});
|
||||
|
||||
/**
|
||||
* Registers declarativeNetRequest rules to redirect PDF requests to the viewer.
|
||||
* The caller should clear any previously existing dynamic DNR rules.
|
||||
*
|
||||
* The logic here is the declarative version of the runtime logic in the
|
||||
* webRequest.onHeadersReceived implementation at
|
||||
* https://github.com/mozilla/pdf.js/blob/0676ea19cf17023ec8c2d6ad69a859c345c01dc1/extensions/chromium/pdfHandler.js#L34-L152
|
||||
*/
|
||||
async function registerPdfRedirectRule() {
|
||||
// "allow" means to ignore rules (from this extension) with lower priority.
|
||||
const ACTION_IGNORE_OTHER_RULES = { type: "allow" };
|
||||
|
||||
// Redirect to viewer. The rule condition is expected to specify regexFilter
|
||||
// that matches the full request URL.
|
||||
const ACTION_REDIRECT_TO_VIEWER = {
|
||||
type: "redirect",
|
||||
redirect: {
|
||||
// DNR does not support transformations such as encodeURIComponent on the
|
||||
// match, so we just concatenate the URL as is without modifications.
|
||||
// TODO: use "?file=\\0" when DNR supports transformations as proposed at
|
||||
// https://github.com/w3c/webextensions/issues/636#issuecomment-2165978322
|
||||
regexSubstitution: VIEWER_URL + "?DNR:\\0",
|
||||
},
|
||||
};
|
||||
|
||||
// Rules in order of prority (highest priority rule first).
|
||||
// The required "id" fields will be auto-generated later.
|
||||
const addRules = [
|
||||
{
|
||||
// Do not redirect for URLs containing pdfjs.action=download.
|
||||
condition: {
|
||||
urlFilter: "pdfjs.action=download",
|
||||
resourceTypes: ["main_frame", "sub_frame"],
|
||||
},
|
||||
action: ACTION_IGNORE_OTHER_RULES,
|
||||
},
|
||||
{
|
||||
// Redirect local PDF files if isAllowedFileSchemeAccess is true. No-op
|
||||
// otherwise and then handled by webNavigation.onBeforeNavigate below.
|
||||
condition: {
|
||||
regexFilter: "^file://.*\\.pdf$",
|
||||
resourceTypes: ["main_frame", "sub_frame"],
|
||||
},
|
||||
action: ACTION_REDIRECT_TO_VIEWER,
|
||||
},
|
||||
{
|
||||
// Respect the Content-Disposition:attachment header in sub_frame. But:
|
||||
// Display the PDF viewer regardless of the Content-Disposition header if
|
||||
// the file is displayed in the main frame, since most often users want to
|
||||
// view a PDF, and servers are often misconfigured.
|
||||
condition: {
|
||||
urlFilter: "*",
|
||||
resourceTypes: ["sub_frame"], // Note: no main_frame, handled below.
|
||||
responseHeaders: [
|
||||
{
|
||||
header: "content-disposition",
|
||||
values: ["attachment*"],
|
||||
},
|
||||
],
|
||||
},
|
||||
action: ACTION_IGNORE_OTHER_RULES,
|
||||
},
|
||||
{
|
||||
// If the query string contains "=download", do not unconditionally force
|
||||
// viewer to open the PDF, but first check whether the Content-Disposition
|
||||
// header specifies an attachment. This allows sites like Google Drive to
|
||||
// operate correctly (#6106).
|
||||
condition: {
|
||||
urlFilter: "=download",
|
||||
resourceTypes: ["main_frame"], // No sub_frame, was handled before.
|
||||
responseHeaders: [
|
||||
{
|
||||
header: "content-disposition",
|
||||
values: ["attachment*"],
|
||||
},
|
||||
],
|
||||
},
|
||||
action: ACTION_IGNORE_OTHER_RULES,
|
||||
},
|
||||
{
|
||||
// Regular http(s) PDF requests.
|
||||
condition: {
|
||||
regexFilter: "^.*$",
|
||||
// The viewer does not have the original request context and issues a
|
||||
// GET request. The original response to POST requests is unavailable.
|
||||
excludedRequestMethods: ["post"],
|
||||
resourceTypes: ["main_frame", "sub_frame"],
|
||||
responseHeaders: [
|
||||
{
|
||||
header: "content-type",
|
||||
values: ["application/pdf", "application/pdf;*"],
|
||||
},
|
||||
],
|
||||
},
|
||||
action: ACTION_REDIRECT_TO_VIEWER,
|
||||
},
|
||||
{
|
||||
// Wrong MIME-type, but a PDF file according to the file name in the URL.
|
||||
condition: {
|
||||
regexFilter: "^.*\\.pdf\\b.*$",
|
||||
// The viewer does not have the original request context and issues a
|
||||
// GET request. The original response to POST requests is unavailable.
|
||||
excludedRequestMethods: ["post"],
|
||||
resourceTypes: ["main_frame", "sub_frame"],
|
||||
responseHeaders: [
|
||||
{
|
||||
header: "content-type",
|
||||
values: ["application/octet-stream", "application/octet-stream;*"],
|
||||
},
|
||||
],
|
||||
},
|
||||
action: ACTION_REDIRECT_TO_VIEWER,
|
||||
},
|
||||
{
|
||||
// Wrong MIME-type, but a PDF file according to Content-Disposition.
|
||||
condition: {
|
||||
regexFilter: "^.*$",
|
||||
// The viewer does not have the original request context and issues a
|
||||
// GET request. The original response to POST requests is unavailable.
|
||||
excludedRequestMethods: ["post"],
|
||||
resourceTypes: ["main_frame", "sub_frame"],
|
||||
responseHeaders: [
|
||||
{
|
||||
header: "content-disposition",
|
||||
values: ["*.pdf", '*.pdf"*', "*.pdf'*"],
|
||||
},
|
||||
],
|
||||
// We only want to match by content-disposition if Content-Type is set
|
||||
// to application/octet-stream. The responseHeaders condition is a
|
||||
// logical OR instead of AND, so to simulate the AND condition we use
|
||||
// the double negation of excludedResponseHeaders + excludedValues.
|
||||
// This matches any request whose content-type header is set and not
|
||||
// "application/octet-stream". It will also match if "content-type" is
|
||||
// not set, but we are okay with that since the browser would usually
|
||||
// try to sniff the MIME type in that case.
|
||||
excludedResponseHeaders: [
|
||||
{
|
||||
header: "content-type",
|
||||
excludedValues: [
|
||||
"application/octet-stream",
|
||||
"application/octet-stream;*",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
action: ACTION_REDIRECT_TO_VIEWER,
|
||||
},
|
||||
];
|
||||
for (const [i, rule] of addRules.entries()) {
|
||||
// id must be unique and at least 1, but i starts at 0. So add +1.
|
||||
rule.id = i + 1;
|
||||
rule.priority = addRules.length - i;
|
||||
}
|
||||
try {
|
||||
// Note: condition.responseHeaders is only supported in Chrome 128+, but
|
||||
// does not trigger errors in Chrome 123 - 127 as explained at:
|
||||
// https://github.com/w3c/webextensions/issues/638#issuecomment-2181124486
|
||||
// We need to detect this and avoid registering rules, because otherwise all
|
||||
// requests are redirected to the viewer instead of just PDF requests,
|
||||
// because Chrome accepts rules while ignoring the responseHeaders condition
|
||||
// - also reported at https://crbug.com/347186592
|
||||
if (!(await isHeaderConditionSupported())) {
|
||||
throw new Error("DNR responseHeaders condition is not supported.");
|
||||
}
|
||||
await chrome.declarativeNetRequest.updateDynamicRules({ addRules });
|
||||
} catch (e) {
|
||||
// When we do not register DNR rules for any reason, fall back to catching
|
||||
// PDF documents via maybeRenderPdfDoc in contentscript.js.
|
||||
console.error("Failed to register rules to redirect PDF requests.");
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
// For the source and explanation of this logic, see
|
||||
// https://github.com/w3c/webextensions/issues/638#issuecomment-2181124486
|
||||
async function isHeaderConditionSupported() {
|
||||
const ruleId = 123456; // Some rule ID that is not already used elsewhere.
|
||||
try {
|
||||
// Throws synchronously if not supported.
|
||||
await chrome.declarativeNetRequest.updateSessionRules({
|
||||
addRules: [
|
||||
{
|
||||
id: ruleId,
|
||||
condition: {
|
||||
responseHeaders: [{ header: "whatever" }],
|
||||
urlFilter: "|does_not_match_anything",
|
||||
},
|
||||
action: { type: "block" },
|
||||
},
|
||||
],
|
||||
});
|
||||
} catch {
|
||||
return false; // responseHeaders condition not supported.
|
||||
}
|
||||
// Chrome may recognize the properties but have the implementation behind a
|
||||
// flag. When the implementation is disabled, validation is skipped too.
|
||||
try {
|
||||
await chrome.declarativeNetRequest.updateSessionRules({
|
||||
removeRuleIds: [ruleId],
|
||||
addRules: [
|
||||
{
|
||||
id: ruleId,
|
||||
condition: {
|
||||
responseHeaders: [],
|
||||
urlFilter: "|does_not_match_anything",
|
||||
},
|
||||
action: { type: "block" },
|
||||
},
|
||||
],
|
||||
});
|
||||
return false; // Validation skipped = feature disabled.
|
||||
} catch {
|
||||
return true; // Validation worked = feature enabled.
|
||||
} finally {
|
||||
await chrome.declarativeNetRequest.updateSessionRules({
|
||||
removeRuleIds: [ruleId],
|
||||
});
|
||||
}
|
||||
}
|
||||
var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html");
|
||||
|
||||
function getViewerURL(pdf_url) {
|
||||
// |pdf_url| may contain a fragment such as "#page=2". That should be passed
|
||||
@ -276,42 +31,174 @@ function getViewerURL(pdf_url) {
|
||||
return VIEWER_URL + "?file=" + encodeURIComponent(pdf_url) + hash;
|
||||
}
|
||||
|
||||
// If the user has not granted access to file:-URLs, then declarativeNetRequest
|
||||
// will not catch the request. It is still visible through the webNavigation
|
||||
// API though, and we can replace the tab with the viewer.
|
||||
// The viewer will detect that it has no access to file:-URLs, and prompt the
|
||||
// user to activate file permissions.
|
||||
chrome.webNavigation.onBeforeNavigate.addListener(
|
||||
function (details) {
|
||||
// Note: pdfjs.action=download is not checked here because that code path
|
||||
// is not reachable for local files through the viewer when we do not have
|
||||
// file:-access.
|
||||
if (details.frameId === 0) {
|
||||
chrome.extension.isAllowedFileSchemeAccess(function (isAllowedAccess) {
|
||||
if (isAllowedAccess) {
|
||||
// Expected to be handled by DNR. Don't do anything.
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @param {Object} details First argument of the webRequest.onHeadersReceived
|
||||
* event. The property "url" is read.
|
||||
* @returns {boolean} True if the PDF file should be downloaded.
|
||||
*/
|
||||
function isPdfDownloadable(details) {
|
||||
if (details.url.includes("pdfjs.action=download")) {
|
||||
return true;
|
||||
}
|
||||
// Display the PDF viewer regardless of the Content-Disposition header if the
|
||||
// file is displayed in the main frame, since most often users want to view
|
||||
// a PDF, and servers are often misconfigured.
|
||||
// If the query string contains "=download", do not unconditionally force the
|
||||
// viewer to open the PDF, but first check whether the Content-Disposition
|
||||
// header specifies an attachment. This allows sites like Google Drive to
|
||||
// operate correctly (#6106).
|
||||
if (details.type === "main_frame" && !details.url.includes("=download")) {
|
||||
return false;
|
||||
}
|
||||
var cdHeader =
|
||||
details.responseHeaders &&
|
||||
getHeaderFromHeaders(details.responseHeaders, "content-disposition");
|
||||
return cdHeader && /^attachment/i.test(cdHeader.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header from the list of headers for a given name.
|
||||
* @param {Array} headers responseHeaders of webRequest.onHeadersReceived
|
||||
* @returns {undefined|{name: string, value: string}} The header, if found.
|
||||
*/
|
||||
function getHeaderFromHeaders(headers, headerName) {
|
||||
for (const header of headers) {
|
||||
if (header.name.toLowerCase() === headerName) {
|
||||
return header;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the request is a PDF file.
|
||||
* @param {Object} details First argument of the webRequest.onHeadersReceived
|
||||
* event. The properties "responseHeaders" and "url"
|
||||
* are read.
|
||||
* @returns {boolean} True if the resource is a PDF file.
|
||||
*/
|
||||
function isPdfFile(details) {
|
||||
var header = getHeaderFromHeaders(details.responseHeaders, "content-type");
|
||||
if (header) {
|
||||
var headerValue = header.value.toLowerCase().split(";", 1)[0].trim();
|
||||
if (headerValue === "application/pdf") {
|
||||
return true;
|
||||
}
|
||||
if (headerValue === "application/octet-stream") {
|
||||
if (details.url.toLowerCase().indexOf(".pdf") > 0) {
|
||||
return true;
|
||||
}
|
||||
var cdHeader = getHeaderFromHeaders(
|
||||
details.responseHeaders,
|
||||
"content-disposition"
|
||||
);
|
||||
if (cdHeader && /\.pdf(["']|$)/i.test(cdHeader.value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a set of headers, and set "Content-Disposition: attachment".
|
||||
* @param {Object} details First argument of the webRequest.onHeadersReceived
|
||||
* event. The property "responseHeaders" is read and
|
||||
* modified if needed.
|
||||
* @returns {Object|undefined} The return value for the onHeadersReceived event.
|
||||
* Object with key "responseHeaders" if the headers
|
||||
* have been modified, undefined otherwise.
|
||||
*/
|
||||
function getHeadersWithContentDispositionAttachment(details) {
|
||||
var headers = details.responseHeaders;
|
||||
var cdHeader = getHeaderFromHeaders(headers, "content-disposition");
|
||||
if (!cdHeader) {
|
||||
cdHeader = { name: "Content-Disposition" };
|
||||
headers.push(cdHeader);
|
||||
}
|
||||
if (!/^attachment/i.test(cdHeader.value)) {
|
||||
cdHeader.value = "attachment" + cdHeader.value.replace(/^[^;]+/i, "");
|
||||
return { responseHeaders: headers };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
chrome.webRequest.onHeadersReceived.addListener(
|
||||
function (details) {
|
||||
if (details.method !== "GET") {
|
||||
// Don't intercept POST requests until http://crbug.com/104058 is fixed.
|
||||
return undefined;
|
||||
}
|
||||
if (!isPdfFile(details)) {
|
||||
return undefined;
|
||||
}
|
||||
if (isPdfDownloadable(details)) {
|
||||
// Force download by ensuring that Content-Disposition: attachment is set
|
||||
return getHeadersWithContentDispositionAttachment(details);
|
||||
}
|
||||
|
||||
var viewerUrl = getViewerURL(details.url);
|
||||
|
||||
// Implemented in preserve-referer.js
|
||||
saveReferer(details);
|
||||
|
||||
return { redirectUrl: viewerUrl };
|
||||
},
|
||||
{
|
||||
urls: ["<all_urls>"],
|
||||
types: ["main_frame", "sub_frame"],
|
||||
},
|
||||
["blocking", "responseHeaders"]
|
||||
);
|
||||
|
||||
chrome.webRequest.onBeforeRequest.addListener(
|
||||
function (details) {
|
||||
if (isPdfDownloadable(details)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var viewerUrl = getViewerURL(details.url);
|
||||
|
||||
return { redirectUrl: viewerUrl };
|
||||
},
|
||||
{
|
||||
urls: ["file://*/*.pdf", "file://*/*.PDF"],
|
||||
types: ["main_frame", "sub_frame"],
|
||||
},
|
||||
["blocking"]
|
||||
);
|
||||
|
||||
chrome.extension.isAllowedFileSchemeAccess(function (isAllowedAccess) {
|
||||
if (isAllowedAccess) {
|
||||
return;
|
||||
}
|
||||
// If the user has not granted access to file:-URLs, then the webRequest API
|
||||
// will not catch the request. It is still visible through the webNavigation
|
||||
// API though, and we can replace the tab with the viewer.
|
||||
// The viewer will detect that it has no access to file:-URLs, and prompt the
|
||||
// user to activate file permissions.
|
||||
chrome.webNavigation.onBeforeNavigate.addListener(
|
||||
function (details) {
|
||||
if (details.frameId === 0 && !isPdfDownloadable(details)) {
|
||||
chrome.tabs.update(details.tabId, {
|
||||
url: getViewerURL(details.url),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
url: [
|
||||
{
|
||||
urlPrefix: "file://",
|
||||
pathSuffix: ".pdf",
|
||||
},
|
||||
{
|
||||
urlPrefix: "file://",
|
||||
pathSuffix: ".PDF",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
{
|
||||
url: [
|
||||
{
|
||||
urlPrefix: "file://",
|
||||
pathSuffix: ".pdf",
|
||||
},
|
||||
{
|
||||
urlPrefix: "file://",
|
||||
pathSuffix: ".PDF",
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
);
|
||||
});
|
||||
|
||||
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
|
||||
if (message && message.action === "getParentOrigin") {
|
||||
@ -358,11 +245,6 @@ chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
|
||||
url,
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
if (message && message.action === "canRequestBody") {
|
||||
sendResponse(canRequestBody(sender.tab.id, sender.frameId));
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
@ -45,37 +45,7 @@
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"enableHWA": {
|
||||
"title": "Enable hardware acceleration",
|
||||
"description": "Whether to enable hardware acceleration.",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"enableAltText": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"enableGuessAltText": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"enableAltTextModelDownload": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"enableNewAltTextWhenAddingImage": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"altTextLearnMoreUrl": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"enableSignatureEditor": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"enableUpdatedAddImage": {
|
||||
"enableML": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
@ -98,6 +68,10 @@
|
||||
"description": "Whether to allow execution of active content (JavaScript) by PDF files.",
|
||||
"default": false
|
||||
},
|
||||
"enableHighlightEditor": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"enableHighlightFloatingButton": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
@ -106,6 +80,10 @@
|
||||
"type": "string",
|
||||
"default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F"
|
||||
},
|
||||
"enableStampEditor": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"disableRange": {
|
||||
"title": "Disable range requests",
|
||||
"description": "Whether to disable range requests (not recommended).",
|
||||
@ -224,11 +202,6 @@
|
||||
"description": "The color is a string as defined in CSS. Its goal is to help improve readability in high contrast mode",
|
||||
"type": "string",
|
||||
"default": "CanvasText"
|
||||
},
|
||||
"enableAutoLinking": {
|
||||
"description": "Enable creation of hyperlinks from text that look like URLs.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,14 +13,20 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
/* import-globals-from pdfHandler.js */
|
||||
/* exported saveReferer */
|
||||
|
||||
"use strict";
|
||||
/**
|
||||
* This file is one part of the Referer persistency implementation. The other
|
||||
* part resides in chromecom.js.
|
||||
*
|
||||
* This file collects Referer headers for every http(s) request, and temporarily
|
||||
* stores the request headers in a dictionary, for REFERRER_IN_MEMORY_TIME ms.
|
||||
* This file collects request headers for every http(s) request, and temporarily
|
||||
* stores the request headers in a dictionary. Upon completion of the request
|
||||
* (success or failure), the headers are discarded.
|
||||
* pdfHandler.js will call saveReferer(details) when it is about to redirect to
|
||||
* the viewer. Upon calling saveReferer, the Referer header is extracted from
|
||||
* the request headers and saved.
|
||||
*
|
||||
* When the viewer is opened, it opens a port ("chromecom-referrer"). This port
|
||||
* is used to set up the webRequest listeners that stick the Referer headers to
|
||||
@ -30,64 +36,49 @@ limitations under the License.
|
||||
* See setReferer in chromecom.js for more explanation of this logic.
|
||||
*/
|
||||
|
||||
/* exported canRequestBody */ // Used in pdfHandler.js
|
||||
|
||||
// Remembers the request headers for every http(s) page request for the duration
|
||||
// of the request.
|
||||
var g_requestHeaders = {};
|
||||
// g_referrers[tabId][frameId] = referrer of PDF frame.
|
||||
var g_referrers = {};
|
||||
var g_referrerTimers = {};
|
||||
// The background script will eventually suspend after 30 seconds of inactivity.
|
||||
// This can be delayed when extension events are firing. To prevent the data
|
||||
// from being kept in memory for too long, cap the data duration to 5 minutes.
|
||||
var REFERRER_IN_MEMORY_TIME = 300000;
|
||||
|
||||
// g_postRequests[tabId] = Set of frameId that were loaded via POST.
|
||||
var g_postRequests = {};
|
||||
|
||||
var rIsReferer = /^referer$/i;
|
||||
chrome.webRequest.onSendHeaders.addListener(
|
||||
function saveReferer(details) {
|
||||
const { tabId, frameId, requestHeaders, method } = details;
|
||||
g_referrers[tabId] ??= {};
|
||||
g_referrers[tabId][frameId] = requestHeaders.find(h =>
|
||||
rIsReferer.test(h.name)
|
||||
)?.value;
|
||||
setCanRequestBody(tabId, frameId, method !== "GET");
|
||||
forgetReferrerEventually(tabId);
|
||||
},
|
||||
{ urls: ["*://*/*"], types: ["main_frame", "sub_frame"] },
|
||||
["requestHeaders", "extraHeaders"]
|
||||
);
|
||||
|
||||
function forgetReferrerEventually(tabId) {
|
||||
if (g_referrerTimers[tabId]) {
|
||||
clearTimeout(g_referrerTimers[tabId]);
|
||||
(function () {
|
||||
var requestFilter = {
|
||||
urls: ["*://*/*"],
|
||||
types: ["main_frame", "sub_frame"],
|
||||
};
|
||||
chrome.webRequest.onSendHeaders.addListener(
|
||||
function (details) {
|
||||
g_requestHeaders[details.requestId] = details.requestHeaders;
|
||||
},
|
||||
requestFilter,
|
||||
["requestHeaders", "extraHeaders"]
|
||||
);
|
||||
chrome.webRequest.onBeforeRedirect.addListener(forgetHeaders, requestFilter);
|
||||
chrome.webRequest.onCompleted.addListener(forgetHeaders, requestFilter);
|
||||
chrome.webRequest.onErrorOccurred.addListener(forgetHeaders, requestFilter);
|
||||
function forgetHeaders(details) {
|
||||
delete g_requestHeaders[details.requestId];
|
||||
}
|
||||
g_referrerTimers[tabId] = setTimeout(() => {
|
||||
delete g_referrers[tabId];
|
||||
delete g_referrerTimers[tabId];
|
||||
delete g_postRequests[tabId];
|
||||
}, REFERRER_IN_MEMORY_TIME);
|
||||
}
|
||||
})();
|
||||
|
||||
// Keeps track of whether a document in tabId + frameId is loaded through a
|
||||
// POST form submission. Although this logic has nothing to do with referrer
|
||||
// tracking, it is still here to enable re-use of the webRequest listener above.
|
||||
function setCanRequestBody(tabId, frameId, isPOST) {
|
||||
if (isPOST) {
|
||||
g_postRequests[tabId] ??= new Set();
|
||||
g_postRequests[tabId].add(frameId);
|
||||
} else {
|
||||
g_postRequests[tabId]?.delete(frameId);
|
||||
/**
|
||||
* @param {object} details - onHeadersReceived event data.
|
||||
*/
|
||||
function saveReferer(details) {
|
||||
var referer =
|
||||
g_requestHeaders[details.requestId] &&
|
||||
getHeaderFromHeaders(g_requestHeaders[details.requestId], "referer");
|
||||
referer = (referer && referer.value) || "";
|
||||
if (!g_referrers[details.tabId]) {
|
||||
g_referrers[details.tabId] = {};
|
||||
}
|
||||
g_referrers[details.tabId][details.frameId] = referer;
|
||||
}
|
||||
|
||||
function canRequestBody(tabId, frameId) {
|
||||
// Returns true unless the frame is known to be loaded through a POST request.
|
||||
// If the background suspends, the information may be lost. This is acceptable
|
||||
// because the information is only potentially needed shortly after document
|
||||
// load, by contentscript.js.
|
||||
return !g_postRequests[tabId]?.has(frameId);
|
||||
}
|
||||
chrome.tabs.onRemoved.addListener(function (tabId) {
|
||||
delete g_referrers[tabId];
|
||||
});
|
||||
|
||||
// This method binds a webRequest event handler which adds the Referer header
|
||||
// to matching PDF resource requests (only if the Referer is non-empty). The
|
||||
@ -96,13 +87,15 @@ chrome.runtime.onConnect.addListener(function onReceivePort(port) {
|
||||
if (port.name !== "chromecom-referrer") {
|
||||
return;
|
||||
}
|
||||
// Note: sender.frameId is only set in Chrome 41+.
|
||||
if (!("frameId" in port.sender)) {
|
||||
port.disconnect();
|
||||
return;
|
||||
}
|
||||
var tabId = port.sender.tab.id;
|
||||
var frameId = port.sender.frameId;
|
||||
var dnrRequestId;
|
||||
|
||||
// If the PDF is viewed for the first time, then the referer will be set here.
|
||||
// Note: g_referrers could be empty if the background script was suspended by
|
||||
// the browser. In that case, chromecom.js may send us the referer (below).
|
||||
var referer = (g_referrers[tabId] && g_referrers[tabId][frameId]) || "";
|
||||
port.onMessage.addListener(function (data) {
|
||||
// If the viewer was opened directly (without opening a PDF URL first), then
|
||||
@ -111,49 +104,80 @@ chrome.runtime.onConnect.addListener(function onReceivePort(port) {
|
||||
if (data.referer) {
|
||||
referer = data.referer;
|
||||
}
|
||||
dnrRequestId = data.dnrRequestId;
|
||||
setStickyReferrer(dnrRequestId, tabId, data.requestUrl, referer, () => {
|
||||
// Acknowledge the message, and include the latest referer for this frame.
|
||||
port.postMessage(referer);
|
||||
});
|
||||
chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
|
||||
if (referer) {
|
||||
// Only add a blocking request handler if the referer has to be rewritten.
|
||||
chrome.webRequest.onBeforeSendHeaders.addListener(
|
||||
onBeforeSendHeaders,
|
||||
{
|
||||
urls: [data.requestUrl],
|
||||
types: ["xmlhttprequest"],
|
||||
tabId,
|
||||
},
|
||||
["blocking", "requestHeaders", "extraHeaders"]
|
||||
);
|
||||
}
|
||||
// Acknowledge the message, and include the latest referer for this frame.
|
||||
port.postMessage(referer);
|
||||
});
|
||||
|
||||
// The port is only disconnected when the other end reloads.
|
||||
port.onDisconnect.addListener(function () {
|
||||
unsetStickyReferrer(dnrRequestId);
|
||||
if (g_referrers[tabId]) {
|
||||
delete g_referrers[tabId][frameId];
|
||||
}
|
||||
chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
|
||||
chrome.webRequest.onHeadersReceived.removeListener(exposeOnHeadersReceived);
|
||||
});
|
||||
});
|
||||
|
||||
function setStickyReferrer(dnrRequestId, tabId, url, referer, callback) {
|
||||
if (!referer) {
|
||||
unsetStickyReferrer(dnrRequestId);
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
const rule = {
|
||||
id: dnrRequestId,
|
||||
condition: {
|
||||
urlFilter: `|${url}|`,
|
||||
// The viewer and background are presumed to have the same origin:
|
||||
initiatorDomains: [location.hostname], // = chrome.runtime.id.
|
||||
resourceTypes: ["xmlhttprequest"],
|
||||
tabIds: [tabId],
|
||||
// Expose some response headers for fetch API calls from PDF.js;
|
||||
// This is a work-around for https://crbug.com/784528
|
||||
chrome.webRequest.onHeadersReceived.addListener(
|
||||
exposeOnHeadersReceived,
|
||||
{
|
||||
urls: ["https://*/*"],
|
||||
types: ["xmlhttprequest"],
|
||||
tabId,
|
||||
},
|
||||
action: {
|
||||
type: "modifyHeaders",
|
||||
requestHeaders: [{ operation: "set", header: "referer", value: referer }],
|
||||
},
|
||||
};
|
||||
chrome.declarativeNetRequest.updateSessionRules(
|
||||
{ removeRuleIds: [dnrRequestId], addRules: [rule] },
|
||||
callback
|
||||
["blocking", "responseHeaders"]
|
||||
);
|
||||
}
|
||||
|
||||
function unsetStickyReferrer(dnrRequestId) {
|
||||
if (dnrRequestId) {
|
||||
chrome.declarativeNetRequest.updateSessionRules({
|
||||
removeRuleIds: [dnrRequestId],
|
||||
});
|
||||
function onBeforeSendHeaders(details) {
|
||||
if (details.frameId !== frameId) {
|
||||
return undefined;
|
||||
}
|
||||
var headers = details.requestHeaders;
|
||||
var refererHeader = getHeaderFromHeaders(headers, "referer");
|
||||
if (!refererHeader) {
|
||||
refererHeader = { name: "Referer" };
|
||||
headers.push(refererHeader);
|
||||
} else if (
|
||||
refererHeader.value &&
|
||||
refererHeader.value.lastIndexOf("chrome-extension:", 0) !== 0
|
||||
) {
|
||||
// Sanity check. If the referer is set, and the value is not the URL of
|
||||
// this extension, then the request was not initiated by this extension.
|
||||
return undefined;
|
||||
}
|
||||
refererHeader.value = referer;
|
||||
return { requestHeaders: headers };
|
||||
}
|
||||
}
|
||||
|
||||
function exposeOnHeadersReceived(details) {
|
||||
if (details.frameId !== frameId) {
|
||||
return undefined;
|
||||
}
|
||||
var headers = details.responseHeaders;
|
||||
var aceh = getHeaderFromHeaders(headers, "access-control-expose-headers");
|
||||
// List of headers that PDF.js uses in src/display/network_utils.js
|
||||
var acehValue =
|
||||
"accept-ranges,content-encoding,content-length,content-disposition";
|
||||
if (aceh) {
|
||||
aceh.value += "," + acehValue;
|
||||
} else {
|
||||
aceh = { name: "Access-Control-Expose-Headers", value: acehValue };
|
||||
headers.push(aceh);
|
||||
}
|
||||
return { responseHeaders: headers };
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright 2024 Mozilla Foundation
|
||||
<!doctype html>
|
||||
<!--
|
||||
Copyright 2015 Mozilla Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -12,15 +13,5 @@ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
importScripts(
|
||||
"options/migration.js",
|
||||
"preserve-referer.js",
|
||||
"pdfHandler.js",
|
||||
"extension-router.js",
|
||||
"suppress-update.js",
|
||||
"telemetry.js"
|
||||
);
|
||||
-->
|
||||
<script src="restoretab.js"></script>
|
||||
31
extensions/chromium/restoretab.js
Normal file
31
extensions/chromium/restoretab.js
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright 2015 Mozilla Foundation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is part of the work-around for crbug.com/511670.
|
||||
* - chromecom.js sets the URL and history state upon unload.
|
||||
* - extension-router.js retrieves the saved state and opens restoretab.html
|
||||
* - restoretab.html (this script) restores the URL and history state.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
var url = decodeURIComponent(location.search.slice(1));
|
||||
var historyState = decodeURIComponent(location.hash.slice(1));
|
||||
|
||||
historyState = historyState === "undefined" ? null : JSON.parse(historyState);
|
||||
|
||||
history.replaceState(historyState, null, url);
|
||||
location.reload();
|
||||
@ -20,10 +20,7 @@ limitations under the License.
|
||||
// viewer is not displaying any PDF files. Otherwise the tabs would close, which
|
||||
// is quite disruptive (crbug.com/511670).
|
||||
chrome.runtime.onUpdateAvailable.addListener(function () {
|
||||
chrome.tabs.query({ url: chrome.runtime.getURL("*") }, tabs => {
|
||||
if (tabs?.length) {
|
||||
return;
|
||||
}
|
||||
if (chrome.extension.getViews({ type: "tab" }).length === 0) {
|
||||
chrome.runtime.reload();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -42,35 +42,8 @@ limitations under the License.
|
||||
return;
|
||||
}
|
||||
|
||||
// The localStorage API is unavailable in service workers. We store data in
|
||||
// chrome.storage.local and use this "localStorage" object to enable
|
||||
// synchronous access in the logic.
|
||||
const localStorage = {
|
||||
telemetryLastTime: 0,
|
||||
telemetryDeduplicationId: "",
|
||||
telemetryLastVersion: "",
|
||||
};
|
||||
|
||||
chrome.alarms.onAlarm.addListener(alarm => {
|
||||
if (alarm.name === "maybeSendPing") {
|
||||
maybeSendPing();
|
||||
}
|
||||
});
|
||||
chrome.storage.session.get({ didPingCheck: false }, async items => {
|
||||
if (items?.didPingCheck) {
|
||||
return;
|
||||
}
|
||||
maybeSendPing();
|
||||
await chrome.alarms.clear("maybeSendPing");
|
||||
await chrome.alarms.create("maybeSendPing", { periodInMinutes: 60 });
|
||||
chrome.storage.session.set({ didPingCheck: true });
|
||||
});
|
||||
|
||||
function updateLocalStorage(key, value) {
|
||||
localStorage[key] = value;
|
||||
// Note: We mirror the data in localStorage because the following is async.
|
||||
chrome.storage.local.set({ [key]: value });
|
||||
}
|
||||
maybeSendPing();
|
||||
setInterval(maybeSendPing, 36e5);
|
||||
|
||||
function maybeSendPing() {
|
||||
getLoggingPref(function (didOptOut) {
|
||||
@ -88,20 +61,12 @@ limitations under the License.
|
||||
// send more pings.
|
||||
return;
|
||||
}
|
||||
doSendPing();
|
||||
});
|
||||
}
|
||||
|
||||
function doSendPing() {
|
||||
chrome.storage.local.get(localStorage, items => {
|
||||
Object.assign(localStorage, items);
|
||||
|
||||
var lastTime = parseInt(localStorage.telemetryLastTime) || 0;
|
||||
var wasUpdated = didUpdateSinceLastCheck();
|
||||
if (!wasUpdated && Date.now() - lastTime < MINIMUM_TIME_BETWEEN_PING) {
|
||||
return;
|
||||
}
|
||||
updateLocalStorage("telemetryLastTime", Date.now());
|
||||
localStorage.telemetryLastTime = Date.now();
|
||||
|
||||
var deduplication_id = getDeduplicationId(wasUpdated);
|
||||
var extension_version = chrome.runtime.getManifest().version;
|
||||
@ -139,7 +104,7 @@ limitations under the License.
|
||||
for (const c of buf) {
|
||||
id += (c >>> 4).toString(16) + (c & 0xf).toString(16);
|
||||
}
|
||||
updateLocalStorage("telemetryDeduplicationId", id);
|
||||
localStorage.telemetryDeduplicationId = id;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
@ -154,7 +119,7 @@ limitations under the License.
|
||||
if (!chromeVersion || localStorage.telemetryLastVersion === chromeVersion) {
|
||||
return false;
|
||||
}
|
||||
updateLocalStorage("telemetryLastVersion", chromeVersion);
|
||||
localStorage.telemetryLastVersion = chromeVersion;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
22
extensions/firefox/.eslintrc
Normal file
22
extensions/firefox/.eslintrc
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
// Note: The root .eslintrc file will define the base rules,
|
||||
// but mozilla/recommended will override them for the rules it sets. Finally,
|
||||
// the rules in this file will take precedence.
|
||||
"extends": [
|
||||
"plugin:mozilla/recommended",
|
||||
],
|
||||
|
||||
"plugins": [
|
||||
"mozilla"
|
||||
],
|
||||
|
||||
"rules": {
|
||||
// Other rules mozilla/recommended hasn't enabled yet.
|
||||
"no-shadow": "error",
|
||||
"arrow-body-style": ["error", "as-needed"],
|
||||
"arrow-parens": ["error", "always"],
|
||||
"constructor-super": "error",
|
||||
"no-confusing-arrow": "error",
|
||||
"no-useless-constructor": "error",
|
||||
},
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
/* Copyright 2024 Mozilla Foundation
|
||||
/* Copyright 2018 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -13,16 +13,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { InkDrawOutline } from "./inkdraw.js";
|
||||
|
||||
class ContourDrawOutline extends InkDrawOutline {
|
||||
toSVGPath() {
|
||||
let path = super.toSVGPath();
|
||||
if (!path.endsWith("Z")) {
|
||||
path += "Z";
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
export { ContourDrawOutline };
|
||||
export const PdfJsDefaultPreferences = Object.freeze(
|
||||
PDFJSDev.eval("DEFAULT_PREFERENCES")
|
||||
);
|
||||
9
external/.eslintrc
vendored
Normal file
9
external/.eslintrc
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": [
|
||||
../.eslintrc
|
||||
],
|
||||
|
||||
"env": {
|
||||
"node": true,
|
||||
},
|
||||
}
|
||||
@ -1,7 +1,10 @@
|
||||
import { types as t, transformSync } from "@babel/core";
|
||||
import fs from "fs";
|
||||
import { join as joinPaths } from "path";
|
||||
import vm from "vm";
|
||||
|
||||
const PDFJS_PREPROCESSOR_NAME = "PDFJSDev";
|
||||
const ROOT_PREFIX = "$ROOT/";
|
||||
|
||||
function isPDFJSPreprocessor(obj) {
|
||||
return obj.type === "Identifier" && obj.name === PDFJS_PREPROCESSOR_NAME;
|
||||
@ -37,6 +40,18 @@ function handlePreprocessorAction(ctx, actionName, args, path) {
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
case "json":
|
||||
if (!t.isStringLiteral(arg)) {
|
||||
throw new Error("Path to JSON is not provided");
|
||||
}
|
||||
let jsonPath = arg.value;
|
||||
if (jsonPath.startsWith(ROOT_PREFIX)) {
|
||||
jsonPath = joinPaths(
|
||||
ctx.rootPath,
|
||||
jsonPath.substring(ROOT_PREFIX.length)
|
||||
);
|
||||
}
|
||||
return JSON.parse(fs.readFileSync(jsonPath, "utf8"));
|
||||
}
|
||||
throw new Error("Unsupported action");
|
||||
} catch (e) {
|
||||
@ -169,7 +184,7 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
|
||||
path.replaceWith(t.importExpression(source));
|
||||
}
|
||||
},
|
||||
"BlockStatement|StaticBlock": {
|
||||
BlockStatement: {
|
||||
// Visit node in post-order so that recursive flattening
|
||||
// of blocks works correctly.
|
||||
exit(path) {
|
||||
@ -200,10 +215,6 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
|
||||
}
|
||||
subExpressionIndex++;
|
||||
}
|
||||
|
||||
if (node.type === "StaticBlock" && node.body.length === 0) {
|
||||
path.remove();
|
||||
}
|
||||
},
|
||||
},
|
||||
Function: {
|
||||
@ -223,26 +234,6 @@ function babelPluginPDFJSPreprocessor(babel, ctx) {
|
||||
}
|
||||
},
|
||||
},
|
||||
ClassMethod: {
|
||||
exit(path) {
|
||||
const {
|
||||
node,
|
||||
parentPath: { parent: classNode },
|
||||
} = path;
|
||||
if (
|
||||
// Remove empty constructors. We only do this for
|
||||
// base classes, as the default constructor of derived
|
||||
// classes is not empty (and an empty constructor
|
||||
// must throw at runtime when constructed).
|
||||
node.kind === "constructor" &&
|
||||
node.body.body.length === 0 &&
|
||||
node.params.every(p => p.type === "Identifier") &&
|
||||
!classNode.superClass
|
||||
) {
|
||||
path.remove();
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
class A {
|
||||
constructor() {
|
||||
console.log("Hi!");
|
||||
}
|
||||
}
|
||||
class B {
|
||||
constructor(x = console.log("Hi!")) {}
|
||||
}
|
||||
class C {
|
||||
constructor({
|
||||
x
|
||||
}) {}
|
||||
}
|
||||
class D {}
|
||||
class E extends A {
|
||||
constructor() {}
|
||||
}
|
||||
class F {
|
||||
constructor() {
|
||||
var a = 0;
|
||||
}
|
||||
}
|
||||
class G {}
|
||||
37
external/builder/fixtures_babel/constructors.js
vendored
37
external/builder/fixtures_babel/constructors.js
vendored
@ -1,37 +0,0 @@
|
||||
class A {
|
||||
constructor() {
|
||||
console.log("Hi!");
|
||||
}
|
||||
}
|
||||
|
||||
class B {
|
||||
constructor(x = console.log("Hi!")) {}
|
||||
}
|
||||
|
||||
class C {
|
||||
constructor({ x }) {}
|
||||
}
|
||||
|
||||
class D {
|
||||
constructor(x, y, z) {}
|
||||
}
|
||||
|
||||
class E extends A {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
class F {
|
||||
constructor() {
|
||||
if (PDFJSDev.test('TRUE')) {
|
||||
var a = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class G {
|
||||
constructor() {
|
||||
if (PDFJSDev.test('FALSE')) {
|
||||
var a = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
class A {
|
||||
static {
|
||||
foo();
|
||||
}
|
||||
static {
|
||||
var a = 0;
|
||||
}
|
||||
}
|
||||
20
external/builder/fixtures_babel/staticblock.js
vendored
20
external/builder/fixtures_babel/staticblock.js
vendored
@ -1,20 +0,0 @@
|
||||
class A {
|
||||
static {}
|
||||
static {
|
||||
{ foo() }
|
||||
}
|
||||
static {
|
||||
{;}
|
||||
}
|
||||
static {
|
||||
if (PDFJSDev.test('TRUE')) {
|
||||
var a = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
if (PDFJSDev.test('FALSE')) {
|
||||
var a = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,9 @@ var g = {
|
||||
},
|
||||
j: 2
|
||||
};
|
||||
var h = {
|
||||
test: "test"
|
||||
};
|
||||
var i = '0';
|
||||
var j = {
|
||||
i: 1
|
||||
@ -5,6 +5,7 @@ var d = PDFJSDev.test('FALSE');
|
||||
var e = PDFJSDev.eval('TRUE');
|
||||
var f = PDFJSDev.eval('TEXT');
|
||||
var g = PDFJSDev.eval('OBJ');
|
||||
var h = PDFJSDev.json('$ROOT/external/builder/fixtures_esprima/evals.json');
|
||||
var i = typeof PDFJSDev === 'undefined' ? PDFJSDev.eval('FALSE') : '0';
|
||||
var j = typeof PDFJSDev !== 'undefined' ? PDFJSDev.eval('OBJ.obj') : '0';
|
||||
var k = !PDFJSDev.test('TRUE');
|
||||
1
external/builder/fixtures_esprima/evals.json
vendored
Normal file
1
external/builder/fixtures_esprima/evals.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{ "test": "test" }
|
||||
@ -1,4 +1,4 @@
|
||||
import { Test } from "import-name";
|
||||
import { Test2 } from './non-alias';
|
||||
export { Test3 } from "import-name";
|
||||
await import(/*webpackIgnore: true*/"./non-alias");
|
||||
await import( /*webpackIgnore: true*/"./non-alias");
|
||||
@ -7,7 +7,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
let errors = 0;
|
||||
|
||||
const baseDir = path.join(__dirname, "fixtures_babel");
|
||||
const baseDir = path.join(__dirname, "fixtures_esprima");
|
||||
const files = fs
|
||||
.readdirSync(baseDir)
|
||||
.filter(function (name) {
|
||||
@ -49,7 +49,7 @@ files.forEach(function (expectationFilename) {
|
||||
errors++;
|
||||
|
||||
// Allow regenerating the expected output using
|
||||
// OVERWRITE=true node ./external/builder/test-fixtures_babel.mjs
|
||||
// OVERWRITE=true node ./external/builder/test-fixtures_esprima.mjs
|
||||
if (process.env.OVERWRITE) {
|
||||
fs.writeFileSync(expectationFilename, out + "\n");
|
||||
}
|
||||
7
external/importL10n/locales.mjs
vendored
7
external/importL10n/locales.mjs
vendored
@ -31,7 +31,7 @@ async function downloadLanguageCodes() {
|
||||
console.log("Downloading language codes...\n");
|
||||
|
||||
const ALL_LOCALES =
|
||||
"https://raw.githubusercontent.com/mozilla/gecko-dev/master/browser/locales/all-locales";
|
||||
"https://hg.mozilla.org/mozilla-central/raw-file/tip/browser/locales/all-locales";
|
||||
|
||||
const response = await fetch(ALL_LOCALES);
|
||||
if (!response.ok) {
|
||||
@ -52,9 +52,8 @@ async function downloadLanguageFiles(root, langCode) {
|
||||
|
||||
// Constants for constructing the URLs. Translations are taken from the
|
||||
// Nightly channel as those are the most recent ones.
|
||||
const MOZ_CENTRAL_ROOT =
|
||||
"https://raw.githubusercontent.com/mozilla-l10n/firefox-l10n/main/";
|
||||
const MOZ_CENTRAL_PDFJS_DIR = "/toolkit/toolkit/pdfviewer/";
|
||||
const MOZ_CENTRAL_ROOT = "https://hg.mozilla.org/l10n-central/";
|
||||
const MOZ_CENTRAL_PDFJS_DIR = "/raw-file/default/toolkit/toolkit/pdfviewer/";
|
||||
|
||||
// Defines which files to download for each language.
|
||||
const files = ["viewer.ftl"];
|
||||
|
||||
39
external/openjpeg/LICENSE_OPENJPEG
vendored
39
external/openjpeg/LICENSE_OPENJPEG
vendored
@ -1,39 +0,0 @@
|
||||
/*
|
||||
* The copyright in this software is being made available under the 2-clauses
|
||||
* BSD License, included below. This software may be subject to other third
|
||||
* party and contributor rights, including patent rights, and no such rights
|
||||
* are granted under this license.
|
||||
*
|
||||
* Copyright (c) 2002-2014, Universite catholique de Louvain (UCL), Belgium
|
||||
* Copyright (c) 2002-2014, Professor Benoit Macq
|
||||
* Copyright (c) 2003-2014, Antonin Descampe
|
||||
* Copyright (c) 2003-2009, Francois-Olivier Devaux
|
||||
* Copyright (c) 2005, Herve Drolon, FreeImage Team
|
||||
* Copyright (c) 2002-2003, Yannick Verschueren
|
||||
* Copyright (c) 2001-2003, David Janssens
|
||||
* Copyright (c) 2011-2012, Centre National d'Etudes Spatiales (CNES), France
|
||||
* Copyright (c) 2012, CS Systemes d'Information, France
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `AS IS'
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
22
external/openjpeg/LICENSE_PDFJS_OPENJPEG
vendored
22
external/openjpeg/LICENSE_PDFJS_OPENJPEG
vendored
@ -1,22 +0,0 @@
|
||||
Copyright (c) 2024, Mozilla Foundation
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
2
external/openjpeg/README.md
vendored
2
external/openjpeg/README.md
vendored
@ -9,4 +9,4 @@ In order to generate the file `openjpeg.js`:
|
||||
## Licensing
|
||||
|
||||
[OpenJPEG](https://www.openjpeg.org/) is under [BSD 2-clause "Simplified" License](https://github.com/uclouvain/openjpeg/blob/master/LICENSE)
|
||||
and [pdf.js.openjpeg](https://github.com/mozilla/pdf.js.openjpeg/) is released under [BSD 2-clause](https://github.com/mozilla/pdf.js.openjpeg/blob/main/LICENSE) license so `openjpeg.js` is released under [BSD 2-clause](https://github.com/mozilla/pdf.js.openjpeg/blob/main/LICENSE) license too.
|
||||
and [pdf.js.openjpeg](https://github.com/mozilla/pdf.js.openjpeg/) is released under [Apache 2](https://github.com/mozilla/pdf.js.openjpeg/blob/main/LICENSE) license so `openjpeg.js` is released under [Apache 2](https://github.com/mozilla/pdf.js.openjpeg/blob/main/LICENSE) license too.
|
||||
|
||||
13
external/openjpeg/openjpeg.js
vendored
13
external/openjpeg/openjpeg.js
vendored
File diff suppressed because one or more lines are too long
BIN
external/openjpeg/openjpeg.wasm
vendored
BIN
external/openjpeg/openjpeg.wasm
vendored
Binary file not shown.
30
external/openjpeg/openjpeg_nowasm_fallback.js
vendored
30
external/openjpeg/openjpeg_nowasm_fallback.js
vendored
File diff suppressed because one or more lines are too long
840
gulpfile.mjs
840
gulpfile.mjs
File diff suppressed because it is too large
Load Diff
13
jsdoc.json
13
jsdoc.json
@ -1,13 +0,0 @@
|
||||
{
|
||||
"source": {
|
||||
"include": ["src/display/api.js"]
|
||||
},
|
||||
"opts": {
|
||||
"destination": "build/jsdoc/"
|
||||
},
|
||||
"templates": {
|
||||
"default": {
|
||||
"includeDate": false
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user