// 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 #include #include #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; // Verifies file contents as they are read. void VerifyContent(const scoped_refptr& 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 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& 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& added_script_ids, const scoped_refptr& verifier) { for (UserScript& script : *user_scripts) { if (added_script_ids.count(script.id()) == 0) continue; scoped_ptr 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 user_scripts, const ExtensionUserScriptLoader::HostsInfo& hosts_info, const std::set& added_script_ids, const scoped_refptr& verifier, UserScriptLoader::LoadScriptsCallback callback) { DCHECK(user_scripts.get()); LoadUserScripts(user_scripts.get(), hosts_info, added_script_ids, verifier); scoped_ptr 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 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 user_scripts, const std::set& changed_hosts, const std::set& 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& 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