From c967b88d9ce2d136f185d20bbf80fa4f697e4f80 Mon Sep 17 00:00:00 2001 From: Daniel Jacobs Date: Thu, 20 Jun 2024 16:45:35 -0400 Subject: [PATCH] extension: Use feature-detection instead of user-agen sniffing --- web/packages/extension/src/background.ts | 78 ++++++++++++++++++------ 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/web/packages/extension/src/background.ts b/web/packages/extension/src/background.ts index eee014ca2a870..d1c3bb2ac1d1b 100644 --- a/web/packages/extension/src/background.ts +++ b/web/packages/extension/src/background.ts @@ -8,16 +8,61 @@ async function contentScriptRegistered() { return matchingScripts?.length > 0; } +async function isHeaderConditionSupported() { + let needCleanup = false; + const ruleId = 4; + try { + // Throws synchronously if not supported. + await utils.declarativeNetRequest.updateDynamicRules({ + addRules: [ + { + id: ruleId, + condition: { responseHeaders: [{ header: "whatever" }] }, + action: { + type: + chrome.declarativeNetRequest.RuleActionType + ?.BLOCK ?? "block", + }, + }, + ], + }); + needCleanup = true; + } 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 utils.declarativeNetRequest.updateDynamicRules({ + removeRuleIds: [ruleId], + addRules: [ + { + id: ruleId, + condition: { responseHeaders: [] }, + action: { + type: + chrome.declarativeNetRequest.RuleActionType + ?.BLOCK ?? "block", + }, + }, + ], + }); + needCleanup = true; + return false; // Validation skipped = feature disabled. + } catch { + return true; // Validation worked = feature enabled. + } finally { + if (needCleanup) { + await utils.declarativeNetRequest.updateDynamicRules({ + removeRuleIds: [ruleId], + }); + } + } +} + async function enable() { - // Unlike other browsers, Chrome registers rules with an unsupported condition. - // Therefore, check that Chrome is a version that supports the responseHeaders condition, - // so the browser in use throws an exception or supports the responseHeaders condition. - // The alternative check here is to use feature detection to detect support for something - // that strictly greater Chrome versions support, but no such features exist yet. - const chrome127OrLess = /Chrome\/([0-9]|1[0-9]|1[0-1][0-9]|12[0-7])\./.test( - navigator.userAgent, - ); - if (utils.declarativeNetRequest && !chrome127OrLess) { + // Checks if the responseHeaders condition is supported and not behind a disabled flag. + if (utils.declarativeNetRequest && (await isHeaderConditionSupported())) { const playerPage = utils.runtime.getURL("/player.html"); const rules = [ { @@ -63,7 +108,6 @@ async function enable() { values: [ "application/octet-stream", "application/binary-stream", - "", ], }, ], @@ -91,16 +135,10 @@ async function enable() { }, }, ]; - try { - await utils.declarativeNetRequest.updateDynamicRules({ - removeRuleIds: [1, 2, 3], - addRules: rules, - }); - } catch (e) { - console.info( - "Failed to register rules: responseHeaders condition unsupported", - ); - } + await utils.declarativeNetRequest.updateDynamicRules({ + removeRuleIds: [1, 2, 3], + addRules: rules, + }); } if ( !utils.scripting ||