diff --git a/electron_paks.gni b/electron_paks.gni index 98e518aca6f..c9b103331d5 100644 --- a/electron_paks.gni +++ b/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" ] + } } } diff --git a/filenames.gni b/filenames.gni index 336b3ad35ed..2b45c3118ad 100644 --- a/filenames.gni +++ b/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", diff --git a/shell/app/atom_content_client.cc b/shell/app/atom_content_client.cc index 7ff1055afb2..c904651b034 100644 --- a/shell/app/atom_content_client.cc +++ b/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( diff --git a/shell/app/atom_main_delegate.cc b/shell/app/atom_main_delegate.cc index 295eb00b4bf..0cf623f9112 100644 --- a/shell/app/atom_main_delegate.cc +++ b/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(); diff --git a/shell/app/atom_main_delegate.h b/shell/app/atom_main_delegate.h index 708d0adcf34..524b54d162d 100644 --- a/shell/app/atom_main_delegate.h +++ b/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; diff --git a/shell/browser/api/atom_api_web_contents.cc b/shell/browser/api/atom_api_web_contents.cc index 2a8b232fcca..3241b38e9b0 100644 --- a/shell/browser/api/atom_api_web_contents.cc +++ b/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( diff --git a/shell/browser/atom_browser_client.cc b/shell/browser/atom_browser_client.cc index 85f55024c66..88498d481ff 100644 --- a/shell/browser/atom_browser_client.cc +++ b/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(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(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 AtomBrowserClient::CreateClientCertStore( @@ -869,6 +945,12 @@ AtomBrowserClient::CreateThrottlesForNavigation( content::NavigationHandle* handle) { std::vector> throttles; throttles.push_back(std::make_unique(handle)); + +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + throttles.push_back( + std::make_unique(handle)); +#endif + return throttles; } diff --git a/shell/browser/atom_browser_client.h b/shell/browser/atom_browser_client.h index 85e6acf27f8..959c1e26783 100644 --- a/shell/browser/atom_browser_client.h +++ b/shell/browser/atom_browser_client.h @@ -219,6 +219,7 @@ class AtomBrowserClient : public content::ContentBrowserClient, scoped_refptr 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; diff --git a/shell/browser/atom_browser_context.cc b/shell/browser/atom_browser_context.cc index e9844f0124e..65f0dc9b1b6 100644 --- a/shell/browser/atom_browser_context.cc +++ b/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() { diff --git a/shell/browser/atom_browser_context.h b/shell/browser/atom_browser_context.h index 0ea9efdedcf..dc1012ce6a7 100644 --- a/shell/browser/atom_browser_context.h +++ b/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, diff --git a/shell/browser/extensions/atom_extension_loader.cc b/shell/browser/extensions/atom_extension_loader.cc index 9bacce1639f..d1e0f95c11f 100644 --- a/shell/browser/extensions/atom_extension_loader.cc +++ b/shell/browser/extensions/atom_extension_loader.cc @@ -28,6 +28,7 @@ scoped_refptr 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(); diff --git a/shell/browser/extensions/atom_extensions_browser_client.cc b/shell/browser/extensions/atom_extensions_browser_client.cc index c3d7ca927cb..39ae7334508 100644 --- a/shell/browser/extensions/atom_extensions_browser_client.cc +++ b/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 AtomExtensionsBrowserClient:: diff --git a/shell/browser/extensions/atom_extensions_browser_client.h b/shell/browser/extensions/atom_extensions_browser_client.h index 400cf6fdef7..210185535b2 100644 --- a/shell/browser/extensions/atom_extensions_browser_client.h +++ b/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 api_client_; + // Support for ProcessManager. + std::unique_ptr + process_manager_delegate_; + // The extension cache used for download and installation. std::unique_ptr extension_cache_; diff --git a/shell/browser/extensions/electron_extensions_api_client.cc b/shell/browser/extensions/electron_extensions_api_client.cc new file mode 100644 index 00000000000..09e7a03126e --- /dev/null +++ b/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 + +#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(); + return messaging_delegate_.get(); +} + +void ElectronExtensionsAPIClient::AttachWebContentsHelpers( + content::WebContents* web_contents) const { + extensions::AtomExtensionWebContentsObserver::CreateForWebContents( + web_contents); +} + +} // namespace extensions diff --git a/shell/browser/extensions/electron_extensions_api_client.h b/shell/browser/extensions/electron_extensions_api_client.h new file mode 100644 index 00000000000..e34c74d95b2 --- /dev/null +++ b/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 + +#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 messaging_delegate_; +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_EXTENSIONS_API_CLIENT_H_ diff --git a/shell/browser/extensions/electron_messaging_delegate.cc b/shell/browser/extensions/electron_messaging_delegate.cc new file mode 100644 index 00000000000..77df80c4799 --- /dev/null +++ b/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 + +#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 +ElectronMessagingDelegate::MaybeGetTabInfo(content::WebContents* web_contents) { + return nullptr; +} + +content::WebContents* ElectronMessagingDelegate::GetWebContentsByTabId( + content::BrowserContext* browser_context, + int tab_id) { + return nullptr; +} + +std::unique_ptr ElectronMessagingDelegate::CreateReceiverForTab( + base::WeakPtr 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( + channel_delegate, receiver_port_id, extension_id, receiver_rfh, + include_child_frames); +} + +std::unique_ptr +ElectronMessagingDelegate::CreateReceiverForNativeApp( + content::BrowserContext* browser_context, + base::WeakPtr 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& callback) { + DCHECK(context->IsOffTheRecord()); + callback.Run(false); +} + +} // namespace extensions diff --git a/shell/browser/extensions/electron_messaging_delegate.h b/shell/browser/extensions/electron_messaging_delegate.h new file mode 100644 index 00000000000..78ba54536f6 --- /dev/null +++ b/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 +#include + +#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 MaybeGetTabInfo( + content::WebContents* web_contents) override; + content::WebContents* GetWebContentsByTabId( + content::BrowserContext* browser_context, + int tab_id) override; + std::unique_ptr CreateReceiverForTab( + base::WeakPtr channel_delegate, + const std::string& extension_id, + const PortId& receiver_port_id, + content::WebContents* receiver_contents, + int receiver_frame_id) override; + std::unique_ptr CreateReceiverForNativeApp( + content::BrowserContext* browser_context, + base::WeakPtr 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& callback) override; + + private: + DISALLOW_COPY_AND_ASSIGN(ElectronMessagingDelegate); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_MESSAGING_DELEGATE_H_ diff --git a/shell/browser/extensions/electron_process_manager_delegate.cc b/shell/browser/extensions/electron_process_manager_delegate.cc new file mode 100644 index 00000000000..db3e9bb027a --- /dev/null +++ b/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 diff --git a/shell/browser/extensions/electron_process_manager_delegate.h b/shell/browser/extensions/electron_process_manager_delegate.h new file mode 100644 index 00000000000..b4e4a6941f3 --- /dev/null +++ b/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_ diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts index dcb2f6db8ce..c362ff6819b 100644 --- a/spec-main/extensions-spec.ts +++ b/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', () => { diff --git a/spec-main/fixtures/extensions/lazy-background-page/background.js b/spec-main/fixtures/extensions/lazy-background-page/background.js new file mode 100644 index 00000000000..a4a75971791 --- /dev/null +++ b/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 }) +}) diff --git a/spec-main/fixtures/extensions/lazy-background-page/content_script.js b/spec-main/fixtures/extensions/lazy-background-page/content_script.js new file mode 100644 index 00000000000..7bf2d9a6f8c --- /dev/null +++ b/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) +}) diff --git a/spec-main/fixtures/extensions/lazy-background-page/manifest.json b/spec-main/fixtures/extensions/lazy-background-page/manifest.json new file mode 100644 index 00000000000..f80e5b92878 --- /dev/null +++ b/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": [""], + "js": ["content_script.js"], + "run_at": "document_start" + } + ], + "manifest_version": 2 +}