{"id":1756,"date":"2022-11-14T12:07:21","date_gmt":"2022-11-14T11:07:21","guid":{"rendered":"http:\/\/blogs.igalia.com\/jfernandez\/?p=1756"},"modified":"2022-11-14T12:07:21","modified_gmt":"2022-11-14T11:07:21","slug":"discovering-chromes-pre-defined-custom-handlers","status":"publish","type":"post","link":"https:\/\/blogs.igalia.com\/jfernandez\/2022\/11\/14\/discovering-chromes-pre-defined-custom-handlers\/","title":{"rendered":"Discovering Chrome&#8217;s pre-defined <br>Custom Handlers"},"content":{"rendered":"<p>In a <a href=\"https:\/\/blogs.igalia.com\/jfernandez\/2022\/08\/10\/new-custom-handlers-component-for-chrome\/\">previous post<\/a> I described some architectural changes I&#8217;ve applied in Chrome to move the Custom Handlers logic into a new component. One of the advantages of this new architecture is an easier extensibility for embedders and less friction with the <em>\/\/chrome<\/em> layer.<\/p>\n<p><a href=\"https:\/\/igalia.com\">Igalia<\/a> and<a href=\"https:\/\/protocol.ai\/\"> Protocol Labs<\/a> has been collaborating for some years already to improve the <strong>multi-protocol<\/strong> capabilities in the main browsers; most of our work has been done in Chrome, but we have interesting contributions in Firefox and of course we have plans to increase or activity in Safari.<\/p>\n<p>As I said in the mentioned post, one of the long-term goals that Protocol Labs has with this work is to improve the integration of the <em>IPFS<\/em> protocol in the main browsers. On this regard, its undeniable that the <strong>Brave<\/strong> browser is the one <a href=\"https:\/\/brave.com\/ipfs-support\/\">leading this effort<\/a>, providing a native implementation of the <em>IPFS<\/em> protocol that can work with both, a local <em>IPFS<\/em> node and the <em>HTTPS<\/em> gateway. For the rest of the browsers, Protocol Labs would like to at least implement the <em>HTTPS gateway<\/em> support, as an <strong>intermediate step<\/strong> and to increase the adoption and use of the protocol.<\/p>\n<p>This is where the Chrome&#8217;s <strong>pre-defined custom handlers<\/strong> feature could provide us a possible approach. This feature allows Chrome to register handlers for specific schemes at compile-time. The <em>ChromeOS<\/em> embedders use this mechanism to register handlers for the <em>mailto<\/em> and <em>webcal<\/em> schemes, to redirect the <em>HTTP<\/em> requests to <em>mail.google.com<\/em> and <em>google.com\/calendar<\/em> sites respectively.<\/p>\n<p>The idea would be that Chrome could use the same approach to register pre-defined handlers for the <em>IPFS<\/em> scheme, redirecting the <em>IPFS<\/em> request to the <em>HTTPS gateways<\/em>.<\/p>\n<p>In order to understand better how this feature would work, I\\&#8217;ll explain first how Chrome deals with the needs of the different embedders regarding the schemes.<\/p>\n<h2>The SchemesRegistry<\/h2>\n<p>The <em>SchemeRegistry<\/em> data structure is used to declare the schemes registered in the browser and assign some specific properties to different subsets of such group of schemes.<\/p>\n<p>The data structure defines different lists to provide these specific features to the schemes; the main and more basic list is perhaps the <em>standard schemes<\/em>, which is defined as follows:<\/p>\n<blockquote><p>A standard-format scheme adheres to what RFC 3986 calls &#8220;generic URI syntax&#8221; (https:\/\/tools.ietf.org\/html\/rfc3986#section-3).<\/p><\/blockquote>\n<p>There are other lists to lend specific properties to a well-defined set of schemes:<\/p>\n<pre lang=\"CPP\">\/\/ Schemes that are allowed for referrers.\nstd::vector<std::string> referrer_schemes = {\n  {kHttpsScheme, SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION},\n  {kHttpScheme, SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION},\n};\n\/\/ Schemes that do not trigger mixed content warning.\nstd::vector<std::string> secure_schemes = {\n  kHttpsScheme, kAboutScheme, kDataScheme, \n  kQuicTransportScheme, kWssScheme,\n};\n<\/std::string><\/std::string><\/pre>\n<p>The following class diagram shows how the <em>SchemeRegistry<\/em> data structure is defined, with some duplication to avoid layering violations, and its relationship with the <em>Content Public API<\/em>, used by the Chrome embedders to define its own behavior for some schemes<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/notes.igalia.com\/uploads\/b92b1657-d4c7-43fc-9c8a-ac0c349f503b.svg\" alt=\"\"><\/p>\n<p>It&#8217;s worth mentioning that this relationship between the <em>\/\/url<\/em>, <em>\/\/content<\/em> and <em>\/\/blink<\/em> layers shows some margin to be improved, as it was noted down by Dimitry Gozman (one of the Content Public API owners) in one of the <a href=\";https:\/\/chromium-review.googlesource.com\/c\/chromium\/src\/+\/3652049\/comments\/88ec1113_c35e10b4\">reviews<\/a> of the patches I&#8217;ve been working on. I hope we could propose some improvements on this code, as part of the goals that <a href=\"https:\/\/igalia.com\">Igalia<\/a> and<a href=\"https:\/\/protocol.ai\/\"> Protocol Labs<\/a> will define for 2023.<\/p>\n<h2>Additional schemes for Embedders<\/h2>\n<p>As it was commented on the preliminary <a href=\"https:\/\/groups.google.com\/a\/chromium.org\/g\/net-dev\/c\/FcRJgdHbttQ\/m\/TwhF0y8EDwAJ;\">discussions<\/a> I had as part of the analysis of the initial refactoring work described in the first section, Chrome already provides a way for embedders to define new schemes and even modifying the behavior of the ones already registered. This is done via the <em>Content Public API<\/em>, as usual.<\/p>\n<blockquote><p>The Chrome&#8217;s Content Layer offers a Public API that embedders can implement to define its own behavior for certain features.<\/p><\/blockquote>\n<p>One of these abstract features is the addition of new schemes to be handled internally. The <em>ContentClient<\/em> interface has a method called <em>AddAdditionalSchemes<\/em> precisely for this purpose. On the other hand, the <em>ContentBrowserClient<\/em> interface provides the <em>HasCustomSchemeHandler<\/em> and <em>IsHandledURL<\/em> methods to allow embedders implement their specific behavior to handler such schemes.<\/p>\n<p>The following class diagram shows how embedders implements the <em>Content Client<\/em> interfaces to provide specific behavior to some schemes:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/notes.igalia.com\/uploads\/aec833af-cc6e-49ba-820d-21b19791dfad.svg\" alt=\"\"><\/p>\n<p>I&#8217;ve applied a <a href=\"https:\/\/chromium-review.googlesource.com\/c\/chromium\/src\/+\/3652049\">refactoring<\/a> to allow defining pre-defined handlers via the <em>SchemeRegistry<\/em>. This is some excerpt of such refactoring:<\/p>\n<pre lang=\"CPP\">\/\/ Schemes with a predefined default custom handler.\nstd::map<std::string, std::string=\"\"> predefined_handler_schemes;\n\nvoid AddPredefinedHandlerScheme(const char* new_scheme, const char* handler) {\n  DoAddSchemeWithHandler(\n      new_scheme, handler,      \n      GetSchemeRegistryWithoutLocking().predefined_handler_schemes);\n}\n\nconst std::map<std::string, std::string=\"\">\nGetPredefinedHandlerSchemes() {\n  return GetSchemeRegistry().predefined_handler_schemes;\n}\n<\/std::string,><\/std::string,><\/pre>\n<p>Finally, embedders would just need to implement the <strong>ChromeContentClient::AddAdditionalSchemes<\/strong> interface to register a scheme and its associated handler.<\/p>\n<p>For instance, in order to install in Android a predefined handler for the <em>IPFS<\/em> protocol, we would just need to add in the <em>AwContentClient::AddAdditionalSchemes<\/em> function the following logic:<\/p>\n<pre lang=\"CPP\">  \n schemes.predefined_handler_schemes.emplace_back(\n      \"ipfs\", \"https:\/\/dweb.link\/ipfs\/?uri=%s\");\n  schemes.predefined_handler_schemes.emplace_back(\n      \"ipns\", \"https:\/\/dweb.link\/ipns\/?uri=%s\");\n<\/pre>\n<h2>Conclusion<\/h2>\n<p>The pre-defined handlers allow us to introduce in the browser a custom behavior for specific schemes.<\/p>\n<p>Unlike the <em>registerProtocolHandler<\/em> method, we could bypass some if the <a href=\"https:\/\/html.spec.whatwg.org\/multipage\/system-state.html#normalize-protocol-handler-parameters\">restrictions<\/a> imposed by the <em>HTML API<\/em>, given that in the context of an embedder browser its possible to have <strong>more control<\/strong> on the navigation use cases.<\/p>\n<p>The custom handlers, either using the pre-defined handlers approach or the <em>registerProtocolHandler<\/em> method, is not the ideal way of introducing <em>multi-protocol<\/em> support in the browser. I personally consider it a promising <strong>intermediate step<\/strong>, but a more solid solution must be designed for these use cases of the browser.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a previous post I described some architectural changes I&#8217;ve applied in Chrome to move the Custom Handlers logic into a new component. One of the advantages of this new architecture is an easier extensibility for embedders and less friction with the \/\/chrome layer. Igalia and Protocol Labs has been collaborating for some years already &hellip; <a href=\"https:\/\/blogs.igalia.com\/jfernandez\/2022\/11\/14\/discovering-chromes-pre-defined-custom-handlers\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Discovering Chrome&#8217;s pre-defined <br \/>Custom Handlers<\/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":[1],"tags":[],"class_list":["post-1756","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/posts\/1756","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=1756"}],"version-history":[{"count":83,"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/posts\/1756\/revisions"}],"predecessor-version":[{"id":1855,"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/posts\/1756\/revisions\/1855"}],"wp:attachment":[{"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/media?parent=1756"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/categories?post=1756"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.igalia.com\/jfernandez\/wp-json\/wp\/v2\/tags?post=1756"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}