Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / image_decoder.cc
blob7d26e8053b1041e221efa4b2f7a865b535a22215
1 // Copyright (c) 2012 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 "chrome/browser/image_decoder.h"
7 #include "base/bind.h"
8 #include "base/thread_task_runner_handle.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/common/chrome_utility_messages.h"
11 #include "chrome/grit/generated_resources.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/utility_process_host.h"
14 #include "ui/base/l10n/l10n_util.h"
16 using content::BrowserThread;
17 using content::UtilityProcessHost;
19 namespace {
21 // static, Leaky to allow access from any thread.
22 base::LazyInstance<ImageDecoder>::Leaky g_decoder = LAZY_INSTANCE_INITIALIZER;
24 // How long to wait after the last request has been received before ending
25 // batch mode.
26 const int kBatchModeTimeoutSeconds = 5;
28 } // namespace
30 ImageDecoder::ImageDecoder()
31 : image_request_id_counter_(0) {
32 // A single ImageDecoder instance should live for the life of the program.
33 // Explicitly add a reference so the object isn't deleted.
34 AddRef();
37 ImageDecoder::~ImageDecoder() {
40 ImageDecoder::ImageRequest::ImageRequest()
41 : task_runner_(base::ThreadTaskRunnerHandle::Get()) {
42 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
45 ImageDecoder::ImageRequest::ImageRequest(
46 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
47 : task_runner_(task_runner) {
48 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
51 ImageDecoder::ImageRequest::~ImageRequest() {
52 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
53 ImageDecoder::Cancel(this);
56 // static
57 void ImageDecoder::Start(ImageRequest* image_request,
58 const std::string& image_data) {
59 StartWithOptions(image_request, image_data, DEFAULT_CODEC, false);
62 // static
63 void ImageDecoder::StartWithOptions(ImageRequest* image_request,
64 const std::string& image_data,
65 ImageCodec image_codec,
66 bool shrink_to_fit) {
67 g_decoder.Pointer()->StartWithOptionsImpl(image_request, image_data,
68 image_codec, shrink_to_fit);
71 void ImageDecoder::StartWithOptionsImpl(ImageRequest* image_request,
72 const std::string& image_data,
73 ImageCodec image_codec,
74 bool shrink_to_fit) {
75 DCHECK(image_request);
76 DCHECK(image_request->task_runner());
78 int request_id;
80 base::AutoLock lock(map_lock_);
81 request_id = image_request_id_counter_++;
82 image_request_id_map_.insert(std::make_pair(request_id, image_request));
85 BrowserThread::PostTask(
86 BrowserThread::IO, FROM_HERE,
87 base::Bind(
88 &ImageDecoder::DecodeImageInSandbox,
89 g_decoder.Pointer(), request_id,
90 std::vector<unsigned char>(image_data.begin(), image_data.end()),
91 image_codec, shrink_to_fit));
94 // static
95 void ImageDecoder::Cancel(ImageRequest* image_request) {
96 DCHECK(image_request);
97 g_decoder.Pointer()->CancelImpl(image_request);
100 void ImageDecoder::DecodeImageInSandbox(
101 int request_id,
102 const std::vector<unsigned char>& image_data,
103 ImageCodec image_codec,
104 bool shrink_to_fit) {
105 DCHECK_CURRENTLY_ON(BrowserThread::IO);
106 base::AutoLock lock(map_lock_);
107 const auto it = image_request_id_map_.find(request_id);
108 if (it == image_request_id_map_.end())
109 return;
111 ImageRequest* image_request = it->second;
112 if (!utility_process_host_) {
113 StartBatchMode();
115 if (!utility_process_host_) {
116 // Utility process failed to start; notify delegate and return.
117 // Without this check, we were seeing crashes on startup. Further
118 // investigation is needed to determine why the utility process
119 // is failing to start. See crbug.com/472272
120 image_request->task_runner()->PostTask(
121 FROM_HERE,
122 base::Bind(&ImageDecoder::RunOnDecodeImageFailed, this, request_id));
123 return;
126 if (!batch_mode_timer_) {
127 // Created here so it will call StopBatchMode() on the right thread.
128 batch_mode_timer_.reset(new base::DelayTimer<ImageDecoder>(
129 FROM_HERE,
130 base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds),
131 this,
132 &ImageDecoder::StopBatchMode));
134 batch_mode_timer_->Reset();
136 switch (image_codec) {
137 #if defined(OS_CHROMEOS)
138 case ROBUST_JPEG_CODEC:
139 utility_process_host_->Send(new ChromeUtilityMsg_RobustJPEGDecodeImage(
140 image_data, request_id));
141 break;
142 #endif // defined(OS_CHROMEOS)
143 case DEFAULT_CODEC:
144 utility_process_host_->Send(new ChromeUtilityMsg_DecodeImage(
145 image_data, shrink_to_fit, request_id));
146 break;
150 void ImageDecoder::CancelImpl(ImageRequest* image_request) {
151 base::AutoLock lock(map_lock_);
152 for (auto it = image_request_id_map_.begin();
153 it != image_request_id_map_.end();) {
154 if (it->second == image_request) {
155 image_request_id_map_.erase(it++);
156 } else {
157 ++it;
162 void ImageDecoder::StartBatchMode() {
163 DCHECK_CURRENTLY_ON(BrowserThread::IO);
164 utility_process_host_ =
165 UtilityProcessHost::Create(
166 this, base::ThreadTaskRunnerHandle::Get().get())->AsWeakPtr();
167 utility_process_host_->SetName(l10n_util::GetStringUTF16(
168 IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME));
169 if (!utility_process_host_->StartBatchMode()) {
170 utility_process_host_.reset();
171 return;
175 void ImageDecoder::StopBatchMode() {
176 DCHECK_CURRENTLY_ON(BrowserThread::IO);
178 // Check for outstanding requests and wait for them to finish.
179 base::AutoLock lock(map_lock_);
180 if (!image_request_id_map_.empty()) {
181 batch_mode_timer_->Reset();
182 return;
186 if (utility_process_host_) {
187 utility_process_host_->EndBatchMode();
188 utility_process_host_.reset();
192 void ImageDecoder::FailAllRequests() {
193 RequestMap requests;
195 base::AutoLock lock(map_lock_);
196 requests = image_request_id_map_;
199 // Since |OnProcessCrashed| and |OnProcessLaunchFailed| are run asynchronously
200 // from the actual event, it's possible for a new utility process to have been
201 // created and sent requests by the time these functions are run. This results
202 // in failing requests that are unaffected by the crash. Although not ideal,
203 // this is valid and simpler than tracking which request is sent to which
204 // utility process, and whether the request has been sent at all.
205 for (const auto& request : requests)
206 OnDecodeImageFailed(request.first);
209 void ImageDecoder::OnProcessCrashed(int exit_code) {
210 DCHECK_CURRENTLY_ON(BrowserThread::IO);
211 FailAllRequests();
214 void ImageDecoder::OnProcessLaunchFailed() {
215 DCHECK_CURRENTLY_ON(BrowserThread::IO);
216 FailAllRequests();
219 bool ImageDecoder::OnMessageReceived(
220 const IPC::Message& message) {
221 bool handled = true;
222 IPC_BEGIN_MESSAGE_MAP(ImageDecoder, message)
223 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Succeeded,
224 OnDecodeImageSucceeded)
225 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Failed,
226 OnDecodeImageFailed)
227 IPC_MESSAGE_UNHANDLED(handled = false)
228 IPC_END_MESSAGE_MAP()
229 return handled;
232 void ImageDecoder::OnDecodeImageSucceeded(
233 const SkBitmap& decoded_image,
234 int request_id) {
235 DCHECK_CURRENTLY_ON(BrowserThread::IO);
236 base::AutoLock lock(map_lock_);
237 auto it = image_request_id_map_.find(request_id);
238 if (it == image_request_id_map_.end())
239 return;
241 ImageRequest* image_request = it->second;
242 image_request->task_runner()->PostTask(
243 FROM_HERE,
244 base::Bind(&ImageDecoder::RunOnImageDecoded,
245 this,
246 decoded_image,
247 request_id));
250 void ImageDecoder::OnDecodeImageFailed(int request_id) {
251 DCHECK_CURRENTLY_ON(BrowserThread::IO);
252 base::AutoLock lock(map_lock_);
253 auto it = image_request_id_map_.find(request_id);
254 if (it == image_request_id_map_.end())
255 return;
257 ImageRequest* image_request = it->second;
258 image_request->task_runner()->PostTask(
259 FROM_HERE,
260 base::Bind(&ImageDecoder::RunOnDecodeImageFailed, this, request_id));
263 void ImageDecoder::RunOnImageDecoded(const SkBitmap& decoded_image,
264 int request_id) {
265 ImageRequest* image_request;
267 base::AutoLock lock(map_lock_);
268 auto it = image_request_id_map_.find(request_id);
269 if (it == image_request_id_map_.end())
270 return;
271 image_request = it->second;
272 image_request_id_map_.erase(it);
275 DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread());
276 image_request->OnImageDecoded(decoded_image);
279 void ImageDecoder::RunOnDecodeImageFailed(int request_id) {
280 ImageRequest* image_request;
282 base::AutoLock lock(map_lock_);
283 auto it = image_request_id_map_.find(request_id);
284 if (it == image_request_id_map_.end())
285 return;
286 image_request = it->second;
287 image_request_id_map_.erase(it);
290 DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread());
291 image_request->OnDecodeImageFailed();