diff options
Diffstat (limited to 'chromium/extensions/browser/extension_user_script_loader.cc')
-rw-r--r-- | chromium/extensions/browser/extension_user_script_loader.cc | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/chromium/extensions/browser/extension_user_script_loader.cc b/chromium/extensions/browser/extension_user_script_loader.cc new file mode 100644 index 00000000000..3b3f10cea83 --- /dev/null +++ b/chromium/extensions/browser/extension_user_script_loader.cc @@ -0,0 +1,239 @@ +// 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 "extensions/browser/extension_user_script_loader.h" + +#include <stddef.h> + +#include <set> +#include <string> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/version.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/render_process_host.h" +#include "extensions/browser/component_extension_resource_manager.h" +#include "extensions/browser/content_verifier.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/extensions_browser_client.h" +#include "extensions/common/file_util.h" +#include "extensions/common/manifest_handlers/default_locale_handler.h" +#include "extensions/common/message_bundle.h" +#include "extensions/common/one_shot_event.h" +#include "ui/base/resource/resource_bundle.h" + +using content::BrowserContext; + +namespace extensions { + +namespace { + +using SubstitutionMap = std::map<std::string, std::string>; + +// Verifies file contents as they are read. +void VerifyContent(const scoped_refptr<ContentVerifier>& verifier, + const std::string& extension_id, + const base::FilePath& extension_root, + const base::FilePath& relative_path, + const std::string& content) { + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + scoped_refptr<ContentVerifyJob> job( + verifier->CreateJobFor(extension_id, extension_root, relative_path)); + if (job.get()) { + job->Start(); + job->BytesRead(content.size(), content.data()); + job->DoneReading(); + } +} + +// Loads user scripts from the extension who owns these scripts. +bool LoadScriptContent(const HostID& host_id, + UserScript::File* script_file, + const SubstitutionMap* localization_messages, + const scoped_refptr<ContentVerifier>& verifier) { + DCHECK(script_file); + std::string content; + const base::FilePath& path = ExtensionResource::GetFilePath( + script_file->extension_root(), script_file->relative_path(), + ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT); + if (path.empty()) { + int resource_id = 0; + if (ExtensionsBrowserClient::Get() + ->GetComponentExtensionResourceManager() + ->IsComponentExtensionResource(script_file->extension_root(), + script_file->relative_path(), + &resource_id)) { + const ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + content = rb.GetRawDataResource(resource_id).as_string(); + } else { + LOG(WARNING) << "Failed to get file path to " + << script_file->relative_path().value() << " from " + << script_file->extension_root().value(); + return false; + } + } else { + if (!base::ReadFileToString(path, &content)) { + LOG(WARNING) << "Failed to load user script file: " << path.value(); + return false; + } + if (verifier.get()) { + content::BrowserThread::PostTask( + content::BrowserThread::IO, FROM_HERE, + base::Bind(&VerifyContent, verifier, host_id.id(), + script_file->extension_root(), + script_file->relative_path(), content)); + } + } + + // Localize the content. + if (localization_messages) { + std::string error; + MessageBundle::ReplaceMessagesWithExternalDictionary(*localization_messages, + &content, &error); + if (!error.empty()) + LOG(WARNING) << "Failed to replace messages in script: " << error; + } + + // Remove BOM from the content. + std::string::size_type index = content.find(base::kUtf8ByteOrderMark); + if (index == 0) + script_file->set_content(content.substr(strlen(base::kUtf8ByteOrderMark))); + else + script_file->set_content(content); + + return true; +} + +SubstitutionMap* GetLocalizationMessages( + const ExtensionUserScriptLoader::HostsInfo& hosts_info, + const HostID& host_id) { + ExtensionUserScriptLoader::HostsInfo::const_iterator iter = + hosts_info.find(host_id); + if (iter == hosts_info.end()) + return nullptr; + return file_util::LoadMessageBundleSubstitutionMap( + iter->second.first, host_id.id(), iter->second.second); +} + +void LoadUserScripts(UserScriptList* user_scripts, + const ExtensionUserScriptLoader::HostsInfo& hosts_info, + const std::set<int>& added_script_ids, + const scoped_refptr<ContentVerifier>& verifier) { + for (UserScript& script : *user_scripts) { + if (added_script_ids.count(script.id()) == 0) + continue; + scoped_ptr<SubstitutionMap> localization_messages( + GetLocalizationMessages(hosts_info, script.host_id())); + for (UserScript::File& script_file : script.js_scripts()) { + if (script_file.GetContent().empty()) + LoadScriptContent(script.host_id(), &script_file, nullptr, verifier); + } + for (UserScript::File& script_file : script.css_scripts()) { + if (script_file.GetContent().empty()) + LoadScriptContent(script.host_id(), &script_file, + localization_messages.get(), verifier); + } + } +} + +void LoadScriptsOnFileThread( + scoped_ptr<UserScriptList> user_scripts, + const ExtensionUserScriptLoader::HostsInfo& hosts_info, + const std::set<int>& added_script_ids, + const scoped_refptr<ContentVerifier>& verifier, + UserScriptLoader::LoadScriptsCallback callback) { + DCHECK(user_scripts.get()); + LoadUserScripts(user_scripts.get(), hosts_info, added_script_ids, verifier); + scoped_ptr<base::SharedMemory> memory = + UserScriptLoader::Serialize(*user_scripts); + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::Bind(callback, base::Passed(&user_scripts), base::Passed(&memory))); +} + +} // namespace + +ExtensionUserScriptLoader::ExtensionUserScriptLoader( + BrowserContext* browser_context, + const HostID& host_id, + bool listen_for_extension_system_loaded) + : UserScriptLoader(browser_context, host_id), + content_verifier_( + ExtensionSystem::Get(browser_context)->content_verifier()), + extension_registry_observer_(this), + weak_factory_(this) { + extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context)); + if (listen_for_extension_system_loaded) { + ExtensionSystem::Get(browser_context) + ->ready() + .Post(FROM_HERE, + base::Bind(&ExtensionUserScriptLoader::OnExtensionSystemReady, + weak_factory_.GetWeakPtr())); + } else { + SetReady(true); + } +} + +ExtensionUserScriptLoader::~ExtensionUserScriptLoader() { +} + +void ExtensionUserScriptLoader::LoadScriptsForTest( + UserScriptList* user_scripts) { + HostsInfo info; + std::set<int> added_script_ids; + for (UserScript& script : *user_scripts) + added_script_ids.insert(script.id()); + + LoadUserScripts(user_scripts, info, added_script_ids, + nullptr /* no verifier for testing */); +} + +void ExtensionUserScriptLoader::LoadScripts( + scoped_ptr<UserScriptList> user_scripts, + const std::set<HostID>& changed_hosts, + const std::set<int>& added_script_ids, + LoadScriptsCallback callback) { + UpdateHostsInfo(changed_hosts); + + content::BrowserThread::PostTask( + content::BrowserThread::FILE, FROM_HERE, + base::Bind(&LoadScriptsOnFileThread, base::Passed(&user_scripts), + hosts_info_, added_script_ids, content_verifier_, callback)); +} + +void ExtensionUserScriptLoader::UpdateHostsInfo( + const std::set<HostID>& changed_hosts) { + ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context()); + for (const HostID& host_id : changed_hosts) { + const Extension* extension = + registry->GetExtensionById(host_id.id(), ExtensionRegistry::ENABLED); + // |changed_hosts_| may include hosts that have been removed, + // which leads to the above lookup failing. In this case, just continue. + if (!extension) + continue; + if (hosts_info_.find(host_id) != hosts_info_.end()) + continue; + hosts_info_[host_id] = ExtensionSet::ExtensionPathAndDefaultLocale( + extension->path(), LocaleInfo::GetDefaultLocale(extension)); + } +} + +void ExtensionUserScriptLoader::OnExtensionUnloaded( + content::BrowserContext* browser_context, + const Extension* extension, + UnloadedExtensionInfo::Reason reason) { + hosts_info_.erase(HostID(HostID::EXTENSIONS, extension->id())); +} + +void ExtensionUserScriptLoader::OnExtensionSystemReady() { + SetReady(true); +} + +} // namespace extensions |