Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(580)

Side by Side Diff: chrome/browser/history/web_history_service.cc

Issue 882753002: Componentize WebHistoryService (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 kHistoryAudioHistoryUrl[] =
44 "https://history.google.com/history/api/lookup?client=audio";
45
46 const char kHistoryAudioHistoryChangeUrl[] =
47 "https://history.google.com/history/api/change";
48
49 const char kPostDataMimeType[] = "text/plain";
50
51 // The maximum number of retries for the URLFetcher requests.
52 const size_t kMaxRetries = 1;
53
54 class RequestImpl : public WebHistoryService::Request,
55 private OAuth2TokenService::Consumer,
56 private net::URLFetcherDelegate {
57 public:
58 ~RequestImpl() override {}
59
60 // Returns the response code received from the server, which will only be
61 // valid if the request succeeded.
62 int GetResponseCode() override { return response_code_; }
63
64 // Returns the contents of the response body received from the server.
65 const std::string& GetResponseBody() override { return response_body_; }
66
67 bool IsPending() override { return is_pending_; }
68
69 private:
70 friend class history::WebHistoryService;
71
72 RequestImpl(Profile* profile,
73 const GURL& url,
74 const WebHistoryService::CompletionCallback& callback)
75 : OAuth2TokenService::Consumer("web_history"),
76 profile_(profile),
77 url_(url),
78 response_code_(0),
79 auth_retry_count_(0),
80 callback_(callback),
81 is_pending_(false) {
82 }
83
84 // Tells the request to do its thang.
85 void Start() override {
86 OAuth2TokenService::ScopeSet oauth_scopes;
87 oauth_scopes.insert(kHistoryOAuthScope);
88
89 ProfileOAuth2TokenService* token_service =
90 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
91 SigninManagerBase* signin_manager =
92 SigninManagerFactory::GetForProfile(profile_);
93 token_request_ = token_service->StartRequest(
94 signin_manager->GetAuthenticatedAccountId(), oauth_scopes, this);
95 is_pending_ = true;
96 }
97
98 // content::URLFetcherDelegate interface.
99 void OnURLFetchComplete(const net::URLFetcher* source) override {
100 DCHECK_EQ(source, url_fetcher_.get());
101 response_code_ = url_fetcher_->GetResponseCode();
102
103 UMA_HISTOGRAM_CUSTOM_ENUMERATION("WebHistory.OAuthTokenResponseCode",
104 net::HttpUtil::MapStatusCodeForHistogram(response_code_),
105 net::HttpUtil::GetStatusCodesForHistogram());
106
107 // If the response code indicates that the token might not be valid,
108 // invalidate the token and try again.
109 if (response_code_ == net::HTTP_UNAUTHORIZED && ++auth_retry_count_ <= 1) {
110 OAuth2TokenService::ScopeSet oauth_scopes;
111 oauth_scopes.insert(kHistoryOAuthScope);
112 ProfileOAuth2TokenService* token_service =
113 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
114 SigninManagerBase* signin_manager =
115 SigninManagerFactory::GetForProfile(profile_);
116 token_service->InvalidateToken(
117 signin_manager->GetAuthenticatedAccountId(),
118 oauth_scopes,
119 access_token_);
120
121 access_token_.clear();
122 Start();
123 return;
124 }
125 url_fetcher_->GetResponseAsString(&response_body_);
126 url_fetcher_.reset();
127 is_pending_ = false;
128 callback_.Run(this, true);
129 // It is valid for the callback to delete |this|, so do not access any
130 // members below here.
131 }
132
133 // OAuth2TokenService::Consumer interface.
134 void OnGetTokenSuccess(const OAuth2TokenService::Request* request,
135 const std::string& access_token,
136 const base::Time& expiration_time) override {
137 token_request_.reset();
138 DCHECK(!access_token.empty());
139 access_token_ = access_token;
140
141 UMA_HISTOGRAM_BOOLEAN("WebHistory.OAuthTokenCompletion", true);
142
143 // Got an access token -- start the actual API request.
144 url_fetcher_.reset(CreateUrlFetcher(access_token));
145 url_fetcher_->Start();
146 }
147
148 void OnGetTokenFailure(const OAuth2TokenService::Request* request,
149 const GoogleServiceAuthError& error) override {
150 token_request_.reset();
151 is_pending_ = false;
152
153 UMA_HISTOGRAM_BOOLEAN("WebHistory.OAuthTokenCompletion", false);
154
155 callback_.Run(this, false);
156 // It is valid for the callback to delete |this|, so do not access any
157 // members below here.
158 }
159
160 // Helper for creating a new URLFetcher for the API request.
161 net::URLFetcher* CreateUrlFetcher(const std::string& access_token) {
162 net::URLFetcher::RequestType request_type = post_data_.empty() ?
163 net::URLFetcher::GET : net::URLFetcher::POST;
164 net::URLFetcher* fetcher = net::URLFetcher::Create(
165 url_, request_type, this);
166 fetcher->SetRequestContext(profile_->GetRequestContext());
167 fetcher->SetMaxRetriesOn5xx(kMaxRetries);
168 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
169 net::LOAD_DO_NOT_SAVE_COOKIES);
170 fetcher->AddExtraRequestHeader("Authorization: Bearer " + access_token);
171 fetcher->AddExtraRequestHeader("X-Developer-Key: " +
172 GaiaUrls::GetInstance()->oauth2_chrome_client_id());
173 if (request_type == net::URLFetcher::POST)
174 fetcher->SetUploadData(kPostDataMimeType, post_data_);
175 return fetcher;
176 }
177
178 void SetPostData(const std::string& post_data) override {
179 post_data_ = post_data;
180 }
181
182 Profile* profile_;
183
184 // The URL of the API endpoint.
185 GURL url_;
186
187 // POST data to be sent with the request (may be empty).
188 std::string post_data_;
189
190 // The OAuth2 access token request.
191 scoped_ptr<OAuth2TokenService::Request> token_request_;
192
193 // The current OAuth2 access token.
194 std::string access_token_;
195
196 // Handles the actual API requests after the OAuth token is acquired.
197 scoped_ptr<net::URLFetcher> url_fetcher_;
198
199 // Holds the response code received from the server.
200 int response_code_;
201
202 // Holds the response body received from the server.
203 std::string response_body_;
204
205 // The number of times this request has already been retried due to
206 // authorization problems.
207 int auth_retry_count_;
208
209 // The callback to execute when the query is complete.
210 WebHistoryService::CompletionCallback callback_;
211
212 // True if the request was started and has not yet completed, otherwise false.
213 bool is_pending_;
214 };
215
216 // Converts a time into a string for use as a parameter in a request to the
217 // history server.
218 std::string ServerTimeString(base::Time time) {
219 if (time < base::Time::UnixEpoch()) {
220 return base::Int64ToString(0);
221 } else {
222 return base::Int64ToString(
223 (time - base::Time::UnixEpoch()).InMicroseconds());
224 }
225 }
226
227 // Returns a URL for querying the history server for a query specified by
228 // |options|. |version_info|, if not empty, should be a token that was received
229 // from the server in response to a write operation. It is used to help ensure
230 // read consistency after a write.
231 GURL GetQueryUrl(const base::string16& text_query,
232 const QueryOptions& options,
233 const std::string& version_info) {
234 GURL url = GURL(kHistoryQueryHistoryUrl);
235 url = net::AppendQueryParameter(url, "titles", "1");
236
237 // Take |begin_time|, |end_time|, and |max_count| from the original query
238 // options, and convert them to the equivalent URL parameters.
239
240 base::Time end_time =
241 std::min(base::Time::FromInternalValue(options.EffectiveEndTime()),
242 base::Time::Now());
243 url = net::AppendQueryParameter(url, "max", ServerTimeString(end_time));
244
245 if (!options.begin_time.is_null()) {
246 url = net::AppendQueryParameter(
247 url, "min", ServerTimeString(options.begin_time));
248 }
249
250 if (options.max_count) {
251 url = net::AppendQueryParameter(
252 url, "num", base::IntToString(options.max_count));
253 }
254
255 if (!text_query.empty())
256 url = net::AppendQueryParameter(url, "q", base::UTF16ToUTF8(text_query));
257
258 if (!version_info.empty())
259 url = net::AppendQueryParameter(url, "kvi", version_info);
260
261 return url;
262 }
263
264 // Creates a DictionaryValue to hold the parameters for a deletion.
265 // Ownership is passed to the caller.
266 // |url| may be empty, indicating a time-range deletion.
267 base::DictionaryValue* CreateDeletion(
268 const std::string& min_time,
269 const std::string& max_time,
270 const GURL& url) {
271 base::DictionaryValue* deletion = new base::DictionaryValue;
272 deletion->SetString("type", "CHROME_HISTORY");
273 if (url.is_valid())
274 deletion->SetString("url", url.spec());
275 deletion->SetString("min_timestamp_usec", min_time);
276 deletion->SetString("max_timestamp_usec", max_time);
277 return deletion;
278 }
279
280 } // namespace
281
282 WebHistoryService::Request::Request() {
283 }
284
285 WebHistoryService::Request::~Request() {
286 }
287
288 WebHistoryService::WebHistoryService(Profile* profile)
289 : profile_(profile),
290 weak_ptr_factory_(this) {
291 }
292
293 WebHistoryService::~WebHistoryService() {
294 STLDeleteElements(&pending_expire_requests_);
295 STLDeleteElements(&pending_audio_history_requests_);
296 }
297
298 WebHistoryService::Request* WebHistoryService::CreateRequest(
299 const GURL& url,
300 const CompletionCallback& callback) {
301 return new RequestImpl(profile_, url, callback);
302 }
303
304 // static
305 scoped_ptr<base::DictionaryValue> WebHistoryService::ReadResponse(
306 WebHistoryService::Request* request) {
307 scoped_ptr<base::DictionaryValue> result;
308 if (request->GetResponseCode() == net::HTTP_OK) {
309 base::Value* value = base::JSONReader::Read(request->GetResponseBody());
310 if (value && value->IsType(base::Value::TYPE_DICTIONARY))
311 result.reset(static_cast<base::DictionaryValue*>(value));
312 else
313 DLOG(WARNING) << "Non-JSON response received from history server.";
314 }
315 return result.Pass();
316 }
317
318 scoped_ptr<WebHistoryService::Request> WebHistoryService::QueryHistory(
319 const base::string16& text_query,
320 const QueryOptions& options,
321 const WebHistoryService::QueryWebHistoryCallback& callback) {
322 // Wrap the original callback into a generic completion callback.
323 CompletionCallback completion_callback = base::Bind(
324 &WebHistoryService::QueryHistoryCompletionCallback, callback);
325
326 GURL url = GetQueryUrl(text_query, options, server_version_info_);
327 scoped_ptr<Request> request(CreateRequest(url, completion_callback));
328 request->Start();
329 return request.Pass();
330 }
331
332 void WebHistoryService::ExpireHistory(
333 const std::vector<ExpireHistoryArgs>& expire_list,
334 const ExpireWebHistoryCallback& callback) {
335 base::DictionaryValue delete_request;
336 scoped_ptr<base::ListValue> deletions(new base::ListValue);
337 base::Time now = base::Time::Now();
338
339 for (std::vector<ExpireHistoryArgs>::const_iterator it = expire_list.begin();
340 it != expire_list.end(); ++it) {
341 // Convert the times to server timestamps.
342 std::string min_timestamp = ServerTimeString(it->begin_time);
343 // TODO(dubroy): Use sane time (crbug.com/146090) here when it's available.
344 base::Time end_time = it->end_time;
345 if (end_time.is_null() || end_time > now)
346 end_time = now;
347 std::string max_timestamp = ServerTimeString(end_time);
348
349 for (std::set<GURL>::const_iterator url_iterator = it->urls.begin();
350 url_iterator != it->urls.end(); ++url_iterator) {
351 deletions->Append(
352 CreateDeletion(min_timestamp, max_timestamp, *url_iterator));
353 }
354 // If no URLs were specified, delete everything in the time range.
355 if (it->urls.empty())
356 deletions->Append(CreateDeletion(min_timestamp, max_timestamp, GURL()));
357 }
358 delete_request.Set("del", deletions.release());
359 std::string post_data;
360 base::JSONWriter::Write(&delete_request, &post_data);
361
362 GURL url(kHistoryDeleteHistoryUrl);
363
364 // Append the version info token, if it is available, to help ensure
365 // consistency with any previous deletions.
366 if (!server_version_info_.empty())
367 url = net::AppendQueryParameter(url, "kvi", server_version_info_);
368
369 // Wrap the original callback into a generic completion callback.
370 CompletionCallback completion_callback =
371 base::Bind(&WebHistoryService::ExpireHistoryCompletionCallback,
372 weak_ptr_factory_.GetWeakPtr(),
373 callback);
374
375 scoped_ptr<Request> request(CreateRequest(url, completion_callback));
376 request->SetPostData(post_data);
377 request->Start();
378 pending_expire_requests_.insert(request.release());
379 }
380
381 void WebHistoryService::ExpireHistoryBetween(
382 const std::set<GURL>& restrict_urls,
383 base::Time begin_time,
384 base::Time end_time,
385 const ExpireWebHistoryCallback& callback) {
386 std::vector<ExpireHistoryArgs> expire_list(1);
387 expire_list.back().urls = restrict_urls;
388 expire_list.back().begin_time = begin_time;
389 expire_list.back().end_time = end_time;
390 ExpireHistory(expire_list, callback);
391 }
392
393 void WebHistoryService::GetAudioHistoryEnabled(
394 const AudioWebHistoryCallback& callback) {
395 // Wrap the original callback into a generic completion callback.
396 CompletionCallback completion_callback =
397 base::Bind(&WebHistoryService::AudioHistoryCompletionCallback,
398 weak_ptr_factory_.GetWeakPtr(),
399 callback);
400
401 GURL url(kHistoryAudioHistoryUrl);
402 scoped_ptr<Request> request(CreateRequest(url, completion_callback));
403 request->Start();
404 pending_audio_history_requests_.insert(request.release());
405 }
406
407 void WebHistoryService::SetAudioHistoryEnabled(
408 bool new_enabled_value,
409 const AudioWebHistoryCallback& callback) {
410 // Wrap the original callback into a generic completion callback.
411 CompletionCallback completion_callback =
412 base::Bind(&WebHistoryService::AudioHistoryCompletionCallback,
413 weak_ptr_factory_.GetWeakPtr(),
414 callback);
415
416 GURL url(kHistoryAudioHistoryChangeUrl);
417 scoped_ptr<Request> request(CreateRequest(url, completion_callback));
418
419 base::DictionaryValue enable_audio_history;
420 enable_audio_history.SetBoolean("enable_history_recording",
421 new_enabled_value);
422 enable_audio_history.SetString("client", "audio");
423 std::string post_data;
424 base::JSONWriter::Write(&enable_audio_history, &post_data);
425 request->SetPostData(post_data);
426
427 request->Start();
428 pending_audio_history_requests_.insert(request.release());
429 }
430
431 size_t WebHistoryService::GetNumberOfPendingAudioHistoryRequests() {
432 return pending_audio_history_requests_.size();
433 }
434
435 // static
436 void WebHistoryService::QueryHistoryCompletionCallback(
437 const WebHistoryService::QueryWebHistoryCallback& callback,
438 WebHistoryService::Request* request,
439 bool success) {
440 scoped_ptr<base::DictionaryValue> response_value;
441 if (success)
442 response_value = ReadResponse(request);
443 callback.Run(request, response_value.get());
444 }
445
446 void WebHistoryService::ExpireHistoryCompletionCallback(
447 const WebHistoryService::ExpireWebHistoryCallback& callback,
448 WebHistoryService::Request* request,
449 bool success) {
450 scoped_ptr<base::DictionaryValue> response_value;
451 if (success) {
452 response_value = ReadResponse(request);
453 if (response_value)
454 response_value->GetString("version_info", &server_version_info_);
455 }
456 callback.Run(response_value.get() && success);
457 // Clean up from pending requests.
458 pending_expire_requests_.erase(request);
459 delete request;
460 }
461
462 void WebHistoryService::AudioHistoryCompletionCallback(
463 const WebHistoryService::AudioWebHistoryCallback& callback,
464 WebHistoryService::Request* request,
465 bool success) {
466 pending_audio_history_requests_.erase(request);
467 scoped_ptr<WebHistoryService::Request> request_ptr(request);
468
469 scoped_ptr<base::DictionaryValue> response_value;
470 bool enabled_value = false;
471 if (success) {
472 response_value = ReadResponse(request_ptr.get());
473 if (response_value)
474 response_value->GetBoolean("history_recording_enabled", &enabled_value);
475 }
476 callback.Run(success, enabled_value);
477 }
478
479 } // namespace history
OLDNEW
« no previous file with comments | « chrome/browser/history/web_history_service.h ('k') | chrome/browser/history/web_history_service_factory.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698