Browse Source

refactor: ginify protocol (#22812)

Jeremy Apthorp 5 years ago
parent
commit
e73d5e3db5

+ 2 - 0
filenames.gni

@@ -290,6 +290,8 @@ filenames = {
     "shell/browser/plugins/plugin_utils.h",
     "shell/browser/pref_store_delegate.cc",
     "shell/browser/pref_store_delegate.h",
+    "shell/browser/protocol_registry.cc",
+    "shell/browser/protocol_registry.h",
     "shell/browser/relauncher.cc",
     "shell/browser/relauncher.h",
     "shell/browser/relauncher_linux.cc",

+ 1 - 1
lib/browser/api/protocol.ts

@@ -9,7 +9,7 @@ Object.setPrototypeOf(protocol, new Proxy({}, {
     if (!app.isReady()) return;
 
     const protocol = session.defaultSession!.protocol;
-    if (!Object.prototype.hasOwnProperty.call(Object.getPrototypeOf(protocol), property)) return;
+    if (!Object.prototype.hasOwnProperty.call(protocol, property)) return;
 
     // Returning a native function directly would throw error.
     return (...args: any[]) => (protocol[property as keyof Electron.Protocol] as Function)(...args);

+ 1 - 1
lib/browser/api/session.js

@@ -2,7 +2,7 @@
 
 const { EventEmitter } = require('events');
 const { app, deprecate } = require('electron');
-const { fromPartition, Session, Protocol } = process.electronBinding('session');
+const { fromPartition, Session } = process.electronBinding('session');
 
 // Public API.
 Object.defineProperties(exports, {

+ 37 - 44
shell/browser/api/electron_api_protocol.cc

@@ -10,8 +10,10 @@
 
 #include "base/stl_util.h"
 #include "content/public/browser/child_process_security_policy.h"
+#include "gin/object_template_builder.h"
 #include "shell/browser/browser.h"
 #include "shell/browser/electron_browser_context.h"
+#include "shell/browser/protocol_registry.h"
 #include "shell/common/gin_converters/callback_converter.h"
 #include "shell/common/gin_converters/net_converter.h"
 #include "shell/common/gin_helper/dictionary.h"
@@ -74,6 +76,8 @@ struct Converter<CustomScheme> {
 namespace electron {
 namespace api {
 
+gin::WrapperInfo Protocol::kWrapperInfo = {gin::kEmbedderNativeGin};
+
 std::vector<std::string> GetStandardSchemes() {
   return g_standard_schemes;
 }
@@ -160,59 +164,45 @@ std::string ErrorCodeToString(ProtocolError error) {
 
 }  // namespace
 
-Protocol::Protocol(v8::Isolate* isolate,
-                   ElectronBrowserContext* browser_context) {
-  Init(isolate);
-  AttachAsUserData(browser_context);
-}
+Protocol::Protocol(v8::Isolate* isolate, ProtocolRegistry* protocol_registry)
+    : protocol_registry_(protocol_registry) {}
 
 Protocol::~Protocol() = default;
 
-void Protocol::RegisterURLLoaderFactories(
-    content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories) {
-  for (const auto& it : handlers_) {
-    factories->emplace(it.first, std::make_unique<ElectronURLLoaderFactory>(
-                                     it.second.first, it.second.second));
-  }
-}
-
 ProtocolError Protocol::RegisterProtocol(ProtocolType type,
                                          const std::string& scheme,
                                          const ProtocolHandler& handler) {
-  const bool added = base::TryEmplace(handlers_, scheme, type, handler).second;
+  bool added = protocol_registry_->RegisterProtocol(type, scheme, handler);
   return added ? ProtocolError::OK : ProtocolError::REGISTERED;
 }
 
 void Protocol::UnregisterProtocol(const std::string& scheme,
                                   gin::Arguments* args) {
-  const bool removed = handlers_.erase(scheme) != 0;
-  const auto error =
-      removed ? ProtocolError::OK : ProtocolError::NOT_REGISTERED;
-  HandleOptionalCallback(args, error);
+  bool removed = protocol_registry_->UnregisterProtocol(scheme);
+  HandleOptionalCallback(
+      args, removed ? ProtocolError::OK : ProtocolError::NOT_REGISTERED);
 }
 
 bool Protocol::IsProtocolRegistered(const std::string& scheme) {
-  return base::Contains(handlers_, scheme);
+  return protocol_registry_->IsProtocolRegistered(scheme);
 }
 
 ProtocolError Protocol::InterceptProtocol(ProtocolType type,
                                           const std::string& scheme,
                                           const ProtocolHandler& handler) {
-  const bool added =
-      base::TryEmplace(intercept_handlers_, scheme, type, handler).second;
+  bool added = protocol_registry_->InterceptProtocol(type, scheme, handler);
   return added ? ProtocolError::OK : ProtocolError::INTERCEPTED;
 }
 
 void Protocol::UninterceptProtocol(const std::string& scheme,
                                    gin::Arguments* args) {
-  const bool removed = intercept_handlers_.erase(scheme) != 0;
-  const auto error =
-      removed ? ProtocolError::OK : ProtocolError::NOT_INTERCEPTED;
-  HandleOptionalCallback(args, error);
+  bool removed = protocol_registry_->UninterceptProtocol(scheme);
+  HandleOptionalCallback(
+      args, removed ? ProtocolError::OK : ProtocolError::NOT_INTERCEPTED);
 }
 
 bool Protocol::IsProtocolIntercepted(const std::string& scheme) {
-  return base::Contains(intercept_handlers_, scheme);
+  return protocol_registry_->IsProtocolIntercepted(scheme);
 }
 
 v8::Local<v8::Promise> Protocol::IsProtocolHandled(const std::string& scheme,
@@ -224,21 +214,21 @@ v8::Local<v8::Promise> Protocol::IsProtocolHandled(const std::string& scheme,
               "instead.",
               "ProtocolDeprecateIsProtocolHandled");
   return gin_helper::Promise<bool>::ResolvedPromise(
-      isolate(), IsProtocolRegistered(scheme) ||
-                     IsProtocolIntercepted(scheme) ||
-                     // The |isProtocolHandled| should return true for builtin
-                     // schemes, however with NetworkService it is impossible to
-                     // know which schemes are registered until a real network
-                     // request is sent.
-                     // So we have to test against a hard-coded builtin schemes
-                     // list make it work with old code. We should deprecate
-                     // this API with the new |isProtocolRegistered| API.
-                     base::Contains(kBuiltinSchemes, scheme));
+      args->isolate(),
+      IsProtocolRegistered(scheme) || IsProtocolIntercepted(scheme) ||
+          // The |isProtocolHandled| should return true for builtin
+          // schemes, however with NetworkService it is impossible to
+          // know which schemes are registered until a real network
+          // request is sent.
+          // So we have to test against a hard-coded builtin schemes
+          // list make it work with old code. We should deprecate
+          // this API with the new |isProtocolRegistered| API.
+          base::Contains(kBuiltinSchemes, scheme));
 }
 
 void Protocol::HandleOptionalCallback(gin::Arguments* args,
                                       ProtocolError error) {
-  CompletionCallback callback;
+  base::RepeatingCallback<void(v8::Local<v8::Value>)> callback;
   if (args->GetNext(&callback)) {
     node::Environment* env = node::Environment::GetCurrent(args->isolate());
     EmitWarning(
@@ -249,7 +239,7 @@ void Protocol::HandleOptionalCallback(gin::Arguments* args,
       callback.Run(v8::Null(args->isolate()));
     else
       callback.Run(v8::Exception::Error(
-          gin::StringToV8(isolate(), ErrorCodeToString(error))));
+          gin::StringToV8(args->isolate(), ErrorCodeToString(error))));
   }
 }
 
@@ -257,14 +247,13 @@ void Protocol::HandleOptionalCallback(gin::Arguments* args,
 gin::Handle<Protocol> Protocol::Create(
     v8::Isolate* isolate,
     ElectronBrowserContext* browser_context) {
-  return gin::CreateHandle(isolate, new Protocol(isolate, browser_context));
+  return gin::CreateHandle(
+      isolate, new Protocol(isolate, browser_context->protocol_registry()));
 }
 
-// static
-void Protocol::BuildPrototype(v8::Isolate* isolate,
-                              v8::Local<v8::FunctionTemplate> prototype) {
-  prototype->SetClassName(gin::StringToV8(isolate, "Protocol"));
-  gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
+gin::ObjectTemplateBuilder Protocol::GetObjectTemplateBuilder(
+    v8::Isolate* isolate) {
+  return gin::Wrappable<Protocol>::GetObjectTemplateBuilder(isolate)
       .SetMethod("registerStringProtocol",
                  &Protocol::RegisterProtocolFor<ProtocolType::kString>)
       .SetMethod("registerBufferProtocol",
@@ -296,6 +285,10 @@ void Protocol::BuildPrototype(v8::Isolate* isolate,
       .SetMethod("isProtocolIntercepted", &Protocol::IsProtocolIntercepted);
 }
 
+const char* Protocol::GetTypeName() {
+  return "Protocol";
+}
+
 }  // namespace api
 }  // namespace electron
 

+ 12 - 14
shell/browser/api/electron_api_protocol.h

@@ -10,13 +10,13 @@
 
 #include "content/public/browser/content_browser_client.h"
 #include "gin/handle.h"
+#include "gin/wrappable.h"
 #include "shell/browser/net/electron_url_loader_factory.h"
-#include "shell/common/gin_helper/dictionary.h"
-#include "shell/common/gin_helper/trackable_object.h"
 
 namespace electron {
 
 class ElectronBrowserContext;
+class ProtocolRegistry;
 
 namespace api {
 
@@ -35,22 +35,19 @@ enum class ProtocolError {
 };
 
 // Protocol implementation based on network services.
-class Protocol : public gin_helper::TrackableObject<Protocol> {
+class Protocol : public gin::Wrappable<Protocol> {
  public:
   static gin::Handle<Protocol> Create(v8::Isolate* isolate,
                                       ElectronBrowserContext* browser_context);
 
-  static void BuildPrototype(v8::Isolate* isolate,
-                             v8::Local<v8::FunctionTemplate> prototype);
-
-  // Used by ElectronBrowserClient for creating URLLoaderFactory.
-  void RegisterURLLoaderFactories(
-      content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories);
-
-  const HandlersMap& intercept_handlers() const { return intercept_handlers_; }
+  // gin::Wrappable
+  static gin::WrapperInfo kWrapperInfo;
+  gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
+      v8::Isolate* isolate) override;
+  const char* GetTypeName() override;
 
  private:
-  Protocol(v8::Isolate* isolate, ElectronBrowserContext* browser_context);
+  Protocol(v8::Isolate* isolate, ProtocolRegistry* protocol_registry);
   ~Protocol() override;
 
   // Callback types.
@@ -91,8 +88,9 @@ class Protocol : public gin_helper::TrackableObject<Protocol> {
   // Be compatible with old interface, which accepts optional callback.
   void HandleOptionalCallback(gin::Arguments* args, ProtocolError error);
 
-  HandlersMap handlers_;
-  HandlersMap intercept_handlers_;
+  // Weak pointer; the lifetime of the ProtocolRegistry is guaranteed to be
+  // longer than the lifetime of this JS interface.
+  ProtocolRegistry* protocol_registry_;
 };
 
 }  // namespace api

+ 6 - 31
shell/browser/api/electron_api_session.cc

@@ -209,24 +209,6 @@ void DownloadIdCallback(content::DownloadManager* download_manager,
       false, std::vector<download::DownloadItem::ReceivedSlice>());
 }
 
-void DestroyGlobalHandle(v8::Isolate* isolate,
-                         const v8::Global<v8::Value>& global_handle) {
-  v8::Locker locker(isolate);
-  v8::HandleScope handle_scope(isolate);
-  if (!global_handle.IsEmpty()) {
-    v8::Local<v8::Value> local_handle = global_handle.Get(isolate);
-    v8::Local<v8::Object> object;
-    if (local_handle->IsObject() &&
-        local_handle->ToObject(isolate->GetCurrentContext()).ToLocal(&object)) {
-      void* ptr = object->GetAlignedPointerFromInternalField(0);
-      if (!ptr)
-        return;
-      delete static_cast<gin_helper::WrappableBase*>(ptr);
-      object->SetAlignedPointerInInternalField(0, nullptr);
-    }
-  }
-}
-
 #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER)
 class DictionaryObserver final : public SpellcheckCustomDictionary::Observer {
  private:
@@ -302,10 +284,6 @@ Session::~Session() {
   }
 #endif
 
-  // TODO(zcbenz): Now since URLRequestContextGetter is gone, is this still
-  // needed?
-  // Refs https://github.com/electron/electron/pull/12305.
-  DestroyGlobalHandle(isolate(), protocol_);
   g_sessions.erase(weak_map_id());
 }
 
@@ -750,11 +728,11 @@ v8::Local<v8::Value> Session::Cookies(v8::Isolate* isolate) {
     auto handle = Cookies::Create(isolate, browser_context());
     cookies_.Reset(isolate, handle.ToV8());
   }
-  return v8::Local<v8::Value>::New(isolate, cookies_);
+  return cookies_.Get(isolate);
 }
 
 v8::Local<v8::Value> Session::Protocol(v8::Isolate* isolate) {
-  return v8::Local<v8::Value>::New(isolate, protocol_);
+  return protocol_.Get(isolate);
 }
 
 v8::Local<v8::Value> Session::ServiceWorkerContext(v8::Isolate* isolate) {
@@ -763,7 +741,7 @@ v8::Local<v8::Value> Session::ServiceWorkerContext(v8::Isolate* isolate) {
     handle = ServiceWorkerContext::Create(isolate, browser_context()).ToV8();
     service_worker_context_.Reset(isolate, handle);
   }
-  return v8::Local<v8::Value>::New(isolate, service_worker_context_);
+  return service_worker_context_.Get(isolate);
 }
 
 v8::Local<v8::Value> Session::WebRequest(v8::Isolate* isolate) {
@@ -771,15 +749,15 @@ v8::Local<v8::Value> Session::WebRequest(v8::Isolate* isolate) {
     auto handle = WebRequest::Create(isolate, browser_context());
     web_request_.Reset(isolate, handle.ToV8());
   }
-  return v8::Local<v8::Value>::New(isolate, web_request_);
+  return web_request_.Get(isolate);
 }
 
 v8::Local<v8::Value> Session::NetLog(v8::Isolate* isolate) {
   if (net_log_.IsEmpty()) {
-    auto handle = electron::api::NetLog::Create(isolate, browser_context());
+    auto handle = NetLog::Create(isolate, browser_context());
     net_log_.Reset(isolate, handle.ToV8());
   }
-  return v8::Local<v8::Value>::New(isolate, net_log_);
+  return net_log_.Get(isolate);
 }
 
 static void StartPreconnectOnUI(
@@ -1052,9 +1030,6 @@ void Initialize(v8::Local<v8::Object> exports,
   dict.Set(
       "Session",
       Session::GetConstructor(isolate)->GetFunction(context).ToLocalChecked());
-  dict.Set(
-      "Protocol",
-      Protocol::GetConstructor(isolate)->GetFunction(context).ToLocalChecked());
   dict.SetMethod("fromPartition", &FromPartition);
 }
 

+ 0 - 2
shell/browser/api/electron_api_session.h

@@ -140,8 +140,6 @@ class Session : public gin_helper::TrackableObject<Session>,
   v8::Global<v8::Value> protocol_;
   v8::Global<v8::Value> net_log_;
   v8::Global<v8::Value> service_worker_context_;
-
-  // Cached object.
   v8::Global<v8::Value> web_request_;
 
   // The client id to enable the network throttler.

+ 14 - 18
shell/browser/electron_browser_client.cc

@@ -81,6 +81,7 @@
 #include "shell/browser/network_hints_handler_impl.h"
 #include "shell/browser/notifications/notification_presenter.h"
 #include "shell/browser/notifications/platform_notification_service.h"
+#include "shell/browser/protocol_registry.h"
 #include "shell/browser/session_preferences.h"
 #include "shell/browser/ui/devtools_manager_delegate.h"
 #include "shell/browser/web_contents_permission_helper.h"
@@ -1162,17 +1163,15 @@ void ElectronBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories(
     NonNetworkURLLoaderFactoryMap* factories) {
   content::WebContents* web_contents =
       content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
-  api::Protocol* protocol = api::Protocol::FromWrappedClass(
-      v8::Isolate::GetCurrent(), web_contents->GetBrowserContext());
+  content::BrowserContext* context = web_contents->GetBrowserContext();
 #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
   factories->emplace(
       extensions::kExtensionScheme,
       extensions::CreateExtensionNavigationURLLoaderFactory(
-          web_contents->GetBrowserContext(),
-          false /* we don't support extensions::WebViewGuest */));
+          context, false /* we don't support extensions::WebViewGuest */));
 #endif
-  if (protocol)
-    protocol->RegisterURLLoaderFactories(factories);
+  auto* protocol_registry = ProtocolRegistry::FromBrowserContext(context);
+  protocol_registry->RegisterURLLoaderFactories(factories);
 }
 
 #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
@@ -1231,10 +1230,8 @@ void ElectronBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories(
       content::WebContents::FromRenderFrameHost(frame_host);
 
   if (web_contents) {
-    api::Protocol* protocol = api::Protocol::FromWrappedClass(
-        v8::Isolate::GetCurrent(), web_contents->GetBrowserContext());
-    if (protocol)
-      protocol->RegisterURLLoaderFactories(factories);
+    ProtocolRegistry::FromBrowserContext(web_contents->GetBrowserContext())
+        ->RegisterURLLoaderFactories(factories);
   }
 #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
   auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id,
@@ -1348,9 +1345,6 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory(
     network::mojom::URLLoaderFactoryOverridePtr* factory_override) {
   v8::Isolate* isolate = v8::Isolate::GetCurrent();
   v8::HandleScope scope(isolate);
-  api::Protocol* protocol =
-      api::Protocol::FromWrappedClass(isolate, browser_context);
-  DCHECK(protocol);
   auto web_request = api::WebRequest::FromOrCreate(isolate, browser_context);
   DCHECK(web_request.get());
 
@@ -1374,12 +1368,14 @@ bool ElectronBrowserClient::WillCreateURLLoaderFactory(
   if (header_client)
     header_client_receiver = header_client->InitWithNewPipeAndPassReceiver();
 
+  auto* protocol_registry =
+      ProtocolRegistry::FromBrowserContext(browser_context);
   new ProxyingURLLoaderFactory(
-      web_request.get(), protocol->intercept_handlers(), browser_context,
-      render_process_id, &next_id_, std::move(navigation_ui_data),
-      std::move(navigation_id), std::move(proxied_receiver),
-      std::move(target_factory_remote), std::move(header_client_receiver),
-      type);
+      web_request.get(), protocol_registry->intercept_handlers(),
+      browser_context, render_process_id, &next_id_,
+      std::move(navigation_ui_data), std::move(navigation_id),
+      std::move(proxied_receiver), std::move(target_factory_remote),
+      std::move(header_client_receiver), type);
 
   if (bypass_redirect_checks)
     *bypass_redirect_checks = true;

+ 2 - 0
shell/browser/electron_browser_context.cc

@@ -42,6 +42,7 @@
 #include "shell/browser/electron_permission_manager.h"
 #include "shell/browser/net/resolve_proxy_helper.h"
 #include "shell/browser/pref_store_delegate.h"
+#include "shell/browser/protocol_registry.h"
 #include "shell/browser/special_storage_policy.h"
 #include "shell/browser/ui/inspectable_web_contents_impl.h"
 #include "shell/browser/web_view_manager.h"
@@ -100,6 +101,7 @@ ElectronBrowserContext::ElectronBrowserContext(const std::string& partition,
           base::ThreadTaskRunnerHandle::Get()),
       in_memory_pref_store_(nullptr),
       storage_policy_(new SpecialStoragePolicy),
+      protocol_registry_(new ProtocolRegistry),
       in_memory_(in_memory),
       weak_factory_(this) {
   // TODO(nornagon): remove once https://crbug.com/1048822 is fixed.

+ 6 - 1
shell/browser/electron_browser_context.h

@@ -47,6 +47,7 @@ class CookieChangeNotifier;
 class ResolveProxyHelper;
 class SpecialStoragePolicy;
 class WebViewManager;
+class ProtocolRegistry;
 
 class ElectronBrowserContext
     : public base::RefCountedDeleteOnSequence<ElectronBrowserContext>,
@@ -143,6 +144,10 @@ class ElectronBrowserContext
   }
 #endif
 
+  ProtocolRegistry* protocol_registry() const {
+    return protocol_registry_.get();
+  }
+
  protected:
   ElectronBrowserContext(const std::string& partition,
                          bool in_memory,
@@ -173,8 +178,8 @@ class ElectronBrowserContext
   std::unique_ptr<MediaDeviceIDSalt> media_device_id_salt_;
   scoped_refptr<ResolveProxyHelper> resolve_proxy_helper_;
   scoped_refptr<storage::SpecialStoragePolicy> storage_policy_;
-
   std::unique_ptr<predictors::PreconnectManager> preconnect_manager_;
+  std::unique_ptr<ProtocolRegistry> protocol_registry_;
 
   std::string user_agent_;
   base::FilePath path_;

+ 58 - 0
shell/browser/protocol_registry.cc

@@ -0,0 +1,58 @@
+// Copyright (c) 2020 Slack Technologies, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include <memory>
+
+#include "shell/browser/electron_browser_context.h"
+#include "shell/browser/protocol_registry.h"
+
+namespace electron {
+
+// static
+ProtocolRegistry* ProtocolRegistry::FromBrowserContext(
+    content::BrowserContext* context) {
+  return static_cast<ElectronBrowserContext*>(context)->protocol_registry();
+}
+
+ProtocolRegistry::ProtocolRegistry() {}
+
+ProtocolRegistry::~ProtocolRegistry() = default;
+
+void ProtocolRegistry::RegisterURLLoaderFactories(
+    content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories) {
+  for (const auto& it : handlers_) {
+    factories->emplace(it.first, std::make_unique<ElectronURLLoaderFactory>(
+                                     it.second.first, it.second.second));
+  }
+}
+
+bool ProtocolRegistry::RegisterProtocol(ProtocolType type,
+                                        const std::string& scheme,
+                                        const ProtocolHandler& handler) {
+  return base::TryEmplace(handlers_, scheme, type, handler).second;
+}
+
+bool ProtocolRegistry::UnregisterProtocol(const std::string& scheme) {
+  return handlers_.erase(scheme) != 0;
+}
+
+bool ProtocolRegistry::IsProtocolRegistered(const std::string& scheme) {
+  return base::Contains(handlers_, scheme);
+}
+
+bool ProtocolRegistry::InterceptProtocol(ProtocolType type,
+                                         const std::string& scheme,
+                                         const ProtocolHandler& handler) {
+  return base::TryEmplace(intercept_handlers_, scheme, type, handler).second;
+}
+
+bool ProtocolRegistry::UninterceptProtocol(const std::string& scheme) {
+  return intercept_handlers_.erase(scheme) != 0;
+}
+
+bool ProtocolRegistry::IsProtocolIntercepted(const std::string& scheme) {
+  return base::Contains(intercept_handlers_, scheme);
+}
+
+}  // namespace electron

+ 53 - 0
shell/browser/protocol_registry.h

@@ -0,0 +1,53 @@
+// Copyright (c) 2020 Slack Technologies, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_BROWSER_PROTOCOL_REGISTRY_H_
+#define SHELL_BROWSER_PROTOCOL_REGISTRY_H_
+
+#include <string>
+
+#include "content/public/browser/content_browser_client.h"
+#include "shell/browser/net/electron_url_loader_factory.h"
+
+namespace content {
+class BrowserContext;
+}  // namespace content
+
+namespace electron {
+
+class ProtocolRegistry {
+ public:
+  ~ProtocolRegistry();
+
+  static ProtocolRegistry* FromBrowserContext(content::BrowserContext*);
+
+  void RegisterURLLoaderFactories(
+      content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories);
+
+  const HandlersMap& intercept_handlers() const { return intercept_handlers_; }
+
+  bool RegisterProtocol(ProtocolType type,
+                        const std::string& scheme,
+                        const ProtocolHandler& handler);
+  bool UnregisterProtocol(const std::string& scheme);
+  bool IsProtocolRegistered(const std::string& scheme);
+
+  bool InterceptProtocol(ProtocolType type,
+                         const std::string& scheme,
+                         const ProtocolHandler& handler);
+  bool UninterceptProtocol(const std::string& scheme);
+  bool IsProtocolIntercepted(const std::string& scheme);
+
+ private:
+  friend class ElectronBrowserContext;
+
+  ProtocolRegistry();
+
+  HandlersMap handlers_;
+  HandlersMap intercept_handlers_;
+};
+
+}  // namespace electron
+
+#endif  // SHELL_BROWSER_PROTOCOL_REGISTRY_H_