blob: 66b557a18d5873c943b95623a289e198089bc22a [file] [log] [blame]
// Copyright 2020 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 "base/memory/weak_ptr.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/renderer/subresource_redirect/login_robots_decider_agent.h"
#include "chrome/renderer/subresource_redirect/robots_rules_parser_cache.h"
#include "chrome/renderer/subresource_redirect/subresource_redirect_url_loader_throttle.h"
#include "chrome/renderer/subresource_redirect/subresource_redirect_util.h"
#include "chrome/test/base/chrome_render_view_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/subresource_redirect/subresource_redirect_test_util.h"
#include "content/public/renderer/render_frame.h"
#include "content/public/renderer/render_view.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/loader/previews_state.h"
#include "third_party/blink/public/platform/web_network_state_notifier.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_request.h"
namespace subresource_redirect {
// Helper class that exposes a callback for the decider to call, and maintains
// the redirect result for verification.
class RedirectResultReceiver {
public:
PublicResourceDecider::ShouldRedirectDecisionCallback GetCallback() {
return base::BindOnce(
&RedirectResultReceiver::OnShouldRedirectDecisionCallback,
weak_ptr_factory_.GetWeakPtr());
}
SubresourceRedirectResult subresource_redirect_result() const {
return redirect_result_;
}
bool did_receive_result() const { return did_receive_result_; }
private:
void OnShouldRedirectDecisionCallback(
SubresourceRedirectResult redirect_result) {
EXPECT_FALSE(did_receive_result_);
did_receive_result_ = true;
redirect_result_ = redirect_result;
}
SubresourceRedirectResult redirect_result_;
bool did_receive_result_ = false;
base::WeakPtrFactory<RedirectResultReceiver> weak_ptr_factory_{this};
};
class SubresourceRedirectLoginRobotsDeciderAgentTest
: public ChromeRenderViewTest {
public:
void SetUpRobotsRules(const std::string& origin_str,
const std::vector<RobotsRule>& patterns) {
const auto origin = url::Origin::Create(GURL(origin_str));
RobotsRulesParserCache& robots_rules_parser_cache =
RobotsRulesParserCache::Get();
if (!robots_rules_parser_cache.DoRobotsRulesParserExist(origin)) {
robots_rules_parser_cache.CreateRobotsRulesParser(
origin, base::TimeDelta::FromSeconds(2));
}
EXPECT_TRUE(robots_rules_parser_cache.DoRobotsRulesParserExist(origin));
robots_rules_parser_cache.UpdateRobotsRules(
origin, GetRobotsRulesProtoString(patterns));
}
bool ShouldRedirectSubresource(const std::string& url) {
RedirectResultReceiver result_receiver;
auto immediate_result =
login_robots_decider_agent_->ShouldRedirectSubresource(
GURL(url), result_receiver.GetCallback());
if (immediate_result) {
// When the reult was sent immediately, callback should not be invoked.
EXPECT_FALSE(result_receiver.did_receive_result());
return *immediate_result == SubresourceRedirectResult::kRedirectable;
}
task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(10));
return result_receiver.did_receive_result() &&
result_receiver.subresource_redirect_result() ==
SubresourceRedirectResult::kRedirectable;
}
void SetLoggedInState(bool is_logged_in) {
login_robots_decider_agent_->SetLoggedInState(is_logged_in);
}
void ReadyToCommitNavigation() {
login_robots_decider_agent_->ReadyToCommitNavigation(nullptr);
}
SubresourceRedirectResult decider_agent_redirect_result() {
return login_robots_decider_agent_->redirect_result_;
}
absl::optional<bool> is_pending_navigation_loggged_in() {
return login_robots_decider_agent_->is_pending_navigation_loggged_in_;
}
protected:
void SetUp() override {
ChromeRenderViewTest::SetUp();
scoped_feature_list_.InitWithFeaturesAndParameters(
{{blink::features::kSubresourceRedirect,
{{"enable_login_robots_based_compression", "true"},
{"enable_public_image_hints_based_compression", "false"}}}},
{});
login_robots_decider_agent_ = new LoginRobotsDeciderAgent(
&associated_interfaces_, GetMainRenderFrame());
}
LoginRobotsDeciderAgent* login_robots_decider_agent_;
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
TestAllowDisallowSingleOrigin) {
SetLoggedInState(false);
ReadyToCommitNavigation();
EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
decider_agent_redirect_result());
EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
SetUpRobotsRules("https://foo.com", {{kRuleTypeAllow, "/public"},
{kRuleTypeDisallow, "/private"}});
EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/public.jpg"));
EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/private.jpg"));
EXPECT_FALSE(ShouldRedirectSubresource("https://m.foo.com/public.jpg"));
EXPECT_FALSE(ShouldRedirectSubresource("https://www.foo.com/public.jpg"));
EXPECT_FALSE(ShouldRedirectSubresource("https://bar.com/public.jpg"));
}
TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
TestHTTPRulesAreSeparate) {
SetLoggedInState(false);
ReadyToCommitNavigation();
EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
decider_agent_redirect_result());
EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
SetUpRobotsRules("https://foo.com", {{kRuleTypeAllow, "/public"},
{kRuleTypeDisallow, "/private"}});
EXPECT_FALSE(ShouldRedirectSubresource("http://foo.com/public.jpg"));
SetUpRobotsRules("http://foo.com", {{kRuleTypeAllow, "/public"},
{kRuleTypeDisallow, "/private"}});
EXPECT_TRUE(ShouldRedirectSubresource("http://foo.com/public.jpg"));
EXPECT_FALSE(ShouldRedirectSubresource("http://m.foo.com/public.jpg"));
EXPECT_FALSE(ShouldRedirectSubresource("http://www.foo.com/public.jpg"));
}
TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest, TestURLWithArguments) {
SetLoggedInState(false);
ReadyToCommitNavigation();
EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
decider_agent_redirect_result());
EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
SetUpRobotsRules("https://foo.com",
{{kRuleTypeAllow, "/*.jpg$"},
{kRuleTypeDisallow, "/*.png?*arg_disallowed"},
{kRuleTypeAllow, "/*.png"},
{kRuleTypeDisallow, "/*.gif?*arg_disallowed"},
{kRuleTypeAllow, "/*.gif"},
{kRuleTypeDisallow, "/"}});
EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/allowed.jpg"));
EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/allowed.png"));
EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/allowed.gif"));
EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/disallowed.jpg?arg"));
EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/allowed.png?arg"));
EXPECT_TRUE(
ShouldRedirectSubresource("https://foo.com/allowed.png?arg_allowed"));
EXPECT_TRUE(ShouldRedirectSubresource(
"https://foo.com/allowed.png?arg_allowed&arg_alllowed2"));
EXPECT_FALSE(ShouldRedirectSubresource(
"https://foo.com/disallowed.png?arg_disallowed"));
EXPECT_FALSE(ShouldRedirectSubresource(
"https://foo.com/disallowed.png?arg_disallowed&arg_disallowed2"));
}
TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
TestRulesAreCaseSensitive) {
SetLoggedInState(false);
ReadyToCommitNavigation();
EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
decider_agent_redirect_result());
EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
SetUpRobotsRules("https://foo.com", {{kRuleTypeAllow, "/allowed"},
{kRuleTypeAllow, "/CamelCase"},
{kRuleTypeAllow, "/CAPITALIZE"},
{kRuleTypeDisallow, "/"}});
EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/allowed.jpg"));
EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/CamelCase.jpg"));
EXPECT_TRUE(ShouldRedirectSubresource("https://foo.com/CAPITALIZE.jpg"));
EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/Allowed.jpg"));
EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/camelcase.jpg"));
EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/capitalize.jpg"));
}
TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
TestDisabledWhenLoggedIn) {
SetLoggedInState(true);
ReadyToCommitNavigation();
EXPECT_EQ(SubresourceRedirectResult::kIneligibleLoginDetected,
decider_agent_redirect_result());
EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
SetUpRobotsRules("https://foo.com", {{kRuleTypeAllow, "/public"},
{kRuleTypeDisallow, "/private"}});
EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/public.jpg"));
EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/private.jpg"));
}
// Test that when logged-in state is sent after the navigation commit, it does
// not take effect.
TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
TestLoggedinStateIgnoredWhenSentOutOfOrder) {
SetUpRobotsRules("https://foo.com", {{kRuleTypeAllow, "/public"},
{kRuleTypeDisallow, "/private"}});
ReadyToCommitNavigation();
SetLoggedInState(false);
EXPECT_EQ(SubresourceRedirectResult::kUnknown,
decider_agent_redirect_result());
EXPECT_FALSE(*is_pending_navigation_loggged_in());
EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/public.jpg"));
}
TEST_F(SubresourceRedirectLoginRobotsDeciderAgentTest,
TestClearedOnNavigation) {
SetLoggedInState(false);
ReadyToCommitNavigation();
EXPECT_EQ(SubresourceRedirectResult::kRedirectable,
decider_agent_redirect_result());
EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
// When a navigation starts the state should be cleared.
ReadyToCommitNavigation();
EXPECT_EQ(SubresourceRedirectResult::kUnknown,
decider_agent_redirect_result());
EXPECT_FALSE(is_pending_navigation_loggged_in().has_value());
EXPECT_FALSE(ShouldRedirectSubresource("https://foo.com/public.jpg"));
}
} // namespace subresource_redirect