Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(406)

Side by Side Diff: chrome/browser/image_decoder.cc

Issue 2475543003: Introduce the image_decoder service (Closed)
Patch Set: . Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/image_decoder.h ('k') | chrome/browser/search/suggestions/image_decoder_impl.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/image_decoder.h" 5 #include "chrome/browser/image_decoder.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/lazy_instance.h"
10 #include "base/threading/thread_task_runner_handle.h" 12 #include "base/threading/thread_task_runner_handle.h"
11 #include "build/build_config.h" 13 #include "build/build_config.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/common/image_decoder.mojom.h"
14 #include "chrome/grit/generated_resources.h"
15 #include "content/public/browser/browser_thread.h" 14 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/utility_process_host.h" 15 #include "content/public/common/service_manager_connection.h"
17 #include "services/service_manager/public/cpp/interface_provider.h" 16 #include "ipc/ipc_channel.h"
17 #include "services/image_decoder/public/cpp/decode.h"
18 #include "services/service_manager/public/cpp/connector.h"
18 #include "third_party/skia/include/core/SkBitmap.h" 19 #include "third_party/skia/include/core/SkBitmap.h"
19 #include "ui/base/l10n/l10n_util.h"
20
21 using content::BrowserThread;
22 using content::UtilityProcessHost;
23 20
24 namespace { 21 namespace {
25 22
26 // static, Leaky to allow access from any thread. 23 // static, Leaky to allow access from any thread.
27 base::LazyInstance<ImageDecoder>::Leaky g_decoder = LAZY_INSTANCE_INITIALIZER; 24 base::LazyInstance<ImageDecoder>::Leaky g_decoder = LAZY_INSTANCE_INITIALIZER;
28 25
29 // How long to wait after the last request has been received before ending 26 const int64_t kMaxImageSizeInBytes =
30 // batch mode. 27 static_cast<int64_t>(IPC::Channel::kMaximumMessageSize);
31 const int kBatchModeTimeoutSeconds = 5;
32 28
29 // Note that this is always called on the thread which initiated the
30 // corresponding image_decoder::Decode request.
33 void OnDecodeImageDone( 31 void OnDecodeImageDone(
34 base::Callback<void(int)> fail_callback, 32 base::Callback<void(int)> fail_callback,
35 base::Callback<void(const SkBitmap&, int)> success_callback, 33 base::Callback<void(const SkBitmap&, int)> success_callback,
36 int request_id, 34 int request_id,
37 const SkBitmap& image) { 35 const SkBitmap& image) {
38 DCHECK_CURRENTLY_ON(BrowserThread::IO);
39 if (!image.isNull() && !image.empty()) 36 if (!image.isNull() && !image.empty())
40 success_callback.Run(image, request_id); 37 success_callback.Run(image, request_id);
41 else 38 else
42 fail_callback.Run(request_id); 39 fail_callback.Run(request_id);
43 } 40 }
44 41
45 } // namespace 42 void BindToBrowserConnector(service_manager::mojom::ConnectorRequest request) {
43 if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
44 content::BrowserThread::PostTask(
45 content::BrowserThread::UI, FROM_HERE,
46 base::Bind(&BindToBrowserConnector, base::Passed(&request)));
47 return;
48 }
46 49
47 ImageDecoder::ImageDecoder() 50 content::ServiceManagerConnection::GetForProcess()->GetConnector()
48 : image_request_id_counter_(0) { 51 ->BindRequest(std::move(request));
49 // A single ImageDecoder instance should live for the life of the program.
50 // Explicitly add a reference so the object isn't deleted.
51 AddRef();
52 } 52 }
53 53
54 ImageDecoder::~ImageDecoder() { 54 void RunDecodeCallbackOnTaskRunner(
55 const image_decoder::mojom::ImageDecoder::DecodeImageCallback& callback,
56 scoped_refptr<base::SequencedTaskRunner> task_runner,
57 const SkBitmap& image) {
58 task_runner->PostTask(FROM_HERE, base::Bind(callback, image));
55 } 59 }
56 60
61 void DecodeImage(
62 std::vector<uint8_t> image_data,
63 image_decoder::mojom::ImageCodec codec,
64 bool shrink_to_fit,
65 const image_decoder::mojom::ImageDecoder::DecodeImageCallback& callback,
66 scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
67 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
68
69 service_manager::mojom::ConnectorRequest connector_request;
70 std::unique_ptr<service_manager::Connector> connector =
71 service_manager::Connector::Create(&connector_request);
72 BindToBrowserConnector(std::move(connector_request));
73
74 image_decoder::Decode(connector.get(), image_data, codec, shrink_to_fit,
75 kMaxImageSizeInBytes,
76 base::Bind(&RunDecodeCallbackOnTaskRunner,
77 callback, callback_task_runner));
78 }
79
80 } // namespace
81
57 ImageDecoder::ImageRequest::ImageRequest() 82 ImageDecoder::ImageRequest::ImageRequest()
58 : task_runner_(base::ThreadTaskRunnerHandle::Get()) { 83 : task_runner_(base::ThreadTaskRunnerHandle::Get()) {
59 DCHECK(sequence_checker_.CalledOnValidSequence()); 84 DCHECK(sequence_checker_.CalledOnValidSequence());
60 } 85 }
61 86
62 ImageDecoder::ImageRequest::ImageRequest( 87 ImageDecoder::ImageRequest::ImageRequest(
63 const scoped_refptr<base::SequencedTaskRunner>& task_runner) 88 const scoped_refptr<base::SequencedTaskRunner>& task_runner)
64 : task_runner_(task_runner) { 89 : task_runner_(task_runner) {
65 DCHECK(sequence_checker_.CalledOnValidSequence()); 90 DCHECK(sequence_checker_.CalledOnValidSequence());
66 } 91 }
67 92
68 ImageDecoder::ImageRequest::~ImageRequest() { 93 ImageDecoder::ImageRequest::~ImageRequest() {
69 DCHECK(sequence_checker_.CalledOnValidSequence()); 94 DCHECK(sequence_checker_.CalledOnValidSequence());
70 ImageDecoder::Cancel(this); 95 ImageDecoder::Cancel(this);
71 } 96 }
72 97
98 ImageDecoder::ImageDecoder() : image_request_id_counter_(0) {}
99
100 ImageDecoder::~ImageDecoder() {}
101
73 // static 102 // static
74 void ImageDecoder::Start(ImageRequest* image_request, 103 void ImageDecoder::Start(ImageRequest* image_request,
75 std::vector<uint8_t> image_data) { 104 std::vector<uint8_t> image_data) {
76 StartWithOptions(image_request, std::move(image_data), DEFAULT_CODEC, false); 105 StartWithOptions(image_request, std::move(image_data), DEFAULT_CODEC, false);
77 } 106 }
78 107
79 // static 108 // static
80 void ImageDecoder::Start(ImageRequest* image_request, 109 void ImageDecoder::Start(ImageRequest* image_request,
81 const std::string& image_data) { 110 const std::string& image_data) {
82 Start(image_request, 111 Start(image_request,
83 std::vector<uint8_t>(image_data.begin(), image_data.end())); 112 std::vector<uint8_t>(image_data.begin(), image_data.end()));
84 } 113 }
85 114
86 // static 115 // static
87 void ImageDecoder::StartWithOptions(ImageRequest* image_request, 116 void ImageDecoder::StartWithOptions(ImageRequest* image_request,
88 std::vector<uint8_t> image_data, 117 std::vector<uint8_t> image_data,
89 ImageCodec image_codec, 118 ImageCodec image_codec,
90 bool shrink_to_fit) { 119 bool shrink_to_fit) {
91 g_decoder.Pointer()->StartWithOptionsImpl(image_request, 120 g_decoder.Get().StartWithOptionsImpl(image_request, std::move(image_data),
92 std::move(image_data), 121 image_codec, shrink_to_fit);
93 image_codec, shrink_to_fit);
94 } 122 }
95 123
96 // static 124 // static
97 void ImageDecoder::StartWithOptions(ImageRequest* image_request, 125 void ImageDecoder::StartWithOptions(ImageRequest* image_request,
98 const std::string& image_data, 126 const std::string& image_data,
99 ImageCodec image_codec, 127 ImageCodec image_codec,
100 bool shrink_to_fit) { 128 bool shrink_to_fit) {
101 StartWithOptions(image_request, 129 StartWithOptions(image_request,
102 std::vector<uint8_t>(image_data.begin(), image_data.end()), 130 std::vector<uint8_t>(image_data.begin(), image_data.end()),
103 image_codec, shrink_to_fit); 131 image_codec, shrink_to_fit);
104 } 132 }
105 133
106 void ImageDecoder::StartWithOptionsImpl(ImageRequest* image_request, 134 void ImageDecoder::StartWithOptionsImpl(ImageRequest* image_request,
107 std::vector<uint8_t> image_data, 135 std::vector<uint8_t> image_data,
108 ImageCodec image_codec, 136 ImageCodec image_codec,
109 bool shrink_to_fit) { 137 bool shrink_to_fit) {
110 DCHECK(image_request); 138 DCHECK(image_request);
111 DCHECK(image_request->task_runner()); 139 DCHECK(image_request->task_runner());
112 140
113 int request_id; 141 int request_id;
114 { 142 {
115 base::AutoLock lock(map_lock_); 143 base::AutoLock lock(map_lock_);
116 request_id = image_request_id_counter_++; 144 request_id = image_request_id_counter_++;
117 image_request_id_map_.insert(std::make_pair(request_id, image_request)); 145 image_request_id_map_.insert(std::make_pair(request_id, image_request));
118 } 146 }
119 147
120 BrowserThread::PostTask( 148 image_decoder::mojom::ImageCodec codec =
121 BrowserThread::IO, FROM_HERE, 149 image_decoder::mojom::ImageCodec::DEFAULT;
122 base::Bind( 150 #if defined(OS_CHROMEOS)
123 &ImageDecoder::DecodeImageInSandbox, 151 if (image_codec == ROBUST_JPEG_CODEC)
124 g_decoder.Pointer(), request_id, 152 codec = image_decoder::mojom::ImageCodec::ROBUST_JPEG;
125 base::Passed(std::move(image_data)), 153 if (image_codec == ROBUST_PNG_CODEC)
126 image_codec, shrink_to_fit)); 154 codec = image_decoder::mojom::ImageCodec::ROBUST_PNG;
155 #endif // defined(OS_CHROMEOS)
156
157 auto callback = base::Bind(
158 &OnDecodeImageDone,
159 base::Bind(&ImageDecoder::OnDecodeImageFailed, base::Unretained(this)),
160 base::Bind(&ImageDecoder::OnDecodeImageSucceeded, base::Unretained(this)),
161 request_id);
162
163 // NOTE: There exist ImageDecoder consumers which implicitly rely on this
164 // operation happening on a thread which always has a ThreadTaskRunnerHandle.
165 // We arbitrarily use the IO thread here to match details of the legacy
166 // implementation.
167 content::BrowserThread::PostTask(
168 content::BrowserThread::IO, FROM_HERE,
169 base::Bind(&DecodeImage, base::Passed(&image_data), codec, shrink_to_fit,
170 callback, make_scoped_refptr(image_request->task_runner())));
127 } 171 }
128 172
129 // static 173 // static
130 void ImageDecoder::Cancel(ImageRequest* image_request) { 174 void ImageDecoder::Cancel(ImageRequest* image_request) {
131 DCHECK(image_request); 175 DCHECK(image_request);
132 g_decoder.Pointer()->CancelImpl(image_request); 176 g_decoder.Get().CancelImpl(image_request);
133 }
134
135 void ImageDecoder::DecodeImageInSandbox(
136 int request_id,
137 std::vector<uint8_t> image_data,
138 ImageCodec image_codec,
139 bool shrink_to_fit) {
140 DCHECK_CURRENTLY_ON(BrowserThread::IO);
141 base::AutoLock lock(map_lock_);
142 const auto it = image_request_id_map_.find(request_id);
143 if (it == image_request_id_map_.end())
144 return;
145
146 ImageRequest* image_request = it->second;
147 if (!utility_process_host_) {
148 StartBatchMode();
149 }
150 if (!utility_process_host_) {
151 // Utility process failed to start; notify delegate and return.
152 // Without this check, we were seeing crashes on startup. Further
153 // investigation is needed to determine why the utility process
154 // is failing to start. See crbug.com/472272
155 image_request->task_runner()->PostTask(
156 FROM_HERE,
157 base::Bind(&ImageDecoder::RunOnDecodeImageFailed, this, request_id));
158 return;
159 }
160
161 if (!batch_mode_timer_) {
162 // Created here so it will call StopBatchMode() on the right thread.
163 batch_mode_timer_.reset(new base::DelayTimer(
164 FROM_HERE, base::TimeDelta::FromSeconds(kBatchModeTimeoutSeconds), this,
165 &ImageDecoder::StopBatchMode));
166 }
167 batch_mode_timer_->Reset();
168
169 mojom::ImageCodec mojo_codec = mojom::ImageCodec::DEFAULT;
170 #if defined(OS_CHROMEOS)
171 if (image_codec == ROBUST_JPEG_CODEC)
172 mojo_codec = mojom::ImageCodec::ROBUST_JPEG;
173 if (image_codec == ROBUST_PNG_CODEC)
174 mojo_codec = mojom::ImageCodec::ROBUST_PNG;
175 #endif // defined(OS_CHROMEOS)
176 decoder_->DecodeImage(
177 image_data, mojo_codec, shrink_to_fit,
178 base::Bind(&OnDecodeImageDone,
179 base::Bind(&ImageDecoder::OnDecodeImageFailed, this),
180 base::Bind(&ImageDecoder::OnDecodeImageSucceeded, this),
181 request_id));
182 } 177 }
183 178
184 void ImageDecoder::CancelImpl(ImageRequest* image_request) { 179 void ImageDecoder::CancelImpl(ImageRequest* image_request) {
185 base::AutoLock lock(map_lock_); 180 base::AutoLock lock(map_lock_);
186 for (auto it = image_request_id_map_.begin(); 181 for (auto it = image_request_id_map_.begin();
187 it != image_request_id_map_.end();) { 182 it != image_request_id_map_.end();) {
188 if (it->second == image_request) { 183 if (it->second == image_request) {
189 image_request_id_map_.erase(it++); 184 image_request_id_map_.erase(it++);
190 } else { 185 } else {
191 ++it; 186 ++it;
192 } 187 }
193 } 188 }
194 } 189 }
195 190
196 void ImageDecoder::StartBatchMode() {
197 DCHECK_CURRENTLY_ON(BrowserThread::IO);
198 utility_process_host_ =
199 UtilityProcessHost::Create(
200 this, base::ThreadTaskRunnerHandle::Get().get())->AsWeakPtr();
201 utility_process_host_->SetName(l10n_util::GetStringUTF16(
202 IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME));
203 if (!utility_process_host_->Start()) {
204 delete utility_process_host_.get();
205 return;
206 }
207 utility_process_host_->GetRemoteInterfaces()->GetInterface(&decoder_);
208 }
209
210 void ImageDecoder::StopBatchMode() {
211 DCHECK_CURRENTLY_ON(BrowserThread::IO);
212 {
213 // Check for outstanding requests and wait for them to finish.
214 base::AutoLock lock(map_lock_);
215 if (!image_request_id_map_.empty()) {
216 batch_mode_timer_->Reset();
217 return;
218 }
219 }
220
221 if (utility_process_host_) {
222 // With Mojo, the utility process needs to be explicitly shut down by
223 // deleting the host.
224 delete utility_process_host_.get();
225 decoder_.reset();
226 utility_process_host_.reset();
227 }
228 }
229
230 void ImageDecoder::FailAllRequests() {
231 RequestMap requests;
232 {
233 base::AutoLock lock(map_lock_);
234 requests = image_request_id_map_;
235 }
236
237 // Since |OnProcessCrashed| and |OnProcessLaunchFailed| are run asynchronously
238 // from the actual event, it's possible for a new utility process to have been
239 // created and sent requests by the time these functions are run. This results
240 // in failing requests that are unaffected by the crash. Although not ideal,
241 // this is valid and simpler than tracking which request is sent to which
242 // utility process, and whether the request has been sent at all.
243 for (const auto& request : requests)
244 OnDecodeImageFailed(request.first);
245 }
246
247 void ImageDecoder::OnProcessCrashed(int exit_code) {
248 DCHECK_CURRENTLY_ON(BrowserThread::IO);
249 FailAllRequests();
250 }
251
252 void ImageDecoder::OnProcessLaunchFailed(int error_code) {
253 DCHECK_CURRENTLY_ON(BrowserThread::IO);
254 FailAllRequests();
255 }
256
257 bool ImageDecoder::OnMessageReceived(const IPC::Message& message) {
258 return false;
259 }
260
261 void ImageDecoder::OnDecodeImageSucceeded( 191 void ImageDecoder::OnDecodeImageSucceeded(
262 const SkBitmap& decoded_image, 192 const SkBitmap& decoded_image,
263 int request_id) { 193 int request_id) {
264 DCHECK_CURRENTLY_ON(BrowserThread::IO);
265 base::AutoLock lock(map_lock_);
266 auto it = image_request_id_map_.find(request_id);
267 if (it == image_request_id_map_.end())
268 return;
269
270 ImageRequest* image_request = it->second;
271 image_request->task_runner()->PostTask(
272 FROM_HERE,
273 base::Bind(&ImageDecoder::RunOnImageDecoded,
274 this,
275 decoded_image,
276 request_id));
277 }
278
279 void ImageDecoder::OnDecodeImageFailed(int request_id) {
280 DCHECK_CURRENTLY_ON(BrowserThread::IO);
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
286 ImageRequest* image_request = it->second;
287 image_request->task_runner()->PostTask(
288 FROM_HERE,
289 base::Bind(&ImageDecoder::RunOnDecodeImageFailed, this, request_id));
290 }
291
292 void ImageDecoder::RunOnImageDecoded(const SkBitmap& decoded_image,
293 int request_id) {
294 ImageRequest* image_request; 194 ImageRequest* image_request;
295 { 195 {
296 base::AutoLock lock(map_lock_); 196 base::AutoLock lock(map_lock_);
297 auto it = image_request_id_map_.find(request_id); 197 auto it = image_request_id_map_.find(request_id);
298 if (it == image_request_id_map_.end()) 198 if (it == image_request_id_map_.end())
299 return; 199 return;
300 image_request = it->second; 200 image_request = it->second;
301 image_request_id_map_.erase(it); 201 image_request_id_map_.erase(it);
302 } 202 }
303 203
304 DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread()); 204 DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread());
305 image_request->OnImageDecoded(decoded_image); 205 image_request->OnImageDecoded(decoded_image);
306 } 206 }
307 207
308 void ImageDecoder::RunOnDecodeImageFailed(int request_id) { 208 void ImageDecoder::OnDecodeImageFailed(int request_id) {
309 ImageRequest* image_request; 209 ImageRequest* image_request;
310 { 210 {
311 base::AutoLock lock(map_lock_); 211 base::AutoLock lock(map_lock_);
312 auto it = image_request_id_map_.find(request_id); 212 auto it = image_request_id_map_.find(request_id);
313 if (it == image_request_id_map_.end()) 213 if (it == image_request_id_map_.end())
314 return; 214 return;
315 image_request = it->second; 215 image_request = it->second;
316 image_request_id_map_.erase(it); 216 image_request_id_map_.erase(it);
317 } 217 }
318 218
319 DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread()); 219 DCHECK(image_request->task_runner()->RunsTasksOnCurrentThread());
320 image_request->OnDecodeImageFailed(); 220 image_request->OnDecodeImageFailed();
321 } 221 }
OLDNEW
« no previous file with comments | « chrome/browser/image_decoder.h ('k') | chrome/browser/search/suggestions/image_decoder_impl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698