blob: 2d12fbb6dd0599b34e7130f0bef2cdef9ce6f651 [file] [log] [blame]
// Copyright 2017 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 "chrome/browser/media/media_engagement_preloaded_list.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "chrome/browser/media/media_engagement_preload.pb.h"
#include "net/base/lookup_string_in_fixed_set.h"
#include "url/origin.h"
#include "url/url_canon.h"
#include "url/url_constants.h"
const char MediaEngagementPreloadedList::kHistogramCheckResultName[] =
"Media.Engagement.PreloadedList.CheckResult";
const char MediaEngagementPreloadedList::kHistogramLoadResultName[] =
"Media.Engagement.PreloadedList.LoadResult";
const char MediaEngagementPreloadedList::kHistogramLoadTimeName[] =
"Media.Engagement.PreloadedList.LoadTime";
const char MediaEngagementPreloadedList::kHistogramLookupTimeName[] =
"Media.Engagement.PreloadedList.LookupTime";
// static
MediaEngagementPreloadedList* MediaEngagementPreloadedList::GetInstance() {
static base::NoDestructor<MediaEngagementPreloadedList> instance;
return instance.get();
}
MediaEngagementPreloadedList::MediaEngagementPreloadedList() {
// This class is allowed to be instantiated on any thread.
DETACH_FROM_SEQUENCE(sequence_checker_);
}
MediaEngagementPreloadedList::~MediaEngagementPreloadedList() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
bool MediaEngagementPreloadedList::loaded() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return is_loaded_;
}
bool MediaEngagementPreloadedList::empty() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return dafsa_.empty();
}
bool MediaEngagementPreloadedList::LoadFromFile(const base::FilePath& path) {
SCOPED_UMA_HISTOGRAM_TIMER(kHistogramLoadTimeName);
DETACH_FROM_SEQUENCE(sequence_checker_);
// Check the file exists.
if (!base::PathExists(path)) {
RecordLoadResult(LoadResult::kFileNotFound);
return false;
}
// Read the file to a string.
std::string file_data;
if (!base::ReadFileToString(path, &file_data)) {
RecordLoadResult(LoadResult::kFileReadFailed);
return false;
}
// Load the preloaded list into a proto message.
chrome_browser_media::PreloadedData message;
if (!message.ParseFromString(file_data)) {
RecordLoadResult(LoadResult::kParseProtoFailed);
return false;
}
// Copy data from the protobuf message.
dafsa_ = std::vector<unsigned char>(
message.dafsa().c_str(),
message.dafsa().c_str() + message.dafsa().length());
RecordLoadResult(LoadResult::kLoaded);
is_loaded_ = true;
return true;
}
bool MediaEngagementPreloadedList::CheckOriginIsPresent(
const url::Origin& origin) const {
SCOPED_UMA_HISTOGRAM_TIMER(kHistogramLookupTimeName);
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Check if we have loaded the data.
if (!loaded()) {
RecordCheckResult(CheckResult::kListNotLoaded);
return false;
}
// Check if the data is empty.
if (empty()) {
RecordCheckResult(CheckResult::kListEmpty);
return false;
}
// By default we are just searching for the hostname.
std::string location(origin.host());
// Add :<port> if we use a non-default port.
if (origin.port() != url::DefaultPortForScheme(origin.scheme().data(),
origin.scheme().length())) {
location.push_back(':');
std::string port(base::UintToString(origin.port()));
location.append(std::move(port));
}
// Check the string is present and check the scheme matches.
DafsaResult result = CheckStringIsPresent(location);
switch (result) {
case DafsaResult::kFoundHttpsOnly:
// Only HTTPS is allowed by default.
if (origin.scheme() == url::kHttpsScheme) {
RecordCheckResult(CheckResult::kFoundHttpsOnly);
return true;
} else {
RecordCheckResult(CheckResult::kFoundHttpsOnlyButWasHttp);
}
break;
case DafsaResult::kFoundHttpOrHttps:
// Allow either HTTP or HTTPS.
if (origin.scheme() == url::kHttpScheme ||
origin.scheme() == url::kHttpsScheme) {
RecordCheckResult(CheckResult::kFoundHttpOrHttps);
return true;
}
break;
case DafsaResult::kNotFound:
RecordCheckResult(CheckResult::kNotFound);
break;
}
// If we do not match then we should record a not found result and return
// false.
return false;
}
MediaEngagementPreloadedList::DafsaResult
MediaEngagementPreloadedList::CheckStringIsPresent(
const std::string& input) const {
return static_cast<MediaEngagementPreloadedList::DafsaResult>(
net::LookupStringInFixedSet(dafsa_.data(), dafsa_.size(), input.c_str(),
input.size()));
}
void MediaEngagementPreloadedList::RecordLoadResult(LoadResult result) {
UMA_HISTOGRAM_ENUMERATION(kHistogramLoadResultName, result,
LoadResult::kCount);
}
void MediaEngagementPreloadedList::RecordCheckResult(CheckResult result) const {
UMA_HISTOGRAM_ENUMERATION(kHistogramCheckResultName, result,
CheckResult::kCount);
}