From 9705169371ec9024c3396c7577f6eb87b3b755de Mon Sep 17 00:00:00 2001 From: Rob Wu Date: Mon, 9 Sep 2024 14:26:26 +0200 Subject: [PATCH] [CRX] Detect availability of DNR responseHeaders before use Critical fix for old but recent Chrome versions; all requests are otherwise redirected to the viewer. --- extensions/chromium/pdfHandler.js | 57 ++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/extensions/chromium/pdfHandler.js b/extensions/chromium/pdfHandler.js index 0fd71e9f1f4be..bd440a923dd69 100644 --- a/extensions/chromium/pdfHandler.js +++ b/extensions/chromium/pdfHandler.js @@ -198,19 +198,68 @@ async function registerPdfRedirectRule() { rule.priority = addRules.length - i; } try { - await chrome.declarativeNetRequest.updateDynamicRules({ addRules }); // 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 do not bother with detecting that because we fall back to catching - // PDF documents via maybeRenderPdfDoc in contentscript.js. + // 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, + urlFilter: "|does_not_match_anything", + condition: { responseHeaders: [{ header: "whatever" }] }, + 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, + urlFilter: "|does_not_match_anything", + condition: { responseHeaders: [] }, + action: { type: "block" }, + }, + ], + }); + return false; // Validation skipped = feature disabled. + } catch { + return true; // Validation worked = feature enabled. + } finally { + await chrome.declarativeNetRequest.updateSessionRules({ + removeRuleIds: [ruleId], + }); + } +} + function getViewerURL(pdf_url) { // |pdf_url| may contain a fragment such as "#page=2". That should be passed // as a fragment to the viewer, not encoded in pdf_url.