Filling the API gaps with creativity

All APIs have their limits and running into them might be frustrating. However, in the huge codebases like the browser and extensive APIs like Chrome Extension API there is a lot of opportunity to fill the gaps with creativity.

As I learned yesterday, one of the most requested features of the React Developer Tools extension is ability to “Inspect with React DevTools”. This functionality should mimic the build-in “Inspect”, but instead of opening Elements panel, it should focus the extensions’ panel instead.

That’s definitely a legit feature request and I’m sure that other dev extensions would also pick it up. Unfortunately, there are no methods in the Chrome Extensions API that would allow to open DevTools or change the DevTools panel. The idea was already proposed back in 2015 by the Adblock Plus team and subsequently shot down.

There are security implications of this API that outweigh the developer convenience.

Empowering extensions while keeping their users safe is definitely a tough task and no one can really blame Chrome team for being cautious in this case. However, one can argue that extensions can already do everything that can be done using DevTools. All the heavy lifting in DevTools is done via the Debugging Protocol and extensions already have an unlimited access to it.

Yesterdays discussion about “Inspect using React DevTools” resurrected the mentioned Chrome ticket and there is again a hope that ability to programmatically open DevTools, or some kind of a safer alternative, will be found.

Please note though, that even if the consensus will be reached, it will take months for these APIs to be implemented and shipped with stable Chrome.

I started poking around to identify all the missing APIs that will be required to implement “Inspect in React DevTools” and see if there is some kind of an alternative that we can put together today.

The context menu was an obvious place to start. Adding a menu item is absolutely possible. Unfortunately, the information passed to the items’ click callback is rather limited. Depending on the DOM element clicked we get very few to no information about it and we somehow need to tell the React Developer Tools what’s the context.

There are couple of solutions here that come to mind, but one I actually tried is based on a content script (script that extension injects to the page) which tracks the last right-clicked element and makes this information available to the other parts of the extension.

The next challenge is to programmatically open DevTools, but I found no way to make it work (it’d be handled as a vulnerability and patched right away anyway). If React Developer Tools user workflow is anything like mine though, this API isn’t really as needed as it seems. When working on an app I definitely use “Inspect” a lot, but the DevTools usually stay open for a very long time. What I decided to go after next is ability to focus on the React Developer Tools extension panel whenever “Inspect in React DevTools” is used.

There is an (undocumented) API that allows to switch panels in DevTools, but it allows that only in a very specific case:

[] will only show the panel if the method is called from a handler called based on some user action, and the only such handler that we currently have may be set with devtools.panels.setOpenResourceHandler()

So we can have a context menu item able to focus our panel but it will only work inside DevTools and only when some kind of a resource is clicked.

Custom item in the context menu of the DevTools Network panel

For our usecase, this is useless. I’ve ended up reading the source code of that API, hoping to find some other undocumented methods. The part that caught my eye was this:

Above code is responsible for passing any potential keyboard shortcuts from the extensions’ panel (where they can’t be understood) to DevTools (that can handle them). The thing that this code is missing is a check that‘d make sure that the passed keystroke was actually triggered by the user (this can be done via isTrusted property on an Event object). Because it’s not being checked, we can dispatch fake keystrokes that would trigger keyboard shortcuts. And there is one keyboard shortcut that is especially interesting for us:

The code I put together to make it work looks like this:

And the solution presents itself in action like so:

The whole thing is of course a hack, and the keyboard shortcut trick is a bug that will probably get fixed at some point. Why not use these “creative” solutions to the missing APIs while we are waiting for the real thing though? Isn’t that what the web devs usually do? 😉