Finish refactoring of DomCodeToUsLayoutKeyboardCode().
[chromium-blink-merge.git] / extensions / browser / content_verifier.cc
blobb85e8eeebcac6f77b6bac1d0b7f300b2568cfd45
1 // Copyright 2014 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/content_verifier.h"
7 #include <algorithm>
9 #include "base/files/file_path.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "extensions/browser/content_hash_fetcher.h"
14 #include "extensions/browser/content_hash_reader.h"
15 #include "extensions/browser/content_verifier_delegate.h"
16 #include "extensions/browser/content_verifier_io_data.h"
17 #include "extensions/browser/extension_registry.h"
18 #include "extensions/common/constants.h"
19 #include "extensions/common/extension_l10n_util.h"
21 namespace extensions {
23 ContentVerifier::ContentVerifier(content::BrowserContext* context,
24 ContentVerifierDelegate* delegate)
25 : shutdown_(false),
26 context_(context),
27 delegate_(delegate),
28 fetcher_(new ContentHashFetcher(
29 context,
30 delegate,
31 base::Bind(&ContentVerifier::OnFetchComplete, this))),
32 observer_(this),
33 io_data_(new ContentVerifierIOData) {
36 ContentVerifier::~ContentVerifier() {
39 void ContentVerifier::Start() {
40 ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
41 observer_.Add(registry);
44 void ContentVerifier::Shutdown() {
45 shutdown_ = true;
46 content::BrowserThread::PostTask(
47 content::BrowserThread::IO,
48 FROM_HERE,
49 base::Bind(&ContentVerifierIOData::Clear, io_data_));
50 observer_.RemoveAll();
51 fetcher_.reset();
54 ContentVerifyJob* ContentVerifier::CreateJobFor(
55 const std::string& extension_id,
56 const base::FilePath& extension_root,
57 const base::FilePath& relative_path) {
58 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
60 const ContentVerifierIOData::ExtensionData* data =
61 io_data_->GetData(extension_id);
62 if (!data)
63 return NULL;
65 std::set<base::FilePath> paths;
66 paths.insert(relative_path);
67 if (!ShouldVerifyAnyPaths(extension_id, extension_root, paths))
68 return NULL;
70 // TODO(asargent) - we can probably get some good performance wins by having
71 // a cache of ContentHashReader's that we hold onto past the end of each job.
72 return new ContentVerifyJob(
73 new ContentHashReader(extension_id, data->version, extension_root,
74 relative_path, delegate_->GetPublicKey()),
75 base::Bind(&ContentVerifier::VerifyFailed, this, extension_id));
78 void ContentVerifier::VerifyFailed(const std::string& extension_id,
79 ContentVerifyJob::FailureReason reason) {
80 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
81 content::BrowserThread::PostTask(
82 content::BrowserThread::UI,
83 FROM_HERE,
84 base::Bind(&ContentVerifier::VerifyFailed, this, extension_id, reason));
85 return;
87 if (shutdown_)
88 return;
90 VLOG(1) << "VerifyFailed " << extension_id << " reason:" << reason;
92 ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
93 const Extension* extension =
94 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
96 if (!extension)
97 return;
99 if (reason == ContentVerifyJob::MISSING_ALL_HASHES) {
100 // If we failed because there were no hashes yet for this extension, just
101 // request some.
102 fetcher_->DoFetch(extension, true /* force */);
103 } else {
104 delegate_->VerifyFailed(extension_id, reason);
108 static base::FilePath MakeImagePathRelative(const base::FilePath& path) {
109 if (path.ReferencesParent())
110 return base::FilePath();
112 std::vector<base::FilePath::StringType> parts;
113 path.GetComponents(&parts);
114 if (parts.empty())
115 return base::FilePath();
117 // Remove the first component if it is '.' or '/' or '//'.
118 const base::FilePath::StringType separators(
119 base::FilePath::kSeparators, base::FilePath::kSeparatorsLength);
120 if (!parts[0].empty() &&
121 (parts[0] == base::FilePath::kCurrentDirectory ||
122 parts[0].find_first_not_of(separators) == std::string::npos))
123 parts.erase(parts.begin());
125 // Note that elsewhere we always normalize path separators to '/' so this
126 // should work for all platforms.
127 return base::FilePath(
128 base::JoinString(parts, base::FilePath::StringType(1, '/')));
131 void ContentVerifier::OnExtensionLoaded(
132 content::BrowserContext* browser_context,
133 const Extension* extension) {
134 if (shutdown_)
135 return;
137 ContentVerifierDelegate::Mode mode = delegate_->ShouldBeVerified(*extension);
138 if (mode != ContentVerifierDelegate::NONE) {
139 // The browser image paths from the extension may not be relative (eg
140 // they might have leading '/' or './'), so we strip those to make
141 // comparing to actual relative paths work later on.
142 std::set<base::FilePath> original_image_paths =
143 delegate_->GetBrowserImagePaths(extension);
145 scoped_ptr<std::set<base::FilePath>> image_paths(
146 new std::set<base::FilePath>);
147 for (const auto& path : original_image_paths) {
148 image_paths->insert(MakeImagePathRelative(path));
151 scoped_ptr<ContentVerifierIOData::ExtensionData> data(
152 new ContentVerifierIOData::ExtensionData(
153 image_paths.Pass(),
154 extension->version() ? *extension->version() : base::Version()));
155 content::BrowserThread::PostTask(content::BrowserThread::IO,
156 FROM_HERE,
157 base::Bind(&ContentVerifierIOData::AddData,
158 io_data_,
159 extension->id(),
160 base::Passed(&data)));
161 fetcher_->ExtensionLoaded(extension);
165 void ContentVerifier::OnExtensionUnloaded(
166 content::BrowserContext* browser_context,
167 const Extension* extension,
168 UnloadedExtensionInfo::Reason reason) {
169 if (shutdown_)
170 return;
171 content::BrowserThread::PostTask(
172 content::BrowserThread::IO,
173 FROM_HERE,
174 base::Bind(
175 &ContentVerifierIOData::RemoveData, io_data_, extension->id()));
176 if (fetcher_)
177 fetcher_->ExtensionUnloaded(extension);
180 void ContentVerifier::OnFetchCompleteHelper(const std::string& extension_id,
181 bool shouldVerifyAnyPathsResult) {
182 if (shouldVerifyAnyPathsResult)
183 delegate_->VerifyFailed(extension_id, ContentVerifyJob::MISSING_ALL_HASHES);
186 void ContentVerifier::OnFetchComplete(
187 const std::string& extension_id,
188 bool success,
189 bool was_force_check,
190 const std::set<base::FilePath>& hash_mismatch_paths) {
191 if (shutdown_)
192 return;
194 VLOG(1) << "OnFetchComplete " << extension_id << " success:" << success;
196 ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
197 const Extension* extension =
198 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
199 if (!delegate_ || !extension)
200 return;
202 ContentVerifierDelegate::Mode mode = delegate_->ShouldBeVerified(*extension);
203 if (was_force_check && !success &&
204 mode == ContentVerifierDelegate::ENFORCE_STRICT) {
205 // We weren't able to get verified_contents.json or weren't able to compute
206 // hashes.
207 delegate_->VerifyFailed(extension_id, ContentVerifyJob::MISSING_ALL_HASHES);
208 } else {
209 content::BrowserThread::PostTaskAndReplyWithResult(
210 content::BrowserThread::IO,
211 FROM_HERE,
212 base::Bind(&ContentVerifier::ShouldVerifyAnyPaths,
213 this,
214 extension_id,
215 extension->path(),
216 hash_mismatch_paths),
217 base::Bind(
218 &ContentVerifier::OnFetchCompleteHelper, this, extension_id));
222 bool ContentVerifier::ShouldVerifyAnyPaths(
223 const std::string& extension_id,
224 const base::FilePath& extension_root,
225 const std::set<base::FilePath>& relative_paths) {
226 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
227 const ContentVerifierIOData::ExtensionData* data =
228 io_data_->GetData(extension_id);
229 if (!data)
230 return false;
232 const std::set<base::FilePath>& browser_images = *(data->browser_image_paths);
234 base::FilePath locales_dir = extension_root.Append(kLocaleFolder);
235 scoped_ptr<std::set<std::string> > all_locales;
237 for (std::set<base::FilePath>::const_iterator i = relative_paths.begin();
238 i != relative_paths.end();
239 ++i) {
240 const base::FilePath& relative_path = *i;
242 if (relative_path == base::FilePath(kManifestFilename))
243 continue;
245 if (ContainsKey(browser_images, relative_path))
246 continue;
248 base::FilePath full_path = extension_root.Append(relative_path);
249 if (locales_dir.IsParent(full_path)) {
250 if (!all_locales) {
251 // TODO(asargent) - see if we can cache this list longer to avoid
252 // having to fetch it more than once for a given run of the
253 // browser. Maybe it can never change at runtime? (Or if it can, maybe
254 // there is an event we can listen for to know to drop our cache).
255 all_locales.reset(new std::set<std::string>);
256 extension_l10n_util::GetAllLocales(all_locales.get());
259 // Since message catalogs get transcoded during installation, we want
260 // to skip those paths.
261 if (full_path.DirName().DirName() == locales_dir &&
262 !extension_l10n_util::ShouldSkipValidation(
263 locales_dir, full_path.DirName(), *all_locales))
264 continue;
266 return true;
268 return false;
271 } // namespace extensions