blob: 0d4e14dd08c28523aa3baa1923c4d3922ff53988 [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"
amistry6f72f042016-04-05 20:42:0711#include "chrome/common/image_decoder.mojom.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"
amistry6f72f042016-04-05 20:42:0715#include "content/public/common/service_registry.h"
16#include "skia/public/type_converters.h"
17#include "third_party/skia/include/core/SkBitmap.h"
thestige3d6b752015-04-07 21:58:4518#include "ui/base/l10n/l10n_util.h"
[email protected]551707a2010-06-16 16:59:4719
[email protected]631bb742011-11-02 11:29:3920using content::BrowserThread;
[email protected]c4f883a2012-02-03 17:02:0721using content::UtilityProcessHost;
[email protected]631bb742011-11-02 11:29:3922
twellingtona71414d2015-03-26 15:09:4523namespace {
24
25// static, Leaky to allow access from any thread.
26base::LazyInstance<ImageDecoder>::Leaky g_decoder = LAZY_INSTANCE_INITIALIZER;
27
28// How long to wait after the last request has been received before ending
29// batch mode.
30const int kBatchModeTimeoutSeconds = 5;
31
amistry6f72f042016-04-05 20:42:0732void OnDecodeImageDone(
33 base::Callback<void(int)> fail_callback,
34 base::Callback<void(const SkBitmap&, int)> success_callback,
35 int request_id, skia::mojom::BitmapPtr image) {
36 DCHECK_CURRENTLY_ON(BrowserThread::IO);
37 if (image) {
38 SkBitmap bitmap = image.To<SkBitmap>();
39 if (!bitmap.empty()) {
40 success_callback.Run(bitmap, request_id);
41 return;
42 }
43 }
44 fail_callback.Run(request_id);
45}
46
twellingtona71414d2015-03-26 15:09:4547} // namespace
48
49ImageDecoder::ImageDecoder()
thestigd3485b692015-04-30 09:47:3250 : image_request_id_counter_(0) {
twellingtona71414d2015-03-26 15:09:4551 // A single ImageDecoder instance should live for the life of the program.
52 // Explicitly add a reference so the object isn't deleted.
53 AddRef();
[email protected]551707a2010-06-16 16:59:4754}
55
twellingtona71414d2015-03-26 15:09:4556ImageDecoder::~ImageDecoder() {
pneubeck93871252015-01-20 11:26:3657}
58
thestigc5eeb9f362015-04-22 18:42:3259ImageDecoder::ImageRequest::ImageRequest()
60 : task_runner_(base::ThreadTaskRunnerHandle::Get()) {
61 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
62}
63
twellingtona71414d2015-03-26 15:09:4564ImageDecoder::ImageRequest::ImageRequest(
65 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
66 : task_runner_(task_runner) {
thestigc5eeb9f362015-04-22 18:42:3267 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
twellingtona71414d2015-03-26 15:09:4568}
[email protected]863e6d962011-05-15 19:39:3569
twellingtona71414d2015-03-26 15:09:4570ImageDecoder::ImageRequest::~ImageRequest() {
thestigc5eeb9f362015-04-22 18:42:3271 DCHECK(sequence_checker_.CalledOnValidSequencedThread());
twellingtona71414d2015-03-26 15:09:4572 ImageDecoder::Cancel(this);
73}
74
75// static
76void ImageDecoder::Start(ImageRequest* image_request,
77 const std::string& image_data) {
78 StartWithOptions(image_request, image_data, DEFAULT_CODEC, false);
79}
80
81// static
82void ImageDecoder::StartWithOptions(ImageRequest* image_request,
83 const std::string& image_data,
84 ImageCodec image_codec,
85 bool shrink_to_fit) {
thestigc5eeb9f362015-04-22 18:42:3286 g_decoder.Pointer()->StartWithOptionsImpl(image_request, image_data,
87 image_codec, shrink_to_fit);
88}
89
90void ImageDecoder::StartWithOptionsImpl(ImageRequest* image_request,
91 const std::string& image_data,
92 ImageCodec image_codec,
93 bool shrink_to_fit) {
twellingtona71414d2015-03-26 15:09:4594 DCHECK(image_request);
95 DCHECK(image_request->task_runner());
thestigc5eeb9f362015-04-22 18:42:3296
97 int request_id;
98 {
99 base::AutoLock lock(map_lock_);
100 request_id = image_request_id_counter_++;
101 image_request_id_map_.insert(std::make_pair(request_id, image_request));
102 }
103
[email protected]cca169b52010-10-08 22:15:55104 BrowserThread::PostTask(
twellingtona71414d2015-03-26 15:09:45105 BrowserThread::IO, FROM_HERE,
106 base::Bind(
107 &ImageDecoder::DecodeImageInSandbox,
thestigc5eeb9f362015-04-22 18:42:32108 g_decoder.Pointer(), request_id,
twellingtona71414d2015-03-26 15:09:45109 std::vector<unsigned char>(image_data.begin(), image_data.end()),
110 image_codec, shrink_to_fit));
[email protected]551707a2010-06-16 16:59:47111}
112
twellingtona71414d2015-03-26 15:09:45113// static
114void ImageDecoder::Cancel(ImageRequest* image_request) {
115 DCHECK(image_request);
116 g_decoder.Pointer()->CancelImpl(image_request);
117}
118
119void ImageDecoder::DecodeImageInSandbox(
thestigc5eeb9f362015-04-22 18:42:32120 int request_id,
twellingtona71414d2015-03-26 15:09:45121 const std::vector<unsigned char>& image_data,
122 ImageCodec image_codec,
123 bool shrink_to_fit) {
124 DCHECK_CURRENTLY_ON(BrowserThread::IO);
thestigc5eeb9f362015-04-22 18:42:32125 base::AutoLock lock(map_lock_);
126 const auto it = image_request_id_map_.find(request_id);
127 if (it == image_request_id_map_.end())
128 return;
129
130 ImageRequest* image_request = it->second;
twellingtona71414d2015-03-26 15:09:45131 if (!utility_process_host_) {
132 StartBatchMode();
133 }
twellington612b9b62015-04-01 00:33:12134 if (!utility_process_host_) {
135 // Utility process failed to start; notify delegate and return.
136 // Without this check, we were seeing crashes on startup. Further
137 // investigation is needed to determine why the utility process
138 // is failing to start. See crbug.com/472272
139 image_request->task_runner()->PostTask(
thestigc5eeb9f362015-04-22 18:42:32140 FROM_HERE,
141 base::Bind(&ImageDecoder::RunOnDecodeImageFailed, this, request_id));
twellington612b9b62015-04-01 00:33:12142 return;
143 }
twellingtona71414d2015-03-26 15:09:45144
thestigd3485b692015-04-30 09:47:32145 if (!batch_mode_timer_) {
146 // Created here so it will call StopBatchMode() on the right thread.
danakj8c3eb802015-09-24 07:53:00147 batch_mode_timer_.reset(new base::DelayTimer(
148 FROM_HERE, base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds), this,
thestigd3485b692015-04-30 09:47:32149 &ImageDecoder::StopBatchMode));
150 }
151 batch_mode_timer_->Reset();
twellingtona71414d2015-03-26 15:09:45152
amistry6f72f042016-04-05 20:42:07153 mojom::ImageCodec mojo_codec = mojom::ImageCodec::DEFAULT;
edward.baker199f66b2015-05-07 19:13:30154#if defined(OS_CHROMEOS)
amistry6f72f042016-04-05 20:42:07155 if (image_codec == ROBUST_JPEG_CODEC)
156 mojo_codec = mojom::ImageCodec::ROBUST_JPEG;
157 if (image_codec == ROBUST_PNG_CODEC)
158 mojo_codec = mojom::ImageCodec::ROBUST_PNG;
edward.baker199f66b2015-05-07 19:13:30159#endif // defined(OS_CHROMEOS)
amistry6f72f042016-04-05 20:42:07160 decoder_->DecodeImage(
161 mojo::Array<uint8_t>::From(image_data),
162 mojo_codec,
163 shrink_to_fit,
164 base::Bind(&OnDecodeImageDone,
165 base::Bind(&ImageDecoder::OnDecodeImageFailed, this),
166 base::Bind(&ImageDecoder::OnDecodeImageSucceeded, this),
167 request_id));
twellingtona71414d2015-03-26 15:09:45168}
169
170void ImageDecoder::CancelImpl(ImageRequest* image_request) {
171 base::AutoLock lock(map_lock_);
172 for (auto it = image_request_id_map_.begin();
173 it != image_request_id_map_.end();) {
174 if (it->second == image_request) {
175 image_request_id_map_.erase(it++);
176 } else {
177 ++it;
178 }
179 }
180}
181
182void ImageDecoder::StartBatchMode() {
183 DCHECK_CURRENTLY_ON(BrowserThread::IO);
184 utility_process_host_ =
pranay.kumar6a179252015-05-05 11:35:15185 UtilityProcessHost::Create(
186 this, base::ThreadTaskRunnerHandle::Get().get())->AsWeakPtr();
thestige3d6b752015-04-07 21:58:45187 utility_process_host_->SetName(l10n_util::GetStringUTF16(
188 IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME));
amistry6f72f042016-04-05 20:42:07189 if (!utility_process_host_->Start()) {
190 delete utility_process_host_.get();
191 return;
twellington612b9b62015-04-01 00:33:12192 }
amistry6f72f042016-04-05 20:42:07193 content::ServiceRegistry* service_registry =
194 utility_process_host_->GetServiceRegistry();
195 service_registry->ConnectToRemoteService(mojo::GetProxy(&decoder_));
twellingtona71414d2015-03-26 15:09:45196}
197
198void ImageDecoder::StopBatchMode() {
199 DCHECK_CURRENTLY_ON(BrowserThread::IO);
thestig3d8ebe3a2015-05-08 03:25:32200 {
201 // Check for outstanding requests and wait for them to finish.
202 base::AutoLock lock(map_lock_);
203 if (!image_request_id_map_.empty()) {
204 batch_mode_timer_->Reset();
205 return;
206 }
207 }
208
twellingtona71414d2015-03-26 15:09:45209 if (utility_process_host_) {
amistry6f72f042016-04-05 20:42:07210 // With Mojo, the utility process needs to be explicitly shut down by
211 // deleting the host.
212 delete utility_process_host_.get();
213 decoder_.reset();
twellingtona71414d2015-03-26 15:09:45214 utility_process_host_.reset();
215 }
amistryd59fe6b2015-04-30 23:50:30216}
217
218void ImageDecoder::FailAllRequests() {
219 RequestMap requests;
220 {
221 base::AutoLock lock(map_lock_);
222 requests = image_request_id_map_;
223 }
224
225 // Since |OnProcessCrashed| and |OnProcessLaunchFailed| are run asynchronously
226 // from the actual event, it's possible for a new utility process to have been
227 // created and sent requests by the time these functions are run. This results
228 // in failing requests that are unaffected by the crash. Although not ideal,
229 // this is valid and simpler than tracking which request is sent to which
230 // utility process, and whether the request has been sent at all.
231 for (const auto& request : requests)
232 OnDecodeImageFailed(request.first);
233}
234
235void ImageDecoder::OnProcessCrashed(int exit_code) {
236 DCHECK_CURRENTLY_ON(BrowserThread::IO);
237 FailAllRequests();
238}
239
240void ImageDecoder::OnProcessLaunchFailed() {
241 DCHECK_CURRENTLY_ON(BrowserThread::IO);
242 FailAllRequests();
twellingtona71414d2015-03-26 15:09:45243}
244
amistry6f72f042016-04-05 20:42:07245bool ImageDecoder::OnMessageReceived(const IPC::Message& message) {
246 return false;
[email protected]373c1062011-06-09 21:11:51247}
248
twellingtona71414d2015-03-26 15:09:45249void ImageDecoder::OnDecodeImageSucceeded(
250 const SkBitmap& decoded_image,
251 int request_id) {
252 DCHECK_CURRENTLY_ON(BrowserThread::IO);
253 base::AutoLock lock(map_lock_);
254 auto it = image_request_id_map_.find(request_id);
thestigc5eeb9f362015-04-22 18:42:32255 if (it == image_request_id_map_.end())
256 return;
twellingtona71414d2015-03-26 15:09:45257
thestigc5eeb9f362015-04-22 18:42:32258 ImageRequest* image_request = it->second;
259 image_request->task_runner()->PostTask(
260 FROM_HERE,
261 base::Bind(&ImageDecoder::RunOnImageDecoded,
262 this,
263 decoded_image,
264 request_id));
[email protected]928c0e712011-03-16 15:01:57265}
266
twellingtona71414d2015-03-26 15:09:45267void ImageDecoder::OnDecodeImageFailed(int request_id) {
268 DCHECK_CURRENTLY_ON(BrowserThread::IO);
269 base::AutoLock lock(map_lock_);
270 auto it = image_request_id_map_.find(request_id);
thestigc5eeb9f362015-04-22 18:42:32271 if (it == image_request_id_map_.end())
272 return;
[email protected]551707a2010-06-16 16:59:47273
thestigc5eeb9f362015-04-22 18:42:32274 ImageRequest* image_request = it->second;
275 image_request->task_runner()->PostTask(
276 FROM_HERE,
277 base::Bind(&ImageDecoder::RunOnDecodeImageFailed, this, request_id));
278}
279
280void ImageDecoder::RunOnImageDecoded(const SkBitmap& decoded_image,
281 int request_id) {
282 ImageRequest* image_request;
283 {
284 base::AutoLock lock(map_lock_);
285 auto it = image_request_id_map_.find(request_id);
286 if (it == image_request_id_map_.end())
287 return;
288 image_request = it->second;
twellingtona71414d2015-03-26 15:09:45289 image_request_id_map_.erase(it);
[email protected]11f16d102012-08-29 23:12:15290 }
thestigc5eeb9f362015-04-22 18:42:32291
292 DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread());
293 image_request->OnImageDecoded(decoded_image);
294}
295
296void ImageDecoder::RunOnDecodeImageFailed(int request_id) {
297 ImageRequest* image_request;
298 {
299 base::AutoLock lock(map_lock_);
300 auto it = image_request_id_map_.find(request_id);
301 if (it == image_request_id_map_.end())
302 return;
303 image_request = it->second;
304 image_request_id_map_.erase(it);
305 }
306
307 DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread());
308 image_request->OnDecodeImageFailed();
[email protected]551707a2010-06-16 16:59:47309}