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 "chrome/browser/browser_process.h"
9 #include "chrome/common/chrome_utility_messages.h"
10 #include "chrome/grit/generated_resources.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "content/public/browser/utility_process_host.h"
13 #include "ui/base/l10n/l10n_util.h"
15 using content::BrowserThread
;
16 using content::UtilityProcessHost
;
20 // static, Leaky to allow access from any thread.
21 base::LazyInstance
<ImageDecoder
>::Leaky g_decoder
= LAZY_INSTANCE_INITIALIZER
;
23 // How long to wait after the last request has been received before ending
25 const int kBatchModeTimeoutSeconds
= 5;
29 ImageDecoder::ImageDecoder()
30 : image_request_id_counter_(0), last_request_(base::TimeTicks::Now()) {
31 // A single ImageDecoder instance should live for the life of the program.
32 // Explicitly add a reference so the object isn't deleted.
36 ImageDecoder::~ImageDecoder() {
39 ImageDecoder::ImageRequest::ImageRequest(
40 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
)
41 : task_runner_(task_runner
) {
44 ImageDecoder::ImageRequest::~ImageRequest() {
45 ImageDecoder::Cancel(this);
49 void ImageDecoder::Start(ImageRequest
* image_request
,
50 const std::string
& image_data
) {
51 StartWithOptions(image_request
, image_data
, DEFAULT_CODEC
, false);
55 void ImageDecoder::StartWithOptions(ImageRequest
* image_request
,
56 const std::string
& image_data
,
57 ImageCodec image_codec
,
59 DCHECK(image_request
);
60 DCHECK(image_request
->task_runner());
61 BrowserThread::PostTask(
62 BrowserThread::IO
, FROM_HERE
,
64 &ImageDecoder::DecodeImageInSandbox
,
65 g_decoder
.Pointer(), image_request
,
66 std::vector
<unsigned char>(image_data
.begin(), image_data
.end()),
67 image_codec
, shrink_to_fit
));
71 void ImageDecoder::Cancel(ImageRequest
* image_request
) {
72 DCHECK(image_request
);
73 g_decoder
.Pointer()->CancelImpl(image_request
);
76 void ImageDecoder::DecodeImageInSandbox(
77 ImageRequest
* image_request
,
78 const std::vector
<unsigned char>& image_data
,
79 ImageCodec image_codec
,
81 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
82 if (!utility_process_host_
) {
85 if (!utility_process_host_
) {
86 // Utility process failed to start; notify delegate and return.
87 // Without this check, we were seeing crashes on startup. Further
88 // investigation is needed to determine why the utility process
89 // is failing to start. See crbug.com/472272
90 image_request
->task_runner()->PostTask(
91 FROM_HERE
, base::Bind(&ImageRequest::OnDecodeImageFailed
,
92 base::Unretained(image_request
)));
96 last_request_
= base::TimeTicks::Now();
97 base::AutoLock
lock(map_lock_
);
98 image_request_id_map_
.insert(
99 std::make_pair(image_request_id_counter_
, image_request
));
101 switch (image_codec
) {
102 case ROBUST_JPEG_CODEC
:
103 utility_process_host_
->Send(new ChromeUtilityMsg_RobustJPEGDecodeImage(
104 image_data
, image_request_id_counter_
));
107 utility_process_host_
->Send(new ChromeUtilityMsg_DecodeImage(
108 image_data
, shrink_to_fit
, image_request_id_counter_
));
112 ++image_request_id_counter_
;
115 void ImageDecoder::CancelImpl(ImageRequest
* image_request
) {
116 base::AutoLock
lock(map_lock_
);
117 for (auto it
= image_request_id_map_
.begin();
118 it
!= image_request_id_map_
.end();) {
119 if (it
->second
== image_request
) {
120 image_request_id_map_
.erase(it
++);
127 void ImageDecoder::StartBatchMode() {
128 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
129 utility_process_host_
=
130 UtilityProcessHost::Create(this, base::MessageLoopProxy::current().get())
132 utility_process_host_
->SetName(l10n_util::GetStringUTF16(
133 IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME
));
134 if (!utility_process_host_
->StartBatchMode()) {
135 utility_process_host_
.reset();
138 batch_mode_timer_
.Start(
139 FROM_HERE
, base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds
),
140 this, &ImageDecoder::StopBatchMode
);
143 void ImageDecoder::StopBatchMode() {
144 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
145 if ((base::TimeTicks::Now() - last_request_
)
146 < base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds
)) {
150 if (utility_process_host_
) {
151 utility_process_host_
->EndBatchMode();
152 utility_process_host_
.reset();
154 batch_mode_timer_
.Stop();
157 bool ImageDecoder::OnMessageReceived(
158 const IPC::Message
& message
) {
160 IPC_BEGIN_MESSAGE_MAP(ImageDecoder
, message
)
161 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Succeeded
,
162 OnDecodeImageSucceeded
)
163 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Failed
,
165 IPC_MESSAGE_UNHANDLED(handled
= false)
166 IPC_END_MESSAGE_MAP()
170 void ImageDecoder::OnDecodeImageSucceeded(
171 const SkBitmap
& decoded_image
,
173 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
174 base::AutoLock
lock(map_lock_
);
175 auto it
= image_request_id_map_
.find(request_id
);
176 if (it
!= image_request_id_map_
.end()) {
177 ImageRequest
* image_request
= it
->second
;
178 image_request
->task_runner()->PostTask(
179 FROM_HERE
, base::Bind(&ImageRequest::OnImageDecoded
,
180 base::Unretained(image_request
), decoded_image
));
182 image_request_id_map_
.erase(it
);
186 void ImageDecoder::OnDecodeImageFailed(int request_id
) {
187 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
188 base::AutoLock
lock(map_lock_
);
189 auto it
= image_request_id_map_
.find(request_id
);
190 if (it
!= image_request_id_map_
.end()) {
191 ImageRequest
* image_request
= it
->second
;
192 image_request
->task_runner()->PostTask(
193 FROM_HERE
, base::Bind(&ImageRequest::OnDecodeImageFailed
,
194 base::Unretained(image_request
)));
196 image_request_id_map_
.erase(it
);