12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/history/web_history_service.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/bind.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/json/json_reader.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/json/json_writer.h"
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/metrics/histogram.h"
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "base/stl_util.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/values.h"
15c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/signin/signin_manager_factory.h"
17effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch#include "components/signin/core/browser/profile_oauth2_token_service.h"
18e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch#include "components/signin/core/browser/signin_manager.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "google_apis/gaia/gaia_urls.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "google_apis/gaia/google_service_auth_error.h"
21424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)#include "google_apis/gaia/oauth2_token_service.h"
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/base/load_flags.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/base/url_util.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/http/http_status_code.h"
25868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "net/http/http_util.h"
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/url_request/url_fetcher.h"
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "net/url_request/url_fetcher_delegate.h"
28eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "url/gurl.h"
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace history {
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kHistoryOAuthScope[] =
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    "https://www.googleapis.com/auth/chromesync";
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kHistoryQueryHistoryUrl[] =
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    "https://history.google.com/history/api/lookup?client=chrome";
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kHistoryDeleteHistoryUrl[] =
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    "https://history.google.com/history/api/delete?client=chrome";
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const char kPostDataMimeType[] = "text/plain";
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The maximum number of retries for the URLFetcher requests.
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const size_t kMaxRetries = 1;
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)class RequestImpl : public WebHistoryService::Request,
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    private OAuth2TokenService::Consumer,
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    private net::URLFetcherDelegate {
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) public:
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual ~RequestImpl() {
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Returns the response code received from the server, which will only be
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // valid if the request succeeded.
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int response_code() { return response_code_; }
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Returns the contents of the response body received from the server.
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const std::string& response_body() { return response_body_; }
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
62c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual bool is_pending() OVERRIDE { return is_pending_; }
63c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) private:
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  friend class history::WebHistoryService;
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  typedef base::Callback<void(Request*, bool)> CompletionCallback;
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  RequestImpl(Profile* profile,
70c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)              const GURL& url,
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              const CompletionCallback& callback)
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      : OAuth2TokenService::Consumer("web_history"),
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        profile_(profile),
74c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        url_(url),
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        response_code_(0),
76c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        auth_retry_count_(0),
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        callback_(callback),
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        is_pending_(false) {
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Tells the request to do its thang.
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void Start() {
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    OAuth2TokenService::ScopeSet oauth_scopes;
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    oauth_scopes.insert(kHistoryOAuthScope);
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    ProfileOAuth2TokenService* token_service =
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SigninManagerBase* signin_manager =
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        SigninManagerFactory::GetForProfile(profile_);
9068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)    token_request_ = token_service->StartRequest(
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        signin_manager->GetAuthenticatedAccountId(), oauth_scopes, this);
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    is_pending_ = true;
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // content::URLFetcherDelegate interface.
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK_EQ(source, url_fetcher_.get());
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    response_code_ = url_fetcher_->GetResponseCode();
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
100868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    UMA_HISTOGRAM_CUSTOM_ENUMERATION("WebHistory.OAuthTokenResponseCode",
101868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        net::HttpUtil::MapStatusCodeForHistogram(response_code_),
102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        net::HttpUtil::GetStatusCodesForHistogram());
103868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // If the response code indicates that the token might not be valid,
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // invalidate the token and try again.
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (response_code_ == net::HTTP_UNAUTHORIZED && ++auth_retry_count_ <= 1) {
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      OAuth2TokenService::ScopeSet oauth_scopes;
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      oauth_scopes.insert(kHistoryOAuthScope);
10968043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      ProfileOAuth2TokenService* token_service =
11068043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)          ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      SigninManagerBase* signin_manager =
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          SigninManagerFactory::GetForProfile(profile_);
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      token_service->InvalidateToken(
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          signin_manager->GetAuthenticatedAccountId(),
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          oauth_scopes,
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          access_token_);
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
11868043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)      access_token_.clear();
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      Start();
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return;
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    url_fetcher_->GetResponseAsString(&response_body_);
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    url_fetcher_.reset();
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    is_pending_ = false;
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    callback_.Run(this, true);
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // It is valid for the callback to delete |this|, so do not access any
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // members below here.
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // OAuth2TokenService::Consumer interface.
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void OnGetTokenSuccess(
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const OAuth2TokenService::Request* request,
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const std::string& access_token,
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const base::Time& expiration_time) OVERRIDE {
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    token_request_.reset();
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK(!access_token.empty());
137c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    access_token_ = access_token;
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
139868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    UMA_HISTOGRAM_BOOLEAN("WebHistory.OAuthTokenCompletion", true);
140868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Got an access token -- start the actual API request.
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    url_fetcher_.reset(CreateUrlFetcher(access_token));
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    url_fetcher_->Start();
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void OnGetTokenFailure(
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const OAuth2TokenService::Request* request,
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const GoogleServiceAuthError& error) OVERRIDE {
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    token_request_.reset();
150c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    is_pending_ = false;
151868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
152868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    UMA_HISTOGRAM_BOOLEAN("WebHistory.OAuthTokenCompletion", false);
153868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    callback_.Run(this, false);
155c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // It is valid for the callback to delete |this|, so do not access any
156c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    // members below here.
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Helper for creating a new URLFetcher for the API request.
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  net::URLFetcher* CreateUrlFetcher(const std::string& access_token) {
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    net::URLFetcher::RequestType request_type = post_data_.empty() ?
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        net::URLFetcher::GET : net::URLFetcher::POST;
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    net::URLFetcher* fetcher = net::URLFetcher::Create(
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        url_, request_type, this);
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fetcher->SetRequestContext(profile_->GetRequestContext());
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fetcher->SetMaxRetriesOn5xx(kMaxRetries);
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          net::LOAD_DO_NOT_SAVE_COOKIES);
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fetcher->AddExtraRequestHeader("Authorization: Bearer " + access_token);
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    fetcher->AddExtraRequestHeader("X-Developer-Key: " +
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        GaiaUrls::GetInstance()->oauth2_chrome_client_id());
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (request_type == net::URLFetcher::POST)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      fetcher->SetUploadData(kPostDataMimeType, post_data_);
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return fetcher;
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  void set_post_data(const std::string& post_data) {
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    post_data_ = post_data;
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Profile* profile_;
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The URL of the API endpoint.
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GURL url_;
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // POST data to be sent with the request (may be empty).
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string post_data_;
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The OAuth2 access token request.
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<OAuth2TokenService::Request> token_request_;
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
192c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The current OAuth2 access token.
193c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  std::string access_token_;
194c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Handles the actual API requests after the OAuth token is acquired.
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<net::URLFetcher> url_fetcher_;
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Holds the response code received from the server.
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int response_code_;
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Holds the response body received from the server.
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string response_body_;
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
204c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // The number of times this request has already been retried due to
205c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // authorization problems.
206c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  int auth_retry_count_;
207c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The callback to execute when the query is complete.
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CompletionCallback callback_;
210c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
211c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // True if the request was started and has not yet completed, otherwise false.
212c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  bool is_pending_;
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)};
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
215c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// Extracts a JSON-encoded HTTP response into a DictionaryValue.
216c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// If |request|'s HTTP response code indicates failure, or if the response
217c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// body is not JSON, a null pointer is returned.
2185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)scoped_ptr<base::DictionaryValue> ReadResponse(RequestImpl* request) {
2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> result;
220c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (request->response_code() == net::HTTP_OK) {
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::Value* value = base::JSONReader::Read(request->response_body());
222c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (value && value->IsType(base::Value::TYPE_DICTIONARY))
2235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      result.reset(static_cast<base::DictionaryValue*>(value));
224c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    else
225c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DLOG(WARNING) << "Non-JSON response received from history server.";
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
227c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return result.Pass();
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Converts a time into a string for use as a parameter in a request to the
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// history server.
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)std::string ServerTimeString(base::Time time) {
233868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  if (time < base::Time::UnixEpoch()) {
234868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return base::Int64ToString(0);
235868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  } else {
236868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    return base::Int64ToString(
237868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        (time - base::Time::UnixEpoch()).InMicroseconds());
238868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  }
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns a URL for querying the history server for a query specified by
242c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// |options|. |version_info|, if not empty, should be a token that was received
243c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// from the server in response to a write operation. It is used to help ensure
244c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// read consistency after a write.
245a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)GURL GetQueryUrl(const base::string16& text_query,
246c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 const QueryOptions& options,
247c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 const std::string& version_info) {
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  GURL url = GURL(kHistoryQueryHistoryUrl);
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  url = net::AppendQueryParameter(url, "titles", "1");
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Take |begin_time|, |end_time|, and |max_count| from the original query
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // options, and convert them to the equivalent URL parameters.
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::Time end_time =
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      std::min(base::Time::FromInternalValue(options.EffectiveEndTime()),
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)               base::Time::Now());
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  url = net::AppendQueryParameter(url, "max", ServerTimeString(end_time));
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!options.begin_time.is_null()) {
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    url = net::AppendQueryParameter(
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        url, "min", ServerTimeString(options.begin_time));
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (options.max_count) {
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    url = net::AppendQueryParameter(
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        url, "num", base::IntToString(options.max_count));
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!text_query.empty())
2705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    url = net::AppendQueryParameter(url, "q", base::UTF16ToUTF8(text_query));
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
272c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!version_info.empty())
273c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    url = net::AppendQueryParameter(url, "kvi", version_info);
274c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
275c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return url;
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Creates a DictionaryValue to hold the parameters for a deletion.
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Ownership is passed to the caller.
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// |url| may be empty, indicating a time-range deletion.
2815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)base::DictionaryValue* CreateDeletion(
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& min_time,
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::string& max_time,
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const GURL& url) {
2855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue* deletion = new base::DictionaryValue;
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  deletion->SetString("type", "CHROME_HISTORY");
2872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (url.is_valid())
2882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    deletion->SetString("url", url.spec());
2892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  deletion->SetString("min_timestamp_usec", min_time);
2902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  deletion->SetString("max_timestamp_usec", max_time);
2912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return deletion;
2922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)WebHistoryService::Request::Request() {
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)WebHistoryService::Request::~Request() {
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)WebHistoryService::WebHistoryService(Profile* profile)
303c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    : profile_(profile),
304c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      weak_ptr_factory_(this) {
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)WebHistoryService::~WebHistoryService() {
308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  STLDeleteElements(&pending_expire_requests_);
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)scoped_ptr<WebHistoryService::Request> WebHistoryService::QueryHistory(
312a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    const base::string16& text_query,
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const QueryOptions& options,
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const WebHistoryService::QueryWebHistoryCallback& callback) {
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Wrap the original callback into a generic completion callback.
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  RequestImpl::CompletionCallback completion_callback = base::Bind(
317c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      &WebHistoryService::QueryHistoryCompletionCallback, callback);
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
319c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  GURL url = GetQueryUrl(text_query, options, server_version_info_);
3202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<RequestImpl> request(
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new RequestImpl(profile_, url, completion_callback));
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  request->Start();
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return request.PassAs<Request>();
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
326a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void WebHistoryService::ExpireHistory(
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::vector<ExpireHistoryArgs>& expire_list,
3282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ExpireWebHistoryCallback& callback) {
3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::DictionaryValue delete_request;
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::ListValue> deletions(new base::ListValue);
3312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::Time now = base::Time::Now();
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (std::vector<ExpireHistoryArgs>::const_iterator it = expire_list.begin();
3342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != expire_list.end(); ++it) {
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Convert the times to server timestamps.
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string min_timestamp = ServerTimeString(it->begin_time);
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // TODO(dubroy): Use sane time (crbug.com/146090) here when it's available.
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::Time end_time = it->end_time;
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (end_time.is_null() || end_time > now)
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      end_time = now;
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    std::string max_timestamp = ServerTimeString(end_time);
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    for (std::set<GURL>::const_iterator url_iterator = it->urls.begin();
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)         url_iterator != it->urls.end(); ++url_iterator) {
3452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      deletions->Append(
3462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          CreateDeletion(min_timestamp, max_timestamp, *url_iterator));
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // If no URLs were specified, delete everything in the time range.
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (it->urls.empty())
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      deletions->Append(CreateDeletion(min_timestamp, max_timestamp, GURL()));
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  delete_request.Set("del", deletions.release());
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::string post_data;
3542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::JSONWriter::Write(&delete_request, &post_data);
3552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
356c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  GURL url(kHistoryDeleteHistoryUrl);
357c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
358c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Append the version info token, if it is available, to help ensure
359c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // consistency with any previous deletions.
360c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!server_version_info_.empty())
361c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    url = net::AppendQueryParameter(url, "kvi", server_version_info_);
362c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
363c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Wrap the original callback into a generic completion callback.
364c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  RequestImpl::CompletionCallback completion_callback =
365c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      base::Bind(&WebHistoryService::ExpireHistoryCompletionCallback,
366c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 weak_ptr_factory_.GetWeakPtr(),
367c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                 callback);
368c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<RequestImpl> request(
370c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      new RequestImpl(profile_, url, completion_callback));
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  request->set_post_data(post_data);
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  request->Start();
373a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  pending_expire_requests_.insert(request.release());
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
376a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void WebHistoryService::ExpireHistoryBetween(
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const std::set<GURL>& restrict_urls,
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::Time begin_time,
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::Time end_time,
3802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const ExpireWebHistoryCallback& callback) {
3812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  std::vector<ExpireHistoryArgs> expire_list(1);
3822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  expire_list.back().urls = restrict_urls;
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  expire_list.back().begin_time = begin_time;
3842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  expire_list.back().end_time = end_time;
385a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ExpireHistory(expire_list, callback);
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
388c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// static
389c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void WebHistoryService::QueryHistoryCompletionCallback(
390c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const WebHistoryService::QueryWebHistoryCallback& callback,
391c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    WebHistoryService::Request* request,
392c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    bool success) {
3935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> response_value;
394c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (success)
395c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    response_value = ReadResponse(static_cast<RequestImpl*>(request));
396c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  callback.Run(request, response_value.get());
397c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
398c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
399c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)void WebHistoryService::ExpireHistoryCompletionCallback(
400c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const WebHistoryService::ExpireWebHistoryCallback& callback,
401c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    WebHistoryService::Request* request,
402c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    bool success) {
4035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> response_value;
404c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (success) {
405c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    response_value = ReadResponse(static_cast<RequestImpl*>(request));
406b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (response_value)
407c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      response_value->GetString("version_info", &server_version_info_);
408c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
409a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  callback.Run(response_value.get() && success);
410a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // Clean up from pending requests.
411a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  pending_expire_requests_.erase(request);
412a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  delete request;
413c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
414c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
4152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace history
416