Proxies are definitely fun. We use them extensively in our browser automation framework, which is largely based on the Web Extensions API [1], to implement syntactic sugar in the interface. The general goal behind Remote Browser [2] is to make it really easy to execute privileged code in a web browser, but to otherwise stay out of your way as much as possible. This is accomplished through the heavy use—some might even say <i>abuse</i>—of proxies.<p>For instance, take a look at this code example of opening a tab.<p><pre><code> await browser.evaluateInBackground(createProperties => (
browser.tabs.create(createProperties)
), { url: 'https://intoli.com' });
</code></pre>
The <i>browser.evaluateInBackground()</i> method is part of the Remote Browser API, and it simply evaluates code in a background script context in the browser. The <i>browser.tabs.create()</i> call, on the other hand, is part of the Web Extensions API. The vast majority of Remote Browser's power is provided through remote code execution and the Web Extensions API, so we really wanted to provide a more concise syntax for performing this sort of operation. Using proxies, we were able to make the <i>browser</i> object directly callable as a shorthand for background context evaluation. This code example is exactly equivalent to the previous one.<p><pre><code> await browser(createProperties => (
browser.tabs.create(createProperties)
), { url: 'https://intoli.com' });
</code></pre>
That's a bit more concise, but the real sugar is in the next step of abstraction. Instead of explicitly evaluating a function in the background script context, you can simply treat the <i>browser</i> object in the client context as though it were the Web Extensions API <i>browser</i> object in the background script context. This code example is again exactly equivalent, and again accomplished using proxies.<p><pre><code> await browser.tabs.create({ url: 'https://intoli.com' });
</code></pre>
The <i>tabs.create</i> method isn't hardcoded into the Remote Browser library. Instead, proxies are used to evaluate any non-local API calls in the background script context. This means that you automatically get full access to the version of the Web Extensions API that your browser supports, whether you're using Chrome, Edge, Firefox, or Opera.<p>The one last piece of syntactic sugar is the shorthand for evaluating code inside of individual tabs. The syntax is very similar to how you evaluate functions in the background script context, you just need to first use square brackets to indicate the tab ID.<p><pre><code> await browser[tab.id](createProperties => (
document.getElementById('some-link').innerHTML
), { url: 'https://intoli.com' });
</code></pre>
This is <i>also</i> implemented using proxies. In fact, the same trap that handles the local Web Extensions API calls handles the tab access, and it differentiates between the two depending on whether or not the property name consists entirely of integers. In both cases, it returns <i>another</i> proxy that is able to handle either additional chaining or function evaluation.<p>Anyway, sorry that this ended up a lot more long-winded than I was expecting. If this has piqued your interest in Remote Browser though, we made an interactive tour of the project that you should check out [3]. You can actually launch and control browsers from inside of your own browser in the tour, and it explains a lot more about the project philosophy and what you can do with it<p>[1] - <a href="https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API" rel="nofollow">https://developer.mozilla.org/en-US/Add-ons/WebExtensions/AP...</a><p>[2] - <a href="http://github.com/intoli/remote-browser/" rel="nofollow">http://github.com/intoli/remote-browser/</a><p>[3] - <a href="https://intoli.com/tour" rel="nofollow">https://intoli.com/tour</a>