blob: c3685ee57951802057d32bf6d00d8613d5d60b44 [file] [log] [blame]
// Copyright 2016 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/subresource_filter/content/renderer/subresource_filter_agent.h"
#include <vector>
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "components/subresource_filter/content/common/subresource_filter_messages.h"
#include "components/subresource_filter/content/renderer/unverified_ruleset_dealer.h"
#include "components/subresource_filter/content/renderer/web_document_subresource_filter_impl.h"
#include "components/subresource_filter/core/common/document_load_statistics.h"
#include "components/subresource_filter/core/common/document_subresource_filter.h"
#include "components/subresource_filter/core/common/memory_mapped_ruleset.h"
#include "components/subresource_filter/core/common/scoped_timers.h"
#include "components/subresource_filter/core/common/time_measurements.h"
#include "content/public/common/content_features.h"
#include "content/public/renderer/render_frame.h"
#include "ipc/ipc_message.h"
#include "third_party/WebKit/public/platform/WebWorkerFetchContext.h"
#include "third_party/WebKit/public/web/WebDataSource.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
namespace subresource_filter {
SubresourceFilterAgent::SubresourceFilterAgent(
content::RenderFrame* render_frame,
UnverifiedRulesetDealer* ruleset_dealer)
: content::RenderFrameObserver(render_frame),
ruleset_dealer_(ruleset_dealer) {
DCHECK(ruleset_dealer);
}
SubresourceFilterAgent::~SubresourceFilterAgent() = default;
GURL SubresourceFilterAgent::GetDocumentURL() {
return render_frame()->GetWebFrame()->GetDocument().Url();
}
void SubresourceFilterAgent::SetSubresourceFilterForCommittedLoad(
std::unique_ptr<blink::WebDocumentSubresourceFilter> filter) {
blink::WebLocalFrame* web_frame = render_frame()->GetWebFrame();
web_frame->DataSource()->SetSubresourceFilter(filter.release());
}
void SubresourceFilterAgent::
SignalFirstSubresourceDisallowedForCommittedLoad() {
render_frame()->Send(new SubresourceFilterHostMsg_DidDisallowFirstSubresource(
render_frame()->GetRoutingID()));
}
void SubresourceFilterAgent::SendDocumentLoadStatistics(
const DocumentLoadStatistics& statistics) {
render_frame()->Send(new SubresourceFilterHostMsg_DocumentLoadStatistics(
render_frame()->GetRoutingID(), statistics));
}
void SubresourceFilterAgent::OnActivateForNextCommittedLoad(
ActivationState activation_state) {
activation_state_for_next_commit_ = activation_state;
}
void SubresourceFilterAgent::RecordHistogramsOnLoadCommitted() {
// Note: ActivationLevel used to be called ActivationState, the legacy name is
// kept for the histogram.
ActivationLevel activation_level =
activation_state_for_next_commit_.activation_level;
UMA_HISTOGRAM_ENUMERATION("SubresourceFilter.DocumentLoad.ActivationState",
static_cast<int>(activation_level),
static_cast<int>(ActivationLevel::LAST) + 1);
if (activation_level != ActivationLevel::DISABLED) {
UMA_HISTOGRAM_BOOLEAN("SubresourceFilter.DocumentLoad.RulesetIsAvailable",
ruleset_dealer_->IsRulesetFileAvailable());
}
}
void SubresourceFilterAgent::RecordHistogramsOnLoadFinished() {
DCHECK(filter_for_last_committed_load_);
const auto& statistics =
filter_for_last_committed_load_->filter().statistics();
UMA_HISTOGRAM_COUNTS_1000(
"SubresourceFilter.DocumentLoad.NumSubresourceLoads.Total",
statistics.num_loads_total);
UMA_HISTOGRAM_COUNTS_1000(
"SubresourceFilter.DocumentLoad.NumSubresourceLoads.Evaluated",
statistics.num_loads_evaluated);
UMA_HISTOGRAM_COUNTS_1000(
"SubresourceFilter.DocumentLoad.NumSubresourceLoads.MatchedRules",
statistics.num_loads_matching_rules);
UMA_HISTOGRAM_COUNTS_1000(
"SubresourceFilter.DocumentLoad.NumSubresourceLoads.Disallowed",
statistics.num_loads_disallowed);
// If ThreadTicks is not supported or performance measuring is switched off,
// then no time measurements have been collected.
if (ScopedThreadTimers::IsSupported() &&
filter_for_last_committed_load_->filter()
.activation_state()
.measure_performance) {
UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
"SubresourceFilter.DocumentLoad.SubresourceEvaluation."
"TotalWallDuration",
statistics.evaluation_total_wall_duration,
base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
50);
UMA_HISTOGRAM_CUSTOM_MICRO_TIMES(
"SubresourceFilter.DocumentLoad.SubresourceEvaluation.TotalCPUDuration",
statistics.evaluation_total_cpu_duration,
base::TimeDelta::FromMicroseconds(1), base::TimeDelta::FromSeconds(10),
50);
} else {
DCHECK(statistics.evaluation_total_wall_duration.is_zero());
DCHECK(statistics.evaluation_total_cpu_duration.is_zero());
}
SendDocumentLoadStatistics(statistics);
}
void SubresourceFilterAgent::ResetActivatonStateForNextCommit() {
activation_state_for_next_commit_ =
ActivationState(ActivationLevel::DISABLED);
}
void SubresourceFilterAgent::OnDestruct() {
delete this;
}
void SubresourceFilterAgent::DidCommitProvisionalLoad(
bool is_new_navigation,
bool is_same_document_navigation) {
if (is_same_document_navigation)
return;
filter_for_last_committed_load_.reset();
// TODO(csharrison): Use WebURL and WebSecurityOrigin for efficiency here,
// which require changes to the unit tests.
const GURL& url = GetDocumentURL();
if (url.SchemeIsHTTPOrHTTPS() || url.SchemeIsFile()) {
RecordHistogramsOnLoadCommitted();
if (activation_state_for_next_commit_.activation_level !=
ActivationLevel::DISABLED &&
ruleset_dealer_->IsRulesetFileAvailable()) {
base::OnceClosure first_disallowed_load_callback(
base::BindOnce(&SubresourceFilterAgent::
SignalFirstSubresourceDisallowedForCommittedLoad,
AsWeakPtr()));
auto ruleset = ruleset_dealer_->GetRuleset();
// TODO(csharrison): Replace with DCHECK when crbug.com/734102 is
// resolved.
CHECK(ruleset);
CHECK(ruleset->data());
auto filter = base::MakeUnique<WebDocumentSubresourceFilterImpl>(
url::Origin(url), activation_state_for_next_commit_,
std::move(ruleset), std::move(first_disallowed_load_callback));
filter_for_last_committed_load_ = filter->AsWeakPtr();
SetSubresourceFilterForCommittedLoad(std::move(filter));
}
}
ResetActivatonStateForNextCommit();
}
void SubresourceFilterAgent::DidFailProvisionalLoad(
const blink::WebURLError& error) {
// TODO(engedy): Add a test with `frame-ancestor` violation to exercise this.
ResetActivatonStateForNextCommit();
}
void SubresourceFilterAgent::DidFinishLoad() {
if (!filter_for_last_committed_load_)
return;
RecordHistogramsOnLoadFinished();
}
bool SubresourceFilterAgent::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(SubresourceFilterAgent, message)
IPC_MESSAGE_HANDLER(SubresourceFilterMsg_ActivateForNextCommittedLoad,
OnActivateForNextCommittedLoad)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void SubresourceFilterAgent::WillCreateWorkerFetchContext(
blink::WebWorkerFetchContext* worker_fetch_context) {
DCHECK(base::FeatureList::IsEnabled(features::kOffMainThreadFetch));
if (!filter_for_last_committed_load_)
return;
if (!ruleset_dealer_->IsRulesetFileAvailable())
return;
base::File ruleset_file = ruleset_dealer_->DuplicateRulesetFile();
if (!ruleset_file.IsValid())
return;
worker_fetch_context->SetSubresourceFilterBuilder(
base::MakeUnique<WebDocumentSubresourceFilterImpl::BuilderImpl>(
url::Origin(GetDocumentURL()),
filter_for_last_committed_load_->filter().activation_state(),
std::move(ruleset_file),
base::BindOnce(&SubresourceFilterAgent::
SignalFirstSubresourceDisallowedForCommittedLoad,
AsWeakPtr())));
}
} // namespace subresource_filter