geek-trusted-wheel-events

4 小时 54 分钟前
 aaa69532

Scroll Events Even the Page Can't Tell Are Fake

Modern infinite-scroll pages don't load their feed because you asked nicely. Many of them check event.isTrusted on scroll events — the browser-set flag that distinguishes input originating from an actual user (OS input pipeline) from input synthesized by JavaScript (element.dispatchEvent, window.scrollBy). If the event says isTrusted: false, the page simply declines to fetch more content. It's a polite little fence, and it is genuinely hard to jump: by specification, JavaScript cannot set isTrusted on a synthesized event. Period.

Which creates a fun problem for Scrapewright, an open-source Chrome extension whose scrapers must trigger lazy-loaded content on pages that employ exactly this filter. Programmatic scrollBy is isTrusted: false forever. The solution the project shipped is the most interesting trick in the repo: wheel events dispatched through Chrome DevTools Protocol's Input domain come out the other side with isTrusted: true — because they enter Chrome through the same input pipeline the OS uses.

The mechanism

The chain, briefly:

  1. The step script calls $scrollToBottom (a DSL primitive). Normal path first: programmatic scrolling, with stall detection — if content stops growing, programmatic scroll is being filtered.
  2. On stall, the extension transiently attaches the Chrome DevTools Protocol to the tab (chrome.debugger.attach) — this is the extension-debugging API, which shows the user the standard "is being debugged" infobar; nothing hidden.
  3. It issues Input.dispatchMouseEvent with type: 'mouseWheel' and a synthetic deltaY, targeting the tab.
  4. CDP Input.* commands are injected at the browser's input layer. The resulting wheel event travels the same path as your trackpad: input pipeline → renderer → event listeners — with isTrusted: true. IntersectionObserver-driven loaders and trust-checking handlers behave exactly as they do for a human.
  5. The debugger detaches. Attempts are capped (a small budget per $scrollToBottom call) so a hopeless page fails fast instead of grinding.

So: the only programmatic mechanism that produces a trusted wheel event in Chrome is CDP Input — not because of a bug, but because CDP Input is the browser's programmable front door to the real input pipeline. Synthetic InputEvents from JS are forever untrusted; CDP-injected ones are "real" by construction.

The nuances that make it a good read

It only works in an active tab. Input.dispatchMouseEvent into a background tab can hang or do nothing, because Chrome produces compositor frames only for the active tab of the focused window — a separate, architectural throttle. The project therefore pairs CDP input with a tab activation layer (activate the scrape tab, keep it sticky, return the user to their last-clicked tab when the scrape tab closes). Two "unrelated" Chrome behaviors turn out to be one requirement.

Detection surface is a design decision. Attaching chrome.debugger is visible (infobar) and observable (console.debugger detection tricks exist, Runtime.enable notifications, etc.). The repo deliberately restricts itself to Input.* commands only — no Runtime.*, Network.*, or DOM.* poking — minimizing both footprint and abuse potential. It's a scraping tool that escalates exactly one rung, for exactly one purpose, with a budget.

It's the fallback, not the default. Ordinary programmatic scroll works on most sites and is faster; the trusted-wheel path exists for the class of sites whose loaders filter on trust. Escalation-on-stall rather than always-on CDP keeps the common path light and the heavy path rare.

Is this legit?

The question deserves the same honesty the code has. The isTrusted fence is designed to distinguish "a human is here" from "code is driving." CDP Input deliberately blurs that — that's literally its function, and it's the same mechanism enterprise RPA tools and Google's own Puppeteer (page.mouse.wheel) expose. Scrapewright uses it inside your own browser, on pages your own session can access, to read content you're entitled to read — the ethical weight sits on what you're entitled to, not on the input mechanism. The repo is equally plain about its limits: this is not an anonymity tool, and its stated scope is repeated, targeted extraction, not mass crawling. If you build on it, keep the same line.

As pure browser engineering, it's a lovely corner of the stack: spec-level security flag → architectural frame-production rule → input-pipeline injection → activation choreography, all cooperating to make one lazy-loaded feed scroll. The whole five-layer anti-throttling stack it lives in is documented in the whitepaper (§9), and it's some of the best practical Chrome-internals writing in any open-source repo this year.

Repo: github.com/singhand-labs/scrapewright — GPLv3, lib/renderer-activation.js and lib/scroll-ops.js are where to start reading.

164 次点击
所在节点    程序员
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1236297

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX