The primary objectives for this milestone were to decide whether to use the
<link> tag and then implement the same in Gecko (Firefox's browser engine). I'm happy to inform you that both of these goals have been successfully accomplished!
Spoiler alert: using
<link> is my recommended way forward for the Web Monetization community. More details below.
Progress on objectives
The objectives under the current milestone were:
- Study relevant specifications and Gecko infrastructure.
- Discuss and decide whether to use the
- Integrate the said element's behavior in Gecko.
<link> is the way forward
Following a discussion outlining various pros and cons of each tag, and a deep dive into various specifications and Gecko infrastructure, we decided to go with the
<link> tag. This means the browser should support:
<link rel="monetization" href="URL" />`
<meta name="monetization" content="$paymentPointer" />
This is a breaking change to the specification, and, because of the impact of the change, a difficult decision for the WM community to adopt. We would essentially be abandoning "payment pointers" — which are well-known in the Web Monetization community — with something that better aligns with the web platform: using regular URLs. I won't get into the technical details here, but you can read the summary of my research in the GitHub issue.
Bringing it to the browser
Once the WM community reached consensus to to go with the
<link>, I proceeded to implement this declarative API in a fork of Firefox at GitHub. Note that the present implementation only handles the fetching and processing of the monetization endpoint (payment pointer), and not the actual monetization.
You can download the current implementation from GitHub. At the time of writing, it supports:
- Getting monetization details from the first valid
<link>element at any time. That is, if you add or remove or modify the link elements, the monetization info is updated. This way, dynamic monetization tags support revenue sharing and single-page applications (SPA) use cases.
- Monetizing only the currently visible tab in the browser (no background tabs).
- Fetching the payment info with proper HTTP headers, while respecting CORS policy and cache-control behavior, as defined by the SPSP protocol.
- To preserve user's privacy, no referrer information (i.e., the URL of current website) or cookies are sent to the monetization receiver (e.g. Uphold).
- Allowing only legitimate payment pointers through the use of a Content Security Policy (CSP). By using the
monetization-src <source>;CSP directive, we can enforce monetizing only certain payment pointers so that malicious actors cannot steal your money.
- Logging error messages and other information to the browser console.
A more up-to-date implementation status can be read in the project's wiki.
The implementation presently does not support monetization of embedded content like iframes. That needs more discussion from the security and performance perspective, and also requires specification of various edge cases. Nevertheless, those cases have been captured in a GitHub issue.
Note: This section contains some technical details and my journey.
I started with going through with various specifications, specifically HTML, DOM, Fetch, InterLedger Protocol, Payment Pointers, Web Monetization, W3C Tag Design Principles, RFC8288 (Web Linking) and RFC2718 (Guidelines for new URL Schemes), to get a better understanding of the platform as a whole.
After familiarizing myself with the specs, I went through Firefox code-base and documentation to analyze how the existing Gecko infrastructure can be used to support
<link> monetization tags.
After studying for a few weeks and various discussions with Marcos Cáceres, I started to summarize my notes into a write-up that can be used by the Web Monetization community to settle the debate whether we should move forward with
As of today, we've settled on using the
Set up development environment
Now as we can't just implement a specification that is not presently on standards track into a browser, I created my own fork of Firefox on GitHub. This allowed me to prototype quickly, without the hassle of integrating a particular feature flag for Web Monetization (generally, something that can be time consuming to add).
Aside: One thing I found interesting is that GitHub doesn't let you push a large repository (4GB in this case) in one go. I had to write a script to split the repository into smaller batches of commits and push them instead (that took a while).
I'll write more on temporarily maintaining a fork of a project as large as Firefox, and my Firefox development workflow some other day on my website.
Let the coding begin!
The easiest part was to support the
HTMLLinkElement.relList.supports() API. I just had to add a new string literal for
rel="monetization". This can be easily used to detect whether a browser supports Web Monetization. Feels good to see some quick progress - git commit!
During the earlier prototyping, I decided to implement the
The JSM approach required a change in thinking — from low level hyper-optimized stuff to high level design. Though, it felt weird to not have any type-checking/hinting support in the editor — I had to jump into WebIDL definitions to understand what JSM can do. Fast forward a few days, I had a working implementation of what's mentioned above (except CSP, which required a lot more fiddling across 18 files).
Creating a Nightly release
Satisfied with the current implementation, I created a Firefox Nightly release for you to try! You can find the download links in the wiki.
After extracting the downloaded archives, you need to run it from command line as:
--jsconsole flag opens Firefox with a "multi process browser console" window, which will display various Web Monetization metadata, logs and error messages.
Next, I'll work towards bringing the actual Web Monetization support in Firefox. For this, we'll first need to specify the role of WM agent (browser extension) and the communication between the WM agent, browser, and the website. Once that's done, we'll hopefully have a working native implementation of Web Monetization. While we discuss and decide above, we'll try to formalize the specification based on the things we've learned so far.
We're already discussing the role of WM agent over GitHub. Feel free to join!
What community support would benefit your project?
I'm curious to understand how we should go with the monetization of iframes and other embedded content like images and videos. Say, you're the page owner, how do you think we should monetize third party content embedded in your site? Conversely, how would you like your content to be monetized if it's embedded in some other website? One idea that comes to mind is to only allow the visible content to be monetized as the user scrolls through the site, but it adds quite a lot of complexity to the browser and has many edge cases. Is this something you would love to see in the first version of the Web Monetization standard, or it can be deferred for later? Let us know what you think in comments below or at GitHub.
Thanks to the Grant for the Web for supporting my workstation purchase. I can't emphasize enough what a difference this has made to my development flow: I'm able to compile Gecko in record time - nearly 7 minutes, compared to 80 minutes on my previous laptop. This enabled me to quickly prototype various implementation strategies without wasting hours waiting for Gecko to compile!
Top comments (3)
This is fantastic progress, well done!
I think the decision to move to the
<link>tag is a good one. One challenge we'll need to explore is what we do with link relations in HTTP headers, have you given that any thought? It's not something we can support from the extension because extensions don't have access to the response headers as far as I know.
On the topic of Payment Pointers, I wouldn't say we're abandoning them but rather that if we use the
<link>tag then we'd need to use the full URL form of the Payment Pointer as opposed to the shortened form which is primarily for user transcription and readability.
As an aside, a Payment Pointer is just a URL, but it is a URL that we expect to have very specific behaviours when dereferenced. In this case the resource it is locating is an account that we can send money to via Interledger. We're doing a lot of work in the Open Payments specification to expand on the use cases for Payment Pointers and Interledger beyond just Web Monetization (think of a Payment Pointer as a Web-native credit card number but with super-powers and better security).
Finally, how do we get some more smart people like yourself to repeat this awesome work on some other browser engines?
@chrislarry and @erikad how can we get more Technical Scholars into the Grant for the Web Program to do this work?
@adrianhopebailie We are very enthusiastic about expanding the Technical Scholars program to additional browsers teams. We're drafting info for potential host organizations that will be shared soon. In the meantime, interested team leads are welcome to reach out to @chrislarry and myself directly.
With independent non-HTML documents like PDF, images etc., I think it should be fairly straightforward - it's similar to web pages. If we were to use
Linkheader along with
<link>elements, it gets a bit more interesting as we need to use either of them. HTTP headers come before response body, so it might make sense to use
Linkeven when a
When such content is embedded into a web page, we run into same problems as with iframes. From implementation perspective, it's easier to run as many payment streams as their are embedded content, but this can be very bad performance wise. If we were to monetize only "visible" or "active" content, it gets difficult to implement with all the edge cases (what if someone adds 100 1px images in a row etc.). Also, with the overhead of setting up payment, user might scroll away from the content before we even set up the payment.
I mean we'll have to use WHATWG URLs, but I get your point. I'll update the report to make it clearer.
This sounds exciting! I feel like we should again look into the
pay://URL scheme 😄