15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/icon_manager.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/stl_util.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/task_runner.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkBitmap.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkCanvas.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void RunCallbackIfNotCanceled(
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const IconManager::IconRequestCallback& callback,
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gfx::Image* image) {
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (is_canceled.Run())
212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  callback.Run(image);
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)struct IconManager::ClientRequest {
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IconRequestCallback callback;
29c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::FilePath file_path;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IconLoader::IconSize size;
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IconManager::IconManager() {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IconManager::~IconManager() {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  STLDeleteValues(&icon_cache_);
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
40c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)gfx::Image* IconManager::LookupIconFromFilepath(const base::FilePath& file_name,
41c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                                IconLoader::IconSize size) {
42c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  GroupMap::iterator it = group_cache_.find(file_name);
43c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (it != group_cache_.end())
44c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return LookupIconFromGroup(it->second, size);
45c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
46c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return NULL;
47c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
48c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
49c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)gfx::Image* IconManager::LookupIconFromGroup(const IconGroupID& group,
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                             IconLoader::IconSize size) {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IconMap::iterator it = icon_cache_.find(CacheKey(group, size));
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it != icon_cache_.end())
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return it->second;
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return NULL;
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::CancelableTaskTracker::TaskId IconManager::LoadIcon(
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const base::FilePath& file_name,
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IconLoader::IconSize size,
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const IconRequestCallback& callback,
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::CancelableTaskTracker* tracker) {
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  IconLoader* loader = new IconLoader(file_name, size, this);
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  loader->AddRef();
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  loader->Start();
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::CancelableTaskTracker::IsCanceledCallback is_canceled;
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::CancelableTaskTracker::TaskId id =
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      tracker->NewTrackedTaskId(&is_canceled);
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  IconRequestCallback callback_runner = base::Bind(
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      &RunCallbackIfNotCanceled, is_canceled, callback);
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ClientRequest client_request = { callback_runner, file_name, size };
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  requests_[loader] = client_request;
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return id;
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// IconLoader::Delegate implementation -----------------------------------------
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool IconManager::OnGroupLoaded(IconLoader* loader,
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                const IconGroupID& group) {
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ClientRequests::iterator rit = requests_.find(loader);
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (rit == requests_.end()) {
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    NOTREACHED();
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  gfx::Image* result = LookupIconFromGroup(group, rit->second.size);
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!result) {
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return OnImageLoaded(loader, result, group);
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)bool IconManager::OnImageLoaded(
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    IconLoader* loader, gfx::Image* result, const IconGroupID& group) {
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ClientRequests::iterator rit = requests_.find(loader);
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Balances the AddRef() in LoadIcon().
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  loader->Release();
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Look up our client state.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rit == requests_.end()) {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NOTREACHED();
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;  // Return false to indicate result should be deleted.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const ClientRequest& client_request = rit->second;
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // Cache the bitmap. Watch out: |result| may be NULL to indicate a current
1121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // failure. We assume that if we have an entry in |icon_cache_|
1131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  // it must not be NULL.
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  CacheKey key(group, client_request.size);
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  IconMap::iterator it = icon_cache_.find(key);
1161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (it != icon_cache_.end()) {
1171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (!result) {
1181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      delete it->second;
1191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      icon_cache_.erase(it);
1201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    } else if (result != it->second) {
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      it->second->SwapRepresentations(result);
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      delete result;
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      result = it->second;
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
1251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  } else if (result) {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    icon_cache_[key] = result;
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  group_cache_[client_request.file_path] = group;
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Inform our client that the request has completed.
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  client_request.callback.Run(result);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  requests_.erase(rit);
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;  // Indicates we took ownership of result.
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)IconManager::CacheKey::CacheKey(const IconGroupID& group,
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                IconLoader::IconSize size)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : group(group),
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      size(size) {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool IconManager::CacheKey::operator<(const CacheKey &other) const {
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (group != other.group)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return group < other.group;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return size < other.size;
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
149