After the removal of 'unsafe-eval' CSP in #18651, WebAssembly fails to
load, resulting in issues such as seen in #18457.
Manifest Version 3 does not allow 'unsafe-eval', does accept the more
specific 'wasm-unsafe-eval' as of Chrome 103. Note that manifest.json
already sets minimum_chrome_version to 103.
This patch also adds `object-src 'self'` because it was required until
Chrome 110. As of Chrome 111, the default is `object-src 'self'` and
`object-src` is no longer required. We could drop `object-src` in the
future, but for now we need to include it to support Chrome 103 - 110.
The minimum required version is Chrome 103 because wildcard support for
web_accessible_resources[].extension_id was introduced in 103, in
c9caeb1a08
A way to broaden compatibility is to drop that key. By doing so, the
minimum required Chrome version is then 96, because of the
chrome.storage.session API (and declarativeNetRequestWithHostAccess).
Since gulpfile.js already defines "Chrome >= 98" and there is no real
point in expanding support to older versions, I'm just setting the
minimum version to 103.
- Replace DOM-based pdfHandler.html (background page) with background.js
(extension service worker).
- Adjust logic of background scripts to account for the fact that the
scripts can execute repeatedly during a browser session. Primarily,
register relevant extension event handlers at the top level and use
in-memory storage.session API to keep track of initialization state.
- Extension URL router: replace blocking webRequest with the service
worker-specific "fetch" event.
- PDF detection: replace blocking webRequest with declarativeNetRequest.
This requires Chrome 128+. The next commit will add a fallback for
earlier Chrome versions.
In MV3, the background script is a service worker. localStorage is not
supported in service workers. Switch to storage.local instead.
Data migration is not implemented because it is not needed due to the
privacy-friendly design of the telemetry: In practice the data is reset
about every 4 weeks, when the major version of Chrome is updated.
MV3 does not support chrome_style in options_ui. Remove it and replace
it with the minimal amount of styles that still has some spacing around
the individual settings for readability.
webRequestBlocking is unavailable in MV3. Non-blocking webRequest can
still be used to detect the Referer, but we have to use
declarativeNetRequest to change the Referer header as needed.
In preparation for migrating the Chrome extension to Manifest Version 3,
this patch removes parts of the manifest that are obsolete and/or
unsupported in MV3.
Remove ftp mentions: ftp was dropped from 6 years ago, in Chrome 59.
Remove file_browser_handlers: does not work on Chrome OS according to
https://github.com/mozilla/pdf.js/issues/14161 . MV3 replacement needs
a different manifest key and logic, which will be added later.
Remove content_security_policy: MV3 does not support unsafe-eval CSP,
and PDF.js does not require it. This may result in a mild performance
degradation in PDFs that contain PostScript.
Remove page_action logic: When this logic was originally introduced,
Chrome showed page action buttons in the address bar, which enabled
the extension to display the button on specific PDF viewer tabs only.
In Chrome 49 (2016), pageActions were dropped from the address bar and
put in an UI area that is hidden by default. The user can pin the button
to be unconditionally visible on the toolbar, which is distracting.
Because the UX is no longer serving the original needs, this patch
removes page_action from the manifest.
When a blob or data-URL is opened with the extension, viewer.html
rewrites the URL. But when the viewer is refreshed (e.g. F5), Chrome
would fail to display the viewer because the extension router was not
set up to recognize such URLs.
Now it is.
This was only ever useful for the Opera extension because the API
requires a whitelisted extension ID. Opera ditched PDF.js from their
extension gallery, so we don't need to keep this in the tree.
Use streamsPrivate API when available.
When the API is not available, the extension will still work on
on http/https/file URLs, but not for POST requests or FTP.
As of writing, the Chromium project has still not whitelisted
the PDF Viewer extension in the Chrome Web Store.
(extension ID oemmndcbldboiebfnladdacbdfmadadm)
Request to whitelist PDF.js in Chromium:
https://code.google.com/p/chromium/issues/detail?id=326949
Opera 19 has whitelisted the PDF Viewer extension from
https://addons.opera.com/extensions/details/pdf-viewer/
(extension ID encfpfilknmenlmjemepncnlbbjlabkc)
(https://github.com/Rob--W/pdf.js/issues/1#issuecomment-32357302)
If you want to test the streamsPrivate feature in Chrome,
edit the build/extensions/manifest.json and add the "key" again
(see this commit for the value of this "key" field).
When a new incognito session is started, the onExecuteMimeTypeHandler event is
often not dispatched in time. Instead, it's triggered in the non-incognito profile.
This commit offers a work-around that allows new incognito instances to view PDF files.
This is needed for propagating the extension's permissions
to the extension's iframe, in the rare event that the PDF is
loaded in a sub frame, and the extension does not have access to the
top frame. For instance, when a http:-PDF file is embedded in a
local file, while "Allow access to local URLs" is disabled.
Note: Propagating permissions by inserting content scripts is an
undocumented feature (http://crbug.com/302548).
Whenever it breaks, the issue (cross-domain permissions for XHR)
can be solved by using a content script that gets the blob using
the XMLHttpRequest API, followed by `postMessage` (via transferables)
to efficiently pass the arraybuffer back to the PDF Viewer.
This method captures all application/pdf streams, loads the viewer
and passes the stream to the PDF.js viewer.
This commit shows a proof of concept using the chrome.streamsPrivate API.
Advantages of new method:
- Access to the response body of the original request, thus fewer
network requests.
- PDFs from non-GET requests (e.g. POST) are now supported.
- FTP files are also supported.
Possible improvements:
- Use declared content scripts instead of dynamic chrome.tabs.executeScript.
This allows the extension to render the viewer in frames when the
extension is disallowed to run executeScript for the top URL.
- Use chrome.declarativeWebRequest instead of webRequest, and replace
background page with event page (don't forget to profile the
difference & will the background/event page still work as intended?).
In Chromium extensions, the viewer's URL looks like this:
chrome-extension://oemmndcbldboiebfnladdacbdfmadadm/http://example.com/file.pdf
Furthermore, the PDF Viewer itself can also add something to the reference fragment:
chrome-extension://oemmndcbldboiebfnladdacbdfmadadm/http://example.com/file.pdf#page=2
Consequently, it is difficult to copy a clean URL (e.g. for sharing over mail)
without having to tidy-up the URL manually.
This commit solves this issue by adding a button to the omnibox,
which shows the clean PDF URL on click.
Before commit:
chrome-extension://EXTENSIONID/content/web/viewer.html?file=http%3A%2F%2Fexample.com%2Ffile.pdf
After commit:
chrome-extension://EXTENSIONID/http://example/file.pdf
Technical details:
- The extension's background page uses the webRequest API to intercept
requests for <extension host>/<real path to pdf>, and redirect it to
the viewer's URL.
- viewer.js uses history.replaceState to rewrite the URL, so that it's
easier for users to recognize and copy-paste URLs.
- The fake paths /http:, /https:, /file:, etc. have been added to the
web_accessible_resources section of the manifest file, in order to
avoid seeing chrome-extension://invalid/ instead of the actual URL
when using history back/forward to navigate from/to the PDF viewer.
- Since the relative path resolving doesn't work because relative URLs
are inaccurate, a <base> tag has been added. This method has already
been proven to work in the Firefox add-on.
Notes:
- This commit has been cherry-picked from crx-using-streams-api.
- Need to merge https://github.com/mozilla/pdf.js/pull/3582 to deal with
a bug in Chrome <=30
- In Chrome, getting the contents of a FTP file is not possible, so
there's no support for FTP files, even though the extension router
recognizes the ftp: scheme.
And use split incognito mode
Previous method:
- Rewrite content type to XHTML, followed by a content script
to cancel and replace the document with the viewer.
( https://github.com/mozilla/pdf.js/pull/3017 )
New method:
- Cancel loading of the document, followed by a redirect to the viewer
Disadvantage of new method:
- URLs are no longer "nice". This will be addressed by cherry-picking
a commit from the crx-using-streams-api branch.
Advantages of new method:
- Idle time is minimal. In some cases (with large documents),
it took too much time before the content script was activated.
During this period, the page looked blank, and the contents of
the PDF file were still retrieved and **discarded**.
With the new method, the idle time is minimal, because the request
is immediately cancelled.
- No FOUXEP (Flash of unhidden XML error page), because the XHTML
Content-Type hack is no longer used.