1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/predictors/resource_prefetcher_manager.h"
6
7#include "base/bind.h"
8#include "base/stl_util.h"
9#include "chrome/browser/predictors/resource_prefetch_predictor.h"
10#include "content/public/browser/browser_thread.h"
11#include "net/url_request/url_request.h"
12#include "net/url_request/url_request_context_getter.h"
13
14using content::BrowserThread;
15
16namespace predictors {
17
18ResourcePrefetcherManager::ResourcePrefetcherManager(
19    ResourcePrefetchPredictor* predictor,
20    const ResourcePrefetchPredictorConfig& config,
21    net::URLRequestContextGetter* context_getter)
22    : predictor_(predictor),
23      config_(config),
24      context_getter_(context_getter) {
25  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
26  CHECK(predictor_);
27  CHECK(context_getter_);
28}
29
30ResourcePrefetcherManager::~ResourcePrefetcherManager() {
31  DCHECK(prefetcher_map_.empty())
32      << "Did not call ShutdownOnUIThread or ShutdownOnIOThread. "
33         " Will leak Prefetcher pointers.";
34}
35
36void ResourcePrefetcherManager::ShutdownOnUIThread() {
37  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
38
39  predictor_ = NULL;
40  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
41      base::Bind(&ResourcePrefetcherManager::ShutdownOnIOThread,
42                 this));
43}
44
45void ResourcePrefetcherManager::ShutdownOnIOThread() {
46  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
47  STLDeleteContainerPairSecondPointers(prefetcher_map_.begin(),
48                                       prefetcher_map_.end());
49}
50
51void ResourcePrefetcherManager::MaybeAddPrefetch(
52    const NavigationID& navigation_id,
53    PrefetchKeyType key_type,
54    scoped_ptr<ResourcePrefetcher::RequestVector> requests) {
55  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
56
57  // Don't add a duplicate prefetch for the same host or URL.
58  std::string key = key_type == PREFETCH_KEY_TYPE_HOST ?
59      navigation_id.main_frame_url.host() : navigation_id.main_frame_url.spec();
60  if (ContainsKey(prefetcher_map_, key))
61    return;
62
63  ResourcePrefetcher* prefetcher = new ResourcePrefetcher(
64      this, config_, navigation_id, key_type, requests.Pass());
65  prefetcher_map_.insert(std::make_pair(key, prefetcher));
66  prefetcher->Start();
67}
68
69void ResourcePrefetcherManager::MaybeRemovePrefetch(
70    const NavigationID& navigation_id) {
71  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
72
73  // Look for a URL based prefetch first.
74  PrefetcherMap::iterator it = prefetcher_map_.find(
75      navigation_id.main_frame_url.spec());
76  if (it != prefetcher_map_.end() &&
77      it->second->navigation_id() == navigation_id) {
78    it->second->Stop();
79    return;
80  }
81
82  // No URL based prefetching, look for host based.
83  it = prefetcher_map_.find(navigation_id.main_frame_url.host());
84  if (it != prefetcher_map_.end() &&
85      it->second->navigation_id() == navigation_id) {
86    it->second->Stop();
87  }
88}
89
90void ResourcePrefetcherManager::ResourcePrefetcherFinished(
91    ResourcePrefetcher* resource_prefetcher,
92    ResourcePrefetcher::RequestVector* requests) {
93  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
94
95  // |predictor_| can only be accessed from the UI thread.
96  scoped_ptr<ResourcePrefetcher::RequestVector> scoped_requests(requests);
97  BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
98      base::Bind(&ResourcePrefetcherManager::ResourcePrefetcherFinishedOnUI,
99                 this,
100                 resource_prefetcher->navigation_id(),
101                 resource_prefetcher->key_type(),
102                 base::Passed(&scoped_requests)));
103
104  const GURL& main_frame_url =
105      resource_prefetcher->navigation_id().main_frame_url;
106  const std::string key =
107      resource_prefetcher->key_type() == PREFETCH_KEY_TYPE_HOST ?
108          main_frame_url.host() : main_frame_url.spec();
109  PrefetcherMap::iterator it = prefetcher_map_.find(key);
110  DCHECK(it != prefetcher_map_.end());
111  delete it->second;
112  prefetcher_map_.erase(it);
113}
114
115void ResourcePrefetcherManager::ResourcePrefetcherFinishedOnUI(
116    const NavigationID& navigation_id,
117    PrefetchKeyType key_type,
118    scoped_ptr<ResourcePrefetcher::RequestVector> requests) {
119  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
120
121  // |predictor_| may have been set to NULL if the predictor is shutting down.
122  if (predictor_) {
123    predictor_->FinishedPrefetchForNavigation(navigation_id,
124                                              key_type,
125                                              requests.release());
126  }
127}
128
129net::URLRequestContext* ResourcePrefetcherManager::GetURLRequestContext() {
130  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
131
132  return context_getter_->GetURLRequestContext();
133}
134
135}  // namespace predictors
136