Merge pull request #8704 from YurySolovyov/update-icon-fetch-impl

Update icon loading API implementation
This commit is contained in:
Kevin Sawicki 2017-02-17 08:21:10 -08:00 committed by GitHub
commit 91677ade89
11 changed files with 191 additions and 246 deletions

View file

@ -30,6 +30,7 @@
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "brightray/browser/brightray_paths.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/icon_manager.h"
#include "chrome/common/chrome_paths.h"
#include "content/public/browser/browser_accessibility_state.h"
@ -890,15 +891,16 @@ void App::GetFileIcon(const base::FilePath& path,
return;
}
IconManager* icon_manager = IconManager::GetInstance();
gfx::Image* icon = icon_manager->LookupIconFromFilepath(normalized_path,
icon_size);
auto icon_manager = g_browser_process->GetIconManager();
gfx::Image* icon =
icon_manager->LookupIconFromFilepath(normalized_path, icon_size);
if (icon) {
callback.Run(v8::Null(isolate()), *icon);
} else {
icon_manager->LoadIcon(normalized_path, icon_size,
base::Bind(&OnIconDataAvailable, isolate(),
callback));
icon_manager->LoadIcon(
normalized_path, icon_size,
base::Bind(&OnIconDataAvailable, isolate(), callback),
&cancelable_task_tracker_);
}
}

View file

@ -13,7 +13,8 @@
#include "atom/browser/browser.h"
#include "atom/browser/browser_observer.h"
#include "atom/common/native_mate_converters/callback.h"
#include "chrome/browser/icon_loader.h"
#include "base/task/cancelable_task_tracker.h"
#include "chrome/browser/icon_manager.h"
#include "chrome/browser/process_singleton.h"
#include "content/public/browser/gpu_data_manager_observer.h"
#include "native_mate/handle.h"
@ -150,6 +151,9 @@ class App : public AtomBrowserClient::Delegate,
std::unique_ptr<CertificateManagerModel> certificate_manager_model_;
#endif
// Tracks tasks requesting file icons.
base::CancelableTaskTracker cancelable_task_tracker_;
DISALLOW_COPY_AND_ASSIGN(App);
};

View file

