Roll src/third_party/WebKit 6cdd902:94953d6 (svn 197985:197991)
[chromium-blink-merge.git] / extensions / browser / content_verifier.cc
blob3bbe4a9a6bdc2d67c697e17625b7fb29d87fe500
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(JoinString(parts, '/'));
130 void ContentVerifier::OnExtensionLoaded(
131 content::BrowserContext* browser_context,
132 const Extension* extension) {
133 if (shutdown_)
134 return;
136 ContentVerifierDelegate::Mode mode = delegate_->ShouldBeVerified(*extension);
137 if (mode != ContentVerifierDelegate::NONE) {
138 // The browser image paths from the extension may not be relative (eg
139 // they might have leading '/' or './'), so we strip those to make
140 // comparing to actual relative paths work later on.
141 std::set<base::FilePath> original_image_paths =
142 delegate_->GetBrowserImagePaths(extension);
144 scoped_ptr<std::set<base::FilePath>> image_paths(
145 new std::set<base::FilePath>);
146 for (const auto& path : original_image_paths) {
147 image_paths->insert(MakeImagePathRelative(path));
150 scoped_ptr<ContentVerifierIOData::ExtensionData> data(
151 new ContentVerifierIOData::ExtensionData(
152 image_paths.Pass(),
153 extension->version() ? *extension->version() : base::Version()));
154 content::BrowserThread::PostTask(content::BrowserThread::IO,
155 FROM_HERE,
156 base::Bind(&ContentVerifierIOData::AddData,
157 io_data_,
158 extension->id(),
159 base::Passed(&data)));
160 fetcher_->ExtensionLoaded(extension);
164 void ContentVerifier::OnExtensionUnloaded(
165 content::BrowserContext* browser_context,
166 const Extension* extension,
167 UnloadedExtensionInfo::Reason reason) {
168 if (shutdown_)
169 return;
170 content::BrowserThread::PostTask(
171 content::BrowserThread::IO,
172 FROM_HERE,
173 base::Bind(
174 &ContentVerifierIOData::RemoveData, io_data_, extension->id()));
175 if (fetcher_)
176 fetcher_->ExtensionUnloaded(extension);
179 void ContentVerifier::OnFetchCompleteHelper(const std::string& extension_id,
180 bool shouldVerifyAnyPathsResult) {
181 if (shouldVerifyAnyPathsResult)
182 delegate_->VerifyFailed(extension_id, ContentVerifyJob::MISSING_ALL_HASHES);
185 void ContentVerifier::OnFetchComplete(
186 const std::string& extension_id,
187 bool success,
188 bool was_force_check,
189 const std::set<base::FilePath>& hash_mismatch_paths) {
190 if (shutdown_)
191 return;
193 VLOG(1) << "OnFetchComplete " << extension_id << " success:" << success;
195 ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
196 const Extension* extension =
197 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
198 if (!delegate_ || !extension)
199 return;
201 ContentVerifierDelegate::Mode mode = delegate_->ShouldBeVerified(*extension);
202 if (was_force_check && !success &&
203 mode == ContentVerifierDelegate::ENFORCE_STRICT) {
204 // We weren't able to get verified_contents.json or weren't able to compute
205 // hashes.
206 delegate_->VerifyFailed(extension_id, ContentVerifyJob::MISSING_ALL_HASHES);
207 } else {
208 content::BrowserThread::PostTaskAndReplyWithResult(
209 content::BrowserThread::IO,
210 FROM_HERE,
211 base::Bind(&ContentVerifier::ShouldVerifyAnyPaths,
212 this,
213 extension_id,
214 extension->path(),
215 hash_mismatch_paths),
216 base::Bind(
217 &ContentVerifier::OnFetchCompleteHelper, this, extension_id));
221 bool ContentVerifier::ShouldVerifyAnyPaths(
222 const std::string& extension_id,
223 const base::FilePath& extension_root,
224 const std::set<base::FilePath>& relative_paths) {
225 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
226 const ContentVerifierIOData::ExtensionData* data =
227 io_data_->GetData(extension_id);
228 if (!data)
229 return false;
231 const std::set<base::FilePath>& browser_images = *(data->browser_image_paths);
233 base::FilePath locales_dir = extension_root.Append(kLocaleFolder);
234 scoped_ptr<std::set<std::string> > all_locales;
236 for (std::set<base::FilePath>::const_iterator i = relative_paths.begin();
237 i != relative_paths.end();
238 ++i) {
239 const base::FilePath& relative_path = *i;
241 if (relative_path == base::FilePath(kManifestFilename))
242 continue;
244 if (ContainsKey(browser_images, relative_path))
245 continue;
247 base::FilePath full_path = extension_root.Append(relative_path);
248 if (locales_dir.IsParent(full_path)) {
249 if (!all_locales) {
250 // TODO(asargent) - see if we can cache this list longer to avoid
251 // having to fetch it more than once for a given run of the
252 // browser. Maybe it can never change at runtime? (Or if it can, maybe
253 // there is an event we can listen for to know to drop our cache).
254 all_locales.reset(new std::set<std::string>);
255 extension_l10n_util::GetAllLocales(all_locales.get());
258 // Since message catalogs get transcoded during installation, we want
259 // to skip those paths.
260 if (full_path.DirName().DirName() == locales_dir &&
261 !extension_l10n_util::ShouldSkipValidation(
262 locales_dir, full_path.DirName(), *all_locales))
263 continue;
265 return true;
267 return false;
270 } // namespace extensions