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"
9 #include "base/files/file_path.h"
10 #include "base/stl_util.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "extensions/browser/content_hash_fetcher.h"
13 #include "extensions/browser/content_hash_reader.h"
14 #include "extensions/browser/content_verifier_delegate.h"
15 #include "extensions/browser/content_verifier_io_data.h"
16 #include "extensions/browser/extension_registry.h"
17 #include "extensions/common/constants.h"
18 #include "extensions/common/extension_l10n_util.h"
20 namespace extensions {
22 ContentVerifier::ContentVerifier(content::BrowserContext* context,
23 ContentVerifierDelegate* delegate)
27 fetcher_(new ContentHashFetcher(
30 base::Bind(&ContentVerifier::OnFetchComplete, this))),
32 io_data_(new ContentVerifierIOData) {
35 ContentVerifier::~ContentVerifier() {
38 void ContentVerifier::Start() {
39 ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
40 observer_.Add(registry);
43 void ContentVerifier::Shutdown() {
45 content::BrowserThread::PostTask(
46 content::BrowserThread::IO,
48 base::Bind(&ContentVerifierIOData::Clear, io_data_));
49 observer_.RemoveAll();
53 ContentVerifyJob* ContentVerifier::CreateJobFor(
54 const std::string& extension_id,
55 const base::FilePath& extension_root,
56 const base::FilePath& relative_path) {
57 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
59 const ContentVerifierIOData::ExtensionData* data =
60 io_data_->GetData(extension_id);
64 std::set<base::FilePath> paths;
65 paths.insert(relative_path);
66 if (!ShouldVerifyAnyPaths(extension_id, extension_root, paths))
69 // TODO(asargent) - we can probably get some good performance wins by having
70 // a cache of ContentHashReader's that we hold onto past the end of each job.
71 return new ContentVerifyJob(
72 new ContentHashReader(extension_id,
76 delegate_->PublicKey()),
77 base::Bind(&ContentVerifier::VerifyFailed, this, extension_id));
80 void ContentVerifier::VerifyFailed(const std::string& extension_id,
81 ContentVerifyJob::FailureReason reason) {
82 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
83 content::BrowserThread::PostTask(
84 content::BrowserThread::UI,
86 base::Bind(&ContentVerifier::VerifyFailed, this, extension_id, reason));
92 VLOG(1) << "VerifyFailed " << extension_id << " reason:" << reason;
94 ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
95 const Extension* extension =
96 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
101 if (reason == ContentVerifyJob::MISSING_ALL_HASHES) {
102 // If we failed because there were no hashes yet for this extension, just
104 fetcher_->DoFetch(extension, true /* force */);
106 delegate_->VerifyFailed(extension_id);
110 void ContentVerifier::OnExtensionLoaded(
111 content::BrowserContext* browser_context,
112 const Extension* extension) {
116 ContentVerifierDelegate::Mode mode = delegate_->ShouldBeVerified(*extension);
117 if (mode != ContentVerifierDelegate::NONE) {
118 scoped_ptr<ContentVerifierIOData::ExtensionData> data(
119 new ContentVerifierIOData::ExtensionData(
120 delegate_->GetBrowserImagePaths(extension),
121 extension->version() ? *extension->version() : base::Version()));
122 content::BrowserThread::PostTask(content::BrowserThread::IO,
124 base::Bind(&ContentVerifierIOData::AddData,
127 base::Passed(&data)));
128 fetcher_->ExtensionLoaded(extension);
132 void ContentVerifier::OnExtensionUnloaded(
133 content::BrowserContext* browser_context,
134 const Extension* extension,
135 UnloadedExtensionInfo::Reason reason) {
138 content::BrowserThread::PostTask(
139 content::BrowserThread::IO,
142 &ContentVerifierIOData::RemoveData, io_data_, extension->id()));
144 fetcher_->ExtensionUnloaded(extension);
147 void ContentVerifier::OnFetchCompleteHelper(const std::string& extension_id,
148 bool shouldVerifyAnyPathsResult) {
149 if (shouldVerifyAnyPathsResult)
150 delegate_->VerifyFailed(extension_id);
153 void ContentVerifier::OnFetchComplete(
154 const std::string& extension_id,
156 bool was_force_check,
157 const std::set<base::FilePath>& hash_mismatch_paths) {
161 VLOG(1) << "OnFetchComplete " << extension_id << " success:" << success;
163 ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
164 const Extension* extension =
165 registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
166 if (!delegate_ || !extension)
169 ContentVerifierDelegate::Mode mode = delegate_->ShouldBeVerified(*extension);
170 if (was_force_check && !success &&
171 mode == ContentVerifierDelegate::ENFORCE_STRICT) {
172 // We weren't able to get verified_contents.json or weren't able to compute
174 delegate_->VerifyFailed(extension_id);
176 content::BrowserThread::PostTaskAndReplyWithResult(
177 content::BrowserThread::IO,
179 base::Bind(&ContentVerifier::ShouldVerifyAnyPaths,
183 hash_mismatch_paths),
185 &ContentVerifier::OnFetchCompleteHelper, this, extension_id));
189 bool ContentVerifier::ShouldVerifyAnyPaths(
190 const std::string& extension_id,
191 const base::FilePath& extension_root,
192 const std::set<base::FilePath>& relative_paths) {
193 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
194 const ContentVerifierIOData::ExtensionData* data =
195 io_data_->GetData(extension_id);
199 const std::set<base::FilePath>& browser_images = data->browser_image_paths;
201 base::FilePath locales_dir = extension_root.Append(kLocaleFolder);
202 scoped_ptr<std::set<std::string> > all_locales;
204 for (std::set<base::FilePath>::const_iterator i = relative_paths.begin();
205 i != relative_paths.end();
207 const base::FilePath& relative_path = *i;
209 if (relative_path == base::FilePath(kManifestFilename))
212 if (ContainsKey(browser_images, relative_path))
215 base::FilePath full_path = extension_root.Append(relative_path);
216 if (locales_dir.IsParent(full_path)) {
218 // TODO(asargent) - see if we can cache this list longer to avoid
219 // having to fetch it more than once for a given run of the
220 // browser. Maybe it can never change at runtime? (Or if it can, maybe
221 // there is an event we can listen for to know to drop our cache).
222 all_locales.reset(new std::set<std::string>);
223 extension_l10n_util::GetAllLocales(all_locales.get());
226 // Since message catalogs get transcoded during installation, we want
227 // to skip those paths.
228 if (full_path.DirName().DirName() == locales_dir &&
229 !extension_l10n_util::ShouldSkipValidation(
230 locales_dir, full_path.DirName(), *all_locales))
238 } // namespace extensions