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 "content/public/browser/browser_thread.h"
11 #include "content/public/browser/utility_process_host.h"
13 using content::BrowserThread
;
14 using content::UtilityProcessHost
;
18 // static, Leaky to allow access from any thread.
19 base::LazyInstance
<ImageDecoder
>::Leaky g_decoder
= LAZY_INSTANCE_INITIALIZER
;
21 // How long to wait after the last request has been received before ending
23 const int kBatchModeTimeoutSeconds
= 5;
27 ImageDecoder::ImageDecoder()
28 : image_request_id_counter_(0), last_request_(base::TimeTicks::Now()) {
29 // A single ImageDecoder instance should live for the life of the program.
30 // Explicitly add a reference so the object isn't deleted.
34 ImageDecoder::~ImageDecoder() {
37 ImageDecoder::ImageRequest::ImageRequest(
38 const scoped_refptr
<base::SequencedTaskRunner
>& task_runner
)
39 : task_runner_(task_runner
) {
42 ImageDecoder::ImageRequest::~ImageRequest() {
43 ImageDecoder::Cancel(this);
47 void ImageDecoder::Start(ImageRequest
* image_request
,
48 const std::string
& image_data
) {
49 StartWithOptions(image_request
, image_data
, DEFAULT_CODEC
, false);
53 void ImageDecoder::StartWithOptions(ImageRequest
* image_request
,
54 const std::string
& image_data
,
55 ImageCodec image_codec
,
57 DCHECK(image_request
);
58 DCHECK(image_request
->task_runner());
59 BrowserThread::PostTask(
60 BrowserThread::IO
, FROM_HERE
,
62 &ImageDecoder::DecodeImageInSandbox
,
63 g_decoder
.Pointer(), image_request
,
64 std::vector
<unsigned char>(image_data
.begin(), image_data
.end()),
65 image_codec
, shrink_to_fit
));
69 void ImageDecoder::Cancel(ImageRequest
* image_request
) {
70 DCHECK(image_request
);
71 g_decoder
.Pointer()->CancelImpl(image_request
);
74 void ImageDecoder::DecodeImageInSandbox(
75 ImageRequest
* image_request
,
76 const std::vector
<unsigned char>& image_data
,
77 ImageCodec image_codec
,
79 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
80 if (!utility_process_host_
) {
83 if (!utility_process_host_
) {
84 // Utility process failed to start; notify delegate and return.
85 // Without this check, we were seeing crashes on startup. Further
86 // investigation is needed to determine why the utility process
87 // is failing to start. See crbug.com/472272
88 image_request
->task_runner()->PostTask(
89 FROM_HERE
, base::Bind(&ImageRequest::OnDecodeImageFailed
,
90 base::Unretained(image_request
)));
94 last_request_
= base::TimeTicks::Now();
95 base::AutoLock
lock(map_lock_
);
96 image_request_id_map_
.insert(
97 std::make_pair(image_request_id_counter_
, image_request
));
99 switch (image_codec
) {
100 case ROBUST_JPEG_CODEC
:
101 utility_process_host_
->Send(new ChromeUtilityMsg_RobustJPEGDecodeImage(
102 image_data
, image_request_id_counter_
));
105 utility_process_host_
->Send(new ChromeUtilityMsg_DecodeImage(
106 image_data
, shrink_to_fit
, image_request_id_counter_
));
110 ++image_request_id_counter_
;
113 void ImageDecoder::CancelImpl(ImageRequest
* image_request
) {
114 base::AutoLock
lock(map_lock_
);
115 for (auto it
= image_request_id_map_
.begin();
116 it
!= image_request_id_map_
.end();) {
117 if (it
->second
== image_request
) {
118 image_request_id_map_
.erase(it
++);
125 void ImageDecoder::StartBatchMode() {
126 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
127 utility_process_host_
=
128 UtilityProcessHost::Create(this, base::MessageLoopProxy::current().get())
130 if (!utility_process_host_
->StartBatchMode()) {
131 utility_process_host_
.reset();
134 batch_mode_timer_
.Start(
135 FROM_HERE
, base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds
),
136 this, &ImageDecoder::StopBatchMode
);
139 void ImageDecoder::StopBatchMode() {
140 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
141 if ((base::TimeTicks::Now() - last_request_
)
142 < base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds
)) {
146 if (utility_process_host_
) {
147 utility_process_host_
->EndBatchMode();
148 utility_process_host_
.reset();
150 batch_mode_timer_
.Stop();
153 bool ImageDecoder::OnMessageReceived(
154 const IPC::Message
& message
) {
156 IPC_BEGIN_MESSAGE_MAP(ImageDecoder
, message
)
157 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Succeeded
,
158 OnDecodeImageSucceeded
)
159 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Failed
,
161 IPC_MESSAGE_UNHANDLED(handled
= false)
162 IPC_END_MESSAGE_MAP()
166 void ImageDecoder::OnDecodeImageSucceeded(
167 const SkBitmap
& decoded_image
,
169 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
170 base::AutoLock
lock(map_lock_
);
171 auto it
= image_request_id_map_
.find(request_id
);
172 if (it
!= image_request_id_map_
.end()) {
173 ImageRequest
* image_request
= it
->second
;
174 image_request
->task_runner()->PostTask(
175 FROM_HERE
, base::Bind(&ImageRequest::OnImageDecoded
,
176 base::Unretained(image_request
), decoded_image
));
178 image_request_id_map_
.erase(it
);
182 void ImageDecoder::OnDecodeImageFailed(int request_id
) {
183 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
184 base::AutoLock
lock(map_lock_
);
185 auto it
= image_request_id_map_
.find(request_id
);
186 if (it
!= image_request_id_map_
.end()) {
187 ImageRequest
* image_request
= it
->second
;
188 image_request
->task_runner()->PostTask(
189 FROM_HERE
, base::Bind(&ImageRequest::OnDecodeImageFailed
,
190 base::Unretained(image_request
)));
192 image_request_id_map_
.erase(it
);