| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/history/core/test/fake_web_history_service.h" |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "base/callback.h" |
| #include "base/macros.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/time/time.h" |
| #include "components/sync/protocol/history_status.pb.h" |
| #include "net/base/url_util.h" |
| #include "net/http/http_status_code.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| |
| namespace history { |
| |
| // FakeRequest ----------------------------------------------------------------- |
| |
| namespace { |
| |
| // TODO(msramek): Find a way to keep these URLs in sync with what is used |
| // in WebHistoryService. |
| |
| const char kLookupUrl[] = "https://history.google.com/history/api/lookup"; |
| |
| const char kDeleteUrl[] = "https://history.google.com/history/api/delete"; |
| |
| const char kChromeClient[] = "chrome"; |
| |
| const char kWebAndAppClient[] = "web_app"; |
| |
| const char kSyncServerHost[] = "clients4.google.com"; |
| |
| } // namespace |
| |
| class FakeWebHistoryService::FakeRequest : public WebHistoryService::Request { |
| public: |
| FakeRequest(FakeWebHistoryService* service, |
| const GURL& url, |
| bool emulate_success, |
| int emulate_response_code, |
| const WebHistoryService::CompletionCallback& callback, |
| base::Time begin, |
| base::Time end, |
| int max_count); |
| |
| // WebHistoryService::Request implementation. |
| bool IsPending() override; |
| int GetResponseCode() override; |
| const std::string& GetResponseBody() override; |
| void SetPostData(const std::string& post_data) override; |
| void SetPostDataAndType(const std::string& post_data, |
| const std::string& mime_type) override; |
| void SetUserAgent(const std::string& user_agent) override; |
| void Start() override; |
| |
| private: |
| FakeWebHistoryService* service_; |
| GURL url_; |
| bool emulate_success_; |
| int emulate_response_code_; |
| const WebHistoryService::CompletionCallback& callback_; |
| base::Time begin_; |
| base::Time end_; |
| int max_count_; |
| bool is_pending_; |
| std::string response_body_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FakeRequest); |
| }; |
| |
| FakeWebHistoryService::FakeRequest::FakeRequest( |
| FakeWebHistoryService* service, |
| const GURL& url, |
| bool emulate_success, |
| int emulate_response_code, |
| const WebHistoryService::CompletionCallback& callback, |
| base::Time begin, |
| base::Time end, |
| int max_count) |
| : service_(service), |
| url_(url), |
| emulate_success_(emulate_success), |
| emulate_response_code_(emulate_response_code), |
| callback_(callback), |
| begin_(begin), |
| end_(end), |
| max_count_(max_count), |
| is_pending_(false) {} |
| |
| bool FakeWebHistoryService::FakeRequest::IsPending() { |
| return is_pending_; |
| } |
| |
| int FakeWebHistoryService::FakeRequest::GetResponseCode() { |
| return emulate_response_code_; |
| } |
| |
| const std::string& FakeWebHistoryService::FakeRequest::GetResponseBody() { |
| std::string client; |
| net::GetValueForKeyInQuery(url_, "client", &client); |
| |
| GURL::Replacements remove_query; |
| remove_query.ClearQuery(); |
| GURL base_url = url_.ReplaceComponents(remove_query); |
| |
| if (base_url == kLookupUrl && client == kChromeClient) { |
| // History query. |
| response_body_ = "{ \"event\": ["; |
| bool more_results_left; |
| auto visits = service_->GetVisitsBetween(begin_, end_, max_count_, |
| &more_results_left); |
| std::vector<std::string> results; |
| for (const FakeWebHistoryService::Visit& visit : visits) { |
| std::string unix_time = std::to_string( |
| (visit.second - base::Time::UnixEpoch()).InMicroseconds()); |
| results.push_back( |
| base::StringPrintf("{\"result\":[{\"id\":[{\"timestamp_usec\":\"%s\"}" |
| "],\"url\":\"%s\"}]}", |
| unix_time.c_str(), visit.first.c_str())); |
| } |
| response_body_ += base::JoinString(results, ","); |
| response_body_ += |
| base::StringPrintf("], \"continuation_token\":\"%s\" }", |
| (more_results_left ? "more_results_left" : "")); |
| |
| } else if (base_url == kDeleteUrl && client == kChromeClient) { |
| // Deletion query. |
| response_body_ = "{ \"just needs to be\" : \"a valid JSON.\" }"; |
| |
| } else if (base_url == kLookupUrl && client == kWebAndAppClient) { |
| // Web and app activity query. |
| response_body_ = base::StringPrintf( |
| "{ \"history_recording_enabled\": %s }", |
| service_->IsWebAndAppActivityEnabled() ? "true" : "false"); |
| |
| } else if (url_.host() == kSyncServerHost) { |
| // Other forms of browsing history query. |
| std::unique_ptr<sync_pb::HistoryStatusResponse> history_status( |
| new sync_pb::HistoryStatusResponse()); |
| history_status->set_has_derived_data( |
| service_->AreOtherFormsOfBrowsingHistoryPresent()); |
| history_status->SerializeToString(&response_body_); |
| } |
| |
| return response_body_; |
| } |
| |
| void FakeWebHistoryService::FakeRequest::SetPostData( |
| const std::string& post_data) { |
| // Unused. |
| } |
| |
| void FakeWebHistoryService::FakeRequest::SetPostDataAndType( |
| const std::string& post_data, |
| const std::string& mime_type) { |
| // Unused. |
| } |
| |
| void FakeWebHistoryService::FakeRequest::SetUserAgent( |
| const std::string& user_agent) { |
| // Unused. |
| } |
| |
| void FakeWebHistoryService::FakeRequest::Start() { |
| is_pending_ = true; |
| callback_.Run(this, emulate_success_); |
| } |
| |
| // FakeWebHistoryService ------------------------------------------------------- |
| |
| FakeWebHistoryService::FakeWebHistoryService() |
| // NOTE: Simply pass null object for IdentityManager. WebHistoryService's |
| // only usage of this object is to fetch access tokens via RequestImpl, and |
| // FakeWebHistoryService deliberately replaces this flow with |
| // FakeWebHistoryService::FakeRequest. |
| : history::WebHistoryService(nullptr, nullptr), |
| emulate_success_(true), |
| emulate_response_code_(net::HTTP_OK), |
| web_and_app_activity_enabled_(false), |
| other_forms_of_browsing_history_present_(false) {} |
| |
| FakeWebHistoryService::~FakeWebHistoryService() { |
| } |
| |
| void FakeWebHistoryService::SetupFakeResponse( |
| bool emulate_success, int emulate_response_code) { |
| emulate_success_ = emulate_success; |
| emulate_response_code_ = emulate_response_code; |
| } |
| |
| void FakeWebHistoryService::AddSyncedVisit( |
| std::string url, base::Time timestamp) { |
| visits_.push_back(make_pair(url, timestamp)); |
| } |
| |
| void FakeWebHistoryService::ClearSyncedVisits() { |
| visits_.clear(); |
| } |
| |
| std::vector<FakeWebHistoryService::Visit> |
| FakeWebHistoryService::GetVisitsBetween(base::Time begin, |
| base::Time end, |
| size_t count, |
| bool* more_results_left) { |
| // Make sure that |visits_| is sorted in reverse chronological order before we |
| // return anything. This means that the most recent results are returned |
| // first. |
| std::sort(visits_.begin(), visits_.end(), |
| [](const Visit& lhs, const Visit rhs) -> bool { |
| return lhs.second > rhs.second; |
| }); |
| *more_results_left = false; |
| std::vector<Visit> result; |
| for (const Visit& visit : visits_) { |
| // |begin| is inclusive, |end| is exclusive. |
| if (visit.second >= begin && visit.second < end) { |
| // We found another valid result, but cannot return it because we've |
| // reached max count. |
| if (count > 0 && result.size() >= count) { |
| *more_results_left = true; |
| break; |
| } |
| |
| result.push_back(visit); |
| } |
| } |
| return result; |
| } |
| |
| base::Time FakeWebHistoryService::GetTimeForKeyInQuery( |
| const GURL& url, const std::string& key) { |
| std::string value; |
| if (!net::GetValueForKeyInQuery(url, key, &value)) |
| return base::Time(); |
| |
| int64_t us; |
| if (!base::StringToInt64(value, &us)) |
| return base::Time(); |
| return base::Time::UnixEpoch() + base::TimeDelta::FromMicroseconds(us); |
| } |
| |
| FakeWebHistoryService::Request* FakeWebHistoryService::CreateRequest( |
| const GURL& url, |
| const CompletionCallback& callback, |
| const net::PartialNetworkTrafficAnnotationTag& partial_traffic_annotation) { |
| // Find the time range endpoints in the URL. |
| base::Time begin = GetTimeForKeyInQuery(url, "min"); |
| base::Time end = GetTimeForKeyInQuery(url, "max"); |
| |
| if (end.is_null()) |
| end = base::Time::Max(); |
| |
| int max_count = 0; |
| std::string max_count_str; |
| if (net::GetValueForKeyInQuery(url, "num", &max_count_str)) |
| base::StringToInt(max_count_str, &max_count); |
| |
| return new FakeRequest(this, url, emulate_success_, emulate_response_code_, |
| callback, begin, end, max_count); |
| } |
| |
| bool FakeWebHistoryService::IsWebAndAppActivityEnabled() { |
| return web_and_app_activity_enabled_; |
| } |
| |
| void FakeWebHistoryService::SetWebAndAppActivityEnabled(bool enabled) { |
| web_and_app_activity_enabled_ = enabled; |
| } |
| |
| bool FakeWebHistoryService::AreOtherFormsOfBrowsingHistoryPresent() { |
| return other_forms_of_browsing_history_present_; |
| } |
| |
| void FakeWebHistoryService::SetOtherFormsOfBrowsingHistoryPresent( |
| bool present) { |
| other_forms_of_browsing_history_present_ = present; |
| } |
| |
| } // namespace history |