• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/command_line.h"
8 #include "base/file_path.h"
9 #include "base/string_number_conversions.h"
10 #include "base/string_util.h"
11 #include "base/threading/thread_restrictions.h"
12 #include "base/time.h"
13 #include "base/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/prefs/pref_service.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/sync/sync_ui_util.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/extensions/extension.h"
21 #include "chrome/common/net/url_fetcher.h"
22 #include "chrome/common/web_resource/web_resource_unpacker.h"
23 #include "content/browser/browser_thread.h"
24 #include "content/common/notification_service.h"
25 #include "googleurl/src/gurl.h"
26 #include "net/base/load_flags.h"
27 #include "net/url_request/url_request_status.h"
28 
29 class WebResourceService::WebResourceFetcher
30     : public URLFetcher::Delegate {
31  public:
WebResourceFetcher(WebResourceService * web_resource_service)32   explicit WebResourceFetcher(WebResourceService* web_resource_service) :
33       ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_factory_(this)),
34       web_resource_service_(web_resource_service) {
35   }
36 
37   // Delay initial load of resource data into cache so as not to interfere
38   // with startup time.
StartAfterDelay(int64 delay_ms)39   void StartAfterDelay(int64 delay_ms) {
40     MessageLoop::current()->PostDelayedTask(FROM_HERE,
41         fetcher_factory_.NewRunnableMethod(&WebResourceFetcher::StartFetch),
42                                            delay_ms);
43   }
44 
45   // Initializes the fetching of data from the resource server.  Data
46   // load calls OnURLFetchComplete.
StartFetch()47   void StartFetch() {
48     // Balanced in OnURLFetchComplete.
49     web_resource_service_->AddRef();
50     // First, put our next cache load on the MessageLoop.
51     MessageLoop::current()->PostDelayedTask(FROM_HERE,
52         fetcher_factory_.NewRunnableMethod(&WebResourceFetcher::StartFetch),
53             web_resource_service_->cache_update_delay_);
54     // If we are still fetching data, exit.
55     if (web_resource_service_->in_fetch_)
56       return;
57     else
58       web_resource_service_->in_fetch_ = true;
59 
60     std::string web_resource_server =
61         web_resource_service_->web_resource_server_;
62     if (web_resource_service_->apply_locale_to_url_) {
63       std::string locale = g_browser_process->GetApplicationLocale();
64       web_resource_server.append(locale);
65     }
66 
67     url_fetcher_.reset(new URLFetcher(GURL(
68         web_resource_server),
69         URLFetcher::GET, this));
70     // Do not let url fetcher affect existing state in profile (by setting
71     // cookies, for example.
72     url_fetcher_->set_load_flags(net::LOAD_DISABLE_CACHE |
73         net::LOAD_DO_NOT_SAVE_COOKIES);
74     net::URLRequestContextGetter* url_request_context_getter =
75         web_resource_service_->profile_->GetRequestContext();
76     url_fetcher_->set_request_context(url_request_context_getter);
77     url_fetcher_->Start();
78   }
79 
80   // From URLFetcher::Delegate.
OnURLFetchComplete(const URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const ResponseCookies & cookies,const std::string & data)81   void OnURLFetchComplete(const URLFetcher* source,
82                           const GURL& url,
83                           const net::URLRequestStatus& status,
84                           int response_code,
85                           const ResponseCookies& cookies,
86                           const std::string& data) {
87     // Delete the URLFetcher when this function exits.
88     scoped_ptr<URLFetcher> clean_up_fetcher(url_fetcher_.release());
89 
90     // Don't parse data if attempt to download was unsuccessful.
91     // Stop loading new web resource data, and silently exit.
92     if (!status.is_success() || (response_code != 200))
93       return;
94 
95     web_resource_service_->UpdateResourceCache(data);
96     web_resource_service_->Release();
97   }
98 
99  private:
100   // So that we can delay our start so as not to affect start-up time; also,
101   // so that we can schedule future cache updates.
102   ScopedRunnableMethodFactory<WebResourceFetcher> fetcher_factory_;
103 
104   // The tool that fetches the url data from the server.
105   scoped_ptr<URLFetcher> url_fetcher_;
106 
107   // Our owner and creator. Ref counted.
108   WebResourceService* web_resource_service_;
109 };
110 
111 // This class coordinates a web resource unpack and parse task which is run in
112 // a separate process.  Results are sent back to this class and routed to
113 // the WebResourceService.
114 class WebResourceService::UnpackerClient
115     : public UtilityProcessHost::Client {
116  public:
UnpackerClient(WebResourceService * web_resource_service,const std::string & json_data)117   UnpackerClient(WebResourceService* web_resource_service,
118                  const std::string& json_data)
119     : web_resource_service_(web_resource_service),
120       json_data_(json_data), got_response_(false) {
121   }
122 
Start()123   void Start() {
124     AddRef();  // balanced in Cleanup.
125 
126     // TODO(willchan): Look for a better signal of whether we're in a unit test
127     // or not. Using |resource_dispatcher_host_| for this is pretty lame.
128     // If we don't have a resource_dispatcher_host_, assume we're in
129     // a test and run the unpacker directly in-process.
130     bool use_utility_process =
131         web_resource_service_->resource_dispatcher_host_ != NULL &&
132         !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
133     if (use_utility_process) {
134       BrowserThread::ID thread_id;
135       CHECK(BrowserThread::GetCurrentThreadIdentifier(&thread_id));
136       BrowserThread::PostTask(
137           BrowserThread::IO, FROM_HERE,
138           NewRunnableMethod(this, &UnpackerClient::StartProcessOnIOThread,
139                             thread_id));
140     } else {
141       WebResourceUnpacker unpacker(json_data_);
142       if (unpacker.Run()) {
143         OnUnpackWebResourceSucceeded(*unpacker.parsed_json());
144       } else {
145         OnUnpackWebResourceFailed(unpacker.error_message());
146       }
147     }
148   }
149 
150  private:
~UnpackerClient()151   ~UnpackerClient() {}
152 
153   // UtilityProcessHost::Client
OnProcessCrashed(int exit_code)154   virtual void OnProcessCrashed(int exit_code) {
155     if (got_response_)
156       return;
157 
158     OnUnpackWebResourceFailed(
159         "Chrome crashed while trying to retrieve web resources.");
160   }
161 
OnUnpackWebResourceSucceeded(const DictionaryValue & parsed_json)162   virtual void OnUnpackWebResourceSucceeded(
163       const DictionaryValue& parsed_json) {
164     web_resource_service_->OnWebResourceUnpacked(parsed_json);
165     Cleanup();
166   }
167 
OnUnpackWebResourceFailed(const std::string & error_message)168   virtual void OnUnpackWebResourceFailed(const std::string& error_message) {
169     web_resource_service_->EndFetch();
170     Cleanup();
171   }
172 
173   // Release reference and set got_response_.
Cleanup()174   void Cleanup() {
175     if (got_response_)
176       return;
177 
178     got_response_ = true;
179     Release();
180   }
181 
StartProcessOnIOThread(BrowserThread::ID thread_id)182   void StartProcessOnIOThread(BrowserThread::ID thread_id) {
183     UtilityProcessHost* host = new UtilityProcessHost(this, thread_id);
184     // TODO(mrc): get proper file path when we start using web resources
185     // that need to be unpacked.
186     host->StartWebResourceUnpacker(json_data_);
187   }
188 
189   scoped_refptr<WebResourceService> web_resource_service_;
190 
191   // Holds raw JSON string.
192   const std::string& json_data_;
193 
194   // True if we got a response from the utility process and have cleaned up
195   // already.
196   bool got_response_;
197 };
198 
WebResourceService(Profile * profile,PrefService * prefs,const char * web_resource_server,bool apply_locale_to_url,NotificationType::Type notification_type,const char * last_update_time_pref_name,int start_fetch_delay,int cache_update_delay)199 WebResourceService::WebResourceService(
200     Profile* profile,
201     PrefService* prefs,
202     const char* web_resource_server,
203     bool apply_locale_to_url,
204     NotificationType::Type notification_type,
205     const char* last_update_time_pref_name,
206     int start_fetch_delay,
207     int cache_update_delay)
208     : prefs_(prefs),
209       profile_(profile),
210       ALLOW_THIS_IN_INITIALIZER_LIST(service_factory_(this)),
211       in_fetch_(false),
212       web_resource_server_(web_resource_server),
213       apply_locale_to_url_(apply_locale_to_url),
214       notification_type_(notification_type),
215       last_update_time_pref_name_(last_update_time_pref_name),
216       start_fetch_delay_(start_fetch_delay),
217       cache_update_delay_(cache_update_delay),
218       web_resource_update_scheduled_(false) {
219   DCHECK(prefs);
220   DCHECK(profile);
221   prefs_->RegisterStringPref(last_update_time_pref_name, "0");
222   resource_dispatcher_host_ = g_browser_process->resource_dispatcher_host();
223   web_resource_fetcher_.reset(new WebResourceFetcher(this));
224 }
225 
~WebResourceService()226 WebResourceService::~WebResourceService() { }
227 
PostNotification(int64 delay_ms)228 void WebResourceService::PostNotification(int64 delay_ms) {
229   if (web_resource_update_scheduled_)
230     return;
231   if (delay_ms > 0) {
232     web_resource_update_scheduled_ = true;
233     MessageLoop::current()->PostDelayedTask(FROM_HERE,
234         service_factory_.NewRunnableMethod(
235             &WebResourceService::WebResourceStateChange), delay_ms);
236   } else if (delay_ms == 0) {
237     WebResourceStateChange();
238   }
239 }
240 
EndFetch()241 void WebResourceService::EndFetch() {
242   in_fetch_ = false;
243 }
244 
OnWebResourceUnpacked(const DictionaryValue & parsed_json)245 void WebResourceService::OnWebResourceUnpacked(
246   const DictionaryValue& parsed_json) {
247   Unpack(parsed_json);
248   EndFetch();
249 }
250 
WebResourceStateChange()251 void WebResourceService::WebResourceStateChange() {
252   web_resource_update_scheduled_ = false;
253   if (notification_type_ == NotificationType::NOTIFICATION_TYPE_COUNT)
254     return;
255   NotificationService* service = NotificationService::current();
256   service->Notify(notification_type_,
257                   Source<WebResourceService>(this),
258                   NotificationService::NoDetails());
259 }
260 
StartAfterDelay()261 void WebResourceService::StartAfterDelay() {
262   int64 delay = start_fetch_delay_;
263   // Check whether we have ever put a value in the web resource cache;
264   // if so, pull it out and see if it's time to update again.
265   if (prefs_->HasPrefPath(last_update_time_pref_name_)) {
266     std::string last_update_pref =
267         prefs_->GetString(last_update_time_pref_name_);
268     if (!last_update_pref.empty()) {
269       double last_update_value;
270       base::StringToDouble(last_update_pref, &last_update_value);
271       int64 ms_until_update = cache_update_delay_ -
272           static_cast<int64>((base::Time::Now() - base::Time::FromDoubleT(
273           last_update_value)).InMilliseconds());
274       delay = ms_until_update > cache_update_delay_ ?
275           cache_update_delay_ : (ms_until_update < start_fetch_delay_ ?
276                                 start_fetch_delay_ : ms_until_update);
277     }
278   }
279   // Start fetch and wait for UpdateResourceCache.
280   web_resource_fetcher_->StartAfterDelay(delay);
281 }
282 
UpdateResourceCache(const std::string & json_data)283 void WebResourceService::UpdateResourceCache(const std::string& json_data) {
284   UnpackerClient* client = new UnpackerClient(this, json_data);
285   client->Start();
286 
287   // Set cache update time in preferences.
288   prefs_->SetString(last_update_time_pref_name_,
289       base::DoubleToString(base::Time::Now().ToDoubleT()));
290 }
291