diff --git a/atom/browser/api/atom_api_app.cc b/atom/browser/api/atom_api_app.cc index caaee9d25cb3..904a63a5d09f 100644 --- a/atom/browser/api/atom_api_app.cc +++ b/atom/browser/api/atom_api_app.cc @@ -30,6 +30,7 @@ #include "base/path_service.h" #include "base/strings/string_util.h" #include "brightray/browser/brightray_paths.h" +#include "chrome/browser/icon_manager.h" #include "chrome/common/chrome_paths.h" #include "content/public/browser/browser_accessibility_state.h" #include "content/public/browser/client_certificate_delegate.h" @@ -335,6 +336,15 @@ namespace api { namespace { +IconLoader::IconSize GetIconSizeByString(const std::string& size) { + if (size == "small") { + return IconLoader::IconSize::SMALL; + } else if (size == "large") { + return IconLoader::IconSize::LARGE; + } + return IconLoader::IconSize::NORMAL; +} + // Return the path constant from string. int GetPathConstant(const std::string& name) { if (name == "appData") @@ -462,6 +472,21 @@ int ImportIntoCertStore( } #endif +void OnIconDataAvailable(v8::Isolate* isolate, + const App::FileIconCallback& callback, + gfx::Image* icon) { + v8::Locker locker(isolate); + v8::HandleScope handle_scope(isolate); + + if (icon && !icon->IsEmpty()) { + callback.Run(v8::Null(isolate), *icon); + } else { + v8::Local error_message = + v8::String::NewFromUtf8(isolate, "Failed to get file icon."); + callback.Run(v8::Exception::Error(error_message), gfx::Image()); + } +} + } // namespace App::App(v8::Isolate* isolate) { @@ -841,6 +866,42 @@ JumpListResult App::SetJumpList(v8::Local val, } #endif // defined(OS_WIN) +void App::GetFileIcon(const base::FilePath& path, + mate::Arguments* args) { + mate::Dictionary options; + IconLoader::IconSize icon_size; + FileIconCallback callback; + + v8::Locker locker(isolate()); + v8::HandleScope handle_scope(isolate()); + + base::FilePath normalized_path = path.NormalizePathSeparators(); + + if (!args->GetNext(&options)) { + icon_size = IconLoader::IconSize::NORMAL; + } else { + std::string icon_size_string; + options.Get("size", &icon_size_string); + icon_size = GetIconSizeByString(icon_size_string); + } + + if (!args->GetNext(&callback)) { + args->ThrowError("Missing required callback function"); + return; + } + + IconManager* icon_manager = IconManager::GetInstance(); + 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)); + } +} + // static mate::Handle App::Create(v8::Isolate* isolate) { return mate::CreateHandle(isolate, new App(isolate)); @@ -909,7 +970,8 @@ void App::BuildPrototype( .SetMethod("isAccessibilitySupportEnabled", &App::IsAccessibilitySupportEnabled) .SetMethod("disableHardwareAcceleration", - &App::DisableHardwareAcceleration); + &App::DisableHardwareAcceleration) + .SetMethod("getFileIcon", &App::GetFileIcon); } } // namespace api diff --git a/atom/browser/api/atom_api_app.h b/atom/browser/api/atom_api_app.h index c7e62927c594..1d8e5804e5d2 100644 --- a/atom/browser/api/atom_api_app.h +++ b/atom/browser/api/atom_api_app.h @@ -13,6 +13,7 @@ #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 "chrome/browser/process_singleton.h" #include "content/public/browser/gpu_data_manager_observer.h" #include "native_mate/handle.h" @@ -43,6 +44,9 @@ class App : public AtomBrowserClient::Delegate, public BrowserObserver, public content::GpuDataManagerObserver { public: + using FileIconCallback = base::Callback, + const gfx::Image&)>; + static mate::Handle Create(v8::Isolate* isolate); static void BuildPrototype(v8::Isolate* isolate, @@ -129,6 +133,8 @@ class App : public AtomBrowserClient::Delegate, void ImportCertificate(const base::DictionaryValue& options, const net::CompletionCallback& callback); #endif + void GetFileIcon(const base::FilePath& path, + mate::Arguments* args); #if defined(OS_WIN) // Get the current Jump List settings. diff --git a/chromium_src/chrome/browser/icon_loader.cc b/chromium_src/chrome/browser/icon_loader.cc new file mode 100644 index 000000000000..afc5d8016ea0 --- /dev/null +++ b/chromium_src/chrome/browser/icon_loader.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// 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() {} + +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)); +} + +void IconLoader::ReadGroup() { + group_ = ReadGroupIDFromFilepath(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. +} diff --git a/chromium_src/chrome/browser/icon_loader.h b/chromium_src/chrome/browser/icon_loader.h new file mode 100644 index 000000000000..33301acd8295 --- /dev/null +++ b/chromium_src/chrome/browser/icon_loader.h @@ -0,0 +1,105 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_ICON_LOADER_H_ +#define CHROME_BROWSER_ICON_LOADER_H_ + +#include +#include + +#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 { + public: + enum IconSize { + SMALL = 0, // 16x16 + NORMAL, // 32x32 + LARGE, // Windows: 32x32, Linux: 48x48, Mac: Unsupported + 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; + + protected: + virtual ~Delegate() {} + }; + + IconLoader(const base::FilePath& file_path, + IconSize size, + Delegate* delegate); + + // Start reading the icon on the file thread. + void Start(); + + private: + friend class base::RefCountedThreadSafe; + + virtual ~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); + + // The thread ReadIcon() should be called on. + static content::BrowserThread::ID ReadIconThreadID(); + + void ReadGroup(); + void OnReadGroup(); + void ReadIcon(); + + void NotifyDelegate(); + + // The task runner object of the thread in which we notify the delegate. + scoped_refptr target_task_runner_; + + base::FilePath file_path_; + + IconGroupID group_; + + IconSize icon_size_; + + std::unique_ptr image_; + + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(IconLoader); +}; + +#endif // CHROME_BROWSER_ICON_LOADER_H_ diff --git a/chromium_src/chrome/browser/icon_loader_auralinux.cc b/chromium_src/chrome/browser/icon_loader_auralinux.cc new file mode 100644 index 000000000000..2a1b0d868f47 --- /dev/null +++ b/chromium_src/chrome/browser/icon_loader_auralinux.cc @@ -0,0 +1,55 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/icon_loader.h" + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/nix/mime_util_xdg.h" +#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; +} + +// static +content::BrowserThread::ID IconLoader::ReadIconThreadID() { + // ReadIcon() calls into views::LinuxUI and GTK2 code, so it must be on the UI + // thread. + return content::BrowserThread::UI; +} + +void IconLoader::ReadIcon() { + int size_pixels = 0; + switch (icon_size_) { + case IconLoader::SMALL: + size_pixels = 16; + break; + case IconLoader::NORMAL: + size_pixels = 32; + break; + case IconLoader::LARGE: + size_pixels = 48; + break; + default: + NOTREACHED(); + } + + views::LinuxUI* ui = views::LinuxUI::instance(); + if (ui) { + gfx::Image image = ui->GetIconForContentType(group_, size_pixels); + if (!image.IsEmpty()) + image_.reset(new gfx::Image(image)); + } + + target_task_runner_->PostTask(FROM_HERE, + base::Bind(&IconLoader::NotifyDelegate, this)); +} diff --git a/chromium_src/chrome/browser/icon_loader_mac.mm b/chromium_src/chrome/browser/icon_loader_mac.mm new file mode 100644 index 000000000000..a9dd42566b9e --- /dev/null +++ b/chromium_src/chrome/browser/icon_loader_mac.mm @@ -0,0 +1,62 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/icon_loader.h" + +#import + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/sys_string_conversions.h" +#include "base/threading/thread.h" +#include "ui/gfx/image/image_skia.h" +#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; +} + +// static +content::BrowserThread::ID IconLoader::ReadIconThreadID() { + return content::BrowserThread::FILE; +} + +void IconLoader::ReadIcon() { + NSString* group = base::SysUTF8ToNSString(group_); + NSWorkspace* workspace = [NSWorkspace sharedWorkspace]; + NSImage* icon = [workspace iconForFileType:group]; + + if (icon_size_ == ALL) { + // The NSImage already has all sizes. + image_.reset(new gfx::Image([icon retain])); + } else { + NSSize size = NSZeroSize; + switch (icon_size_) { + case IconLoader::SMALL: + size = NSMakeSize(16, 16); + break; + case IconLoader::NORMAL: + size = NSMakeSize(32, 32); + break; + default: + NOTREACHED(); + } + gfx::ImageSkia image_skia(gfx::ImageSkiaFromResizedNSImage(icon, size)); + if (!image_skia.isNull()) { + image_skia.MakeThreadSafe(); + image_.reset(new gfx::Image(image_skia)); + } + } + + target_task_runner_->PostTask(FROM_HERE, + base::Bind(&IconLoader::NotifyDelegate, this)); +} diff --git a/chromium_src/chrome/browser/icon_loader_win.cc b/chromium_src/chrome/browser/icon_loader_win.cc new file mode 100644 index 000000000000..079c1f4828d2 --- /dev/null +++ b/chromium_src/chrome/browser/icon_loader_win.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/icon_loader.h" + +#include +#include + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/threading/thread.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/display/win/dpi.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/icon_util.h" +#include "ui/gfx/image/image_skia.h" + +// static +IconGroupID IconLoader::ReadGroupIDFromFilepath( + const base::FilePath& filepath) { + if (!IsIconMutableFromFilepath(filepath)) + return filepath.Extension(); + return filepath.value(); +} + +// static +bool IconLoader::IsIconMutableFromFilepath(const base::FilePath& filepath) { + return filepath.MatchesExtension(L".exe") || + filepath.MatchesExtension(L".dll") || + filepath.MatchesExtension(L".ico"); +} + +// static +content::BrowserThread::ID IconLoader::ReadIconThreadID() { + return content::BrowserThread::FILE; +} + +void IconLoader::ReadIcon() { + int size = 0; + switch (icon_size_) { + case IconLoader::SMALL: + size = SHGFI_SMALLICON; + break; + case IconLoader::NORMAL: + size = 0; + break; + case IconLoader::LARGE: + size = SHGFI_LARGEICON; + break; + default: + NOTREACHED(); + } + + image_.reset(); + + SHFILEINFO file_info = {0}; + if (SHGetFileInfo(group_.c_str(), FILE_ATTRIBUTE_NORMAL, &file_info, + sizeof(SHFILEINFO), + SHGFI_ICON | size | SHGFI_USEFILEATTRIBUTES)) { + std::unique_ptr bitmap( + IconUtil::CreateSkBitmapFromHICON(file_info.hIcon)); + if (bitmap.get()) { + 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)); +} diff --git a/chromium_src/chrome/browser/icon_manager.cc b/chromium_src/chrome/browser/icon_manager.cc new file mode 100644 index 000000000000..95073fcdd755 --- /dev/null +++ b/chromium_src/chrome/browser/icon_manager.cc @@ -0,0 +1,128 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/icon_manager.h" + +#include +#include + +#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; +}; + +// static +IconManager* IconManager::GetInstance() { + return base::Singleton::get(); +} + +IconManager::IconManager() {} + +IconManager::~IconManager() { + base::STLDeleteValues(&icon_cache_); +} + +gfx::Image* IconManager::LookupIconFromFilepath(const base::FilePath& file_name, + IconLoader::IconSize size) { + GroupMap::iterator it = group_cache_.find(file_name); + if (it != group_cache_.end()) + return LookupIconFromGroup(it->second, size); + + return NULL; +} + +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; + + 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(); + loader->Start(); + + ClientRequest client_request = {callback, file_name, size}; + requests_[loader] = client_request; +} + +// IconLoader::Delegate implementation ----------------------------------------- + +bool IconManager::OnGroupLoaded(IconLoader* loader, const IconGroupID& group) { + ClientRequests::iterator rit = requests_.find(loader); + if (rit == requests_.end()) { + NOTREACHED(); + return false; + } + + gfx::Image* result = LookupIconFromGroup(group, rit->second.size); + if (!result) { + return false; + } + + return OnImageLoaded(loader, result, 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, + IconLoader::IconSize size) + : group(group), size(size) {} + +bool IconManager::CacheKey::operator<(const CacheKey& other) const { + return std::tie(group, size) < std::tie(other.group, other.size); +} diff --git a/chromium_src/chrome/browser/icon_manager.h b/chromium_src/chrome/browser/icon_manager.h new file mode 100644 index 000000000000..dd6de8252756 --- /dev/null +++ b/chromium_src/chrome/browser/icon_manager.h @@ -0,0 +1,122 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Class for finding and caching Windows explorer icons. The IconManager +// lives on the UI thread but performs icon extraction work on the file thread +// to avoid blocking the UI thread with potentially expensive COM and disk +// operations. +// +// Terminology +// +// Windows files have icons associated with them that can be of two types: +// 1. "Per class": the icon used for this file is used for all files with the +// same file extension or class. Examples are PDF or MP3 files, which use +// the same icon for all files of that type. +// 2. "Per instance": the icon used for this file is embedded in the file +// itself and is unique. Executable files are typically "per instance". +// +// Files that end in the following extensions are considered "per instance": +// .exe +// .dll +// .ico +// The IconManager will do explicit icon loads on the full path of these files +// and cache the results per file. All other file types will be looked up by +// file extension and the results will be cached per extension. That way, all +// .mp3 files will share one icon, but all .exe files will have their own icon. +// +// POSIX files don't have associated icons. We query the OS by the file's +// mime type. +// +// The IconManager can be queried in two ways: +// 1. A quick, synchronous check of its caches which does not touch the disk: +// IconManager::LookupIcon() +// 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 +// 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. +// +// Icon bitmaps returned should be treated as const since they may be referenced +// by other clients. Make a copy of the icon if you need to modify it. + +#ifndef CHROME_BROWSER_ICON_MANAGER_H_ +#define CHROME_BROWSER_ICON_MANAGER_H_ + +#include + +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/singleton.h" +#include "chrome/browser/icon_loader.h" +#include "ui/gfx/image/image.h" + +class IconManager : public IconLoader::Delegate { + public: + static IconManager* GetInstance(); + // 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, + IconLoader::IconSize size); + + typedef base::Callback IconRequestCallback; + + // 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 + // this function is called. + // + // Note: + // 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 + // 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; + + private: + friend struct base::DefaultSingletonTraits; + + IconManager(); + ~IconManager() override; + + struct CacheKey { + CacheKey(const IconGroupID& group, IconLoader::IconSize size); + + // Used as a key in the map below, so we need this comparator. + bool operator<(const CacheKey& other) const; + + IconGroupID group; + IconLoader::IconSize size; + }; + + gfx::Image* LookupIconFromGroup(const IconGroupID& group, + IconLoader::IconSize size); + + typedef std::map IconMap; + IconMap icon_cache_; + + typedef std::map GroupMap; + GroupMap group_cache_; + + // Asynchronous requests that have not yet been completed. + struct ClientRequest; + typedef std::map ClientRequests; + ClientRequests requests_; + + DISALLOW_COPY_AND_ASSIGN(IconManager); +}; + +#endif // CHROME_BROWSER_ICON_MANAGER_H_ diff --git a/docs/api/app.md b/docs/api/app.md index 83e071618d56..d34daeddde78 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -401,6 +401,28 @@ You can request the following paths by the name: * `videos` Directory for a user's videos. * `pepperFlashSystemPlugin` Full path to the system version of the Pepper Flash plugin. +### `app.getFileIcon(path[, options], callback)` + +* `path` String +* `options` Object (optional) + * `size` String + * `small` - 16x16 + * `normal` - 32x32 + * `large` - 48x48 on _Linux_, 32x32 on _Windows_, unsupported on _macOS_. +* `callback` Function + * `error` Error + * `icon` [NativeImage](native-image.md) + +Fetches a path's associated icon. + +On _Windows_, there a 2 kinds of icons: + +- Icons associated with certain file extensions, like `.mp3`, `.png`, etc. +- Icons inside the file itself, like `.exe`, `.dll`, `.ico`. + +On _Linux_ and _macOS_, icons depend on the application associated with file +mime type. + ### `app.setPath(name, path)` * `name` String diff --git a/electron.gyp b/electron.gyp index adf38cfbe50c..a657d2d1c560 100644 --- a/electron.gyp +++ b/electron.gyp @@ -328,6 +328,7 @@ }], # OS=="mac" and mas_build==1 ['OS=="linux"', { 'sources': [ + '<@(lib_sources_linux)', '<@(lib_sources_nss)', ], 'link_settings': { diff --git a/filenames.gypi b/filenames.gypi index 636cbb606385..5b16beaa91a1 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -474,6 +474,12 @@ 'chromium_src/chrome/browser/browser_process.h', 'chromium_src/chrome/browser/chrome_process_finder_win.cc', 'chromium_src/chrome/browser/chrome_process_finder_win.h', + 'chromium_src/chrome/browser/icon_loader_mac.mm', + 'chromium_src/chrome/browser/icon_loader_win.cc', + 'chromium_src/chrome/browser/icon_loader.cc', + 'chromium_src/chrome/browser/icon_loader.h', + 'chromium_src/chrome/browser/icon_manager.cc', + 'chromium_src/chrome/browser/icon_manager.h', 'chromium_src/chrome/browser/chrome_notification_types.h', 'chromium_src/chrome/browser/extensions/global_shortcut_listener.cc', 'chromium_src/chrome/browser/extensions/global_shortcut_listener.h', @@ -601,6 +607,9 @@ '<@(native_mate_files)', '<(SHARED_INTERMEDIATE_DIR)/atom_natives.h', ], + 'lib_sources_linux': [ + 'chromium_src/chrome/browser/icon_loader_auralinux.cc', + ], 'lib_sources_nss': [ 'chromium_src/chrome/browser/certificate_manager_model.cc', 'chromium_src/chrome/browser/certificate_manager_model.h', diff --git a/spec/api-app-spec.js b/spec/api-app-spec.js index 085522a0cc9c..9bfd77173cfb 100644 --- a/spec/api-app-spec.js +++ b/spec/api-app-spec.js @@ -456,4 +456,66 @@ describe('app module', function () { assert.equal(app.isDefaultProtocolClient(protocol), false) }) }) + + describe('getFileIcon() API', function () { + const iconPath = path.join(__dirname, 'fixtures/assets/icon.ico') + const sizes = { + small: 16, + normal: 32, + large: process.platform === 'win32' ? 32 : 48 + } + + it('fetches a non-empty icon', function (done) { + app.getFileIcon(iconPath, function (err, icon) { + assert.equal(err, null) + assert.equal(icon.isEmpty(), false) + done() + }) + }) + + it('fetches normal icon size by default', function (done) { + app.getFileIcon(iconPath, function (err, icon) { + const size = icon.getSize() + assert.equal(err, null) + assert.equal(size.height, sizes.normal) + assert.equal(size.width, sizes.normal) + done() + }) + }) + + describe('size option', function () { + it('fetches a small icon', function (done) { + app.getFileIcon(iconPath, { size: 'small' }, function (err, icon) { + const size = icon.getSize() + assert.equal(err, null) + assert.equal(size.height, sizes.small) + assert.equal(size.width, sizes.small) + done() + }) + }) + + it('fetches a normal icon', function (done) { + app.getFileIcon(iconPath, { size: 'normal' }, function (err, icon) { + const size = icon.getSize() + assert.equal(err, null) + assert.equal(size.height, sizes.normal) + assert.equal(size.width, sizes.normal) + done() + }) + }) + + it('fetches a large icon', function (done) { + // macOS does not support large icons + if (process.platform === 'darwin') return done() + + app.getFileIcon(iconPath, { size: 'large' }, function (err, icon) { + const size = icon.getSize() + assert.equal(err, null) + assert.equal(size.height, sizes.large) + assert.equal(size.width, sizes.large) + done() + }) + }) + }) + }) })