Browse Source

feat: [extensions] background pages (#21591)

Jeremy Apthorp 5 years ago
parent
commit
8bc0c92137

+ 8 - 0
electron_paks.gni

@@ -1,4 +1,5 @@
 import("//build/config/locales.gni")
+import("//electron/buildflags/buildflags.gni")
 import("//printing/buildflags/buildflags.gni")
 import("//tools/grit/repack.gni")
 import("//ui/base/ui_features.gni")
@@ -87,6 +88,13 @@ template("electron_extra_paks") {
       sources += [ "$root_gen_dir/chrome/print_preview_resources.pak" ]
       deps += [ "//chrome/browser/resources:print_preview_resources" ]
     }
+    if (enable_electron_extensions) {
+      sources += [
+        "$root_gen_dir/extensions/extensions_renderer_resources.pak",
+        "$root_gen_dir/extensions/extensions_resources.pak",
+      ]
+      deps += [ "//extensions:extensions_resources" ]
+    }
   }
 }
 

+ 6 - 0
filenames.gni

@@ -607,6 +607,12 @@ filenames = {
     "shell/browser/extensions/atom_extension_web_contents_observer.h",
     "shell/browser/extensions/atom_navigation_ui_data.cc",
     "shell/browser/extensions/atom_navigation_ui_data.h",
+    "shell/browser/extensions/electron_process_manager_delegate.cc",
+    "shell/browser/extensions/electron_process_manager_delegate.h",
+    "shell/browser/extensions/electron_extensions_api_client.cc",
+    "shell/browser/extensions/electron_extensions_api_client.h",
+    "shell/browser/extensions/electron_messaging_delegate.cc",
+    "shell/browser/extensions/electron_messaging_delegate.h",
     "shell/common/extensions/atom_extensions_api_provider.cc",
     "shell/common/extensions/atom_extensions_api_provider.h",
     "shell/common/extensions/atom_extensions_client.cc",

+ 2 - 1
shell/app/atom_content_client.cc

@@ -16,6 +16,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "content/public/common/content_constants.h"
 #include "electron/buildflags/buildflags.h"
+#include "extensions/common/constants.h"
 #include "ppapi/buildflags/buildflags.h"
 #include "shell/browser/atom_paths.h"
 #include "shell/common/options_switches.h"
@@ -225,7 +226,7 @@ void AtomContentClient::AddAdditionalSchemes(Schemes* schemes) {
                                 &schemes->cors_enabled_schemes);
 
   schemes->service_worker_schemes.emplace_back(url::kFileScheme);
-  schemes->standard_schemes.emplace_back("chrome-extension");
+  schemes->standard_schemes.emplace_back(extensions::kExtensionScheme);
 }
 
 void AtomContentClient::AddPepperPlugins(

+ 11 - 0
shell/app/atom_main_delegate.cc

@@ -19,8 +19,10 @@
 #include "base/mac/bundle_locations.h"
 #include "base/path_service.h"
 #include "chrome/common/chrome_paths.h"
+#include "components/content_settings/core/common/content_settings_pattern.h"
 #include "content/public/common/content_switches.h"
 #include "electron/buildflags/buildflags.h"
+#include "extensions/common/constants.h"
 #include "ipc/ipc_buildflags.h"
 #include "services/service_manager/embedder/switches.h"
 #include "services/service_manager/sandbox/switches.h"
@@ -131,6 +133,11 @@ AtomMainDelegate::AtomMainDelegate() = default;
 
 AtomMainDelegate::~AtomMainDelegate() = default;
 
+const char* const AtomMainDelegate::kNonWildcardDomainNonPortSchemes[] = {
+    extensions::kExtensionScheme};
+const size_t AtomMainDelegate::kNonWildcardDomainNonPortSchemesSize =
+    base::size(kNonWildcardDomainNonPortSchemes);
+
 bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
   auto* command_line = base::CommandLine::ForCurrentProcess();
 
@@ -187,6 +194,10 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
       tracing::TracingSamplerProfiler::CreateOnMainThread();
 
   chrome::RegisterPathProvider();
+#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
+  ContentSettingsPattern::SetNonWildcardDomainNonPortSchemes(
+      kNonWildcardDomainNonPortSchemes, kNonWildcardDomainNonPortSchemesSize);
+#endif
 
 #if defined(OS_MACOSX)
   OverrideChildProcessPath();

+ 2 - 0
shell/app/atom_main_delegate.h

@@ -21,6 +21,8 @@ void LoadResourceBundle(const std::string& locale);
 
 class AtomMainDelegate : public content::ContentMainDelegate {
  public:
+  static const char* const kNonWildcardDomainNonPortSchemes[];
+  static const size_t kNonWildcardDomainNonPortSchemesSize;
   AtomMainDelegate();
   ~AtomMainDelegate() override;
 

+ 4 - 0
shell/browser/api/atom_api_web_contents.cc

@@ -358,6 +358,10 @@ WebContents::WebContents(v8::Isolate* isolate,
   Init(isolate);
   AttachAsUserData(web_contents);
   InitZoomController(web_contents, gin::Dictionary::CreateEmpty(isolate));
+#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
+  extensions::AtomExtensionWebContentsObserver::CreateForWebContents(
+      web_contents);
+#endif
   registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser,
                                              base::Unretained(this)));
   bindings_.set_connection_error_handler(base::BindRepeating(

+ 84 - 2
shell/browser/atom_browser_client.cc

@@ -27,6 +27,7 @@
 #include "chrome/common/chrome_version.h"
 #include "components/net_log/chrome_net_log.h"
 #include "components/network_hints/common/network_hints.mojom.h"
+#include "content/public/browser/browser_main_runner.h"
 #include "content/public/browser/browser_ppapi_host.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/client_certificate_delegate.h"
@@ -43,6 +44,8 @@
 #include "content/public/common/web_preferences.h"
 #include "electron/buildflags/buildflags.h"
 #include "electron/grit/electron_resources.h"
+#include "extensions/browser/extension_protocols.h"
+#include "extensions/common/constants.h"
 #include "net/base/escape.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "ppapi/host/ppapi_host.h"
@@ -123,6 +126,16 @@
 #include "chrome/browser/printing/printing_message_filter.h"
 #endif  // BUILDFLAG(ENABLE_PRINTING)
 
+#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
+#include "extensions/browser/extension_message_filter.h"
+#include "extensions/browser/extension_navigation_throttle.h"
+#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/info_map.h"
+#include "extensions/browser/process_map.h"
+#include "extensions/common/extension.h"
+#include "shell/browser/extensions/atom_extension_system.h"
+#endif
+
 #if defined(OS_MACOSX)
 #include "content/common/mac_helpers.h"
 #include "content/public/common/child_process_host.h"
@@ -226,6 +239,8 @@ bool AtomBrowserClient::ShouldForceNewSiteInstance(
   if (url.SchemeIs(url::kJavaScriptScheme))
     // "javacript:" scheme should always use same SiteInstance
     return false;
+  if (url.SchemeIs(extensions::kExtensionScheme))
+    return false;
 
   content::SiteInstance* current_instance = current_rfh->GetSiteInstance();
   content::SiteInstance* speculative_instance =
@@ -359,9 +374,16 @@ void AtomBrowserClient::RenderProcessWillLaunch(
   if (IsProcessObserved(process_id))
     return;
 
+  auto* browser_context = host->GetBrowserContext();
+
 #if BUILDFLAG(ENABLE_PRINTING)
-  host->AddFilter(new printing::PrintingMessageFilter(
-      process_id, host->GetBrowserContext()));
+  host->AddFilter(
+      new printing::PrintingMessageFilter(process_id, browser_context));
+#endif
+
+#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
+  host->AddFilter(
+      new extensions::ExtensionMessageFilter(process_id, browser_context));
 #endif
 
   ProcessPreferences prefs;
@@ -708,6 +730,32 @@ void AtomBrowserClient::GetAdditionalWebUISchemes(
   additional_schemes->push_back(content::kChromeDevToolsScheme);
 }
 
+void AtomBrowserClient::SiteInstanceGotProcess(
+    content::SiteInstance* site_instance) {
+#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
+  auto* browser_context =
+      static_cast<AtomBrowserContext*>(site_instance->GetBrowserContext());
+  extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(browser_context);
+  const extensions::Extension* extension =
+      registry->enabled_extensions().GetExtensionOrAppByURL(
+          site_instance->GetSiteURL());
+  if (!extension)
+    return;
+
+  extensions::ProcessMap::Get(browser_context)
+      ->Insert(extension->id(), site_instance->GetProcess()->GetID(),
+               site_instance->GetId());
+
+  base::PostTask(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(&extensions::InfoMap::RegisterExtensionProcess,
+                     browser_context->extension_system()->info_map(),
+                     extension->id(), site_instance->GetProcess()->GetID(),
+                     site_instance->GetId()));
+#endif  // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
+}
+
 void AtomBrowserClient::SiteInstanceDeleting(
     content::SiteInstance* site_instance) {
   // We are storing weak_ptr, is it fundamental to maintain the map up-to-date
@@ -719,6 +767,34 @@ void AtomBrowserClient::SiteInstanceDeleting(
       break;
     }
   }
+
+#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
+  // Don't do anything if we're shutting down.
+  if (content::BrowserMainRunner::ExitedMainMessageLoop())
+    return;
+
+  auto* browser_context =
+      static_cast<AtomBrowserContext*>(site_instance->GetBrowserContext());
+  // If this isn't an extension renderer there's nothing to do.
+  extensions::ExtensionRegistry* registry =
+      extensions::ExtensionRegistry::Get(browser_context);
+  const extensions::Extension* extension =
+      registry->enabled_extensions().GetExtensionOrAppByURL(
+          site_instance->GetSiteURL());
+  if (!extension)
+    return;
+
+  extensions::ProcessMap::Get(browser_context)
+      ->Remove(extension->id(), site_instance->GetProcess()->GetID(),
+               site_instance->GetId());
+
+  base::PostTask(
+      FROM_HERE, {BrowserThread::IO},
+      base::BindOnce(&extensions::InfoMap::UnregisterExtensionProcess,
+                     browser_context->extension_system()->info_map(),
+                     extension->id(), site_instance->GetProcess()->GetID(),
+                     site_instance->GetId()));
+#endif
 }
 
 std::unique_ptr<net::ClientCertStore> AtomBrowserClient::CreateClientCertStore(
@@ -869,6 +945,12 @@ AtomBrowserClient::CreateThrottlesForNavigation(
     content::NavigationHandle* handle) {
   std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
   throttles.push_back(std::make_unique<AtomNavigationThrottle>(handle));
+
+#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
+  throttles.push_back(
+      std::make_unique<extensions::ExtensionNavigationThrottle>(handle));
+#endif
+
   return throttles;
 }
 

+ 1 - 0
shell/browser/atom_browser_client.h

@@ -219,6 +219,7 @@ class AtomBrowserClient : public content::ContentBrowserClient,
       scoped_refptr<net::HttpResponseHeaders> response_headers,
       bool first_auth_attempt,
       LoginAuthRequiredCallback auth_required_callback) override;
+  void SiteInstanceGotProcess(content::SiteInstance* site_instance) override;
 
   // content::RenderProcessHostObserver:
   void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;

+ 3 - 4
shell/browser/atom_browser_context.cc

@@ -146,14 +146,13 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition,
 AtomBrowserContext::~AtomBrowserContext() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   NotifyWillBeDestroyed(this);
+  // Notify any keyed services of browser context destruction.
+  BrowserContextDependencyManager::GetInstance()->DestroyBrowserContextServices(
+      this);
   ShutdownStoragePartitions();
 
   BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE,
                             std::move(resource_context_));
-
-  // Notify any keyed services of browser context destruction.
-  BrowserContextDependencyManager::GetInstance()->DestroyBrowserContextServices(
-      this);
 }
 
 void AtomBrowserContext::InitPrefs() {

+ 6 - 0
shell/browser/atom_browser_context.h

@@ -141,6 +141,12 @@ class AtomBrowserContext
     return weak_factory_.GetWeakPtr();
   }
 
+#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
+  extensions::AtomExtensionSystem* extension_system() {
+    return extension_system_;
+  }
+#endif
+
  protected:
   AtomBrowserContext(const std::string& partition,
                      bool in_memory,

+ 1 - 0
shell/browser/extensions/atom_extension_loader.cc

@@ -28,6 +28,7 @@ scoped_refptr<const Extension> LoadUnpacked(
   // app_shell only supports unpacked extensions.
   // NOTE: If you add packed extension support consider removing the flag
   // FOLLOW_SYMLINKS_ANYWHERE below. Packed extensions should not have symlinks.
+  // TODO(nornagon): these LOG()s should surface as JS exceptions
   if (!base::DirectoryExists(extension_dir)) {
     LOG(ERROR) << "Extension directory not found: "
                << extension_dir.AsUTF8Unsafe();

+ 6 - 4
shell/browser/extensions/atom_extensions_browser_client.cc

@@ -35,6 +35,8 @@
 // #include "shell/browser/extensions/atom_extensions_browser_api_provider.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
 #include "shell/browser/extensions/atom_navigation_ui_data.h"
+#include "shell/browser/extensions/electron_extensions_api_client.h"
+#include "shell/browser/extensions/electron_process_manager_delegate.h"
 
 using content::BrowserContext;
 using content::BrowserThread;
@@ -42,10 +44,10 @@ using content::BrowserThread;
 namespace electron {
 
 AtomExtensionsBrowserClient::AtomExtensionsBrowserClient()
-    : api_client_(new extensions::ExtensionsAPIClient),
-      // : api_client_(new extensions::AtomExtensionsAPIClient),
+    : api_client_(new extensions::ElectronExtensionsAPIClient),
+      process_manager_delegate_(new extensions::ElectronProcessManagerDelegate),
       extension_cache_(new extensions::NullExtensionCache()) {
-  // app_shell does not have a concept of channel yet, so leave UNKNOWN to
+  // Electron does not have a concept of channel, so leave UNKNOWN to
   // enable all channel-dependent extension APIs.
   extensions::SetCurrentChannel(version_info::Channel::UNKNOWN);
 
@@ -168,7 +170,7 @@ void AtomExtensionsBrowserClient::GetEarlyExtensionPrefsObservers(
 
 extensions::ProcessManagerDelegate*
 AtomExtensionsBrowserClient::GetProcessManagerDelegate() const {
-  return NULL;
+  return process_manager_delegate_.get();
 }
 
 std::unique_ptr<extensions::ExtensionHostDelegate> AtomExtensionsBrowserClient::

+ 5 - 0
shell/browser/extensions/atom_extensions_browser_client.h

@@ -21,6 +21,7 @@ namespace extensions {
 class ExtensionsAPIClient;
 class KioskDelegate;
 class ProcessManagerDelegate;
+class ElectronProcessManagerDelegate;
 class ProcessMap;
 }  // namespace extensions
 
@@ -134,6 +135,10 @@ class AtomExtensionsBrowserClient : public extensions::ExtensionsBrowserClient {
   // Support for extension APIs.
   std::unique_ptr<extensions::ExtensionsAPIClient> api_client_;
 
+  // Support for ProcessManager.
+  std::unique_ptr<extensions::ElectronProcessManagerDelegate>
+      process_manager_delegate_;
+
   // The extension cache used for download and installation.
   std::unique_ptr<extensions::ExtensionCache> extension_cache_;
 

+ 29 - 0
shell/browser/extensions/electron_extensions_api_client.cc

@@ -0,0 +1,29 @@
+// Copyright (c) 2019 Slack Technologies, Inc.
+// Use of this source code is governed by the MIT license that can be
+// found in the LICENSE file.
+
+#include "shell/browser/extensions/electron_extensions_api_client.h"
+
+#include <memory>
+
+#include "shell/browser/extensions/atom_extension_web_contents_observer.h"
+#include "shell/browser/extensions/electron_messaging_delegate.h"
+
+namespace extensions {
+
+ElectronExtensionsAPIClient::ElectronExtensionsAPIClient() = default;
+ElectronExtensionsAPIClient::~ElectronExtensionsAPIClient() = default;
+
+MessagingDelegate* ElectronExtensionsAPIClient::GetMessagingDelegate() {
+  if (!messaging_delegate_)
+    messaging_delegate_ = std::make_unique<ElectronMessagingDelegate>();
+  return messaging_delegate_.get();
+}
+
+void ElectronExtensionsAPIClient::AttachWebContentsHelpers(
+    content::WebContents* web_contents) const {
+  extensions::AtomExtensionWebContentsObserver::CreateForWebContents(
+      web_contents);
+}
+
+}  // namespace extensions

+ 32 - 0
shell/browser/extensions/electron_extensions_api_client.h

@@ -0,0 +1,32 @@
+// Copyright (c) 2019 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_EXTENSIONS_ELECTRON_EXTENSIONS_API_CLIENT_H_
+#define SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSIONS_API_CLIENT_H_
+
+#include <memory>
+
+#include "extensions/browser/api/extensions_api_client.h"
+
+namespace extensions {
+
+class ElectronMessagingDelegate;
+
+class ElectronExtensionsAPIClient : public ExtensionsAPIClient {
+ public:
+  ElectronExtensionsAPIClient();
+  ~ElectronExtensionsAPIClient() override;
+
+  // ExtensionsAPIClient
+  MessagingDelegate* GetMessagingDelegate() override;
+  void AttachWebContentsHelpers(
+      content::WebContents* web_contents) const override;
+
+ private:
+  std::unique_ptr<ElectronMessagingDelegate> messaging_delegate_;
+};
+
+}  // namespace extensions
+
+#endif  // SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSIONS_API_CLIENT_H_

+ 94 - 0
shell/browser/extensions/electron_messaging_delegate.cc

@@ -0,0 +1,94 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shell/browser/extensions/electron_messaging_delegate.h"
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "extensions/browser/api/messaging/extension_message_port.h"
+#include "extensions/browser/api/messaging/native_message_host.h"
+#include "extensions/browser/extension_api_frame_id_map.h"
+#include "extensions/browser/pref_names.h"
+#include "extensions/common/api/messaging/port_id.h"
+#include "extensions/common/extension.h"
+#include "ui/gfx/native_widget_types.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+ElectronMessagingDelegate::ElectronMessagingDelegate() = default;
+ElectronMessagingDelegate::~ElectronMessagingDelegate() = default;
+
+MessagingDelegate::PolicyPermission
+ElectronMessagingDelegate::IsNativeMessagingHostAllowed(
+    content::BrowserContext* browser_context,
+    const std::string& native_host_name) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+  return PolicyPermission::DISALLOW;
+}
+
+std::unique_ptr<base::DictionaryValue>
+ElectronMessagingDelegate::MaybeGetTabInfo(content::WebContents* web_contents) {
+  return nullptr;
+}
+
+content::WebContents* ElectronMessagingDelegate::GetWebContentsByTabId(
+    content::BrowserContext* browser_context,
+    int tab_id) {
+  return nullptr;
+}
+
+std::unique_ptr<MessagePort> ElectronMessagingDelegate::CreateReceiverForTab(
+    base::WeakPtr<MessagePort::ChannelDelegate> channel_delegate,
+    const std::string& extension_id,
+    const PortId& receiver_port_id,
+    content::WebContents* receiver_contents,
+    int receiver_frame_id) {
+  // Frame ID -1 is every frame in the tab.
+  bool include_child_frames = receiver_frame_id == -1;
+  content::RenderFrameHost* receiver_rfh =
+      include_child_frames ? receiver_contents->GetMainFrame()
+                           : ExtensionApiFrameIdMap::GetRenderFrameHostById(
+                                 receiver_contents, receiver_frame_id);
+  if (!receiver_rfh)
+    return nullptr;
+
+  return std::make_unique<ExtensionMessagePort>(
+      channel_delegate, receiver_port_id, extension_id, receiver_rfh,
+      include_child_frames);
+}
+
+std::unique_ptr<MessagePort>
+ElectronMessagingDelegate::CreateReceiverForNativeApp(
+    content::BrowserContext* browser_context,
+    base::WeakPtr<MessagePort::ChannelDelegate> channel_delegate,
+    content::RenderFrameHost* source,
+    const std::string& extension_id,
+    const PortId& receiver_port_id,
+    const std::string& native_app_name,
+    bool allow_user_level,
+    std::string* error_out) {
+  return nullptr;
+}
+
+void ElectronMessagingDelegate::QueryIncognitoConnectability(
+    content::BrowserContext* context,
+    const Extension* target_extension,
+    content::WebContents* source_contents,
+    const GURL& source_url,
+    const base::Callback<void(bool)>& callback) {
+  DCHECK(context->IsOffTheRecord());
+  callback.Run(false);
+}
+
+}  // namespace extensions

+ 58 - 0
shell/browser/extensions/electron_messaging_delegate.h

@@ -0,0 +1,58 @@
+// Copyright (c) 2019 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_EXTENSIONS_ELECTRON_MESSAGING_DELEGATE_H_
+#define SHELL_BROWSER_EXTENSIONS_ELECTRON_MESSAGING_DELEGATE_H_
+
+#include <memory>
+#include <string>
+
+#include "extensions/browser/api/messaging/messaging_delegate.h"
+
+namespace extensions {
+
+// Helper class for Chrome-specific features of the extension messaging API.
+class ElectronMessagingDelegate : public MessagingDelegate {
+ public:
+  ElectronMessagingDelegate();
+  ~ElectronMessagingDelegate() override;
+
+  // MessagingDelegate:
+  PolicyPermission IsNativeMessagingHostAllowed(
+      content::BrowserContext* browser_context,
+      const std::string& native_host_name) override;
+  std::unique_ptr<base::DictionaryValue> MaybeGetTabInfo(
+      content::WebContents* web_contents) override;
+  content::WebContents* GetWebContentsByTabId(
+      content::BrowserContext* browser_context,
+      int tab_id) override;
+  std::unique_ptr<MessagePort> CreateReceiverForTab(
+      base::WeakPtr<MessagePort::ChannelDelegate> channel_delegate,
+      const std::string& extension_id,
+      const PortId& receiver_port_id,
+      content::WebContents* receiver_contents,
+      int receiver_frame_id) override;
+  std::unique_ptr<MessagePort> CreateReceiverForNativeApp(
+      content::BrowserContext* browser_context,
+      base::WeakPtr<MessagePort::ChannelDelegate> channel_delegate,
+      content::RenderFrameHost* source,
+      const std::string& extension_id,
+      const PortId& receiver_port_id,
+      const std::string& native_app_name,
+      bool allow_user_level,
+      std::string* error_out) override;
+  void QueryIncognitoConnectability(
+      content::BrowserContext* context,
+      const Extension* extension,
+      content::WebContents* web_contents,
+      const GURL& url,
+      const base::Callback<void(bool)>& callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ElectronMessagingDelegate);
+};
+
+}  // namespace extensions
+
+#endif  // SHELL_BROWSER_EXTENSIONS_ELECTRON_MESSAGING_DELEGATE_H_

+ 40 - 0
shell/browser/extensions/electron_process_manager_delegate.cc

@@ -0,0 +1,40 @@
+// Copyright 2019 Slack Technologies, Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+#include "base/debug/stack_trace.h"
+
+#include "shell/browser/extensions/electron_process_manager_delegate.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/one_shot_event.h"
+#include "build/build_config.h"
+#include "content/public/browser/notification_service.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/process_manager.h"
+#include "extensions/browser/process_manager_factory.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/permissions/permissions_data.h"
+
+namespace extensions {
+
+ElectronProcessManagerDelegate::ElectronProcessManagerDelegate() = default;
+ElectronProcessManagerDelegate::~ElectronProcessManagerDelegate() = default;
+
+bool ElectronProcessManagerDelegate::AreBackgroundPagesAllowedForContext(
+    content::BrowserContext* context) const {
+  return true;
+}
+
+bool ElectronProcessManagerDelegate::IsExtensionBackgroundPageAllowed(
+    content::BrowserContext* context,
+    const Extension& extension) const {
+  return true;
+}
+
+bool ElectronProcessManagerDelegate::DeferCreatingStartupBackgroundHosts(
+    content::BrowserContext* context) const {
+  return false;
+}
+
+}  // namespace extensions

+ 41 - 0
shell/browser/extensions/electron_process_manager_delegate.h

@@ -0,0 +1,41 @@
+// Copyright 2019 Slack Technologies, Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHELL_BROWSER_EXTENSIONS_ELECTRON_PROCESS_MANAGER_DELEGATE_H_
+#define SHELL_BROWSER_EXTENSIONS_ELECTRON_PROCESS_MANAGER_DELEGATE_H_
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "extensions/browser/process_manager_delegate.h"
+
+class Browser;
+class Profile;
+
+namespace extensions {
+
+// Support for ProcessManager. Controls cases where Electron wishes to disallow
+// extension background pages or defer their creation.
+class ElectronProcessManagerDelegate : public ProcessManagerDelegate {
+ public:
+  ElectronProcessManagerDelegate();
+  ~ElectronProcessManagerDelegate() override;
+
+  // ProcessManagerDelegate implementation:
+  bool AreBackgroundPagesAllowedForContext(
+      content::BrowserContext* context) const override;
+  bool IsExtensionBackgroundPageAllowed(
+      content::BrowserContext* context,
+      const Extension& extension) const override;
+  bool DeferCreatingStartupBackgroundHosts(
+      content::BrowserContext* context) const override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ElectronProcessManagerDelegate);
+};
+
+}  // namespace extensions
+
+#endif  // SHELL_BROWSER_EXTENSIONS_ELECTRON_PROCESS_MANAGER_DELEGATE_H_

+ 18 - 0
spec-main/extensions-spec.ts

@@ -88,6 +88,24 @@ ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome ex
       }
     })
   })
+
+  describe('background pages', () => {
+    it('loads a lazy background page when sending a message', async () => {
+      const customSession = session.fromPartition(`persist:${require('uuid').v4()}`)
+      ;(customSession as any).loadExtension(path.join(fixtures, 'extensions', 'lazy-background-page'))
+      const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true } })
+      try {
+        w.loadURL(url)
+        const [, resp] = await emittedOnce(ipcMain, 'bg-page-message-response')
+        expect(resp.message).to.deep.equal({ some: 'message' })
+        expect(resp.sender.id).to.be.a('string')
+        expect(resp.sender.origin).to.equal(url)
+        expect(resp.sender.url).to.equal(url + '/')
+      } finally {
+        w.destroy()
+      }
+    })
+  })
 })
 
 ifdescribe(!process.electronBinding('features').isExtensionsEnabled())('chrome extensions', () => {

+ 4 - 0
spec-main/fixtures/extensions/lazy-background-page/background.js

@@ -0,0 +1,4 @@
+/* eslint-disable no-undef */
+chrome.runtime.onMessage.addListener((message, sender, reply) => {
+  reply({ message, sender })
+})

+ 6 - 0
spec-main/fixtures/extensions/lazy-background-page/content_script.js

@@ -0,0 +1,6 @@
+/* eslint-disable no-undef */
+chrome.runtime.sendMessage({ some: 'message' }, (response) => {
+  const script = document.createElement('script')
+  script.textContent = `require('electron').ipcRenderer.send('bg-page-message-response', ${JSON.stringify(response)})`
+  document.documentElement.appendChild(script)
+})

+ 16 - 0
spec-main/fixtures/extensions/lazy-background-page/manifest.json

@@ -0,0 +1,16 @@
+{
+  "name": "lazy-background-page",
+  "version": "1.0",
+  "background": {
+    "scripts": ["background.js"],
+    "persistent": false
+  },
+  "content_scripts": [
+    {
+      "matches": ["<all_urls>"],
+      "js": ["content_script.js"],
+      "run_at": "document_start"
+    }
+  ],
+  "manifest_version": 2
+}