Add ICU message format support
[chromium-blink-merge.git] / extensions / browser / content_verify_job.cc
blobefd763eaf7294e48e7b7fd59267a2405d5ae7137
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_verify_job.h"
7 #include <algorithm>
9 #include "base/metrics/histogram.h"
10 #include "base/stl_util.h"
11 #include "base/task_runner_util.h"
12 #include "base/timer/elapsed_timer.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "crypto/secure_hash.h"
15 #include "crypto/sha2.h"
16 #include "extensions/browser/content_hash_reader.h"
18 namespace extensions {
20 namespace {
22 ContentVerifyJob::TestDelegate* g_test_delegate = NULL;
23 ContentVerifyJob::TestObserver* g_test_observer = NULL;
25 class ScopedElapsedTimer {
26 public:
27 ScopedElapsedTimer(base::TimeDelta* total) : total_(total) { DCHECK(total_); }
29 ~ScopedElapsedTimer() { *total_ += timer.Elapsed(); }
31 private:
32 // Some total amount of time we should add our elapsed time to at
33 // destruction.
34 base::TimeDelta* total_;
36 // A timer for how long this object has been alive.
37 base::ElapsedTimer timer;
40 } // namespace
42 ContentVerifyJob::ContentVerifyJob(ContentHashReader* hash_reader,
43 const FailureCallback& failure_callback)
44 : done_reading_(false),
45 hashes_ready_(false),
46 total_bytes_read_(0),
47 current_block_(0),
48 current_hash_byte_count_(0),
49 hash_reader_(hash_reader),
50 failure_callback_(failure_callback),
51 failed_(false) {
52 // It's ok for this object to be constructed on a different thread from where
53 // it's used.
54 thread_checker_.DetachFromThread();
57 ContentVerifyJob::~ContentVerifyJob() {
58 UMA_HISTOGRAM_COUNTS("ExtensionContentVerifyJob.TimeSpentUS",
59 time_spent_.InMicroseconds());
62 void ContentVerifyJob::Start() {
63 DCHECK(thread_checker_.CalledOnValidThread());
64 if (g_test_observer)
65 g_test_observer->JobStarted(hash_reader_->extension_id(),
66 hash_reader_->relative_path());
67 base::PostTaskAndReplyWithResult(
68 content::BrowserThread::GetBlockingPool(),
69 FROM_HERE,
70 base::Bind(&ContentHashReader::Init, hash_reader_),
71 base::Bind(&ContentVerifyJob::OnHashesReady, this));
74 void ContentVerifyJob::BytesRead(int count, const char* data) {
75 ScopedElapsedTimer timer(&time_spent_);
76 DCHECK(thread_checker_.CalledOnValidThread());
77 if (failed_)
78 return;
79 if (g_test_delegate) {
80 FailureReason reason =
81 g_test_delegate->BytesRead(hash_reader_->extension_id(), count, data);
82 if (reason != NONE)
83 return DispatchFailureCallback(reason);
85 if (!hashes_ready_) {
86 queue_.append(data, count);
87 return;
89 DCHECK_GE(count, 0);
90 int bytes_added = 0;
92 while (bytes_added < count) {
93 if (current_block_ >= hash_reader_->block_count())
94 return DispatchFailureCallback(HASH_MISMATCH);
96 if (!current_hash_.get()) {
97 current_hash_byte_count_ = 0;
98 current_hash_.reset(
99 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
101 // Compute how many bytes we should hash, and add them to the current hash.
102 int bytes_to_hash =
103 std::min(hash_reader_->block_size() - current_hash_byte_count_,
104 count - bytes_added);
105 DCHECK(bytes_to_hash > 0);
106 current_hash_->Update(data + bytes_added, bytes_to_hash);
107 bytes_added += bytes_to_hash;
108 current_hash_byte_count_ += bytes_to_hash;
109 total_bytes_read_ += bytes_to_hash;
111 // If we finished reading a block worth of data, finish computing the hash
112 // for it and make sure the expected hash matches.
113 if (current_hash_byte_count_ == hash_reader_->block_size() &&
114 !FinishBlock()) {
115 DispatchFailureCallback(HASH_MISMATCH);
116 return;
121 void ContentVerifyJob::DoneReading() {
122 ScopedElapsedTimer timer(&time_spent_);
123 DCHECK(thread_checker_.CalledOnValidThread());
124 if (failed_)
125 return;
126 if (g_test_delegate) {
127 FailureReason reason =
128 g_test_delegate->DoneReading(hash_reader_->extension_id());
129 if (reason != NONE) {
130 DispatchFailureCallback(reason);
131 return;
134 done_reading_ = true;
135 if (hashes_ready_) {
136 if (!FinishBlock())
137 DispatchFailureCallback(HASH_MISMATCH);
138 else if (g_test_observer)
139 g_test_observer->JobFinished(hash_reader_->extension_id(),
140 hash_reader_->relative_path(), failed_);
144 bool ContentVerifyJob::FinishBlock() {
145 if (current_hash_byte_count_ <= 0)
146 return true;
147 std::string final(crypto::kSHA256Length, 0);
148 current_hash_->Finish(string_as_array(&final), final.size());
149 current_hash_.reset();
150 current_hash_byte_count_ = 0;
152 int block = current_block_++;
154 const std::string* expected_hash = NULL;
155 if (!hash_reader_->GetHashForBlock(block, &expected_hash) ||
156 *expected_hash != final)
157 return false;
159 return true;
162 void ContentVerifyJob::OnHashesReady(bool success) {
163 if (!success && !g_test_delegate) {
164 if (!hash_reader_->content_exists()) {
165 // Ignore verification of non-existent resources.
166 return;
167 } else if (hash_reader_->have_verified_contents() &&
168 hash_reader_->have_computed_hashes()) {
169 DispatchFailureCallback(NO_HASHES_FOR_FILE);
170 } else {
171 DispatchFailureCallback(MISSING_ALL_HASHES);
173 return;
176 hashes_ready_ = true;
177 if (!queue_.empty()) {
178 std::string tmp;
179 queue_.swap(tmp);
180 BytesRead(tmp.size(), string_as_array(&tmp));
182 if (done_reading_) {
183 ScopedElapsedTimer timer(&time_spent_);
184 if (!FinishBlock())
185 DispatchFailureCallback(HASH_MISMATCH);
186 else if (g_test_observer) {
187 g_test_observer->JobFinished(hash_reader_->extension_id(),
188 hash_reader_->relative_path(), failed_);
193 // static
194 void ContentVerifyJob::SetDelegateForTests(TestDelegate* delegate) {
195 g_test_delegate = delegate;
198 // static
199 void ContentVerifyJob::SetObserverForTests(TestObserver* observer) {
200 g_test_observer = observer;
203 void ContentVerifyJob::DispatchFailureCallback(FailureReason reason) {
204 DCHECK(!failed_);
205 failed_ = true;
206 if (!failure_callback_.is_null()) {
207 VLOG(1) << "job failed for " << hash_reader_->extension_id() << " "
208 << hash_reader_->relative_path().MaybeAsASCII()
209 << " reason:" << reason;
210 failure_callback_.Run(reason);
211 failure_callback_.Reset();
213 if (g_test_observer)
214 g_test_observer->JobFinished(
215 hash_reader_->extension_id(), hash_reader_->relative_path(), failed_);
218 } // namespace extensions