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

Side by Side Diff: chrome/browser/extensions/image_loading_tracker.cc

Issue 12213093: Remove ImageLoadingTracker class (Closed) Base URL: https://git.chromium.org/chromium/src.git@Issue_163929
Patch Set: Fix merge conflict Created 7 years, 10 months 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
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/extensions/image_loading_tracker.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/file_util.h"
12 #include "base/threading/sequenced_worker_pool.h"
13 #include "chrome/browser/extensions/image_loader.h"
14 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
15 #include "chrome/common/chrome_notification_types.h"
16 #include "chrome/common/extensions/extension.h"
17 #include "chrome/common/extensions/extension_constants.h"
18 #include "chrome/common/extensions/extension_file_util.h"
19 #include "chrome/common/extensions/extension_resource.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/notification_service.h"
22 #include "skia/ext/image_operations.h"
23 #include "third_party/skia/include/core/SkBitmap.h"
24 #include "ui/base/resource/resource_bundle.h"
25 #include "ui/gfx/image/image.h"
26 #include "ui/gfx/image/image_skia_rep.h"
27 #include "webkit/glue/image_decoder.h"
28
29 using content::BrowserThread;
30 using extensions::Extension;
31 using extensions::Manifest;
32
33 namespace {
34
35 bool ShouldResizeImageRepresentation(
36 ImageLoadingTracker::ImageRepresentation::ResizeCondition resize_method,
37 const gfx::Size& decoded_size,
38 const gfx::Size& desired_size) {
39 switch (resize_method) {
40 case ImageLoadingTracker::ImageRepresentation::ALWAYS_RESIZE:
41 return decoded_size != desired_size;
42 case ImageLoadingTracker::ImageRepresentation::RESIZE_WHEN_LARGER:
43 return decoded_size.width() > desired_size.width() ||
44 decoded_size.height() > desired_size.height();
45 default:
46 NOTREACHED();
47 return false;
48 }
49 }
50
51 } // namespace
52
53 ////////////////////////////////////////////////////////////////////////////////
54 // ImageLoadingTracker::Observer
55
56 ImageLoadingTracker::Observer::~Observer() {}
57
58 ////////////////////////////////////////////////////////////////////////////////
59 // ImageLoadingTracker::ImageRepresentation
60
61 ImageLoadingTracker::ImageRepresentation::ImageRepresentation(
62 const ExtensionResource& resource,
63 ResizeCondition resize_method,
64 const gfx::Size& desired_size,
65 ui::ScaleFactor scale_factor)
66 : resource(resource),
67 resize_method(resize_method),
68 desired_size(desired_size),
69 scale_factor(scale_factor) {
70 }
71
72 ImageLoadingTracker::ImageRepresentation::~ImageRepresentation() {
73 }
74
75 ////////////////////////////////////////////////////////////////////////////////
76 // ImageLoadingTracker::PendingLoadInfo
77
78 ImageLoadingTracker::PendingLoadInfo::PendingLoadInfo()
79 : extension(NULL),
80 cache(CACHE),
81 pending_count(0) {
82 }
83
84 ImageLoadingTracker::PendingLoadInfo::~PendingLoadInfo() {}
85
86 ////////////////////////////////////////////////////////////////////////////////
87 // ImageLoadingTracker::ImageLoader
88
89 // A RefCounted class for loading bitmaps/image reps on the File thread and
90 // reporting back on the UI thread.
91 class ImageLoadingTracker::ImageLoader
92 : public base::RefCountedThreadSafe<ImageLoader> {
93 public:
94 explicit ImageLoader(ImageLoadingTracker* tracker)
95 : tracker_(tracker) {
96 CHECK(BrowserThread::GetCurrentThreadIdentifier(&callback_thread_id_));
97 DCHECK(!BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
98 }
99
100 // Lets this class know that the tracker is no longer interested in the
101 // results.
102 void StopTracking() {
103 tracker_ = NULL;
104 }
105
106 // Instructs the loader to load a task on the blocking pool.
107 void LoadImage(const ImageRepresentation& image_info, int id) {
108 DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
109 BrowserThread::PostBlockingPoolTask(
110 FROM_HERE,
111 base::Bind(&ImageLoader::LoadOnBlockingPool, this, image_info, id));
112 }
113
114 void LoadOnBlockingPool(const ImageRepresentation& image_info, int id) {
115 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
116
117 // Read the file from disk.
118 std::string file_contents;
119 base::FilePath path = image_info.resource.GetFilePath();
120 if (path.empty() || !file_util::ReadFileToString(path, &file_contents)) {
121 ReportBack(NULL, image_info, gfx::Size(), id);
122 return;
123 }
124
125 // Decode the bitmap using WebKit's image decoder.
126 const unsigned char* data =
127 reinterpret_cast<const unsigned char*>(file_contents.data());
128 webkit_glue::ImageDecoder decoder;
129 scoped_ptr<SkBitmap> decoded(new SkBitmap());
130 // Note: This class only decodes bitmaps from extension resources. Chrome
131 // doesn't (for security reasons) directly load extension resources provided
132 // by the extension author, but instead decodes them in a separate
133 // locked-down utility process. Only if the decoding succeeds is the image
134 // saved from memory to disk and subsequently used in the Chrome UI.
135 // Chrome is therefore decoding bitmaps here that were generated by Chrome.
136 *decoded = decoder.Decode(data, file_contents.length());
137 if (decoded->empty()) {
138 ReportBack(NULL, image_info, gfx::Size(), id);
139 return; // Unable to decode.
140 }
141
142 gfx::Size original_size(decoded->width(), decoded->height());
143 *decoded = ResizeIfNeeded(*decoded, image_info);
144
145 ReportBack(decoded.release(), image_info, original_size, id);
146 }
147
148 // Instructs the loader to load a resource on the UI thread.
149 void LoadResource(const ImageRepresentation& image_info,
150 int id,
151 int resource_id) {
152 DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
153
154 if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
155 LoadResourceOnUIThread(image_info, id, resource_id);
156 return;
157 }
158
159 BrowserThread::PostTask(
160 BrowserThread::UI, FROM_HERE,
161 base::Bind(&ImageLoader::LoadResourceOnUIThread, this, image_info,
162 id, resource_id));
163 }
164
165 void LoadResourceOnUIThread(const ImageRepresentation& image_info,
166 int id,
167 int resource_id) {
168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
169
170 // Bundled image resources is only safe to be loaded on UI thread.
171 gfx::ImageSkia* image =
172 ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id);
173 image->MakeThreadSafe();
174
175 BrowserThread::PostBlockingPoolTask(
176 FROM_HERE,
177 base::Bind(&ImageLoader::ResizeOnBlockingPool, this, image_info,
178 id, *image));
179 }
180
181 void ResizeOnBlockingPool(const ImageRepresentation& image_info,
182 int id,
183 const gfx::ImageSkia& image) {
184 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
185 // TODO(xiyuan): Clean up to use SkBitmap here and in LoadOnBlockingPool.
186 scoped_ptr<SkBitmap> bitmap(new SkBitmap);
187 gfx::Size original_size(image.width(), image.height());
188 *bitmap = ResizeIfNeeded(*image.bitmap(), image_info);
189 ReportBack(bitmap.release(), image_info, original_size, id);
190 }
191
192 void ReportBack(const SkBitmap* bitmap, const ImageRepresentation& image_info,
193 const gfx::Size& original_size, int id) {
194 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
195
196 BrowserThread::PostTask(
197 callback_thread_id_, FROM_HERE,
198 base::Bind(&ImageLoader::ReportOnCallingThread, this,
199 bitmap, image_info, original_size, id));
200 }
201
202 void ReportOnCallingThread(const SkBitmap* bitmap,
203 const ImageRepresentation& image_info,
204 const gfx::Size& original_size,
205 int id) {
206 DCHECK(BrowserThread::CurrentlyOn(callback_thread_id_));
207
208 if (tracker_)
209 tracker_->OnBitmapLoaded(bitmap, image_info, original_size, id, true);
210
211 if (bitmap)
212 delete bitmap;
213 }
214
215 private:
216 friend class base::RefCountedThreadSafe<ImageLoader>;
217 ~ImageLoader() {}
218
219 SkBitmap ResizeIfNeeded(const SkBitmap& bitmap,
220 const ImageRepresentation& image_info) {
221 gfx::Size original_size(bitmap.width(), bitmap.height());
222 if (ShouldResizeImageRepresentation(image_info.resize_method,
223 original_size,
224 image_info.desired_size)) {
225 return skia::ImageOperations::Resize(
226 bitmap, skia::ImageOperations::RESIZE_LANCZOS3,
227 image_info.desired_size.width(), image_info.desired_size.height());
228 }
229
230 return bitmap;
231 }
232
233 // The tracker we are loading the bitmap for. If NULL, it means the tracker is
234 // no longer interested in the reply.
235 ImageLoadingTracker* tracker_;
236
237 // The thread that we need to call back on to report that we are done.
238 BrowserThread::ID callback_thread_id_;
239
240 DISALLOW_COPY_AND_ASSIGN(ImageLoader);
241 };
242
243 ////////////////////////////////////////////////////////////////////////////////
244 // ImageLoadingTracker
245
246 ImageLoadingTracker::ImageLoadingTracker(Observer* observer)
247 : observer_(observer),
248 next_id_(0) {
249 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
250 content::NotificationService::AllSources());
251 }
252
253 ImageLoadingTracker::~ImageLoadingTracker() {
254 // The loader is created lazily and is NULL if the tracker is destroyed before
255 // any valid image load tasks have been posted.
256 if (loader_)
257 loader_->StopTracking();
258 }
259
260 void ImageLoadingTracker::LoadImage(const Extension* extension,
261 const ExtensionResource& resource,
262 const gfx::Size& max_size,
263 CacheParam cache) {
264 std::vector<ImageRepresentation> info_list;
265 info_list.push_back(ImageRepresentation(
266 resource,
267 ImageRepresentation::RESIZE_WHEN_LARGER,
268 max_size,
269 ui::SCALE_FACTOR_100P));
270 LoadImages(extension, info_list, cache);
271 }
272
273 void ImageLoadingTracker::LoadImages(
274 const Extension* extension,
275 const std::vector<ImageRepresentation>& info_list,
276 CacheParam cache) {
277 PendingLoadInfo load_info;
278 load_info.extension = extension;
279 load_info.cache = cache;
280 load_info.extension_id = extension->id();
281 load_info.pending_count = info_list.size();
282 int id = next_id_++;
283 load_map_[id] = load_info;
284
285 for (std::vector<ImageRepresentation>::const_iterator it = info_list.begin();
286 it != info_list.end(); ++it) {
287 // If we don't have a path we don't need to do any further work, just
288 // respond back.
289 if (it->resource.relative_path().empty()) {
290 OnBitmapLoaded(NULL, *it, it->desired_size, id, false);
291 continue;
292 }
293
294 DCHECK(extension->path() == it->resource.extension_root());
295
296 // See if the extension has the bitmap already.
297 if (extension->HasCachedImage(it->resource, it->desired_size)) {
298 SkBitmap bitmap = extension->GetCachedImage(it->resource,
299 it->desired_size);
300 OnBitmapLoaded(&bitmap, *it, it->desired_size, id, false);
301 continue;
302 }
303
304 // Instruct the ImageLoader to load this on the File thread. LoadImage and
305 // LoadResource do not block.
306 if (!loader_)
307 loader_ = new ImageLoader(this);
308
309 int resource_id = -1;
310 if (extension->location() == Manifest::COMPONENT &&
311 extensions::ImageLoader::IsComponentExtensionResource(
312 extension->path(), it->resource.relative_path(), &resource_id)) {
313 loader_->LoadResource(*it, id, resource_id);
314 } else {
315 loader_->LoadImage(*it, id);
316 }
317 }
318 }
319
320 void ImageLoadingTracker::OnBitmapLoaded(
321 const SkBitmap* bitmap,
322 const ImageRepresentation& image_info,
323 const gfx::Size& original_size,
324 int id,
325 bool should_cache) {
326 LoadMap::iterator load_map_it = load_map_.find(id);
327 DCHECK(load_map_it != load_map_.end());
328 PendingLoadInfo* info = &load_map_it->second;
329
330 // Save the pending results.
331 DCHECK_GT(info->pending_count, 0u);
332 info->pending_count--;
333 if (bitmap) {
334 info->image_skia.AddRepresentation(gfx::ImageSkiaRep(*bitmap,
335 image_info.scale_factor));
336 }
337
338 // Add to the extension's bitmap cache if requested.
339 DCHECK(info->cache != CACHE || info->extension);
340 if (should_cache && info->cache == CACHE && !image_info.resource.empty() &&
341 !info->extension->HasCachedImage(image_info.resource, original_size)) {
342 info->extension->SetCachedImage(image_info.resource,
343 bitmap ? *bitmap : SkBitmap(),
344 original_size);
345 }
346
347 // If all pending bitmaps are done then report back.
348 if (info->pending_count == 0) {
349 gfx::Image image;
350 std::string extension_id = info->extension_id;
351
352 if (!info->image_skia.isNull()) {
353 info->image_skia.MakeThreadSafe();
354 image = gfx::Image(info->image_skia);
355 }
356
357 load_map_.erase(load_map_it);
358
359 // ImageLoadingTracker might be deleted after the callback so don't do
360 // anything after this statement.
361 observer_->OnImageLoaded(image, extension_id, id);
362 }
363 }
364
365 void ImageLoadingTracker::Observe(int type,
366 const content::NotificationSource& source,
367 const content::NotificationDetails& details) {
368 DCHECK(type == chrome::NOTIFICATION_EXTENSION_UNLOADED);
369
370 const Extension* extension =
371 content::Details<extensions::UnloadedExtensionInfo>(details)->extension;
372
373 // Remove reference to this extension from all pending load entries. This
374 // ensures we don't attempt to cache the bitmap when the load completes.
375 for (LoadMap::iterator i = load_map_.begin(); i != load_map_.end(); ++i) {
376 PendingLoadInfo* info = &i->second;
377 if (info->extension == extension) {
378 info->extension = NULL;
379 info->cache = DONT_CACHE;
380 }
381 }
382 }
OLDNEW
« no previous file with comments | « chrome/browser/extensions/image_loading_tracker.h ('k') | chrome/browser/extensions/image_loading_tracker_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698