1702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul// Copyright 2014 The Chromium Authors. All rights reserved.
2702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul// Use of this source code is governed by a BSD-style license that can be
3702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul// found in the LICENSE file.
4702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
5702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "extensions/browser/content_verifier.h"
6702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
7702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include <algorithm>
8702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
9702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "base/files/file_path.h"
10702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "base/stl_util.h"
11702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "base/strings/string_util.h"
12702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "content/public/browser/browser_thread.h"
13702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "extensions/browser/content_hash_fetcher.h"
14702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "extensions/browser/content_hash_reader.h"
15702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "extensions/browser/content_verifier_delegate.h"
16702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "extensions/browser/content_verifier_io_data.h"
17702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "extensions/browser/extension_registry.h"
18702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "extensions/common/constants.h"
19702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul#include "extensions/common/extension_l10n_util.h"
20702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
21702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paulnamespace extensions {
22702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
23702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian PaulContentVerifier::ContentVerifier(content::BrowserContext* context,
24702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                                 ContentVerifierDelegate* delegate)
25702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    : shutdown_(false),
26702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      context_(context),
27702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      delegate_(delegate),
28702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      fetcher_(new ContentHashFetcher(
29702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul          context,
30702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul          delegate,
31702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul          base::Bind(&ContentVerifier::OnFetchComplete, this))),
32702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      observer_(this),
33702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      io_data_(new ContentVerifierIOData) {
34702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
35702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
36702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian PaulContentVerifier::~ContentVerifier() {
37702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
38702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
39702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paulvoid ContentVerifier::Start() {
40702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
41702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  observer_.Add(registry);
42702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
43702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
44702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paulvoid ContentVerifier::Shutdown() {
45702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  shutdown_ = true;
46702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  content::BrowserThread::PostTask(
47702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      content::BrowserThread::IO,
48702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      FROM_HERE,
49702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      base::Bind(&ContentVerifierIOData::Clear, io_data_));
50702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  observer_.RemoveAll();
51702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  fetcher_.reset();
52702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
53702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
54702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian PaulContentVerifyJob* ContentVerifier::CreateJobFor(
55702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const std::string& extension_id,
56702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const base::FilePath& extension_root,
57702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const base::FilePath& relative_path) {
58702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
59702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
60702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  const ContentVerifierIOData::ExtensionData* data =
61702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      io_data_->GetData(extension_id);
62702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (!data)
63702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return NULL;
64702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
65702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  std::set<base::FilePath> paths;
66702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  paths.insert(relative_path);
67702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (!ShouldVerifyAnyPaths(extension_id, extension_root, paths))
68702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return NULL;
69702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
70702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  // TODO(asargent) - we can probably get some good performance wins by having
71702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  // a cache of ContentHashReader's that we hold onto past the end of each job.
72702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  return new ContentVerifyJob(
73702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      new ContentHashReader(extension_id,
74702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                            data->version,
75702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                            extension_root,
76702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                            relative_path,
77702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                            delegate_->PublicKey()),
78702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      base::Bind(&ContentVerifier::VerifyFailed, this, extension_id));
79702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
80702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
81702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paulvoid ContentVerifier::VerifyFailed(const std::string& extension_id,
82702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                                   ContentVerifyJob::FailureReason reason) {
83702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
84702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    content::BrowserThread::PostTask(
85702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        content::BrowserThread::UI,
86702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        FROM_HERE,
87702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        base::Bind(&ContentVerifier::VerifyFailed, this, extension_id, reason));
88702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return;
89702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  }
90702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (shutdown_)
91702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return;
92702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
93702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  VLOG(1) << "VerifyFailed " << extension_id << " reason:" << reason;
94702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
95702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
96702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  const Extension* extension =
97702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
98702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
99702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (!extension)
100702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return;
101702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
102702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (reason == ContentVerifyJob::MISSING_ALL_HASHES) {
103702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    // If we failed because there were no hashes yet for this extension, just
104702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    // request some.
105702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    fetcher_->DoFetch(extension, true /* force */);
106702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  } else {
107702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    delegate_->VerifyFailed(extension_id, reason);
108702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  }
109702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
110702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
111702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paulstatic base::FilePath MakeImagePathRelative(const base::FilePath& path) {
112702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (path.ReferencesParent())
113702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return base::FilePath();
114702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
115702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  std::vector<base::FilePath::StringType> parts;
116702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  path.GetComponents(&parts);
117702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (parts.empty())
118702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return base::FilePath();
119702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
120702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  // Remove the first component if it is '.' or '/' or '//'.
121702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  const base::FilePath::StringType separators(
122702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      base::FilePath::kSeparators, base::FilePath::kSeparatorsLength);
123702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (!parts[0].empty() &&
124702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      (parts[0] == base::FilePath::kCurrentDirectory ||
125702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul       parts[0].find_first_not_of(separators) == std::string::npos))
126702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    parts.erase(parts.begin());
127702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
128702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  // Note that elsewhere we always normalize path separators to '/' so this
129702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  // should work for all platforms.
130702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  return base::FilePath(JoinString(parts, '/'));
131702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
132702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
133702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paulvoid ContentVerifier::OnExtensionLoaded(
134702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    content::BrowserContext* browser_context,
135702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const Extension* extension) {
136702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (shutdown_)
137702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return;
138702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
139702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  ContentVerifierDelegate::Mode mode = delegate_->ShouldBeVerified(*extension);
140702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (mode != ContentVerifierDelegate::NONE) {
141702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    // The browser image paths from the extension may not be relative (eg
142702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    // they might have leading '/' or './'), so we strip those to make
143702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    // comparing to actual relative paths work later on.
144702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    std::set<base::FilePath> original_image_paths =
145702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        delegate_->GetBrowserImagePaths(extension);
146702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
147702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    scoped_ptr<std::set<base::FilePath>> image_paths(
148702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        new std::set<base::FilePath>);
149702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    for (const auto& path : original_image_paths) {
150702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      image_paths->insert(MakeImagePathRelative(path));
151702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    }
152702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
153702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    scoped_ptr<ContentVerifierIOData::ExtensionData> data(
154702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        new ContentVerifierIOData::ExtensionData(
155702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul            image_paths.Pass(),
156702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul            extension->version() ? *extension->version() : base::Version()));
157702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    content::BrowserThread::PostTask(content::BrowserThread::IO,
158702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                                     FROM_HERE,
159702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                                     base::Bind(&ContentVerifierIOData::AddData,
160702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                                                io_data_,
161702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                                                extension->id(),
162702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                                                base::Passed(&data)));
163702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    fetcher_->ExtensionLoaded(extension);
164702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  }
165702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
166702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
167702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paulvoid ContentVerifier::OnExtensionUnloaded(
168702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    content::BrowserContext* browser_context,
169702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const Extension* extension,
170702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    UnloadedExtensionInfo::Reason reason) {
171702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (shutdown_)
172702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return;
173702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  content::BrowserThread::PostTask(
174702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      content::BrowserThread::IO,
175702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      FROM_HERE,
176702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      base::Bind(
177702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul          &ContentVerifierIOData::RemoveData, io_data_, extension->id()));
178702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (fetcher_)
179702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    fetcher_->ExtensionUnloaded(extension);
180702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
181702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
182702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paulvoid ContentVerifier::OnFetchCompleteHelper(const std::string& extension_id,
183702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                                            bool shouldVerifyAnyPathsResult) {
184702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (shouldVerifyAnyPathsResult)
185702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    delegate_->VerifyFailed(extension_id, ContentVerifyJob::MISSING_ALL_HASHES);
186702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
187702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
188702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paulvoid ContentVerifier::OnFetchComplete(
189702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const std::string& extension_id,
190702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    bool success,
191702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    bool was_force_check,
192702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const std::set<base::FilePath>& hash_mismatch_paths) {
193702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (shutdown_)
194702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return;
195702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
196702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  VLOG(1) << "OnFetchComplete " << extension_id << " success:" << success;
197702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
198702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  ExtensionRegistry* registry = ExtensionRegistry::Get(context_);
199702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  const Extension* extension =
200702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      registry->GetExtensionById(extension_id, ExtensionRegistry::EVERYTHING);
201702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (!delegate_ || !extension)
202702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return;
203702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
204702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  ContentVerifierDelegate::Mode mode = delegate_->ShouldBeVerified(*extension);
205702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (was_force_check && !success &&
206702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      mode == ContentVerifierDelegate::ENFORCE_STRICT) {
207702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    // We weren't able to get verified_contents.json or weren't able to compute
208702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    // hashes.
209702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    delegate_->VerifyFailed(extension_id, ContentVerifyJob::MISSING_ALL_HASHES);
210702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  } else {
211702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    content::BrowserThread::PostTaskAndReplyWithResult(
212702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        content::BrowserThread::IO,
213702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        FROM_HERE,
214702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        base::Bind(&ContentVerifier::ShouldVerifyAnyPaths,
215702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                   this,
216702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                   extension_id,
217702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                   extension->path(),
218702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul                   hash_mismatch_paths),
219702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        base::Bind(
220702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul            &ContentVerifier::OnFetchCompleteHelper, this, extension_id));
221702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  }
222702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
223702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
224702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paulbool ContentVerifier::ShouldVerifyAnyPaths(
225702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const std::string& extension_id,
226702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const base::FilePath& extension_root,
227702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const std::set<base::FilePath>& relative_paths) {
228702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
229702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  const ContentVerifierIOData::ExtensionData* data =
230702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      io_data_->GetData(extension_id);
231702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  if (!data)
232702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return false;
233702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
234702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  const std::set<base::FilePath>& browser_images = *(data->browser_image_paths);
235702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
236702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  base::FilePath locales_dir = extension_root.Append(kLocaleFolder);
237702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  scoped_ptr<std::set<std::string> > all_locales;
238702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
239702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  for (std::set<base::FilePath>::const_iterator i = relative_paths.begin();
240702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul       i != relative_paths.end();
241702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul       ++i) {
242702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    const base::FilePath& relative_path = *i;
243702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
244702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    if (relative_path == base::FilePath(kManifestFilename))
245702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      continue;
246702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
247702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    if (ContainsKey(browser_images, relative_path))
248702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      continue;
249702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
250702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    base::FilePath full_path = extension_root.Append(relative_path);
251702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    if (locales_dir.IsParent(full_path)) {
252702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      if (!all_locales) {
253702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        // TODO(asargent) - see if we can cache this list longer to avoid
254702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        // having to fetch it more than once for a given run of the
255702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        // browser. Maybe it can never change at runtime? (Or if it can, maybe
256702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        // there is an event we can listen for to know to drop our cache).
257702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        all_locales.reset(new std::set<std::string>);
258702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        extension_l10n_util::GetAllLocales(all_locales.get());
259702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      }
260702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
261702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      // Since message catalogs get transcoded during installation, we want
262702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      // to skip those paths.
263702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul      if (full_path.DirName().DirName() == locales_dir &&
264702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul          !extension_l10n_util::ShouldSkipValidation(
265702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul              locales_dir, full_path.DirName(), *all_locales))
266702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul        continue;
267702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    }
268702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul    return true;
269702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  }
270702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul  return false;
271702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}
272702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul
273702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul}  // namespace extensions
274702b5b076b7591560e7e701e0c9ff2eeb30fa966Brian Paul