blob: 2fe0ecab4aa1a9324a76c65d56f7963fb22f9209 [file] [log] [blame]
hanxic0503d72015-02-05 14:27:321// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
hanxi3b2b3df2015-02-24 15:28:075#include "extensions/browser/extension_user_script_loader.h"
hanxic0503d72015-02-05 14:27:326
avic9cec102015-12-23 00:39:267#include <stddef.h>
8
hanxic0503d72015-02-05 14:27:329#include <set>
10#include <string>
11
12#include "base/bind.h"
13#include "base/bind_helpers.h"
14#include "base/files/file_path.h"
15#include "base/files/file_util.h"
lazyboy28acad82016-06-22 23:14:2016#include "base/strings/string_util.h"
hanxic0503d72015-02-05 14:27:3217#include "base/version.h"
hanxi3b2b3df2015-02-24 15:28:0718#include "content/public/browser/browser_context.h"
hanxic0503d72015-02-05 14:27:3219#include "content/public/browser/browser_thread.h"
20#include "content/public/browser/notification_service.h"
21#include "content/public/browser/render_process_host.h"
22#include "extensions/browser/component_extension_resource_manager.h"
23#include "extensions/browser/content_verifier.h"
24#include "extensions/browser/extension_registry.h"
25#include "extensions/browser/extension_system.h"
26#include "extensions/browser/extensions_browser_client.h"
27#include "extensions/common/file_util.h"
28#include "extensions/common/manifest_handlers/default_locale_handler.h"
29#include "extensions/common/message_bundle.h"
30#include "extensions/common/one_shot_event.h"
31#include "ui/base/resource/resource_bundle.h"
32
hanxi3b2b3df2015-02-24 15:28:0733using content::BrowserContext;
34
hanxic0503d72015-02-05 14:27:3235namespace extensions {
36
37namespace {
38
hanxifeb6a64f2015-04-24 19:21:4039using SubstitutionMap = std::map<std::string, std::string>;
40
hanxic0503d72015-02-05 14:27:3241// Verifies file contents as they are read.
42void VerifyContent(const scoped_refptr<ContentVerifier>& verifier,
43 const std::string& extension_id,
44 const base::FilePath& extension_root,
45 const base::FilePath& relative_path,
46 const std::string& content) {
47 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
48 scoped_refptr<ContentVerifyJob> job(
49 verifier->CreateJobFor(extension_id, extension_root, relative_path));
50 if (job.get()) {
51 job->Start();
52 job->BytesRead(content.size(), content.data());
53 job->DoneReading();
54 }
55}
56
57// Loads user scripts from the extension who owns these scripts.
hanxifeb6a64f2015-04-24 19:21:4058bool LoadScriptContent(const HostID& host_id,
59 UserScript::File* script_file,
60 const SubstitutionMap* localization_messages,
61 const scoped_refptr<ContentVerifier>& verifier) {
hanxic0503d72015-02-05 14:27:3262 DCHECK(script_file);
63 std::string content;
64 const base::FilePath& path = ExtensionResource::GetFilePath(
65 script_file->extension_root(), script_file->relative_path(),
66 ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT);
67 if (path.empty()) {
68 int resource_id = 0;
69 if (ExtensionsBrowserClient::Get()
70 ->GetComponentExtensionResourceManager()
71 ->IsComponentExtensionResource(script_file->extension_root(),
72 script_file->relative_path(),
73 &resource_id)) {
74 const ResourceBundle& rb = ResourceBundle::GetSharedInstance();
75 content = rb.GetRawDataResource(resource_id).as_string();
76 } else {
77 LOG(WARNING) << "Failed to get file path to "
78 << script_file->relative_path().value() << " from "
79 << script_file->extension_root().value();
80 return false;
81 }
82 } else {
83 if (!base::ReadFileToString(path, &content)) {
84 LOG(WARNING) << "Failed to load user script file: " << path.value();
85 return false;
86 }
87 if (verifier.get()) {
88 content::BrowserThread::PostTask(
89 content::BrowserThread::IO, FROM_HERE,
90 base::Bind(&VerifyContent, verifier, host_id.id(),
91 script_file->extension_root(),
92 script_file->relative_path(), content));
93 }
94 }
95
96 // Localize the content.
97 if (localization_messages) {
98 std::string error;
99 MessageBundle::ReplaceMessagesWithExternalDictionary(*localization_messages,
100 &content, &error);
101 if (!error.empty())
102 LOG(WARNING) << "Failed to replace messages in script: " << error;
103 }
104
105 // Remove BOM from the content.
lazyboy28acad82016-06-22 23:14:20106 if (base::StartsWith(content, base::kUtf8ByteOrderMark,
107 base::CompareCase::SENSITIVE)) {
hanxic0503d72015-02-05 14:27:32108 script_file->set_content(content.substr(strlen(base::kUtf8ByteOrderMark)));
lazyboy28acad82016-06-22 23:14:20109 } else {
hanxic0503d72015-02-05 14:27:32110 script_file->set_content(content);
lazyboy28acad82016-06-22 23:14:20111 }
hanxic0503d72015-02-05 14:27:32112
113 return true;
114}
115
hanxifeb6a64f2015-04-24 19:21:40116SubstitutionMap* GetLocalizationMessages(
117 const ExtensionUserScriptLoader::HostsInfo& hosts_info,
118 const HostID& host_id) {
119 ExtensionUserScriptLoader::HostsInfo::const_iterator iter =
120 hosts_info.find(host_id);
121 if (iter == hosts_info.end())
122 return nullptr;
123 return file_util::LoadMessageBundleSubstitutionMap(
124 iter->second.first, host_id.id(), iter->second.second);
125}
126
127void LoadUserScripts(UserScriptList* user_scripts,
128 const ExtensionUserScriptLoader::HostsInfo& hosts_info,
129 const std::set<int>& added_script_ids,
130 const scoped_refptr<ContentVerifier>& verifier) {
lazyboy12c77d72016-08-19 20:06:09131 for (const std::unique_ptr<UserScript>& script : *user_scripts) {
132 if (added_script_ids.count(script->id()) == 0)
hanxifeb6a64f2015-04-24 19:21:40133 continue;
lazyboy12c77d72016-08-19 20:06:09134 for (const std::unique_ptr<UserScript::File>& script_file :
135 script->js_scripts()) {
136 if (script_file->GetContent().empty())
137 LoadScriptContent(script->host_id(), script_file.get(), nullptr,
138 verifier);
hanxifeb6a64f2015-04-24 19:21:40139 }
lazyboy12c77d72016-08-19 20:06:09140 if (script->css_scripts().size() > 0) {
lazyboy4ff54b32016-08-09 20:49:08141 std::unique_ptr<SubstitutionMap> localization_messages(
lazyboy12c77d72016-08-19 20:06:09142 GetLocalizationMessages(hosts_info, script->host_id()));
143 for (const std::unique_ptr<UserScript::File>& script_file :
144 script->css_scripts()) {
145 if (script_file->GetContent().empty()) {
146 LoadScriptContent(script->host_id(), script_file.get(),
lazyboy4ff54b32016-08-09 20:49:08147 localization_messages.get(), verifier);
148 }
149 }
hanxifeb6a64f2015-04-24 19:21:40150 }
151 }
152}
153
154void LoadScriptsOnFileThread(
dchengf5d241082016-04-21 03:43:11155 std::unique_ptr<UserScriptList> user_scripts,
hanxifeb6a64f2015-04-24 19:21:40156 const ExtensionUserScriptLoader::HostsInfo& hosts_info,
157 const std::set<int>& added_script_ids,
158 const scoped_refptr<ContentVerifier>& verifier,
159 UserScriptLoader::LoadScriptsCallback callback) {
160 DCHECK(user_scripts.get());
161 LoadUserScripts(user_scripts.get(), hosts_info, added_script_ids, verifier);
dchengf5d241082016-04-21 03:43:11162 std::unique_ptr<base::SharedMemory> memory =
hanxifeb6a64f2015-04-24 19:21:40163 UserScriptLoader::Serialize(*user_scripts);
164 content::BrowserThread::PostTask(
165 content::BrowserThread::UI, FROM_HERE,
166 base::Bind(callback, base::Passed(&user_scripts), base::Passed(&memory)));
167}
168
hanxic0503d72015-02-05 14:27:32169} // namespace
170
171ExtensionUserScriptLoader::ExtensionUserScriptLoader(
hanxi3b2b3df2015-02-24 15:28:07172 BrowserContext* browser_context,
hanxic0503d72015-02-05 14:27:32173 const HostID& host_id,
174 bool listen_for_extension_system_loaded)
hanxifeb6a64f2015-04-24 19:21:40175 : UserScriptLoader(browser_context, host_id),
176 content_verifier_(
hanxi3b2b3df2015-02-24 15:28:07177 ExtensionSystem::Get(browser_context)->content_verifier()),
hanxic0503d72015-02-05 14:27:32178 extension_registry_observer_(this),
179 weak_factory_(this) {
hanxi3b2b3df2015-02-24 15:28:07180 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context));
hanxic0503d72015-02-05 14:27:32181 if (listen_for_extension_system_loaded) {
hanxi3b2b3df2015-02-24 15:28:07182 ExtensionSystem::Get(browser_context)
183 ->ready()
184 .Post(FROM_HERE,
185 base::Bind(&ExtensionUserScriptLoader::OnExtensionSystemReady,
186 weak_factory_.GetWeakPtr()));
hanxic0503d72015-02-05 14:27:32187 } else {
188 SetReady(true);
189 }
190}
191
192ExtensionUserScriptLoader::~ExtensionUserScriptLoader() {
193}
194
hanxifeb6a64f2015-04-24 19:21:40195void ExtensionUserScriptLoader::LoadScriptsForTest(
196 UserScriptList* user_scripts) {
197 HostsInfo info;
198 std::set<int> added_script_ids;
lazyboy12c77d72016-08-19 20:06:09199 for (const std::unique_ptr<UserScript>& script : *user_scripts)
200 added_script_ids.insert(script->id());
hanxifeb6a64f2015-04-24 19:21:40201
202 LoadUserScripts(user_scripts, info, added_script_ids,
203 nullptr /* no verifier for testing */);
204}
205
206void ExtensionUserScriptLoader::LoadScripts(
dchengf5d241082016-04-21 03:43:11207 std::unique_ptr<UserScriptList> user_scripts,
hanxifeb6a64f2015-04-24 19:21:40208 const std::set<HostID>& changed_hosts,
209 const std::set<int>& added_script_ids,
210 LoadScriptsCallback callback) {
211 UpdateHostsInfo(changed_hosts);
212
213 content::BrowserThread::PostTask(
214 content::BrowserThread::FILE, FROM_HERE,
215 base::Bind(&LoadScriptsOnFileThread, base::Passed(&user_scripts),
216 hosts_info_, added_script_ids, content_verifier_, callback));
217}
218
hanxic0503d72015-02-05 14:27:32219void ExtensionUserScriptLoader::UpdateHostsInfo(
220 const std::set<HostID>& changed_hosts) {
hanxi3b2b3df2015-02-24 15:28:07221 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
hanxic0503d72015-02-05 14:27:32222 for (const HostID& host_id : changed_hosts) {
223 const Extension* extension =
224 registry->GetExtensionById(host_id.id(), ExtensionRegistry::ENABLED);
225 // |changed_hosts_| may include hosts that have been removed,
226 // which leads to the above lookup failing. In this case, just continue.
227 if (!extension)
228 continue;
hanxifeb6a64f2015-04-24 19:21:40229 if (hosts_info_.find(host_id) != hosts_info_.end())
230 continue;
231 hosts_info_[host_id] = ExtensionSet::ExtensionPathAndDefaultLocale(
232 extension->path(), LocaleInfo::GetDefaultLocale(extension));
hanxic0503d72015-02-05 14:27:32233 }
234}
235
hanxic0503d72015-02-05 14:27:32236void ExtensionUserScriptLoader::OnExtensionUnloaded(
237 content::BrowserContext* browser_context,
238 const Extension* extension,
239 UnloadedExtensionInfo::Reason reason) {
hanxifeb6a64f2015-04-24 19:21:40240 hosts_info_.erase(HostID(HostID::EXTENSIONS, extension->id()));
hanxic0503d72015-02-05 14:27:32241}
242
243void ExtensionUserScriptLoader::OnExtensionSystemReady() {
244 SetReady(true);
245}
246
247} // namespace extensions