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