Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / history / web_history_service.cc
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/history/web_history_service.h"
6
7 #include "base/bind.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/metrics/histogram.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
16 #include "chrome/browser/signin/signin_manager_factory.h"
17 #include "components/signin/core/browser/profile_oauth2_token_service.h"
18 #include "components/signin/core/browser/signin_manager.h"
19 #include "google_apis/gaia/gaia_urls.h"
20 #include "google_apis/gaia/google_service_auth_error.h"
21 #include "google_apis/gaia/oauth2_token_service.h"
22 #include "net/base/load_flags.h"
23 #include "net/base/url_util.h"
24 #include "net/http/http_status_code.h"
25 #include "net/http/http_util.h"
26 #include "net/url_request/url_fetcher.h"
27 #include "net/url_request/url_fetcher_delegate.h"
28 #include "url/gurl.h"
29
30 namespace history {
31
32 namespace {
33
34 const char kHistoryOAuthScope[] =
35     "https://www.googleapis.com/auth/chromesync";
36
37 const char kHistoryQueryHistoryUrl[] =
38     "https://history.google.com/history/api/lookup?client=chrome";
39
40 const char kHistoryDeleteHistoryUrl[] =
41     "https://history.google.com/history/api/delete?client=chrome";
42
43 const char kPostDataMimeType[] = "text/plain";
44
45 // The maximum number of retries for the URLFetcher requests.
46 const size_t kMaxRetries = 1;
47
48 class RequestImpl : public WebHistoryService::Request,
49                     private OAuth2TokenService::Consumer,
50                     private net::URLFetcherDelegate {
51  public:
52   ~RequestImpl() override {}
53
54   // Returns the response code received from the server, which will only be
55   // valid if the request succeeded.
56   int response_code() { return response_code_; }
57
58   // Returns the contents of the response body received from the server.
59   const std::string& response_body() { return response_body_; }
60
61   bool is_pending() override { return is_pending_; }
62
63  private:
64   friend class history::WebHistoryService;
65
66   typedef base::Callback<void(Request*, bool)> CompletionCallback;
67
68   RequestImpl(Profile* profile,
69               const GURL& url,
70               const CompletionCallback& callback)
71       : OAuth2TokenService::Consumer("web_history"),
72         profile_(profile),
73         url_(url),
74         response_code_(0),
75         auth_retry_count_(0),
76         callback_(callback),
77         is_pending_(false) {
78   }
79
80   // Tells the request to do its thang.
81   void Start() {
82     OAuth2TokenService::ScopeSet oauth_scopes;
83     oauth_scopes.insert(kHistoryOAuthScope);
84
85     ProfileOAuth2TokenService* token_service =
86         ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
87     SigninManagerBase* signin_manager =
88         SigninManagerFactory::GetForProfile(profile_);
89     token_request_ = token_service->StartRequest(
90         signin_manager->GetAuthenticatedAccountId(), oauth_scopes, this);
91     is_pending_ = true;
92   }
93
94   // content::URLFetcherDelegate interface.
95   void OnURLFetchComplete(const net::URLFetcher* source) override {
96     DCHECK_EQ(source, url_fetcher_.get());
97     response_code_ = url_fetcher_->GetResponseCode();
98
99     UMA_HISTOGRAM_CUSTOM_ENUMERATION("WebHistory.OAuthTokenResponseCode",
100         net::HttpUtil::MapStatusCodeForHistogram(response_code_),
101         net::HttpUtil::GetStatusCodesForHistogram());
102
103     // If the response code indicates that the token might not be valid,
104     // invalidate the token and try again.
105     if (response_code_ == net::HTTP_UNAUTHORIZED && ++auth_retry_count_ <= 1) {
106       OAuth2TokenService::ScopeSet oauth_scopes;
107       oauth_scopes.insert(kHistoryOAuthScope);
108       ProfileOAuth2TokenService* token_service =
109           ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
110       SigninManagerBase* signin_manager =
111           SigninManagerFactory::GetForProfile(profile_);
112       token_service->InvalidateToken(
113           signin_manager->GetAuthenticatedAccountId(),
114           oauth_scopes,
115           access_token_);
116
117       access_token_.clear();
118       Start();
119       return;
120     }
121     url_fetcher_->GetResponseAsString(&response_body_);
122     url_fetcher_.reset();
123     is_pending_ = false;
124     callback_.Run(this, true);
125     // It is valid for the callback to delete |this|, so do not access any
126     // members below here.
127   }
128
129   // OAuth2TokenService::Consumer interface.
130   void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
131                          const std::string& access_token,
132                          const base::Time& expiration_time) override {
133     token_request_.reset();
134     DCHECK(!access_token.empty());
135     access_token_ = access_token;
136
137     UMA_HISTOGRAM_BOOLEAN("WebHistory.OAuthTokenCompletion", true);
138
139     // Got an access token -- start the actual API request.
140     url_fetcher_.reset(CreateUrlFetcher(access_token));
141     url_fetcher_->Start();
142   }
143
144   void OnGetTokenFailure(const OAuth2TokenService::Request* request,
145                          const GoogleServiceAuthError& error) override {
146     token_request_.reset();
147     is_pending_ = false;
148
149     UMA_HISTOGRAM_BOOLEAN("WebHistory.OAuthTokenCompletion", false);
150
151     callback_.Run(this, false);
152     // It is valid for the callback to delete |this|, so do not access any
153     // members below here.
154   }
155
156   // Helper for creating a new URLFetcher for the API request.
157   net::URLFetcher* CreateUrlFetcher(const std::string& access_token) {
158     net::URLFetcher::RequestType request_type = post_data_.empty() ?
159         net::URLFetcher::GET : net::URLFetcher::POST;
160     net::URLFetcher* fetcher = net::URLFetcher::Create(
161         url_, request_type, this);
162     fetcher->SetRequestContext(profile_->GetRequestContext());
163     fetcher->SetMaxRetriesOn5xx(kMaxRetries);
164     fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
165                           net::LOAD_DO_NOT_SAVE_COOKIES);
166     fetcher->AddExtraRequestHeader("Authorization: Bearer " + access_token);
167     fetcher->AddExtraRequestHeader("X-Developer-Key: " +
168         GaiaUrls::GetInstance()->oauth2_chrome_client_id());
169     if (request_type == net::URLFetcher::POST)
170       fetcher->SetUploadData(kPostDataMimeType, post_data_);
171     return fetcher;
172   }
173
174   void set_post_data(const std::string& post_data) {
175     post_data_ = post_data;
176   }
177
178   Profile* profile_;
179
180   // The URL of the API endpoint.
181   GURL url_;
182
183   // POST data to be sent with the request (may be empty).
184   std::string post_data_;
185
186   // The OAuth2 access token request.
187   scoped_ptr<OAuth2TokenService::Request> token_request_;
188
189   // The current OAuth2 access token.
190   std::string access_token_;
191
192   // Handles the actual API requests after the OAuth token is acquired.
193   scoped_ptr<net::URLFetcher> url_fetcher_;
194
195   // Holds the response code received from the server.
196   int response_code_;
197
198   // Holds the response body received from the server.
199   std::string response_body_;
200
201   // The number of times this request has already been retried due to
202   // authorization problems.
203   int auth_retry_count_;
204
205   // The callback to execute when the query is complete.
206   CompletionCallback callback_;
207
208   // True if the request was started and has not yet completed, otherwise false.
209   bool is_pending_;
210 };
211
212 // Extracts a JSON-encoded HTTP response into a DictionaryValue.
213 // If |request|'s HTTP response code indicates failure, or if the response
214 // body is not JSON, a null pointer is returned.
215 scoped_ptr<base::DictionaryValue> ReadResponse(RequestImpl* request) {
216   scoped_ptr<base::DictionaryValue> result;
217   if (request->response_code() == net::HTTP_OK) {
218     base::Value* value = base::JSONReader::Read(request->response_body());
219     if (value && value->IsType(base::Value::TYPE_DICTIONARY))
220       result.reset(static_cast<base::DictionaryValue*>(value));
221     else
222       DLOG(WARNING) << "Non-JSON response received from history server.";
223   }
224   return result.Pass();
225 }
226
227 // Converts a time into a string for use as a parameter in a request to the
228 // history server.
229 std::string ServerTimeString(base::Time time) {
230   if (time < base::Time::UnixEpoch()) {
231     return base::Int64ToString(0);
232   } else {
233     return base::Int64ToString(
234         (time - base::Time::UnixEpoch()).InMicroseconds());
235   }
236 }
237
238 // Returns a URL for querying the history server for a query specified by
239 // |options|. |version_info|, if not empty, should be a token that was received
240 // from the server in response to a write operation. It is used to help ensure
241 // read consistency after a write.
242 GURL GetQueryUrl(const base::string16& text_query,
243                  const QueryOptions& options,
244                  const std::string& version_info) {
245   GURL url = GURL(kHistoryQueryHistoryUrl);
246   url = net::AppendQueryParameter(url, "titles", "1");
247
248   // Take |begin_time|, |end_time|, and |max_count| from the original query
249   // options, and convert them to the equivalent URL parameters.
250
251   base::Time end_time =
252       std::min(base::Time::FromInternalValue(options.EffectiveEndTime()),
253                base::Time::Now());
254   url = net::AppendQueryParameter(url, "max", ServerTimeString(end_time));
255
256   if (!options.begin_time.is_null()) {
257     url = net::AppendQueryParameter(
258         url, "min", ServerTimeString(options.begin_time));
259   }
260
261   if (options.max_count) {
262     url = net::AppendQueryParameter(
263         url, "num", base::IntToString(options.max_count));
264   }
265
266   if (!text_query.empty())
267     url = net::AppendQueryParameter(url, "q", base::UTF16ToUTF8(text_query));
268
269   if (!version_info.empty())
270     url = net::AppendQueryParameter(url, "kvi", version_info);
271
272   return url;
273 }
274
275 // Creates a DictionaryValue to hold the parameters for a deletion.
276 // Ownership is passed to the caller.
277 // |url| may be empty, indicating a time-range deletion.
278 base::DictionaryValue* CreateDeletion(
279     const std::string& min_time,
280     const std::string& max_time,
281     const GURL& url) {
282   base::DictionaryValue* deletion = new base::DictionaryValue;
283   deletion->SetString("type", "CHROME_HISTORY");
284   if (url.is_valid())
285     deletion->SetString("url", url.spec());
286   deletion->SetString("min_timestamp_usec", min_time);
287   deletion->SetString("max_timestamp_usec", max_time);
288   return deletion;
289 }
290
291 }  // namespace
292
293 WebHistoryService::Request::Request() {
294 }
295
296 WebHistoryService::Request::~Request() {
297 }
298
299 WebHistoryService::WebHistoryService(Profile* profile)
300     : profile_(profile),
301       weak_ptr_factory_(this) {
302 }
303
304 WebHistoryService::~WebHistoryService() {
305   STLDeleteElements(&pending_expire_requests_);
306 }
307
308 scoped_ptr<WebHistoryService::Request> WebHistoryService::QueryHistory(
309     const base::string16& text_query,
310     const QueryOptions& options,
311     const WebHistoryService::QueryWebHistoryCallback& callback) {
312   // Wrap the original callback into a generic completion callback.
313   RequestImpl::CompletionCallback completion_callback = base::Bind(
314       &WebHistoryService::QueryHistoryCompletionCallback, callback);
315
316   GURL url = GetQueryUrl(text_query, options, server_version_info_);
317   scoped_ptr<RequestImpl> request(
318       new RequestImpl(profile_, url, completion_callback));
319   request->Start();
320   return request.Pass();
321 }
322
323 void WebHistoryService::ExpireHistory(
324     const std::vector<ExpireHistoryArgs>& expire_list,
325     const ExpireWebHistoryCallback& callback) {
326   base::DictionaryValue delete_request;
327   scoped_ptr<base::ListValue> deletions(new base::ListValue);
328   base::Time now = base::Time::Now();
329
330   for (std::vector<ExpireHistoryArgs>::const_iterator it = expire_list.begin();
331        it != expire_list.end(); ++it) {
332     // Convert the times to server timestamps.
333     std::string min_timestamp = ServerTimeString(it->begin_time);
334     // TODO(dubroy): Use sane time (crbug.com/146090) here when it's available.
335     base::Time end_time = it->end_time;
336     if (end_time.is_null() || end_time > now)
337       end_time = now;
338     std::string max_timestamp = ServerTimeString(end_time);
339
340     for (std::set<GURL>::const_iterator url_iterator = it->urls.begin();
341          url_iterator != it->urls.end(); ++url_iterator) {
342       deletions->Append(
343           CreateDeletion(min_timestamp, max_timestamp, *url_iterator));
344     }
345     // If no URLs were specified, delete everything in the time range.
346     if (it->urls.empty())
347       deletions->Append(CreateDeletion(min_timestamp, max_timestamp, GURL()));
348   }
349   delete_request.Set("del", deletions.release());
350   std::string post_data;
351   base::JSONWriter::Write(&delete_request, &post_data);
352
353   GURL url(kHistoryDeleteHistoryUrl);
354
355   // Append the version info token, if it is available, to help ensure
356   // consistency with any previous deletions.
357   if (!server_version_info_.empty())
358     url = net::AppendQueryParameter(url, "kvi", server_version_info_);
359
360   // Wrap the original callback into a generic completion callback.
361   RequestImpl::CompletionCallback completion_callback =
362       base::Bind(&WebHistoryService::ExpireHistoryCompletionCallback,
363                  weak_ptr_factory_.GetWeakPtr(),
364                  callback);
365
366   scoped_ptr<RequestImpl> request(
367       new RequestImpl(profile_, url, completion_callback));
368   request->set_post_data(post_data);
369   request->Start();
370   pending_expire_requests_.insert(request.release());
371 }
372
373 void WebHistoryService::ExpireHistoryBetween(
374     const std::set<GURL>& restrict_urls,
375     base::Time begin_time,
376     base::Time end_time,
377     const ExpireWebHistoryCallback& callback) {
378   std::vector<ExpireHistoryArgs> expire_list(1);
379   expire_list.back().urls = restrict_urls;
380   expire_list.back().begin_time = begin_time;
381   expire_list.back().end_time = end_time;
382   ExpireHistory(expire_list, callback);
383 }
384
385 // static
386 void WebHistoryService::QueryHistoryCompletionCallback(
387     const WebHistoryService::QueryWebHistoryCallback& callback,
388     WebHistoryService::Request* request,
389     bool success) {
390   scoped_ptr<base::DictionaryValue> response_value;
391   if (success)
392     response_value = ReadResponse(static_cast<RequestImpl*>(request));
393   callback.Run(request, response_value.get());
394 }
395
396 void WebHistoryService::ExpireHistoryCompletionCallback(
397     const WebHistoryService::ExpireWebHistoryCallback& callback,
398     WebHistoryService::Request* request,
399     bool success) {
400   scoped_ptr<base::DictionaryValue> response_value;
401   if (success) {
402     response_value = ReadResponse(static_cast<RequestImpl*>(request));
403     if (response_value)
404       response_value->GetString("version_info", &server_version_info_);
405   }
406   callback.Run(response_value.get() && success);
407   // Clean up from pending requests.
408   pending_expire_requests_.erase(request);
409   delete request;
410 }
411
412 }  // namespace history