@ -4,13 +4,15 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/icon_manager.h"
#include "chrome/browser/printing/print_job_manager.h"
#include "ui/base/l10n/l10n_util.h"
BrowserProcess* g_browser_process = NULL;
BrowserProcess::BrowserProcess()
: print_job_manager_(new printing::PrintJobManager) {
: print_job_manager_(new printing::PrintJobManager),
icon_manager_(new IconManager) {
g_browser_process = this;
}
@ -22,6 +24,12 @@ std::string BrowserProcess::GetApplicationLocale() {
return l10n_util::GetApplicationLocale("");
}
IconManager* BrowserProcess::GetIconManager() {
if (!icon_manager_.get())
icon_manager_.reset(new IconManager);
return icon_manager_.get();
}
printing::PrintJobManager* BrowserProcess::print_job_manager() {
return print_job_manager_.get();
}

View file

@ -15,6 +15,8 @@
#include "base/macros.h"
class IconManager;
namespace printing {
class PrintJobManager;
}
@ -27,11 +29,13 @@ class BrowserProcess {
~BrowserProcess();
std::string GetApplicationLocale();
IconManager* GetIconManager();
printing::PrintJobManager* print_job_manager();
private:
std::unique_ptr<printing::PrintJobManager> print_job_manager_;
std::unique_ptr<IconManager> icon_manager_;
DISALLOW_COPY_AND_ASSIGN(BrowserProcess);
};

View file

@ -3,46 +3,40 @@
// found in the LICENSE file.
#include "chrome/browser/icon_loader.h"
#include "base/bind.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/browser/browser_thread.h"
using content::BrowserThread;
IconLoader::IconLoader(const base::FilePath& file_path,
IconSize size,
Delegate* delegate)
: target_task_runner_(NULL),
file_path_(file_path),
icon_size_(size),
delegate_(delegate) {}
IconLoader::~IconLoader() {}
// static
IconLoader* IconLoader::Create(const base::FilePath& file_path,
IconSize size,
IconLoadedCallback callback) {
return new IconLoader(file_path, size, callback);
}
void IconLoader::Start() {
target_task_runner_ = base::ThreadTaskRunnerHandle::Get();
BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE,
base::Bind(&IconLoader::ReadGroup, this),
base::Bind(&IconLoader::OnReadGroup, this));
BrowserThread::PostTaskAndReply(
BrowserThread::FILE, FROM_HERE,
base::Bind(&IconLoader::ReadGroup, base::Unretained(this)),
base::Bind(&IconLoader::OnReadGroup, base::Unretained(this)));
}
IconLoader::IconLoader(const base::FilePath& file_path,
IconSize size,
IconLoadedCallback callback)
: file_path_(file_path), icon_size_(size), callback_(callback) {}
IconLoader::~IconLoader() {}
void IconLoader::ReadGroup() {
group_ = ReadGroupIDFromFilepath(file_path_);
group_ = GroupForFilepath(file_path_);
}
void IconLoader::OnReadGroup() {
if (IsIconMutableFromFilepath(file_path_) ||
!delegate_->OnGroupLoaded(this, group_)) {
BrowserThread::PostTask(ReadIconThreadID(), FROM_HERE,
base::Bind(&IconLoader::ReadIcon, this));
}
}
void IconLoader::NotifyDelegate() {
// If the delegate takes ownership of the Image, release it from the scoped
// pointer.
if (delegate_->OnImageLoaded(this, image_.get(), group_))
ignore_result(image_.release()); // Can't ignore return value.
BrowserThread::PostTask(
ReadIconThreadID(), FROM_HERE,
base::Bind(&IconLoader::ReadIcon, base::Unretained(this)));
}

View file

