52321c9267ac9e6cdc3e366a47584026e7b58d66
[platform/framework/web/crosswalk.git] / src / extensions / browser / content_verifier.cc
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.
4
5 #include "extensions/browser/content_verifier.h"
6
7 #include <algorithm>
8
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"
19
20 namespace extensions {
21
22 ContentVerifier::ContentVerifier(content::BrowserContext* context,
23                                  ContentVerifierDelegate* delegate)
24     : shutdown_(false),
25       context_(context),
26       delegate_(delegate),
27       fetcher_(new ContentHashFetcher(
28           context,
29           delegate,
30           base::Bind(&ContentVerifier::OnFetchComplete, this))),
31       observer_(this),
32       io_data_(new ContentVerifierIOData) {
33 }
34
35 ContentVerifier::~ContentVerifier() {
36 }
37
38 void ContentVerifier::Start() {
39   ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
40   observer_.Add(registry);
41 }
42
43 void ContentVerifier::Shutdown() {
44   shutdown_ = true;
45   content::BrowserThread::PostTask(
46       content::BrowserThread::IO,
47       FROM_HERE,
48       base::Bind(&ContentVerifierIOData::Clear, io_data_));
49   observer_.RemoveAll();
50   fetcher_.reset();
51 }
52
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);
58
59   const ContentVerifierIOData::ExtensionData* data =
60       io_data_->GetData(extension_id);
61   if (!data)
62     return NULL;
63
64   std::set<base::FilePath> paths;
65   paths.insert(relative_path);
66   if (!ShouldVerifyAnyPaths(extension_id, extension_root, paths))
67     return NULL;
68
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,
73                             data->version,
74                             extension_root,
75                             relative_path,
76                             delegate_->PublicKey()),
77       base::Bind(&ContentVerifier::VerifyFailed, this, extension_id));
78 }
79
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,
85         FROM_HERE,
86         base::Bind(&ContentVerifier::VerifyFailed, this, extension_id, reason));
87     return;
88   }
89   if (shutdown_)
90     return;
91
92   VLOG(1) << "VerifyFailed " << extension_id << " reason:" << reason;
93
94   ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
95   const Extension* extension =
96       registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
97
98   if (!extension)
99     return;
100
101   if (reason == ContentVerifyJob::MISSING_ALL_HASHES) {
102     // If we failed because there were no hashes yet for this extension, just
103     // request some.
104     fetcher_->DoFetch(extension, true /* force */);
105   } else {
106     delegate_->VerifyFailed(extension_id);
107   }
108 }
109
110 void ContentVerifier::OnExtensionLoaded(
111     content::BrowserContext* browser_context,
112     const Extension* extension) {
113   if (shutdown_)
114     return;
115
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,
123                                      FROM_HERE,
124                                      base::Bind(&ContentVerifierIOData::AddData,
125                                                 io_data_,
126                                                 extension->id(),
127                                                 base::Passed(&data)));
128     fetcher_->ExtensionLoaded(extension);
129   }
130 }
131
132 void ContentVerifier::OnExtensionUnloaded(
133     content::BrowserContext* browser_context,
134     const Extension* extension,
135     UnloadedExtensionInfo::Reason reason) {
136   if (shutdown_)
137     return;
138   content::BrowserThread::PostTask(
139       content::BrowserThread::IO,
140       FROM_HERE,
141       base::Bind(
142           &ContentVerifierIOData::RemoveData, io_data_, extension->id()));
143   if (fetcher_)
144     fetcher_->ExtensionUnloaded(extension);
145 }
146
147 void ContentVerifier::OnFetchCompleteHelper(const std::string& extension_id,
148                                             bool shouldVerifyAnyPathsResult) {
149   if (shouldVerifyAnyPathsResult)
150     delegate_->VerifyFailed(extension_id);
151 }
152
153 void ContentVerifier::OnFetchComplete(
154     const std::string& extension_id,
155     bool success,
156     bool was_force_check,
157     const std::set<base::FilePath>& hash_mismatch_paths) {
158   if (shutdown_)
159     return;
160
161   VLOG(1) << "OnFetchComplete " << extension_id << " success:" << success;
162
163   ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
164   const Extension* extension =
165       registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
166   if (!delegate_ || !extension)
167     return;
168
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
173     // hashes.
174     delegate_->VerifyFailed(extension_id);
175   } else {
176     content::BrowserThread::PostTaskAndReplyWithResult(
177         content::BrowserThread::IO,
178         FROM_HERE,
179         base::Bind(&ContentVerifier::ShouldVerifyAnyPaths,
180                    this,
181                    extension_id,
182                    extension->path(),
183                    hash_mismatch_paths),
184         base::Bind(
185             &ContentVerifier::OnFetchCompleteHelper, this, extension_id));
186   }
187 }
188
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);
196   if (!data)
197     return false;
198
199   const std::set<base::FilePath>& browser_images = data->browser_image_paths;
200
201   base::FilePath locales_dir = extension_root.Append(kLocaleFolder);
202   scoped_ptr<std::set<std::string> > all_locales;
203
204   for (std::set<base::FilePath>::const_iterator i = relative_paths.begin();
205        i != relative_paths.end();
206        ++i) {
207     const base::FilePath& relative_path = *i;
208
209     if (relative_path == base::FilePath(kManifestFilename))
210       continue;
211
212     if (ContainsKey(browser_images, relative_path))
213       continue;
214
215     base::FilePath full_path = extension_root.Append(relative_path);
216     if (locales_dir.IsParent(full_path)) {
217       if (!all_locales) {
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());
224       }
225
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))
231         continue;
232     }
233     return true;
234   }
235   return false;
236 }
237
238 }  // namespace extensions