From c2b79b67feb4abb39210cfe79325136bf0812275 Mon Sep 17 00:00:00 2001 From: Andrey Mnatsakanov Date: Sat, 11 Jul 2026 16:37:10 +0200 Subject: [PATCH] fix(js): fire trusted input/change from setFileInputFiles (#385) DOM.setFileInputFiles dispatched plain input/change events, so event.isTrusted was false. Upload flows and anti-bot code that gate their change handler on isTrusted ignored them, which silently broke the exact case the feature targets. The Input domain already marks its synthesized clicks/keys trusted via __obscura_markTrusted; do the same here. Fixes #384. --- crates/obscura-js/js/bootstrap.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/obscura-js/js/bootstrap.js b/crates/obscura-js/js/bootstrap.js index 8182f50..841b28c 100644 --- a/crates/obscura-js/js/bootstrap.js +++ b/crates/obscura-js/js/bootstrap.js @@ -4534,8 +4534,13 @@ globalThis.__obscura_setInputFiles = function(el, specs) { return new File([bytes], s.name || "", { type: s.type || "" }); }); el._files = _makeFileList(files); - try { el.dispatchEvent(new Event("input", { bubbles: true })); } catch (_e) {} - try { el.dispatchEvent(new Event("change", { bubbles: true })); } catch (_e) {} + // Mark the events trusted (isTrusted === true), like the Input domain does + // for synthesized clicks/keys. A real selection fires + // trusted events; upload flows that gate their change handler on + // event.isTrusted (common in frameworks and anti-bot code) ignore untrusted + // ones, which would silently break the exact case this feature targets. + try { el.dispatchEvent(globalThis.__obscura_markTrusted(new Event("input", { bubbles: true }))); } catch (_e) {} + try { el.dispatchEvent(globalThis.__obscura_markTrusted(new Event("change", { bubbles: true }))); } catch (_e) {} }; globalThis.Event = class Event { constructor(t,o={}) { this.type=t;this.bubbles=!!o.bubbles;this.cancelable=!!o.cancelable;this.composed=!!o.composed;this.defaultPrevented=false;this.target=null;this.currentTarget=null;this.eventPhase=0;this.timeStamp=Date.now();this._propagationStopped=false;this._immediatePropagationStopped=false; }