@ -8,31 +8,30 @@
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/single_thread_task_runner.h"
#include "build/build_config.h"
#include "content/public/browser/browser_thread.h"
#include "ui/gfx/image/image.h"
#if defined(OS_WIN)
// On Windows, we group files by their extension, with several exceptions:
// .dll, .exe, .ico. See IconManager.h for explanation.
typedef std::wstring IconGroupID;
#elif defined(OS_POSIX)
// On POSIX, we group files by MIME type.
typedef std::string IconGroupID;
#endif
////////////////////////////////////////////////////////////////////////////////
//
// A facility to read a file containing an icon asynchronously in the IO
// thread. Returns the icon in the form of an ImageSkia.
//
////////////////////////////////////////////////////////////////////////////////
class IconLoader : public base::RefCountedThreadSafe<IconLoader> {
class IconLoader {
public:
// An IconGroup is a class of files that all share the same icon. For all
// platforms but Windows, and for most files on Windows, it is the file type
// (e.g. all .mp3 files share an icon, all .html files share an icon). On
// Windows, for certain file types (.exe, .dll, etc), each file of that type
// is assumed to have a unique icon. In that case, each of those files is a
// group to itself.
using IconGroup = base::FilePath::StringType;
enum IconSize {
SMALL = 0, // 16x16
NORMAL, // 32x32
@ -40,42 +39,32 @@ class IconLoader : public base::RefCountedThreadSafe<IconLoader> {
ALL, // All sizes available
};
class Delegate {
public:
// Invoked when an icon group has been read, but before the icon data
// is read. If the icon is already cached, this method should call and
// return the results of OnImageLoaded with the cached image.
virtual bool OnGroupLoaded(IconLoader* source,
const IconGroupID& group) = 0;
// Invoked when an icon has been read. |source| is the IconLoader. If the
// icon has been successfully loaded, result is non-null. This method must
// return true if it is taking ownership of the returned image.
virtual bool OnImageLoaded(IconLoader* source,
gfx::Image* result,
const IconGroupID& group) = 0;
// The callback invoked when an icon has been read. The parameters are:
// - The icon that was loaded, or null if there was a failure to load it.
// - The determined group from the original requested path.
using IconLoadedCallback =
base::Callback<void(std::unique_ptr<gfx::Image>, const IconGroup&)>;
protected:
virtual ~Delegate() {}
};
// Creates an IconLoader, which owns itself. If the IconLoader might outlive
// the caller, be sure to use a weak pointer in the |callback|.
static IconLoader* Create(const base::FilePath& file_path,
IconSize size,
IconLoadedCallback callback);
IconLoader(const base::FilePath& file_path,
IconSize size,
Delegate* delegate);
// Start reading the icon on the file thread.
// Starts the process of reading the icon. When the reading of the icon is
// complete, the IconLoadedCallback callback will be fulfilled, and the
// IconLoader will delete itself.
void Start();
private:
friend class base::RefCountedThreadSafe<IconLoader>;
IconLoader(const base::FilePath& file_path,
IconSize size,
IconLoadedCallback callback);
virtual ~IconLoader();
~IconLoader();
// Get the identifying string for the given file. The implementation
// is in icon_loader_[platform].cc.
static IconGroupID ReadGroupIDFromFilepath(const base::FilePath& path);
// Some icons (exe's on windows) can change as they're loaded.
static bool IsIconMutableFromFilepath(const base::FilePath& path);
// Given a file path, get the group for the given file.
static IconGroup GroupForFilepath(const base::FilePath& file_path);
// The thread ReadIcon() should be called on.
static content::BrowserThread::ID ReadIconThreadID();
@ -84,20 +73,18 @@ class IconLoader : public base::RefCountedThreadSafe<IconLoader> {
void OnReadGroup();
void ReadIcon();
void NotifyDelegate();
// The task runner object of the thread in which we notify the delegate.
scoped_refptr<base::SingleThreadTaskRunner> target_task_runner_;
base::FilePath file_path_;
IconGroupID group_;
IconGroup group_;
IconSize icon_size_;
std::unique_ptr<gfx::Image> image_;
Delegate* delegate_;
IconLoadedCallback callback_;
DISALLOW_COPY_AND_ASSIGN(IconLoader);
};

View file

@ -10,14 +10,9 @@
#include "ui/views/linux_ui/linux_ui.h"
// static
IconGroupID IconLoader::ReadGroupIDFromFilepath(
const base::FilePath& filepath) {
return base::nix::GetFileMimeType(filepath);
}
// static
bool IconLoader::IsIconMutableFromFilepath(const base::FilePath&) {
return false;
IconLoader::IconGroup IconLoader::GroupForFilepath(
const base::FilePath& file_path) {
return base::nix::GetFileMimeType(file_path);
}
// static
@ -50,6 +45,7 @@ void IconLoader::ReadIcon() {
image_.reset(new gfx::Image(image));
}
target_task_runner_->PostTask(FROM_HERE,
base::Bind(&IconLoader::NotifyDelegate, this));
target_task_runner_->PostTask(
FROM_HERE, base::Bind(callback_, base::Passed(&image_), group_));
delete this;
}

View file

@ -15,14 +15,9 @@
#include "ui/gfx/image/image_skia_util_mac.h"
// static
IconGroupID IconLoader::ReadGroupIDFromFilepath(
const base::FilePath& filepath) {
return filepath.Extension();
}
// static
bool IconLoader::IsIconMutableFromFilepath(const base::FilePath&) {
return false;
IconLoader::IconGroup IconLoader::GroupForFilepath(
const base::FilePath& file_path) {
return file_path.Extension();
}
// static
@ -57,6 +52,7 @@ void IconLoader::ReadIcon() {
}
}
target_task_runner_->PostTask(FROM_HERE,
base::Bind(&IconLoader::NotifyDelegate, this));
target_task_runner_->PostTask(
FROM_HERE, base::Bind(callback_, base::Passed(&image_), group_));
delete this;
}

View file

@ -17,18 +17,15 @@
#include "ui/gfx/image/image_skia.h"
// static
IconGroupID IconLoader::ReadGroupIDFromFilepath(
const base::FilePath& filepath) {
if (!IsIconMutableFromFilepath(filepath))
return filepath.Extension();
return filepath.value();
}
IconLoader::IconGroup IconLoader::GroupForFilepath(
const base::FilePath& file_path) {
if (file_path.MatchesExtension(L".exe") ||
file_path.MatchesExtension(L".dll") ||
file_path.MatchesExtension(L".ico")) {
return file_path.value();
}
// static
bool IconLoader::IsIconMutableFromFilepath(const base::FilePath& filepath) {
return filepath.MatchesExtension(L".exe") ||
filepath.MatchesExtension(L".dll") ||
filepath.MatchesExtension(L".ico");
return file_path.Extension();
}
// static
@ -54,22 +51,22 @@ void IconLoader::ReadIcon() {
image_.reset();
SHFILEINFO file_info = {0};
SHFILEINFO file_info = { 0 };
if (SHGetFileInfo(group_.c_str(), FILE_ATTRIBUTE_NORMAL, &file_info,
sizeof(SHFILEINFO),
SHGFI_ICON | size | SHGFI_USEFILEATTRIBUTES)) {
sizeof(SHFILEINFO),
SHGFI_ICON | size | SHGFI_USEFILEATTRIBUTES)) {
std::unique_ptr<SkBitmap> bitmap(
IconUtil::CreateSkBitmapFromHICON(file_info.hIcon));
if (bitmap.get()) {
gfx::ImageSkia image_skia(
gfx::ImageSkiaRep(*bitmap, display::win::GetDPIScale()));
gfx::ImageSkia image_skia(gfx::ImageSkiaRep(*bitmap,
display::win::GetDPIScale()));
image_skia.MakeThreadSafe();
image_.reset(new gfx::Image(image_skia));
DestroyIcon(file_info.hIcon);
}
}
// Always notify the delegate, regardless of success.
target_task_runner_->PostTask(FROM_HERE,
base::Bind(&IconLoader::NotifyDelegate, this));
target_task_runner_->PostTask(
FROM_HERE, base::Bind(callback_, base::Passed(&image_), group_));
delete this;
}

View file

@ -8,121 +8,86 @@
#include <tuple>
#include "base/bind.h"
#include "base/stl_util.h"
#include "base/task_runner.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"
struct IconManager::ClientRequest {
IconRequestCallback callback;
base::FilePath file_path;
IconLoader::IconSize size;
};
namespace {
// static
IconManager* IconManager::GetInstance() {
return base::Singleton<IconManager>::get();
void RunCallbackIfNotCanceled(
const base::CancelableTaskTracker::IsCanceledCallback& is_canceled,
const IconManager::IconRequestCallback& callback,
gfx::Image* image) {
if (is_canceled.Run())
return;
callback.Run(image);
}
IconManager::IconManager() {}
} // namespace
IconManager::IconManager() : weak_factory_(this) {}
IconManager::~IconManager() {
base::STLDeleteValues(&icon_cache_);
}
gfx::Image* IconManager::LookupIconFromFilepath(const base::FilePath& file_name,
gfx::Image* IconManager::LookupIconFromFilepath(const base::FilePath& file_path,
IconLoader::IconSize size) {
GroupMap::iterator it = group_cache_.find(file_name);
if (it != group_cache_.end())
return LookupIconFromGroup(it->second, size);
auto group_it = group_cache_.find(file_path);
if (group_it == group_cache_.end())
return nullptr;
return NULL;
CacheKey key(group_it->second, size);
auto icon_it = icon_cache_.find(key);
if (icon_it == icon_cache_.end())
return nullptr;
return icon_it->second.get();
}
gfx::Image* IconManager::LookupIconFromGroup(const IconGroupID& group,
IconLoader::IconSize size) {
IconMap::iterator it = icon_cache_.find(CacheKey(group, size));
if (it != icon_cache_.end())
return it->second;
base::CancelableTaskTracker::TaskId IconManager::LoadIcon(
const base::FilePath& file_path,
IconLoader::IconSize size,
const IconRequestCallback& callback,
base::CancelableTaskTracker* tracker) {
base::CancelableTaskTracker::IsCanceledCallback is_canceled;
base::CancelableTaskTracker::TaskId id =
tracker->NewTrackedTaskId(&is_canceled);
IconRequestCallback callback_runner = base::Bind(
&RunCallbackIfNotCanceled, is_canceled, callback);
return nullptr;
}
void IconManager::LoadIcon(const base::FilePath& file_name,
IconLoader::IconSize size,
const IconRequestCallback& callback) {
IconLoader* loader = new IconLoader(file_name, size, this);
loader->AddRef();
IconLoader* loader = IconLoader::Create(
file_path, size,
base::Bind(&IconManager::OnIconLoaded, weak_factory_.GetWeakPtr(),
callback_runner, file_path, size));
loader->Start();
ClientRequest client_request = {callback, file_name, size};
requests_[loader] = client_request;
return id;
}
// IconLoader::Delegate implementation -----------------------------------------
bool IconManager::OnGroupLoaded(IconLoader* loader, const IconGroupID& group) {
ClientRequests::iterator rit = requests_.find(loader);
if (rit == requests_.end()) {
NOTREACHED();
return false;
void IconManager::OnIconLoaded(IconRequestCallback callback,
base::FilePath file_path,
IconLoader::IconSize size,
std::unique_ptr<gfx::Image> result,
const IconLoader::IconGroup& group) {
// Cache the bitmap. Watch out: |result| may be null, which indicates a
// failure. We assume that if we have an entry in |icon_cache_| it must not be
// null.
CacheKey key(group, size);
if (result) {
callback.Run(result.get());
icon_cache_[key] = std::move(result);
} else {
callback.Run(nullptr);
icon_cache_.erase(key);
}
gfx::Image* result = LookupIconFromGroup(group, rit->second.size);
if (!result) {
return false;
}
return OnImageLoaded(loader, result, group);
group_cache_[file_path] = group;
}
bool IconManager::OnImageLoaded(IconLoader* loader,
gfx::Image* result,
const IconGroupID& group) {
ClientRequests::iterator rit = requests_.find(loader);
// Balances the AddRef() in LoadIcon().
loader->Release();
// Look up our client state.
if (rit == requests_.end()) {
NOTREACHED();
return false; // Return false to indicate result should be deleted.
}
const ClientRequest& client_request = rit->second;
// Cache the bitmap. Watch out: |result| may be NULL to indicate a current
// failure. We assume that if we have an entry in |icon_cache_|
// it must not be NULL.
CacheKey key(group, client_request.size);
IconMap::iterator it = icon_cache_.find(key);
if (it != icon_cache_.end()) {
if (!result) {
delete it->second;
icon_cache_.erase(it);
} else if (result != it->second) {
it->second->SwapRepresentations(result);
delete result;
result = it->second;
}
} else if (result) {
icon_cache_[key] = result;
}
group_cache_[client_request.file_path] = group;
// Inform our client that the request has completed.
client_request.callback.Run(result);
requests_.erase(rit);
return true; // Indicates we took ownership of result.
}
IconManager::CacheKey::CacheKey(const IconGroupID& group,
IconManager::CacheKey::CacheKey(const IconLoader::IconGroup& group,
IconLoader::IconSize size)
: group(group), size(size) {}
bool IconManager::CacheKey::operator<(const CacheKey& other) const {
bool IconManager::CacheKey::operator<(const CacheKey &other) const {
return std::tie(group, size) < std::tie(other.group, other.size);
}

View file

@ -34,7 +34,7 @@
// 2. An asynchronous icon load from a file on the file thread:
// IconManager::LoadIcon()
//
// When using the second (asychronous) method, callers must supply a callback
// When using the second (asynchronous) method, callers must supply a callback
// which will be run once the icon has been extracted. The icon manager will
// cache the results of the icon extraction so that subsequent lookups will be
// fast.
@ -46,25 +46,29 @@
#define CHROME_BROWSER_ICON_MANAGER_H_
#include <map>
#include <memory>
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "base/task/cancelable_task_tracker.h"
#include "chrome/browser/icon_loader.h"
#include "ui/gfx/image/image.h"
class IconManager : public IconLoader::Delegate {
class IconManager {
public:
static IconManager* GetInstance();
IconManager();
~IconManager();
// Synchronous call to examine the internal caches for the icon. Returns the
// icon if we have already loaded it, NULL if we don't have it and must load
// it via 'LoadIcon'. The returned bitmap is owned by the IconManager and must
// not be free'd by the caller. If the caller needs to modify the icon, it
// must make a copy and modify the copy.
gfx::Image* LookupIconFromFilepath(const base::FilePath& file_name,
// icon if we have already loaded it, or null if we don't have it and must
// load it via LoadIcon(). The returned bitmap is owned by the IconManager and
// must not be free'd by the caller. If the caller needs to modify the icon,
// it must make a copy and modify the copy.
gfx::Image* LookupIconFromFilepath(const base::FilePath& file_path,
IconLoader::IconSize size);
typedef base::Callback<void(gfx::Image*)> IconRequestCallback;
using IconRequestCallback = base::Callback<void(gfx::Image*)>;
// Asynchronous call to lookup and return the icon associated with file. The
// work is done on the file thread, with the callbacks running on the thread
@ -74,47 +78,35 @@ class IconManager : public IconLoader::Delegate {
// 1. This does *not* check the cache.
// 2. The returned bitmap pointer is *not* owned by callback. So callback
// should never keep it or delete it.
// 3. The gfx::Image pointer passed to the callback may be NULL if decoding
// 3. The gfx::Image pointer passed to the callback will be null if decoding
// failed.
void LoadIcon(const base::FilePath& file_name,
IconLoader::IconSize size,
const IconRequestCallback& callback);
// IconLoader::Delegate interface.
bool OnGroupLoaded(IconLoader* loader, const IconGroupID& group) override;
bool OnImageLoaded(IconLoader* loader,
gfx::Image* result,
const IconGroupID& group) override;
base::CancelableTaskTracker::TaskId LoadIcon(
const base::FilePath& file_name,
IconLoader::IconSize size,
const IconRequestCallback& callback,
base::CancelableTaskTracker* tracker);
private:
friend struct base::DefaultSingletonTraits<IconManager>;
IconManager();
~IconManager() override;
void OnIconLoaded(IconRequestCallback callback,
base::FilePath file_path,
IconLoader::IconSize size,
std::unique_ptr<gfx::Image> result,
const IconLoader::IconGroup& group);
struct CacheKey {
CacheKey(const IconGroupID& group, IconLoader::IconSize size);
CacheKey(const IconLoader::IconGroup& group, IconLoader::IconSize size);
// Used as a key in the map below, so we need this comparator.
bool operator<(const CacheKey& other) const;
bool operator<(const CacheKey &other) const;
IconGroupID group;
IconLoader::IconGroup group;
IconLoader::IconSize size;
};
gfx::Image* LookupIconFromGroup(const IconGroupID& group,
IconLoader::IconSize size);
std::map<base::FilePath, IconLoader::IconGroup> group_cache_;
std::map<CacheKey, std::unique_ptr<gfx::Image>> icon_cache_;
typedef std::map<CacheKey, gfx::Image*> IconMap;
IconMap icon_cache_;
typedef std::map<base::FilePath, IconGroupID> GroupMap;
GroupMap group_cache_;
// Asynchronous requests that have not yet been completed.
struct ClientRequest;
typedef std::map<IconLoader*, ClientRequest> ClientRequests;
ClientRequests requests_;
base::WeakPtrFactory<IconManager> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(IconManager);
};