diff --git a/extensions/chromium/.eslintrc b/extensions/chromium/.eslintrc
index ba6fdbd4bf4ce..dd74b7b7c64ab 100644
--- a/extensions/chromium/.eslintrc
+++ b/extensions/chromium/.eslintrc
@@ -14,4 +14,23 @@
"rules": {
"no-var": "off",
},
+
+ "overrides": [
+ {
+ // Include all files referenced in background.js
+ "files": [
+ "options/migration.js",
+ "preserve-referer.js",
+ "pdfHandler.js",
+ "extension-router.js",
+ "suppress-update.js",
+ "telemetry.js"
+ ],
+ "env": {
+ // Background script is a service worker.
+ "browser": false,
+ "serviceworker": true
+ }
+ }
+ ]
}
diff --git a/extensions/chromium/restoretab.html b/extensions/chromium/background.js
similarity index 71%
rename from extensions/chromium/restoretab.html
rename to extensions/chromium/background.js
index 6e6fa425dd559..bb448be549c0c 100644
--- a/extensions/chromium/restoretab.html
+++ b/extensions/chromium/background.js
@@ -1,6 +1,5 @@
-
-
-
+*/
+
+"use strict";
+
+importScripts(
+ "options/migration.js",
+ "preserve-referer.js",
+ "pdfHandler.js",
+ "extension-router.js",
+ "suppress-update.js",
+ "telemetry.js"
+);
diff --git a/extensions/chromium/contentscript.js b/extensions/chromium/contentscript.js
index aa6edb7b3f3aa..83ebf96a225c7 100644
--- a/extensions/chromium/contentscript.js
+++ b/extensions/chromium/contentscript.js
@@ -16,13 +16,16 @@ limitations under the License.
"use strict";
-var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html");
+var VIEWER_URL = chrome.runtime.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") {
@@ -221,3 +224,38 @@ 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);
+}
diff --git a/extensions/chromium/extension-router.js b/extensions/chromium/extension-router.js
index a128a30d111e0..5bbb0d1a580c7 100644
--- a/extensions/chromium/extension-router.js
+++ b/extensions/chromium/extension-router.js
@@ -17,8 +17,8 @@ limitations under the License.
"use strict";
(function ExtensionRouterClosure() {
- var VIEWER_URL = chrome.extension.getURL("content/web/viewer.html");
- var CRX_BASE_URL = chrome.extension.getURL("/");
+ var VIEWER_URL = chrome.runtime.getURL("content/web/viewer.html");
+ var CRX_BASE_URL = chrome.runtime.getURL("/");
var schemes = [
"http",
@@ -55,73 +55,50 @@ limitations under the License.
return undefined;
}
- // 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) {
+ function resolveViewerURL(originalUrl) {
+ if (originalUrl.startsWith(CRX_BASE_URL)) {
// This listener converts chrome-extension://.../http://...pdf to
// chrome-extension://.../content/web/viewer.html?file=http%3A%2F%2F...pdf
- var url = parseExtensionURL(details.url);
+ var url = parseExtensionURL(originalUrl);
if (url) {
url = VIEWER_URL + "?file=" + url;
- var i = details.url.indexOf("#");
+ var i = originalUrl.indexOf("#");
if (i > 0) {
- url += details.url.slice(i);
+ url += originalUrl.slice(i);
}
- console.log("Redirecting " + details.url + " to " + url);
- return { redirectUrl: url };
- }
- return undefined;
- },
- {
- types: ["main_frame", "sub_frame"],
- urls: schemes.map(function (scheme) {
- // Format: "chrome-extension://[EXTENSIONID]/*"
- 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);
+ return url;
}
}
- );
- console.log("Set up extension URL router.");
+ return undefined;
+ }
- 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,
- });
+ 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));
}
- localStorage.removeItem(key);
}
});
+
+ // 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 });
+ }
+ },
+ { url: [{ urlPrefix: CRX_BASE_URL }] }
+ );
+
+ console.log("Set up extension URL router.");
})();
diff --git a/extensions/chromium/manifest.json b/extensions/chromium/manifest.json
index 77c39c7770226..27be0ea96ac9f 100644
--- a/extensions/chromium/manifest.json
+++ b/extensions/chromium/manifest.json
@@ -1,6 +1,6 @@
{
- "minimum_chrome_version": "88",
- "manifest_version": 2,
+ "minimum_chrome_version": "103",
+ "manifest_version": 3,
"name": "PDF Viewer",
"version": "PDFJSSCRIPT_VERSION",
"description": "Uses HTML5 to display PDF files directly in the browser.",
@@ -10,13 +10,14 @@
"16": "icon16.png"
},
"permissions": [
+ "alarms",
+ "declarativeNetRequestWithHostAccess",
"webRequest",
- "webRequestBlocking",
- "",
"tabs",
"webNavigation",
"storage"
],
+ "host_permissions": [""],
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*", "file://*/*"],
@@ -30,23 +31,28 @@
"managed_schema": "preferences_schema.json"
},
"options_ui": {
- "page": "options/options.html",
- "chrome_style": true
+ "page": "options/options.html"
},
"options_page": "options/options.html",
"background": {
- "page": "pdfHandler.html"
+ "service_worker": "background.js"
},
"incognito": "split",
"web_accessible_resources": [
- "content/web/viewer.html",
- "http:/*",
- "https:/*",
- "file:/*",
- "chrome-extension:/*",
- "blob:*",
- "data:*",
- "filesystem:/*",
- "drive:*"
+ {
+ "resources": [
+ "content/web/viewer.html",
+ "http:/*",
+ "https:/*",
+ "file:/*",
+ "chrome-extension:/*",
+ "blob:*",
+ "data:*",
+ "filesystem:/*",
+ "drive:*"
+ ],
+ "matches": [""],
+ "extension_ids": ["*"]
+ }
]
}
diff --git a/extensions/chromium/options/migration.js b/extensions/chromium/options/migration.js
index dd8fb6ef73d1a..9b084e45ae3f9 100644
--- a/extensions/chromium/options/migration.js
+++ b/extensions/chromium/options/migration.js
@@ -13,10 +13,14 @@ 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"] */
+"use strict";
-(function () {
- "use strict";
+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;
+ }
var storageLocal = chrome.storage.local;
var storageSync = chrome.storage.sync;
@@ -37,16 +41,12 @@ limitations under the License.
});
});
- function getStorageNames(callback) {
- var x = new XMLHttpRequest();
+ async function getStorageNames(callback) {
var schema_location = chrome.runtime.getManifest().storage.managed_schema;
- 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();
+ var res = await fetch(chrome.runtime.getURL(schema_location));
+ var storageManifest = await res.json();
+ var storageKeys = Object.keys(storageManifest.properties);
+ callback(storageKeys);
}
// Save |values| to storage.sync and delete the values with that key from
@@ -150,4 +150,4 @@ limitations under the License.
}
);
}
-})();
+});
diff --git a/extensions/chromium/options/options.html b/extensions/chromium/options/options.html
index 78385926fc98f..bd18c2456465f 100644
--- a/extensions/chromium/options/options.html
+++ b/extensions/chromium/options/options.html
@@ -19,13 +19,19 @@
PDF.js viewer options
@@ -34,8 +40,7 @@
-
-
+