| /**************************************************************************** |
| ** |
| ** Copyright (C) 2018 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtWebEngine module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| // 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 "extension_system_qt.h" |
| |
| #include <algorithm> |
| |
| #include "base/base_paths.h" |
| #include "base/base_switches.h" |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/json/json_string_value_serializer.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/path_service.h" |
| #include "base/strings/string_tokenizer.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/time/time.h" |
| #include "base/trace_event/trace_event.h" |
| #include "build/build_config.h" |
| #include "components/crx_file/id_util.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/plugin_service.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/browser/url_data_source.h" |
| #include "content/public/common/webplugininfo.h" |
| #include "extensions/browser/content_verifier.h" |
| #include "extensions/browser/content_verifier_delegate.h" |
| #include "extensions/browser/extension_pref_store.h" |
| #include "extensions/browser/extension_pref_value_map.h" |
| #include "extensions/browser/extension_pref_value_map_factory.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/info_map.h" |
| #include "extensions/browser/notification_types.h" |
| #include "extensions/browser/null_app_sorting.h" |
| #include "extensions/browser/quota_service.h" |
| #include "extensions/browser/renderer_startup_helper.h" |
| #include "extensions/browser/runtime_data.h" |
| #include "extensions/browser/shared_user_script_master.h" |
| #include "extensions/browser/service_worker_manager.h" |
| #include "extensions/browser/value_store/value_store_factory_impl.h" |
| #include "extensions/common/constants.h" |
| #include "extensions/common/extension_messages.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "extensions/common/manifest_handlers/mime_types_handler.h" |
| #include "extensions/common/manifest_url_handlers.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "chrome/grit/component_extension_resources.h" |
| #include "chrome/grit/browser_resources.h" |
| #include "net/base/mime_util.h" |
| |
| using content::BrowserThread; |
| |
| namespace extensions { |
| |
| namespace { |
| |
| std::string GenerateId(const base::DictionaryValue *manifest, const base::FilePath &path) |
| { |
| std::string raw_key; |
| std::string id_input; |
| CHECK(manifest->GetString(manifest_keys::kPublicKey, &raw_key)); |
| CHECK(Extension::ParsePEMKeyBytes(raw_key, &id_input)); |
| std::string id = crx_file::id_util::GenerateId(id_input); |
| return id; |
| } |
| |
| // Implementation based on ComponentLoader::ParseManifest. |
| std::unique_ptr<base::DictionaryValue> ParseManifest(const std::string &manifest_contents) |
| { |
| JSONStringValueDeserializer deserializer(manifest_contents); |
| std::unique_ptr<base::Value> manifest(deserializer.Deserialize(NULL, NULL)); |
| |
| if (!manifest.get() || !manifest->is_dict()) { |
| LOG(ERROR) << "Failed to parse extension manifest."; |
| return NULL; |
| } |
| // Transfer ownership to the caller. |
| return base::DictionaryValue::From(std::move(manifest)); |
| } |
| |
| } // namespace |
| |
| // Dummy Content Verifier Delegate. Added to prevent crashes. |
| class ContentVerifierDelegateQt : public ContentVerifierDelegate |
| { |
| public: |
| ~ContentVerifierDelegateQt() override {} |
| |
| // This should return what verification mode is appropriate for the given |
| // extension, if any. |
| bool ShouldBeVerified(const Extension &extension) override { return false; } |
| |
| // Should return the public key to use for validating signatures via the two |
| // out parameters. |
| ContentVerifierKey GetPublicKey() override { return ContentVerifierKey(); } |
| // This should return a URL that can be used to fetch the |
| // verified_contents.json containing signatures for the given extension |
| // id/version pair. |
| GURL GetSignatureFetchUrl(const std::string &extension_id, const base::Version &version) override { return GURL(); } |
| |
| // This should return the set of file paths for images used within the |
| // browser process. (These may get transcoded during the install process). |
| std::set<base::FilePath> GetBrowserImagePaths(const extensions::Extension *extension) override |
| { |
| return std::set<base::FilePath>(); |
| } |
| |
| // Called when the content verifier detects that a read of a file inside |
| // an extension did not match its expected hash. |
| void VerifyFailed(const std::string &extension_id, ContentVerifyJob::FailureReason reason) override {} |
| |
| // Called when ExtensionSystem is shutting down. |
| void Shutdown() override {} |
| }; |
| |
| void ExtensionSystemQt::LoadExtension(std::string extension_id, std::unique_ptr<base::DictionaryValue> manifest, const base::FilePath &directory) |
| { |
| int flags = Extension::REQUIRE_KEY; |
| std::string error; |
| scoped_refptr<const Extension> extension = Extension::Create( |
| directory, |
| Manifest::COMPONENT, |
| *manifest, |
| flags, |
| &error); |
| if (!extension.get()) |
| LOG(ERROR) << error; |
| |
| base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, |
| base::Bind(&InfoMap::AddExtension, |
| base::Unretained(info_map()), |
| base::RetainedRef(extension), |
| base::Time::Now(), |
| true, |
| false)); |
| extension_registry_->AddEnabled(extension.get()); |
| |
| NotifyExtensionLoaded(extension.get()); |
| } |
| |
| void ExtensionSystemQt::OnExtensionRegisteredWithRequestContexts(scoped_refptr<const extensions::Extension> extension) |
| { |
| extension_registry_->AddReady(extension); |
| if (extension_registry_->enabled_extensions().Contains(extension->id())) |
| extension_registry_->TriggerOnReady(extension.get()); |
| } |
| |
| // Implementation based on ExtensionService::NotifyExtensionLoaded. |
| void ExtensionSystemQt::NotifyExtensionLoaded(const Extension *extension) |
| { |
| // The URLRequestContexts need to be first to know that the extension |
| // was loaded, otherwise a race can arise where a renderer that is created |
| // for the extension may try to load an extension URL with an extension id |
| // that the request context doesn't yet know about. The profile is responsible |
| // for ensuring its URLRequestContexts appropriately discover the loaded |
| // extension. |
| RegisterExtensionWithRequestContexts( |
| extension, |
| base::Bind(&ExtensionSystemQt::OnExtensionRegisteredWithRequestContexts, |
| weak_ptr_factory_.GetWeakPtr(), |
| base::WrapRefCounted(extension))); |
| |
| // Tell renderers about the loaded extension. |
| renderer_helper_->OnExtensionLoaded(*extension); |
| |
| // Tell subsystems that use the ExtensionRegistryObserver::OnExtensionLoaded |
| // about the new extension. |
| // |
| // NOTE: It is important that this happen after notifying the renderers about |
| // the new extensions so that if we navigate to an extension URL in |
| // ExtensionRegistryObserver::OnExtensionLoaded the renderer is guaranteed to |
| // know about it. |
| extension_registry_->TriggerOnLoaded(extension); |
| |
| // Register plugins included with the extension. |
| // Implementation based on PluginManager::OnExtensionLoaded. |
| const MimeTypesHandler *handler = MimeTypesHandler::GetHandler(extension); |
| if (handler && !handler->handler_url().empty()) { |
| content::WebPluginInfo info; |
| info.type = content::WebPluginInfo::PLUGIN_TYPE_BROWSER_PLUGIN; |
| info.name = base::UTF8ToUTF16(extension->name()); |
| info.path = base::FilePath::FromUTF8Unsafe(extension->url().spec()); |
| for (std::set<std::string>::const_iterator mime_type = handler->mime_type_set().begin(); |
| mime_type != handler->mime_type_set().end(); ++mime_type) { |
| content::WebPluginMimeType mime_type_info; |
| mime_type_info.mime_type = *mime_type; |
| base::FilePath::StringType file_extension; |
| if (net::GetPreferredExtensionForMimeType(*mime_type, &file_extension)) { |
| mime_type_info.file_extensions.push_back( |
| base::FilePath(file_extension).AsUTF8Unsafe()); |
| } |
| info.mime_types.push_back(mime_type_info); |
| } |
| content::PluginService *plugin_service = |
| content::PluginService::GetInstance(); |
| plugin_service->RefreshPlugins(); |
| plugin_service->RegisterInternalPlugin(info, true); |
| } |
| } |
| |
| bool ExtensionSystemQt::FinishDelayedInstallationIfReady(const std::string &extension_id, bool install_immediately) |
| { |
| // TODO mibrunin |
| return false; |
| } |
| |
| void ExtensionSystemQt::Shutdown() |
| { |
| if (content_verifier_.get()) |
| content_verifier_->Shutdown(); |
| } |
| |
| ServiceWorkerManager *ExtensionSystemQt::service_worker_manager() |
| { |
| return service_worker_manager_.get(); |
| } |
| |
| ExtensionService *ExtensionSystemQt::extension_service() |
| { |
| return nullptr; |
| } |
| |
| RuntimeData *ExtensionSystemQt::runtime_data() |
| { |
| return runtime_data_.get(); |
| } |
| |
| ManagementPolicy *ExtensionSystemQt::management_policy() |
| { |
| return nullptr; |
| } |
| |
| SharedUserScriptMaster *ExtensionSystemQt::shared_user_script_master() |
| { |
| return shared_user_script_master_.get(); |
| } |
| |
| StateStore *ExtensionSystemQt::state_store() |
| { |
| return nullptr; |
| } |
| |
| StateStore *ExtensionSystemQt::rules_store() |
| { |
| return nullptr; |
| } |
| |
| scoped_refptr<ValueStoreFactory> ExtensionSystemQt::store_factory() |
| { |
| return store_factory_; |
| } |
| |
| InfoMap *ExtensionSystemQt::info_map() |
| { |
| if (!info_map_.get()) |
| info_map_ = new InfoMap; |
| return info_map_.get(); |
| } |
| |
| QuotaService *ExtensionSystemQt::quota_service() |
| { |
| return quota_service_.get(); |
| } |
| |
| AppSorting *ExtensionSystemQt::app_sorting() |
| { |
| return app_sorting_.get(); |
| } |
| |
| ContentVerifier *ExtensionSystemQt::content_verifier() |
| { |
| if (!content_verifier_.get()) { |
| content_verifier_ = new ContentVerifier(browser_context_, std::make_unique<ContentVerifierDelegateQt>()); |
| } |
| return content_verifier_.get(); |
| } |
| |
| ExtensionSystemQt::ExtensionSystemQt(content::BrowserContext *browserContext) |
| : browser_context_(browserContext) |
| , store_factory_(new ValueStoreFactoryImpl(browserContext->GetPath())) |
| , extension_registry_(ExtensionRegistry::Get(browserContext)) |
| , renderer_helper_(extensions::RendererStartupHelperFactory::GetForBrowserContext(browserContext)) |
| , initialized_(false) |
| , weak_ptr_factory_(this) |
| { |
| } |
| |
| ExtensionSystemQt::~ExtensionSystemQt() |
| { |
| } |
| |
| void ExtensionSystemQt::Init(bool extensions_enabled) |
| { |
| if (initialized_) |
| return; |
| |
| initialized_ = true; |
| |
| service_worker_manager_.reset(new ServiceWorkerManager(browser_context_)); |
| runtime_data_.reset(new RuntimeData(extension_registry_)); |
| quota_service_.reset(new QuotaService); |
| app_sorting_.reset(new NullAppSorting); |
| |
| shared_user_script_master_ = |
| std::make_unique<SharedUserScriptMaster>(browser_context_); |
| |
| // Make the chrome://extension-icon/ resource available. |
| // content::URLDataSource::Add(browser_context_, new ExtensionIconSource(browser_context_)); |
| |
| if (extensions_enabled) { |
| // Inform the rest of the extensions system to start. |
| ready_.Signal(); |
| content::NotificationService::current()->Notify( |
| NOTIFICATION_EXTENSIONS_READY_DEPRECATED, |
| content::Source<content::BrowserContext>(browser_context_), |
| content::NotificationService::NoDetails()); |
| |
| std::string pdf_manifest = ui::ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_PDF_MANIFEST).as_string(); |
| base::ReplaceFirstSubstringAfterOffset(&pdf_manifest, 0, "<NAME>", "chromium-pdf"); |
| |
| std::unique_ptr<base::DictionaryValue> pdfManifestDict = ParseManifest(pdf_manifest); |
| base::FilePath path; |
| base::PathService::Get(base::DIR_QT_LIBRARY_DATA, &path); |
| path = path.Append(base::FilePath(FILE_PATH_LITERAL("pdf"))); |
| std::string id = GenerateId(pdfManifestDict.get(), path); |
| LoadExtension(id, std::move(pdfManifestDict), path); |
| } |
| } |
| |
| void ExtensionSystemQt::InitForRegularProfile(bool extensions_enabled) |
| { |
| if (initialized_) |
| return; // Already initialized. |
| // The InfoMap needs to be created before the ProcessManager. |
| info_map(); |
| |
| Init(extensions_enabled); |
| } |
| |
| std::unique_ptr<ExtensionSet> ExtensionSystemQt::GetDependentExtensions(const Extension *extension) |
| { |
| return base::WrapUnique(new ExtensionSet()); |
| } |
| |
| #if !defined(TOOLKIT_QT) |
| void ExtensionSystemQt::InstallUpdate(const std::string &extension_id, |
| const std::string &public_key, |
| const base::FilePath &unpacked_dir, |
| bool install_immediately, |
| InstallUpdateCallback install_update_callback) |
| { |
| NOTREACHED() << "Not yet implemented"; |
| base::DeleteFile(unpacked_dir, true /* recursive */); |
| std::move(install_update_callback).Run(CrxInstallError(CrxInstallErrorType::DECLINED, CrxInstallErrorDetail::DISALLOWED_BY_POLICY)); |
| } |
| #endif |
| |
| void ExtensionSystemQt::RegisterExtensionWithRequestContexts(const Extension *extension, |
| const base::Closure &callback) |
| { |
| base::Time install_time = base::Time::Now(); |
| |
| bool incognito_enabled = false; |
| bool notifications_disabled = false; |
| |
| base::PostTaskWithTraitsAndReply( |
| FROM_HERE, {BrowserThread::IO}, |
| base::Bind(&InfoMap::AddExtension, info_map(), |
| base::RetainedRef(extension), install_time, incognito_enabled, |
| notifications_disabled), |
| callback); |
| } |
| |
| void ExtensionSystemQt::UnregisterExtensionWithRequestContexts(const std::string &extension_id, |
| const UnloadedExtensionReason reason) |
| { |
| base::PostTaskWithTraits( |
| FROM_HERE, {BrowserThread::IO}, |
| base::Bind(&InfoMap::RemoveExtension, info_map(), extension_id, reason)); |
| } |
| } // namespace extensions |