blob: c587695faba823e156b22b6e4711710aca4ce451 [file] [log] [blame]
[email protected]44013f682012-05-31 13:49:401// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]551707a2010-06-16 16:59:472// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]6d33da172011-11-22 03:56:095#include "chrome/browser/image_decoder.h"
[email protected]551707a2010-06-16 16:59:476
[email protected]31893772011-10-11 17:41:507#include "base/bind.h"
thestigc5eeb9f362015-04-22 18:42:328#include "base/thread_task_runner_handle.h"
avi6846aef2015-12-26 01:09:389#include "build/build_config.h"
[email protected]551707a2010-06-16 16:59:4710#include "chrome/browser/browser_process.h"
[email protected]373c1062011-06-09 21:11:5111#include "chrome/common/chrome_utility_messages.h"
thestige3d6b752015-04-07 21:58:4512#include "chrome/grit/generated_resources.h"
[email protected]c38831a12011-10-28 12:44:4913#include "content/public/browser/browser_thread.h"
[email protected]c4f883a2012-02-03 17:02:0714#include "content/public/browser/utility_process_host.h"
thestige3d6b752015-04-07 21:58:4515#include "ui/base/l10n/l10n_util.h"
[email protected]551707a2010-06-16 16:59:4716
[email protected]631bb742011-11-02 11:29:3917using content::BrowserThread;
[email protected]c4f883a2012-02-03 17:02:0718using content::UtilityProcessHost;
[email protected]631bb742011-11-02 11:29:3919
twellingtona71414d2015-03-26 15:09:4520namespace {
21
22// static, Leaky to allow access from any thread.
23base::LazyInstance<ImageDecoder>::Leaky g_decoder = LAZY_INSTANCE_INITIALIZER;
24
25// How long to wait after the last request has been received before ending
26// batch mode.
27const int kBatchModeTimeoutSeconds = 5;
28
29} // namespace
30
31ImageDecoder::ImageDecoder()
thestigd3485b692015-04-30 09:47:3232 : image_request_id_counter_(0) {
twellingtona71414d2015-03-26 15:09:4533 // A single ImageDecoder instance should live for the life of the program.
34 // Explicitly add a reference so the object isn't deleted.
35 AddRef();
[email protected]551707a2010-06-16 16:59:4736}
37
twellingtona71414d2015-03-26 15:09:4538ImageDecoder::~ImageDecoder() {
pneubeck93871252015-01-20 11:26:3639}
40
thestigc5eeb9f362015-04-22 18:42:3241ImageDecoder::ImageRequest::ImageRequest()
42 : task_runner_(base::ThreadTaskRunnerHandle::Get()) {
43 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
44}
45
twellingtona71414d2015-03-26 15:09:4546ImageDecoder::ImageRequest::ImageRequest(
47 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
48 : task_runner_(task_runner) {
thestigc5eeb9f362015-04-22 18:42:3249 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
twellingtona71414d2015-03-26 15:09:4550}
[email protected]863e6d962011-05-15 19:39:3551
twellingtona71414d2015-03-26 15:09:4552ImageDecoder::ImageRequest::~ImageRequest() {
thestigc5eeb9f362015-04-22 18:42:3253 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
twellingtona71414d2015-03-26 15:09:4554 ImageDecoder::Cancel(this);
55}
56
57// static
58void ImageDecoder::Start(ImageRequest* image_request,
59 const std::string& image_data) {
60 StartWithOptions(image_request, image_data, DEFAULT_CODEC, false);
61}
62
63// static
64void ImageDecoder::StartWithOptions(ImageRequest* image_request,
65 const std::string& image_data,
66 ImageCodec image_codec,
67 bool shrink_to_fit) {
thestigc5eeb9f362015-04-22 18:42:3268 g_decoder.Pointer()->StartWithOptionsImpl(image_request, image_data,
69 image_codec, shrink_to_fit);
70}
71
72void ImageDecoder::StartWithOptionsImpl(ImageRequest* image_request,
73 const std::string& image_data,
74 ImageCodec image_codec,
75 bool shrink_to_fit) {
twellingtona71414d2015-03-26 15:09:4576 DCHECK(image_request);
77 DCHECK(image_request->task_runner());
thestigc5eeb9f362015-04-22 18:42:3278
79 int request_id;
80 {
81 base::AutoLock lock(map_lock_);
82 request_id = image_request_id_counter_++;
83 image_request_id_map_.insert(std::make_pair(request_id, image_request));
84 }
85
[email protected]cca169b52010-10-08 22:15:5586 BrowserThread::PostTask(
twellingtona71414d2015-03-26 15:09:4587 BrowserThread::IO, FROM_HERE,
88 base::Bind(
89 &ImageDecoder::DecodeImageInSandbox,
thestigc5eeb9f362015-04-22 18:42:3290 g_decoder.Pointer(), request_id,
twellingtona71414d2015-03-26 15:09:4591 std::vector<unsigned char>(image_data.begin(), image_data.end()),
92 image_codec, shrink_to_fit));
[email protected]551707a2010-06-16 16:59:4793}
94
twellingtona71414d2015-03-26 15:09:4595// static
96void ImageDecoder::Cancel(ImageRequest* image_request) {
97 DCHECK(image_request);
98 g_decoder.Pointer()->CancelImpl(image_request);
99}
100
101void ImageDecoder::DecodeImageInSandbox(
thestigc5eeb9f362015-04-22 18:42:32102 int request_id,
twellingtona71414d2015-03-26 15:09:45103 const std::vector<unsigned char>& image_data,
104 ImageCodec image_codec,
105 bool shrink_to_fit) {
106 DCHECK_CURRENTLY_ON(BrowserThread::IO);
thestigc5eeb9f362015-04-22 18:42:32107 base::AutoLock lock(map_lock_);
108 const auto it = image_request_id_map_.find(request_id);
109 if (it == image_request_id_map_.end())
110 return;
111
112 ImageRequest* image_request = it->second;
twellingtona71414d2015-03-26 15:09:45113 if (!utility_process_host_) {
114 StartBatchMode();
115 }
twellington612b9b62015-04-01 00:33:12116 if (!utility_process_host_) {
117 // Utility process failed to start; notify delegate and return.
118 // Without this check, we were seeing crashes on startup. Further
119 // investigation is needed to determine why the utility process
120 // is failing to start. See crbug.com/472272
121 image_request->task_runner()->PostTask(
thestigc5eeb9f362015-04-22 18:42:32122 FROM_HERE,
123 base::Bind(&ImageDecoder::RunOnDecodeImageFailed, this, request_id));
twellington612b9b62015-04-01 00:33:12124 return;
125 }
twellingtona71414d2015-03-26 15:09:45126
thestigd3485b692015-04-30 09:47:32127 if (!batch_mode_timer_) {
128 // Created here so it will call StopBatchMode() on the right thread.
danakj8c3eb802015-09-24 07:53:00129 batch_mode_timer_.reset(new base::DelayTimer(
130 FROM_HERE, base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds), this,
thestigd3485b692015-04-30 09:47:32131 &ImageDecoder::StopBatchMode));
132 }
133 batch_mode_timer_->Reset();
twellingtona71414d2015-03-26 15:09:45134
135 switch (image_codec) {
edward.baker199f66b2015-05-07 19:13:30136#if defined(OS_CHROMEOS)
twellingtona71414d2015-03-26 15:09:45137 case ROBUST_JPEG_CODEC:
138 utility_process_host_->Send(new ChromeUtilityMsg_RobustJPEGDecodeImage(
thestigc5eeb9f362015-04-22 18:42:32139 image_data, request_id));
twellingtona71414d2015-03-26 15:09:45140 break;
edward.baker199f66b2015-05-07 19:13:30141#endif // defined(OS_CHROMEOS)
twellingtona71414d2015-03-26 15:09:45142 case DEFAULT_CODEC:
143 utility_process_host_->Send(new ChromeUtilityMsg_DecodeImage(
thestigc5eeb9f362015-04-22 18:42:32144 image_data, shrink_to_fit, request_id));
twellingtona71414d2015-03-26 15:09:45145 break;
146 }
twellingtona71414d2015-03-26 15:09:45147}
148
149void ImageDecoder::CancelImpl(ImageRequest* image_request) {
150 base::AutoLock lock(map_lock_);
151 for (auto it = image_request_id_map_.begin();
152 it != image_request_id_map_.end();) {
153 if (it->second == image_request) {
154 image_request_id_map_.erase(it++);
155 } else {
156 ++it;
157 }
158 }
159}
160
161void ImageDecoder::StartBatchMode() {
162 DCHECK_CURRENTLY_ON(BrowserThread::IO);
163 utility_process_host_ =
pranay.kumar6a179252015-05-05 11:35:15164 UtilityProcessHost::Create(
165 this, base::ThreadTaskRunnerHandle::Get().get())->AsWeakPtr();
thestige3d6b752015-04-07 21:58:45166 utility_process_host_->SetName(l10n_util::GetStringUTF16(
167 IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME));
twellington612b9b62015-04-01 00:33:12168 if (!utility_process_host_->StartBatchMode()) {
169 utility_process_host_.reset();
170 return;
171 }
twellingtona71414d2015-03-26 15:09:45172}
173
174void ImageDecoder::StopBatchMode() {
175 DCHECK_CURRENTLY_ON(BrowserThread::IO);
thestig3d8ebe3a2015-05-08 03:25:32176 {
177 // Check for outstanding requests and wait for them to finish.
178 base::AutoLock lock(map_lock_);
179 if (!image_request_id_map_.empty()) {
180 batch_mode_timer_->Reset();
181 return;
182 }
183 }
184
twellingtona71414d2015-03-26 15:09:45185 if (utility_process_host_) {
186 utility_process_host_->EndBatchMode();
187 utility_process_host_.reset();
188 }
amistryd59fe6b2015-04-30 23:50:30189}
190
191void ImageDecoder::FailAllRequests() {
192 RequestMap requests;
193 {
194 base::AutoLock lock(map_lock_);
195 requests = image_request_id_map_;
196 }
197
198 // Since |OnProcessCrashed| and |OnProcessLaunchFailed| are run asynchronously
199 // from the actual event, it's possible for a new utility process to have been
200 // created and sent requests by the time these functions are run. This results
201 // in failing requests that are unaffected by the crash. Although not ideal,
202 // this is valid and simpler than tracking which request is sent to which
203 // utility process, and whether the request has been sent at all.
204 for (const auto& request : requests)
205 OnDecodeImageFailed(request.first);
206}
207
208void ImageDecoder::OnProcessCrashed(int exit_code) {
209 DCHECK_CURRENTLY_ON(BrowserThread::IO);
210 FailAllRequests();
211}
212
213void ImageDecoder::OnProcessLaunchFailed() {
214 DCHECK_CURRENTLY_ON(BrowserThread::IO);
215 FailAllRequests();
twellingtona71414d2015-03-26 15:09:45216}
217
218bool ImageDecoder::OnMessageReceived(
219 const IPC::Message& message) {
[email protected]373c1062011-06-09 21:11:51220 bool handled = true;
221 IPC_BEGIN_MESSAGE_MAP(ImageDecoder, message)
[email protected]2ccf45c2011-08-19 23:35:50222 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Succeeded,
[email protected]373c1062011-06-09 21:11:51223 OnDecodeImageSucceeded)
[email protected]2ccf45c2011-08-19 23:35:50224 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_DecodeImage_Failed,
225 OnDecodeImageFailed)
[email protected]373c1062011-06-09 21:11:51226 IPC_MESSAGE_UNHANDLED(handled = false)
[email protected]1f946bc2011-11-17 00:52:30227 IPC_END_MESSAGE_MAP()
[email protected]373c1062011-06-09 21:11:51228 return handled;
229}
230
twellingtona71414d2015-03-26 15:09:45231void ImageDecoder::OnDecodeImageSucceeded(
232 const SkBitmap& decoded_image,
233 int request_id) {
234 DCHECK_CURRENTLY_ON(BrowserThread::IO);
235 base::AutoLock lock(map_lock_);
236 auto it = image_request_id_map_.find(request_id);
thestigc5eeb9f362015-04-22 18:42:32237 if (it == image_request_id_map_.end())
238 return;
twellingtona71414d2015-03-26 15:09:45239
thestigc5eeb9f362015-04-22 18:42:32240 ImageRequest* image_request = it->second;
241 image_request->task_runner()->PostTask(
242 FROM_HERE,
243 base::Bind(&ImageDecoder::RunOnImageDecoded,
244 this,
245 decoded_image,
246 request_id));
[email protected]928c0e712011-03-16 15:01:57247}
248
twellingtona71414d2015-03-26 15:09:45249void ImageDecoder::OnDecodeImageFailed(int request_id) {
250 DCHECK_CURRENTLY_ON(BrowserThread::IO);
251 base::AutoLock lock(map_lock_);
252 auto it = image_request_id_map_.find(request_id);
thestigc5eeb9f362015-04-22 18:42:32253 if (it == image_request_id_map_.end())
254 return;
[email protected]551707a2010-06-16 16:59:47255
thestigc5eeb9f362015-04-22 18:42:32256 ImageRequest* image_request = it->second;
257 image_request->task_runner()->PostTask(
258 FROM_HERE,
259 base::Bind(&ImageDecoder::RunOnDecodeImageFailed, this, request_id));
260}
261
262void ImageDecoder::RunOnImageDecoded(const SkBitmap& decoded_image,
263 int request_id) {
264 ImageRequest* image_request;
265 {
266 base::AutoLock lock(map_lock_);
267 auto it = image_request_id_map_.find(request_id);
268 if (it == image_request_id_map_.end())
269 return;
270 image_request = it->second;
twellingtona71414d2015-03-26 15:09:45271 image_request_id_map_.erase(it);
[email protected]11f16d102012-08-29 23:12:15272 }
thestigc5eeb9f362015-04-22 18:42:32273
274 DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread());
275 image_request->OnImageDecoded(decoded_image);
276}
277
278void ImageDecoder::RunOnDecodeImageFailed(int request_id) {
279 ImageRequest* image_request;
280 {
281 base::AutoLock lock(map_lock_);
282 auto it = image_request_id_map_.find(request_id);
283 if (it == image_request_id_map_.end())
284 return;
285 image_request = it->second;
286 image_request_id_map_.erase(it);
287 }
288
289 DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread());
290 image_request->OnDecodeImageFailed();
[email protected]551707a2010-06-16 16:59:47291}