Inspiration

Debugging H5 pages on real mobile devices is painful: iOS exposes only the WebKit Inspector Protocol (WIP) while Android uses the Chrome DevTools Protocol (CDP), and no single tool debugs both through the familiar Chrome DevTools UI with full Chrome-extension support. We wanted one Electron-based debugger that unifies iOS and Android under CDP and lets developers keep using their favorite DevTools extensions.

What it does

  • Unifies iOS (WIP) and Android (CDP) H5 debugging inside a single Electron app driving the Chrome DevTools frontend.
  • Supports Chrome extensions (DevTools panels, background scripts, content-script-style logic) against remote mobile pages.
  • Provides a universal in-page shim runtime so plugins communicate through a clean chrome.* messaging surface instead of poking the page directly. ## How we built it
  • Hybrid architecture: the extension runtime (shim + background/devtools/content-script logic) runs in a host-side disposable realm (hidden window/worker) that is destroyed with zero residue when a session ends; the page keeps only one universal, idempotent shim runtime that handles inspectedWindow.eval landing and the messaging bridge.
  • Protocol bridging: a CDP/WIP adapter translates and forwards messages, with an interest-filter and coalescing layer to suppress event floods at the source.
  • Console-free uplink (iOS): because WIP lacks Runtime.addBinding and native page→debugger push events, the page→debugger channel is implemented via Runtime.evaluate pull (poll-drain / drain-on-demand), keeping the console channel clean for real logs.
  • WASM acceleration: hot, large, structured payloads (DOM/Elements tree diff, Network/HAR, Heap/Profiler) are parsed and diffed in WASM with columnar typed-array export, keeping compute off the JS main thread and teardown clean. ## Challenges we ran into
  • No isolated world on iOS: WIP can't create a runtime separate from the main world, so injected plugin scripts couldn't be cleanly torn down — every session re-injected them and triggered a communication "avalanche" (N copies piling into the main world, each spawning listeners/channels, amplified by fan-out broadcast).
  • WIP protocol gaps: iOS lacks addScriptToEvaluateOnNewDocument/removeScriptToEvaluateOnNewDocument and Runtime.addBinding, so injection and the page→debugger uplink had to be re-engineered around Runtime.evaluate + context-cleared listeners.
  • Transport pressure: console-sentinel uplinks congested the up/down channel and bled into real logs; window.prompt-based bridging was rejected because it blocks the JS thread. ## Accomplishments that we're proud of A clean hybrid architecture that achieves near-isolated-world-like teardown on iOS without an isolated world. ## What we learned The "avalanche" is not a single bug but three necessary conditions (non-idempotent injection + unrecoverable main-world side effects + fan-out broadcast) — remove any one and it stops. ## What's next for h5 debug Build the runnable hybrid-architecture skeleton (host-side disposable realm with dispose, single universal shim, content-script logic moved into the host realm).

Built With

Share this project:

Updates