diff --git a/BUILD.gn b/BUILD.gn index 8a9f3ca47d09..425efe604a91 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -627,6 +627,20 @@ source_set("electron_lib") { if (enable_pepper_flash) { deps += [ "components/pepper_flash" ] } + + public_deps += [ "shell/common/extensions/api:extensions_features" ] + deps += [ + "//components/pref_registry", + "//components/user_prefs", + "//extensions/browser", + "//extensions/browser:core_api_provider", + "//extensions/common", + "//extensions/common:core_api_provider", + "//extensions/renderer", + ] + if (enable_electron_extensions) { + sources += filenames.lib_sources_extensions + } } electron_paks("packed_resources") { diff --git a/buildflags/BUILD.gn b/buildflags/BUILD.gn index 73e4c1f22f5a..359a0609415f 100644 --- a/buildflags/BUILD.gn +++ b/buildflags/BUILD.gn @@ -17,6 +17,7 @@ buildflag_header("buildflags") { "ENABLE_PDF_VIEWER=$enable_pdf_viewer", "ENABLE_TTS=$enable_tts", "ENABLE_COLOR_CHOOSER=$enable_color_chooser", + "ENABLE_ELECTRON_EXTENSIONS=$enable_electron_extensions", "OVERRIDE_LOCATION_PROVIDER=$enable_fake_location_provider", ] } diff --git a/buildflags/buildflags.gni b/buildflags/buildflags.gni index 2425b927b389..86c25bdca422 100644 --- a/buildflags/buildflags.gni +++ b/buildflags/buildflags.gni @@ -26,4 +26,7 @@ declare_args() { # Enable flash plugin support. enable_pepper_flash = true + + # Enable Chrome extensions support. + enable_electron_extensions = false } diff --git a/filenames.gni b/filenames.gni index c1fad555ef46..445363e5aae2 100644 --- a/filenames.gni +++ b/filenames.gni @@ -604,6 +604,35 @@ filenames = { "chromium_src/chrome/browser/certificate_manager_model.h", ] + lib_sources_extensions = [ + "shell/browser/extensions/api/runtime/atom_runtime_api_delegate.cc", + "shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h", + "shell/browser/extensions/atom_extensions_browser_client.cc", + "shell/browser/extensions/atom_extensions_browser_client.h", + "shell/browser/extensions/atom_browser_context_keyed_service_factories.cc", + "shell/browser/extensions/atom_browser_context_keyed_service_factories.h", + "shell/browser/extensions/atom_display_info_provider.cc", + "shell/browser/extensions/atom_display_info_provider.h", + "shell/browser/extensions/atom_extension_host_delegate.cc", + "shell/browser/extensions/atom_extension_host_delegate.h", + "shell/browser/extensions/atom_extension_loader.cc", + "shell/browser/extensions/atom_extension_loader.h", + "shell/browser/extensions/atom_extension_system.cc", + "shell/browser/extensions/atom_extension_system.h", + "shell/browser/extensions/atom_extension_system_factory.cc", + "shell/browser/extensions/atom_extension_system_factory.h", + "shell/browser/extensions/atom_extension_web_contents_observer.cc", + "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/common/extensions/atom_extensions_api_provider.cc", + "shell/common/extensions/atom_extensions_api_provider.h", + "shell/common/extensions/atom_extensions_client.cc", + "shell/common/extensions/atom_extensions_client.h", + "shell/renderer/extensions/atom_extensions_renderer_client.cc", + "shell/renderer/extensions/atom_extensions_renderer_client.h", + ] + app_sources = [ "shell/app/atom_main.cc", "shell/app/atom_main.h", diff --git a/shell/browser/api/atom_api_session.cc b/shell/browser/api/atom_api_session.cc index ececeb94b8e3..38422a3072ce 100644 --- a/shell/browser/api/atom_api_session.cc +++ b/shell/browser/api/atom_api_session.cc @@ -67,6 +67,10 @@ #include "shell/common/options_switches.h" #include "ui/base/l10n/l10n_util.h" +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#include "shell/browser/extensions/atom_extension_system.h" +#endif + using content::BrowserThread; using content::StoragePartition; @@ -638,6 +642,14 @@ std::vector Session::GetPreloads() const { return prefs->preloads(); } +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +void Session::LoadChromeExtension(const base::FilePath extension_path) { + auto* extension_system = static_cast( + extensions::ExtensionSystem::Get(browser_context())); + extension_system->LoadExtension(extension_path); +} +#endif + v8::Local Session::Cookies(v8::Isolate* isolate) { if (cookies_.IsEmpty()) { auto handle = Cookies::Create(isolate, browser_context()); @@ -745,6 +757,9 @@ void Session::BuildPrototype(v8::Isolate* isolate, &Session::CreateInterruptedDownload) .SetMethod("setPreloads", &Session::SetPreloads) .SetMethod("getPreloads", &Session::GetPreloads) +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + .SetMethod("loadChromeExtension", &Session::LoadChromeExtension) +#endif .SetProperty("cookies", &Session::Cookies) .SetProperty("netLog", &Session::NetLog) .SetProperty("protocol", &Session::Protocol) diff --git a/shell/browser/api/atom_api_session.h b/shell/browser/api/atom_api_session.h index 2332adeeb3c0..83b0bda589a7 100644 --- a/shell/browser/api/atom_api_session.h +++ b/shell/browser/api/atom_api_session.h @@ -10,6 +10,7 @@ #include "base/values.h" #include "content/public/browser/download_manager.h" +#include "electron/buildflags/buildflags.h" #include "native_mate/handle.h" #include "shell/browser/api/trackable_object.h" #include "shell/browser/atom_blob_reader.h" @@ -86,6 +87,10 @@ class Session : public mate::TrackableObject, v8::Local WebRequest(v8::Isolate* isolate); v8::Local NetLog(v8::Isolate* isolate); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + void LoadChromeExtension(const base::FilePath extension_path); +#endif + protected: Session(v8::Isolate* isolate, AtomBrowserContext* browser_context); ~Session() override; diff --git a/shell/browser/api/atom_api_web_contents.cc b/shell/browser/api/atom_api_web_contents.cc index 47412e5788fa..cc84fad406e6 100644 --- a/shell/browser/api/atom_api_web_contents.cc +++ b/shell/browser/api/atom_api_web_contents.cc @@ -41,6 +41,7 @@ #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "content/public/common/context_menu_params.h" +#include "electron/buildflags/buildflags.h" #include "electron/shell/common/api/api.mojom.h" #include "mojo/public/cpp/system/platform_handle.h" #include "native_mate/converter.h" @@ -112,6 +113,10 @@ #include "components/printing/common/print_messages.h" #endif +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#include "shell/browser/extensions/atom_extension_web_contents_observer.h" +#endif + namespace mate { #if BUILDFLAG(ENABLE_PRINTING) @@ -456,12 +461,13 @@ void WebContents::InitWithSessionAndOptions( // Save the preferences in C++. new WebContentsPreferences(web_contents(), options); - // Initialize permission helper. WebContentsPermissionHelper::CreateForWebContents(web_contents()); - // Initialize security state client. SecurityStateTabHelper::CreateForWebContents(web_contents()); - // Initialize zoom controller. InitZoomController(web_contents(), options); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + extensions::AtomExtensionWebContentsObserver::CreateForWebContents( + web_contents()); +#endif registry_.AddInterface(base::BindRepeating(&WebContents::BindElectronBrowser, base::Unretained(this))); diff --git a/shell/browser/atom_browser_context.cc b/shell/browser/atom_browser_context.cc index d165f593e0bf..f91c9b836e7b 100644 --- a/shell/browser/atom_browser_context.cc +++ b/shell/browser/atom_browser_context.cc @@ -10,6 +10,7 @@ #include "base/files/file_path.h" #include "base/path_service.h" #include "base/strings/string_util.h" +#include "base/task/post_task.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/threading/thread_restrictions.h" #include "chrome/common/chrome_paths.h" @@ -43,6 +44,22 @@ #include "shell/common/application_info.h" #include "shell/common/options_switches.h" +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/user_prefs/user_prefs.h" +#include "extensions/browser/browser_context_keyed_service_factories.h" +#include "extensions/browser/extension_pref_store.h" +#include "extensions/browser/extension_pref_value_map_factory.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/pref_names.h" +#include "extensions/common/extension_api.h" +#include "shell/browser/extensions/atom_browser_context_keyed_service_factories.h" +#include "shell/browser/extensions/atom_extension_system.h" +#include "shell/browser/extensions/atom_extension_system_factory.h" +#include "shell/browser/extensions/atom_extensions_browser_client.h" +#include "shell/common/extensions/atom_extensions_client.h" +#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + using content::BrowserThread; namespace electron { @@ -91,6 +108,8 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition, content::BrowserContext::Initialize(this, path_); + BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(this); + // Initialize Pref Registry. InitPrefs(); @@ -102,7 +121,15 @@ AtomBrowserContext::AtomBrowserContext(const std::string& partition, cookie_change_notifier_ = std::make_unique(this); - BrowserContextDependencyManager::GetInstance()->MarkBrowserContextLive(this); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + BrowserContextDependencyManager::GetInstance()->CreateBrowserContextServices( + this); + + extension_system_ = static_cast( + extensions::ExtensionSystem::Get(this)); + extension_system_->InitForRegularProfile(true /* extensions_enabled */); + extension_system_->FinishInitialization(); +#endif } AtomBrowserContext::~AtomBrowserContext() { @@ -131,7 +158,16 @@ void AtomBrowserContext::InitPrefs() { pref_store->ReadPrefs(); // Synchronous. prefs_factory.set_user_prefs(pref_store); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + auto* ext_pref_store = new ExtensionPrefStore( + ExtensionPrefValueMapFactory::GetForBrowserContext(this), + IsOffTheRecord()); + prefs_factory.set_extension_prefs(ext_pref_store); + + auto registry = WrapRefCounted(new user_prefs::PrefRegistrySyncable); +#else auto registry = WrapRefCounted(new PrefRegistrySimple); +#endif registry->RegisterFilePathPref(prefs::kSelectFileLastDirectory, base::FilePath()); @@ -144,11 +180,17 @@ void AtomBrowserContext::InitPrefs() { MediaDeviceIDSalt::RegisterPrefs(registry.get()); ZoomLevelDelegate::RegisterPrefs(registry.get()); PrefProxyConfigTrackerImpl::RegisterPrefs(registry.get()); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + extensions::ExtensionPrefs::RegisterProfilePrefs(registry.get()); +#endif prefs_ = prefs_factory.Create( registry.get(), std::make_unique(weak_factory_.GetWeakPtr())); prefs_->UpdateCommandLinePrefStore(new ValueMapPrefStore); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + user_prefs::UserPrefs::Set(this, prefs_.get()); +#endif } void AtomBrowserContext::SetUserAgent(const std::string& user_agent) { @@ -305,6 +347,16 @@ AtomBrowserContext::GetClientHintsControllerDelegate() { return nullptr; } +void AtomBrowserContext::SetCorsOriginAccessListForOrigin( + const url::Origin& source_origin, + std::vector allow_patterns, + std::vector block_patterns, + base::OnceClosure closure) { + // TODO(nornagon): actually set the CORS access lists. This is called from + // extensions/browser/renderer_startup_helper.cc. + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(closure)); +} + ResolveProxyHelper* AtomBrowserContext::GetResolveProxyHelper() { if (!resolve_proxy_helper_) { resolve_proxy_helper_ = base::MakeRefCounted(this); diff --git a/shell/browser/atom_browser_context.h b/shell/browser/atom_browser_context.h index 322e1cc7389d..591c5490dee7 100644 --- a/shell/browser/atom_browser_context.h +++ b/shell/browser/atom_browser_context.h @@ -15,6 +15,7 @@ #include "chrome/browser/net/proxy_config_monitor.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/resource_context.h" +#include "electron/buildflags/buildflags.h" #include "shell/browser/media/media_device_id_salt.h" #include "shell/browser/net/url_request_context_getter.h" @@ -26,6 +27,12 @@ namespace storage { class SpecialStoragePolicy; } +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +namespace extensions { +class AtomExtensionSystem; +} +#endif + namespace electron { class AtomBlobReader; @@ -41,6 +48,27 @@ class AtomBrowserContext : public base::RefCountedDeleteOnSequence, public content::BrowserContext { public: + // partition_id => browser_context + struct PartitionKey { + std::string partition; + bool in_memory; + + PartitionKey(const std::string& partition, bool in_memory) + : partition(partition), in_memory(in_memory) {} + + bool operator<(const PartitionKey& other) const { + if (partition == other.partition) + return in_memory < other.in_memory; + return partition < other.partition; + } + + bool operator==(const PartitionKey& other) const { + return (partition == other.partition) && (in_memory == other.in_memory); + } + }; + using BrowserContextMap = + std::map>; + // Get or create the BrowserContext according to its |partition| and // |in_memory|. The |options| will be passed to constructor when there is no // existing BrowserContext. @@ -49,6 +77,10 @@ class AtomBrowserContext bool in_memory, const base::DictionaryValue& options = base::DictionaryValue()); + static BrowserContextMap browser_context_map() { + return browser_context_map_; + } + void SetUserAgent(const std::string& user_agent); std::string GetUserAgent() const; bool CanUseHttpCache() const; @@ -84,6 +116,13 @@ class AtomBrowserContext content::ClientHintsControllerDelegate* GetClientHintsControllerDelegate() override; + // extensions deps + void SetCorsOriginAccessListForOrigin( + const url::Origin& source_origin, + std::vector allow_patterns, + std::vector block_patterns, + base::OnceClosure closure) override; + CookieChangeNotifier* cookie_change_notifier() const { return cookie_change_notifier_.get(); } @@ -114,26 +153,6 @@ class AtomBrowserContext // Initialize pref registry. void InitPrefs(); - // partition_id => browser_context - struct PartitionKey { - std::string partition; - bool in_memory; - - PartitionKey(const std::string& partition, bool in_memory) - : partition(partition), in_memory(in_memory) {} - - bool operator<(const PartitionKey& other) const { - if (partition == other.partition) - return in_memory < other.in_memory; - return partition < other.partition; - } - - bool operator==(const PartitionKey& other) const { - return (partition == other.partition) && (in_memory == other.in_memory); - } - }; - using BrowserContextMap = - std::map>; static BrowserContextMap browser_context_map_; // Self-destructing class responsible for creating URLRequestContextGetter @@ -162,6 +181,11 @@ class AtomBrowserContext bool use_cache_ = true; int max_cache_size_ = 0; +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + // Owned by the KeyedService system. + extensions::AtomExtensionSystem* extension_system_; +#endif + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(AtomBrowserContext); diff --git a/shell/browser/atom_browser_main_parts.cc b/shell/browser/atom_browser_main_parts.cc index c5501f131630..14d4ab14e710 100644 --- a/shell/browser/atom_browser_main_parts.cc +++ b/shell/browser/atom_browser_main_parts.cc @@ -92,6 +92,15 @@ #include "device/bluetooth/dbus/dbus_bluez_manager_wrapper_linux.h" #endif +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "extensions/browser/browser_context_keyed_service_factories.h" +#include "extensions/common/extension_api.h" +#include "shell/browser/extensions/atom_browser_context_keyed_service_factories.h" +#include "shell/browser/extensions/atom_extensions_browser_client.h" +#include "shell/common/extensions/atom_extensions_client.h" +#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + namespace electron { namespace { @@ -375,6 +384,10 @@ int AtomBrowserMainParts::PreCreateThreads() { } void AtomBrowserMainParts::PostDestroyThreads() { +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + extensions_browser_client_.reset(); + extensions::ExtensionsBrowserClient::Set(nullptr); +#endif #if defined(OS_LINUX) device::BluetoothAdapterFactory::Shutdown(); bluez::DBusBluezManagerWrapperLinux::Shutdown(); @@ -415,6 +428,18 @@ void AtomBrowserMainParts::PreMainMessageLoopRun() { node_bindings_->PrepareMessageLoop(); node_bindings_->RunMessageLoop(); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + extensions_client_ = std::make_unique(); + extensions::ExtensionsClient::Set(extensions_client_.get()); + + // BrowserContextKeyedAPIServiceFactories require an ExtensionsBrowserClient. + extensions_browser_client_ = std::make_unique(); + extensions::ExtensionsBrowserClient::Set(extensions_browser_client_.get()); + + extensions::EnsureBrowserContextKeyedServiceFactoriesBuilt(); + extensions::electron::EnsureBrowserContextKeyedServiceFactoriesBuilt(); +#endif + // url::Add*Scheme are not threadsafe, this helps prevent data races. url::LockSchemeRegistries(); diff --git a/shell/browser/atom_browser_main_parts.h b/shell/browser/atom_browser_main_parts.h index 9a15d94ae3d5..ce2269fb29e3 100644 --- a/shell/browser/atom_browser_main_parts.h +++ b/shell/browser/atom_browser_main_parts.h @@ -15,6 +15,7 @@ #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_main_parts.h" #include "content/public/common/main_function_params.h" +#include "electron/buildflags/buildflags.h" #include "services/device/public/mojom/geolocation_control.mojom.h" #include "ui/views/layout/layout_provider.h" @@ -29,14 +30,20 @@ class WMState; namespace electron { -class ElectronBindings; +class AtomBrowserContext; class Browser; +class ElectronBindings; class JavascriptEnvironment; class NodeBindings; class NodeDebugger; class NodeEnvironment; class BridgeTaskRunner; +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +class AtomExtensionsClient; +class AtomExtensionsBrowserClient; +#endif + #if defined(TOOLKIT_VIEWS) class ViewsDelegate; #endif @@ -128,6 +135,11 @@ class AtomBrowserMainParts : public content::BrowserMainParts { std::unique_ptr icon_manager_; std::unique_ptr field_trial_list_; +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + std::unique_ptr extensions_client_; + std::unique_ptr extensions_browser_client_; +#endif + base::RepeatingTimer gc_timer_; // List of callbacks should be executed before destroying JS env. diff --git a/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.cc b/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.cc new file mode 100644 index 000000000000..37216dbbe04f --- /dev/null +++ b/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.cc @@ -0,0 +1,55 @@ +// Copyright 2014 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/api/runtime/atom_runtime_api_delegate.h" + +#include + +#include "build/build_config.h" +#include "extensions/common/api/runtime.h" +#include "shell/browser/extensions/atom_extension_system.h" + +using extensions::api::runtime::PlatformInfo; + +namespace extensions { + +AtomRuntimeAPIDelegate::AtomRuntimeAPIDelegate( + content::BrowserContext* browser_context) + : browser_context_(browser_context) { + DCHECK(browser_context_); +} + +AtomRuntimeAPIDelegate::~AtomRuntimeAPIDelegate() = default; + +void AtomRuntimeAPIDelegate::AddUpdateObserver(UpdateObserver* observer) {} + +void AtomRuntimeAPIDelegate::RemoveUpdateObserver(UpdateObserver* observer) {} + +void AtomRuntimeAPIDelegate::ReloadExtension(const std::string& extension_id) { + static_cast(ExtensionSystem::Get(browser_context_)) + ->ReloadExtension(extension_id); +} + +bool AtomRuntimeAPIDelegate::CheckForUpdates( + const std::string& extension_id, + const UpdateCheckCallback& callback) { + return false; +} + +void AtomRuntimeAPIDelegate::OpenURL(const GURL& uninstall_url) {} + +bool AtomRuntimeAPIDelegate::GetPlatformInfo(PlatformInfo* info) { + // TODO(nornagon): put useful information here. +#if defined(OS_LINUX) + info->os = api::runtime::PLATFORM_OS_LINUX; +#endif + return true; +} // namespace extensions + +bool AtomRuntimeAPIDelegate::RestartDevice(std::string* error_message) { + *error_message = "Restart is not supported in Electron"; + return false; +} + +} // namespace extensions diff --git a/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h b/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h new file mode 100644 index 000000000000..d49c468577c7 --- /dev/null +++ b/shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h @@ -0,0 +1,42 @@ +// Copyright 2014 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_API_RUNTIME_ATOM_RUNTIME_API_DELEGATE_H_ +#define SHELL_BROWSER_EXTENSIONS_API_RUNTIME_ATOM_RUNTIME_API_DELEGATE_H_ + +#include + +#include "base/macros.h" +#include "extensions/browser/api/runtime/runtime_api_delegate.h" + +namespace content { +class BrowserContext; +} // namespace content + +namespace extensions { + +class AtomRuntimeAPIDelegate : public RuntimeAPIDelegate { + public: + explicit AtomRuntimeAPIDelegate(content::BrowserContext* browser_context); + ~AtomRuntimeAPIDelegate() override; + + // RuntimeAPIDelegate implementation. + void AddUpdateObserver(UpdateObserver* observer) override; + void RemoveUpdateObserver(UpdateObserver* observer) override; + void ReloadExtension(const std::string& extension_id) override; + bool CheckForUpdates(const std::string& extension_id, + const UpdateCheckCallback& callback) override; + void OpenURL(const GURL& uninstall_url) override; + bool GetPlatformInfo(api::runtime::PlatformInfo* info) override; + bool RestartDevice(std::string* error_message) override; + + private: + content::BrowserContext* browser_context_; + + DISALLOW_COPY_AND_ASSIGN(AtomRuntimeAPIDelegate); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_API_RUNTIME_ATOM_RUNTIME_API_DELEGATE_H_ diff --git a/shell/browser/extensions/atom_browser_context_keyed_service_factories.cc b/shell/browser/extensions/atom_browser_context_keyed_service_factories.cc new file mode 100644 index 000000000000..8101a63cf5b5 --- /dev/null +++ b/shell/browser/extensions/atom_browser_context_keyed_service_factories.cc @@ -0,0 +1,25 @@ +// Copyright 2014 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/atom_browser_context_keyed_service_factories.h" + +#include "extensions/browser/updater/update_service_factory.h" +// #include "extensions/shell/browser/api/identity/identity_api.h" +#include "shell/browser/extensions/atom_extension_system_factory.h" + +namespace extensions { +namespace electron { + +void EnsureBrowserContextKeyedServiceFactoriesBuilt() { + // IdentityAPI::GetFactoryInstance(); + + // TODO(rockot): Remove this once UpdateService is supported across all + // extensions embedders (and namely chrome.) + UpdateServiceFactory::GetInstance(); + + AtomExtensionSystemFactory::GetInstance(); +} + +} // namespace electron +} // namespace extensions diff --git a/shell/browser/extensions/atom_browser_context_keyed_service_factories.h b/shell/browser/extensions/atom_browser_context_keyed_service_factories.h new file mode 100644 index 000000000000..536b75c86f0b --- /dev/null +++ b/shell/browser/extensions/atom_browser_context_keyed_service_factories.h @@ -0,0 +1,18 @@ +// Copyright 2014 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_ +#define SHELL_BROWSER_EXTENSIONS_ATOM_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_ + +namespace extensions { +namespace electron { + +// Ensures the existence of any BrowserContextKeyedServiceFactory provided by +// the core extensions code. +void EnsureBrowserContextKeyedServiceFactoriesBuilt(); + +} // namespace electron +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ATOM_BROWSER_CONTEXT_KEYED_SERVICE_FACTORIES_H_ diff --git a/shell/browser/extensions/atom_display_info_provider.cc b/shell/browser/extensions/atom_display_info_provider.cc new file mode 100644 index 000000000000..92567b568017 --- /dev/null +++ b/shell/browser/extensions/atom_display_info_provider.cc @@ -0,0 +1,11 @@ +// Copyright 2014 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/atom_display_info_provider.h" + +namespace extensions { + +AtomDisplayInfoProvider::AtomDisplayInfoProvider() = default; + +} // namespace extensions diff --git a/shell/browser/extensions/atom_display_info_provider.h b/shell/browser/extensions/atom_display_info_provider.h new file mode 100644 index 000000000000..22458339bd79 --- /dev/null +++ b/shell/browser/extensions/atom_display_info_provider.h @@ -0,0 +1,23 @@ +// Copyright 2014 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_DISPLAY_INFO_PROVIDER_H_ +#define SHELL_BROWSER_EXTENSIONS_ATOM_DISPLAY_INFO_PROVIDER_H_ + +#include "base/macros.h" +#include "extensions/browser/api/system_display/display_info_provider.h" + +namespace extensions { + +class AtomDisplayInfoProvider : public DisplayInfoProvider { + public: + AtomDisplayInfoProvider(); + + private: + DISALLOW_COPY_AND_ASSIGN(AtomDisplayInfoProvider); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ATOM_DISPLAY_INFO_PROVIDER_H_ diff --git a/shell/browser/extensions/atom_extension_host_delegate.cc b/shell/browser/extensions/atom_extension_host_delegate.cc new file mode 100644 index 000000000000..2342eb662db4 --- /dev/null +++ b/shell/browser/extensions/atom_extension_host_delegate.cc @@ -0,0 +1,88 @@ +// Copyright 2014 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/atom_extension_host_delegate.h" + +#include +#include +#include + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "extensions/browser/media_capture_util.h" +#include "extensions/browser/serial_extension_host_queue.h" +#include "shell/browser/extensions/atom_extension_web_contents_observer.h" + +namespace extensions { + +AtomExtensionHostDelegate::AtomExtensionHostDelegate() {} + +AtomExtensionHostDelegate::~AtomExtensionHostDelegate() {} + +void AtomExtensionHostDelegate::OnExtensionHostCreated( + content::WebContents* web_contents) { + AtomExtensionWebContentsObserver::CreateForWebContents(web_contents); +} + +void AtomExtensionHostDelegate::OnRenderViewCreatedForBackgroundPage( + ExtensionHost* host) {} + +content::JavaScriptDialogManager* +AtomExtensionHostDelegate::GetJavaScriptDialogManager() { + // TODO(jamescook): Create a JavaScriptDialogManager or reuse the one from + // content_shell. + NOTREACHED(); + return nullptr; +} + +void AtomExtensionHostDelegate::CreateTab( + std::unique_ptr web_contents, + const std::string& extension_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture) { + // TODO(jamescook): Should app_shell support opening popup windows? + NOTREACHED(); +} + +void AtomExtensionHostDelegate::ProcessMediaAccessRequest( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback, + const Extension* extension) { + // Allow access to the microphone and/or camera. + media_capture_util::GrantMediaStreamRequest(web_contents, request, + std::move(callback), extension); +} + +bool AtomExtensionHostDelegate::CheckMediaAccessPermission( + content::RenderFrameHost* render_frame_host, + const GURL& security_origin, + blink::mojom::MediaStreamType type, + const Extension* extension) { + media_capture_util::VerifyMediaAccessPermission(type, extension); + return true; +} + +static base::LazyInstance::DestructorAtExit g_queue = + LAZY_INSTANCE_INITIALIZER; + +ExtensionHostQueue* AtomExtensionHostDelegate::GetExtensionHostQueue() const { + return g_queue.Pointer(); +} + +content::PictureInPictureResult +AtomExtensionHostDelegate::EnterPictureInPicture( + content::WebContents* web_contents, + const viz::SurfaceId& surface_id, + const gfx::Size& natural_size) { + NOTREACHED(); + return content::PictureInPictureResult(); +} + +void AtomExtensionHostDelegate::ExitPictureInPicture() { + NOTREACHED(); +} + +} // namespace extensions diff --git a/shell/browser/extensions/atom_extension_host_delegate.h b/shell/browser/extensions/atom_extension_host_delegate.h new file mode 100644 index 000000000000..689f757ef3d0 --- /dev/null +++ b/shell/browser/extensions/atom_extension_host_delegate.h @@ -0,0 +1,52 @@ +// Copyright 2014 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_HOST_DELEGATE_H_ +#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_HOST_DELEGATE_H_ + +#include +#include + +#include "base/macros.h" +#include "extensions/browser/extension_host_delegate.h" + +namespace extensions { + +// A minimal ExtensionHostDelegate. +class AtomExtensionHostDelegate : public ExtensionHostDelegate { + public: + AtomExtensionHostDelegate(); + ~AtomExtensionHostDelegate() override; + + // ExtensionHostDelegate implementation. + void OnExtensionHostCreated(content::WebContents* web_contents) override; + void OnRenderViewCreatedForBackgroundPage(ExtensionHost* host) override; + content::JavaScriptDialogManager* GetJavaScriptDialogManager() override; + void CreateTab(std::unique_ptr web_contents, + const std::string& extension_id, + WindowOpenDisposition disposition, + const gfx::Rect& initial_rect, + bool user_gesture) override; + void ProcessMediaAccessRequest(content::WebContents* web_contents, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback, + const Extension* extension) override; + bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host, + const GURL& security_origin, + blink::mojom::MediaStreamType type, + const Extension* extension) override; + ExtensionHostQueue* GetExtensionHostQueue() const override; + content::PictureInPictureResult EnterPictureInPicture( + content::WebContents* web_contents, + const viz::SurfaceId& surface_id, + const gfx::Size& natural_size) override; + void ExitPictureInPicture() override; + + private: + DISALLOW_COPY_AND_ASSIGN(AtomExtensionHostDelegate); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_HOST_DELEGATE_H_ diff --git a/shell/browser/extensions/atom_extension_loader.cc b/shell/browser/extensions/atom_extension_loader.cc new file mode 100644 index 000000000000..9bacce1639fb --- /dev/null +++ b/shell/browser/extensions/atom_extension_loader.cc @@ -0,0 +1,158 @@ +// Copyright 2018 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/atom_extension_loader.h" + +#include "base/auto_reset.h" +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/logging.h" +#include "base/sequenced_task_runner.h" +#include "base/task_runner_util.h" +#include "base/threading/thread_restrictions.h" +#include "extensions/browser/extension_file_task_runner.h" +#include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/common/file_util.h" + +namespace extensions { + +using LoadErrorBehavior = ExtensionRegistrar::LoadErrorBehavior; + +namespace { + +scoped_refptr LoadUnpacked( + const base::FilePath& extension_dir) { + // 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. + if (!base::DirectoryExists(extension_dir)) { + LOG(ERROR) << "Extension directory not found: " + << extension_dir.AsUTF8Unsafe(); + return nullptr; + } + + int load_flags = Extension::FOLLOW_SYMLINKS_ANYWHERE; + std::string load_error; + scoped_refptr extension = file_util::LoadExtension( + extension_dir, Manifest::COMMAND_LINE, load_flags, &load_error); + if (!extension.get()) { + LOG(ERROR) << "Loading extension at " << extension_dir.value() + << " failed with: " << load_error; + return nullptr; + } + + // Log warnings. + if (extension->install_warnings().size()) { + LOG(WARNING) << "Warnings loading extension at " << extension_dir.value() + << ":"; + for (const auto& warning : extension->install_warnings()) + LOG(WARNING) << warning.message; + } + + return extension; +} + +} // namespace + +AtomExtensionLoader::AtomExtensionLoader( + content::BrowserContext* browser_context) + : browser_context_(browser_context), + extension_registrar_(browser_context, this), + weak_factory_(this) {} + +AtomExtensionLoader::~AtomExtensionLoader() = default; + +const Extension* AtomExtensionLoader::LoadExtension( + const base::FilePath& extension_dir) { + // TODO(nornagon): load extensions asynchronously on + // GetExtensionFileTaskRunner() + base::ScopedAllowBlockingForTesting allow_blocking; + scoped_refptr extension = LoadUnpacked(extension_dir); + if (extension) + extension_registrar_.AddExtension(extension); + + return extension.get(); +} + +void AtomExtensionLoader::ReloadExtension(ExtensionId extension_id) { + const Extension* extension = ExtensionRegistry::Get(browser_context_) + ->GetInstalledExtension(extension_id); + // We shouldn't be trying to reload extensions that haven't been added. + DCHECK(extension); + + // This should always start false since it's only set here, or in + // LoadExtensionForReload() as a result of the call below. + DCHECK_EQ(false, did_schedule_reload_); + base::AutoReset reset_did_schedule_reload(&did_schedule_reload_, false); + + extension_registrar_.ReloadExtension(extension_id, LoadErrorBehavior::kQuiet); + if (did_schedule_reload_) + return; +} + +void AtomExtensionLoader::FinishExtensionReload( + const ExtensionId old_extension_id, + scoped_refptr extension) { + if (extension) { + extension_registrar_.AddExtension(extension); + } +} + +void AtomExtensionLoader::PreAddExtension(const Extension* extension, + const Extension* old_extension) { + if (old_extension) + return; + + // The extension might be disabled if a previous reload attempt failed. In + // that case, we want to remove that disable reason. + ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_); + if (extension_prefs->IsExtensionDisabled(extension->id()) && + extension_prefs->HasDisableReason(extension->id(), + disable_reason::DISABLE_RELOAD)) { + extension_prefs->RemoveDisableReason(extension->id(), + disable_reason::DISABLE_RELOAD); + // Only re-enable the extension if there are no other disable reasons. + if (extension_prefs->GetDisableReasons(extension->id()) == + disable_reason::DISABLE_NONE) { + extension_prefs->SetExtensionEnabled(extension->id()); + } + } +} + +void AtomExtensionLoader::PostActivateExtension( + scoped_refptr extension) {} + +void AtomExtensionLoader::PostDeactivateExtension( + scoped_refptr extension) {} + +void AtomExtensionLoader::LoadExtensionForReload( + const ExtensionId& extension_id, + const base::FilePath& path, + LoadErrorBehavior load_error_behavior) { + CHECK(!path.empty()); + + base::PostTaskAndReplyWithResult( + GetExtensionFileTaskRunner().get(), FROM_HERE, + base::BindOnce(&LoadUnpacked, path), + base::BindOnce(&AtomExtensionLoader::FinishExtensionReload, + weak_factory_.GetWeakPtr(), extension_id)); + did_schedule_reload_ = true; +} + +bool AtomExtensionLoader::CanEnableExtension(const Extension* extension) { + return true; +} + +bool AtomExtensionLoader::CanDisableExtension(const Extension* extension) { + // Extensions cannot be disabled by the user. + return false; +} + +bool AtomExtensionLoader::ShouldBlockExtension(const Extension* extension) { + return false; +} + +} // namespace extensions diff --git a/shell/browser/extensions/atom_extension_loader.h b/shell/browser/extensions/atom_extension_loader.h new file mode 100644 index 000000000000..9b0f431389d0 --- /dev/null +++ b/shell/browser/extensions/atom_extension_loader.h @@ -0,0 +1,86 @@ +// Copyright 2018 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_LOADER_H_ +#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_LOADER_H_ + +#include +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "extensions/browser/extension_registrar.h" +#include "extensions/common/extension_id.h" + +namespace base { +class FilePath; +} // namespace base + +namespace content { +class BrowserContext; +} // namespace content + +namespace extensions { + +class Extension; + +// Handles extension loading and reloading using ExtensionRegistrar. +class AtomExtensionLoader : public ExtensionRegistrar::Delegate { + public: + explicit AtomExtensionLoader(content::BrowserContext* browser_context); + ~AtomExtensionLoader() override; + + // Loads an unpacked extension from a directory synchronously. Returns the + // extension on success, or nullptr otherwise. + const Extension* LoadExtension(const base::FilePath& extension_dir); + + // Starts reloading the extension. A keep-alive is maintained until the + // reload succeeds/fails. If the extension is an app, it will be launched upon + // reloading. + // This may invalidate references to the old Extension object, so it takes the + // ID by value. + void ReloadExtension(ExtensionId extension_id); + + private: + // If the extension loaded successfully, enables it. If it's an app, launches + // it. If the load failed, updates ShellKeepAliveRequester. + void FinishExtensionReload(const ExtensionId old_extension_id, + scoped_refptr extension); + + // ExtensionRegistrar::Delegate: + void PreAddExtension(const Extension* extension, + const Extension* old_extension) override; + void PostActivateExtension(scoped_refptr extension) override; + void PostDeactivateExtension( + scoped_refptr extension) override; + void LoadExtensionForReload( + const ExtensionId& extension_id, + const base::FilePath& path, + ExtensionRegistrar::LoadErrorBehavior load_error_behavior) override; + bool CanEnableExtension(const Extension* extension) override; + bool CanDisableExtension(const Extension* extension) override; + bool ShouldBlockExtension(const Extension* extension) override; + + content::BrowserContext* browser_context_; // Not owned. + + // Registers and unregisters extensions. + ExtensionRegistrar extension_registrar_; + + // Holds keep-alives for relaunching apps. + // ShellKeepAliveRequester keep_alive_requester_; + + // Indicates that we posted the (asynchronous) task to start reloading. + // Used by ReloadExtension() to check whether ExtensionRegistrar calls + // LoadExtensionForReload(). + bool did_schedule_reload_ = false; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(AtomExtensionLoader); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_LOADER_H_ diff --git a/shell/browser/extensions/atom_extension_system.cc b/shell/browser/extensions/atom_extension_system.cc new file mode 100644 index 000000000000..a8cd881075ee --- /dev/null +++ b/shell/browser/extensions/atom_extension_system.cc @@ -0,0 +1,186 @@ +// Copyright 2014 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/atom_extension_system.h" + +#include +#include + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/task/post_task.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_source.h" +#include "extensions/browser/api/app_runtime/app_runtime_api.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/info_map.h" +#include "extensions/browser/notification_types.h" +#include "extensions/browser/null_app_sorting.h" +#include "extensions/browser/quota_service.h" +#include "extensions/browser/runtime_data.h" +#include "extensions/browser/service_worker_manager.h" +#include "extensions/browser/shared_user_script_master.h" +#include "extensions/browser/value_store/value_store_factory_impl.h" +#include "extensions/common/constants.h" +#include "extensions/common/file_util.h" +#include "shell/browser/extensions/atom_extension_loader.h" + +using content::BrowserContext; +using content::BrowserThread; + +namespace extensions { + +AtomExtensionSystem::AtomExtensionSystem(BrowserContext* browser_context) + : browser_context_(browser_context), + store_factory_(new ValueStoreFactoryImpl(browser_context->GetPath())), + weak_factory_(this) {} + +AtomExtensionSystem::~AtomExtensionSystem() = default; + +const Extension* AtomExtensionSystem::LoadExtension( + const base::FilePath& extension_dir) { + return extension_loader_->LoadExtension(extension_dir); +} + +const Extension* AtomExtensionSystem::LoadApp(const base::FilePath& app_dir) { + CHECK(false); // Should never call LoadApp + return nullptr; +} + +void AtomExtensionSystem::FinishInitialization() { + // Inform the rest of the extensions system to start. + ready_.Signal(); + content::NotificationService::current()->Notify( + NOTIFICATION_EXTENSIONS_READY_DEPRECATED, + content::Source(browser_context_), + content::NotificationService::NoDetails()); +} + +void AtomExtensionSystem::ReloadExtension(const ExtensionId& extension_id) { + extension_loader_->ReloadExtension(extension_id); +} + +void AtomExtensionSystem::Shutdown() { + extension_loader_.reset(); +} + +void AtomExtensionSystem::InitForRegularProfile(bool extensions_enabled) { + service_worker_manager_ = + std::make_unique(browser_context_); + runtime_data_ = + std::make_unique(ExtensionRegistry::Get(browser_context_)); + quota_service_ = std::make_unique(); + shared_user_script_master_ = + std::make_unique(browser_context_); + app_sorting_ = std::make_unique(); + extension_loader_ = std::make_unique(browser_context_); +} + +void AtomExtensionSystem::InitForIncognitoProfile() { + NOTREACHED(); +} + +ExtensionService* AtomExtensionSystem::extension_service() { + return nullptr; +} + +RuntimeData* AtomExtensionSystem::runtime_data() { + return runtime_data_.get(); +} + +ManagementPolicy* AtomExtensionSystem::management_policy() { + return nullptr; +} + +ServiceWorkerManager* AtomExtensionSystem::service_worker_manager() { + return service_worker_manager_.get(); +} + +SharedUserScriptMaster* AtomExtensionSystem::shared_user_script_master() { + return new SharedUserScriptMaster(browser_context_); +} + +StateStore* AtomExtensionSystem::state_store() { + return nullptr; +} + +StateStore* AtomExtensionSystem::rules_store() { + return nullptr; +} + +scoped_refptr AtomExtensionSystem::store_factory() { + return store_factory_; +} + +InfoMap* AtomExtensionSystem::info_map() { + if (!info_map_.get()) + info_map_ = new InfoMap; + return info_map_.get(); +} + +QuotaService* AtomExtensionSystem::quota_service() { + return quota_service_.get(); +} + +AppSorting* AtomExtensionSystem::app_sorting() { + return app_sorting_.get(); +} + +void AtomExtensionSystem::RegisterExtensionWithRequestContexts( + const Extension* extension, + const base::Closure& callback) { + base::PostTaskWithTraitsAndReply( + FROM_HERE, {BrowserThread::IO}, + base::Bind(&InfoMap::AddExtension, info_map(), + base::RetainedRef(extension), base::Time::Now(), false, false), + callback); +} + +void AtomExtensionSystem::UnregisterExtensionWithRequestContexts( + const std::string& extension_id, + const UnloadedExtensionReason reason) {} + +const base::OneShotEvent& AtomExtensionSystem::ready() const { + return ready_; +} + +ContentVerifier* AtomExtensionSystem::content_verifier() { + return nullptr; +} + +std::unique_ptr AtomExtensionSystem::GetDependentExtensions( + const Extension* extension) { + return std::make_unique(); +} + +void AtomExtensionSystem::InstallUpdate( + const std::string& extension_id, + const std::string& public_key, + const base::FilePath& temp_dir, + bool install_immediately, + InstallUpdateCallback install_update_callback) { + NOTREACHED(); + base::DeleteFile(temp_dir, true /* recursive */); +} + +bool AtomExtensionSystem::FinishDelayedInstallationIfReady( + const std::string& extension_id, + bool install_immediately) { + NOTREACHED(); + return false; +} + +void AtomExtensionSystem::OnExtensionRegisteredWithRequestContexts( + scoped_refptr extension) { + ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_); + registry->AddReady(extension); + registry->TriggerOnReady(extension.get()); +} + +} // namespace extensions diff --git a/shell/browser/extensions/atom_extension_system.h b/shell/browser/extensions/atom_extension_system.h new file mode 100644 index 000000000000..a118ed10039f --- /dev/null +++ b/shell/browser/extensions/atom_extension_system.h @@ -0,0 +1,119 @@ +// Copyright 2014 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_H_ +#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_H_ + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/one_shot_event.h" +#include "extensions/browser/extension_system.h" + +namespace base { +class FilePath; +} + +namespace content { +class BrowserContext; +} + +namespace extensions { + +class AtomExtensionLoader; +class ValueStoreFactory; + +// A simplified version of ExtensionSystem for app_shell. Allows +// app_shell to skip initialization of services it doesn't need. +class AtomExtensionSystem : public ExtensionSystem { + public: + using InstallUpdateCallback = ExtensionSystem::InstallUpdateCallback; + explicit AtomExtensionSystem(content::BrowserContext* browser_context); + ~AtomExtensionSystem() override; + + // Loads an unpacked extension from a directory. Returns the extension on + // success, or nullptr otherwise. + const Extension* LoadExtension(const base::FilePath& extension_dir); + + // Loads an unpacked platform app from a directory. Returns the extension on + // success, or nullptr otherwise. + // Currently this just calls LoadExtension, as apps are not loaded differently + // than other extensions. Use LaunchApp() to actually launch the loaded app. + const Extension* LoadApp(const base::FilePath& app_dir); + + // Finish initialization for the shell extension system. + void FinishInitialization(); + + // Reloads the extension with id |extension_id|. + void ReloadExtension(const ExtensionId& extension_id); + + // KeyedService implementation: + void Shutdown() override; + + // ExtensionSystem implementation: + void InitForRegularProfile(bool extensions_enabled) override; + void InitForIncognitoProfile() override; + ExtensionService* extension_service() override; + RuntimeData* runtime_data() override; + ManagementPolicy* management_policy() override; + ServiceWorkerManager* service_worker_manager() override; + SharedUserScriptMaster* shared_user_script_master() override; + StateStore* state_store() override; + StateStore* rules_store() override; + scoped_refptr store_factory() override; + InfoMap* info_map() override; + QuotaService* quota_service() override; + AppSorting* app_sorting() override; + void RegisterExtensionWithRequestContexts( + const Extension* extension, + const base::Closure& callback) override; + void UnregisterExtensionWithRequestContexts( + const std::string& extension_id, + const UnloadedExtensionReason reason) override; + const base::OneShotEvent& ready() const override; + ContentVerifier* content_verifier() override; + std::unique_ptr GetDependentExtensions( + const Extension* extension) override; + void InstallUpdate(const std::string& extension_id, + const std::string& public_key, + const base::FilePath& temp_dir, + bool install_immediately, + InstallUpdateCallback install_update_callback) override; + bool FinishDelayedInstallationIfReady(const std::string& extension_id, + bool install_immediately) override; + + private: + void OnExtensionRegisteredWithRequestContexts( + scoped_refptr extension); + content::BrowserContext* browser_context_; // Not owned. + + // Data to be accessed on the IO thread. Must outlive process_manager_. + scoped_refptr info_map_; + + std::unique_ptr service_worker_manager_; + std::unique_ptr runtime_data_; + std::unique_ptr quota_service_; + std::unique_ptr shared_user_script_master_; + std::unique_ptr app_sorting_; + + std::unique_ptr extension_loader_; + + scoped_refptr store_factory_; + + // Signaled when the extension system has completed its startup tasks. + base::OneShotEvent ready_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(AtomExtensionSystem); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_H_ diff --git a/shell/browser/extensions/atom_extension_system_factory.cc b/shell/browser/extensions/atom_extension_system_factory.cc new file mode 100644 index 000000000000..cdc7d1ec8d65 --- /dev/null +++ b/shell/browser/extensions/atom_extension_system_factory.cc @@ -0,0 +1,51 @@ +// Copyright 2014 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/atom_extension_system_factory.h" + +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "extensions/browser/extension_prefs_factory.h" +#include "extensions/browser/extension_registry_factory.h" +#include "shell/browser/extensions/atom_extension_system.h" + +using content::BrowserContext; + +namespace extensions { + +ExtensionSystem* AtomExtensionSystemFactory::GetForBrowserContext( + BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +AtomExtensionSystemFactory* AtomExtensionSystemFactory::GetInstance() { + return base::Singleton::get(); +} + +AtomExtensionSystemFactory::AtomExtensionSystemFactory() + : ExtensionSystemProvider("AtomExtensionSystem", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(ExtensionPrefsFactory::GetInstance()); + DependsOn(ExtensionRegistryFactory::GetInstance()); +} + +AtomExtensionSystemFactory::~AtomExtensionSystemFactory() {} + +KeyedService* AtomExtensionSystemFactory::BuildServiceInstanceFor( + BrowserContext* context) const { + return new AtomExtensionSystem(context); +} + +BrowserContext* AtomExtensionSystemFactory::GetBrowserContextToUse( + BrowserContext* context) const { + // Use a separate instance for incognito. + return context; +} + +bool AtomExtensionSystemFactory::ServiceIsCreatedWithBrowserContext() const { + return true; +} + +} // namespace extensions diff --git a/shell/browser/extensions/atom_extension_system_factory.h b/shell/browser/extensions/atom_extension_system_factory.h new file mode 100644 index 000000000000..6ec20f45c301 --- /dev/null +++ b/shell/browser/extensions/atom_extension_system_factory.h @@ -0,0 +1,41 @@ +// Copyright 2014 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_FACTORY_H_ +#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_FACTORY_H_ + +#include "base/macros.h" +#include "base/memory/singleton.h" +#include "extensions/browser/extension_system_provider.h" + +namespace extensions { + +// A factory that provides AtomExtensionSystem. +class AtomExtensionSystemFactory : public ExtensionSystemProvider { + public: + // ExtensionSystemProvider implementation: + ExtensionSystem* GetForBrowserContext( + content::BrowserContext* context) override; + + static AtomExtensionSystemFactory* GetInstance(); + + private: + friend struct base::DefaultSingletonTraits; + + AtomExtensionSystemFactory(); + ~AtomExtensionSystemFactory() override; + + // BrowserContextKeyedServiceFactory implementation: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const override; + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; + + DISALLOW_COPY_AND_ASSIGN(AtomExtensionSystemFactory); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_SYSTEM_FACTORY_H_ diff --git a/shell/browser/extensions/atom_extension_web_contents_observer.cc b/shell/browser/extensions/atom_extension_web_contents_observer.cc new file mode 100644 index 000000000000..4beb9030375e --- /dev/null +++ b/shell/browser/extensions/atom_extension_web_contents_observer.cc @@ -0,0 +1,26 @@ +// Copyright 2014 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/atom_extension_web_contents_observer.h" + +namespace extensions { + +AtomExtensionWebContentsObserver::AtomExtensionWebContentsObserver( + content::WebContents* web_contents) + : ExtensionWebContentsObserver(web_contents) {} + +AtomExtensionWebContentsObserver::~AtomExtensionWebContentsObserver() {} + +void AtomExtensionWebContentsObserver::CreateForWebContents( + content::WebContents* web_contents) { + content::WebContentsUserData< + AtomExtensionWebContentsObserver>::CreateForWebContents(web_contents); + + // Initialize this instance if necessary. + FromWebContents(web_contents)->Initialize(); +} + +WEB_CONTENTS_USER_DATA_KEY_IMPL(AtomExtensionWebContentsObserver) + +} // namespace extensions diff --git a/shell/browser/extensions/atom_extension_web_contents_observer.h b/shell/browser/extensions/atom_extension_web_contents_observer.h new file mode 100644 index 000000000000..75c4fc72853b --- /dev/null +++ b/shell/browser/extensions/atom_extension_web_contents_observer.h @@ -0,0 +1,37 @@ +// Copyright 2014 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_WEB_CONTENTS_OBSERVER_H_ +#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_WEB_CONTENTS_OBSERVER_H_ + +#include "base/macros.h" +#include "content/public/browser/web_contents_user_data.h" +#include "extensions/browser/extension_web_contents_observer.h" + +namespace extensions { + +// The app_shell version of ExtensionWebContentsObserver. +class AtomExtensionWebContentsObserver + : public ExtensionWebContentsObserver, + public content::WebContentsUserData { + public: + ~AtomExtensionWebContentsObserver() override; + + // Creates and initializes an instance of this class for the given + // |web_contents|, if it doesn't already exist. + static void CreateForWebContents(content::WebContents* web_contents); + + private: + friend class content::WebContentsUserData; + + explicit AtomExtensionWebContentsObserver(content::WebContents* web_contents); + + WEB_CONTENTS_USER_DATA_KEY_DECL(); + + DISALLOW_COPY_AND_ASSIGN(AtomExtensionWebContentsObserver); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSION_WEB_CONTENTS_OBSERVER_H_ diff --git a/shell/browser/extensions/atom_extensions_browser_client.cc b/shell/browser/extensions/atom_extensions_browser_client.cc new file mode 100644 index 000000000000..eac6c47beef5 --- /dev/null +++ b/shell/browser/extensions/atom_extensions_browser_client.cc @@ -0,0 +1,300 @@ +// Copyright 2014 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/atom_extensions_browser_client.h" + +#include + +#include "base/bind.h" +#include "base/memory/ptr_util.h" +#include "base/task/post_task.h" +#include "build/build_config.h" +#include "components/version_info/version_info.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/resource_request_info.h" +#include "content/public/common/user_agent.h" +#include "extensions/browser/api/extensions_api_client.h" +#include "extensions/browser/component_extension_resource_manager.h" +#include "extensions/browser/core_extensions_browser_api_provider.h" +#include "extensions/browser/event_router.h" +#include "extensions/browser/mojo/interface_registration.h" +#include "extensions/browser/null_app_sorting.h" +#include "extensions/browser/updater/null_extension_cache.h" +#include "extensions/browser/url_request_util.h" +#include "extensions/common/features/feature_channel.h" +#include "shell/browser/atom_browser_client.h" +#include "shell/browser/atom_browser_context.h" +#include "shell/browser/browser.h" +#include "shell/browser/extensions/api/runtime/atom_runtime_api_delegate.h" +#include "shell/browser/extensions/atom_extension_host_delegate.h" +#include "shell/browser/extensions/atom_extension_system_factory.h" +#include "shell/browser/extensions/atom_extension_web_contents_observer.h" +// #include "shell/browser/extensions/atom_extensions_api_client.h" +// #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" + +using content::BrowserContext; +using content::BrowserThread; + +namespace electron { + +AtomExtensionsBrowserClient::AtomExtensionsBrowserClient() + : api_client_(new extensions::ExtensionsAPIClient), + // : api_client_(new extensions::AtomExtensionsAPIClient), + extension_cache_(new extensions::NullExtensionCache()) { + // app_shell does not have a concept of channel yet, so leave UNKNOWN to + // enable all channel-dependent extension APIs. + extensions::SetCurrentChannel(version_info::Channel::UNKNOWN); + + AddAPIProvider( + std::make_unique()); + // AddAPIProvider(std::make_unique()); +} + +AtomExtensionsBrowserClient::~AtomExtensionsBrowserClient() {} + +bool AtomExtensionsBrowserClient::IsShuttingDown() { + return electron::Browser::Get()->is_shutting_down(); +} + +bool AtomExtensionsBrowserClient::AreExtensionsDisabled( + const base::CommandLine& command_line, + BrowserContext* context) { + return false; +} + +bool AtomExtensionsBrowserClient::IsValidContext(BrowserContext* context) { + auto context_map = AtomBrowserContext::browser_context_map(); + for (auto const& entry : context_map) { + if (entry.second && entry.second.get() == context) + return true; + } + return false; +} + +bool AtomExtensionsBrowserClient::IsSameContext(BrowserContext* first, + BrowserContext* second) { + return first == second; +} + +bool AtomExtensionsBrowserClient::HasOffTheRecordContext( + BrowserContext* context) { + return false; +} + +BrowserContext* AtomExtensionsBrowserClient::GetOffTheRecordContext( + BrowserContext* context) { + // app_shell only supports a single context. + return nullptr; +} + +BrowserContext* AtomExtensionsBrowserClient::GetOriginalContext( + BrowserContext* context) { + DCHECK(context); + if (context->IsOffTheRecord()) { + return AtomBrowserContext::From("", false).get(); + } else { + return context; + } +} + +bool AtomExtensionsBrowserClient::IsGuestSession( + BrowserContext* context) const { + return false; +} + +bool AtomExtensionsBrowserClient::IsExtensionIncognitoEnabled( + const std::string& extension_id, + content::BrowserContext* context) const { + return false; +} + +bool AtomExtensionsBrowserClient::CanExtensionCrossIncognito( + const extensions::Extension* extension, + content::BrowserContext* context) const { + return false; +} + +base::FilePath AtomExtensionsBrowserClient::GetBundleResourcePath( + const network::ResourceRequest& request, + const base::FilePath& extension_resources_path, + int* resource_id) const { + *resource_id = 0; + return base::FilePath(); +} + +void AtomExtensionsBrowserClient::LoadResourceFromResourceBundle( + const network::ResourceRequest& request, + network::mojom::URLLoaderRequest loader, + const base::FilePath& resource_relative_path, + int resource_id, + const std::string& content_security_policy, + network::mojom::URLLoaderClientPtr client, + bool send_cors_header) { + NOTREACHED() << "Load resources from bundles not supported."; +} + +bool AtomExtensionsBrowserClient::AllowCrossRendererResourceLoad( + const GURL& url, + content::ResourceType resource_type, + ui::PageTransition page_transition, + int child_id, + bool is_incognito, + const extensions::Extension* extension, + const extensions::ExtensionSet& extensions, + const extensions::ProcessMap& process_map) { + bool allowed = false; + if (extensions::url_request_util::AllowCrossRendererResourceLoad( + url, resource_type, page_transition, child_id, is_incognito, + extension, extensions, process_map, &allowed)) { + return allowed; + } + + // Couldn't determine if resource is allowed. Block the load. + return false; +} + +PrefService* AtomExtensionsBrowserClient::GetPrefServiceForContext( + BrowserContext* context) { + return static_cast(context)->prefs(); +} + +void AtomExtensionsBrowserClient::GetEarlyExtensionPrefsObservers( + content::BrowserContext* context, + std::vector* observers) const {} + +extensions::ProcessManagerDelegate* +AtomExtensionsBrowserClient::GetProcessManagerDelegate() const { + return NULL; +} + +std::unique_ptr AtomExtensionsBrowserClient:: + CreateExtensionHostDelegate() { // TODO(samuelmaddock): + return base::WrapUnique(new extensions::AtomExtensionHostDelegate); +} + +bool AtomExtensionsBrowserClient::DidVersionUpdate(BrowserContext* context) { + // TODO(jamescook): We might want to tell extensions when app_shell updates. + return false; +} + +void AtomExtensionsBrowserClient::PermitExternalProtocolHandler() {} + +bool AtomExtensionsBrowserClient::IsInDemoMode() { + return false; +} + +bool AtomExtensionsBrowserClient::IsScreensaverInDemoMode( + const std::string& app_id) { + return false; +} + +bool AtomExtensionsBrowserClient::IsRunningInForcedAppMode() { + return false; +} + +bool AtomExtensionsBrowserClient::IsAppModeForcedForApp( + const extensions::ExtensionId& extension_id) { + return false; +} + +bool AtomExtensionsBrowserClient::IsLoggedInAsPublicAccount() { + return false; +} + +extensions::ExtensionSystemProvider* +AtomExtensionsBrowserClient::GetExtensionSystemFactory() { + return extensions::AtomExtensionSystemFactory::GetInstance(); +} + +void AtomExtensionsBrowserClient::RegisterExtensionInterfaces( + service_manager::BinderRegistryWithArgs* + registry, + content::RenderFrameHost* render_frame_host, + const extensions::Extension* extension) const { + RegisterInterfacesForExtension(registry, render_frame_host, extension); +} + +std::unique_ptr +AtomExtensionsBrowserClient::CreateRuntimeAPIDelegate( + content::BrowserContext* context) const { + return std::make_unique(context); +} + +const extensions::ComponentExtensionResourceManager* +AtomExtensionsBrowserClient::GetComponentExtensionResourceManager() { + return NULL; +} + +void AtomExtensionsBrowserClient::BroadcastEventToRenderers( + extensions::events::HistogramValue histogram_value, + const std::string& event_name, + std::unique_ptr args) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + base::PostTaskWithTraits( + FROM_HERE, {BrowserThread::UI}, + base::BindOnce(&AtomExtensionsBrowserClient::BroadcastEventToRenderers, + base::Unretained(this), histogram_value, event_name, + std::move(args))); + return; + } + + std::unique_ptr event( + new extensions::Event(histogram_value, event_name, std::move(args))); + auto context_map = AtomBrowserContext::browser_context_map(); + for (auto const& entry : context_map) { + if (entry.second) { + extensions::EventRouter::Get(entry.second.get()) + ->BroadcastEvent(std::move(event)); + } + } +} + +extensions::ExtensionCache* AtomExtensionsBrowserClient::GetExtensionCache() { + return extension_cache_.get(); +} + +bool AtomExtensionsBrowserClient::IsBackgroundUpdateAllowed() { + return true; +} + +bool AtomExtensionsBrowserClient::IsMinBrowserVersionSupported( + const std::string& min_version) { + return true; +} + +void AtomExtensionsBrowserClient::SetAPIClientForTest( + extensions::ExtensionsAPIClient* api_client) { + api_client_.reset(api_client); +} + +extensions::ExtensionWebContentsObserver* +AtomExtensionsBrowserClient::GetExtensionWebContentsObserver( + content::WebContents* web_contents) { + return extensions::AtomExtensionWebContentsObserver::FromWebContents( + web_contents); +} + +extensions::KioskDelegate* AtomExtensionsBrowserClient::GetKioskDelegate() { + return nullptr; +} + +bool AtomExtensionsBrowserClient::IsLockScreenContext( + content::BrowserContext* context) { + return false; +} + +std::string AtomExtensionsBrowserClient::GetApplicationLocale() { + return AtomBrowserClient::Get()->GetApplicationLocale(); +} + +std::string AtomExtensionsBrowserClient::GetUserAgent() const { + return AtomBrowserClient::Get()->GetUserAgent(); +} + +} // namespace electron diff --git a/shell/browser/extensions/atom_extensions_browser_client.h b/shell/browser/extensions/atom_extensions_browser_client.h new file mode 100644 index 000000000000..a21988d9b999 --- /dev/null +++ b/shell/browser/extensions/atom_extensions_browser_client.h @@ -0,0 +1,139 @@ +// Copyright 2014 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_CLIENT_H_ +#define SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_CLIENT_H_ + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "build/build_config.h" +#include "extensions/browser/extensions_browser_client.h" + +class PrefService; + +namespace extensions { +class ExtensionsAPIClient; +class KioskDelegate; +class ProcessManagerDelegate; +class ProcessMap; +} // namespace extensions + +namespace electron { + +// An ExtensionsBrowserClient that supports a single content::BrowserContext +// with no related incognito context. +// Must be initialized via InitWithBrowserContext() once the BrowserContext is +// created. +class AtomExtensionsBrowserClient : public extensions::ExtensionsBrowserClient { + public: + AtomExtensionsBrowserClient(); + ~AtomExtensionsBrowserClient() override; + + // ExtensionsBrowserClient overrides: + bool IsShuttingDown() override; + bool AreExtensionsDisabled(const base::CommandLine& command_line, + content::BrowserContext* context) override; + bool IsValidContext(content::BrowserContext* context) override; + bool IsSameContext(content::BrowserContext* first, + content::BrowserContext* second) override; + bool HasOffTheRecordContext(content::BrowserContext* context) override; + content::BrowserContext* GetOffTheRecordContext( + content::BrowserContext* context) override; + content::BrowserContext* GetOriginalContext( + content::BrowserContext* context) override; + bool IsGuestSession(content::BrowserContext* context) const override; + bool IsExtensionIncognitoEnabled( + const std::string& extension_id, + content::BrowserContext* context) const override; + bool CanExtensionCrossIncognito( + const extensions::Extension* extension, + content::BrowserContext* context) const override; + base::FilePath GetBundleResourcePath( + const network::ResourceRequest& request, + const base::FilePath& extension_resources_path, + int* resource_id) const override; + void LoadResourceFromResourceBundle( + const network::ResourceRequest& request, + network::mojom::URLLoaderRequest loader, + const base::FilePath& resource_relative_path, + int resource_id, + const std::string& content_security_policy, + network::mojom::URLLoaderClientPtr client, + bool send_cors_header) override; + bool AllowCrossRendererResourceLoad( + const GURL& url, + content::ResourceType resource_type, + ui::PageTransition page_transition, + int child_id, + bool is_incognito, + const extensions::Extension* extension, + const extensions::ExtensionSet& extensions, + const extensions::ProcessMap& process_map) override; + PrefService* GetPrefServiceForContext( + content::BrowserContext* context) override; + void GetEarlyExtensionPrefsObservers( + content::BrowserContext* context, + std::vector* observers) + const override; + extensions::ProcessManagerDelegate* GetProcessManagerDelegate() + const override; + std::unique_ptr + CreateExtensionHostDelegate() override; + bool DidVersionUpdate(content::BrowserContext* context) override; + void PermitExternalProtocolHandler() override; + bool IsInDemoMode() override; + bool IsScreensaverInDemoMode(const std::string& app_id) override; + bool IsRunningInForcedAppMode() override; + bool IsAppModeForcedForApp( + const extensions::ExtensionId& extension_id) override; + bool IsLoggedInAsPublicAccount() override; + extensions::ExtensionSystemProvider* GetExtensionSystemFactory() override; + void RegisterExtensionInterfaces( + service_manager::BinderRegistryWithArgs* + registry, + content::RenderFrameHost* render_frame_host, + const extensions::Extension* extension) const override; + std::unique_ptr CreateRuntimeAPIDelegate( + content::BrowserContext* context) const override; + const extensions::ComponentExtensionResourceManager* + GetComponentExtensionResourceManager() override; + void BroadcastEventToRenderers( + extensions::events::HistogramValue histogram_value, + const std::string& event_name, + std::unique_ptr args) override; + extensions::ExtensionCache* GetExtensionCache() override; + bool IsBackgroundUpdateAllowed() override; + bool IsMinBrowserVersionSupported(const std::string& min_version) override; + extensions::ExtensionWebContentsObserver* GetExtensionWebContentsObserver( + content::WebContents* web_contents) override; + extensions::KioskDelegate* GetKioskDelegate() override; + bool IsLockScreenContext(content::BrowserContext* context) override; + std::string GetApplicationLocale() override; + std::string GetUserAgent() const override; + + // |context| is the single BrowserContext used for IsValidContext(). + // |pref_service| is used for GetPrefServiceForContext(). + void InitWithBrowserContext(content::BrowserContext* context, + PrefService* pref_service); + + // Sets the API client. + void SetAPIClientForTest(extensions::ExtensionsAPIClient* api_client); + + private: + // Support for extension APIs. + std::unique_ptr api_client_; + + // The extension cache used for download and installation. + std::unique_ptr extension_cache_; + + DISALLOW_COPY_AND_ASSIGN(AtomExtensionsBrowserClient); +}; + +} // namespace electron + +#endif // SHELL_BROWSER_EXTENSIONS_ATOM_EXTENSIONS_BROWSER_CLIENT_H_ diff --git a/shell/browser/extensions/atom_navigation_ui_data.cc b/shell/browser/extensions/atom_navigation_ui_data.cc new file mode 100644 index 000000000000..ab1b8afd2220 --- /dev/null +++ b/shell/browser/extensions/atom_navigation_ui_data.cc @@ -0,0 +1,40 @@ +// Copyright 2016 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/atom_navigation_ui_data.h" + +#include + +#include "content/public/browser/navigation_handle.h" +#include "extensions/common/constants.h" + +namespace extensions { + +AtomNavigationUIData::AtomNavigationUIData() {} + +AtomNavigationUIData::AtomNavigationUIData( + content::NavigationHandle* navigation_handle) { + extension_data_ = std::make_unique( + navigation_handle, extension_misc::kUnknownTabId, + extension_misc::kUnknownWindowId); +} + +AtomNavigationUIData::~AtomNavigationUIData() {} + +std::unique_ptr AtomNavigationUIData::Clone() { + std::unique_ptr copy = + std::make_unique(); + + if (extension_data_) + copy->SetExtensionNavigationUIData(extension_data_->DeepCopy()); + + return std::move(copy); +} + +void AtomNavigationUIData::SetExtensionNavigationUIData( + std::unique_ptr extension_data) { + extension_data_ = std::move(extension_data); +} + +} // namespace extensions diff --git a/shell/browser/extensions/atom_navigation_ui_data.h b/shell/browser/extensions/atom_navigation_ui_data.h new file mode 100644 index 000000000000..f757ee5e8d50 --- /dev/null +++ b/shell/browser/extensions/atom_navigation_ui_data.h @@ -0,0 +1,48 @@ +// Copyright 2016 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_ATOM_NAVIGATION_UI_DATA_H_ +#define SHELL_BROWSER_EXTENSIONS_ATOM_NAVIGATION_UI_DATA_H_ + +#include + +#include "base/macros.h" +#include "content/public/browser/navigation_ui_data.h" +#include "extensions/browser/extension_navigation_ui_data.h" + +namespace extensions { + +// PlzNavigate +// Contains data that is passed from the UI thread to the IO thread at the +// beginning of each navigation. The class is instantiated on the UI thread, +// then a copy created using Clone is passed to the content::ResourceRequestInfo +// on the IO thread. +class AtomNavigationUIData : public content::NavigationUIData { + public: + AtomNavigationUIData(); + explicit AtomNavigationUIData(content::NavigationHandle* navigation_handle); + ~AtomNavigationUIData() override; + + // Creates a new ChromeNavigationUIData that is a deep copy of the original. + // Any changes to the original after the clone is created will not be + // reflected in the clone. |extension_data_| is deep copied. + std::unique_ptr Clone() override; + + void SetExtensionNavigationUIData( + std::unique_ptr extension_data); + + ExtensionNavigationUIData* GetExtensionNavigationUIData() const { + return extension_data_.get(); + } + + private: + // Manages the lifetime of optional ExtensionNavigationUIData information. + std::unique_ptr extension_data_; + + DISALLOW_COPY_AND_ASSIGN(AtomNavigationUIData); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ATOM_NAVIGATION_UI_DATA_H_ diff --git a/shell/common/api/features.cc b/shell/common/api/features.cc index e08102938b37..f905183a2fc7 100644 --- a/shell/common/api/features.cc +++ b/shell/common/api/features.cc @@ -41,6 +41,10 @@ bool IsPrintingEnabled() { return BUILDFLAG(ENABLE_PRINTING); } +bool IsExtensionsEnabled() { + return BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS); +} + bool IsComponentBuild() { #if defined(COMPONENT_BUILD) return true; @@ -64,6 +68,7 @@ void Initialize(v8::Local exports, dict.SetMethod("isTtsEnabled", &IsTtsEnabled); dict.SetMethod("isPrintingEnabled", &IsPrintingEnabled); dict.SetMethod("isComponentBuild", &IsComponentBuild); + dict.SetMethod("isExtensionsEnabled", &IsExtensionsEnabled); } } // namespace diff --git a/shell/common/extensions/api/BUILD.gn b/shell/common/extensions/api/BUILD.gn new file mode 100644 index 000000000000..847a1521d16f --- /dev/null +++ b/shell/common/extensions/api/BUILD.gn @@ -0,0 +1,35 @@ +# Copyright 2015 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. + +import("//extensions/buildflags/buildflags.gni") +import("//tools/json_schema_compiler/json_features.gni") +import("//tools/json_schema_compiler/json_schema_api.gni") + +assert(enable_extensions) + +################################################################################ +# Public Targets + +group("extensions_features") { + public_deps = [ + ":manifest_features", + + # TODO(devlin): It would be nicer to have this dependency hoisted up to + # //extensions/common (since that's where it's consumed), but there's some + # cycles to be resolved first. + "//extensions/common/api:extensions_features", + ] +} + +################################################################################ +# Private Targets + +json_features("manifest_features") { + feature_type = "ManifestFeature" + method_name = "AddAtomManifestFeatures" + sources = [ + "_manifest_features.json", + ] + visibility = [ ":extensions_features" ] +} diff --git a/shell/common/extensions/api/_manifest_features.json b/shell/common/extensions/api/_manifest_features.json new file mode 100644 index 000000000000..96e17ec222b4 --- /dev/null +++ b/shell/common/extensions/api/_manifest_features.json @@ -0,0 +1,14 @@ +// Copyright (c) 2012 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. + +// This features file defines manifest keys implemented under src/chrome. +// See chrome/common/extensions/api/_features.md to understand this file, as +// well as feature.h, simple_feature.h, and feature_provider.h. + +{ + "content_scripts": { + "channel": "stable", + "extension_types": ["extension", "legacy_packaged_app"] + } +} diff --git a/shell/common/extensions/atom_extensions_api_provider.cc b/shell/common/extensions/atom_extensions_api_provider.cc new file mode 100644 index 000000000000..e77bf5869dde --- /dev/null +++ b/shell/common/extensions/atom_extensions_api_provider.cc @@ -0,0 +1,67 @@ +// Copyright 2018 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/common/extensions/atom_extensions_api_provider.h" + +#include + +#include "electron/buildflags/buildflags.h" +#include "extensions/common/features/json_feature_provider_source.h" + +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#include "shell/common/extensions/api/manifest_features.h" +#endif + +namespace electron { + +AtomExtensionsAPIProvider::AtomExtensionsAPIProvider() = default; +AtomExtensionsAPIProvider::~AtomExtensionsAPIProvider() = default; + +// TODO(samuelmaddock): generate API features? + +void AtomExtensionsAPIProvider::AddAPIFeatures( + extensions::FeatureProvider* provider) { + // AddShellAPIFeatures(provider); +} + +void AtomExtensionsAPIProvider::AddManifestFeatures( + extensions::FeatureProvider* provider) { +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + // TODO(samuelmaddock): why is the extensions namespace generated? + extensions::AddAtomManifestFeatures(provider); +#endif +} + +void AtomExtensionsAPIProvider::AddPermissionFeatures( + extensions::FeatureProvider* provider) { + // No shell-specific permission features. +} + +void AtomExtensionsAPIProvider::AddBehaviorFeatures( + extensions::FeatureProvider* provider) { + // No shell-specific behavior features. +} + +void AtomExtensionsAPIProvider::AddAPIJSONSources( + extensions::JSONFeatureProviderSource* json_source) { + // json_source->LoadJSON(IDR_SHELL_EXTENSION_API_FEATURES); +} + +bool AtomExtensionsAPIProvider::IsAPISchemaGenerated(const std::string& name) { + // return shell::api::ShellGeneratedSchemas::IsGenerated(name); + return false; +} + +base::StringPiece AtomExtensionsAPIProvider::GetAPISchema( + const std::string& name) { + // return shell::api::ShellGeneratedSchemas::Get(name); + return ""; +} + +void AtomExtensionsAPIProvider::RegisterPermissions( + extensions::PermissionsInfo* permissions_info) {} + +void AtomExtensionsAPIProvider::RegisterManifestHandlers() {} + +} // namespace electron diff --git a/shell/common/extensions/atom_extensions_api_provider.h b/shell/common/extensions/atom_extensions_api_provider.h new file mode 100644 index 000000000000..80a6303c6108 --- /dev/null +++ b/shell/common/extensions/atom_extensions_api_provider.h @@ -0,0 +1,39 @@ +// Copyright 2018 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. + +#ifndef SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_API_PROVIDER_H_ +#define SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_API_PROVIDER_H_ + +#include + +#include "base/macros.h" +#include "extensions/common/extensions_api_provider.h" + +namespace electron { + +class AtomExtensionsAPIProvider : public extensions::ExtensionsAPIProvider { + public: + AtomExtensionsAPIProvider(); + ~AtomExtensionsAPIProvider() override; + + // ExtensionsAPIProvider: + void AddAPIFeatures(extensions::FeatureProvider* provider) override; + void AddManifestFeatures(extensions::FeatureProvider* provider) override; + void AddPermissionFeatures(extensions::FeatureProvider* provider) override; + void AddBehaviorFeatures(extensions::FeatureProvider* provider) override; + void AddAPIJSONSources( + extensions::JSONFeatureProviderSource* json_source) override; + bool IsAPISchemaGenerated(const std::string& name) override; + base::StringPiece GetAPISchema(const std::string& name) override; + void RegisterPermissions( + extensions::PermissionsInfo* permissions_info) override; + void RegisterManifestHandlers() override; + + private: + DISALLOW_COPY_AND_ASSIGN(AtomExtensionsAPIProvider); +}; + +} // namespace electron + +#endif // SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_API_PROVIDER_H_ diff --git a/shell/common/extensions/atom_extensions_client.cc b/shell/common/extensions/atom_extensions_client.cc new file mode 100644 index 000000000000..d1d2d0dfc5c9 --- /dev/null +++ b/shell/common/extensions/atom_extensions_client.cc @@ -0,0 +1,143 @@ +// Copyright 2014 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/common/extensions/atom_extensions_client.h" + +#include +#include + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "components/version_info/version_info.h" +#include "content/public/common/user_agent.h" +#include "extensions/common/core_extensions_api_provider.h" +#include "extensions/common/extension_urls.h" +#include "extensions/common/features/simple_feature.h" +#include "extensions/common/permissions/permission_message_provider.h" +#include "extensions/common/url_pattern_set.h" +#include "shell/common/extensions/atom_extensions_api_provider.h" + +using extensions::ExtensionsClient; + +namespace electron { + +namespace { + +// TODO(jamescook): Refactor ChromePermissionsMessageProvider so we can share +// code. For now, this implementation does nothing. +class AtomPermissionMessageProvider + : public extensions::PermissionMessageProvider { + public: + AtomPermissionMessageProvider() {} + ~AtomPermissionMessageProvider() override {} + + // PermissionMessageProvider implementation. + extensions::PermissionMessages GetPermissionMessages( + const extensions::PermissionIDSet& permissions) const override { + return extensions::PermissionMessages(); + } + + extensions::PermissionMessages GetPowerfulPermissionMessages( + const extensions::PermissionIDSet& permissions) const override { + return extensions::PermissionMessages(); + } + + bool IsPrivilegeIncrease( + const extensions::PermissionSet& granted_permissions, + const extensions::PermissionSet& requested_permissions, + extensions::Manifest::Type extension_type) const override { + // Ensure we implement this before shipping. + CHECK(false); + return false; + } + + extensions::PermissionIDSet GetAllPermissionIDs( + const extensions::PermissionSet& permissions, + extensions::Manifest::Type extension_type) const override { + return extensions::PermissionIDSet(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(AtomPermissionMessageProvider); +}; + +base::LazyInstance::DestructorAtExit + g_permission_message_provider = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +AtomExtensionsClient::AtomExtensionsClient() + : webstore_base_url_(extension_urls::kChromeWebstoreBaseURL), + webstore_update_url_(extension_urls::kChromeWebstoreUpdateURL) { + AddAPIProvider(std::make_unique()); + AddAPIProvider(std::make_unique()); +} + +AtomExtensionsClient::~AtomExtensionsClient() {} + +void AtomExtensionsClient::Initialize() { + // TODO(jamescook): Do we need to whitelist any extensions? +} + +void AtomExtensionsClient::InitializeWebStoreUrls( + base::CommandLine* command_line) {} + +const extensions::PermissionMessageProvider& +AtomExtensionsClient::GetPermissionMessageProvider() const { + NOTIMPLEMENTED(); + return g_permission_message_provider.Get(); +} + +const std::string AtomExtensionsClient::GetProductName() { + // TODO(samuelmaddock): + return "app_shell"; +} + +void AtomExtensionsClient::FilterHostPermissions( + const extensions::URLPatternSet& hosts, + extensions::URLPatternSet* new_hosts, + extensions::PermissionIDSet* permissions) const { + NOTIMPLEMENTED(); +} + +void AtomExtensionsClient::SetScriptingWhitelist( + const ExtensionsClient::ScriptingWhitelist& whitelist) { + scripting_whitelist_ = whitelist; +} + +const ExtensionsClient::ScriptingWhitelist& +AtomExtensionsClient::GetScriptingWhitelist() const { + // TODO(jamescook): Real whitelist. + return scripting_whitelist_; +} + +extensions::URLPatternSet AtomExtensionsClient::GetPermittedChromeSchemeHosts( + const extensions::Extension* extension, + const extensions::APIPermissionSet& api_permissions) const { + NOTIMPLEMENTED(); + return extensions::URLPatternSet(); +} + +bool AtomExtensionsClient::IsScriptableURL(const GURL& url, + std::string* error) const { + // No restrictions on URLs. + return true; +} + +const GURL& AtomExtensionsClient::GetWebstoreBaseURL() const { + return webstore_base_url_; +} + +const GURL& AtomExtensionsClient::GetWebstoreUpdateURL() const { + return webstore_update_url_; +} + +bool AtomExtensionsClient::IsBlacklistUpdateURL(const GURL& url) const { + // TODO(rockot): Maybe we want to do something else here. For now we accept + // any URL as a blacklist URL because we don't really care. + return true; +} + +} // namespace electron diff --git a/shell/common/extensions/atom_extensions_client.h b/shell/common/extensions/atom_extensions_client.h new file mode 100644 index 000000000000..6601b323faf7 --- /dev/null +++ b/shell/common/extensions/atom_extensions_client.h @@ -0,0 +1,65 @@ +// Copyright 2014 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. + +#ifndef SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_CLIENT_H_ +#define SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_CLIENT_H_ + +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "extensions/common/extensions_client.h" +#include "url/gurl.h" + +namespace extensions { +class APIPermissionSet; +class Extension; +class PermissionMessageProvider; +class PermissionIDSet; +class ScriptingWhitelist; +class URLPatternSet; +} // namespace extensions + +namespace electron { + +// The app_shell implementation of ExtensionsClient. +class AtomExtensionsClient : public extensions::ExtensionsClient { + public: + typedef extensions::ExtensionsClient::ScriptingWhitelist ScriptingWhitelist; + + AtomExtensionsClient(); + ~AtomExtensionsClient() override; + + // ExtensionsClient overrides: + void Initialize() override; + void InitializeWebStoreUrls(base::CommandLine* command_line) override; + const extensions::PermissionMessageProvider& GetPermissionMessageProvider() + const override; + const std::string GetProductName() override; + void FilterHostPermissions( + const extensions::URLPatternSet& hosts, + extensions::URLPatternSet* new_hosts, + extensions::PermissionIDSet* permissions) const override; + void SetScriptingWhitelist(const ScriptingWhitelist& whitelist) override; + const ScriptingWhitelist& GetScriptingWhitelist() const override; + extensions::URLPatternSet GetPermittedChromeSchemeHosts( + const extensions::Extension* extension, + const extensions::APIPermissionSet& api_permissions) const override; + bool IsScriptableURL(const GURL& url, std::string* error) const override; + const GURL& GetWebstoreBaseURL() const override; + const GURL& GetWebstoreUpdateURL() const override; + bool IsBlacklistUpdateURL(const GURL& url) const override; + + private: + ScriptingWhitelist scripting_whitelist_; + + const GURL webstore_base_url_; + const GURL webstore_update_url_; + + DISALLOW_COPY_AND_ASSIGN(AtomExtensionsClient); +}; + +} // namespace electron + +#endif // SHELL_COMMON_EXTENSIONS_ATOM_EXTENSIONS_CLIENT_H_ diff --git a/shell/renderer/atom_renderer_client.cc b/shell/renderer/atom_renderer_client.cc index b564299422cf..c19baec31a91 100644 --- a/shell/renderer/atom_renderer_client.cc +++ b/shell/renderer/atom_renderer_client.cc @@ -50,6 +50,7 @@ void AtomRendererClient::RenderFrameCreated( void AtomRendererClient::RunScriptsAtDocumentStart( content::RenderFrame* render_frame) { + RendererClientBase::RunScriptsAtDocumentStart(render_frame); // Inform the document start pharse. v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); node::Environment* env = GetEnvironment(render_frame); @@ -59,6 +60,7 @@ void AtomRendererClient::RunScriptsAtDocumentStart( void AtomRendererClient::RunScriptsAtDocumentEnd( content::RenderFrame* render_frame) { + RendererClientBase::RunScriptsAtDocumentEnd(render_frame); // Inform the document end pharse. v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); node::Environment* env = GetEnvironment(render_frame); diff --git a/shell/renderer/atom_sandboxed_renderer_client.cc b/shell/renderer/atom_sandboxed_renderer_client.cc index 689373b26b3d..12e0dc4e2897 100644 --- a/shell/renderer/atom_sandboxed_renderer_client.cc +++ b/shell/renderer/atom_sandboxed_renderer_client.cc @@ -165,6 +165,7 @@ void AtomSandboxedRendererClient::RenderViewCreated( void AtomSandboxedRendererClient::RunScriptsAtDocumentStart( content::RenderFrame* render_frame) { + RendererClientBase::RunScriptsAtDocumentStart(render_frame); if (injected_frames_.find(render_frame) == injected_frames_.end()) return; @@ -180,6 +181,7 @@ void AtomSandboxedRendererClient::RunScriptsAtDocumentStart( void AtomSandboxedRendererClient::RunScriptsAtDocumentEnd( content::RenderFrame* render_frame) { + RendererClientBase::RunScriptsAtDocumentEnd(render_frame); if (injected_frames_.find(render_frame) == injected_frames_.end()) return; diff --git a/shell/renderer/extensions/atom_extensions_renderer_client.cc b/shell/renderer/extensions/atom_extensions_renderer_client.cc new file mode 100644 index 000000000000..337268f9a85a --- /dev/null +++ b/shell/renderer/extensions/atom_extensions_renderer_client.cc @@ -0,0 +1,65 @@ +// Copyright 2014 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/renderer/extensions/atom_extensions_renderer_client.h" + +#include "content/public/renderer/render_thread.h" +#include "extensions/renderer/dispatcher.h" +#include "extensions/renderer/dispatcher_delegate.h" + +namespace electron { + +AtomExtensionsRendererClient::AtomExtensionsRendererClient() + : dispatcher_(std::make_unique( + std::make_unique())) { + dispatcher_->OnRenderThreadStarted(content::RenderThread::Get()); +} + +AtomExtensionsRendererClient::~AtomExtensionsRendererClient() {} + +bool AtomExtensionsRendererClient::IsIncognitoProcess() const { + // app_shell doesn't support off-the-record contexts. + return false; +} + +int AtomExtensionsRendererClient::GetLowestIsolatedWorldId() const { + // app_shell doesn't need to reserve world IDs for anything other than + // extensions, so we always return 1. Note that 0 is reserved for the global + // world. + // TODO(samuelmaddock): skip electron worlds + return 10; +} + +extensions::Dispatcher* AtomExtensionsRendererClient::GetDispatcher() { + return dispatcher_.get(); +} + +bool AtomExtensionsRendererClient::ExtensionAPIEnabledForServiceWorkerScript( + const GURL& scope, + const GURL& script_url) const { + // TODO(nornagon): adapt logic from chrome's version + return true; +} + +bool AtomExtensionsRendererClient::AllowPopup() { + // TODO(samuelmaddock): + return false; +} + +void AtomExtensionsRendererClient::RunScriptsAtDocumentStart( + content::RenderFrame* render_frame) { + dispatcher_->RunScriptsAtDocumentStart(render_frame); +} + +void AtomExtensionsRendererClient::RunScriptsAtDocumentEnd( + content::RenderFrame* render_frame) { + dispatcher_->RunScriptsAtDocumentEnd(render_frame); +} + +void AtomExtensionsRendererClient::RunScriptsAtDocumentIdle( + content::RenderFrame* render_frame) { + dispatcher_->RunScriptsAtDocumentIdle(render_frame); +} + +} // namespace electron diff --git a/shell/renderer/extensions/atom_extensions_renderer_client.h b/shell/renderer/extensions/atom_extensions_renderer_client.h new file mode 100644 index 000000000000..e38db538fba9 --- /dev/null +++ b/shell/renderer/extensions/atom_extensions_renderer_client.h @@ -0,0 +1,51 @@ +// Copyright (c) 2017 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef SHELL_RENDERER_EXTENSIONS_ATOM_EXTENSIONS_RENDERER_CLIENT_H_ +#define SHELL_RENDERER_EXTENSIONS_ATOM_EXTENSIONS_RENDERER_CLIENT_H_ + +#include + +#include "base/macros.h" +#include "extensions/renderer/extensions_renderer_client.h" + +namespace content { +class RenderFrame; +} + +namespace extensions { +class Dispatcher; +} + +namespace electron { + +class AtomExtensionsRendererClient + : public extensions::ExtensionsRendererClient { + public: + AtomExtensionsRendererClient(); + ~AtomExtensionsRendererClient() override; + + // ExtensionsRendererClient implementation. + bool IsIncognitoProcess() const override; + int GetLowestIsolatedWorldId() const override; + extensions::Dispatcher* GetDispatcher() override; + bool ExtensionAPIEnabledForServiceWorkerScript( + const GURL& scope, + const GURL& script_url) const override; + + bool AllowPopup(); + + void RunScriptsAtDocumentStart(content::RenderFrame* render_frame); + void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame); + void RunScriptsAtDocumentIdle(content::RenderFrame* render_frame); + + private: + std::unique_ptr dispatcher_; + + DISALLOW_COPY_AND_ASSIGN(AtomExtensionsRendererClient); +}; + +} // namespace electron + +#endif // SHELL_RENDERER_EXTENSIONS_ATOM_EXTENSIONS_RENDERER_CLIENT_H_ diff --git a/shell/renderer/renderer_client_base.cc b/shell/renderer/renderer_client_base.cc index b8b600a66050..ebfe87b4fb0f 100644 --- a/shell/renderer/renderer_client_base.cc +++ b/shell/renderer/renderer_client_base.cc @@ -15,6 +15,7 @@ #include "content/public/common/content_constants.h" #include "content/public/common/content_switches.h" #include "content/public/renderer/render_frame.h" +#include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "electron/buildflags/buildflags.h" #include "native_mate/dictionary.h" @@ -62,6 +63,17 @@ #include "shell/renderer/printing/print_render_frame_helper_delegate.h" #endif // BUILDFLAG(ENABLE_PRINTING) +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#include "extensions/common/extensions_client.h" +#include "extensions/renderer/dispatcher.h" +#include "extensions/renderer/extension_frame_helper.h" +#include "extensions/renderer/guest_view/extensions_guest_view_container.h" +#include "extensions/renderer/guest_view/extensions_guest_view_container_dispatcher.h" +#include "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container.h" +#include "shell/common/extensions/atom_extensions_client.h" +#include "shell/renderer/extensions/atom_extensions_renderer_client.h" +#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + namespace electron { namespace { @@ -128,6 +140,18 @@ void RendererClientBase::RenderThreadStarted() { } #endif +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + auto* thread = content::RenderThread::Get(); + + extensions_client_.reset(CreateExtensionsClient()); + extensions::ExtensionsClient::Set(extensions_client_.get()); + + extensions_renderer_client_.reset(new AtomExtensionsRendererClient); + extensions::ExtensionsRendererClient::Set(extensions_renderer_client_.get()); + + thread->AddObserver(extensions_renderer_client_->GetDispatcher()); +#endif + blink::WebCustomElement::AddEmbedderCustomElementName("webview"); blink::WebCustomElement::AddEmbedderCustomElementName("browserplugin"); @@ -236,6 +260,14 @@ void RendererClientBase::RenderFrameCreated( } } } + +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + auto* dispatcher = extensions_renderer_client_->GetDispatcher(); + // ExtensionFrameHelper destroys itself when the RenderFrame is destroyed. + new extensions::ExtensionFrameHelper(render_frame, dispatcher); + + dispatcher->OnRenderFrameCreated(render_frame); +#endif } void RendererClientBase::DidClearWindowObject( @@ -291,6 +323,27 @@ void RendererClientBase::DidSetUserAgent(const std::string& user_agent) { #endif } +void RendererClientBase::RunScriptsAtDocumentStart( + content::RenderFrame* render_frame) { +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + extensions_renderer_client_.get()->RunScriptsAtDocumentStart(render_frame); +#endif +} + +void RendererClientBase::RunScriptsAtDocumentIdle( + content::RenderFrame* render_frame) { +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + extensions_renderer_client_.get()->RunScriptsAtDocumentIdle(render_frame); +#endif +} + +void RendererClientBase::RunScriptsAtDocumentEnd( + content::RenderFrame* render_frame) { +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + extensions_renderer_client_.get()->RunScriptsAtDocumentEnd(render_frame); +#endif +} + v8::Local RendererClientBase::GetContext( blink::WebLocalFrame* frame, v8::Isolate* isolate) const { @@ -310,6 +363,12 @@ v8::Local RendererClientBase::RunScript( return script->Run(context).ToLocalChecked(); } +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +extensions::ExtensionsClient* RendererClientBase::CreateExtensionsClient() { + return new AtomExtensionsClient; +} +#endif + bool RendererClientBase::IsWebViewFrame( v8::Handle context, content::RenderFrame* render_frame) const { diff --git a/shell/renderer/renderer_client_base.h b/shell/renderer/renderer_client_base.h index 15397933fa53..6c124adafbd1 100644 --- a/shell/renderer/renderer_client_base.h +++ b/shell/renderer/renderer_client_base.h @@ -10,6 +10,7 @@ #include #include "content/public/renderer/content_renderer_client.h" +#include "electron/buildflags/buildflags.h" #include "third_party/blink/public/web/web_local_frame.h" // In SHARED_INTERMEDIATE_DIR. #include "widevine_cdm_version.h" // NOLINT(build/include) @@ -18,8 +19,18 @@ #include "chrome/renderer/media/chrome_key_systems_provider.h" // nogncheck #endif +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +namespace extensions { +class ExtensionsClient; +} +#endif + namespace electron { +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +class AtomExtensionsRendererClient; +#endif + class RendererClientBase : public content::ContentRendererClient { public: RendererClientBase(); @@ -67,7 +78,22 @@ class RendererClientBase : public content::ContentRendererClient { bool IsKeySystemsUpdateNeeded() override; void DidSetUserAgent(const std::string& user_agent) override; + void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override; + void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override; + void RunScriptsAtDocumentIdle(content::RenderFrame* render_frame) override; + + protected: +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + // app_shell embedders may need custom extensions client interfaces. + // This class takes ownership of the returned object. + virtual extensions::ExtensionsClient* CreateExtensionsClient(); +#endif + private: +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + std::unique_ptr extensions_client_; + std::unique_ptr extensions_renderer_client_; +#endif #if defined(WIDEVINE_CDM_AVAILABLE) ChromeKeySystemsProvider key_systems_provider_; #endif diff --git a/spec-main/api-net-log-spec.js b/spec-main/api-net-log-spec.js index 4d51f84d52f3..5cefbab65a96 100644 --- a/spec-main/api-net-log-spec.js +++ b/spec-main/api-net-log-spec.js @@ -13,7 +13,7 @@ const dumpFileDynamic = path.join(os.tmpdir(), 'net_log_dynamic.json') const { expect } = chai chai.use(dirtyChai) const isCI = global.isCI -const netLog = session.fromPartition('net-log').netLog +const testNetLog = () => session.fromPartition('net-log').netLog describe('netLog module', () => { let server @@ -47,7 +47,7 @@ describe('netLog module', () => { }) beforeEach(() => { - expect(netLog.currentlyLogging).to.be.false() + expect(testNetLog().currentlyLogging).to.be.false() }) afterEach(() => { try { @@ -60,29 +60,29 @@ describe('netLog module', () => { } catch (e) { // Ignore error } - expect(netLog.currentlyLogging).to.be.false() + expect(testNetLog().currentlyLogging).to.be.false() }) it('should begin and end logging to file when .startLogging() and .stopLogging() is called', async () => { - await netLog.startLogging(dumpFileDynamic) + await testNetLog().startLogging(dumpFileDynamic) - expect(netLog.currentlyLogging).to.be.true() + expect(testNetLog().currentlyLogging).to.be.true() - expect(netLog.currentlyLoggingPath).to.equal(dumpFileDynamic) + expect(testNetLog().currentlyLoggingPath).to.equal(dumpFileDynamic) - await netLog.stopLogging() + await testNetLog().stopLogging() expect(fs.existsSync(dumpFileDynamic)).to.be.true() }) it('should throw an error when .stopLogging() is called without calling .startLogging()', async () => { - await expect(netLog.stopLogging()).to.be.rejectedWith('No net log in progress') + await expect(testNetLog().stopLogging()).to.be.rejectedWith('No net log in progress') }) it('should throw an error when .startLogging() is called with an invalid argument', () => { - expect(() => netLog.startLogging('')).to.throw() - expect(() => netLog.startLogging(null)).to.throw() - expect(() => netLog.startLogging([])).to.throw() + expect(() => testNetLog().startLogging('')).to.throw() + expect(() => testNetLog().startLogging(null)).to.throw() + expect(() => testNetLog().startLogging([])).to.throw() }) it('should begin and end logging automatically when --log-net-log is passed', done => { diff --git a/spec-main/extensions-spec.ts b/spec-main/extensions-spec.ts new file mode 100644 index 000000000000..f18ec31f658f --- /dev/null +++ b/spec-main/extensions-spec.ts @@ -0,0 +1,90 @@ +import { expect } from 'chai' +import { session, BrowserWindow, ipcMain } from 'electron' +import { closeAllWindows } from './window-helpers' +import * as http from 'http' +import { AddressInfo } from 'net' +import * as path from 'path' +import { ifdescribe } from './spec-helpers' +import { emittedOnce } from './events-helpers' + +const fixtures = path.join(__dirname, 'fixtures') + +ifdescribe(process.electronBinding('features').isExtensionsEnabled())('chrome extensions', () => { + // NB. extensions are only allowed on http://, https:// and ftp:// (!) urls by default. + let server: http.Server + let url: string + before(async () => { + server = http.createServer((req, res) => res.end()) + await new Promise(resolve => server.listen(0, '127.0.0.1', () => { + url = `http://127.0.0.1:${(server.address() as AddressInfo).port}` + resolve() + })) + }) + after(() => { + server.close() + }) + + afterEach(closeAllWindows) + it('loads an extension', async () => { + // NB. we have to use a persist: session (i.e. non-OTR) because the + // extension registry is redirected to the main session. so installing an + // extension in an in-memory session results in it being installed in the + // default session. + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + (customSession as any).loadChromeExtension(path.join(fixtures, 'extensions', 'red-bg')) + const w = new BrowserWindow({show: false, webPreferences: {session: customSession}}) + await w.loadURL(url) + const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') + expect(bg).to.equal('red') + }) + + it('confines an extension to the session it was loaded in', async () => { + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + (customSession as any).loadChromeExtension(path.join(fixtures, 'extensions', 'red-bg')) + const w = new BrowserWindow({show: false}) // not in the session + await w.loadURL(url) + const bg = await w.webContents.executeJavaScript('document.documentElement.style.backgroundColor') + expect(bg).to.equal('') + }) + + describe('chrome.runtime', () => { + let content: any + before(async () => { + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + (customSession as any).loadChromeExtension(path.join(fixtures, 'extensions', 'chrome-runtime')) + const w = new BrowserWindow({show: false, webPreferences: { session: customSession }}) + try { + await w.loadURL(url) + content = JSON.parse(await w.webContents.executeJavaScript('document.documentElement.textContent')) + expect(content).to.be.an('object') + } finally { + w.destroy() + } + }) + it('getManifest()', () => { + expect(content.manifest).to.be.an('object').with.property('name', 'chrome-runtime') + }) + it('id', () => { + expect(content.id).to.be.a('string').with.lengthOf(32) + }) + it('getURL()', () => { + expect(content.url).to.be.a('string').and.match(/^chrome-extension:\/\/.*main.js$/) + }) + }) + + describe('chrome.storage', () => { + it('stores and retrieves a key', async () => { + const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); + (customSession as any).loadChromeExtension(path.join(fixtures, 'extensions', 'chrome-storage')) + const w = new BrowserWindow({show: false, webPreferences: { session: customSession, nodeIntegration: true }}) + try { + const p = emittedOnce(ipcMain, 'storage-success') + await w.loadURL(url) + const [, v] = await p + expect(v).to.equal('value') + } finally { + w.destroy() + } + }) + }) +}) diff --git a/spec-main/fixtures/blank.html b/spec-main/fixtures/blank.html new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/spec-main/fixtures/extensions/chrome-runtime/main.js b/spec-main/fixtures/extensions/chrome-runtime/main.js new file mode 100644 index 000000000000..9caebefa5e97 --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-runtime/main.js @@ -0,0 +1,5 @@ +document.documentElement.textContent = JSON.stringify({ + manifest: chrome.runtime.getManifest(), + id: chrome.runtime.id, + url: chrome.runtime.getURL('main.js'), +}) diff --git a/spec-main/fixtures/extensions/chrome-runtime/manifest.json b/spec-main/fixtures/extensions/chrome-runtime/manifest.json new file mode 100644 index 000000000000..3428ef156ebe --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-runtime/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "chrome-runtime", + "version": "1.0", + "content_scripts": [ + { + "matches": [""], + "js": ["main.js"], + "run_at": "document_start" + } + ], + "manifest_version": 2 +} diff --git a/spec-main/fixtures/extensions/chrome-storage/main.js b/spec-main/fixtures/extensions/chrome-storage/main.js new file mode 100644 index 000000000000..91beec17c649 --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-storage/main.js @@ -0,0 +1,7 @@ +chrome.storage.local.set({key: 'value'}, () => { + chrome.storage.local.get(['key'], ({key}) => { + const script = document.createElement('script') + script.textContent = `require('electron').ipcRenderer.send('storage-success', ${JSON.stringify(key)})` + document.documentElement.appendChild(script) + }) +}) diff --git a/spec-main/fixtures/extensions/chrome-storage/manifest.json b/spec-main/fixtures/extensions/chrome-storage/manifest.json new file mode 100644 index 000000000000..ffcc0c1f6e28 --- /dev/null +++ b/spec-main/fixtures/extensions/chrome-storage/manifest.json @@ -0,0 +1,15 @@ +{ + "name": "chrome-storage", + "version": "1.0", + "content_scripts": [ + { + "matches": [""], + "js": ["main.js"], + "run_at": "document_start" + } + ], + "permissions": [ + "storage" + ], + "manifest_version": 2 +} diff --git a/spec-main/fixtures/extensions/red-bg/main.js b/spec-main/fixtures/extensions/red-bg/main.js new file mode 100644 index 000000000000..787b050adc8c --- /dev/null +++ b/spec-main/fixtures/extensions/red-bg/main.js @@ -0,0 +1 @@ +document.documentElement.style.backgroundColor = 'red' diff --git a/spec-main/fixtures/extensions/red-bg/manifest.json b/spec-main/fixtures/extensions/red-bg/manifest.json new file mode 100644 index 000000000000..53a9a6a11279 --- /dev/null +++ b/spec-main/fixtures/extensions/red-bg/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "red-bg", + "version": "1.0", + "content_scripts": [ + { + "matches": [""], + "js": ["main.js"], + "run_at": "document_start" + } + ], + "manifest_version": 2 +} diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index 5801ab94a3a9..d57c7b853c67 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -10,6 +10,7 @@ declare namespace NodeJS { isViewApiEnabled(): boolean; isTtsEnabled(): boolean; isPrintingEnabled(): boolean; + isExtensionsEnabled(): boolean; isComponentBuild(): boolean; }