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"
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
;
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
26 const int kBatchModeTimeoutSeconds
= 5;
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.
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);
57 void ImageDecoder::Start(ImageRequest
* image_request
,
58 const std::string
& image_data
) {
59 StartWithOptions(image_request
, image_data
, DEFAULT_CODEC
, false);
63 void ImageDecoder::StartWithOptions(ImageRequest
* image_request
,
64 const std::string
& image_data
,
65 ImageCodec image_codec
,
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
,
75 DCHECK(image_request
);
76 DCHECK(image_request
->task_runner());
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
,
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
));
95 void ImageDecoder::Cancel(ImageRequest
* image_request
) {
96 DCHECK(image_request
);
97 g_decoder
.Pointer()->CancelImpl(image_request
);
100 void ImageDecoder::DecodeImageInSandbox(
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())
111 ImageRequest
* image_request
= it
->second
;
112 if (!utility_process_host_
) {
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(
122 base::Bind(&ImageDecoder::RunOnDecodeImageFailed
, this, request_id
));
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
>(
130 base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds
),
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
));
142 #endif // defined(OS_CHROMEOS)
144 utility_process_host_
->Send(new ChromeUtilityMsg_DecodeImage(
145 image_data
, shrink_to_fit
, request_id
));
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
++);
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();
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();
186 if (utility_process_host_
) {
187 utility_process_host_
->EndBatchMode();
188 utility_process_host_
.reset();
192 void ImageDecoder::FailAllRequests() {
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
);
214 void ImageDecoder::OnProcessLaunchFailed() {
215 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
219 bool ImageDecoder::OnMessageReceived(
220 const IPC::Message
& message
) {
222 IPC_BEGIN_MESSAGE_MAP(ImageDecoder
, message
)
223 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Succeeded
,
224 OnDecodeImageSucceeded
)
225 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Failed
,
227 IPC_MESSAGE_UNHANDLED(handled
= false)
228 IPC_END_MESSAGE_MAP()
232 void ImageDecoder::OnDecodeImageSucceeded(
233 const SkBitmap
& decoded_image
,
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())
241 ImageRequest
* image_request
= it
->second
;
242 image_request
->task_runner()->PostTask(
244 base::Bind(&ImageDecoder::RunOnImageDecoded
,
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())
257 ImageRequest
* image_request
= it
->second
;
258 image_request
->task_runner()->PostTask(
260 base::Bind(&ImageDecoder::RunOnDecodeImageFailed
, this, request_id
));
263 void ImageDecoder::RunOnImageDecoded(const SkBitmap
& decoded_image
,
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())
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())
286 image_request
= it
->second
;
287 image_request_id_map_
.erase(it
);
290 DCHECK(image_request
->task_runner()->RunsTasksOnCurrentThread());
291 image_request
->OnDecodeImageFailed();