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 base::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
58base::CancelableTaskTracker::TaskId IconManager::LoadIcon(
59    const base::FilePath& file_name,
60    IconLoader::IconSize size,
61    const IconRequestCallback& callback,
62    base::CancelableTaskTracker* tracker) {
63  IconLoader* loader = new IconLoader(file_name, size, this);
64  loader->AddRef();
65  loader->Start();
66
67  base::CancelableTaskTracker::IsCanceledCallback is_canceled;
68  base::CancelableTaskTracker::TaskId id =
69      tracker->NewTrackedTaskId(&is_canceled);
70  IconRequestCallback callback_runner = base::Bind(
71      &RunCallbackIfNotCanceled, is_canceled, callback);
72
73  ClientRequest client_request = { callback_runner, file_name, size };
74  requests_[loader] = client_request;
75  return id;
76}
77
78// IconLoader::Delegate implementation -----------------------------------------
79
80bool IconManager::OnGroupLoaded(IconLoader* loader,
81                                const IconGroupID& group) {
82  ClientRequests::iterator rit = requests_.find(loader);
83  if (rit == requests_.end()) {
84    NOTREACHED();
85    return false;
86  }
87
88  gfx::Image* result = LookupIconFromGroup(group, rit->second.size);
89  if (!result) {
90    return false;
91  }
92
93  return OnImageLoaded(loader, result, group);
94}
95
96bool IconManager::OnImageLoaded(
97    IconLoader* loader, gfx::Image* result, const IconGroupID& group) {
98  ClientRequests::iterator rit = requests_.find(loader);
99
100  // Balances the AddRef() in LoadIcon().
101  loader->Release();
102
103  // Look up our client state.
104  if (rit == requests_.end()) {
105    NOTREACHED();
106    return false;  // Return false to indicate result should be deleted.
107  }
108
109  const ClientRequest& client_request = rit->second;
110
111  // Cache the bitmap. Watch out: |result| may be NULL to indicate a current
112  // failure. We assume that if we have an entry in |icon_cache_|
113  // it must not be NULL.
114  CacheKey key(group, client_request.size);
115  IconMap::iterator it = icon_cache_.find(key);
116  if (it != icon_cache_.end()) {
117    if (!result) {
118      delete it->second;
119      icon_cache_.erase(it);
120    } else if (result != it->second) {
121      it->second->SwapRepresentations(result);
122      delete result;
123      result = it->second;
124    }
125  } else if (result) {
126    icon_cache_[key] = result;
127  }
128
129  group_cache_[client_request.file_path] = group;
130
131  // Inform our client that the request has completed.
132  client_request.callback.Run(result);
133  requests_.erase(rit);
134
135  return true;  // Indicates we took ownership of result.
136}
137
138IconManager::CacheKey::CacheKey(const IconGroupID& group,
139                                IconLoader::IconSize size)
140    : group(group),
141      size(size) {
142}
143
144bool IconManager::CacheKey::operator<(const CacheKey &other) const {
145  if (group != other.group)
146    return group < other.group;
147  return size < other.size;
148}
149