{"id":1715,"date":"2022-08-10T12:19:16","date_gmt":"2022-08-10T10:19:16","guid":{"rendered":"http:\/\/blogs.igalia.com\/jfernandez\/?p=1715"},"modified":"2022-08-31T20:06:24","modified_gmt":"2022-08-31T18:06:24","slug":"new-custom-handlers-component-for-chrome","status":"publish","type":"post","link":"https:\/\/blogs.igalia.com\/jfernandez\/2022\/08\/10\/new-custom-handlers-component-for-chrome\/","title":{"rendered":"New Custom Handlers component for Chrome"},"content":{"rendered":"<p>The HTML Standard section on Custom Handlers describes the procedure to register custom protocol handlers for URLs with specific schemes. When a URL is followed, the protocol dictates which part of the system handles it by consulting registries. In some cases, handlers may fall to the OS level and and launch a desktop application (eg. mailto:\/\/ may launch a native application) or, the browser may redirect the request to a different HTTP(S) URL (eg. mailto:\/\/ can go to gmail).<\/p>\n<p>There are multiple ways to invoke the <em>registerProtocolHandler<\/em> method from the <em>HTML API<\/em>. The most common is through Browser Extensions, or add-ons, where the user must install third-party software to add or modify a browser&#8217;s feature. However, this approach puts on the users the responsibility of ensuring the extension is secure and that it respects their privacy. Ideally, downstream browsers could ship built-in protocol handlers that take this load off of users, but this was previously difficult.<\/p>\n<p>During the last years <a href=\"http:\/\/igalia.com\">Igalia<\/a> and <a href=\"https:\/\/protocol.ai\/\">Protocol Labs<\/a> have been working on improving the support of this <em>HTML API<\/em> in several of the main web engines, with the long term goal of getting the <em>IPFS<\/em> protocol a first-class member inside the most relevant browsers.<\/p>\n<p>In this post I&#8217;m going to explain the most recent improvements, especially in Chrome, and some side advantages for Chromium embedders that we got on the way.<\/p>\n<h2>Componentization of the Custom Handler logic<\/h2>\n<p>The first challenge faced by Chromium-based browsers wishing to add support for a new protocol was the architecture. Most of the logic and relevant codebase lived in the <em>\/\/chrome<\/em> layer. Thus, any intent to introduce a behavior change on this logic would require a direct fork and patch the <em>\/\/chrome<\/em> layers with the new logic. The design wasn&#8217;t defined to allow Chrome embedders to extend it with new features<\/p>\n<p>The Chromium project provides a Public Content API to allow embedders reusing a great part of the browser&#8217;s common logic and to implement, if needed, specific behavior though the specialization of these APIs. These would seem to be the ideal way of extending the browser capabilities to handle new protocols. However, accessing <em>\/\/chrome<\/em> from any layer (eg. <em>\/\/content<\/em> or <em>\/\/components<\/em>) is forbidden and constitutes a layering violation.<\/p>\n<p>After some discussion with Chrome engineers (special thanks to Kuniko Yasuda and Colin Blundell) we decided to move the Protocol Handlers logic to the <em>\/\/components<\/em> layer and create a new Custom Handlers component.<\/p>\n<p>The following diagram provides a high-level overview of the architectural change:<\/p>\n<figure id=\"attachment_1738\" aria-describedby=\"caption-attachment-1738\" style=\"width: 422px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/components.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1738\" src=\"http:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/components-627x1024.png\" alt=\"Custom Handlers logic moved to the \/\/component layer\" width=\"422\" height=\"690\" srcset=\"https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/components-627x1024.png 627w, https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/components-184x300.png 184w, https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/components-768x1255.png 768w, https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/components.png 784w\" sizes=\"auto, (max-width: 422px) 100vw, 422px\" \/><\/a><figcaption id=\"caption-attachment-1738\" class=\"wp-caption-text\">Changes in the Chrome&#8217;s component design<\/figcaption><\/figure>\n<p>The new component makes the Protocol Handler logic <strong>Chrome independent<\/strong>, bringing us the possibility of using the Content Public API to introduce the mentioned behavior changes, as we&#8217;ll see later in this post.<\/p>\n<p>Only the registry&#8217;s Delegate and the Factory classes will remain in the <em>\/\/chrome<\/em> layer.<\/p>\n<p>The <strong>Delegate<\/strong> class was defined to provide OS integration, like setting as default browser, which needs to access the underlying operating systems interfaces. It also deals with other browser-specific logic, like Profiles. With the new <em>componentized<\/em> design, embedders may provide their own Delegate implementations with different strategies to interact with the operating system&#8217;s desktop or even support additional flavors.<\/p>\n<p>On the other hand, the <strong>Factory<\/strong> (in addition to the creation of the appropriated Delegate&#8217;s instance) provides a <em>BrowserContext<\/em> that allows the registry to operate under Incognito mode.<\/p>\n<p>Lets now get a deeper look into the component, trying to understand the responsibilities of each class. The following class diagram illustrates the multi-process architecture of the Custom Handler logic:<\/p>\n<figure id=\"attachment_1740\" aria-describedby=\"caption-attachment-1740\" style=\"width: 840px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/multi-process-class-diagram.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1740 size-large\" src=\"http:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/multi-process-class-diagram-1024x654.png\" alt=\"Class diagram of the registerProtocolHandler logic in Chrome's multi-process architecture\" width=\"840\" height=\"536\" srcset=\"https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/multi-process-class-diagram-1024x654.png 1024w, https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/multi-process-class-diagram-300x192.png 300w, https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/multi-process-class-diagram-768x490.png 768w, https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/multi-process-class-diagram-1200x766.png 1200w\" sizes=\"auto, (max-width: 840px) 100vw, 840px\" \/><\/a><figcaption id=\"caption-attachment-1740\" class=\"wp-caption-text\">Multi-process architecture class diagram<\/figcaption><\/figure>\n<p>The <em>ProtocolHandlerRegistry<\/em> class has been modified to allow operating without user preferences storage; this is particularly useful to move most of the browser tests to the new component and avoid the dependency with the <em>prefs<\/em> and <em>user_prefs<\/em> components (eg. testing). In this scenario the registered handlers will be stored in memory only.<\/p>\n<p>It&#8217;s worth mentioning that as part of this architectural change, I&#8217;ve applied some code cleanup and refactoring, following the policies dictated in the <a href=\"https:\/\/bugs.chromium.org\/p\/chromium\/issues\/detail?id=1187001\">base::Value Code Health<\/a> task-force. Some examples:<\/p>\n<ul>\n<li>Use of StringPiece&nbsp; (<a href=\"https:\/\/crrev.com\/3445540\">CL#3445540<\/a>)<\/li>\n<li>Get rid of DictionaryValue and ListValue (<a href=\"https:\/\/crrev.com\/3484122\">CL#3484122<\/a>)<\/li>\n<\/ul>\n<p>The <em>ProtocolHandlerThrottle<\/em> implements <strong>URL redirection<\/strong> with the handler provided by the registry. The <em>ChromeContentBrowserClient<\/em> and <em>ShellBrowserContentClient<\/em> classes use it to implement the <em>CreateURLLoaderThrottles<\/em> method of the <em>ContentBrowserClient<\/em> interface.<\/p>\n<p>The specialized request is the responsible for handling the permission prompt dialog response, except for the the content-shell implementations, which bypass the Permission APIs (more on this later). I&#8217;ve also been working on a new design that relies on the <em>PermissionContextBase::CreatePermissionRequest<\/em> interface, so that we could create our own custom request and avoid the direct call to the <em>PermissionRequestManager<\/em>. Unfortunately this still needs some additional support to deal with the <em>RegisterProtocolHandlerPermissionRequest<\/em>&#8216;s specific parameters needed for creating new instances, but I won&#8217;t extend on this, as it deserves its own post.<\/p>\n<p>Finally, I&#8217;d like to remark that the new component provides a simple registry factory, designed mainly for testing purposes. It&#8217;s basically the same as the one implemented in the <em>\/\/chrome<\/em> layer except some limitations of the new component:<\/p>\n<ul>\n<li>No incognito mode<\/li>\n<li>Use of a mocking Delegate (no actual OS interaction)<\/li>\n<\/ul>\n<h2>Single-Point security checks<\/h2>\n<p>I also wanted to apply some refactoring as well to improve the <strong>inter-process consistency<\/strong> and increase the amount of <strong>shared code<\/strong> between the renderer and browser process.<\/p>\n<p>One of the most relevant parts of this refactoring was the one applied to the implement the Custom Handlers&#8217; <a href=\"https:\/\/html.spec.whatwg.org\/multipage\/system-state.html#normalize-protocol-handler-parameters\">normalization process<\/a>, which goes from some URL&#8217;s syntax validation to the security and privacy checks. I&#8217;ve landed some patches (<a href=\"https:\/\/crrev.com\/c\/3507332\">CL#3507332<\/a>, <a href=\"https:\/\/crrev.com\/c\/3692750\">CL#3692750<\/a>) to implement functions that are shared among the renderer and browser processes to implement the mentioned normalization procedure.<\/p>\n<figure id=\"attachment_1744\" aria-describedby=\"caption-attachment-1744\" style=\"width: 840px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/single-point-security.png\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-1744 size-large\" src=\"http:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/single-point-security-1024x456.png\" alt=\"Refactoring to implement a single-point security and privacy checks\" width=\"840\" height=\"374\" srcset=\"https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/single-point-security-1024x456.png 1024w, https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/single-point-security-300x134.png 300w, https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/single-point-security-768x342.png 768w, https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/single-point-security-1200x534.png 1200w, https:\/\/blogs.igalia.com\/jfernandez\/files\/2022\/08\/single-point-security.png 1658w\" sizes=\"auto, (max-width: 840px) 100vw, 840px\" \/><\/a><figcaption id=\"caption-attachment-1744\" class=\"wp-caption-text\">Security and privacy logic refactoring<\/figcaption><\/figure>\n<p>The code has been moved to blink\/common so that it could be invoked also by the new Custom Handlers component described before. Both the <em>WebContentsImpl<\/em> (run by the Browser process) and the <em>NavigatorContentUtils<\/em> (by the Renderer process) rely on this common code now.<\/p>\n<p>Additionally, I have moved all the security and privacy checks from the Browser class, in Chrome, to the <strong>WebContentsImpl<\/strong> class. This implies that any embedder that implements the <em>WebContentsDelegate<\/em> interface shouldn&#8217;t need to bother about these checks, assuming that any ProtocolHandler instance is valid.<\/p>\n<h1>Conclusion<\/h1>\n<p>In the introduction I mentioned that the main goal of this refactoring was to decouple the Custom Handlers logic from the Chrome&#8217;s specific codebase; this would allow us to modify or even extend the implementation of the Custom Handlers APIs.<\/p>\n<p>The componentization of some parts of the Chrome&#8217;s codebase has many advantages, as it&#8217;s described in the <a href=\"https:\/\/www.chromium.org\/developers\/design-documents\/cookbook\/\">Browser Components design document<\/a>. Additionally, a new Custom Handlers component would provide other interesting advantages on different fronts.<\/p>\n<p>One of the most interesting use cases is the definition of Web Platform Tests for the Custom Handlers APIs. This goal has 2 main challenges to address:<\/p>\n<ul>\n<li>Testing Automation<\/li>\n<li>Content Shell support<\/li>\n<\/ul>\n<p>Finally, we would like this new component to be useful for Chrome embedders, and even browsers built directly on top of a full featured Chrome, to implement new Custom Handlers related features. This line of work would eventually be useful to improve the support of distributed protocols like IPFS, as an intermediate step towards a <strong>native implementation<\/strong> of the protocols in the browser.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The HTML Standard section on Custom Handlers describes the procedure to register custom protocol handlers for URLs with specific schemes. When a URL is followed, the protocol dictates which part of the system handles it by consulting registries. In some cases, handlers may fall to the OS level and and launch a desktop application (eg. &hellip; <a href=\"https:\/\/blogs.igalia.com\/jfernandez\/2022\/08\/10\/new-custom-handlers-component-for-chrome\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">New Custom Handlers component for Chrome<\/span><\/a><\/p>\n","protected":false},"author":20,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23,11],"tags":[],"class_list":["post-1715","post","type-post","status-publish","format-standard","hentry","category-chromium","category-planet"],"_links":{"self":[{"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/posts\/1715","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/users\/20"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/comments?post=1715"}],"version-history":[{"count":33,"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/posts\/1715\/revisions"}],"predecessor-version":[{"id":1853,"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/posts\/1715\/revisions\/1853"}],"wp:attachment":[{"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/media?parent=1715"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/categories?post=1715"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/tags?post=1715"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}