diff --git a/BUILD.gn b/BUILD.gn index 100ebdf3a892..38712e4d4fc7 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -629,8 +629,6 @@ source_set("electron_lib") { deps += [ "//components/printing/common:mojo_interfaces" ] } - deps += [ "shell/common/extensions/api:extensions_features" ] - deps += [ "shell/common/extensions/api" ] deps += [ "//components/pref_registry", "//components/user_prefs", @@ -642,12 +640,22 @@ source_set("electron_lib") { ] if (enable_electron_extensions) { sources += filenames.lib_sources_extensions + deps += [ + "shell/browser/extensions/api:api_registration", + "shell/common/extensions/api", + "shell/common/extensions/api:extensions_features", + "//chrome/browser/resources:component_extension_resources", + "//components/zoom", + ] } if (enable_pdf) { # Printing depends on some //pdf code, so it needs to be built even if the # pdf viewer isn't enabled. - deps += [ "//pdf" ] + deps += [ + "//pdf", + "//pdf:features", + ] } if (enable_pdf_viewer) { deps += [ diff --git a/buildflags/buildflags.gni b/buildflags/buildflags.gni index 9bd6fe1ed6d8..ee6f22bf4357 100644 --- a/buildflags/buildflags.gni +++ b/buildflags/buildflags.gni @@ -14,7 +14,7 @@ declare_args() { enable_view_api = false - enable_pdf_viewer = false + enable_pdf_viewer = true enable_tts = true diff --git a/chromium_src/BUILD.gn b/chromium_src/BUILD.gn index fa60b2ca5479..d19e324c51c9 100644 --- a/chromium_src/BUILD.gn +++ b/chromium_src/BUILD.gn @@ -232,6 +232,12 @@ static_library("chrome") { if (enable_electron_extensions) { sources += [ + "//chrome/browser/extensions/chrome_url_request_util.cc", + "//chrome/browser/extensions/chrome_url_request_util.h", + "//chrome/browser/pdf/pdf_extension_util.cc", + "//chrome/browser/pdf/pdf_extension_util.h", + "//chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc", + "//chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h", "//chrome/renderer/extensions/extension_hooks_delegate.cc", "//chrome/renderer/extensions/extension_hooks_delegate.h", "//chrome/renderer/extensions/tabs_hooks_delegate.cc", diff --git a/electron_paks.gni b/electron_paks.gni index 6eb6ade1d7d9..2e405e6680fc 100644 --- a/electron_paks.gni +++ b/electron_paks.gni @@ -92,10 +92,14 @@ template("electron_extra_paks") { } if (enable_electron_extensions) { sources += [ + "$root_gen_dir/chrome/component_extension_resources.pak", "$root_gen_dir/extensions/extensions_renderer_resources.pak", "$root_gen_dir/extensions/extensions_resources.pak", ] - deps += [ "//extensions:extensions_resources" ] + deps += [ + "//chrome/browser/resources:component_extension_resources", + "//extensions:extensions_resources", + ] } } } diff --git a/electron_resources.grd b/electron_resources.grd index e46d6f6e7911..ae47cedd4232 100644 --- a/electron_resources.grd +++ b/electron_resources.grd @@ -17,7 +17,8 @@ - + + diff --git a/filenames.gni b/filenames.gni index 530b70919c93..70c31d964202 100644 --- a/filenames.gni +++ b/filenames.gni @@ -279,6 +279,8 @@ filenames = { "shell/browser/notifications/win/win32_notification.h", "shell/browser/notifications/win/windows_toast_notification.cc", "shell/browser/notifications/win/windows_toast_notification.h", + "shell/browser/plugins/plugin_utils.cc", + "shell/browser/plugins/plugin_utils.h", "shell/browser/pref_store_delegate.cc", "shell/browser/pref_store_delegate.h", "shell/browser/relauncher.cc", @@ -594,14 +596,18 @@ filenames = { ] lib_sources_extensions = [ + "shell/browser/extensions/api/resources_private/resources_private_api.cc", + "shell/browser/extensions/api/resources_private/resources_private_api.h", "shell/browser/extensions/api/runtime/electron_runtime_api_delegate.cc", "shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h", "shell/browser/extensions/api/tabs/tabs_api.cc", "shell/browser/extensions/api/tabs/tabs_api.h", - "shell/browser/extensions/electron_extensions_browser_client.cc", - "shell/browser/extensions/electron_extensions_browser_client.h", + "shell/browser/extensions/api/streams_private/streams_private_api.cc", + "shell/browser/extensions/api/streams_private/streams_private_api.h", "shell/browser/extensions/electron_browser_context_keyed_service_factories.cc", "shell/browser/extensions/electron_browser_context_keyed_service_factories.h", + "shell/browser/extensions/electron_component_extension_resource_manager.cc", + "shell/browser/extensions/electron_component_extension_resource_manager.h", "shell/browser/extensions/electron_display_info_provider.cc", "shell/browser/extensions/electron_display_info_provider.h", "shell/browser/extensions/electron_extension_host_delegate.cc", @@ -614,24 +620,26 @@ filenames = { "shell/browser/extensions/electron_extension_system_factory.h", "shell/browser/extensions/electron_extension_web_contents_observer.cc", "shell/browser/extensions/electron_extension_web_contents_observer.h", - "shell/browser/extensions/electron_navigation_ui_data.cc", - "shell/browser/extensions/electron_navigation_ui_data.h", - "shell/browser/extensions/electron_process_manager_delegate.cc", - "shell/browser/extensions/electron_process_manager_delegate.h", "shell/browser/extensions/electron_extensions_api_client.cc", "shell/browser/extensions/electron_extensions_api_client.h", "shell/browser/extensions/electron_extensions_browser_api_provider.cc", "shell/browser/extensions/electron_extensions_browser_api_provider.h", + "shell/browser/extensions/electron_extensions_browser_client.cc", + "shell/browser/extensions/electron_extensions_browser_client.h", "shell/browser/extensions/electron_messaging_delegate.cc", "shell/browser/extensions/electron_messaging_delegate.h", + "shell/browser/extensions/electron_navigation_ui_data.cc", + "shell/browser/extensions/electron_navigation_ui_data.h", + "shell/browser/extensions/electron_process_manager_delegate.cc", + "shell/browser/extensions/electron_process_manager_delegate.h", "shell/common/extensions/electron_extensions_api_provider.cc", "shell/common/extensions/electron_extensions_api_provider.h", "shell/common/extensions/electron_extensions_client.cc", "shell/common/extensions/electron_extensions_client.h", - "shell/renderer/extensions/electron_extensions_renderer_client.cc", - "shell/renderer/extensions/electron_extensions_renderer_client.h", "shell/renderer/extensions/electron_extensions_dispatcher_delegate.cc", "shell/renderer/extensions/electron_extensions_dispatcher_delegate.h", + "shell/renderer/extensions/electron_extensions_renderer_client.cc", + "shell/renderer/extensions/electron_extensions_renderer_client.h", ] app_sources = [ diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 7e372d58f720..cc9206139258 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -82,3 +82,5 @@ accessible_pane_view.patch fixme_grit_conflicts.patch fix_use_the_new_mediaplaypause_key_listener_for_internal_chrome.patch fix_use_native_window_button_positions_when_macos_locale_is_rtl.patch +use_electron_resources_in_pdf_util.patch +hack_plugin_response_interceptor_to_point_to_electron.patch diff --git a/patches/chromium/hack_plugin_response_interceptor_to_point_to_electron.patch b/patches/chromium/hack_plugin_response_interceptor_to_point_to_electron.patch new file mode 100644 index 000000000000..8952d3cf0eb0 --- /dev/null +++ b/patches/chromium/hack_plugin_response_interceptor_to_point_to_electron.patch @@ -0,0 +1,24 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jeremy Apthorp +Date: Mon, 10 Feb 2020 11:52:23 -0800 +Subject: hack plugin response interceptor to point to electron + +chrome's streams_private_api does prerender and other things and would +require a largeish patch to get working, so just redirect it to our +implementation instead. + +diff --git a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc +index 1c9aeb8fa71d054159c2ccccccbe57aa474d417e..0bd6412f2c79f77f936720b1a0795b50ecefd26c 100644 +--- a/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc ++++ b/chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc +@@ -8,8 +8,8 @@ + #include "base/feature_list.h" + #include "base/guid.h" + #include "base/task/post_task.h" +-#include "chrome/browser/extensions/api/streams_private/streams_private_api.h" +-#include "chrome/browser/plugins/plugin_utils.h" ++#include "electron/shell/browser/extensions/api/streams_private/streams_private_api.h" ++#include "electron/shell/browser/plugins/plugin_utils.h" + #include "content/public/browser/browser_task_traits.h" + #include "content/public/browser/browser_thread.h" + #include "content/public/browser/download_utils.h" diff --git a/patches/chromium/use_electron_resources_in_pdf_util.patch b/patches/chromium/use_electron_resources_in_pdf_util.patch new file mode 100644 index 000000000000..d3acae7d7556 --- /dev/null +++ b/patches/chromium/use_electron_resources_in_pdf_util.patch @@ -0,0 +1,22 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Jeremy Apthorp +Date: Mon, 10 Feb 2020 10:37:48 -0800 +Subject: use electron resources in pdf_util + +Without this, the ID for IDR_PDF_MANIFEST will be wrong on linux +and cause a DCHECK(), since the resource will be loaded as an empty +string. + +diff --git a/chrome/browser/pdf/pdf_extension_util.cc b/chrome/browser/pdf/pdf_extension_util.cc +index 877d0ba2f48359243527ddebf1d91132b3d5c455..cd69de498c4968ade0ffabf45b72d4d63b70b10f 100644 +--- a/chrome/browser/pdf/pdf_extension_util.cc ++++ b/chrome/browser/pdf/pdf_extension_util.cc +@@ -6,7 +6,7 @@ + + #include "base/strings/string_util.h" + #include "chrome/common/chrome_content_client.h" +-#include "chrome/grit/browser_resources.h" ++#include "electron/grit/electron_resources.h" + #include "ui/base/resource/resource_bundle.h" + + namespace pdf_extension_util { diff --git a/shell/app/electron_content_client.cc b/shell/app/electron_content_client.cc index a3938c36a27a..def214d67d91 100644 --- a/shell/app/electron_content_client.cc +++ b/shell/app/electron_content_client.cc @@ -33,12 +33,13 @@ #endif // defined(WIDEVINE_CDM_AVAILABLE) #if BUILDFLAG(ENABLE_PDF_VIEWER) -#include "pdf/pdf.h" // nogncheck -#include "pdf/pdf_ppapi.h" // nogncheck -#include "shell/common/electron_constants.h" // nogncheck -#endif // BUILDFLAG(ENABLE_PDF_VIEWER) +#include "pdf/pdf.h" // nogncheck +#include "pdf/pdf_ppapi.h" // nogncheck +#include "shell/common/electron_constants.h" +#endif // BUILDFLAG(ENABLE_PDF_VIEWER) #if BUILDFLAG(ENABLE_PLUGINS) +#include "content/public/browser/plugin_service.h" #include "content/public/common/pepper_plugin_info.h" #include "ppapi/shared_impl/ppapi_permissions.h" #endif // BUILDFLAG(ENABLE_PLUGINS) @@ -155,7 +156,8 @@ void ComputeBuiltInPlugins(std::vector* plugins) { pdf_info.is_out_of_process = true; pdf_info.name = "Chromium PDF Viewer"; pdf_info.description = "Portable Document Format"; - pdf_info.path = base::FilePath(FILE_PATH_LITERAL("internal-pdf-viewer")); + // This isn't a real file path; it's just used as a unique identifier. + pdf_info.path = base::FilePath(kPdfPluginPath); content::WebPluginMimeType pdf_mime_type(kPdfPluginMimeType, "pdf", "Portable Document Format"); pdf_info.mime_types.push_back(pdf_mime_type); @@ -166,6 +168,21 @@ void ComputeBuiltInPlugins(std::vector* plugins) { chrome_pdf::PPP_ShutdownModule; pdf_info.permissions = ppapi::PERMISSION_PDF | ppapi::PERMISSION_DEV; plugins->push_back(pdf_info); + + // NB. in Chrome, this plugin isn't registered until the PDF extension is + // loaded. However, in Electron, we load the PDF extension unconditionally + // when it is enabled in the build, so we're OK to load the plugin eagerly + // here. + content::WebPluginInfo info; + info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN; + info.name = base::UTF8ToUTF16("Chromium PDF Viewer"); + // This isn't a real file path; it's just used as a unique identifier. + info.path = base::FilePath::FromUTF8Unsafe(extension_misc::kPdfExtensionId); + info.background_color = content::WebPluginInfo::kDefaultBackgroundColor; + info.mime_types.emplace_back("application/pdf", "pdf", + "Portable Document Format"); + content::PluginService::GetInstance()->RefreshPlugins(); + content::PluginService::GetInstance()->RegisterInternalPlugin(info, true); #endif // BUILDFLAG(ENABLE_PDF_VIEWER) } #endif // BUILDFLAG(ENABLE_PLUGINS) diff --git a/shell/app/electron_main_delegate.cc b/shell/app/electron_main_delegate.cc index 45eae234226c..0430df19f7a4 100644 --- a/shell/app/electron_main_delegate.cc +++ b/shell/app/electron_main_delegate.cc @@ -118,15 +118,6 @@ void LoadResourceBundle(const std::string& locale) { bundle.ReloadLocaleResources(locale); bundle.AddDataPackFromPath(pak_dir.Append(FILE_PATH_LITERAL("resources.pak")), ui::SCALE_FACTOR_NONE); -#if BUILDFLAG(ENABLE_PDF_VIEWER) - NOTIMPLEMENTED() - << "Hi, whoever's fixing PDF support! Thanks! The pdf " - "viewer resources haven't been ported over to the GN build yet, so " - "you'll probably need to change this bit of code."; - bundle.AddDataPackFromPath( - pak_dir.Append(FILE_PATH_LITERAL("pdf_viewer_resources.pak")), - ui::GetSupportedScaleFactors()[0]); -#endif // BUILDFLAG(ENABLE_PDF_VIEWER) } ElectronMainDelegate::ElectronMainDelegate() = default; diff --git a/shell/browser/api/electron_api_session.cc b/shell/browser/api/electron_api_session.cc index e73727c9887b..7c173c5328a8 100644 --- a/shell/browser/api/electron_api_session.cc +++ b/shell/browser/api/electron_api_session.cc @@ -702,7 +702,8 @@ v8::Local Session::GetAllExtensions() { auto installed_extensions = registry->GenerateInstalledExtensionsSet(); std::vector extensions_vector; for (const auto& extension : *installed_extensions) { - extensions_vector.emplace_back(extension.get()); + if (extension->location() != extensions::Manifest::COMPONENT) + extensions_vector.emplace_back(extension.get()); } return gin::ConvertToV8(isolate(), extensions_vector); } diff --git a/shell/browser/electron_browser_client.cc b/shell/browser/electron_browser_client.cc index 0f5f7480c05d..3a4ab2508176 100644 --- a/shell/browser/electron_browser_client.cc +++ b/shell/browser/electron_browser_client.cc @@ -49,6 +49,7 @@ #include "extensions/common/constants.h" #include "net/base/escape.h" #include "net/ssl/ssl_cert_request_info.h" +#include "ppapi/buildflags/buildflags.h" #include "ppapi/host/ppapi_host.h" #include "printing/buildflags/buildflags.h" #include "services/device/public/cpp/geolocation/location_provider.h" @@ -129,13 +130,29 @@ #endif // BUILDFLAG(ENABLE_PRINTING) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#include "chrome/common/webui_url_constants.h" +#include "content/public/browser/child_process_security_policy.h" +#include "content/public/browser/file_url_loader.h" +#include "content/public/browser/web_ui_url_loader_factory.h" +#include "extensions/browser/api/mime_handler_private/mime_handler_private.h" +#include "extensions/browser/extension_host.h" #include "extensions/browser/extension_message_filter.h" #include "extensions/browser/extension_navigation_throttle.h" #include "extensions/browser/extension_registry.h" +#include "extensions/browser/guest_view/extensions_guest_view_message_filter.h" +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" #include "extensions/browser/info_map.h" +#include "extensions/browser/process_manager.h" #include "extensions/browser/process_map.h" +#include "extensions/common/api/mime_handler.mojom.h" #include "extensions/common/extension.h" #include "shell/browser/extensions/electron_extension_system.h" +#include "shell/browser/extensions/electron_extension_web_contents_observer.h" +#endif + +#if BUILDFLAG(ENABLE_PLUGINS) +#include "chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.h" // nogncheck +#include "shell/browser/plugins/plugin_utils.h" #endif #if defined(OS_MACOSX) @@ -443,6 +460,8 @@ void ElectronBrowserClient::RenderProcessWillLaunch( #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) host->AddFilter( new extensions::ExtensionMessageFilter(process_id, browser_context)); + host->AddFilter(new extensions::ExtensionsGuestViewMessageFilter( + process_id, browser_context)); #endif ProcessPreferences prefs; @@ -785,6 +804,7 @@ void ElectronBrowserClient::GetAdditionalAllowedSchemesForFileSystem( additional_schemes->insert(additional_schemes->end(), schemes_list.begin(), schemes_list.end()); additional_schemes->push_back(content::kChromeDevToolsScheme); + additional_schemes->push_back(content::kChromeUIScheme); } void ElectronBrowserClient::GetAdditionalWebUISchemes( @@ -1144,28 +1164,123 @@ void ElectronBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories( protocol->RegisterURLLoaderFactories(factories); } +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +namespace { + +// The FileURLLoaderFactory provided to the extension background pages. +// Checks with the ChildProcessSecurityPolicy to validate the file access. +class FileURLLoaderFactory : public network::mojom::URLLoaderFactory { + public: + explicit FileURLLoaderFactory(int child_id) : child_id_(child_id) {} + + private: + // network::mojom::URLLoaderFactory: + void CreateLoaderAndStart( + mojo::PendingReceiver loader, + int32_t routing_id, + int32_t request_id, + uint32_t options, + const network::ResourceRequest& request, + mojo::PendingRemote client, + const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) + override { + if (!content::ChildProcessSecurityPolicy::GetInstance()->CanRequestURL( + child_id_, request.url)) { + mojo::Remote(std::move(client)) + ->OnComplete( + network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED)); + return; + } + content::CreateFileURLLoaderBypassingSecurityChecks( + request, std::move(loader), std::move(client), + /*observer=*/nullptr, + /* allow_directory_listing */ true); + } + + void Clone( + mojo::PendingReceiver loader) override { + receivers_.Add(this, std::move(loader)); + } + + int child_id_; + mojo::ReceiverSet receivers_; + DISALLOW_COPY_AND_ASSIGN(FileURLLoaderFactory); +}; + +} // namespace +#endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + void ElectronBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories( int render_process_id, int render_frame_id, NonNetworkURLLoaderFactoryMap* factories) { -#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) - auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id, - render_frame_id); - if (factory) - factories->emplace(extensions::kExtensionScheme, std::move(factory)); -#endif - - // Chromium may call this even when NetworkService is not enabled. content::RenderFrameHost* frame_host = content::RenderFrameHost::FromID(render_process_id, render_frame_id); content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(frame_host); + if (web_contents) { api::Protocol* protocol = api::Protocol::FromWrappedClass( v8::Isolate::GetCurrent(), web_contents->GetBrowserContext()); if (protocol) protocol->RegisterURLLoaderFactories(factories); } +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id, + render_frame_id); + if (factory) + factories->emplace(extensions::kExtensionScheme, std::move(factory)); + + if (!web_contents) + return; + + extensions::ElectronExtensionWebContentsObserver* web_observer = + extensions::ElectronExtensionWebContentsObserver::FromWebContents( + web_contents); + + // There is nothing to do if no ElectronExtensionWebContentsObserver is + // attached to the |web_contents|. + if (!web_observer) + return; + + const extensions::Extension* extension = + web_observer->GetExtensionFromFrame(frame_host, false); + if (!extension) + return; + + // Support for chrome:// scheme if appropriate. + if (extension->is_extension() && + extensions::Manifest::IsComponentLocation(extension->location())) { + // Components of chrome that are implemented as extensions or platform apps + // are allowed to use chrome://resources/ and chrome://theme/ URLs. + factories->emplace( + content::kChromeUIScheme, + content::CreateWebUIURLLoader(frame_host, content::kChromeUIScheme, + {content::kChromeUIResourcesHost})); + } + + // Extension with a background page get file access that gets approval from + // ChildProcessSecurityPolicy. + extensions::ExtensionHost* host = + extensions::ProcessManager::Get(web_contents->GetBrowserContext()) + ->GetBackgroundHostForExtension(extension->id()); + if (host) { + factories->emplace(url::kFileScheme, std::make_unique( + render_process_id)); + } +#endif +} + +bool ElectronBrowserClient::ShouldTreatURLSchemeAsFirstPartyWhenTopLevel( + base::StringPiece scheme, + bool is_embedded_origin_secure) { + if (is_embedded_origin_secure && scheme == content::kChromeUIScheme) + return true; +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + return scheme == extensions::kExtensionScheme; +#else + return false; +#endif } bool ElectronBrowserClient::WillInterceptWebSocket( @@ -1331,11 +1446,31 @@ void ElectronBrowserClient::BindHostReceiverForRenderer( #endif } +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +void BindMimeHandlerService( + content::RenderFrameHost* frame_host, + mojo::PendingReceiver + receiver) { + content::WebContents* contents = + content::WebContents::FromRenderFrameHost(frame_host); + auto* guest_view = + extensions::MimeHandlerViewGuest::FromWebContents(contents); + if (!guest_view) + return; + extensions::MimeHandlerServiceImpl::Create(guest_view->GetStreamWeakPtr(), + std::move(receiver)); +} +#endif + void ElectronBrowserClient::RegisterBrowserInterfaceBindersForFrame( content::RenderFrameHost* render_frame_host, service_manager::BinderMapWithContext* map) { map->Add( base::BindRepeating(&BindNetworkHintsHandler)); +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + map->Add( + base::BindRepeating(&BindMimeHandlerService)); +#endif } std::unique_ptr @@ -1353,4 +1488,35 @@ ElectronBrowserClient::CreateLoginDelegate( first_auth_attempt, std::move(auth_required_callback)); } +std::vector> +ElectronBrowserClient::CreateURLLoaderThrottles( + const network::ResourceRequest& request, + content::BrowserContext* browser_context, + const base::RepeatingCallback& wc_getter, + content::NavigationUIData* navigation_ui_data, + int frame_tree_node_id) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + std::vector> result; + +#if BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + result.push_back(std::make_unique( + request.resource_type, frame_tree_node_id)); +#endif + + return result; +} + +base::flat_set +ElectronBrowserClient::GetPluginMimeTypesWithExternalHandlers( + content::BrowserContext* browser_context) { + base::flat_set mime_types; +#if BUILDFLAG(ENABLE_PLUGINS) && BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + auto map = PluginUtils::GetMimeTypeToExtensionIdMap(browser_context); + for (const auto& pair : map) + mime_types.insert(pair.first); +#endif + return mime_types; +} + } // namespace electron diff --git a/shell/browser/electron_browser_client.h b/shell/browser/electron_browser_client.h index 4ac718f7c34d..280420dcbba8 100644 --- a/shell/browser/electron_browser_client.h +++ b/shell/browser/electron_browser_client.h @@ -195,6 +195,9 @@ class ElectronBrowserClient : public content::ContentBrowserClient, bool* bypass_redirect_checks, bool* disable_secure_dns, network::mojom::URLLoaderFactoryOverridePtr* factory_override) override; + bool ShouldTreatURLSchemeAsFirstPartyWhenTopLevel( + base::StringPiece scheme, + bool is_embedded_origin_secure) override; void OverrideURLLoaderFactoryParams( content::BrowserContext* browser_context, const url::Origin& origin, @@ -230,6 +233,15 @@ class ElectronBrowserClient : public content::ContentBrowserClient, bool first_auth_attempt, LoginAuthRequiredCallback auth_required_callback) override; void SiteInstanceGotProcess(content::SiteInstance* site_instance) override; + std::vector> + CreateURLLoaderThrottles( + const network::ResourceRequest& request, + content::BrowserContext* browser_context, + const base::RepeatingCallback& wc_getter, + content::NavigationUIData* navigation_ui_data, + int frame_tree_node_id) override; + base::flat_set GetPluginMimeTypesWithExternalHandlers( + content::BrowserContext* browser_context) override; bool IsSuitableHost(content::RenderProcessHost* process_host, const GURL& site_url) override; bool ShouldUseProcessPerSite(content::BrowserContext* browser_context, diff --git a/shell/browser/extensions/api/BUILD.gn b/shell/browser/extensions/api/BUILD.gn new file mode 100644 index 000000000000..bb19064bed01 --- /dev/null +++ b/shell/browser/extensions/api/BUILD.gn @@ -0,0 +1,35 @@ +# 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. + +import("//extensions/buildflags/buildflags.gni") +import("//tools/json_schema_compiler/json_schema_api.gni") + +assert(enable_extensions, + "Cannot depend on extensions because enable_extensions=false.") + +function_registration("api_registration") { + sources = [ + "//electron/shell/common/extensions/api/extension.json", + "//electron/shell/common/extensions/api/resources_private.idl", + "//electron/shell/common/extensions/api/tabs.json", + ] + impl_dir = "//electron/shell/browser/extensions/api" + configs = [ "//build/config:precompiled_headers" ] + bundle_name = "Electron" + root_namespace = "extensions::api::%(namespace)s" + schema_include_rules = "extensions/common/api:extensions::api::%(namespace)s" + + deps = [ + # Different APIs include headers from these targets. + "//components/zoom", + "//content/public/browser", + "//extensions/browser", + + # Different APIs include some headers from chrome/common that in turn + # include generated headers from these targets. + # TODO(brettw) this should be made unnecessary if possible. + "//electron/shell/common/extensions/api", + ] + deps += [ "//extensions/common/api" ] +} diff --git a/shell/browser/extensions/api/resources_private/resources_private_api.cc b/shell/browser/extensions/api/resources_private/resources_private_api.cc new file mode 100644 index 000000000000..73d8ea1edac8 --- /dev/null +++ b/shell/browser/extensions/api/resources_private/resources_private_api.cc @@ -0,0 +1,113 @@ +// 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. + +#include "shell/browser/extensions/api/resources_private/resources_private_api.h" + +#include +#include +#include + +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/common/extensions/api/resources_private.h" +#include "chrome/grit/generated_resources.h" +#include "components/strings/grit/components_strings.h" +#include "components/zoom/page_zoom_constants.h" +#include "pdf/buildflags.h" +#include "printing/buildflags/buildflags.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/webui/web_ui_util.h" + +#if BUILDFLAG(ENABLE_PDF) +#include "pdf/pdf_features.h" +#endif // BUILDFLAG(ENABLE_PDF) + +// To add a new component to this API, simply: +// 1. Add your component to the Component enum in +// chrome/common/extensions/api/resources_private.idl +// 2. Create an AddStringsForMyComponent(base::DictionaryValue * dict) method. +// 3. Tie in that method to the switch statement in Run() + +namespace extensions { + +namespace { + +void AddStringsForPdf(base::DictionaryValue* dict) { +#if BUILDFLAG(ENABLE_PDF) + static constexpr webui::LocalizedString kPdfResources[] = { + {"passwordDialogTitle", IDS_PDF_PASSWORD_DIALOG_TITLE}, + {"passwordPrompt", IDS_PDF_NEED_PASSWORD}, + {"passwordSubmit", IDS_PDF_PASSWORD_SUBMIT}, + {"passwordInvalid", IDS_PDF_PASSWORD_INVALID}, + {"pageLoading", IDS_PDF_PAGE_LOADING}, + {"pageLoadFailed", IDS_PDF_PAGE_LOAD_FAILED}, + {"errorDialogTitle", IDS_PDF_ERROR_DIALOG_TITLE}, + {"pageReload", IDS_PDF_PAGE_RELOAD_BUTTON}, + {"bookmarks", IDS_PDF_BOOKMARKS}, + {"labelPageNumber", IDS_PDF_LABEL_PAGE_NUMBER}, + {"tooltipRotateCW", IDS_PDF_TOOLTIP_ROTATE_CW}, + {"tooltipDownload", IDS_PDF_TOOLTIP_DOWNLOAD}, + {"tooltipPrint", IDS_PDF_TOOLTIP_PRINT}, + {"tooltipFitToPage", IDS_PDF_TOOLTIP_FIT_PAGE}, + {"tooltipFitToWidth", IDS_PDF_TOOLTIP_FIT_WIDTH}, + {"tooltipZoomIn", IDS_PDF_TOOLTIP_ZOOM_IN}, + {"tooltipZoomOut", IDS_PDF_TOOLTIP_ZOOM_OUT}, + }; + for (const auto& resource : kPdfResources) + dict->SetString(resource.name, l10n_util::GetStringUTF16(resource.id)); + + dict->SetString("presetZoomFactors", zoom::GetPresetZoomFactorsAsJSON()); +#endif // BUILDFLAG(ENABLE_PDF) +} + +void AddAdditionalDataForPdf(base::DictionaryValue* dict) { +#if BUILDFLAG(ENABLE_PDF) + dict->SetKey("pdfFormSaveEnabled", + base::Value(base::FeatureList::IsEnabled( + chrome_pdf::features::kSaveEditedPDFForm))); + dict->SetKey("pdfAnnotationsEnabled", + base::Value(base::FeatureList::IsEnabled( + chrome_pdf::features::kPDFAnnotations))); + + // TODO(nornagon): enable printing once it works. + bool enable_printing = false; + dict->SetKey("printingEnabled", base::Value(enable_printing)); +#endif // BUILDFLAG(ENABLE_PDF) +} + +} // namespace + +namespace get_strings = api::resources_private::GetStrings; + +ResourcesPrivateGetStringsFunction::ResourcesPrivateGetStringsFunction() {} + +ResourcesPrivateGetStringsFunction::~ResourcesPrivateGetStringsFunction() {} + +ExtensionFunction::ResponseAction ResourcesPrivateGetStringsFunction::Run() { + std::unique_ptr params( + get_strings::Params::Create(*args_)); + auto dict = std::make_unique(); + + api::resources_private::Component component = params->component; + + switch (component) { + case api::resources_private::COMPONENT_PDF: + AddStringsForPdf(dict.get()); + AddAdditionalDataForPdf(dict.get()); + break; + case api::resources_private::COMPONENT_IDENTITY: + NOTREACHED(); + break; + case api::resources_private::COMPONENT_NONE: + NOTREACHED(); + break; + } + + const std::string& app_locale = g_browser_process->GetApplicationLocale(); + webui::SetLoadTimeDataDefaults(app_locale, dict.get()); + + return RespondNow(OneArgument(std::move(dict))); +} + +} // namespace extensions diff --git a/shell/browser/extensions/api/resources_private/resources_private_api.h b/shell/browser/extensions/api/resources_private/resources_private_api.h new file mode 100644 index 000000000000..8dcd8f990c18 --- /dev/null +++ b/shell/browser/extensions/api/resources_private/resources_private_api.h @@ -0,0 +1,31 @@ +// 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_API_RESOURCES_PRIVATE_RESOURCES_PRIVATE_API_H_ +#define SHELL_BROWSER_EXTENSIONS_API_RESOURCES_PRIVATE_RESOURCES_PRIVATE_API_H_ + +#include "base/macros.h" +#include "extensions/browser/extension_function.h" + +namespace extensions { + +class ResourcesPrivateGetStringsFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("resourcesPrivate.getStrings", + RESOURCESPRIVATE_GETSTRINGS) + ResourcesPrivateGetStringsFunction(); + + protected: + ~ResourcesPrivateGetStringsFunction() override; + + // Override from ExtensionFunction: + ExtensionFunction::ResponseAction Run() override; + + private: + DISALLOW_COPY_AND_ASSIGN(ResourcesPrivateGetStringsFunction); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_API_RESOURCES_PRIVATE_RESOURCES_PRIVATE_API_H_ diff --git a/shell/browser/extensions/api/streams_private/streams_private_api.cc b/shell/browser/extensions/api/streams_private/streams_private_api.cc new file mode 100644 index 000000000000..0976ad3d4a80 --- /dev/null +++ b/shell/browser/extensions/api/streams_private/streams_private_api.cc @@ -0,0 +1,74 @@ +// 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. + +#include "electron/shell/browser/extensions/api/streams_private/streams_private_api.h" + +#include + +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/browser/web_contents.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_stream_manager.h" +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest.h" +#include "extensions/common/manifest_handlers/mime_types_handler.h" +#include "shell/browser/api/electron_api_web_contents.h" + +namespace extensions { + +void StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent( + const std::string& extension_id, + const std::string& view_id, + bool embedded, + int frame_tree_node_id, + int render_process_id, + int render_frame_id, + content::mojom::TransferrableURLLoaderPtr transferrable_loader, + const GURL& original_url) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + content::WebContents* web_contents = nullptr; + if (frame_tree_node_id != -1) { + web_contents = + content::WebContents::FromFrameTreeNodeId(frame_tree_node_id); + } else { + web_contents = content::WebContents::FromRenderFrameHost( + content::RenderFrameHost::FromID(render_process_id, render_frame_id)); + } + if (!web_contents) + return; + + auto* browser_context = web_contents->GetBrowserContext(); + + const extensions::Extension* extension = + extensions::ExtensionRegistry::Get(browser_context) + ->enabled_extensions() + .GetByID(extension_id); + if (!extension) + return; + + MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension); + if (!handler->HasPlugin()) + return; + + // If the mime handler uses MimeHandlerViewGuest, the MimeHandlerViewGuest + // will take ownership of the stream. + GURL handler_url( + extensions::Extension::GetBaseURLFromExtensionId(extension_id).spec() + + handler->handler_url()); + int tab_id = -1; + auto* api_contents = electron::api::WebContents::FromWrappedClass( + v8::Isolate::GetCurrent(), web_contents); + if (api_contents) + tab_id = api_contents->ID(); + std::unique_ptr stream_container( + new extensions::StreamContainer( + tab_id, embedded, handler_url, extension_id, + std::move(transferrable_loader), original_url)); + extensions::MimeHandlerStreamManager::Get(browser_context) + ->AddStream(view_id, std::move(stream_container), frame_tree_node_id, + render_process_id, render_frame_id); +} + +} // namespace extensions diff --git a/shell/browser/extensions/api/streams_private/streams_private_api.h b/shell/browser/extensions/api/streams_private/streams_private_api.h new file mode 100644 index 000000000000..97525ecdf35c --- /dev/null +++ b/shell/browser/extensions/api/streams_private/streams_private_api.h @@ -0,0 +1,44 @@ +// 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. + +#ifndef SHELL_BROWSER_EXTENSIONS_API_STREAMS_PRIVATE_STREAMS_PRIVATE_API_H_ +#define SHELL_BROWSER_EXTENSIONS_API_STREAMS_PRIVATE_STREAMS_PRIVATE_API_H_ + +#include +#include + +#include "base/macros.h" +#include "content/public/common/transferrable_url_loader.mojom.h" + +namespace extensions { + +// TODO(devlin): This is now only used for the MimeTypesHandler API. We should +// rename and move it to make that clear. https://crbug.com/890401. +class StreamsPrivateAPI { + public: + // Send the onExecuteMimeTypeHandler event to |extension_id|. If the viewer is + // being opened in a BrowserPlugin, specify a non-empty |view_id| of the + // plugin. |embedded| should be set to whether the document is embedded + // within another document. The |frame_tree_node_id| parameter is used for the + // top level plugins case. (PDF, etc). If this parameter has a valid value + // then it overrides the |render_process_id| and |render_frame_id| parameters. + // The |render_process_id| is the id of the renderer process. The + // |render_frame_id| is the routing id of the RenderFrameHost. + // + // If the network service is not enabled, |stream| is used; otherwise, + // |transferrable_loader| and |original_url| are used instead. + static void SendExecuteMimeTypeHandlerEvent( + const std::string& extension_id, + const std::string& view_id, + bool embedded, + int frame_tree_node_id, + int render_process_id, + int render_frame_id, + content::mojom::TransferrableURLLoaderPtr transferrable_loader, + const GURL& original_url); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_API_STREAMS_PRIVATE_STREAMS_PRIVATE_API_H_ diff --git a/shell/browser/extensions/api/tabs/tabs_api.cc b/shell/browser/extensions/api/tabs/tabs_api.cc index cff0bd6d121d..be7576061b79 100644 --- a/shell/browser/extensions/api/tabs/tabs_api.cc +++ b/shell/browser/extensions/api/tabs/tabs_api.cc @@ -12,13 +12,48 @@ #include "extensions/common/manifest_constants.h" #include "extensions/common/permissions/permissions_data.h" #include "shell/browser/api/electron_api_web_contents.h" +#include "shell/browser/web_contents_zoom_controller.h" +#include "shell/common/extensions/api/tabs.h" +#include "third_party/blink/public/common/page/page_zoom.h" + +using electron::WebContentsZoomController; namespace extensions { +namespace tabs = api::tabs; + const char kFrameNotFoundError[] = "No frame with id * in tab *."; +const char kPerOriginOnlyInAutomaticError[] = + "Can only set scope to " + "\"per-origin\" in \"automatic\" mode."; using api::extension_types::InjectDetails; +namespace { +void ZoomModeToZoomSettings(WebContentsZoomController::ZoomMode zoom_mode, + api::tabs::ZoomSettings* zoom_settings) { + DCHECK(zoom_settings); + switch (zoom_mode) { + case WebContentsZoomController::ZoomMode::DEFAULT: + zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC; + zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN; + break; + case WebContentsZoomController::ZoomMode::ISOLATED: + zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_AUTOMATIC; + zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB; + break; + case WebContentsZoomController::ZoomMode::MANUAL: + zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_MANUAL; + zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB; + break; + case WebContentsZoomController::ZoomMode::DISABLED: + zoom_settings->mode = api::tabs::ZOOM_SETTINGS_MODE_DISABLED; + zoom_settings->scope = api::tabs::ZOOM_SETTINGS_SCOPE_PER_TAB; + break; + } +} +} // namespace + ExecuteCodeInTabFunction::ExecuteCodeInTabFunction() : execute_tab_id_(-1) {} ExecuteCodeInTabFunction::~ExecuteCodeInTabFunction() {} @@ -130,4 +165,149 @@ bool TabsExecuteScriptFunction::ShouldInsertCSS() const { return false; } +ExtensionFunction::ResponseAction TabsGetFunction::Run() { + std::unique_ptr params(tabs::Get::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params.get()); + int tab_id = params->tab_id; + + auto* contents = electron::api::WebContents::FromWeakMapID( + v8::Isolate::GetCurrent(), tab_id); + if (!contents) + return RespondNow(Error("No such tab")); + + tabs::Tab tab; + + tab.id.reset(new int(tab_id)); + // TODO(nornagon): in Chrome, the tab URL is only available to extensions + // that have the "tabs" (or "activeTab") permission. We should do the same + // permission check here. + tab.url = std::make_unique( + contents->web_contents()->GetLastCommittedURL().spec()); + + return RespondNow(ArgumentList(tabs::Get::Results::Create(std::move(tab)))); +} + +ExtensionFunction::ResponseAction TabsSetZoomFunction::Run() { + std::unique_ptr params( + tabs::SetZoom::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + int tab_id = params->tab_id ? *params->tab_id : -1; + auto* contents = electron::api::WebContents::FromWeakMapID( + v8::Isolate::GetCurrent(), tab_id); + if (!contents) + return RespondNow(Error("No such tab")); + + auto* web_contents = contents->web_contents(); + GURL url(web_contents->GetVisibleURL()); + std::string error; + if (extension()->permissions_data()->IsRestrictedUrl(url, &error)) + return RespondNow(Error(error)); + + auto* zoom_controller = contents->GetZoomController(); + double zoom_level = + params->zoom_factor > 0 + ? blink::PageZoomFactorToZoomLevel(params->zoom_factor) + : blink::PageZoomFactorToZoomLevel( + zoom_controller->GetDefaultZoomFactor()); + + zoom_controller->SetZoomLevel(zoom_level); + + return RespondNow(NoArguments()); +} + +ExtensionFunction::ResponseAction TabsGetZoomFunction::Run() { + std::unique_ptr params( + tabs::GetZoom::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + int tab_id = params->tab_id ? *params->tab_id : -1; + auto* contents = electron::api::WebContents::FromWeakMapID( + v8::Isolate::GetCurrent(), tab_id); + if (!contents) + return RespondNow(Error("No such tab")); + + double zoom_level = contents->GetZoomController()->GetZoomLevel(); + double zoom_factor = blink::PageZoomLevelToZoomFactor(zoom_level); + + return RespondNow(ArgumentList(tabs::GetZoom::Results::Create(zoom_factor))); +} + +ExtensionFunction::ResponseAction TabsGetZoomSettingsFunction::Run() { + std::unique_ptr params( + tabs::GetZoomSettings::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + int tab_id = params->tab_id ? *params->tab_id : -1; + auto* contents = electron::api::WebContents::FromWeakMapID( + v8::Isolate::GetCurrent(), tab_id); + if (!contents) + return RespondNow(Error("No such tab")); + + auto* zoom_controller = contents->GetZoomController(); + WebContentsZoomController::ZoomMode zoom_mode = + contents->GetZoomController()->zoom_mode(); + api::tabs::ZoomSettings zoom_settings; + ZoomModeToZoomSettings(zoom_mode, &zoom_settings); + zoom_settings.default_zoom_factor.reset( + new double(blink::PageZoomLevelToZoomFactor( + zoom_controller->GetDefaultZoomLevel()))); + + return RespondNow( + ArgumentList(api::tabs::GetZoomSettings::Results::Create(zoom_settings))); +} + +ExtensionFunction::ResponseAction TabsSetZoomSettingsFunction::Run() { + using api::tabs::ZoomSettings; + + std::unique_ptr params( + tabs::SetZoomSettings::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + int tab_id = params->tab_id ? *params->tab_id : -1; + auto* contents = electron::api::WebContents::FromWeakMapID( + v8::Isolate::GetCurrent(), tab_id); + if (!contents) + return RespondNow(Error("No such tab")); + + std::string error; + GURL url(contents->web_contents()->GetVisibleURL()); + if (extension()->permissions_data()->IsRestrictedUrl(url, &error)) + return RespondNow(Error(error)); + + // "per-origin" scope is only available in "automatic" mode. + if (params->zoom_settings.scope == tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN && + params->zoom_settings.mode != tabs::ZOOM_SETTINGS_MODE_AUTOMATIC && + params->zoom_settings.mode != tabs::ZOOM_SETTINGS_MODE_NONE) { + return RespondNow(Error(kPerOriginOnlyInAutomaticError)); + } + + // Determine the correct internal zoom mode to set |web_contents| to from the + // user-specified |zoom_settings|. + WebContentsZoomController::ZoomMode zoom_mode = + WebContentsZoomController::ZoomMode::DEFAULT; + switch (params->zoom_settings.mode) { + case tabs::ZOOM_SETTINGS_MODE_NONE: + case tabs::ZOOM_SETTINGS_MODE_AUTOMATIC: + switch (params->zoom_settings.scope) { + case tabs::ZOOM_SETTINGS_SCOPE_NONE: + case tabs::ZOOM_SETTINGS_SCOPE_PER_ORIGIN: + zoom_mode = WebContentsZoomController::ZoomMode::DEFAULT; + break; + case tabs::ZOOM_SETTINGS_SCOPE_PER_TAB: + zoom_mode = WebContentsZoomController::ZoomMode::ISOLATED; + } + break; + case tabs::ZOOM_SETTINGS_MODE_MANUAL: + zoom_mode = WebContentsZoomController::ZoomMode::MANUAL; + break; + case tabs::ZOOM_SETTINGS_MODE_DISABLED: + zoom_mode = WebContentsZoomController::ZoomMode::DISABLED; + } + + contents->GetZoomController()->SetZoomMode(zoom_mode); + + return RespondNow(NoArguments()); +} + } // namespace extensions diff --git a/shell/browser/extensions/api/tabs/tabs_api.h b/shell/browser/extensions/api/tabs/tabs_api.h index 1848907e17ff..0a3ed2d3cb59 100644 --- a/shell/browser/extensions/api/tabs/tabs_api.h +++ b/shell/browser/extensions/api/tabs/tabs_api.h @@ -44,6 +44,48 @@ class TabsExecuteScriptFunction : public ExecuteCodeInTabFunction { DECLARE_EXTENSION_FUNCTION("tabs.executeScript", TABS_EXECUTESCRIPT) }; +class TabsGetFunction : public ExtensionFunction { + ~TabsGetFunction() override {} + ResponseAction Run() override; + DECLARE_EXTENSION_FUNCTION("tabs.get", TABS_GET) +}; + +class TabsSetZoomFunction : public ExtensionFunction { + private: + ~TabsSetZoomFunction() override {} + + ResponseAction Run() override; + + DECLARE_EXTENSION_FUNCTION("tabs.setZoom", TABS_SETZOOM) +}; + +class TabsGetZoomFunction : public ExtensionFunction { + private: + ~TabsGetZoomFunction() override {} + + ResponseAction Run() override; + + DECLARE_EXTENSION_FUNCTION("tabs.getZoom", TABS_GETZOOM) +}; + +class TabsSetZoomSettingsFunction : public ExtensionFunction { + private: + ~TabsSetZoomSettingsFunction() override {} + + ResponseAction Run() override; + + DECLARE_EXTENSION_FUNCTION("tabs.setZoomSettings", TABS_SETZOOMSETTINGS) +}; + +class TabsGetZoomSettingsFunction : public ExtensionFunction { + private: + ~TabsGetZoomSettingsFunction() override {} + + ResponseAction Run() override; + + DECLARE_EXTENSION_FUNCTION("tabs.getZoomSettings", TABS_GETZOOMSETTINGS) +}; + } // namespace extensions #endif // SHELL_BROWSER_EXTENSIONS_API_TABS_TABS_API_H_ diff --git a/shell/browser/extensions/electron_browser_context_keyed_service_factories.cc b/shell/browser/extensions/electron_browser_context_keyed_service_factories.cc index eb0a91fc9150..5b6213f260b4 100644 --- a/shell/browser/extensions/electron_browser_context_keyed_service_factories.cc +++ b/shell/browser/extensions/electron_browser_context_keyed_service_factories.cc @@ -5,15 +5,12 @@ #include "shell/browser/extensions/electron_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/electron_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(); diff --git a/shell/browser/extensions/electron_component_extension_resource_manager.cc b/shell/browser/extensions/electron_component_extension_resource_manager.cc new file mode 100644 index 000000000000..e4a548b4b892 --- /dev/null +++ b/shell/browser/extensions/electron_component_extension_resource_manager.cc @@ -0,0 +1,89 @@ +// 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/electron_component_extension_resource_manager.h" + +#include + +#include "base/logging.h" +#include "base/path_service.h" +#include "base/stl_util.h" +#include "base/values.h" +#include "build/build_config.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/grit/component_extension_resources_map.h" + +namespace extensions { + +ElectronComponentExtensionResourceManager:: + ElectronComponentExtensionResourceManager() { + AddComponentResourceEntries(kComponentExtensionResources, + kComponentExtensionResourcesSize); +} + +ElectronComponentExtensionResourceManager:: + ~ElectronComponentExtensionResourceManager() {} + +bool ElectronComponentExtensionResourceManager::IsComponentExtensionResource( + const base::FilePath& extension_path, + const base::FilePath& resource_path, + int* resource_id) const { + base::FilePath directory_path = extension_path; + base::FilePath resources_dir; + base::FilePath relative_path; + if (!base::PathService::Get(chrome::DIR_RESOURCES, &resources_dir) || + !resources_dir.AppendRelativePath(directory_path, &relative_path)) { + return false; + } + relative_path = relative_path.Append(resource_path); + relative_path = relative_path.NormalizePathSeparators(); + + auto entry = path_to_resource_id_.find(relative_path); + if (entry != path_to_resource_id_.end()) { + *resource_id = entry->second; + return true; + } + + return false; +} + +const ui::TemplateReplacements* +ElectronComponentExtensionResourceManager::GetTemplateReplacementsForExtension( + const std::string& extension_id) const { + auto it = extension_template_replacements_.find(extension_id); + if (it == extension_template_replacements_.end()) { + return nullptr; + } + return &it->second; +} + +void ElectronComponentExtensionResourceManager::AddComponentResourceEntries( + const GritResourceMap* entries, + size_t size) { + base::FilePath gen_folder_path = base::FilePath().AppendASCII( + "@out_folder@/gen/chrome/browser/resources/"); + gen_folder_path = gen_folder_path.NormalizePathSeparators(); + + for (size_t i = 0; i < size; ++i) { + base::FilePath resource_path = + base::FilePath().AppendASCII(entries[i].name); + resource_path = resource_path.NormalizePathSeparators(); + + if (!gen_folder_path.IsParent(resource_path)) { + DCHECK(!base::Contains(path_to_resource_id_, resource_path)); + path_to_resource_id_[resource_path] = entries[i].value; + } else { + // If the resource is a generated file, strip the generated folder's path, + // so that it can be served from a normal URL (as if it were not + // generated). + base::FilePath effective_path = + base::FilePath().AppendASCII(resource_path.AsUTF8Unsafe().substr( + gen_folder_path.value().length())); + DCHECK(!base::Contains(path_to_resource_id_, effective_path)); + path_to_resource_id_[effective_path] = entries[i].value; + } + } +} + +} // namespace extensions diff --git a/shell/browser/extensions/electron_component_extension_resource_manager.h b/shell/browser/extensions/electron_component_extension_resource_manager.h new file mode 100644 index 000000000000..5823ea5b2bd3 --- /dev/null +++ b/shell/browser/extensions/electron_component_extension_resource_manager.h @@ -0,0 +1,50 @@ +// 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_ELECTRON_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_ +#define SHELL_BROWSER_EXTENSIONS_ELECTRON_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_ + +#include + +#include +#include + +#include "base/files/file_path.h" +#include "base/macros.h" +#include "extensions/browser/component_extension_resource_manager.h" + +struct GritResourceMap; + +namespace extensions { + +class ElectronComponentExtensionResourceManager + : public ComponentExtensionResourceManager { + public: + ElectronComponentExtensionResourceManager(); + ~ElectronComponentExtensionResourceManager() override; + + // Overridden from ComponentExtensionResourceManager: + bool IsComponentExtensionResource(const base::FilePath& extension_path, + const base::FilePath& resource_path, + int* resource_id) const override; + const ui::TemplateReplacements* GetTemplateReplacementsForExtension( + const std::string& extension_id) const override; + + private: + void AddComponentResourceEntries(const GritResourceMap* entries, size_t size); + + // A map from a resource path to the resource ID. Used by + // IsComponentExtensionResource. + std::map path_to_resource_id_; + + // A map from an extension ID to its i18n template replacements. + std::map + extension_template_replacements_; + + DISALLOW_COPY_AND_ASSIGN(ElectronComponentExtensionResourceManager); +}; + +} // namespace extensions + +#endif // SHELL_BROWSER_EXTENSIONS_ELECTRON_COMPONENT_EXTENSION_RESOURCE_MANAGER_H_ diff --git a/shell/browser/extensions/electron_extension_loader.h b/shell/browser/extensions/electron_extension_loader.h index 0dda5a6d009e..f7050fa84a21 100644 --- a/shell/browser/extensions/electron_extension_loader.h +++ b/shell/browser/extensions/electron_extension_loader.h @@ -49,6 +49,8 @@ class ElectronExtensionLoader : public ExtensionRegistrar::Delegate { void UnloadExtension(const ExtensionId& extension_id, extensions::UnloadedExtensionReason reason); + ExtensionRegistrar* registrar() { return &extension_registrar_; } + private: // If the extension loaded successfully, enables it. If it's an app, launches // it. If the load failed, updates ShellKeepAliveRequester. diff --git a/shell/browser/extensions/electron_extension_system.cc b/shell/browser/extensions/electron_extension_system.cc index 2e54a838284f..390525209668 100644 --- a/shell/browser/extensions/electron_extension_system.cc +++ b/shell/browser/extensions/electron_extension_system.cc @@ -11,7 +11,11 @@ #include "base/bind.h" #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/json/json_string_value_serializer.h" +#include "base/path_service.h" #include "base/task/post_task.h" +#include "chrome/browser/pdf/pdf_extension_util.h" +#include "chrome/common/chrome_paths.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -84,6 +88,35 @@ void ElectronExtensionSystem::InitForRegularProfile(bool extensions_enabled) { app_sorting_ = std::make_unique(); extension_loader_ = std::make_unique(browser_context_); + + if (!browser_context_->IsOffTheRecord()) + LoadComponentExtensions(); +} + +std::unique_ptr ParseManifest( + base::StringPiece manifest_contents) { + JSONStringValueDeserializer deserializer(manifest_contents); + std::unique_ptr manifest = deserializer.Deserialize(NULL, NULL); + + if (!manifest.get() || !manifest->is_dict()) { + LOG(ERROR) << "Failed to parse extension manifest."; + return std::unique_ptr(); + } + return base::DictionaryValue::From(std::move(manifest)); +} + +void ElectronExtensionSystem::LoadComponentExtensions() { + std::string utf8_error; + std::string pdf_manifest_string = pdf_extension_util::GetManifest(); + std::unique_ptr pdf_manifest = + ParseManifest(pdf_manifest_string); + base::FilePath root_directory; + CHECK(base::PathService::Get(chrome::DIR_RESOURCES, &root_directory)); + root_directory = root_directory.Append(FILE_PATH_LITERAL("pdf")); + scoped_refptr pdf_extension = extensions::Extension::Create( + root_directory, extensions::Manifest::COMPONENT, *pdf_manifest, + extensions::Extension::REQUIRE_KEY, &utf8_error); + extension_loader_->registrar()->AddExtension(pdf_extension); } ExtensionService* ElectronExtensionSystem::extension_service() { diff --git a/shell/browser/extensions/electron_extension_system.h b/shell/browser/extensions/electron_extension_system.h index 728eab19d27f..4b05e4d0e30e 100644 --- a/shell/browser/extensions/electron_extension_system.h +++ b/shell/browser/extensions/electron_extension_system.h @@ -88,6 +88,8 @@ class ElectronExtensionSystem : public ExtensionSystem { private: void OnExtensionRegisteredWithRequestContexts( scoped_refptr extension); + void LoadComponentExtensions(); + content::BrowserContext* browser_context_; // Not owned. // Data to be accessed on the IO thread. Must outlive process_manager_. diff --git a/shell/browser/extensions/electron_extensions_api_client.cc b/shell/browser/extensions/electron_extensions_api_client.cc index 0001a7b18ed7..c280df252c6c 100644 --- a/shell/browser/extensions/electron_extensions_api_client.cc +++ b/shell/browser/extensions/electron_extensions_api_client.cc @@ -5,12 +5,34 @@ #include "shell/browser/extensions/electron_extensions_api_client.h" #include +#include +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_guest_delegate.h" #include "shell/browser/extensions/electron_extension_web_contents_observer.h" #include "shell/browser/extensions/electron_messaging_delegate.h" namespace extensions { +class ElectronMimeHandlerViewGuestDelegate + : public MimeHandlerViewGuestDelegate { + public: + ElectronMimeHandlerViewGuestDelegate() {} + ~ElectronMimeHandlerViewGuestDelegate() override {} + + // MimeHandlerViewGuestDelegate. + bool HandleContextMenu(content::WebContents* web_contents, + const content::ContextMenuParams& params) override { + // TODO(nornagon): surface this event to JS + LOG(INFO) << "HCM"; + return true; + } + void RecordLoadMetric(bool in_main_frame, + const std::string& mime_type) override {} + + private: + DISALLOW_COPY_AND_ASSIGN(ElectronMimeHandlerViewGuestDelegate); +}; + ElectronExtensionsAPIClient::ElectronExtensionsAPIClient() = default; ElectronExtensionsAPIClient::~ElectronExtensionsAPIClient() = default; @@ -26,4 +48,10 @@ void ElectronExtensionsAPIClient::AttachWebContentsHelpers( web_contents); } +std::unique_ptr +ElectronExtensionsAPIClient::CreateMimeHandlerViewGuestDelegate( + MimeHandlerViewGuest* guest) const { + return std::make_unique(); +} + } // namespace extensions diff --git a/shell/browser/extensions/electron_extensions_api_client.h b/shell/browser/extensions/electron_extensions_api_client.h index e34c74d95b29..1747d7e98266 100644 --- a/shell/browser/extensions/electron_extensions_api_client.h +++ b/shell/browser/extensions/electron_extensions_api_client.h @@ -22,6 +22,9 @@ class ElectronExtensionsAPIClient : public ExtensionsAPIClient { MessagingDelegate* GetMessagingDelegate() override; void AttachWebContentsHelpers( content::WebContents* web_contents) const override; + std::unique_ptr + CreateMimeHandlerViewGuestDelegate( + MimeHandlerViewGuest* guest) const override; private: std::unique_ptr messaging_delegate_; diff --git a/shell/browser/extensions/electron_extensions_browser_api_provider.cc b/shell/browser/extensions/electron_extensions_browser_api_provider.cc index f5c2f14fdf9d..f434274bccdb 100644 --- a/shell/browser/extensions/electron_extensions_browser_api_provider.cc +++ b/shell/browser/extensions/electron_extensions_browser_api_provider.cc @@ -5,6 +5,7 @@ #include "shell/browser/extensions/electron_extensions_browser_api_provider.h" #include "extensions/browser/extension_function_registry.h" +#include "shell/browser/extensions/api/generated_api_registration.h" #include "shell/browser/extensions/api/tabs/tabs_api.h" namespace extensions { @@ -16,11 +17,8 @@ ElectronExtensionsBrowserAPIProvider::~ElectronExtensionsBrowserAPIProvider() = void ElectronExtensionsBrowserAPIProvider::RegisterExtensionFunctions( ExtensionFunctionRegistry* registry) { - registry->RegisterFunction(); - /* // Generated APIs from Electron. api::ElectronGeneratedFunctionRegistry::RegisterAll(registry); - */ } } // namespace extensions diff --git a/shell/browser/extensions/electron_extensions_browser_client.cc b/shell/browser/extensions/electron_extensions_browser_client.cc index af97bcabeb7e..d05c250667ef 100644 --- a/shell/browser/extensions/electron_extensions_browser_client.cc +++ b/shell/browser/extensions/electron_extensions_browser_client.cc @@ -8,8 +8,12 @@ #include "base/bind.h" #include "base/memory/ptr_util.h" +#include "base/path_service.h" #include "base/task/post_task.h" #include "build/build_config.h" +#include "chrome/browser/extensions/chrome_url_request_util.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/extensions/chrome_manifest_url_handlers.h" #include "components/version_info/version_info.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" @@ -20,17 +24,21 @@ #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/extension_protocols.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 "extensions/common/file_util.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/manifest_url_handlers.h" +#include "net/base/mime_util.h" #include "services/network/public/mojom/url_loader.mojom.h" #include "shell/browser/browser.h" #include "shell/browser/electron_browser_client.h" #include "shell/browser/electron_browser_context.h" #include "shell/browser/extensions/api/runtime/electron_runtime_api_delegate.h" +#include "shell/browser/extensions/electron_component_extension_resource_manager.h" #include "shell/browser/extensions/electron_extension_host_delegate.h" #include "shell/browser/extensions/electron_extension_system_factory.h" #include "shell/browser/extensions/electron_extension_web_contents_observer.h" @@ -38,9 +46,11 @@ #include "shell/browser/extensions/electron_extensions_browser_api_provider.h" #include "shell/browser/extensions/electron_navigation_ui_data.h" #include "shell/browser/extensions/electron_process_manager_delegate.h" +#include "ui/base/resource/resource_bundle.h" using content::BrowserContext; using content::BrowserThread; +using extensions::ExtensionsBrowserClient; namespace electron { @@ -51,6 +61,8 @@ ElectronExtensionsBrowserClient::ElectronExtensionsBrowserClient() // Electron does not have a concept of channel, so leave UNKNOWN to // enable all channel-dependent extension APIs. extensions::SetCurrentChannel(version_info::Channel::UNKNOWN); + resource_manager_.reset( + new extensions::ElectronComponentExtensionResourceManager()); AddAPIProvider( std::make_unique()); @@ -127,7 +139,28 @@ base::FilePath ElectronExtensionsBrowserClient::GetBundleResourcePath( const base::FilePath& extension_resources_path, int* resource_id) const { *resource_id = 0; - return base::FilePath(); + base::FilePath chrome_resources_path; + if (!base::PathService::Get(chrome::DIR_RESOURCES, &chrome_resources_path)) + return base::FilePath(); + + // Since component extension resources are included in + // component_extension_resources.pak file in |chrome_resources_path|, + // calculate the extension |request_relative_path| against + // |chrome_resources_path|. + if (!chrome_resources_path.IsParent(extension_resources_path)) + return base::FilePath(); + + const base::FilePath request_relative_path = + extensions::file_util::ExtensionURLToRelativeFilePath(request.url); + if (!ExtensionsBrowserClient::Get() + ->GetComponentExtensionResourceManager() + ->IsComponentExtensionResource(extension_resources_path, + request_relative_path, resource_id)) { + return base::FilePath(); + } + DCHECK_NE(0, *resource_id); + + return request_relative_path; } void ElectronExtensionsBrowserClient::LoadResourceFromResourceBundle( @@ -138,7 +171,9 @@ void ElectronExtensionsBrowserClient::LoadResourceFromResourceBundle( const std::string& content_security_policy, mojo::PendingRemote client, bool send_cors_header) { - NOTREACHED() << "Load resources from bundles not supported."; + extensions::chrome_url_request_util::LoadResourceFromResourceBundle( + request, std::move(loader), resource_relative_path, resource_id, + content_security_policy, std::move(client), send_cors_header); } namespace { @@ -160,8 +195,7 @@ bool AllowCrossRendererResourceLoad(const GURL& url, // If there aren't any explicitly marked web accessible resources, the // load should be allowed only if it is by DevTools. A close approximation is // checking if the extension contains a DevTools page. - if (extension && !extensions::ManifestURL::Get( - extension, extensions::manifest_keys::kDevToolsPage) + if (extension && !extensions::chrome_manifest_urls::GetDevToolsPage(extension) .is_empty()) { *allowed = true; return true; @@ -255,7 +289,7 @@ ElectronExtensionsBrowserClient::CreateRuntimeAPIDelegate( const extensions::ComponentExtensionResourceManager* ElectronExtensionsBrowserClient::GetComponentExtensionResourceManager() { - return NULL; + return resource_manager_.get(); } void ElectronExtensionsBrowserClient::BroadcastEventToRenderers( diff --git a/shell/browser/extensions/electron_extensions_browser_client.h b/shell/browser/extensions/electron_extensions_browser_client.h index 796f7570a00b..308675099405 100644 --- a/shell/browser/extensions/electron_extensions_browser_client.h +++ b/shell/browser/extensions/electron_extensions_browser_client.h @@ -24,6 +24,7 @@ class KioskDelegate; class ProcessManagerDelegate; class ElectronProcessManagerDelegate; class ProcessMap; +class ElectronComponentExtensionResourceManager; } // namespace extensions namespace electron { @@ -139,6 +140,9 @@ class ElectronExtensionsBrowserClient // The extension cache used for download and installation. std::unique_ptr extension_cache_; + std::unique_ptr + resource_manager_; + DISALLOW_COPY_AND_ASSIGN(ElectronExtensionsBrowserClient); }; diff --git a/shell/browser/plugins/plugin_utils.cc b/shell/browser/plugins/plugin_utils.cc new file mode 100644 index 000000000000..e2633ed1ca5d --- /dev/null +++ b/shell/browser/plugins/plugin_utils.cc @@ -0,0 +1,63 @@ +// 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/plugins/plugin_utils.h" + +#include + +#include "base/values.h" +#include "content/public/common/webplugininfo.h" +#include "extensions/buildflags/buildflags.h" +#include "url/gurl.h" +#include "url/origin.h" + +#if BUILDFLAG(ENABLE_EXTENSIONS) +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_util.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handlers/mime_types_handler.h" +#endif + +// static +std::string PluginUtils::GetExtensionIdForMimeType( + content::BrowserContext* browser_context, + const std::string& mime_type) { + auto map = GetMimeTypeToExtensionIdMap(browser_context); + auto it = map.find(mime_type); + if (it != map.end()) + return it->second; + return std::string(); +} + +base::flat_map +PluginUtils::GetMimeTypeToExtensionIdMap( + content::BrowserContext* browser_context) { + base::flat_map mime_type_to_extension_id_map; +#if BUILDFLAG(ENABLE_EXTENSIONS) + std::vector whitelist = MimeTypesHandler::GetMIMETypeWhitelist(); + // Go through the white-listed extensions and try to use them to intercept + // the URL request. + for (const std::string& extension_id : whitelist) { + const extensions::Extension* extension = + extensions::ExtensionRegistry::Get(browser_context) + ->enabled_extensions() + .GetByID(extension_id); + // The white-listed extension may not be installed, so we have to nullptr + // check |extension|. + if (!extension) { + continue; + } + + if (MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension)) { + for (const auto& supported_mime_type : handler->mime_type_set()) { + DCHECK(!base::Contains(mime_type_to_extension_id_map, + supported_mime_type)); + mime_type_to_extension_id_map[supported_mime_type] = extension_id; + } + } + } +#endif + return mime_type_to_extension_id_map; +} diff --git a/shell/browser/plugins/plugin_utils.h b/shell/browser/plugins/plugin_utils.h new file mode 100644 index 000000000000..3d3462f14901 --- /dev/null +++ b/shell/browser/plugins/plugin_utils.h @@ -0,0 +1,34 @@ +// 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_PLUGINS_PLUGIN_UTILS_H_ +#define SHELL_BROWSER_PLUGINS_PLUGIN_UTILS_H_ + +#include + +#include "base/containers/flat_map.h" +#include "base/macros.h" + +namespace content { +class BrowserContext; +} + +class PluginUtils { + public: + // If there's an extension that is allowed to handle |mime_type|, returns its + // ID. Otherwise returns an empty string. + static std::string GetExtensionIdForMimeType( + content::BrowserContext* browser_context, + const std::string& mime_type); + + // Returns a map populated with MIME types that are handled by an extension as + // keys and the corresponding extensions Ids as values. + static base::flat_map GetMimeTypeToExtensionIdMap( + content::BrowserContext* browser_context); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(PluginUtils); +}; + +#endif // SHELL_BROWSER_PLUGINS_PLUGIN_UTILS_H_ diff --git a/shell/browser/ui/inspectable_web_contents_impl.cc b/shell/browser/ui/inspectable_web_contents_impl.cc index 89dc68fb0687..e568b9195843 100644 --- a/shell/browser/ui/inspectable_web_contents_impl.cc +++ b/shell/browser/ui/inspectable_web_contents_impl.cc @@ -50,11 +50,10 @@ #include "ui/display/screen.h" #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#include "chrome/common/extensions/chrome_manifest_url_handlers.h" #include "content/public/browser/child_process_security_policy.h" #include "content/public/browser/render_process_host.h" #include "extensions/browser/extension_registry.h" -#include "extensions/common/manifest_constants.h" -#include "extensions/common/manifest_url_handlers.h" #include "extensions/common/permissions/permissions_data.h" #include "shell/browser/electron_browser_context.h" #endif @@ -601,8 +600,8 @@ void InspectableWebContentsImpl::AddDevToolsExtensionsToClient() { base::ListValue results; for (auto& extension : registry->enabled_extensions()) { - auto devtools_page_url = extensions::ManifestURL::Get( - extension.get(), extensions::manifest_keys::kDevToolsPage); + auto devtools_page_url = + extensions::chrome_manifest_urls::GetDevToolsPage(extension.get()); if (devtools_page_url.is_empty()) continue; diff --git a/shell/common/electron_constants.cc b/shell/common/electron_constants.cc index 48bf7ae523e2..c53c72415d33 100644 --- a/shell/common/electron_constants.cc +++ b/shell/common/electron_constants.cc @@ -37,11 +37,8 @@ const char kRunAsNode[] = "ELECTRON_RUN_AS_NODE"; #if BUILDFLAG(ENABLE_PDF_VIEWER) const char kPdfPluginMimeType[] = "application/x-google-chrome-pdf"; -const char kPdfPluginPath[] = "chrome://pdf-viewer/"; -const char kPdfPluginSrc[] = "src"; - -const char kPdfViewerUIOrigin[] = "chrome://pdf-viewer/"; -const char kPdfViewerUIHost[] = "pdf-viewer"; +const base::FilePath::CharType kPdfPluginPath[] = + FILE_PATH_LITERAL("internal-pdf-viewer"); #endif // BUILDFLAG(ENABLE_PDF_VIEWER) } // namespace electron diff --git a/shell/common/electron_constants.h b/shell/common/electron_constants.h index 43d8bfe8be6f..010768f5990d 100644 --- a/shell/common/electron_constants.h +++ b/shell/common/electron_constants.h @@ -5,6 +5,7 @@ #ifndef SHELL_COMMON_ELECTRON_CONSTANTS_H_ #define SHELL_COMMON_ELECTRON_CONSTANTS_H_ +#include "base/files/file_path.h" #include "build/build_config.h" #include "electron/buildflags/buildflags.h" @@ -39,12 +40,7 @@ extern const char kRunAsNode[]; #if BUILDFLAG(ENABLE_PDF_VIEWER) // The MIME type used for the PDF plugin. extern const char kPdfPluginMimeType[]; -extern const char kPdfPluginPath[]; -extern const char kPdfPluginSrc[]; - -// Constants for PDF viewer webui. -extern const char kPdfViewerUIOrigin[]; -extern const char kPdfViewerUIHost[]; +extern const base::FilePath::CharType kPdfPluginPath[]; #endif // BUILDFLAG(ENABLE_PDF_VIEWER) } // namespace electron diff --git a/shell/common/extensions/api/BUILD.gn b/shell/common/extensions/api/BUILD.gn index f7fd7071666b..717ec0b29ee5 100644 --- a/shell/common/extensions/api/BUILD.gn +++ b/shell/common/extensions/api/BUILD.gn @@ -22,6 +22,7 @@ group("extensions_features") { public_deps = [ ":api_features", ":manifest_features", + ":permission_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 @@ -36,6 +37,7 @@ group("extensions_features") { generated_json_strings("generated_api_json_strings") { sources = [ "extension.json", + "resources_private.idl", "tabs.json", ] @@ -50,7 +52,10 @@ generated_json_strings("generated_api_json_strings") { } generated_types("generated_api_types") { - sources = [ "tabs.json" ] + sources = [ + "resources_private.idl", + "tabs.json", + ] configs = [ "//build/config:precompiled_headers" ] schema_include_rules = "extensions/common/api:extensions::api::%(namespace)s" @@ -73,3 +78,10 @@ json_features("api_features") { sources = [ "_api_features.json" ] visibility = [ ":extensions_features" ] } + +json_features("permission_features") { + feature_type = "PermissionFeature" + method_name = "AddElectronPermissionFeatures" + sources = [ "_permission_features.json" ] + visibility = [ ":extensions_features" ] +} diff --git a/shell/common/extensions/api/_api_features.json b/shell/common/extensions/api/_api_features.json index 5714a4af9182..685cc8ff09b8 100644 --- a/shell/common/extensions/api/_api_features.json +++ b/shell/common/extensions/api/_api_features.json @@ -15,5 +15,21 @@ }, "extension.getURL": { "contexts": ["blessed_extension", "unblessed_extension", "content_script"] - } + }, + "mimeHandlerViewGuestInternal": { + "internal": true, + "contexts": "all", + "channel": "stable", + "matches": [""] + }, + "resourcesPrivate": [{ + "dependencies": ["permission:resourcesPrivate"], + "contexts": ["blessed_extension"] + }, { + "channel": "stable", + "contexts": ["webui"], + "matches": [ + "chrome://print/*" + ] + }] } diff --git a/shell/common/extensions/api/_permission_features.json b/shell/common/extensions/api/_permission_features.json new file mode 100644 index 000000000000..6f0aacd58603 --- /dev/null +++ b/shell/common/extensions/api/_permission_features.json @@ -0,0 +1,9 @@ +{ + "resourcesPrivate": { + "channel": "stable", + "extension_types": [ + "extension" + ], + "location": "component" + } +} diff --git a/shell/common/extensions/api/resources_private.idl b/shell/common/extensions/api/resources_private.idl new file mode 100644 index 000000000000..836b366d0e6b --- /dev/null +++ b/shell/common/extensions/api/resources_private.idl @@ -0,0 +1,23 @@ +// 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. + +// resourcesPrivate. +namespace resourcesPrivate { + enum Component { identity, pdf }; + + callback GetStringsCallback = void (object result); + + interface Functions { + // Gets localized strings for a component extension. Includes default WebUI + // loadTimeData values for text and language settings (fontsize, fontfamily, + // language, textdirection). See + // chrome/browser/extensions/api/resources_private/resources_private_api.cc + // for instructions on adding a new component to this API. + // + // |component| : Internal chrome component to get strings for. + // |callback| : Called with a dictionary mapping names to strings. + static void getStrings(Component component, + GetStringsCallback callback); + }; +}; diff --git a/shell/common/extensions/api/tabs.json b/shell/common/extensions/api/tabs.json index de26d4370afc..354fa2487b87 100644 --- a/shell/common/extensions/api/tabs.json +++ b/shell/common/extensions/api/tabs.json @@ -2,7 +2,142 @@ { "namespace": "tabs", "description": "Use the chrome.tabs API to interact with the browser's tab system. You can use this API to create, modify, and rearrange tabs in the browser.", + "types": [ + { "id": "MutedInfoReason", + "type": "string", + "description": "An event that caused a muted state change.", + "enum": [ + {"name": "user", "description": "A user input action set the muted state."}, + {"name": "capture", "description": "Tab capture was started, forcing a muted state change."}, + {"name": "extension", "description": "An extension, identified by the extensionId field, set the muted state."} + ] + }, + { + "id": "MutedInfo", + "type": "object", + "description": "The tab's muted state and the reason for the last state change.", + "properties": { + "muted": { + "type": "boolean", + "description": "Whether the tab is muted (prevented from playing sound). The tab may be muted even if it has not played or is not currently playing sound. Equivalent to whether the 'muted' audio indicator is showing." + }, + "reason": { + "$ref": "MutedInfoReason", + "optional": true, + "description": "The reason the tab was muted or unmuted. Not set if the tab's mute state has never been changed." + }, + "extensionId": { + "type": "string", + "optional": true, + "description": "The ID of the extension that changed the muted state. Not set if an extension was not the reason the muted state last changed." + } + } + }, + { + "id": "Tab", + "type": "object", + "properties": { + "id": {"type": "integer", "minimum": -1, "optional": true, "description": "The ID of the tab. Tab IDs are unique within a browser session. Under some circumstances a tab may not be assigned an ID; for example, when querying foreign tabs using the $(ref:sessions) API, in which case a session ID may be present. Tab ID can also be set to chrome.tabs.TAB_ID_NONE for apps and devtools windows."}, + // TODO(kalman): Investigate how this is ending up as -1 (based on window type? a bug?) and whether it should be optional instead. + "index": {"type": "integer", "minimum": -1, "description": "The zero-based index of the tab within its window."}, + "windowId": {"type": "integer", "minimum": 0, "description": "The ID of the window that contains the tab."}, + "openerTabId": {"type": "integer", "minimum": 0, "optional": true, "description": "The ID of the tab that opened this tab, if any. This property is only present if the opener tab still exists."}, + "selected": {"type": "boolean", "description": "Whether the tab is selected.", "deprecated": "Please use $(ref:tabs.Tab.highlighted)."}, + "highlighted": {"type": "boolean", "description": "Whether the tab is highlighted."}, + "active": {"type": "boolean", "description": "Whether the tab is active in its window. Does not necessarily mean the window is focused."}, + "pinned": {"type": "boolean", "description": "Whether the tab is pinned."}, + "audible": {"type": "boolean", "optional": true, "description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the 'speaker audio' indicator is showing."}, + "discarded": {"type": "boolean", "description": "Whether the tab is discarded. A discarded tab is one whose content has been unloaded from memory, but is still visible in the tab strip. Its content is reloaded the next time it is activated."}, + "autoDiscardable": {"type": "boolean", "description": "Whether the tab can be discarded automatically by the browser when resources are low."}, + "mutedInfo": {"$ref": "MutedInfo", "optional": true, "description": "The tab's muted state and the reason for the last state change."}, + "url": {"type": "string", "optional": true, "description": "The last committed URL of the main frame of the tab. This property is only present if the extension's manifest includes the \"tabs\" permission and may be an empty string if the tab has not yet committed. See also $(ref:Tab.pendingUrl)."}, + "pendingUrl": {"type": "string", "optional": true, "description": "The URL the tab is navigating to, before it has committed. This property is only present if the extension's manifest includes the \"tabs\" permission and there is a pending navigation."}, + "title": {"type": "string", "optional": true, "description": "The title of the tab. This property is only present if the extension's manifest includes the \"tabs\" permission."}, + "favIconUrl": {"type": "string", "optional": true, "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the \"tabs\" permission. It may also be an empty string if the tab is loading."}, + "status": {"type": "string", "optional": true, "description": "Either loading or complete."}, + "incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."}, + "width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."}, + "height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."}, + "sessionId": {"type": "string", "optional": true, "description": "The session ID used to uniquely identify a tab obtained from the $(ref:sessions) API."} + } + }, + { + "id": "ZoomSettingsMode", + "type": "string", + "description": "Defines how zoom changes are handled, i.e., which entity is responsible for the actual scaling of the page; defaults to automatic.", + "enum": [ + { + "name": "automatic", + "description": "Zoom changes are handled automatically by the browser." + }, + { + "name": "manual", + "description": "Overrides the automatic handling of zoom changes. The onZoomChange event will still be dispatched, and it is the extension's responsibility to listen for this event and manually scale the page. This mode does not support per-origin zooming, and thus ignores the scope zoom setting and assumes per-tab." + }, + { + "name": "disabled", + "description": "Disables all zooming in the tab. The tab reverts to the default zoom level, and all attempted zoom changes are ignored." + } + ] + }, + { + "id": "ZoomSettingsScope", + "type": "string", + "description": "Defines whether zoom changes persist for the page's origin, or only take effect in this tab; defaults to per-origin when in automatic mode, and per-tab otherwise.", + "enum": [ + { + "name": "per-origin", + "description": "Zoom changes persist in the zoomed page's origin, i.e., all other tabs navigated to that same origin are zoomed as well. Moreover, per-origin zoom changes are saved with the origin, meaning that when navigating to other pages in the same origin, they are all zoomed to the same zoom factor. The per-origin scope is only available in the automatic mode." + }, + { + "name": "per-tab", + "description": "Zoom changes only take effect in this tab, and zoom changes in other tabs do not affect the zooming of this tab. Also, per-tab zoom changes are reset on navigation; navigating a tab always loads pages with their per-origin zoom factors." + } + ] + }, + { + "id": "ZoomSettings", + "type": "object", + "description": "Defines how zoom changes in a tab are handled and at what scope.", + "properties": { + "mode": { + "$ref": "ZoomSettingsMode", + "description": "Defines how zoom changes are handled, i.e., which entity is responsible for the actual scaling of the page; defaults to automatic.", + "optional": true + }, + "scope": { + "$ref": "ZoomSettingsScope", + "description": "Defines whether zoom changes persist for the page's origin, or only take effect in this tab; defaults to per-origin when in automatic mode, and per-tab otherwise.", + "optional": true + }, + "defaultZoomFactor": { + "type": "number", + "optional": true, + "description": "Used to return the default zoom level for the current tab in calls to tabs.getZoomSettings." + } + } + } + ], "functions": [ + { + "name": "get", + "type": "function", + "description": "Retrieves details about the specified tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0 + }, + { + "type": "function", + "name": "callback", + "parameters": [ + {"name": "tab", "$ref": "Tab"} + ] + } + ] + }, { "name": "executeScript", "type": "function", @@ -81,6 +216,127 @@ ] } ] + }, + { + "name": "setZoom", + "type": "function", + "description": "Zooms a specified tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "optional": true, + "description": "The ID of the tab to zoom; defaults to the active tab of the current window." + }, + { + "type": "number", + "name": "zoomFactor", + "description": "The new zoom factor. A value of 0 sets the tab to its current default zoom factor. Values greater than 0 specify a (possibly non-default) zoom factor for the tab." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "description": "Called after the zoom factor has been changed.", + "parameters": [] + } + ] + }, + { + "name": "getZoom", + "type": "function", + "description": "Gets the current zoom factor of a specified tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "minimum": 0, + "optional": true, + "description": "The ID of the tab to get the current zoom factor from; defaults to the active tab of the current window." + }, + { + "type": "function", + "name": "callback", + "description": "Called with the tab's current zoom factor after it has been fetched.", + "parameters": [ + { + "type": "number", + "name": "zoomFactor", + "description": "The tab's current zoom factor." + } + ] + } + ] + }, + { + "name": "setZoomSettings", + "type": "function", + "description": "Sets the zoom settings for a specified tab, which define how zoom changes are handled. These settings are reset to defaults upon navigating the tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "optional": true, + "minimum": 0, + "description": "The ID of the tab to change the zoom settings for; defaults to the active tab of the current window." + }, + { + "$ref": "ZoomSettings", + "name": "zoomSettings", + "description": "Defines how zoom changes are handled and at what scope." + }, + { + "type": "function", + "name": "callback", + "optional": true, + "description": "Called after the zoom settings are changed.", + "parameters": [] + } + ] + }, + { + "name": "getZoomSettings", + "type": "function", + "description": "Gets the current zoom settings of a specified tab.", + "parameters": [ + { + "type": "integer", + "name": "tabId", + "optional": true, + "minimum": 0, + "description": "The ID of the tab to get the current zoom settings from; defaults to the active tab of the current window." + }, + { + "type": "function", + "name": "callback", + "description": "Called with the tab's current zoom settings.", + "parameters": [ + { + "$ref": "ZoomSettings", + "name": "zoomSettings", + "description": "The tab's current zoom settings." + } + ] + } + ] + } + ], + "events": [ + { + "name": "onZoomChange", + "type": "function", + "description": "Fired when a tab is zoomed.", + "parameters": [{ + "type": "object", + "name": "ZoomChangeInfo", + "properties": { + "tabId": {"type": "integer", "minimum": 0}, + "oldZoomFactor": {"type": "number"}, + "newZoomFactor": {"type": "number"}, + "zoomSettings": {"$ref": "ZoomSettings"} + } + }] } ] } diff --git a/shell/common/extensions/electron_extensions_api_provider.cc b/shell/common/extensions/electron_extensions_api_provider.cc index 276bb1de419e..e02aee3f2df0 100644 --- a/shell/common/extensions/electron_extensions_api_provider.cc +++ b/shell/common/extensions/electron_extensions_api_provider.cc @@ -10,6 +10,7 @@ #include "base/containers/span.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/common/extensions/chrome_manifest_url_handlers.h" #include "electron/buildflags/buildflags.h" #include "electron/shell/common/extensions/api/generated_schemas.h" #include "extensions/common/alias.h" @@ -23,45 +24,20 @@ #include "extensions/common/permissions/permissions_info.h" #include "shell/common/extensions/api/api_features.h" #include "shell/common/extensions/api/manifest_features.h" +#include "shell/common/extensions/api/permission_features.h" namespace extensions { namespace keys = manifest_keys; namespace errors = manifest_errors; -// Parses the "devtools_page" manifest key. -class DevToolsPageHandler : public ManifestHandler { - public: - DevToolsPageHandler() = default; - ~DevToolsPageHandler() override = default; - - bool Parse(Extension* extension, base::string16* error) override { - std::unique_ptr manifest_url(new ManifestURL); - std::string devtools_str; - if (!extension->manifest()->GetString(keys::kDevToolsPage, &devtools_str)) { - *error = base::ASCIIToUTF16(errors::kInvalidDevToolsPage); - return false; - } - manifest_url->url_ = extension->GetResourceURL(devtools_str); - extension->SetManifestData(keys::kDevToolsPage, std::move(manifest_url)); - PermissionsParser::AddAPIPermission(extension, APIPermission::kDevtools); - return true; - } - - private: - base::span Keys() const override { - static constexpr const char* kKeys[] = {keys::kDevToolsPage}; - return kKeys; - } - - DISALLOW_COPY_AND_ASSIGN(DevToolsPageHandler); -}; - constexpr APIPermissionInfo::InitInfo permissions_to_register[] = { {APIPermission::kDevtools, "devtools", APIPermissionInfo::kFlagImpliesFullURLAccess | APIPermissionInfo::kFlagCannotBeOptional | APIPermissionInfo::kFlagInternal}, + {APIPermission::kResourcesPrivate, "resourcesPrivate", + APIPermissionInfo::kFlagCannotBeOptional}, }; base::span GetPermissionInfos() { return base::make_span(permissions_to_register); @@ -89,7 +65,7 @@ void ElectronExtensionsAPIProvider::AddManifestFeatures( void ElectronExtensionsAPIProvider::AddPermissionFeatures( extensions::FeatureProvider* provider) { - // No shell-specific permission features. + extensions::AddElectronPermissionFeatures(provider); } void ElectronExtensionsAPIProvider::AddBehaviorFeatures( diff --git a/shell/common/extensions/electron_extensions_client.cc b/shell/common/extensions/electron_extensions_client.cc index fc300ad20d0d..6dd1d401c412 100644 --- a/shell/common/extensions/electron_extensions_client.cc +++ b/shell/common/extensions/electron_extensions_client.cc @@ -117,7 +117,6 @@ extensions::URLPatternSet ElectronExtensionsClient::GetPermittedChromeSchemeHosts( const extensions::Extension* extension, const extensions::APIPermissionSet& api_permissions) const { - NOTIMPLEMENTED(); return extensions::URLPatternSet(); } diff --git a/shell/renderer/renderer_client_base.cc b/shell/renderer/renderer_client_base.cc index 383e73ef421e..1a2f87a846e3 100644 --- a/shell/renderer/renderer_client_base.cc +++ b/shell/renderer/renderer_client_base.cc @@ -67,12 +67,16 @@ #endif // BUILDFLAG(ENABLE_PRINTING) #if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) +#include "base/strings/utf_string_conversions.h" +#include "content/public/common/webplugininfo.h" +#include "extensions/common/constants.h" #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 "extensions/renderer/guest_view/mime_handler_view/mime_handler_view_container_manager.h" #include "shell/common/extensions/electron_extensions_client.h" #include "shell/renderer/extensions/electron_extensions_renderer_client.h" #endif // BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) @@ -271,6 +275,11 @@ void RendererClientBase::RenderFrameCreated( new extensions::ExtensionFrameHelper(render_frame, dispatcher); dispatcher->OnRenderFrameCreated(render_frame); + + render_frame->GetAssociatedInterfaceRegistry()->AddInterface( + base::BindRepeating( + &extensions::MimeHandlerViewContainerManager::BindReceiver, + render_frame->GetRoutingID())); #endif #if BUILDFLAG(ENABLE_BUILTIN_SPELLCHECKER) @@ -337,6 +346,52 @@ void RendererClientBase::DidSetUserAgent(const std::string& user_agent) { #endif } +content::BrowserPluginDelegate* RendererClientBase::CreateBrowserPluginDelegate( + content::RenderFrame* render_frame, + const content::WebPluginInfo& info, + const std::string& mime_type, + const GURL& original_url) { +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) + // TODO(nornagon): check the mime type isn't content::kBrowserPluginMimeType? + return new extensions::MimeHandlerViewContainer(render_frame, info, mime_type, + original_url); +#else + return nullptr; +#endif +} + +bool RendererClientBase::IsPluginHandledExternally( + content::RenderFrame* render_frame, + const blink::WebElement& plugin_element, + const GURL& original_url, + const std::string& mime_type) { +#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS) && BUILDFLAG(ENABLE_PLUGINS) + DCHECK(plugin_element.HasHTMLTagName("object") || + plugin_element.HasHTMLTagName("embed")); + // TODO(nornagon): this info should be shared with the data in + // electron_content_client.cc / ComputeBuiltInPlugins. + content::WebPluginInfo info; + info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN; + info.name = base::UTF8ToUTF16("Chromium PDF Viewer"); + info.path = base::FilePath::FromUTF8Unsafe(extension_misc::kPdfExtensionId); + info.background_color = content::WebPluginInfo::kDefaultBackgroundColor; + info.mime_types.emplace_back("application/pdf", "pdf", + "Portable Document Format"); + return extensions::MimeHandlerViewContainerManager::Get( + content::RenderFrame::FromWebFrame( + plugin_element.GetDocument().GetFrame()), + true /* create_if_does_not_exist */) + ->CreateFrameContainer(plugin_element, original_url, mime_type, info); +#else + return false; +#endif +} + +bool RendererClientBase::IsOriginIsolatedPepperPlugin( + const base::FilePath& plugin_path) { + return plugin_path.value() == kPdfPluginPath; +} + std::unique_ptr RendererClientBase::CreatePrescientNetworking( content::RenderFrame* render_frame) { diff --git a/shell/renderer/renderer_client_base.h b/shell/renderer/renderer_client_base.h index 70c673d0bffe..aded8a0543c5 100644 --- a/shell/renderer/renderer_client_base.h +++ b/shell/renderer/renderer_client_base.h @@ -100,6 +100,16 @@ class RendererClientBase : public content::ContentRendererClient override; bool IsKeySystemsUpdateNeeded() override; void DidSetUserAgent(const std::string& user_agent) override; + content::BrowserPluginDelegate* CreateBrowserPluginDelegate( + content::RenderFrame* render_frame, + const content::WebPluginInfo& info, + const std::string& mime_type, + const GURL& original_url) override; + bool IsPluginHandledExternally(content::RenderFrame* render_frame, + const blink::WebElement& plugin_element, + const GURL& original_url, + const std::string& mime_type) override; + bool IsOriginIsolatedPepperPlugin(const base::FilePath& plugin_path) override; void RunScriptsAtDocumentStart(content::RenderFrame* render_frame) override; void RunScriptsAtDocumentEnd(content::RenderFrame* render_frame) override; diff --git a/spec-main/chromium-spec.ts b/spec-main/chromium-spec.ts index fceb4221616e..d94439a0be9d 100644 --- a/spec-main/chromium-spec.ts +++ b/spec-main/chromium-spec.ts @@ -973,117 +973,25 @@ describe('chromium features', () => { ifdescribe(features.isPDFViewerEnabled())('PDF Viewer', () => { const pdfSource = url.format({ - pathname: path.join(fixturesPath, 'assets', 'cat.pdf').replace(/\\/g, '/'), - protocol: 'file', - slashes: true - }) - const pdfSourceWithParams = url.format({ - pathname: path.join(fixturesPath, 'assets', 'cat.pdf').replace(/\\/g, '/'), - query: { - a: 1, - b: 2 - }, + pathname: path.join(__dirname, 'fixtures', 'cat.pdf').replace(/\\/g, '/'), protocol: 'file', slashes: true }) - const createBrowserWindow = ({ plugins, preload }: { plugins: boolean, preload: string }) => { - return new BrowserWindow({ - show: false, - webPreferences: { - preload: path.join(fixturesPath, 'module', preload), - plugins: plugins - } - }) - } - - const testPDFIsLoadedInSubFrame = (page: string, preloadFile: string, done: Function) => { - const pagePath = url.format({ - pathname: path.join(fixturesPath, 'pages', page).replace(/\\/g, '/'), - protocol: 'file', - slashes: true - }) - - const w = createBrowserWindow({ plugins: true, preload: preloadFile }) - ipcMain.once('pdf-loaded', (event, state) => { - expect(state).to.equal('success') - done() - }) - w.webContents.on('page-title-updated', () => { - const parsedURL = url.parse(w.webContents.getURL(), true) - expect(parsedURL.protocol).to.equal('chrome:') - expect(parsedURL.hostname).to.equal('pdf-viewer') - expect(parsedURL.query.src).to.equal(pagePath) - expect(w.webContents.getTitle()).to.equal('cat.pdf') - }) - w.loadFile(path.join(fixturesPath, 'pages', page)) - } - - it('opens when loading a pdf resource as top level navigation', (done) => { - const w = createBrowserWindow({ plugins: true, preload: 'preload-pdf-loaded.js' }) - ipcMain.once('pdf-loaded', (event, state) => { - expect(state).to.equal('success') - done() - }) - w.webContents.on('page-title-updated', () => { - const parsedURL = url.parse(w.webContents.getURL(), true) - expect(parsedURL.protocol).to.equal('chrome:') - expect(parsedURL.hostname).to.equal('pdf-viewer') - expect(parsedURL.query.src).to.equal(pdfSource) - expect(w.webContents.getTitle()).to.equal('cat.pdf') - }) - w.webContents.loadURL(pdfSource) - }) - - it('opens a pdf link given params, the query string should be escaped', (done) => { - const w = createBrowserWindow({ plugins: true, preload: 'preload-pdf-loaded.js' }) - ipcMain.once('pdf-loaded', (event, state) => { - expect(state).to.equal('success') - done() - }) - w.webContents.on('page-title-updated', () => { - const parsedURL = url.parse(w.webContents.getURL(), true) - expect(parsedURL.protocol).to.equal('chrome:') - expect(parsedURL.hostname).to.equal('pdf-viewer') - expect(parsedURL.query.src).to.equal(pdfSourceWithParams) - expect(parsedURL.query.b).to.be.undefined() - expect(parsedURL.search!.endsWith('%3Fa%3D1%26b%3D2')).to.be.true() - expect(w.webContents.getTitle()).to.equal('cat.pdf') - }) - w.webContents.loadURL(pdfSourceWithParams) - }) - - it('should download a pdf when plugins are disabled', async () => { - const w = createBrowserWindow({ plugins: false, preload: 'preload-pdf-loaded.js' }) - w.webContents.loadURL(pdfSource) - const [state, filename, mimeType] = await new Promise(resolve => { - session.defaultSession.once('will-download', (event, item) => { - item.setSavePath(path.join(fixturesPath, 'mock.pdf')) - item.on('done', (e, state) => { - resolve([state, item.getFilename(), item.getMimeType()]) - }) - }) - }) - expect(state).to.equal('completed') - expect(filename).to.equal('cat.pdf') - expect(mimeType).to.equal('application/pdf') - fs.unlinkSync(path.join(fixturesPath, 'mock.pdf')) - }) - - it('should not open when pdf is requested as sub resource', async () => { + it('opens when loading a pdf resource as top level navigation', async () => { const w = new BrowserWindow({ show: false }) - w.loadURL('about:blank') - const [status, title] = await w.webContents.executeJavaScript(`fetch(${JSON.stringify(pdfSource)}).then(res => [res.status, document.title])`) - expect(status).to.equal(200) - expect(title).to.not.equal('cat.pdf') + w.loadURL(pdfSource) + const [, contents] = await emittedOnce(app, 'web-contents-created') + expect(contents.getURL()).to.equal('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html') + await emittedOnce(contents, 'did-finish-load') }) - it('opens when loading a pdf resource in a iframe', (done) => { - testPDFIsLoadedInSubFrame('pdf-in-iframe.html', 'preload-pdf-loaded-in-subframe.js', done) - }) - - it('opens when loading a pdf resource in a nested iframe', (done) => { - testPDFIsLoadedInSubFrame('pdf-in-nested-iframe.html', 'preload-pdf-loaded-in-nested-subframe.js', done) + it('opens when loading a pdf resource in a iframe', async () => { + const w = new BrowserWindow({ show: false }) + w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'pdf-in-iframe.html')) + const [, contents] = await emittedOnce(app, 'web-contents-created') + expect(contents.getURL()).to.equal('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html') + await emittedOnce(contents, 'did-finish-load') }) }) diff --git a/spec/fixtures/assets/cat.pdf b/spec-main/fixtures/cat.pdf similarity index 100% rename from spec/fixtures/assets/cat.pdf rename to spec-main/fixtures/cat.pdf diff --git a/spec-main/fixtures/pages/pdf-in-iframe.html b/spec-main/fixtures/pages/pdf-in-iframe.html new file mode 100644 index 000000000000..e42aa85490a0 --- /dev/null +++ b/spec-main/fixtures/pages/pdf-in-iframe.html @@ -0,0 +1,6 @@ + + +