Commit bf5b0e09 authored by Josh Karlin's avatar Josh Karlin Committed by Commit Bot

[AdTracking] Include the top of the stack in AdTracker detection

Includes the script of the top stack frame in ads detection processing.

Bug: 807640
Change-Id: I8f4d0364ea088bf00abcc7310e4dccce7fec78f3
Reviewed-on: https://chromium-review.googlesource.com/986337
Commit-Queue: Josh Karlin <jkarlin@chromium.org>
Reviewed-by: default avatarNate Chapin <japhet@chromium.org>
Cr-Commit-Position: refs/heads/master@{#549509}
parent c63a0ba6
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/frame/AdTracker.h" #include "third_party/blink/renderer/core/frame/AdTracker.h"
#include "third_party/blink/renderer/bindings/core/v8/source_location.h"
#include "third_party/blink/renderer/core/CoreProbeSink.h" #include "third_party/blink/renderer/core/CoreProbeSink.h"
#include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/probe/core_probes.h" #include "third_party/blink/renderer/core/probe/core_probes.h"
...@@ -27,6 +28,13 @@ void AdTracker::Shutdown() { ...@@ -27,6 +28,13 @@ void AdTracker::Shutdown() {
local_root_ = nullptr; local_root_ = nullptr;
} }
String AdTracker::ScriptAtTopOfStack(ExecutionContext* execution_context) {
std::unique_ptr<blink::SourceLocation> current_stack_trace =
SourceLocation::Capture(execution_context);
// TODO(jkarlin): Url() sometimes returns String(), why?
return current_stack_trace ? current_stack_trace->Url() : "";
}
void AdTracker::WillExecuteScript(const String& script_url) { void AdTracker::WillExecuteScript(const String& script_url) {
bool is_ad = bool is_ad =
script_url.IsEmpty() ? false : known_ad_scripts_.Contains(script_url); script_url.IsEmpty() ? false : known_ad_scripts_.Contains(script_url);
...@@ -76,22 +84,30 @@ void AdTracker::WillSendRequest(ExecutionContext* execution_context, ...@@ -76,22 +84,30 @@ void AdTracker::WillSendRequest(ExecutionContext* execution_context,
Resource::Type resource_type) { Resource::Type resource_type) {
// If the resource is not already marked as an ad, check if any executing // If the resource is not already marked as an ad, check if any executing
// script is an ad. If yes, mark this as an ad. // script is an ad. If yes, mark this as an ad.
if (!request.IsAdResource() && AnyExecutingScriptsTaggedAsAdResource()) if (!request.IsAdResource() &&
AnyExecutingScriptsTaggedAsAdResource(execution_context))
request.SetIsAdResource(); request.SetIsAdResource();
// If it is a script marked as an ad, append it to the known ad scripts set. // If it is a script marked as an ad, append it to the known ad scripts set.
if (resource_type != Resource::kScript || !request.IsAdResource()) { if (resource_type == Resource::kScript && request.IsAdResource())
return; AppendToKnownAdScripts(request.Url());
}
AppendToKnownAdScripts(request.Url());
} }
// Keeping a separate function to easily access from tests. // This is a separate function for testing purposes.
void AdTracker::AppendToKnownAdScripts(const KURL& url) { void AdTracker::AppendToKnownAdScripts(const KURL& url) {
known_ad_scripts_.insert(url.GetString()); known_ad_scripts_.insert(url.GetString());
} }
bool AdTracker::AnyExecutingScriptsTaggedAsAdResource() { bool AdTracker::AnyExecutingScriptsTaggedAsAdResource(
ExecutionContext* execution_context) {
// The pseudo-stack contains entry points into the stack (e.g., when v8 is
// executed) but not the entire stack. It's cheap to retrieve the top of the
// stack so scan that as well.
String top_script = ScriptAtTopOfStack(execution_context);
if (!top_script.IsEmpty() && known_ad_scripts_.Contains(top_script))
return true;
// Scan the pseudo-stack for ad scripts.
for (const auto& executing_script : executing_scripts_) { for (const auto& executing_script : executing_scripts_) {
if (executing_script.is_ad) if (executing_script.is_ad)
return true; return true;
......
...@@ -27,8 +27,7 @@ class ExecuteScript; ...@@ -27,8 +27,7 @@ class ExecuteScript;
// Tracker for tagging resources as ads based on the call stack scripts. // Tracker for tagging resources as ads based on the call stack scripts.
// The tracker is maintained per local root. // The tracker is maintained per local root.
class CORE_EXPORT AdTracker final class CORE_EXPORT AdTracker : public GarbageCollectedFinalized<AdTracker> {
: public GarbageCollectedFinalized<AdTracker> {
public: public:
// Instrumenting methods. // Instrumenting methods.
// Called when a script module or script gets executed from native code. // Called when a script module or script gets executed from native code.
...@@ -56,7 +55,11 @@ class CORE_EXPORT AdTracker final ...@@ -56,7 +55,11 @@ class CORE_EXPORT AdTracker final
void Shutdown(); void Shutdown();
explicit AdTracker(LocalFrame*); explicit AdTracker(LocalFrame*);
~AdTracker(); virtual ~AdTracker();
protected:
// Protected for testing.
virtual String ScriptAtTopOfStack(ExecutionContext*);
private: private:
friend class FrameFetchContextSubresourceFilterTest; friend class FrameFetchContextSubresourceFilterTest;
...@@ -69,7 +72,7 @@ class CORE_EXPORT AdTracker final ...@@ -69,7 +72,7 @@ class CORE_EXPORT AdTracker final
// Returns true if any script in the pseudo call stack has been identified as // Returns true if any script in the pseudo call stack has been identified as
// an ad earlier. An ad is identified as an ad if AppendToKnownAdScripts has // an ad earlier. An ad is identified as an ad if AppendToKnownAdScripts has
// been called on it earlier. // been called on it earlier.
bool AnyExecutingScriptsTaggedAsAdResource(); bool AnyExecutingScriptsTaggedAsAdResource(ExecutionContext*);
Member<LocalFrame> local_root_; Member<LocalFrame> local_root_;
......
...@@ -13,6 +13,27 @@ ...@@ -13,6 +13,27 @@
namespace blink { namespace blink {
namespace {
class TestAdTracker : public AdTracker {
public:
explicit TestAdTracker(LocalFrame* frame) : AdTracker(frame) {}
void SetScriptAtTopOfStack(String url) { script_at_top_ = url; }
~TestAdTracker() override {}
protected:
// Returns "" by default unless SetScriptAtTopOfStack is called with
// something else.
String ScriptAtTopOfStack(ExecutionContext* execution_context) override {
return script_at_top_;
}
private:
String script_at_top_;
};
} // namespace
class AdTrackerTest : public testing::Test { class AdTrackerTest : public testing::Test {
protected: protected:
void SetUp() override; void SetUp() override;
...@@ -26,21 +47,22 @@ class AdTrackerTest : public testing::Test { ...@@ -26,21 +47,22 @@ class AdTrackerTest : public testing::Test {
} }
bool AnyExecutingScriptsTaggedAsAdResource() { bool AnyExecutingScriptsTaggedAsAdResource() {
return ad_tracker_->AnyExecutingScriptsTaggedAsAdResource(); return ad_tracker_->AnyExecutingScriptsTaggedAsAdResource(
&page_holder_->GetDocument());
} }
void AppendToKnownAdScripts(const KURL& url) { void AppendToKnownAdScripts(const KURL& url) {
ad_tracker_->AppendToKnownAdScripts(url); ad_tracker_->AppendToKnownAdScripts(url);
} }
Persistent<AdTracker> ad_tracker_; Persistent<TestAdTracker> ad_tracker_;
std::unique_ptr<DummyPageHolder> page_holder_; std::unique_ptr<DummyPageHolder> page_holder_;
}; };
void AdTrackerTest::SetUp() { void AdTrackerTest::SetUp() {
page_holder_ = DummyPageHolder::Create(IntSize(800, 600)); page_holder_ = DummyPageHolder::Create(IntSize(800, 600));
page_holder_->GetDocument().SetURL(KURL("https://example.com/foo")); page_holder_->GetDocument().SetURL(KURL("https://example.com/foo"));
ad_tracker_ = new AdTracker(GetFrame()); ad_tracker_ = new TestAdTracker(GetFrame());
} }
void AdTrackerTest::TearDown() { void AdTrackerTest::TearDown() {
...@@ -64,4 +86,31 @@ TEST_F(AdTrackerTest, AnyExecutingScriptsTaggedAsAdResource_False) { ...@@ -64,4 +86,31 @@ TEST_F(AdTrackerTest, AnyExecutingScriptsTaggedAsAdResource_False) {
EXPECT_FALSE(AnyExecutingScriptsTaggedAsAdResource()); EXPECT_FALSE(AnyExecutingScriptsTaggedAsAdResource());
} }
TEST_F(AdTrackerTest, TopOfStackIncluded) {
KURL ad_script_url("https://example.com/ad.js");
AppendToKnownAdScripts(ad_script_url);
WillExecuteScript("https://example.com/foo.js");
WillExecuteScript("https://example.com/bar.js");
EXPECT_FALSE(AnyExecutingScriptsTaggedAsAdResource());
ad_tracker_->SetScriptAtTopOfStack("https://www.example.com/baz.js");
EXPECT_FALSE(AnyExecutingScriptsTaggedAsAdResource());
ad_tracker_->SetScriptAtTopOfStack(ad_script_url);
EXPECT_TRUE(AnyExecutingScriptsTaggedAsAdResource());
ad_tracker_->SetScriptAtTopOfStack("https://www.example.com/baz.js");
EXPECT_FALSE(AnyExecutingScriptsTaggedAsAdResource());
ad_tracker_->SetScriptAtTopOfStack("");
EXPECT_FALSE(AnyExecutingScriptsTaggedAsAdResource());
ad_tracker_->SetScriptAtTopOfStack(WTF::String());
EXPECT_FALSE(AnyExecutingScriptsTaggedAsAdResource());
WillExecuteScript(ad_script_url);
EXPECT_TRUE(AnyExecutingScriptsTaggedAsAdResource());
}
} // namespace blink } // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment