Commit 5a25f300 authored by mathp's avatar mathp Committed by Commit bot

[Suggestions] Move ImageManager to the component

Keeps the fetcher functionality as ImageFetcherImpl in chrome/browser/search/suggestions.

BUG=387751
TBR=blundell
TEST=ImageManagerTest,ImageFetcherBrowserTest

Review URL: https://codereview.chromium.org/543753002

Cr-Commit-Position: refs/heads/master@{#296503}
parent ee652576
// Copyright 2014 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/search/suggestions/image_fetcher_impl.h"
#include <string>
#include "content/public/browser/browser_thread.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_request_context_getter.h"
namespace suggestions {
ImageFetcherImpl::ImageFetcherImpl(
net::URLRequestContextGetter* url_request_context)
: url_request_context_(url_request_context) {}
ImageFetcherImpl::~ImageFetcherImpl() {}
ImageFetcherImpl::ImageRequest::ImageRequest() : fetcher(NULL) {}
ImageFetcherImpl::ImageRequest::ImageRequest(chrome::BitmapFetcher* f)
: fetcher(f) {}
ImageFetcherImpl::ImageRequest::~ImageRequest() { delete fetcher; }
void ImageFetcherImpl::SetImageFetcherDelegate(ImageFetcherDelegate* delegate) {
DCHECK(delegate);
delegate_ = delegate;
}
void ImageFetcherImpl::StartOrQueueNetworkRequest(
const GURL& url, const GURL& image_url,
base::Callback<void(const GURL&, const SkBitmap*)> callback) {
// Before starting to fetch the image. Look for a request in progress for
// |image_url|, and queue if appropriate.
ImageRequestMap::iterator it = pending_net_requests_.find(image_url);
if (it == pending_net_requests_.end()) {
// |image_url| is not being fetched, so create a request and initiate
// the fetch.
ImageRequest request(new chrome::BitmapFetcher(image_url, this));
request.url = url;
request.callbacks.push_back(callback);
request.fetcher->Start(
url_request_context_, std::string(),
net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
net::LOAD_NORMAL);
pending_net_requests_[image_url].swap(&request);
} else {
// Request in progress. Register as an interested callback.
it->second.callbacks.push_back(callback);
}
}
void ImageFetcherImpl::OnFetchComplete(const GURL image_url,
const SkBitmap* bitmap) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
ImageRequestMap::iterator image_iter = pending_net_requests_.find(image_url);
DCHECK(image_iter != pending_net_requests_.end());
ImageRequest* request = &image_iter->second;
// Here |bitmap| could be NULL or a pointer to a bitmap which is owned by the
// BitmapFetcher and which ceases to exist after this function. Pass the
// un-owned pointer to the registered callbacks.
for (CallbackVector::iterator callback_iter = request->callbacks.begin();
callback_iter != request->callbacks.end(); ++callback_iter) {
callback_iter->Run(request->url, bitmap);
}
// Inform the ImageFetcherDelegate.
if (delegate_) {
delegate_->OnImageFetched(request->url, bitmap);
}
// Erase the completed ImageRequest.
pending_net_requests_.erase(image_iter);
}
} // namespace suggestions
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef CHROME_BROWSER_SEARCH_SUGGESTIONS_IMAGE_MANAGER_IMPL_H_ #ifndef CHROME_BROWSER_SEARCH_SUGGESTIONS_IMAGE_FETCHER_IMPL_H_
#define CHROME_BROWSER_SEARCH_SUGGESTIONS_IMAGE_MANAGER_IMPL_H_ #define CHROME_BROWSER_SEARCH_SUGGESTIONS_IMAGE_FETCHER_IMPL_H_
#include <map> #include <map>
#include <utility> #include <utility>
...@@ -11,13 +11,8 @@ ...@@ -11,13 +11,8 @@
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/hash_tables.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h" #include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
#include "components/leveldb_proto/proto_database.h" #include "components/suggestions/image_fetcher.h"
#include "components/suggestions/image_manager.h"
#include "components/suggestions/proto/suggestions.pb.h"
#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -27,49 +22,32 @@ class URLRequestContextGetter; ...@@ -27,49 +22,32 @@ class URLRequestContextGetter;
namespace suggestions { namespace suggestions {
class ImageData; // A class used to fetch server images.
class SuggestionsProfile; class ImageFetcherImpl : public ImageFetcher,
// A class used to fetch server images asynchronously and manage the caching
// layer (both in memory and on disk).
class ImageManagerImpl : public ImageManager,
public chrome::BitmapFetcherDelegate { public chrome::BitmapFetcherDelegate {
public: public:
typedef std::vector<ImageData> ImageDataVector; explicit ImageFetcherImpl(net::URLRequestContextGetter* url_request_context);
virtual ~ImageFetcherImpl();
ImageManagerImpl(
net::URLRequestContextGetter* url_request_context, virtual void SetImageFetcherDelegate(ImageFetcherDelegate* delegate) OVERRIDE;
scoped_ptr<leveldb_proto::ProtoDatabase<ImageData> > database,
const base::FilePath& database_dir); virtual void StartOrQueueNetworkRequest(
virtual ~ImageManagerImpl(); const GURL& url, const GURL& image_url,
// Overrides from ImageManager.
virtual void Initialize(const SuggestionsProfile& suggestions) OVERRIDE;
// Should be called from the UI thread.
virtual void GetImageForURL(
const GURL& url,
base::Callback<void(const GURL&, const SkBitmap*)> callback) OVERRIDE; base::Callback<void(const GURL&, const SkBitmap*)> callback) OVERRIDE;
private: private:
friend class MockImageManagerImpl; // Inherited from BitmapFetcherDelegate. Runs on the UI thread.
friend class ImageManagerImplBrowserTest; virtual void OnFetchComplete(const GURL image_url,
FRIEND_TEST_ALL_PREFIXES(ImageManagerImplTest, InitializeTest); const SkBitmap* bitmap) OVERRIDE;
FRIEND_TEST_ALL_PREFIXES(ImageManagerImplBrowserTest,
GetImageForURLNetworkCacheHit);
FRIEND_TEST_ALL_PREFIXES(ImageManagerImplBrowserTest,
GetImageForURLNetworkCacheNotInitialized);
// Used for testing.
ImageManagerImpl();
typedef std::vector<base::Callback<void(const GURL&, const SkBitmap*)> > typedef std::vector<base::Callback<void(const GURL&, const SkBitmap*)> >
CallbackVector; CallbackVector;
typedef base::hash_map<std::string, SkBitmap> ImageMap;
// State related to an image fetch (associated website url, image_url, // State related to an image fetch (associated website url, image_url,
// fetcher, pending callbacks). // fetcher, pending callbacks).
struct ImageRequest { struct ImageRequest {
ImageRequest(); ImageRequest();
// Struct takes ownership of |f|.
explicit ImageRequest(chrome::BitmapFetcher* f); explicit ImageRequest(chrome::BitmapFetcher* f);
~ImageRequest(); ~ImageRequest();
...@@ -90,81 +68,17 @@ class ImageManagerImpl : public ImageManager, ...@@ -90,81 +68,17 @@ class ImageManagerImpl : public ImageManager,
typedef std::map<const GURL, ImageRequest> ImageRequestMap; typedef std::map<const GURL, ImageRequest> ImageRequestMap;
// Looks up image URL for |url|. If found, writes the result to |image_url|
// and returns true. Otherwise just returns false.
bool GetImageURL(const GURL& url, GURL* image_url);
void QueueCacheRequest(
const GURL& url, const GURL& image_url,
base::Callback<void(const GURL&, const SkBitmap*)> callback);
void ServeFromCacheOrNetwork(
const GURL& url, const GURL& image_url,
base::Callback<void(const GURL&, const SkBitmap*)> callback);
// Will return false if no bitmap was found corresponding to |url|, else
// return true and call |callback| with the found bitmap.
bool ServeFromCache(
const GURL& url,
base::Callback<void(const GURL&, const SkBitmap*)> callback);
// Returns null if the |url| had no entry in the cache.
SkBitmap* GetBitmapFromCache(const GURL& url);
void StartOrQueueNetworkRequest(
const GURL& url, const GURL& image_url,
base::Callback<void(const GURL&, const SkBitmap*)> callback);
// Inherited from BitmapFetcherDelegate. Runs on the UI thread.
virtual void OnFetchComplete(const GURL image_url,
const SkBitmap* bitmap) OVERRIDE;
// Save the image bitmap in the cache and in the database.
void SaveImage(const GURL& url, const SkBitmap& bitmap);
// Database callback methods.
// Will initiate loading the entries.
void OnDatabaseInit(bool success);
// Will transfer the loaded |entries| in memory (|image_map_|).
void OnDatabaseLoad(bool success, scoped_ptr<ImageDataVector> entries);
void OnDatabaseSave(bool success);
// Take entries from the database and put them in the local cache.
void LoadEntriesInCache(scoped_ptr<ImageDataVector> entries);
void ServePendingCacheRequests();
// From SkBitmap to the vector of JPEG-encoded bytes, |dst|. Visible only for
// testing.
static bool EncodeImage(const SkBitmap& bitmap,
std::vector<unsigned char>* dest);
// Map from URL to image URL. Should be kept up to date when a new
// SuggestionsProfile is available.
std::map<GURL, GURL> image_url_map_;
// Map from each image URL to the request information (associated website // Map from each image URL to the request information (associated website
// url, fetcher, pending callbacks). // url, fetcher, pending callbacks).
ImageRequestMap pending_net_requests_; ImageRequestMap pending_net_requests_;
// Map from website URL to request information, used for pending cache ImageFetcherDelegate* delegate_;
// requests while the database hasn't loaded.
ImageRequestMap pending_cache_requests_;
// Holding the bitmaps in memory, keyed by website URL string.
ImageMap image_map_;
net::URLRequestContextGetter* url_request_context_; net::URLRequestContextGetter* url_request_context_;
scoped_ptr<leveldb_proto::ProtoDatabase<ImageData> > database_; DISALLOW_COPY_AND_ASSIGN(ImageFetcherImpl);
bool database_ready_;
base::WeakPtrFactory<ImageManagerImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ImageManagerImpl);
}; };
} // namespace suggestions } // namespace suggestions
#endif // CHROME_BROWSER_SEARCH_SUGGESTIONS_IMAGE_MANAGER_IMPL_H_ #endif // CHROME_BROWSER_SEARCH_SUGGESTIONS_IMAGE_FETCHER_IMPL_H_
// Copyright 2014 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/search/suggestions/image_fetcher_impl.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/suggestions/image_fetcher_delegate.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
class SkBitmap;
namespace suggestions {
namespace {
const char kTestUrl[] = "http://go.com/";
const char kTestImagePath[] = "files/image_decoding/droids.png";
const char kInvalidImagePath[] = "files/DOESNOTEXIST";
const base::FilePath::CharType kDocRoot[] =
FILE_PATH_LITERAL("chrome/test/data");
class TestImageFetcherDelegate : public ImageFetcherDelegate {
public:
TestImageFetcherDelegate()
: num_delegate_valid_called_(0),
num_delegate_null_called_(0) {}
virtual ~TestImageFetcherDelegate() {};
// Perform additional tasks when an image has been fetched.
virtual void OnImageFetched(const GURL& url, const SkBitmap* bitmap)
OVERRIDE {
if (bitmap) {
num_delegate_valid_called_++;
} else {
num_delegate_null_called_++;
}
};
int num_delegate_valid_called() { return num_delegate_valid_called_; }
int num_delegate_null_called() { return num_delegate_null_called_; }
private:
int num_delegate_valid_called_;
int num_delegate_null_called_;
};
} // end namespace
class ImageFetcherImplBrowserTest : public InProcessBrowserTest {
protected:
ImageFetcherImplBrowserTest()
: num_callback_valid_called_(0),
num_callback_null_called_(0),
test_server_(net::SpawnedTestServer::TYPE_HTTP,
net::SpawnedTestServer::kLocalhost,
base::FilePath(kDocRoot)) {}
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
ASSERT_TRUE(test_server_.Start());
InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
}
virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
test_server_.Stop();
}
ImageFetcherImpl* CreateImageFetcher() {
ImageFetcherImpl* fetcher =
new ImageFetcherImpl(browser()->profile()->GetRequestContext());
fetcher->SetImageFetcherDelegate(&delegate_);
return fetcher;
}
void OnImageAvailable(base::RunLoop* loop,
const GURL& url,
const SkBitmap* bitmap) {
if (bitmap) {
num_callback_valid_called_++;
} else {
num_callback_null_called_++;
}
loop->Quit();
}
void StartOrQueueNetworkRequestHelper(const GURL& image_url) {
scoped_ptr<ImageFetcherImpl> image_fetcher_(CreateImageFetcher());
base::RunLoop run_loop;
image_fetcher_->StartOrQueueNetworkRequest(
GURL(kTestUrl),
image_url,
base::Bind(&ImageFetcherImplBrowserTest::OnImageAvailable,
base::Unretained(this), &run_loop));
run_loop.Run();
}
int num_callback_valid_called_;
int num_callback_null_called_;
net::SpawnedTestServer test_server_;
TestImageFetcherDelegate delegate_;
DISALLOW_COPY_AND_ASSIGN(ImageFetcherImplBrowserTest);
};
IN_PROC_BROWSER_TEST_F(ImageFetcherImplBrowserTest, NormalFetch) {
GURL image_url(test_server_.GetURL(kTestImagePath).spec());
StartOrQueueNetworkRequestHelper(image_url);
EXPECT_EQ(1, num_callback_valid_called_);
EXPECT_EQ(0, num_callback_null_called_);
EXPECT_EQ(1, delegate_.num_delegate_valid_called());
EXPECT_EQ(0, delegate_.num_delegate_null_called());
}
IN_PROC_BROWSER_TEST_F(ImageFetcherImplBrowserTest, MultipleFetch) {
GURL image_url(test_server_.GetURL(kTestImagePath).spec());
for (int i = 0; i < 5; i++) {
StartOrQueueNetworkRequestHelper(image_url);
}
EXPECT_EQ(5, num_callback_valid_called_);
EXPECT_EQ(0, num_callback_null_called_);
EXPECT_EQ(5, delegate_.num_delegate_valid_called());
EXPECT_EQ(0, delegate_.num_delegate_null_called());
}
IN_PROC_BROWSER_TEST_F(ImageFetcherImplBrowserTest, InvalidFetch) {
GURL invalid_image_url(test_server_.GetURL(kInvalidImagePath).spec());
StartOrQueueNetworkRequestHelper(invalid_image_url);
EXPECT_EQ(0, num_callback_valid_called_);
EXPECT_EQ(1, num_callback_null_called_);
EXPECT_EQ(0, delegate_.num_delegate_valid_called());
EXPECT_EQ(1, delegate_.num_delegate_null_called());
}
} // namespace suggestions
// Copyright 2014 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 <string>
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/search/suggestions/image_manager_impl.h"
#include "chrome/test/base/testing_profile.h"
#include "components/leveldb_proto/proto_database.h"
#include "components/leveldb_proto/testing/fake_db.h"
#include "components/suggestions/proto/suggestions.pb.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace {
using leveldb_proto::test::FakeDB;
using suggestions::ImageData;
using suggestions::ImageManagerImpl;
typedef base::hash_map<std::string, ImageData> EntryMap;
const char kTestUrl[] = "http://go.com/";
const char kTestImageUrl[] = "http://thumb.com/anchor_download_test.png";
class ImageManagerImplTest : public testing::Test {
protected:
ImageManagerImpl* CreateImageManager(Profile* profile) {
FakeDB<ImageData>* fake_db = new FakeDB<ImageData>(&db_model_);
return new ImageManagerImpl(
profile->GetRequestContext(),
scoped_ptr<leveldb_proto::ProtoDatabase<ImageData> >(fake_db),
FakeDB<ImageData>::DirectoryForTestDB());
}
content::TestBrowserThreadBundle thread_bundle_;
EntryMap db_model_;
};
} // namespace
namespace suggestions {
TEST_F(ImageManagerImplTest, InitializeTest) {
SuggestionsProfile suggestions_profile;
ChromeSuggestion* suggestion = suggestions_profile.add_suggestions();
suggestion->set_url(kTestUrl);
suggestion->set_thumbnail(kTestImageUrl);
TestingProfile profile;
scoped_ptr<ImageManagerImpl> image_manager(
CreateImageManager(&profile));
image_manager->Initialize(suggestions_profile);
GURL output;
EXPECT_TRUE(image_manager->GetImageURL(GURL(kTestUrl), &output));
EXPECT_EQ(GURL(kTestImageUrl), output);
EXPECT_FALSE(image_manager->GetImageURL(GURL("http://b.com"), &output));
}
} // namespace suggestions
...@@ -8,12 +8,13 @@ ...@@ -8,12 +8,13 @@
#include "base/prefs/pref_service.h" #include "base/prefs/pref_service.h"
#include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/suggestions/image_manager_impl.h" #include "chrome/browser/search/suggestions/image_fetcher_impl.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/leveldb_proto/proto_database.h" #include "components/leveldb_proto/proto_database.h"
#include "components/leveldb_proto/proto_database_impl.h" #include "components/leveldb_proto/proto_database_impl.h"
#include "components/pref_registry/pref_registry_syncable.h" #include "components/pref_registry/pref_registry_syncable.h"
#include "components/suggestions/blacklist_store.h" #include "components/suggestions/blacklist_store.h"
#include "components/suggestions/image_fetcher.h"
#include "components/suggestions/image_manager.h" #include "components/suggestions/image_manager.h"
#include "components/suggestions/proto/suggestions.pb.h" #include "components/suggestions/proto/suggestions.pb.h"
#include "components/suggestions/suggestions_service.h" #include "components/suggestions/suggestions_service.h"
...@@ -66,18 +67,19 @@ KeyedService* SuggestionsServiceFactory::BuildServiceInstanceFor( ...@@ -66,18 +67,19 @@ KeyedService* SuggestionsServiceFactory::BuildServiceInstanceFor(
new BlacklistStore(the_profile->GetPrefs())); new BlacklistStore(the_profile->GetPrefs()));
scoped_ptr<leveldb_proto::ProtoDatabaseImpl<ImageData> > db( scoped_ptr<leveldb_proto::ProtoDatabaseImpl<ImageData> > db(
new leveldb_proto::ProtoDatabaseImpl<ImageData>( new leveldb_proto::ProtoDatabaseImpl<ImageData>(background_task_runner));
background_task_runner));
base::FilePath database_dir( base::FilePath database_dir(
the_profile->GetPath().Append(FILE_PATH_LITERAL("Thumbnails"))); the_profile->GetPath().Append(FILE_PATH_LITERAL("Thumbnails")));
scoped_ptr<ImageManagerImpl> thumbnail_manager(new ImageManagerImpl( scoped_ptr<ImageFetcherImpl> image_fetcher(
the_profile->GetRequestContext(), new ImageFetcherImpl(the_profile->GetRequestContext()));
scoped_ptr<ImageManager> thumbnail_manager(new ImageManager(
image_fetcher.PassAs<ImageFetcher>(),
db.PassAs<leveldb_proto::ProtoDatabase<ImageData> >(), database_dir)); db.PassAs<leveldb_proto::ProtoDatabase<ImageData> >(), database_dir));
return new SuggestionsService( return new SuggestionsService(
the_profile->GetRequestContext(), suggestions_store.Pass(), the_profile->GetRequestContext(), suggestions_store.Pass(),
thumbnail_manager.PassAs<ImageManager>(), blacklist_store.Pass()); thumbnail_manager.Pass(), blacklist_store.Pass());
} }
void SuggestionsServiceFactory::RegisterProfilePrefs( void SuggestionsServiceFactory::RegisterProfilePrefs(
......
...@@ -1070,8 +1070,8 @@ ...@@ -1070,8 +1070,8 @@
'browser/search/most_visited_iframe_source.h', 'browser/search/most_visited_iframe_source.h',
'browser/search/search.cc', 'browser/search/search.cc',
'browser/search/search.h', 'browser/search/search.h',
'browser/search/suggestions/image_manager_impl.cc', 'browser/search/suggestions/image_fetcher_impl.cc',
'browser/search/suggestions/image_manager_impl.h', 'browser/search/suggestions/image_fetcher_impl.h',
'browser/search/suggestions/suggestions_service_factory.cc', 'browser/search/suggestions/suggestions_service_factory.cc',
'browser/search/suggestions/suggestions_service_factory.h', 'browser/search/suggestions/suggestions_service_factory.h',
'browser/search/suggestions/suggestions_source.cc', 'browser/search/suggestions/suggestions_source.cc',
......
...@@ -1376,7 +1376,7 @@ ...@@ -1376,7 +1376,7 @@
'browser/safe_browsing/safe_browsing_blocking_page_test.cc', 'browser/safe_browsing/safe_browsing_blocking_page_test.cc',
'browser/safe_browsing/safe_browsing_service_browsertest.cc', 'browser/safe_browsing/safe_browsing_service_browsertest.cc',
'browser/safe_browsing/safe_browsing_test.cc', 'browser/safe_browsing/safe_browsing_test.cc',
'browser/search/suggestions/image_manager_impl_browsertest.cc', 'browser/search/suggestions/image_fetcher_impl_browsertest.cc',
'browser/service_process/service_process_control_browsertest.cc', 'browser/service_process/service_process_control_browsertest.cc',
'browser/services/gcm/fake_gcm_profile_service.cc', 'browser/services/gcm/fake_gcm_profile_service.cc',
'browser/services/gcm/fake_gcm_profile_service.h', 'browser/services/gcm/fake_gcm_profile_service.h',
......
...@@ -696,7 +696,6 @@ ...@@ -696,7 +696,6 @@
'browser/search/most_visited_iframe_source_unittest.cc', 'browser/search/most_visited_iframe_source_unittest.cc',
'browser/search/search_android_unittest.cc', 'browser/search/search_android_unittest.cc',
'browser/search/search_unittest.cc', 'browser/search/search_unittest.cc',
'browser/search/suggestions/image_manager_impl_unittest.cc',
'browser/search_engines/default_search_pref_migration_unittest.cc', 'browser/search_engines/default_search_pref_migration_unittest.cc',
'browser/search_engines/search_provider_install_data_unittest.cc', 'browser/search_engines/search_provider_install_data_unittest.cc',
'browser/search_engines/template_url_scraper_unittest.cc', 'browser/search_engines/template_url_scraper_unittest.cc',
......
...@@ -216,6 +216,7 @@ ...@@ -216,6 +216,7 @@
'storage_monitor/storage_monitor_unittest.cc', 'storage_monitor/storage_monitor_unittest.cc',
'storage_monitor/storage_monitor_win_unittest.cc', 'storage_monitor/storage_monitor_win_unittest.cc',
'suggestions/blacklist_store_unittest.cc', 'suggestions/blacklist_store_unittest.cc',
'suggestions/image_manager_unittest.cc',
'suggestions/suggestions_service_unittest.cc', 'suggestions/suggestions_service_unittest.cc',
'suggestions/suggestions_store_unittest.cc', 'suggestions/suggestions_store_unittest.cc',
'sync_driver/non_ui_data_type_controller_unittest.cc', 'sync_driver/non_ui_data_type_controller_unittest.cc',
......
...@@ -19,10 +19,14 @@ ...@@ -19,10 +19,14 @@
'components.gyp:keyed_service_core', 'components.gyp:keyed_service_core',
'components.gyp:pref_registry', 'components.gyp:pref_registry',
'components.gyp:variations', 'components.gyp:variations',
'components.gyp:variations_http_provider',
], ],
'sources': [ 'sources': [
'suggestions/blacklist_store.cc', 'suggestions/blacklist_store.cc',
'suggestions/blacklist_store.h', 'suggestions/blacklist_store.h',
'suggestions/image_fetcher.h',
'suggestions/image_fetcher_delegate.h',
'suggestions/image_manager.cc',
'suggestions/image_manager.h', 'suggestions/image_manager.h',
'suggestions/proto/suggestions.proto', 'suggestions/proto/suggestions.proto',
'suggestions/suggestions_pref_names.cc', 'suggestions/suggestions_pref_names.cc',
......
...@@ -6,6 +6,9 @@ static_library("suggestions") { ...@@ -6,6 +6,9 @@ static_library("suggestions") {
sources = [ sources = [
"blacklist_store.cc", "blacklist_store.cc",
"blacklist_store.h", "blacklist_store.h",
"image_fetcher.h",
"image_fetcher_delegate.h",
"image_manager.cc",
"image_manager.h", "image_manager.h",
"suggestions_pref_names.cc", "suggestions_pref_names.cc",
"suggestions_pref_names.h", "suggestions_pref_names.h",
......
include_rules = [ include_rules = [
"+components/keyed_service/core", "+components/keyed_service/core",
"+components/leveldb_proto",
"+components/pref_registry", "+components/pref_registry",
"+components/variations", "+components/variations",
"+net", "+net",
......
// Copyright 2014 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 COMPONENTS_SUGGESTIONS_IMAGE_FETCHER_H_
#define COMPONENTS_SUGGESTIONS_IMAGE_FETCHER_H_
#include "base/callback.h"
#include "components/suggestions/image_fetcher_delegate.h"
#include "url/gurl.h"
class SkBitmap;
namespace suggestions {
// A class used to fetch server images.
class ImageFetcher {
public:
ImageFetcher() {}
virtual ~ImageFetcher() {}
virtual void SetImageFetcherDelegate(ImageFetcherDelegate* delegate) = 0;
virtual void StartOrQueueNetworkRequest(
const GURL& url, const GURL& image_url,
base::Callback<void(const GURL&, const SkBitmap*)> callback) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(ImageFetcher);
};
} // namespace suggestions
#endif // COMPONENTS_SUGGESTIONS_IMAGE_FETCHER_H_
// Copyright 2014 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 COMPONENTS_SUGGESTIONS_IMAGE_FETCHER_DELEGATE_H_
#define COMPONENTS_SUGGESTIONS_IMAGE_FETCHER_DELEGATE_H_
class GURL;
class SkBitmap;
namespace suggestions {
class ImageFetcherDelegate {
public:
ImageFetcherDelegate() {}
// Called when an image was fetched. |url| represents the website for which
// the image was fetched. |bitmap| is deleted once out of scope.
virtual void OnImageFetched(const GURL& url, const SkBitmap* bitmap) = 0;
protected:
virtual ~ImageFetcherDelegate() {}
DISALLOW_COPY_AND_ASSIGN(ImageFetcherDelegate);
};
} // namespace suggestions
#endif // COMPONENTS_SUGGESTIONS_IMAGE_FETCHER_DELEGATE_H_
...@@ -2,12 +2,10 @@ ...@@ -2,12 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/browser/search/suggestions/image_manager_impl.h" #include "components/suggestions/image_manager.h"
#include "base/memory/ref_counted_memory.h" #include "base/bind.h"
#include "content/public/browser/browser_thread.h" #include "components/suggestions/image_fetcher.h"
#include "net/base/load_flags.h"
#include "net/url_request/url_request_context_getter.h"
#include "ui/gfx/codec/jpeg_codec.h" #include "ui/gfx/codec/jpeg_codec.h"
using leveldb_proto::ProtoDatabase; using leveldb_proto::ProtoDatabase;
...@@ -23,30 +21,27 @@ SkBitmap* DecodeImage(const std::vector<unsigned char>& encoded_data) { ...@@ -23,30 +21,27 @@ SkBitmap* DecodeImage(const std::vector<unsigned char>& encoded_data) {
namespace suggestions { namespace suggestions {
ImageManagerImpl::ImageManagerImpl() : weak_ptr_factory_(this) {} ImageManager::ImageManager() : weak_ptr_factory_(this) {}
ImageManagerImpl::ImageManagerImpl( ImageManager::ImageManager(scoped_ptr<ImageFetcher> image_fetcher,
net::URLRequestContextGetter* url_request_context,
scoped_ptr<ProtoDatabase<ImageData> > database, scoped_ptr<ProtoDatabase<ImageData> > database,
const base::FilePath& database_dir) const base::FilePath& database_dir)
: url_request_context_(url_request_context), : image_fetcher_(image_fetcher.Pass()),
database_(database.Pass()), database_(database.Pass()),
database_ready_(false), database_ready_(false),
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
database_->Init(database_dir, base::Bind(&ImageManagerImpl::OnDatabaseInit, image_fetcher_->SetImageFetcherDelegate(this);
database_->Init(database_dir, base::Bind(&ImageManager::OnDatabaseInit,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
ImageManagerImpl::~ImageManagerImpl() {} ImageManager::~ImageManager() {}
ImageManagerImpl::ImageRequest::ImageRequest() : fetcher(NULL) {} ImageManager::ImageCacheRequest::ImageCacheRequest() {}
ImageManagerImpl::ImageRequest::ImageRequest(chrome::BitmapFetcher* f) ImageManager::ImageCacheRequest::~ImageCacheRequest() {}
: fetcher(f) {}
ImageManagerImpl::ImageRequest::~ImageRequest() { delete fetcher; } void ImageManager::Initialize(const SuggestionsProfile& suggestions) {
void ImageManagerImpl::Initialize(const SuggestionsProfile& suggestions) {
image_url_map_.clear(); image_url_map_.clear();
for (int i = 0; i < suggestions.suggestions_size(); ++i) { for (int i = 0; i < suggestions.suggestions_size(); ++i) {
const ChromeSuggestion& suggestion = suggestions.suggestions(i); const ChromeSuggestion& suggestion = suggestions.suggestions(i);
...@@ -56,10 +51,10 @@ void ImageManagerImpl::Initialize(const SuggestionsProfile& suggestions) { ...@@ -56,10 +51,10 @@ void ImageManagerImpl::Initialize(const SuggestionsProfile& suggestions) {
} }
} }
void ImageManagerImpl::GetImageForURL( void ImageManager::GetImageForURL(
const GURL& url, const GURL& url,
base::Callback<void(const GURL&, const SkBitmap*)> callback) { base::Callback<void(const GURL&, const SkBitmap*)> callback) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); DCHECK(thread_checker_.CalledOnValidThread());
// If |url| is not found in |image_url_map_|, then invoke |callback| with // If |url| is not found in |image_url_map_|, then invoke |callback| with
// NULL since there is no associated image for this |url|. // NULL since there is no associated image for this |url|.
GURL image_url; GURL image_url;
...@@ -79,40 +74,44 @@ void ImageManagerImpl::GetImageForURL( ...@@ -79,40 +74,44 @@ void ImageManagerImpl::GetImageForURL(
ServeFromCacheOrNetwork(url, image_url, callback); ServeFromCacheOrNetwork(url, image_url, callback);
} }
bool ImageManagerImpl::GetImageURL(const GURL& url, GURL* image_url) { void ImageManager::OnImageFetched(const GURL& url, const SkBitmap* bitmap) {
SaveImage(url, *bitmap);
}
bool ImageManager::GetImageURL(const GURL& url, GURL* image_url) {
std::map<GURL, GURL>::iterator it = image_url_map_.find(url); std::map<GURL, GURL>::iterator it = image_url_map_.find(url);
if (it == image_url_map_.end()) return false; // Not found. if (it == image_url_map_.end()) return false; // Not found.
*image_url = it->second; *image_url = it->second;
return true; return true;
} }
void ImageManagerImpl::QueueCacheRequest( void ImageManager::QueueCacheRequest(
const GURL& url, const GURL& image_url, const GURL& url, const GURL& image_url,
base::Callback<void(const GURL&, const SkBitmap*)> callback) { base::Callback<void(const GURL&, const SkBitmap*)> callback) {
// To be served when the database has loaded. // To be served when the database has loaded.
ImageRequestMap::iterator it = pending_cache_requests_.find(url); ImageCacheRequestMap::iterator it = pending_cache_requests_.find(url);
if (it == pending_cache_requests_.end()) { if (it == pending_cache_requests_.end()) {
ImageRequest request(NULL); ImageCacheRequest request;
request.url = url; request.url = url;
request.image_url = image_url; request.image_url = image_url;
request.callbacks.push_back(callback); request.callbacks.push_back(callback);
pending_cache_requests_[url].swap(&request); pending_cache_requests_[url] = request;
} else { } else {
// Request already queued for this url. // Request already queued for this url.
it->second.callbacks.push_back(callback); it->second.callbacks.push_back(callback);
} }
} }
void ImageManagerImpl::ServeFromCacheOrNetwork( void ImageManager::ServeFromCacheOrNetwork(
const GURL& url, const GURL& image_url, const GURL& url, const GURL& image_url,
base::Callback<void(const GURL&, const SkBitmap*)> callback) { base::Callback<void(const GURL&, const SkBitmap*)> callback) {
// If there is a image available in memory, return it. // If there is a image available in memory, return it.
if (!ServeFromCache(url, callback)) { if (!ServeFromCache(url, callback)) {
StartOrQueueNetworkRequest(url, image_url, callback); image_fetcher_->StartOrQueueNetworkRequest(url, image_url, callback);
} }
} }
bool ImageManagerImpl::ServeFromCache( bool ImageManager::ServeFromCache(
const GURL& url, const GURL& url,
base::Callback<void(const GURL&, const SkBitmap*)> callback) { base::Callback<void(const GURL&, const SkBitmap*)> callback) {
SkBitmap* bitmap = GetBitmapFromCache(url); SkBitmap* bitmap = GetBitmapFromCache(url);
...@@ -123,7 +122,7 @@ bool ImageManagerImpl::ServeFromCache( ...@@ -123,7 +122,7 @@ bool ImageManagerImpl::ServeFromCache(
return false; return false;
} }
SkBitmap* ImageManagerImpl::GetBitmapFromCache(const GURL& url) { SkBitmap* ImageManager::GetBitmapFromCache(const GURL& url) {
ImageMap::iterator image_iter = image_map_.find(url.spec()); ImageMap::iterator image_iter = image_map_.find(url.spec());
if (image_iter != image_map_.end()) { if (image_iter != image_map_.end()) {
return &image_iter->second; return &image_iter->second;
...@@ -131,56 +130,7 @@ SkBitmap* ImageManagerImpl::GetBitmapFromCache(const GURL& url) { ...@@ -131,56 +130,7 @@ SkBitmap* ImageManagerImpl::GetBitmapFromCache(const GURL& url) {
return NULL; return NULL;
} }
void ImageManagerImpl::StartOrQueueNetworkRequest( void ImageManager::SaveImage(const GURL& url, const SkBitmap& bitmap) {
const GURL& url, const GURL& image_url,
base::Callback<void(const GURL&, const SkBitmap*)> callback) {
// Before starting to fetch the image. Look for a request in progress for
// |image_url|, and queue if appropriate.
ImageRequestMap::iterator it = pending_net_requests_.find(image_url);
if (it == pending_net_requests_.end()) {
// |image_url| is not being fetched, so create a request and initiate
// the fetch.
ImageRequest request(new chrome::BitmapFetcher(image_url, this));
request.url = url;
request.callbacks.push_back(callback);
request.fetcher->Start(
url_request_context_, std::string(),
net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
net::LOAD_NORMAL);
pending_net_requests_[image_url].swap(&request);
} else {
// Request in progress. Register as an interested callback.
it->second.callbacks.push_back(callback);
}
}
void ImageManagerImpl::OnFetchComplete(const GURL image_url,
const SkBitmap* bitmap) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
ImageRequestMap::iterator image_iter = pending_net_requests_.find(image_url);
DCHECK(image_iter != pending_net_requests_.end());
ImageRequest* request = &image_iter->second;
// Here |bitmap| could be NULL or a pointer to a bitmap which is owned by the
// BitmapFetcher and which ceases to exist after this function. Pass the
// un-owned pointer to the registered callbacks.
for (CallbackVector::iterator callback_iter = request->callbacks.begin();
callback_iter != request->callbacks.end(); ++callback_iter) {
callback_iter->Run(request->url, bitmap);
}
// Save the bitmap to the in-memory model as well as the database, because it
// is now the freshest representation of the image.
// TODO(mathp): Handle null (no image found), possible deletion in DB.
if (bitmap) SaveImage(request->url, *bitmap);
// Erase the completed ImageRequest.
pending_net_requests_.erase(image_iter);
}
void ImageManagerImpl::SaveImage(const GURL& url, const SkBitmap& bitmap) {
// Update the image map. // Update the image map.
image_map_.insert(std::make_pair(url.spec(), bitmap)); image_map_.insert(std::make_pair(url.spec(), bitmap));
...@@ -200,23 +150,23 @@ void ImageManagerImpl::SaveImage(const GURL& url, const SkBitmap& bitmap) { ...@@ -200,23 +150,23 @@ void ImageManagerImpl::SaveImage(const GURL& url, const SkBitmap& bitmap) {
new std::vector<std::string>()); new std::vector<std::string>());
entries_to_save->push_back(std::make_pair(data.url(), data)); entries_to_save->push_back(std::make_pair(data.url(), data));
database_->UpdateEntries(entries_to_save.Pass(), keys_to_remove.Pass(), database_->UpdateEntries(entries_to_save.Pass(), keys_to_remove.Pass(),
base::Bind(&ImageManagerImpl::OnDatabaseSave, base::Bind(&ImageManager::OnDatabaseSave,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
} }
void ImageManagerImpl::OnDatabaseInit(bool success) { void ImageManager::OnDatabaseInit(bool success) {
if (!success) { if (!success) {
DVLOG(1) << "Image database init failed."; DVLOG(1) << "Image database init failed.";
database_.reset(); database_.reset();
ServePendingCacheRequests(); ServePendingCacheRequests();
return; return;
} }
database_->LoadEntries(base::Bind(&ImageManagerImpl::OnDatabaseLoad, database_->LoadEntries(base::Bind(&ImageManager::OnDatabaseLoad,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
void ImageManagerImpl::OnDatabaseLoad(bool success, void ImageManager::OnDatabaseLoad(bool success,
scoped_ptr<ImageDataVector> entries) { scoped_ptr<ImageDataVector> entries) {
if (!success) { if (!success) {
DVLOG(1) << "Image database load failed."; DVLOG(1) << "Image database load failed.";
...@@ -230,7 +180,7 @@ void ImageManagerImpl::OnDatabaseLoad(bool success, ...@@ -230,7 +180,7 @@ void ImageManagerImpl::OnDatabaseLoad(bool success,
ServePendingCacheRequests(); ServePendingCacheRequests();
} }
void ImageManagerImpl::OnDatabaseSave(bool success) { void ImageManager::OnDatabaseSave(bool success) {
if (!success) { if (!success) {
DVLOG(1) << "Image database save failed."; DVLOG(1) << "Image database save failed.";
database_.reset(); database_.reset();
...@@ -238,7 +188,7 @@ void ImageManagerImpl::OnDatabaseSave(bool success) { ...@@ -238,7 +188,7 @@ void ImageManagerImpl::OnDatabaseSave(bool success) {
} }
} }
void ImageManagerImpl::LoadEntriesInCache(scoped_ptr<ImageDataVector> entries) { void ImageManager::LoadEntriesInCache(scoped_ptr<ImageDataVector> entries) {
for (ImageDataVector::iterator it = entries->begin(); it != entries->end(); for (ImageDataVector::iterator it = entries->begin(); it != entries->end();
++it) { ++it) {
std::vector<unsigned char> encoded_data(it->data().begin(), std::vector<unsigned char> encoded_data(it->data().begin(),
...@@ -251,10 +201,10 @@ void ImageManagerImpl::LoadEntriesInCache(scoped_ptr<ImageDataVector> entries) { ...@@ -251,10 +201,10 @@ void ImageManagerImpl::LoadEntriesInCache(scoped_ptr<ImageDataVector> entries) {
} }
} }
void ImageManagerImpl::ServePendingCacheRequests() { void ImageManager::ServePendingCacheRequests() {
for (ImageRequestMap::iterator it = pending_cache_requests_.begin(); for (ImageCacheRequestMap::iterator it = pending_cache_requests_.begin();
it != pending_cache_requests_.end(); ++it) { it != pending_cache_requests_.end(); ++it) {
const ImageRequest& request = it->second; const ImageCacheRequest& request = it->second;
for (CallbackVector::const_iterator callback_it = request.callbacks.begin(); for (CallbackVector::const_iterator callback_it = request.callbacks.begin();
callback_it != request.callbacks.end(); ++callback_it) { callback_it != request.callbacks.end(); ++callback_it) {
ServeFromCacheOrNetwork(request.url, request.image_url, *callback_it); ServeFromCacheOrNetwork(request.url, request.image_url, *callback_it);
...@@ -263,7 +213,7 @@ void ImageManagerImpl::ServePendingCacheRequests() { ...@@ -263,7 +213,7 @@ void ImageManagerImpl::ServePendingCacheRequests() {
} }
// static // static
bool ImageManagerImpl::EncodeImage(const SkBitmap& bitmap, bool ImageManager::EncodeImage(const SkBitmap& bitmap,
std::vector<unsigned char>* dest) { std::vector<unsigned char>* dest) {
SkAutoLockPixels bitmap_lock(bitmap); SkAutoLockPixels bitmap_lock(bitmap);
if (!bitmap.readyToDraw() || bitmap.isNull()) { if (!bitmap.readyToDraw() || bitmap.isNull()) {
......
...@@ -5,31 +5,146 @@ ...@@ -5,31 +5,146 @@
#ifndef COMPONENTS_SUGGESTIONS_IMAGE_MANAGER_H_ #ifndef COMPONENTS_SUGGESTIONS_IMAGE_MANAGER_H_
#define COMPONENTS_SUGGESTIONS_IMAGE_MANAGER_H_ #define COMPONENTS_SUGGESTIONS_IMAGE_MANAGER_H_
#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/hash_tables.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/threading/thread_checker.h"
#include "components/leveldb_proto/proto_database.h"
#include "components/suggestions/image_fetcher_delegate.h"
#include "components/suggestions/proto/suggestions.pb.h" #include "components/suggestions/proto/suggestions.pb.h"
#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia.h"
#include "url/gurl.h" #include "url/gurl.h"
namespace net {
class URLRequestContextGetter;
}
namespace suggestions { namespace suggestions {
// An interface to retrieve images related to a specific URL. class ImageData;
class ImageManager { class ImageFetcher;
class SuggestionsProfile;
// A class used to fetch server images asynchronously and manage the caching
// layer (both in memory and on disk).
class ImageManager : public ImageFetcherDelegate {
public: public:
ImageManager() {} typedef std::vector<ImageData> ImageDataVector;
virtual ~ImageManager() {}
ImageManager(scoped_ptr<ImageFetcher> image_fetcher,
scoped_ptr<leveldb_proto::ProtoDatabase<ImageData> > database,
const base::FilePath& database_dir);
virtual ~ImageManager();
// (Re)Initializes states using data received from a SuggestionService. We're virtual void Initialize(const SuggestionsProfile& suggestions);
// not doing this in the constructor because an instance may be long-lived.
virtual void Initialize(const SuggestionsProfile& suggestions) = 0;
// Retrieves stored image for website |url| asynchronously. Calls |callback| // Should be called from the UI thread.
// with Bitmap pointer if found, and NULL otherwise.
virtual void GetImageForURL( virtual void GetImageForURL(
const GURL& url, const GURL& url,
base::Callback<void(const GURL&, const SkBitmap*)> callback) = 0; base::Callback<void(const GURL&, const SkBitmap*)> callback);
protected:
// Perform additional tasks when an image has been fetched.
virtual void OnImageFetched(const GURL& url, const SkBitmap* bitmap) OVERRIDE;
private: private:
friend class MockImageManager;
friend class ImageManagerTest;
FRIEND_TEST_ALL_PREFIXES(ImageManagerTest, InitializeTest);
FRIEND_TEST_ALL_PREFIXES(ImageManagerTest, GetImageForURLNetworkCacheHit);
FRIEND_TEST_ALL_PREFIXES(ImageManagerTest,
GetImageForURLNetworkCacheNotInitialized);
// Used for testing.
ImageManager();
typedef std::vector<base::Callback<void(const GURL&, const SkBitmap*)> >
CallbackVector;
typedef base::hash_map<std::string, SkBitmap> ImageMap;
// State related to an image fetch (associated website url, image_url,
// pending callbacks).
struct ImageCacheRequest {
ImageCacheRequest();
~ImageCacheRequest();
GURL url;
GURL image_url;
// Queue for pending callbacks, which may accumulate while the request is in
// flight.
CallbackVector callbacks;
};
typedef std::map<const GURL, ImageCacheRequest> ImageCacheRequestMap;
// Looks up image URL for |url|. If found, writes the result to |image_url|
// and returns true. Otherwise just returns false.
bool GetImageURL(const GURL& url, GURL* image_url);
void QueueCacheRequest(
const GURL& url, const GURL& image_url,
base::Callback<void(const GURL&, const SkBitmap*)> callback);
void ServeFromCacheOrNetwork(
const GURL& url, const GURL& image_url,
base::Callback<void(const GURL&, const SkBitmap*)> callback);
// Will return false if no bitmap was found corresponding to |url|, else
// return true and call |callback| with the found bitmap.
bool ServeFromCache(
const GURL& url,
base::Callback<void(const GURL&, const SkBitmap*)> callback);
// Returns null if the |url| had no entry in the cache.
SkBitmap* GetBitmapFromCache(const GURL& url);
// Save the image bitmap in the cache and in the database.
void SaveImage(const GURL& url, const SkBitmap& bitmap);
// Database callback methods.
// Will initiate loading the entries.
void OnDatabaseInit(bool success);
// Will transfer the loaded |entries| in memory (|image_map_|).
void OnDatabaseLoad(bool success, scoped_ptr<ImageDataVector> entries);
void OnDatabaseSave(bool success);
// Take entries from the database and put them in the local cache.
void LoadEntriesInCache(scoped_ptr<ImageDataVector> entries);
void ServePendingCacheRequests();
// From SkBitmap to the vector of JPEG-encoded bytes, |dst|. Visible only for
// testing.
static bool EncodeImage(const SkBitmap& bitmap,
std::vector<unsigned char>* dest);
// Map from URL to image URL. Should be kept up to date when a new
// SuggestionsProfile is available.
std::map<GURL, GURL> image_url_map_;
// Map from website URL to request information, used for pending cache
// requests while the database hasn't loaded.
ImageCacheRequestMap pending_cache_requests_;
// Holding the bitmaps in memory, keyed by website URL string.
ImageMap image_map_;
scoped_ptr<ImageFetcher> image_fetcher_;
scoped_ptr<leveldb_proto::ProtoDatabase<ImageData> > database_;
bool database_ready_;
base::WeakPtrFactory<ImageManager> weak_ptr_factory_;
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(ImageManager); DISALLOW_COPY_AND_ASSIGN(ImageManager);
}; };
......
...@@ -6,106 +6,66 @@ ...@@ -6,106 +6,66 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/search/suggestions/image_manager_impl.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "components/leveldb_proto/proto_database.h" #include "components/leveldb_proto/proto_database.h"
#include "components/leveldb_proto/testing/fake_db.h" #include "components/leveldb_proto/testing/fake_db.h"
#include "components/suggestions/image_fetcher.h"
#include "components/suggestions/image_fetcher_delegate.h"
#include "components/suggestions/image_manager.h"
#include "components/suggestions/proto/suggestions.pb.h" #include "components/suggestions/proto/suggestions.pb.h"
#include "content/public/test/test_utils.h" #include "testing/gmock/include/gmock/gmock.h"
#include "net/base/load_flags.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_skia.h"
#include "url/gurl.h" #include "url/gurl.h"
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::_;
namespace suggestions { namespace suggestions {
const char kTestUrl1[] = "http://go.com/"; const char kTestUrl1[] = "http://go.com/";
const char kTestUrl2[] = "http://goal.com/"; const char kTestUrl2[] = "http://goal.com/";
const char kTestBitmapUrl[] = "http://test.com";
const char kTestImagePath[] = "files/image_decoding/droids.png"; const char kTestImagePath[] = "files/image_decoding/droids.png";
const char kInvalidImagePath[] = "files/DOESNOTEXIST"; const char kInvalidImagePath[] = "files/DOESNOTEXIST";
const base::FilePath::CharType kDocRoot[] =
FILE_PATH_LITERAL("chrome/test/data");
using chrome::BitmapFetcher;
using content::BrowserThread;
using leveldb_proto::test::FakeDB; using leveldb_proto::test::FakeDB;
using suggestions::ImageData; using suggestions::ImageData;
using suggestions::ImageManagerImpl; using suggestions::ImageManager;
typedef base::hash_map<std::string, ImageData> EntryMap; typedef base::hash_map<std::string, ImageData> EntryMap;
void AddEntry(const ImageData& d, EntryMap* map) { (*map)[d.url()] = d; } void AddEntry(const ImageData& d, EntryMap* map) { (*map)[d.url()] = d; }
class ImageManagerImplBrowserTest : public InProcessBrowserTest { class MockImageFetcher : public suggestions::ImageFetcher {
public: public:
ImageManagerImplBrowserTest() MockImageFetcher() {}
: num_callback_null_called_(0), virtual ~MockImageFetcher() {}
num_callback_valid_called_(0), MOCK_METHOD3(StartOrQueueNetworkRequest,
test_server_(net::SpawnedTestServer::TYPE_HTTP, void(const GURL&, const GURL&,
net::SpawnedTestServer::kLocalhost, base::Callback<void(const GURL&, const SkBitmap*)>));
base::FilePath(kDocRoot)) {} MOCK_METHOD1(SetImageFetcherDelegate, void(ImageFetcherDelegate*));
};
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
ASSERT_TRUE(test_server_.Start());
InProcessBrowserTest::SetUpInProcessBrowserTestFixture();
}
virtual void TearDownInProcessBrowserTestFixture() OVERRIDE { class ImageManagerTest : public testing::Test {
test_server_.Stop(); public:
} ImageManagerTest()
: mock_image_fetcher_(NULL),
num_callback_null_called_(0),
num_callback_valid_called_(0) {}
virtual void SetUpOnMainThread() OVERRIDE { virtual void SetUp() OVERRIDE {
fake_db_ = new FakeDB<ImageData>(&db_model_); fake_db_ = new FakeDB<ImageData>(&db_model_);
image_manager_.reset(CreateImageManagerImpl(fake_db_)); image_manager_.reset(CreateImageManager(fake_db_));
} }
virtual void TearDownOnMainThread() OVERRIDE { virtual void TearDown() OVERRIDE {
fake_db_ = NULL; fake_db_ = NULL;
db_model_.clear(); db_model_.clear();
image_manager_.reset(); image_manager_.reset();
test_image_manager_.reset();
} }
void InitializeTestBitmapData() { void InitializeDefaultImageMapAndDatabase(ImageManager* image_manager,
FakeDB<ImageData>* test_fake_db = new FakeDB<ImageData>(&db_model_); FakeDB<ImageData>* fake_db) {
test_image_manager_.reset(CreateImageManagerImpl(test_fake_db));
suggestions::SuggestionsProfile suggestions_profile;
suggestions::ChromeSuggestion* suggestion =
suggestions_profile.add_suggestions();
suggestion->set_url(kTestBitmapUrl);
suggestion->set_thumbnail(test_server_.GetURL(kTestImagePath).spec());
test_image_manager_->Initialize(suggestions_profile);
// Initialize empty database.
test_fake_db->InitCallback(true);
test_fake_db->LoadCallback(true);
base::RunLoop run_loop;
// Fetch existing URL.
test_image_manager_->GetImageForURL(
GURL(kTestBitmapUrl),
base::Bind(&ImageManagerImplBrowserTest::OnTestImageAvailable,
base::Unretained(this), &run_loop));
run_loop.Run();
}
void OnTestImageAvailable(base::RunLoop* loop, const GURL& url,
const SkBitmap* bitmap) {
CHECK(bitmap);
// Copy the resource locally.
test_bitmap_ = *bitmap;
loop->Quit();
}
void InitializeDefaultImageMapAndDatabase(
ImageManagerImpl* image_manager, FakeDB<ImageData>* fake_db) {
CHECK(image_manager); CHECK(image_manager);
CHECK(fake_db); CHECK(fake_db);
...@@ -113,7 +73,7 @@ class ImageManagerImplBrowserTest : public InProcessBrowserTest { ...@@ -113,7 +73,7 @@ class ImageManagerImplBrowserTest : public InProcessBrowserTest {
suggestions::ChromeSuggestion* suggestion = suggestions::ChromeSuggestion* suggestion =
suggestions_profile.add_suggestions(); suggestions_profile.add_suggestions();
suggestion->set_url(kTestUrl1); suggestion->set_url(kTestUrl1);
suggestion->set_thumbnail(test_server_.GetURL(kTestImagePath).spec()); suggestion->set_thumbnail(kTestImagePath);
image_manager->Initialize(suggestions_profile); image_manager->Initialize(suggestions_profile);
...@@ -123,10 +83,13 @@ class ImageManagerImplBrowserTest : public InProcessBrowserTest { ...@@ -123,10 +83,13 @@ class ImageManagerImplBrowserTest : public InProcessBrowserTest {
} }
ImageData GetSampleImageData(const std::string& url) { ImageData GetSampleImageData(const std::string& url) {
// Create test bitmap.
SkBitmap bm;
bm.allocN32Pixels(2, 2);
ImageData data; ImageData data;
data.set_url(url); data.set_url(url);
std::vector<unsigned char> encoded; std::vector<unsigned char> encoded;
EXPECT_TRUE(ImageManagerImpl::EncodeImage(test_bitmap_, &encoded)); EXPECT_TRUE(ImageManager::EncodeImage(bm, &encoded));
data.set_data(std::string(encoded.begin(), encoded.end())); data.set_data(std::string(encoded.begin(), encoded.end()));
return data; return data;
} }
...@@ -135,127 +98,81 @@ class ImageManagerImplBrowserTest : public InProcessBrowserTest { ...@@ -135,127 +98,81 @@ class ImageManagerImplBrowserTest : public InProcessBrowserTest {
const SkBitmap* bitmap) { const SkBitmap* bitmap) {
if (bitmap) { if (bitmap) {
num_callback_valid_called_++; num_callback_valid_called_++;
std::vector<unsigned char> actual;
std::vector<unsigned char> expected;
EXPECT_TRUE(ImageManagerImpl::EncodeImage(*bitmap, &actual));
EXPECT_TRUE(ImageManagerImpl::EncodeImage(test_bitmap_, &expected));
// Check first 100 bytes.
std::string actual_str(actual.begin(), actual.begin() + 100);
std::string expected_str(expected.begin(), expected.begin() + 100);
EXPECT_EQ(expected_str, actual_str);
} else { } else {
num_callback_null_called_++; num_callback_null_called_++;
} }
loop->Quit(); loop->Quit();
} }
ImageManagerImpl* CreateImageManagerImpl(FakeDB<ImageData>* fake_db) { ImageManager* CreateImageManager(FakeDB<ImageData>* fake_db) {
return new ImageManagerImpl( mock_image_fetcher_ = new StrictMock<MockImageFetcher>();
browser()->profile()->GetRequestContext(), EXPECT_CALL(*mock_image_fetcher_, SetImageFetcherDelegate(_));
return new ImageManager(
scoped_ptr<ImageFetcher>(mock_image_fetcher_),
scoped_ptr<leveldb_proto::ProtoDatabase<ImageData> >(fake_db), scoped_ptr<leveldb_proto::ProtoDatabase<ImageData> >(fake_db),
FakeDB<ImageData>::DirectoryForTestDB()); FakeDB<ImageData>::DirectoryForTestDB());
} }
EntryMap db_model_; EntryMap db_model_;
// Owned by the ImageManagerImpl under test. // Owned by the ImageManager under test.
FakeDB<ImageData>* fake_db_; FakeDB<ImageData>* fake_db_;
SkBitmap test_bitmap_; MockImageFetcher* mock_image_fetcher_;
scoped_ptr<ImageManagerImpl> test_image_manager_;
int num_callback_null_called_; int num_callback_null_called_;
int num_callback_valid_called_; int num_callback_valid_called_;
net::SpawnedTestServer test_server_;
// Under test. // Under test.
scoped_ptr<ImageManagerImpl> image_manager_; scoped_ptr<ImageManager> image_manager_;
}; };
IN_PROC_BROWSER_TEST_F(ImageManagerImplBrowserTest, GetImageForURLNetwork) { TEST_F(ImageManagerTest, InitializeTest) {
InitializeTestBitmapData(); SuggestionsProfile suggestions_profile;
InitializeDefaultImageMapAndDatabase(image_manager_.get(), fake_db_); ChromeSuggestion* suggestion = suggestions_profile.add_suggestions();
suggestion->set_url(kTestUrl1);
base::RunLoop run_loop; suggestion->set_thumbnail(kTestImagePath);
// Fetch existing URL.
image_manager_->GetImageForURL(
GURL(kTestUrl1),
base::Bind(&ImageManagerImplBrowserTest::OnImageAvailable,
base::Unretained(this), &run_loop));
run_loop.Run();
EXPECT_EQ(0, num_callback_null_called_); image_manager_->Initialize(suggestions_profile);
EXPECT_EQ(1, num_callback_valid_called_);
base::RunLoop run_loop2; GURL output;
// Fetch non-existing URL. EXPECT_TRUE(image_manager_->GetImageURL(GURL(kTestUrl1), &output));
image_manager_->GetImageForURL( EXPECT_EQ(GURL(kTestImagePath), output);
GURL(kTestUrl2),
base::Bind(&ImageManagerImplBrowserTest::OnImageAvailable,
base::Unretained(this), &run_loop2));
run_loop2.Run();
EXPECT_EQ(1, num_callback_null_called_); EXPECT_FALSE(image_manager_->GetImageURL(GURL("http://b.com"), &output));
EXPECT_EQ(1, num_callback_valid_called_);
} }
IN_PROC_BROWSER_TEST_F(ImageManagerImplBrowserTest, TEST_F(ImageManagerTest, GetImageForURLNetwork) {
GetImageForURLNetworkMultiple) {
InitializeTestBitmapData();
InitializeDefaultImageMapAndDatabase(image_manager_.get(), fake_db_); InitializeDefaultImageMapAndDatabase(image_manager_.get(), fake_db_);
// Fetch non-existing URL, and add more while request is in flight. // We expect the fetcher to go to network and call the callback.
base::RunLoop run_loop; EXPECT_CALL(*mock_image_fetcher_, StartOrQueueNetworkRequest(_, _, _));
for (int i = 0; i < 5; i++) {
// Fetch existing URL. // Fetch existing URL.
image_manager_->GetImageForURL( base::RunLoop run_loop;
GURL(kTestUrl1), image_manager_->GetImageForURL(GURL(kTestUrl1),
base::Bind(&ImageManagerImplBrowserTest::OnImageAvailable, base::Bind(&ImageManagerTest::OnImageAvailable,
base::Unretained(this), &run_loop)); base::Unretained(this), &run_loop));
}
run_loop.Run();
EXPECT_EQ(0, num_callback_null_called_);
EXPECT_EQ(5, num_callback_valid_called_);
}
IN_PROC_BROWSER_TEST_F(ImageManagerImplBrowserTest,
GetImageForURLNetworkInvalid) {
SuggestionsProfile suggestions_profile;
ChromeSuggestion* suggestion = suggestions_profile.add_suggestions();
suggestion->set_url(kTestUrl1);
suggestion->set_thumbnail(test_server_.GetURL(kInvalidImagePath).spec());
image_manager_->Initialize(suggestions_profile);
// Database will be initialized and loaded without anything in it.
fake_db_->InitCallback(true);
fake_db_->LoadCallback(true);
base::RunLoop run_loop; // Will not go to network and use the fetcher since URL is invalid.
// Fetch existing URL that has invalid image. // Fetch non-existing URL.
image_manager_->GetImageForURL( image_manager_->GetImageForURL(GURL(kTestUrl2),
GURL(kTestUrl1), base::Bind(&ImageManagerTest::OnImageAvailable,
base::Bind(&ImageManagerImplBrowserTest::OnImageAvailable,
base::Unretained(this), &run_loop)); base::Unretained(this), &run_loop));
run_loop.Run(); run_loop.Run();
EXPECT_EQ(1, num_callback_null_called_); EXPECT_EQ(1, num_callback_null_called_);
EXPECT_EQ(0, num_callback_valid_called_);
} }
IN_PROC_BROWSER_TEST_F(ImageManagerImplBrowserTest, TEST_F(ImageManagerTest, GetImageForURLNetworkCacheHit) {
GetImageForURLNetworkCacheHit) {
InitializeTestBitmapData();
SuggestionsProfile suggestions_profile; SuggestionsProfile suggestions_profile;
ChromeSuggestion* suggestion = suggestions_profile.add_suggestions(); ChromeSuggestion* suggestion = suggestions_profile.add_suggestions();
suggestion->set_url(kTestUrl1); suggestion->set_url(kTestUrl1);
// The URL we set is invalid, to show that it will fail from network. // The URL we set is invalid, to show that it will fail from network.
suggestion->set_thumbnail(test_server_.GetURL(kInvalidImagePath).spec()); suggestion->set_thumbnail(kInvalidImagePath);
// Create the ImageManagerImpl with an added entry in the database. // Create the ImageManager with an added entry in the database.
AddEntry(GetSampleImageData(kTestUrl1), &db_model_); AddEntry(GetSampleImageData(kTestUrl1), &db_model_);
FakeDB<ImageData>* fake_db = new FakeDB<ImageData>(&db_model_); FakeDB<ImageData>* fake_db = new FakeDB<ImageData>(&db_model_);
image_manager_.reset(CreateImageManagerImpl(fake_db)); image_manager_.reset(CreateImageManager(fake_db));
image_manager_->Initialize(suggestions_profile); image_manager_->Initialize(suggestions_profile);
fake_db->InitCallback(true); fake_db->InitCallback(true);
fake_db->LoadCallback(true); fake_db->LoadCallback(true);
...@@ -264,9 +181,8 @@ IN_PROC_BROWSER_TEST_F(ImageManagerImplBrowserTest, ...@@ -264,9 +181,8 @@ IN_PROC_BROWSER_TEST_F(ImageManagerImplBrowserTest,
EXPECT_FALSE(bitmap->isNull()); EXPECT_FALSE(bitmap->isNull());
base::RunLoop run_loop; base::RunLoop run_loop;
image_manager_->GetImageForURL( image_manager_->GetImageForURL(GURL(kTestUrl1),
GURL(kTestUrl1), base::Bind(&ImageManagerTest::OnImageAvailable,
base::Bind(&ImageManagerImplBrowserTest::OnImageAvailable,
base::Unretained(this), &run_loop)); base::Unretained(this), &run_loop));
run_loop.Run(); run_loop.Run();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment