1// Copyright (c) 2012 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/web_resource/web_resource_service.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "base/prefs/pref_service.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/time/time.h"
14#include "base/values.h"
15#include "chrome/browser/browser_process.h"
16#include "components/google/core/browser/google_util.h"
17#include "net/base/load_flags.h"
18#include "net/url_request/url_fetcher.h"
19#include "net/url_request/url_request_status.h"
20#include "url/gurl.h"
21
22WebResourceService::WebResourceService(
23    PrefService* prefs,
24    const GURL& web_resource_server,
25    bool apply_locale_to_url,
26    const char* last_update_time_pref_name,
27    int start_fetch_delay_ms,
28    int cache_update_delay_ms)
29    : prefs_(prefs),
30      json_unpacker_(NULL),
31      in_fetch_(false),
32      web_resource_server_(web_resource_server),
33      apply_locale_to_url_(apply_locale_to_url),
34      last_update_time_pref_name_(last_update_time_pref_name),
35      start_fetch_delay_ms_(start_fetch_delay_ms),
36      cache_update_delay_ms_(cache_update_delay_ms),
37      weak_ptr_factory_(this) {
38  resource_request_allowed_notifier_.Init(this);
39  DCHECK(prefs);
40}
41
42WebResourceService::~WebResourceService() {
43  if (in_fetch_)
44    EndFetch();
45}
46
47void WebResourceService::OnUnpackFinished(
48    const base::DictionaryValue& parsed_json) {
49  Unpack(parsed_json);
50  EndFetch();
51}
52
53void WebResourceService::OnUnpackError(const std::string& error_message) {
54  LOG(ERROR) << error_message;
55  EndFetch();
56}
57
58void WebResourceService::EndFetch() {
59  if (json_unpacker_) {
60    json_unpacker_->ClearDelegate();
61    json_unpacker_ = NULL;
62  }
63  in_fetch_ = false;
64}
65
66void WebResourceService::StartAfterDelay() {
67  // If resource requests are not allowed, we'll get a callback when they are.
68  if (resource_request_allowed_notifier_.ResourceRequestsAllowed())
69    OnResourceRequestsAllowed();
70}
71
72void WebResourceService::OnResourceRequestsAllowed() {
73  int64 delay = start_fetch_delay_ms_;
74  // Check whether we have ever put a value in the web resource cache;
75  // if so, pull it out and see if it's time to update again.
76  if (prefs_->HasPrefPath(last_update_time_pref_name_)) {
77    std::string last_update_pref =
78        prefs_->GetString(last_update_time_pref_name_);
79    if (!last_update_pref.empty()) {
80      double last_update_value;
81      base::StringToDouble(last_update_pref, &last_update_value);
82      int64 ms_until_update = cache_update_delay_ms_ -
83          static_cast<int64>((base::Time::Now() - base::Time::FromDoubleT(
84          last_update_value)).InMilliseconds());
85      // Wait at least |start_fetch_delay_ms_|.
86      if (ms_until_update > start_fetch_delay_ms_)
87        delay = ms_until_update;
88    }
89  }
90  // Start fetch and wait for UpdateResourceCache.
91  ScheduleFetch(delay);
92}
93
94// Delay initial load of resource data into cache so as not to interfere
95// with startup time.
96void WebResourceService::ScheduleFetch(int64 delay_ms) {
97  base::MessageLoop::current()->PostDelayedTask(
98      FROM_HERE,
99      base::Bind(&WebResourceService::StartFetch,
100                 weak_ptr_factory_.GetWeakPtr()),
101      base::TimeDelta::FromMilliseconds(delay_ms));
102}
103
104// Initializes the fetching of data from the resource server.  Data
105// load calls OnURLFetchComplete.
106void WebResourceService::StartFetch() {
107  // First, put our next cache load on the MessageLoop.
108  ScheduleFetch(cache_update_delay_ms_);
109
110  // Set cache update time in preferences.
111  prefs_->SetString(last_update_time_pref_name_,
112      base::DoubleToString(base::Time::Now().ToDoubleT()));
113
114  // If we are still fetching data, exit.
115  if (in_fetch_)
116    return;
117  in_fetch_ = true;
118
119  // Balanced in OnURLFetchComplete.
120  AddRef();
121
122  GURL web_resource_server =
123      apply_locale_to_url_
124          ? google_util::AppendGoogleLocaleParam(
125                web_resource_server_, g_browser_process->GetApplicationLocale())
126          : web_resource_server_;
127
128  DVLOG(1) << "WebResourceService StartFetch " << web_resource_server;
129  url_fetcher_.reset(net::URLFetcher::Create(
130      web_resource_server, net::URLFetcher::GET, this));
131  // Do not let url fetcher affect existing state in system context
132  // (by setting cookies, for example).
133  url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE |
134                             net::LOAD_DO_NOT_SEND_COOKIES |
135                             net::LOAD_DO_NOT_SAVE_COOKIES);
136  net::URLRequestContextGetter* url_request_context_getter =
137      g_browser_process->system_request_context();
138  url_fetcher_->SetRequestContext(url_request_context_getter);
139  url_fetcher_->Start();
140}
141
142void WebResourceService::OnURLFetchComplete(const net::URLFetcher* source) {
143  // Delete the URLFetcher when this function exits.
144  scoped_ptr<net::URLFetcher> clean_up_fetcher(url_fetcher_.release());
145
146  if (source->GetStatus().is_success() && source->GetResponseCode() == 200) {
147    std::string data;
148    source->GetResponseAsString(&data);
149
150    // UnpackerClient calls EndFetch and releases itself on completion.
151    json_unpacker_ = JSONAsynchronousUnpacker::Create(this);
152    json_unpacker_->Start(data);
153  } else {
154    // Don't parse data if attempt to download was unsuccessful.
155    // Stop loading new web resource data, and silently exit.
156    // We do not call UnpackerClient, so we need to call EndFetch ourselves.
157    EndFetch();
158  }
159
160  Release();
161}
162