1 // 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.
5 #include "extensions/browser/extension_user_script_loader.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/version.h"
15 #include "content/public/browser/browser_context.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/browser/notification_service.h"
18 #include "content/public/browser/render_process_host.h"
19 #include "extensions/browser/component_extension_resource_manager.h"
20 #include "extensions/browser/content_verifier.h"
21 #include "extensions/browser/extension_registry.h"
22 #include "extensions/browser/extension_system.h"
23 #include "extensions/browser/extensions_browser_client.h"
24 #include "extensions/common/file_util.h"
25 #include "extensions/common/manifest_handlers/default_locale_handler.h"
26 #include "extensions/common/message_bundle.h"
27 #include "extensions/common/one_shot_event.h"
28 #include "ui/base/resource/resource_bundle.h"
30 using content::BrowserContext
;
32 namespace extensions
{
36 using SubstitutionMap
= std::map
<std::string
, std::string
>;
38 // Verifies file contents as they are read.
39 void VerifyContent(const scoped_refptr
<ContentVerifier
>& verifier
,
40 const std::string
& extension_id
,
41 const base::FilePath
& extension_root
,
42 const base::FilePath
& relative_path
,
43 const std::string
& content
) {
44 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
45 scoped_refptr
<ContentVerifyJob
> job(
46 verifier
->CreateJobFor(extension_id
, extension_root
, relative_path
));
49 job
->BytesRead(content
.size(), content
.data());
54 // Loads user scripts from the extension who owns these scripts.
55 bool LoadScriptContent(const HostID
& host_id
,
56 UserScript::File
* script_file
,
57 const SubstitutionMap
* localization_messages
,
58 const scoped_refptr
<ContentVerifier
>& verifier
) {
61 const base::FilePath
& path
= ExtensionResource::GetFilePath(
62 script_file
->extension_root(), script_file
->relative_path(),
63 ExtensionResource::SYMLINKS_MUST_RESOLVE_WITHIN_ROOT
);
66 if (ExtensionsBrowserClient::Get()
67 ->GetComponentExtensionResourceManager()
68 ->IsComponentExtensionResource(script_file
->extension_root(),
69 script_file
->relative_path(),
71 const ResourceBundle
& rb
= ResourceBundle::GetSharedInstance();
72 content
= rb
.GetRawDataResource(resource_id
).as_string();
74 LOG(WARNING
) << "Failed to get file path to "
75 << script_file
->relative_path().value() << " from "
76 << script_file
->extension_root().value();
80 if (!base::ReadFileToString(path
, &content
)) {
81 LOG(WARNING
) << "Failed to load user script file: " << path
.value();
85 content::BrowserThread::PostTask(
86 content::BrowserThread::IO
, FROM_HERE
,
87 base::Bind(&VerifyContent
, verifier
, host_id
.id(),
88 script_file
->extension_root(),
89 script_file
->relative_path(), content
));
93 // Localize the content.
94 if (localization_messages
) {
96 MessageBundle::ReplaceMessagesWithExternalDictionary(*localization_messages
,
99 LOG(WARNING
) << "Failed to replace messages in script: " << error
;
102 // Remove BOM from the content.
103 std::string::size_type index
= content
.find(base::kUtf8ByteOrderMark
);
105 script_file
->set_content(content
.substr(strlen(base::kUtf8ByteOrderMark
)));
107 script_file
->set_content(content
);
112 SubstitutionMap
* GetLocalizationMessages(
113 const ExtensionUserScriptLoader::HostsInfo
& hosts_info
,
114 const HostID
& host_id
) {
115 ExtensionUserScriptLoader::HostsInfo::const_iterator iter
=
116 hosts_info
.find(host_id
);
117 if (iter
== hosts_info
.end())
119 return file_util::LoadMessageBundleSubstitutionMap(
120 iter
->second
.first
, host_id
.id(), iter
->second
.second
);
123 void LoadUserScripts(UserScriptList
* user_scripts
,
124 const ExtensionUserScriptLoader::HostsInfo
& hosts_info
,
125 const std::set
<int>& added_script_ids
,
126 const scoped_refptr
<ContentVerifier
>& verifier
) {
127 for (UserScript
& script
: *user_scripts
) {
128 if (added_script_ids
.count(script
.id()) == 0)
130 scoped_ptr
<SubstitutionMap
> localization_messages(
131 GetLocalizationMessages(hosts_info
, script
.host_id()));
132 for (UserScript::File
& script_file
: script
.js_scripts()) {
133 if (script_file
.GetContent().empty())
134 LoadScriptContent(script
.host_id(), &script_file
, nullptr, verifier
);
136 for (UserScript::File
& script_file
: script
.css_scripts()) {
137 if (script_file
.GetContent().empty())
138 LoadScriptContent(script
.host_id(), &script_file
,
139 localization_messages
.get(), verifier
);
144 void LoadScriptsOnFileThread(
145 scoped_ptr
<UserScriptList
> user_scripts
,
146 const ExtensionUserScriptLoader::HostsInfo
& hosts_info
,
147 const std::set
<int>& added_script_ids
,
148 const scoped_refptr
<ContentVerifier
>& verifier
,
149 UserScriptLoader::LoadScriptsCallback callback
) {
150 DCHECK(user_scripts
.get());
151 LoadUserScripts(user_scripts
.get(), hosts_info
, added_script_ids
, verifier
);
152 scoped_ptr
<base::SharedMemory
> memory
=
153 UserScriptLoader::Serialize(*user_scripts
);
154 content::BrowserThread::PostTask(
155 content::BrowserThread::UI
, FROM_HERE
,
156 base::Bind(callback
, base::Passed(&user_scripts
), base::Passed(&memory
)));
161 ExtensionUserScriptLoader::ExtensionUserScriptLoader(
162 BrowserContext
* browser_context
,
163 const HostID
& host_id
,
164 bool listen_for_extension_system_loaded
)
165 : UserScriptLoader(browser_context
, host_id
),
167 ExtensionSystem::Get(browser_context
)->content_verifier()),
168 extension_registry_observer_(this),
169 weak_factory_(this) {
170 extension_registry_observer_
.Add(ExtensionRegistry::Get(browser_context
));
171 if (listen_for_extension_system_loaded
) {
172 ExtensionSystem::Get(browser_context
)
175 base::Bind(&ExtensionUserScriptLoader::OnExtensionSystemReady
,
176 weak_factory_
.GetWeakPtr()));
182 ExtensionUserScriptLoader::~ExtensionUserScriptLoader() {
185 void ExtensionUserScriptLoader::LoadScriptsForTest(
186 UserScriptList
* user_scripts
) {
188 std::set
<int> added_script_ids
;
189 for (UserScript
& script
: *user_scripts
)
190 added_script_ids
.insert(script
.id());
192 LoadUserScripts(user_scripts
, info
, added_script_ids
,
193 nullptr /* no verifier for testing */);
196 void ExtensionUserScriptLoader::LoadScripts(
197 scoped_ptr
<UserScriptList
> user_scripts
,
198 const std::set
<HostID
>& changed_hosts
,
199 const std::set
<int>& added_script_ids
,
200 LoadScriptsCallback callback
) {
201 UpdateHostsInfo(changed_hosts
);
203 content::BrowserThread::PostTask(
204 content::BrowserThread::FILE, FROM_HERE
,
205 base::Bind(&LoadScriptsOnFileThread
, base::Passed(&user_scripts
),
206 hosts_info_
, added_script_ids
, content_verifier_
, callback
));
209 void ExtensionUserScriptLoader::UpdateHostsInfo(
210 const std::set
<HostID
>& changed_hosts
) {
211 ExtensionRegistry
* registry
= ExtensionRegistry::Get(browser_context());
212 for (const HostID
& host_id
: changed_hosts
) {
213 const Extension
* extension
=
214 registry
->GetExtensionById(host_id
.id(), ExtensionRegistry::ENABLED
);
215 // |changed_hosts_| may include hosts that have been removed,
216 // which leads to the above lookup failing. In this case, just continue.
219 if (hosts_info_
.find(host_id
) != hosts_info_
.end())
221 hosts_info_
[host_id
] = ExtensionSet::ExtensionPathAndDefaultLocale(
222 extension
->path(), LocaleInfo::GetDefaultLocale(extension
));
226 void ExtensionUserScriptLoader::OnExtensionUnloaded(
227 content::BrowserContext
* browser_context
,
228 const Extension
* extension
,
229 UnloadedExtensionInfo::Reason reason
) {
230 hosts_info_
.erase(HostID(HostID::EXTENSIONS
, extension
->id()));
233 void ExtensionUserScriptLoader::OnExtensionSystemReady() {
237 } // namespace extensions