blob: c72c341ce3104a89a335e51889189cfa6e5f1f8c [file] [log] [blame]
// Copyright 2012 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/web_resource/web_resource_service.h"
#include <memory>
#include "base/bind.h"
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/data_use_measurement/core/data_use_user_data.h"
#include "components/google/core/common/google_util.h"
#include "components/prefs/pref_service.h"
#include "net/base/load_flags.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "url/gurl.h"
// No anonymous namespace, because const variables automatically get internal
// linkage.
const char kInvalidDataTypeError[] =
"Data from web resource server is missing or not valid JSON.";
const char kUnexpectedJSONFormatError[] =
"Data from web resource server does not have expected format.";
namespace web_resource {
WebResourceService::WebResourceService(
PrefService* prefs,
const GURL& web_resource_server,
const std::string& application_locale,
const char* last_update_time_pref_name,
int start_fetch_delay_ms,
int cache_update_delay_ms,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const char* disable_network_switch,
const ParseJSONCallback& parse_json_callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation,
ResourceRequestAllowedNotifier::NetworkConnectionTrackerGetter
network_connection_tracker_getter)
: prefs_(prefs),
resource_request_allowed_notifier_(new ResourceRequestAllowedNotifier(
prefs,
disable_network_switch,
std::move(network_connection_tracker_getter))),
fetch_scheduled_(false),
in_fetch_(false),
web_resource_server_(web_resource_server),
application_locale_(application_locale),
last_update_time_pref_name_(last_update_time_pref_name),
start_fetch_delay_ms_(start_fetch_delay_ms),
cache_update_delay_ms_(cache_update_delay_ms),
url_loader_factory_(url_loader_factory),
parse_json_callback_(parse_json_callback),
traffic_annotation_(traffic_annotation),
weak_ptr_factory_(this) {
resource_request_allowed_notifier_->Init(this, false /* leaky */);
DCHECK(prefs);
}
void WebResourceService::StartAfterDelay() {
// If resource requests are not allowed, we'll get a callback when they are.
if (resource_request_allowed_notifier_->ResourceRequestsAllowed())
OnResourceRequestsAllowed();
}
WebResourceService::~WebResourceService() = default;
void WebResourceService::OnSimpleLoaderComplete(
std::unique_ptr<std::string> response_body) {
simple_url_loader_.reset();
if (response_body) {
// Calls EndFetch() on completion.
// Full JSON parsing might spawn a utility process (for security).
// To limit the the number of simultaneously active processes
// (on Android in particular) we short-cut the full parsing in the case of
// trivially "empty" JSONs.
if (response_body->empty() || *response_body == "{}") {
OnUnpackFinished(std::make_unique<base::DictionaryValue>());
} else {
parse_json_callback_.Run(*response_body,
base::Bind(&WebResourceService::OnUnpackFinished,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&WebResourceService::OnUnpackError,
weak_ptr_factory_.GetWeakPtr()));
}
} else {
// Don't parse data if attempt to download was unsuccessful.
// Stop loading new web resource data, and silently exit.
// We do not call parse_json_callback_, so we need to call EndFetch()
// ourselves.
EndFetch();
}
}
// Delay initial load of resource data into cache so as not to interfere
// with startup time.
void WebResourceService::ScheduleFetch(int64_t delay_ms) {
if (fetch_scheduled_)
return;
fetch_scheduled_ = true;
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&WebResourceService::StartFetch,
weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(delay_ms));
}
void WebResourceService::SetResourceRequestAllowedNotifier(
std::unique_ptr<ResourceRequestAllowedNotifier> notifier) {
resource_request_allowed_notifier_ = std::move(notifier);
resource_request_allowed_notifier_->Init(this, false /* leaky */);
}
bool WebResourceService::GetFetchScheduled() const {
return fetch_scheduled_;
}
// Initializes the fetching of data from the resource server. Data
// load calls OnSimpleLoaderComplete.
void WebResourceService::StartFetch() {
// Set to false so that next fetch can be scheduled after this fetch or
// if we receive notification that resource is allowed.
fetch_scheduled_ = false;
// Check whether fetching is allowed.
if (!resource_request_allowed_notifier_->ResourceRequestsAllowed())
return;
// First, put our next cache load on the MessageLoop.
ScheduleFetch(cache_update_delay_ms_);
// Set cache update time in preferences.
prefs_->SetString(last_update_time_pref_name_,
base::NumberToString(base::Time::Now().ToDoubleT()));
// If we are still fetching data, exit.
if (in_fetch_)
return;
in_fetch_ = true;
GURL web_resource_server =
application_locale_.empty()
? web_resource_server_
: google_util::AppendGoogleLocaleParam(web_resource_server_,
application_locale_);
DVLOG(1) << "WebResourceService StartFetch " << web_resource_server;
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = web_resource_server;
// Do not let url fetcher affect existing state in system context
// (by setting cookies, for example).
resource_request->load_flags = net::LOAD_DISABLE_CACHE |
net::LOAD_DO_NOT_SEND_COOKIES |
net::LOAD_DO_NOT_SAVE_COOKIES;
// TODO(https://crbug.com/808498): Re-add data use measurement once
// SimpleURLLoader supports it.
// ID=data_use_measurement::DataUseUserData::WEB_RESOURCE_SERVICE
simple_url_loader_ = network::SimpleURLLoader::Create(
std::move(resource_request), traffic_annotation_);
simple_url_loader_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
url_loader_factory_.get(),
base::BindOnce(&WebResourceService::OnSimpleLoaderComplete,
base::Unretained(this)));
}
void WebResourceService::EndFetch() {
in_fetch_ = false;
}
void WebResourceService::OnUnpackFinished(std::unique_ptr<base::Value> value) {
if (!value) {
// Page information not properly read, or corrupted.
OnUnpackError(kInvalidDataTypeError);
return;
}
const base::DictionaryValue* dict = nullptr;
if (!value->GetAsDictionary(&dict)) {
OnUnpackError(kUnexpectedJSONFormatError);
return;
}
Unpack(*dict);
EndFetch();
}
void WebResourceService::OnUnpackError(const std::string& error_message) {
LOG(ERROR) << error_message;
EndFetch();
}
void WebResourceService::OnResourceRequestsAllowed() {
int64_t delay = start_fetch_delay_ms_;
// Check whether we have ever put a value in the web resource cache;
// if so, pull it out and see if it's time to update again.
if (prefs_->HasPrefPath(last_update_time_pref_name_)) {
std::string last_update_pref =
prefs_->GetString(last_update_time_pref_name_);
if (!last_update_pref.empty()) {
double last_update_value;
base::StringToDouble(last_update_pref, &last_update_value);
int64_t ms_until_update =
cache_update_delay_ms_ -
static_cast<int64_t>(
(base::Time::Now() - base::Time::FromDoubleT(last_update_value))
.InMilliseconds());
// Wait at least |start_fetch_delay_ms_|.
if (ms_until_update > start_fetch_delay_ms_)
delay = ms_until_update;
}
}
// Start fetch and wait for UpdateResourceCache.
ScheduleFetch(delay);
}
} // namespace web_resource