1// Copyright (c) 2011 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/icon_manager.h"
6
7#include "base/bind.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/stl_util.h"
10#include "base/task_runner.h"
11#include "third_party/skia/include/core/SkBitmap.h"
12#include "third_party/skia/include/core/SkCanvas.h"
13
14namespace {
15
16void RunCallbackIfNotCanceled(
17    const CancelableTaskTracker::IsCanceledCallback& is_canceled,
18    const IconManager::IconRequestCallback& callback,
19    gfx::Image* image) {
20  if (is_canceled.Run())
21    return;
22  callback.Run(image);
23}
24
25}  // namespace
26
27struct IconManager::ClientRequest {
28  IconRequestCallback callback;
29  base::FilePath file_path;
30  IconLoader::IconSize size;
31};
32
33IconManager::IconManager() {
34}
35
36IconManager::~IconManager() {
37  STLDeleteValues(&icon_cache_);
38}
39
40gfx::Image* IconManager::LookupIconFromFilepath(const base::FilePath& file_name,
41                                                IconLoader::IconSize size) {
42  GroupMap::iterator it = group_cache_.find(file_name);
43  if (it != group_cache_.end())
44    return LookupIconFromGroup(it->second, size);
45
46  return NULL;
47}
48
49gfx::Image* IconManager::LookupIconFromGroup(const IconGroupID& group,
50                                             IconLoader::IconSize size) {
51  IconMap::iterator it = icon_cache_.find(CacheKey(group, size));
52  if (it != icon_cache_.end())
53    return it->second;
54
55  return NULL;
56}
57
58CancelableTaskTracker::TaskId IconManager::LoadIcon(
59    const base::FilePath& file_name,
60    IconLoader::IconSize size,
61    const IconRequestCallback& callback,
62    CancelableTaskTracker* tracker) {
63  IconLoader* loader = new IconLoader(file_name, size, this);
64  loader->AddRef();
65  loader->Start();
66
67  CancelableTaskTracker::IsCanceledCallback is_canceled;
68  CancelableTaskTracker::TaskId id = tracker->NewTrackedTaskId(&is_canceled);
69  IconRequestCallback callback_runner = base::Bind(
70      &RunCallbackIfNotCanceled, is_canceled, callback);
71
72  ClientRequest client_request = { callback_runner, file_name, size };
73  requests_[loader] = client_request;
74  return id;
75}
76
77// IconLoader::Delegate implementation -----------------------------------------
78
79bool IconManager::OnGroupLoaded(IconLoader* loader,
80                                const IconGroupID& group) {
81  ClientRequests::iterator rit = requests_.find(loader);
82  if (rit == requests_.end()) {
83    NOTREACHED();
84    return false;
85  }
86
87  gfx::Image* result = LookupIconFromGroup(group, rit->second.size);
88  if (!result) {
89    return false;
90  }
91
92  return OnImageLoaded(loader, result, group);
93}
94
95bool IconManager::OnImageLoaded(
96    IconLoader* loader, gfx::Image* result, const IconGroupID& group) {
97  ClientRequests::iterator rit = requests_.find(loader);
98
99  // Balances the AddRef() in LoadIcon().
100  loader->Release();
101
102  // Look up our client state.
103  if (rit == requests_.end()) {
104    NOTREACHED();
105    return false;  // Return false to indicate result should be deleted.
106  }
107
108  const ClientRequest& client_request = rit->second;
109
110  // Cache the bitmap. Watch out: |result| may be NULL to indicate a current
111  // failure. We assume that if we have an entry in |icon_cache_|
112  // it must not be NULL.
113  CacheKey key(group, client_request.size);
114  IconMap::iterator it = icon_cache_.find(key);
115  if (it != icon_cache_.end()) {
116    if (!result) {
117      delete it->second;
118      icon_cache_.erase(it);
119    } else if (result != it->second) {
120      it->second->SwapRepresentations(result);
121      delete result;
122      result = it->second;
123    }
124  } else if (result) {
125    icon_cache_[key] = result;
126  }
127
128  group_cache_[client_request.file_path] = group;
129
130  // Inform our client that the request has completed.
131  client_request.callback.Run(result);
132  requests_.erase(rit);
133
134  return true;  // Indicates we took ownership of result.
135}
136
137IconManager::CacheKey::CacheKey(const IconGroupID& group,
138                                IconLoader::IconSize size)
139    : group(group),
140      size(size) {
141}
142
143bool IconManager::CacheKey::operator<(const CacheKey &other) const {
144  if (group != other.group)
145    return group < other.group;
146  return size < other.size;
147}
148