blob: 5613993dc057282c03a9f8eddbff87af520b46d2 [file] [log] [blame]
// 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/bitmap_fetcher/bitmap_fetcher_service.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/bitmap_fetcher/bitmap_fetcher.h"
#include "chrome/browser/profiles/profile.h"
#include "net/base/load_flags.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace {
const size_t kMaxRequests = 25; // Maximum number of inflight requests allowed.
const int kMaxCacheEntries = 5; // Maximum number of cache entries.
} // namespace.
class BitmapFetcherRequest {
public:
BitmapFetcherRequest(BitmapFetcherService::RequestId request_id,
BitmapFetcherService::Observer* observer);
~BitmapFetcherRequest();
void NotifyImageChanged(const SkBitmap& bitmap);
BitmapFetcherService::RequestId request_id() const { return request_id_; }
// Weak ptr |fetcher| is used to identify associated fetchers.
void set_fetcher(const chrome::BitmapFetcher* fetcher) { fetcher_ = fetcher; }
const chrome::BitmapFetcher* get_fetcher() const { return fetcher_; }
private:
const BitmapFetcherService::RequestId request_id_;
scoped_ptr<BitmapFetcherService::Observer> observer_;
const chrome::BitmapFetcher* fetcher_;
DISALLOW_COPY_AND_ASSIGN(BitmapFetcherRequest);
};
BitmapFetcherRequest::BitmapFetcherRequest(
BitmapFetcherService::RequestId request_id,
BitmapFetcherService::Observer* observer)
: request_id_(request_id), observer_(observer) {
}
BitmapFetcherRequest::~BitmapFetcherRequest() {
}
void BitmapFetcherRequest::NotifyImageChanged(const SkBitmap& bitmap) {
if (!bitmap.empty())
observer_->OnImageChanged(request_id_, bitmap);
}
BitmapFetcherService::CacheEntry::CacheEntry() {
}
BitmapFetcherService::CacheEntry::~CacheEntry() {
}
BitmapFetcherService::BitmapFetcherService(content::BrowserContext* context)
: cache_(kMaxCacheEntries), current_request_id_(1), context_(context) {
}
BitmapFetcherService::~BitmapFetcherService() {
}
void BitmapFetcherService::CancelRequest(int request_id) {
ScopedVector<BitmapFetcherRequest>::iterator iter;
for (iter = requests_.begin(); iter != requests_.end(); ++iter) {
if ((*iter)->request_id() == request_id) {
requests_.erase(iter);
// Deliberately leave the associated fetcher running to populate cache.
return;
}
}
}
BitmapFetcherService::RequestId BitmapFetcherService::RequestImage(
const GURL& url,
Observer* observer) {
// Create a new request, assigning next available request ID.
++current_request_id_;
if (current_request_id_ == REQUEST_ID_INVALID)
++current_request_id_;
int request_id = current_request_id_;
scoped_ptr<BitmapFetcherRequest> request(
new BitmapFetcherRequest(request_id, observer));
// Reject invalid URLs.
if (!url.is_valid())
return REQUEST_ID_INVALID;
// Check for existing images first.
base::OwningMRUCache<GURL, CacheEntry*>::iterator iter = cache_.Get(url);
if (iter != cache_.end()) {
BitmapFetcherService::CacheEntry* entry = iter->second;
request->NotifyImageChanged(*(entry->bitmap.get()));
// There is no request ID associated with this - data is already delivered.
return REQUEST_ID_INVALID;
}
// Limit number of simultaneous in-flight requests.
if (requests_.size() > kMaxRequests)
return REQUEST_ID_INVALID;
// Make sure there's a fetcher for this URL and attach to request.
const chrome::BitmapFetcher* fetcher = EnsureFetcherForUrl(url);
request->set_fetcher(fetcher);
requests_.push_back(request.release());
return requests_.back()->request_id();
}
void BitmapFetcherService::Prefetch(const GURL& url) {
if (url.is_valid())
EnsureFetcherForUrl(url);
}
chrome::BitmapFetcher* BitmapFetcherService::CreateFetcher(const GURL& url) {
chrome::BitmapFetcher* new_fetcher = new chrome::BitmapFetcher(url, this);
new_fetcher->Init(
context_->GetRequestContext(),
std::string(),
net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE,
net::LOAD_NORMAL);
new_fetcher->Start();
return new_fetcher;
}
const chrome::BitmapFetcher* BitmapFetcherService::EnsureFetcherForUrl(
const GURL& url) {
const chrome::BitmapFetcher* fetcher = FindFetcherForUrl(url);
if (fetcher)
return fetcher;
chrome::BitmapFetcher* new_fetcher = CreateFetcher(url);
active_fetchers_.push_back(new_fetcher);
return new_fetcher;
}
const chrome::BitmapFetcher* BitmapFetcherService::FindFetcherForUrl(
const GURL& url) {
for (BitmapFetchers::iterator iter = active_fetchers_.begin();
iter != active_fetchers_.end();
++iter) {
if (url == (*iter)->url())
return *iter;
}
return NULL;
}
void BitmapFetcherService::RemoveFetcher(const chrome::BitmapFetcher* fetcher) {
for (BitmapFetchers::iterator iter = active_fetchers_.begin();
iter != active_fetchers_.end();
++iter) {
if (fetcher == (*iter)) {
active_fetchers_.erase(iter);
return;
}
}
NOTREACHED(); // RemoveFetcher should always result in removal.
}
void BitmapFetcherService::OnFetchComplete(const GURL& url,
const SkBitmap* bitmap) {
DCHECK(bitmap); // can never be NULL, guaranteed by BitmapFetcher.
const chrome::BitmapFetcher* fetcher = FindFetcherForUrl(url);
DCHECK(fetcher);
// Notify all attached requests of completion.
ScopedVector<BitmapFetcherRequest>::iterator iter = requests_.begin();
while (iter != requests_.end()) {
if ((*iter)->get_fetcher() == fetcher) {
(*iter)->NotifyImageChanged(*bitmap);
iter = requests_.erase(iter);
} else {
++iter;
}
}
if (!bitmap->isNull()) {
CacheEntry* entry = new CacheEntry;
entry->bitmap.reset(new SkBitmap(*bitmap));
cache_.Put(fetcher->url(), entry);
}
RemoveFetcher(fetcher);
}