summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorasanka@chromium.org <asanka@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-09 19:38:02 +0000
committerasanka@chromium.org <asanka@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-01-09 19:38:02 +0000
commit45e4fab2ec46c7a3ebe6476f5a6d25f2aaf1e4cf (patch)
treeaae06a3d01902d89c562d4a32a4771302f267c08 /chrome/browser
parent431b5cd77b2985869e33bf31d7eccd479a2a5372 (diff)
downloadchromium_src-45e4fab2ec46c7a3ebe6476f5a6d25f2aaf1e4cf.zip
chromium_src-45e4fab2ec46c7a3ebe6476f5a6d25f2aaf1e4cf.tar.gz
chromium_src-45e4fab2ec46c7a3ebe6476f5a6d25f2aaf1e4cf.tar.bz2
Implement chrome.experimental.downloads.getFileIcon().
BUG=12133 TEST=browser_tests --gtest_filter=DownloadExtensionTest.*:DownloadsApiTest.* Review URL: http://codereview.chromium.org/8519004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@116898 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/download/download_extension_api.cc133
-rw-r--r--chrome/browser/download/download_extension_api.h28
-rw-r--r--chrome/browser/download/download_extension_test.cc290
-rw-r--r--chrome/browser/download/download_file_icon_extractor.h33
-rw-r--r--chrome/browser/extensions/extension_function_dispatcher.cc1
5 files changed, 475 insertions, 10 deletions
diff --git a/chrome/browser/download/download_extension_api.cc b/chrome/browser/download/download_extension_api.cc
index f551c94..7f9effc 100644
--- a/chrome/browser/download/download_extension_api.cc
+++ b/chrome/browser/download/download_extension_api.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -22,6 +22,7 @@
#include "base/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
+#include "chrome/browser/download/download_file_icon_extractor.h"
#include "chrome/browser/download/download_service.h"
#include "chrome/browser/download/download_service_factory.h"
#include "chrome/browser/download/download_util.h"
@@ -31,6 +32,7 @@
#include "chrome/browser/icon_manager.h"
#include "chrome/browser/renderer_host/chrome_render_message_filter.h"
#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/webui/web_ui_util.h"
#include "content/browser/download/download_file_manager.h"
#include "content/browser/download/download_id.h"
#include "content/browser/download/download_state_info.h"
@@ -50,15 +52,19 @@ using content::DownloadManager;
namespace download_extension_errors {
// Error messages
-const char kNotImplementedError[] = "NotImplemented.";
const char kGenericError[] = "I'm afraid I can't do that.";
-const char kInvalidURLError[] = "Invalid URL.";
+const char kIconNotFoundError[] = "Icon not found.";
const char kInvalidOperationError[] = "Invalid operation.";
+const char kInvalidURLError[] = "Invalid URL.";
+const char kNotImplementedError[] = "NotImplemented.";
} // namespace download_extension_errors
namespace {
+// Default icon size for getFileIcon() in pixels.
+const int kDefaultIconSize = 32;
+
// Parameter keys
const char kBodyKey[] = "body";
const char kBytesReceivedKey[] = "bytesReceived";
@@ -80,6 +86,7 @@ const char kMethodKey[] = "method";
const char kMimeKey[] = "mime";
const char kPausedKey[] = "paused";
const char kSaveAsKey[] = "saveAs";
+const char kSizeKey[] = "size";
const char kStartTimeKey[] = "startTime";
const char kStateComplete[] = "complete";
const char kStateInProgress[] = "in_progress";
@@ -497,6 +504,126 @@ bool DownloadsDragFunction::RunInternal() {
}
namespace {
+
+class DownloadFileIconExtractorImpl : public DownloadFileIconExtractor {
+ public:
+ DownloadFileIconExtractorImpl() {}
+
+ ~DownloadFileIconExtractorImpl() {}
+
+ virtual bool ExtractIconURLForPath(const FilePath& path,
+ IconLoader::IconSize icon_size,
+ IconURLCallback callback) OVERRIDE;
+ private:
+ void OnIconLoadComplete(IconManager::Handle handle, gfx::Image* icon);
+
+ CancelableRequestConsumer cancelable_consumer_;
+ IconURLCallback callback_;
+};
+
+bool DownloadFileIconExtractorImpl::ExtractIconURLForPath(
+ const FilePath& path,
+ IconLoader::IconSize icon_size,
+ IconURLCallback callback) {
+ callback_ = callback;
+ IconManager* im = g_browser_process->icon_manager();
+ // The contents of the file at |path| may have changed since a previous
+ // request, in which case the associated icon may also have changed.
+ // Therefore, for the moment we always call LoadIcon instead of attempting
+ // a LookupIcon.
+ im->LoadIcon(
+ path, icon_size, &cancelable_consumer_,
+ base::Bind(&DownloadFileIconExtractorImpl::OnIconLoadComplete,
+ base::Unretained(this)));
+ return true;
+}
+
+void DownloadFileIconExtractorImpl::OnIconLoadComplete(
+ IconManager::Handle handle,
+ gfx::Image* icon) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ std::string url;
+ if (icon)
+ url = web_ui_util::GetImageDataUrl(*icon);
+ callback_.Run(url);
+}
+
+IconLoader::IconSize IconLoaderSizeFromPixelSize(int pixel_size) {
+ switch (pixel_size) {
+ case 16: return IconLoader::SMALL;
+ case 32: return IconLoader::NORMAL;
+ default:
+ NOTREACHED();
+ return IconLoader::NORMAL;
+ }
+}
+
+} // namespace
+
+DownloadsGetFileIconFunction::DownloadsGetFileIconFunction()
+ : AsyncDownloadsFunction(DOWNLOADS_FUNCTION_GET_FILE_ICON),
+ icon_size_(kDefaultIconSize),
+ icon_extractor_(new DownloadFileIconExtractorImpl()) {
+}
+
+DownloadsGetFileIconFunction::~DownloadsGetFileIconFunction() {}
+
+void DownloadsGetFileIconFunction::SetIconExtractorForTesting(
+ DownloadFileIconExtractor* extractor) {
+ DCHECK(extractor);
+ icon_extractor_.reset(extractor);
+}
+
+bool DownloadsGetFileIconFunction::ParseArgs() {
+ int dl_id = 0;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &dl_id));
+
+ base::DictionaryValue* options = NULL;
+ EXTENSION_FUNCTION_VALIDATE(args_->GetDictionary(1, &options));
+ if (options->HasKey(kSizeKey)) {
+ EXTENSION_FUNCTION_VALIDATE(options->GetInteger(kSizeKey, &icon_size_));
+ // We only support 16px and 32px icons. This is enforced in
+ // experimental.downloads.json.
+ DCHECK(icon_size_ == 16 || icon_size_ == 32);
+ }
+
+ DownloadManager* download_manager =
+ DownloadServiceFactory::GetForProfile(profile())->GetDownloadManager();
+ DownloadItem* download_item = download_manager->GetDownloadItem(dl_id);
+ if (download_item == NULL) {
+ // The DownloadItem is is added to history when the path is determined. If
+ // the download is not in history, then we don't have a path / final
+ // filename and no icon.
+ error_ = download_extension_errors::kInvalidOperationError;
+ return false;
+ }
+ // In-progress downloads return the intermediate filename for GetFullPath()
+ // which doesn't have the final extension. Therefore we won't be able to
+ // derive a good file icon for it. So we use GetTargetFilePath() instead.
+ path_ = download_item->GetTargetFilePath();
+ DCHECK(!path_.empty());
+ return true;
+}
+
+bool DownloadsGetFileIconFunction::RunInternal() {
+ DCHECK(!path_.empty());
+ DCHECK(icon_extractor_.get());
+ EXTENSION_FUNCTION_VALIDATE(icon_extractor_->ExtractIconURLForPath(
+ path_, IconLoaderSizeFromPixelSize(icon_size_),
+ base::Bind(&DownloadsGetFileIconFunction::OnIconURLExtracted, this)));
+ return true;
+}
+
+void DownloadsGetFileIconFunction::OnIconURLExtracted(const std::string& url) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ if (url.empty())
+ error_ = download_extension_errors::kIconNotFoundError;
+ else
+ result_.reset(base::Value::CreateStringValue(url));
+ SendResponse(error_.empty());
+}
+
+namespace {
base::DictionaryValue* DownloadItemToJSON(DownloadItem* item) {
base::DictionaryValue* json = new base::DictionaryValue();
json->SetInteger(kIdKey, item->GetId());
diff --git a/chrome/browser/download/download_extension_api.h b/chrome/browser/download/download_extension_api.h
index 84e7825..dea8e67 100644
--- a/chrome/browser/download/download_extension_api.h
+++ b/chrome/browser/download/download_extension_api.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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.
@@ -18,6 +18,7 @@
#include "content/public/browser/download_item.h"
#include "content/public/browser/download_manager.h"
+class DownloadFileIconExtractor;
class ResourceDispatcherHost;
namespace content {
@@ -31,10 +32,11 @@ class ResourceContext;
namespace download_extension_errors {
// Errors that can be returned through chrome.extension.lastError.message.
-extern const char kNotImplementedError[];
extern const char kGenericError[];
-extern const char kInvalidUrlError[];
+extern const char kIconNotFoundError[];
extern const char kInvalidOperationError[];
+extern const char kInvalidUrlError[];
+extern const char kNotImplementedError[];
} // namespace download_extension_errors
@@ -51,6 +53,7 @@ class DownloadsFunctionInterface {
DOWNLOADS_FUNCTION_ACCEPT_DANGER = 7,
DOWNLOADS_FUNCTION_SHOW = 8,
DOWNLOADS_FUNCTION_DRAG = 9,
+ DOWNLOADS_FUNCTION_GET_FILE_ICON = 10,
// Insert new values here, not at the beginning.
DOWNLOADS_FUNCTION_LAST
};
@@ -267,6 +270,25 @@ class DownloadsDragFunction : public AsyncDownloadsFunction {
DISALLOW_COPY_AND_ASSIGN(DownloadsDragFunction);
};
+class DownloadsGetFileIconFunction : public AsyncDownloadsFunction {
+ public:
+ DownloadsGetFileIconFunction();
+ virtual ~DownloadsGetFileIconFunction();
+ void SetIconExtractorForTesting(DownloadFileIconExtractor* extractor);
+ DECLARE_EXTENSION_FUNCTION_NAME("experimental.downloads.getFileIcon");
+
+ protected:
+ virtual bool ParseArgs() OVERRIDE;
+ virtual bool RunInternal() OVERRIDE;
+
+ private:
+ void OnIconURLExtracted(const std::string& url);
+ FilePath path_;
+ int icon_size_;
+ scoped_ptr<DownloadFileIconExtractor> icon_extractor_;
+ DISALLOW_COPY_AND_ASSIGN(DownloadsGetFileIconFunction);
+};
+
class ExtensionDownloadsEventRouter
: public content::DownloadManager::Observer {
public:
diff --git a/chrome/browser/download/download_extension_test.cc b/chrome/browser/download/download_extension_test.cc
index 6132e92..ffea074 100644
--- a/chrome/browser/download/download_extension_test.cc
+++ b/chrome/browser/download/download_extension_test.cc
@@ -1,9 +1,14 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// 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 <algorithm>
+
+#include "base/file_util.h"
+#include "base/scoped_temp_dir.h"
#include "base/stringprintf.h"
#include "chrome/browser/download/download_extension_api.h"
+#include "chrome/browser/download/download_file_icon_extractor.h"
#include "chrome/browser/download/download_service.h"
#include "chrome/browser/download/download_service_factory.h"
#include "chrome/browser/download/download_test_observer.h"
@@ -15,19 +20,26 @@
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
+#include "content/browser/download/download_persistent_store_info.h"
#include "content/browser/net/url_request_slow_download_job.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_manager.h"
+#include "net/base/data_url.h"
+#include "net/base/net_util.h"
+#include "ui/gfx/codec/png_codec.h"
+using content::BrowserThread;
using content::DownloadItem;
using content::DownloadManager;
+namespace {
+
class DownloadExtensionTest : public InProcessBrowserTest {
protected:
// InProcessBrowserTest
virtual void SetUpOnMainThread() OVERRIDE {
- content::BrowserThread::PostTask(
- content::BrowserThread::IO, FROM_HERE,
+ BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
base::Bind(&chrome_browser_net::SetUrlRequestMocksEnabled, true));
InProcessBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(CreateAndSetDownloadsDirectory(browser()));
@@ -46,6 +58,11 @@ class DownloadExtensionTest : public InProcessBrowserTest {
scoped_ptr<DownloadTestObserver> observer(
CreateDownloadObserver(1, DownloadItem::IN_PROGRESS));
GURL slow_download_url(URLRequestSlowDownloadJob::kUnknownSizeUrl);
+ DownloadManager* manager = GetDownloadManager();
+
+ EXPECT_EQ(0, manager->InProgressCount());
+ if (manager->InProgressCount() != 0)
+ return NULL;
ui_test_utils::NavigateToURLWithDisposition(
browser(), slow_download_url, CURRENT_TAB,
@@ -57,7 +74,7 @@ class DownloadExtensionTest : public InProcessBrowserTest {
return NULL;
DownloadManager::DownloadVector items;
- GetDownloadManager()->GetAllDownloads(FilePath(), &items);
+ manager->GetAllDownloads(FilePath(), &items);
DownloadItem* new_item = NULL;
for (DownloadManager::DownloadVector::iterator iter = items.begin();
@@ -71,6 +88,16 @@ class DownloadExtensionTest : public InProcessBrowserTest {
return new_item;
}
+ void FinishPendingSlowDownloads() {
+ scoped_ptr<DownloadTestObserver> observer(
+ CreateDownloadObserver(1, DownloadItem::COMPLETE));
+ GURL finish_url(URLRequestSlowDownloadJob::kFinishDownloadUrl);
+ ui_test_utils::NavigateToURLWithDisposition(
+ browser(), finish_url, NEW_FOREGROUND_TAB,
+ ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+ observer->WaitForFinished();
+ }
+
DownloadTestObserver* CreateDownloadObserver(
size_t download_count,
DownloadItem::DownloadState finished_state) {
@@ -81,6 +108,8 @@ class DownloadExtensionTest : public InProcessBrowserTest {
bool RunFunction(UIThreadExtensionFunction* function,
const std::string& args) {
+ // extension_function_test_utils::RunFunction() does not take
+ // ownership of |function|.
scoped_refptr<ExtensionFunction> function_owner(function);
return extension_function_test_utils::RunFunction(
function, args, browser(), extension_function_test_utils::NONE);
@@ -92,10 +121,51 @@ class DownloadExtensionTest : public InProcessBrowserTest {
function, args, browser(), extension_function_test_utils::NONE);
}
+ base::Value* RunFunctionAndReturnResult(UIThreadExtensionFunction* function,
+ const std::string& args) {
+ return extension_function_test_utils::RunFunctionAndReturnResult(
+ function, args, browser());
+ }
+
+ bool RunFunctionAndReturnString(UIThreadExtensionFunction* function,
+ const std::string& args,
+ std::string* result_string) {
+ scoped_ptr<base::Value> result(RunFunctionAndReturnResult(function, args));
+ EXPECT_TRUE(result.get());
+ return result.get() && result->GetAsString(result_string);
+ }
+
std::string DownloadItemIdAsArgList(const DownloadItem* download_item) {
return base::StringPrintf("[%d]", download_item->GetId());
}
+ // Checks if a data URL encoded image is a PNG of a given size.
+ void ExpectDataURLIsPNGWithSize(const std::string& url, int expected_size) {
+ std::string mime_type;
+ std::string charset;
+ std::string data;
+ EXPECT_FALSE(url.empty());
+ EXPECT_TRUE(net::DataURL::Parse(GURL(url), &mime_type, &charset, &data));
+ EXPECT_STREQ("image/png", mime_type.c_str());
+ EXPECT_FALSE(data.empty());
+
+ if (data.empty())
+ return;
+
+ int width = -1, height = -1;
+ std::vector<unsigned char> decoded_data;
+ EXPECT_TRUE(gfx::PNGCodec::Decode(
+ reinterpret_cast<const unsigned char*>(data.c_str()), data.length(),
+ gfx::PNGCodec::FORMAT_RGBA, &decoded_data,
+ &width, &height));
+ EXPECT_EQ(expected_size, width);
+ EXPECT_EQ(expected_size, height);
+ }
+
+ const FilePath& downloads_directory() {
+ return downloads_directory_.path();
+ }
+
private:
bool CreateAndSetDownloadsDirectory(Browser* browser) {
if (!downloads_directory_.CreateUniqueTempDir())
@@ -109,6 +179,47 @@ class DownloadExtensionTest : public InProcessBrowserTest {
ScopedTempDir downloads_directory_;
};
+class MockIconExtractorImpl : public DownloadFileIconExtractor {
+ public:
+ MockIconExtractorImpl(const FilePath& path, IconLoader::IconSize icon_size,
+ const std::string& response)
+ : expected_path_(path),
+ expected_icon_size_(icon_size),
+ response_(response) {
+ }
+ virtual ~MockIconExtractorImpl() {}
+
+ virtual bool ExtractIconURLForPath(const FilePath& path,
+ IconLoader::IconSize icon_size,
+ IconURLCallback callback) OVERRIDE {
+ EXPECT_STREQ(expected_path_.value().c_str(), path.value().c_str());
+ EXPECT_EQ(expected_icon_size_, icon_size);
+ if (expected_path_ == path &&
+ expected_icon_size_ == icon_size) {
+ callback_ = callback;
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ base::Bind(&MockIconExtractorImpl::RunCallback,
+ base::Unretained(this)));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private:
+ void RunCallback() {
+ callback_.Run(response_);
+ }
+
+ FilePath expected_path_;
+ IconLoader::IconSize expected_icon_size_;
+ std::string response_;
+ IconURLCallback callback_;
+};
+
+} // namespace
+
IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_PauseResumeCancel) {
DownloadItem* download_item = CreateSlowTestDownload();
ASSERT_TRUE(download_item);
@@ -164,3 +275,174 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_PauseResumeCancel) {
// Calling pause()/resume()/cancel() with invalid download Ids is
// tested in the API test (DownloadsApiTest).
}
+
+// Test downloads.getFileIcon() on in-progress, finished, cancelled and deleted
+// download items.
+IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_FileIcon_Active) {
+ DownloadItem* download_item = CreateSlowTestDownload();
+ ASSERT_TRUE(download_item);
+
+ // Get the icon for the in-progress download. This call should succeed even
+ // if the file type isn't registered.
+ std::string args = base::StringPrintf("[%d, {}]", download_item->GetId());
+ std::string result_string;
+ EXPECT_TRUE(RunFunctionAndReturnString(new DownloadsGetFileIconFunction(),
+ args, &result_string));
+
+ // Note: We are checking if the result is a Data URL that has encoded
+ // image/png data for an icon of a specific size. Of these, only the icon size
+ // is specified in the API contract. The image type (image/png) and URL type
+ // (Data) are implementation details.
+
+ // The default size for icons returned by getFileIcon() is 32x32.
+ ExpectDataURLIsPNGWithSize(result_string, 32);
+
+ // Test whether the correct path is being pased into the icon extractor.
+ FilePath expected_path(download_item->GetTargetFilePath());
+ scoped_refptr<DownloadsGetFileIconFunction> function(
+ new DownloadsGetFileIconFunction());
+ function->SetIconExtractorForTesting(new MockIconExtractorImpl(
+ expected_path, IconLoader::NORMAL, "foo"));
+ EXPECT_TRUE(RunFunctionAndReturnString(function.release(), args,
+ &result_string));
+
+ // Now try a 16x16 icon.
+ args = base::StringPrintf("[%d, {\"size\": 16}]", download_item->GetId());
+ EXPECT_TRUE(RunFunctionAndReturnString(new DownloadsGetFileIconFunction(),
+ args, &result_string));
+ ExpectDataURLIsPNGWithSize(result_string, 16);
+
+ // Explicitly asking for 32x32 should give us a 32x32 icon.
+ args = base::StringPrintf("[%d, {\"size\": 32}]", download_item->GetId());
+ EXPECT_TRUE(RunFunctionAndReturnString(new DownloadsGetFileIconFunction(),
+ args, &result_string));
+ ExpectDataURLIsPNGWithSize(result_string, 32);
+
+ // Finish the download and try again.
+ FinishPendingSlowDownloads();
+ EXPECT_EQ(DownloadItem::COMPLETE, download_item->GetState());
+ EXPECT_TRUE(RunFunctionAndReturnString(new DownloadsGetFileIconFunction(),
+ args, &result_string));
+ ExpectDataURLIsPNGWithSize(result_string, 32);
+
+ // Check the path passed to the icon extractor post-completion.
+ function = new DownloadsGetFileIconFunction();
+ function->SetIconExtractorForTesting(new MockIconExtractorImpl(
+ expected_path, IconLoader::NORMAL, "foo"));
+ EXPECT_TRUE(RunFunctionAndReturnString(function.release(), args,
+ &result_string));
+
+ // Now create another download.
+ download_item = CreateSlowTestDownload();
+ ASSERT_TRUE(download_item);
+ expected_path = download_item->GetTargetFilePath();
+
+ // Cancel the download. As long as the download has a target path, we should
+ // be able to query the file icon.
+ download_item->Cancel(true);
+ // Let cleanup complete on the FILE thread.
+ ui_test_utils::RunAllPendingInMessageLoop(BrowserThread::FILE);
+ args = base::StringPrintf("[%d, {\"size\": 32}]", download_item->GetId());
+ EXPECT_TRUE(RunFunctionAndReturnString(new DownloadsGetFileIconFunction(),
+ args, &result_string));
+ ExpectDataURLIsPNGWithSize(result_string, 32);
+
+ // Check the path passed to the icon extractor post-cancellation.
+ function = new DownloadsGetFileIconFunction();
+ function->SetIconExtractorForTesting(new MockIconExtractorImpl(
+ expected_path, IconLoader::NORMAL, "foo"));
+ EXPECT_TRUE(RunFunctionAndReturnString(function.release(), args,
+ &result_string));
+
+ // Simulate an error during icon load by invoking the mock with an empty
+ // result string.
+ function = new DownloadsGetFileIconFunction();
+ function->SetIconExtractorForTesting(new MockIconExtractorImpl(
+ expected_path, IconLoader::NORMAL, ""));
+ std::string error = RunFunctionAndReturnError(function.release(), args);
+ EXPECT_STREQ(download_extension_errors::kIconNotFoundError,
+ error.c_str());
+
+ // Once the download item is deleted, we should return kInvalidOperationError.
+ download_item->Delete(DownloadItem::DELETE_DUE_TO_USER_DISCARD);
+ error = RunFunctionAndReturnError(new DownloadsGetFileIconFunction(), args);
+ EXPECT_STREQ(download_extension_errors::kInvalidOperationError,
+ error.c_str());
+
+ // Asking for icons of other (invalid) sizes is tested in the API test
+ // (DownloadsApiTest).
+}
+
+// Test that we can acquire file icons for history downloads regardless of
+// whether they exist or not. If the file doesn't exist we should receive a
+// generic icon from the OS/toolkit that may or may not be specific to the file
+// type.
+IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadsApi_FileIcon_History) {
+ base::Time current(base::Time::Now());
+ FilePath real_path(
+ downloads_directory().Append(FILE_PATH_LITERAL("real.txt")));
+ FilePath fake_path(
+ downloads_directory().Append(FILE_PATH_LITERAL("fake.txt")));
+ DownloadPersistentStoreInfo history_entries[] = {
+ DownloadPersistentStoreInfo(
+ real_path,
+ GURL("http://does.not.exist/foo"),
+ GURL(),
+ current, current, // start_time == end_time == current.
+ 1, 1, // received_bytes == total_bytes == 1.
+ DownloadItem::COMPLETE,
+ 1, false),
+ DownloadPersistentStoreInfo(
+ fake_path,
+ GURL("http://does.not.exist/bar"),
+ GURL(),
+ current, current, // start_time == end_time == current.
+ 1, 1, // received_bytes == total_bytes == 1.
+ DownloadItem::COMPLETE,
+ 2, false)
+ };
+ std::vector<DownloadPersistentStoreInfo> entries(
+ history_entries, history_entries + arraysize(history_entries));
+
+ DownloadManager* manager = GetDownloadManager();
+ manager->OnPersistentStoreQueryComplete(&entries);
+
+ EXPECT_EQ(0, file_util::WriteFile(real_path, "", 0));
+ ASSERT_TRUE(file_util::PathExists(real_path));
+ ASSERT_FALSE(file_util::PathExists(fake_path));
+
+ DownloadManager::DownloadVector all_downloads;
+ manager->SearchDownloads(string16(), &all_downloads);
+ ASSERT_EQ(2u, all_downloads.size());
+ if (all_downloads[0]->GetId() > all_downloads[1]->GetId())
+ std::swap(all_downloads[0], all_downloads[1]);
+ EXPECT_EQ(real_path.value(), all_downloads[0]->GetFullPath().value());
+ EXPECT_EQ(fake_path.value(), all_downloads[1]->GetFullPath().value());
+
+ for (DownloadManager::DownloadVector::iterator iter = all_downloads.begin();
+ iter != all_downloads.end();
+ ++iter) {
+ std::string args(base::StringPrintf("[%d, {\"size\": 32}]",
+ (*iter)->GetId()));
+ std::string result_string;
+ EXPECT_TRUE(RunFunctionAndReturnString(
+ new DownloadsGetFileIconFunction(), args, &result_string));
+ // Note: We are checking if the result is a Data URL that has encoded
+ // image/png data for an icon of a specific size. Of these, only the icon
+ // size is specified in the API contract. The image type (image/png) and URL
+ // type (Data) are implementation details.
+ ExpectDataURLIsPNGWithSize(result_string, 32);
+
+ // Use a MockIconExtractorImpl to test if the correct path is being passed
+ // into the DownloadFileIconExtractor.
+ scoped_refptr<DownloadsGetFileIconFunction> function(
+ new DownloadsGetFileIconFunction());
+ function->SetIconExtractorForTesting(new MockIconExtractorImpl(
+ (*iter)->GetFullPath(), IconLoader::NORMAL, "hello"));
+ EXPECT_TRUE(RunFunctionAndReturnString(function.release(), args,
+ &result_string));
+ EXPECT_STREQ("hello", result_string.c_str());
+ }
+
+ // The temporary files should be cleaned up when the ScopedTempDir is removed.
+}
diff --git a/chrome/browser/download/download_file_icon_extractor.h b/chrome/browser/download/download_file_icon_extractor.h
new file mode 100644
index 0000000..6543ae9
--- /dev/null
+++ b/chrome/browser/download/download_file_icon_extractor.h
@@ -0,0 +1,33 @@
+// 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_DOWNLOAD_DOWNLOAD_FILE_ICON_EXTRACTOR_H_
+#define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_FILE_ICON_EXTRACTOR_H_
+#pragma once
+
+#include <string>
+
+#include "base/callback.h"
+#include "base/file_path.h"
+#include "chrome/browser/icon_loader.h"
+
+// Helper class for DownloadsGetFileIconFunction. Only used for a single icon
+// extraction.
+class DownloadFileIconExtractor {
+ public:
+ // Callback for |ExtractIconForPath|. The parameter is a URL as a string for a
+ // suitable icon. The string could be empty if the icon could not be
+ // determined.
+ typedef base::Callback<void(const std::string&)> IconURLCallback;
+
+ virtual ~DownloadFileIconExtractor() {}
+
+ // Should return false if the request was invalid. If the return value is
+ // true, then |callback| should be called with the result.
+ virtual bool ExtractIconURLForPath(const FilePath& path,
+ IconLoader::IconSize icon_size,
+ IconURLCallback callback) = 0;
+};
+
+#endif // CHROME_BROWSER_DOWNLOAD_DOWNLOAD_FILE_ICON_EXTRACTOR_H_
diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc
index 6d41e2a..e1a122b 100644
--- a/chrome/browser/extensions/extension_function_dispatcher.cc
+++ b/chrome/browser/extensions/extension_function_dispatcher.cc
@@ -484,6 +484,7 @@ void FactoryRegistry::ResetFunctions() {
RegisterFunction<DownloadsAcceptDangerFunction>();
RegisterFunction<DownloadsShowFunction>();
RegisterFunction<DownloadsDragFunction>();
+ RegisterFunction<DownloadsGetFileIconFunction>();
// PageCapture
RegisterFunction<PageCaptureSaveAsMHTMLFunction>();