// 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)); // 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->Start( context_->GetRequestContext(), std::string(), net::URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE, net::LOAD_NORMAL); 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); }