// Chrome 151 vs 152: paste into the DevTools console on any https:// page and press Enter.
// Each line prints "<change> <check> = <value>". Expected values: clearcotelabs.com/chrome-releases/152
(async () => {
const checks = [
["grease-brand", () => navigator.userAgentData.brands.map((b) => `${b.brand};v=${b.version}`).join(', ')],
["tls-trust-anchors", () => JSON.parse(document.querySelector('pre').textContent).tls.ja4],
["tls-trust-anchors", () => JSON.parse(document.querySelector('pre').textContent).tls.extensions.some((e) => e.name === 'Unknown extension 51764')],
["cpu-performance", () => 'cpuPerformance' in navigator],
["cpu-performance", () => typeof navigator.cpuPerformance],
["opaque-range", () => typeof OpaqueRange],
["opaque-range", () => Object.getPrototypeOf(Range.prototype).constructor.name],
["opaque-range", () => 'startContainer' in AbstractRange.prototype],
["opaque-range", () => typeof HTMLInputElement.prototype.createValueRange],
["permissions-policy-interface", () => typeof PermissionsPolicy],
["permissions-policy-interface", () => FeaturePolicy.name],
["permissions-policy-interface", () => Object.prototype.toString.call(document.featurePolicy)],
["window-chrome-configurable", () => Object.getOwnPropertyDescriptor(window, 'chrome').configurable],
["window-chrome-configurable", () => Object.getOwnPropertyNames(window).indexOf('chrome') < Object.getOwnPropertyNames(window).indexOf('WebAssembly')],
["privacy-sandbox-stubbed", () => (() => { try { return navigator.adAuctionComponents(1).length; } catch (e) { return e.name; } })()],
["privacy-sandbox-stubbed", () => document.browsingTopics().then(() => 'resolved', (e) => e.name)],
["privacy-sandbox-stubbed", () => navigator.protectedAudience.queryFeatureSupport('sellerNonce')],
["privacy-sandbox-stubbed", () => navigator.canLoadAdAuctionFencedFrame()],
["webaudio-rustfft", () => ((c) => { const o = c.createOscillator(); o.type = 'triangle'; o.frequency.value = 1e4; const k = c.createDynamicsCompressor(); k.threshold.value = -50; k.knee.value = 40; k.ratio.value = 12; k.attack.value = 0; k.release.value = 0.25; o.connect(k); k.connect(c.destination); o.start(0); return c.startRendering().then((b) => b.getChannelData(0).slice(4500, 5e3).reduce((s, x) => s + Math.abs(x), 0)); })(new OfflineAudioContext(1, 44100, 44100))],
["device-orientation-permission", () => typeof DeviceOrientationEvent.requestPermission],
["device-orientation-permission", () => typeof DeviceMotionEvent.requestPermission],
["window-drag", () => CSS.supports('window-drag', 'move')],
["window-drag", () => 'windowDrag' in document.body.style],
["html-truncated-markup-declaration", () => new DOMParser().parseFromString('a<!d>b', 'text/html').body.innerHTML],
["corner-shape-serialization", () => getComputedStyle(document.documentElement).cornerShape],
["console-context-dirxml", () => typeof console.context().dirxml],
["console-context-dirxml", () => typeof console.context().dirXml],
["v8-strict-builtin-closures", () => (() => { try { return typeof Proxy.revocable({}, {}).revoke.caller; } catch (e) { return e.name; } })()],
["v8-strict-builtin-closures", () => new Promise((r) => Promise.resolve().finally(function f() { r(typeof f.caller); }))],
["iir-filter-double-precision", () => (() => { const m = new Float32Array(1); new IIRFilterNode(new OfflineAudioContext(1, 1, 44100), { feedforward: [1], feedback: [1.1, -1.0999] }).getFrequencyResponse(new Float32Array(1), m, new Float32Array(1)); return m[0]; })()],
["devtools-proxy-prototype", () => (() => { let hit = false; console.log(Object.create(new Proxy({}, { ownKeys() { hit = true; return []; } }))); return hit; })()],
["property-order-152", () => Object.getOwnPropertyNames(window).indexOf('onpagereveal') < Object.getOwnPropertyNames(window).indexOf('onpageswap')],
["property-order-152", () => Object.getOwnPropertyNames(PointerEvent.prototype).indexOf('persistentDeviceId') < Object.getOwnPropertyNames(PointerEvent.prototype).indexOf('getPredictedEvents')],
["property-order-152", () => Object.getOwnPropertyNames(PerformanceNavigationTiming.prototype).indexOf('confidence') < Object.getOwnPropertyNames(PerformanceNavigationTiming.prototype).indexOf('toJSON')],
["property-order-152", () => Object.getOwnPropertyNames(PerformancePaintTiming.prototype)[0]],
["property-order-152", () => Object.getOwnPropertyNames(window).indexOf('PerformanceLongAnimationFrameTiming') < Object.getOwnPropertyNames(window).indexOf('Performance')],
["connection-allowlists", () => fetch('https://example.com/', { mode: 'no-cors' }).then(() => 'sent', (e) => e.name)],
["privacy-sandbox-request-options", () => (() => { let read = false; new Request('/', { get attributionReporting() { read = true; } }); return read; })()],
["privacy-sandbox-request-options", () => (() => { let read = false; new Request('/', { get browsingTopics() { read = true; } }); return read; })()],
["attribution-reporting-policy-removed", () => document.featurePolicy.features().includes('attribution-reporting')],
["reference-target", () => 'referenceTarget' in ShadowRoot.prototype],
["reference-target", () => 'shadowRootReferenceTarget' in HTMLTemplateElement.prototype],
["shadowroot-slot-assignment", () => 'shadowRootSlotAssignment' in HTMLTemplateElement.prototype],
["css-alpha-function", () => CSS.supports('color', 'alpha(from red / 50%)')],
["css-serialization-152", () => Object.assign(document.createElement('div').style, { backgroundSize: '10px' }).backgroundSize],
["css-serialization-152", () => Object.assign(document.createElement('div').style, { width: 'calc(1px / sibling-index())' }).width],
["svg-attribute-selector-case", () => document.createRange().createContextualFragment('<svg type=A>').firstChild.matches('[type=a]')],
["processing-instruction-case", () => document.createRange().createContextualFragment('<?Foo bar?>').firstChild.target],
["processing-instruction-case", () => document.createProcessingInstruction('x', 'A="1"').getAttribute('A')],
["sanitizer-processing-instructions", () => 'processingInstructions' in new Sanitizer().get()],
["sanitizer-processing-instructions", () => Document.parseHTML('a<!--x-->b').body.childNodes.length],
["url-data-scheme-case", () => new URL('DATA:,a' + String.fromCharCode(10) + 'b').href],
["raf-callback-ids", () => [requestAnimationFrame(() => {})].map((a) => { const m = document.createElement('marquee'); m.start(); m.stop(); return requestAnimationFrame(() => {}) - a; })[0]],
["typed-om-152", () => new Promise((r) => r(document.createElement('div').attributeStyleMap.set('opacity', CSS.percent(50)))).then(() => 'ok', (e) => e.name)],
["regexp-split-v-flag", () => (() => { const v = []; class R extends RegExp { exec() { v.push(this.lastIndex); return null; } } ('a' + String.fromCodePoint(128512) + 'b').split(new R('x', 'v')); return v.join(); })()],
["wasm-error-flag-names", () => (() => { try { new WebAssembly.Module(new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 3, 2, 1, 0, 10, 6, 1, 4, 0, 224, 0, 11])); return 'compiled'; } catch (e) { return e.message.slice(e.message.indexOf('(enable')); } })()],
["create-image-bitmap-timing", () => (async () => { const o = []; createImageBitmap(new ImageData(1, 1)).then(() => o.push(1)); Promise.resolve().then(() => o.push(2)); await new Promise((r) => setTimeout(r, 50)); return o.join(); })()],
["create-image-bitmap-timing", () => createImageBitmap(new ImageData(1, 1), { resizeWidth: 0 }).then(() => 'ok', (e) => e.message.includes('resizeWidth'))],
["audio-buffer-source-loop-start", () => (async () => { const c = new OfflineAudioContext(1, 8, 8192), b = c.createBuffer(1, 4, 8192); b.getChannelData(0).set([1, 2, 3, 4]); const s = new AudioBufferSourceNode(c, { buffer: b, loop: true, loopStart: 2 / 8192 }); s.connect(c.destination); s.start(); return [...(await c.startRendering()).getChannelData(0)]; })()],
["get-display-media-audio-selection", () => (() => { let r = false; navigator.mediaDevices.getDisplayMedia({ get audioSelection() { r = true; } }).catch(() => {}); return r; })()],
["rtc-ice-candidate-tls", () => new RTCIceCandidate({ candidate: 'candidate:1 1 tls 1 1.2.3.4 443 typ host fingerprint sha-256;AA', sdpMid: '0' }).address],
["webgl-angle-validation-152", () => (() => { const g = document.createElement('canvas').getContext('webgl2'); g.bindTexture(g.TEXTURE_2D, g.createTexture()); g.texImage2D(g.TEXTURE_2D, 0, g.RGBA, 1, 1, 0, g.RGBA, g.UNSIGNED_BYTE, null); g.texSubImage2D(g.TEXTURE_2D, 0, 0, 0, 1, 1, g.RGBA, g.UNSIGNED_SHORT_4_4_4_4, new Uint16Array(1)); return g.getError(); })()],
["swiftshader-max-draw-buffers", () => (() => { const g = document.createElement('canvas').getContext('webgl2'); return g.getParameter(g.MAX_DRAW_BUFFERS); })()],
["css-pseudo-element-types", () => document.documentElement.pseudo('::backdrop') !== null],
["css-pseudo-element-types", () => document.documentElement.pseudo('::view-transition') !== null],
["css-parsing-152", () => CSS.supports('overflow-clip-margin', '-1px')],
["css-parsing-152", () => ((s) => s.currentTranslate === s.currentTranslate)(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))],
["disabled-control-blur", () => ((i) => { document.body.append(i); i.focus(); i.disabled = true; const r = document.activeElement === i; i.remove(); return r; })(document.createElement('input'))],
["jspi-async-stack", () => (async () => { const b = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0, 0, 2, 7, 1, 1, 109, 1, 102, 0, 0, 3, 2, 1, 0, 7, 7, 1, 3, 114, 117, 110, 0, 1, 10, 6, 1, 4, 0, 16, 0, 11]); const { instance: i } = await WebAssembly.instantiate(b, { m: { f: new WebAssembly.Suspending(async () => { await 0; throw Error(); }) } }); try { await WebAssembly.promising(i.exports.run)(); } catch (e) { return e.stack.includes('wasm-function'); } })()],
["webgpu-subgroup-size-control", () => navigator.gpu.requestAdapter().then((a) => !!a && a.features.has('subgroup-size-control'))],
];
const out = [];
for (const [id, f] of checks) {
let v;
try { v = await f(); } catch (e) { v = "throws " + e.name; }
const expr = f.toString().replace(/^\(\)\s*=>\s*/, "");
out.push(id.padEnd(28) + expr + " = " + (typeof v === "string" ? JSON.stringify(v) : String(v)));
}
console.log(out.join("\n"));
})();