blob: 332cce4d9a3cb6c656a59afc447dcc341eba96a3 [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 "components/previews/core/previews_logger.h"
#include "base/command_line.h"
#include "base/strings/stringprintf.h"
#include "components/previews/core/previews_logger_observer.h"
#include "components/previews/core/previews_switches.h"
namespace previews {
std::string GetDescriptionForInfoBarDescription(previews::PreviewsType type) {
return base::StringPrintf("%s InfoBar shown",
previews::GetStringNameForType(type).c_str());
}
namespace {
static const char kPreviewDecisionMadeEventType[] = "Decision";
static const char kPreviewNavigationEventType[] = "Navigation";
const size_t kMaximumNavigationLogs = 10;
const size_t kMaximumDecisionLogs = 25;
std::string GetDescriptionForPreviewsNavigation(PreviewsType type,
bool opt_out) {
return base::StringPrintf("%s preview - user opt-out: %s",
GetStringNameForType(type).c_str(),
opt_out ? "True" : "False");
}
std::string GetReasonDescription(PreviewsEligibilityReason reason,
bool want_inverse_description) {
switch (reason) {
case PreviewsEligibilityReason::ALLOWED:
DCHECK(!want_inverse_description);
return "Allowed";
case PreviewsEligibilityReason::BLACKLIST_UNAVAILABLE:
return want_inverse_description ? "Blacklist not null"
: "Blacklist failed to be created";
case PreviewsEligibilityReason::BLACKLIST_DATA_NOT_LOADED:
return want_inverse_description ? "Blacklist loaded from disk"
: "Blacklist not loaded from disk yet";
case PreviewsEligibilityReason::USER_RECENTLY_OPTED_OUT:
return want_inverse_description ? "User did not opt out recently"
: "User recently opted out";
case PreviewsEligibilityReason::USER_BLACKLISTED:
return want_inverse_description ? "Not all previews are blacklisted"
: "All previews are blacklisted";
case PreviewsEligibilityReason::HOST_BLACKLISTED:
return want_inverse_description
? "Host is not blacklisted on all previews"
: "All previews on this host are blacklisted";
case PreviewsEligibilityReason::NETWORK_QUALITY_UNAVAILABLE:
return want_inverse_description ? "Network quality available"
: "Network quality unavailable";
case PreviewsEligibilityReason::NETWORK_NOT_SLOW:
return want_inverse_description ? "Network is slow" : "Network not slow";
case PreviewsEligibilityReason::RELOAD_DISALLOWED:
return want_inverse_description
? "Page reloads allowed"
: "Page reloads do not show previews for this preview type";
case PreviewsEligibilityReason::HOST_BLACKLISTED_BY_SERVER:
return want_inverse_description ? "Host not blacklisted by server rules"
: "Host blacklisted by server rules";
case PreviewsEligibilityReason::HOST_NOT_WHITELISTED_BY_SERVER:
return want_inverse_description ? "Host whitelisted by server rules"
: "Host not whitelisted by server rules";
case PreviewsEligibilityReason::ALLOWED_WITHOUT_OPTIMIZATION_HINTS:
return want_inverse_description
? "Not allowed (without server rule check)"
: "Allowed (but without server rule check)";
case PreviewsEligibilityReason::COMMITTED:
return want_inverse_description ? "Not Committed" : "Committed";
case PreviewsEligibilityReason::CACHE_CONTROL_NO_TRANSFORM:
return want_inverse_description
? "Cache-control no-transform not received"
: "Cache-control no-transform received";
case PreviewsEligibilityReason::LAST:
break;
case PreviewsEligibilityReason::NETWORK_NOT_SLOW_FOR_SESSION:
return want_inverse_description
? "Network is slow enough for the session"
: "Network not slow enough for the session";
case PreviewsEligibilityReason::DEVICE_OFFLINE:
return want_inverse_description ? "Device is online"
: "Device is offline";
}
NOTREACHED();
return "";
}
std::string GetDescriptionForPreviewsDecision(
PreviewsEligibilityReason reason,
PreviewsType type,
bool want_inverse_description = false) {
return base::StringPrintf(
"%s preview - %s", GetStringNameForType(type).c_str(),
GetReasonDescription(reason, want_inverse_description).c_str());
}
} // namespace
PreviewsLogger::MessageLog::MessageLog(const std::string& event_type,
const std::string& event_description,
const GURL& url,
base::Time time,
uint64_t page_id)
: event_type(event_type),
event_description(event_description),
url(url),
time(time),
page_id(page_id) {}
PreviewsLogger::MessageLog::MessageLog(const MessageLog& other)
: event_type(other.event_type),
event_description(other.event_description),
url(other.url),
time(other.time),
page_id(other.page_id) {}
PreviewsLogger::PreviewsLogger()
: blacklist_ignored_(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kIgnorePreviewsBlacklist)) {}
PreviewsLogger::~PreviewsLogger() {}
void PreviewsLogger::AddAndNotifyObserver(PreviewsLoggerObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_list_.AddObserver(observer);
// Notify the status of blacklist decisions ingored.
observer->OnIgnoreBlacklistDecisionStatusChanged(blacklist_ignored_);
// Merge navigation logs and decision logs in chronological order, and push
// them to |observer|.
auto navigation_ptr = navigations_logs_.begin();
auto decision_ptr = decisions_logs_.begin();
while (navigation_ptr != navigations_logs_.end() ||
decision_ptr != decisions_logs_.end()) {
if (navigation_ptr == navigations_logs_.end()) {
observer->OnNewMessageLogAdded(*decision_ptr);
++decision_ptr;
continue;
}
if (decision_ptr == decisions_logs_.end()) {
observer->OnNewMessageLogAdded(*navigation_ptr);
++navigation_ptr;
continue;
}
if (navigation_ptr->time < decision_ptr->time) {
observer->OnNewMessageLogAdded(*navigation_ptr);
++navigation_ptr;
} else {
observer->OnNewMessageLogAdded(*decision_ptr);
++decision_ptr;
}
}
// Push the current state of blacklist (user blacklisted state and all
// blacklisted hosts).
observer->OnUserBlacklistedStatusChange(user_blacklisted_status_);
for (auto entry : blacklisted_hosts_) {
observer->OnNewBlacklistedHost(entry.first, entry.second);
}
}
void PreviewsLogger::RemoveObserver(PreviewsLoggerObserver* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
observer_list_.RemoveObserver(observer);
if (observer_list_.begin() == observer_list_.end()) {
// |observer_list_| is empty.
observer->OnLastObserverRemove();
}
}
void PreviewsLogger::LogMessage(const std::string& event_type,
const std::string& event_description,
const GURL& url,
base::Time time,
uint64_t page_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Notify observers about the new MessageLog.
for (auto& observer : observer_list_) {
observer.OnNewMessageLogAdded(
MessageLog(event_type, event_description, url, time, page_id));
}
}
void PreviewsLogger::LogPreviewNavigation(const GURL& url,
PreviewsType type,
bool opt_out,
base::Time time,
uint64_t page_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GE(kMaximumNavigationLogs, navigations_logs_.size());
std::string description = GetDescriptionForPreviewsNavigation(type, opt_out);
LogMessage(kPreviewNavigationEventType, description, url, time, page_id);
// Pop out the oldest message when the list is full.
if (navigations_logs_.size() >= kMaximumNavigationLogs) {
navigations_logs_.pop_front();
}
navigations_logs_.emplace_back(kPreviewNavigationEventType, description, url,
time, page_id);
}
void PreviewsLogger::LogPreviewDecisionMade(
PreviewsEligibilityReason reason,
const GURL& url,
base::Time time,
PreviewsType type,
std::vector<PreviewsEligibilityReason>&& passed_reasons,
uint64_t page_id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GE(kMaximumDecisionLogs, decisions_logs_.size());
// Logs all passed decisions messages.
for (auto decision : passed_reasons) {
std::string decision_description = GetDescriptionForPreviewsDecision(
decision, type, true /* want_inverse_description */);
LogMessage(kPreviewDecisionMadeEventType, decision_description, url, time,
page_id);
decisions_logs_.emplace_back(kPreviewDecisionMadeEventType,
decision_description, url, time, page_id);
}
std::string description = GetDescriptionForPreviewsDecision(reason, type);
LogMessage(kPreviewDecisionMadeEventType, description, url, time, page_id);
// Pop out the older messages when the list is full.
while (decisions_logs_.size() >= kMaximumDecisionLogs) {
decisions_logs_.pop_front();
}
decisions_logs_.emplace_back(kPreviewDecisionMadeEventType, description, url,
time, page_id);
}
void PreviewsLogger::OnNewBlacklistedHost(const std::string& host,
base::Time time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blacklisted_hosts_[host] = time;
for (auto& observer : observer_list_) {
observer.OnNewBlacklistedHost(host, time);
}
}
void PreviewsLogger::OnUserBlacklistedStatusChange(bool blacklisted) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
user_blacklisted_status_ = blacklisted;
for (auto& observer : observer_list_) {
observer.OnUserBlacklistedStatusChange(blacklisted);
}
}
void PreviewsLogger::OnBlacklistCleared(base::Time time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blacklisted_hosts_.clear();
navigations_logs_.clear();
decisions_logs_.clear();
for (auto& observer : observer_list_) {
observer.OnBlacklistCleared(time);
}
}
void PreviewsLogger::OnIgnoreBlacklistDecisionStatusChanged(bool ignored) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
blacklist_ignored_ = ignored;
for (auto& observer : observer_list_) {
observer.OnIgnoreBlacklistDecisionStatusChanged(ignored);
}
}
} // namespace previews