Commit a1e6a91f authored by Robert Ogden's avatar Robert Ogden Committed by Commit Bot

Add triggering logic to HTTPS Server Previews

This adds the basic triggering logic to the Previews Navigation Throttle
(not including loadshedding or bypassing which will come in the next
CL).
Also creates a NavigationThrottleManager interface to decouple the Nav
Throttle from the decider. The decider will keep all the state for the
Nav Throttle since the throttle's lifetime is so short.

Most of the triggering testing is done in browser tests for sake of
clarity and complexity.

Bug: 864651
Change-Id: I1d1772c25ddfa159051bd5b1064c53256ced39f3
Reviewed-on: https://chromium-review.googlesource.com/1152164
Commit-Queue: Robert Ogden <robertogden@chromium.org>
Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Reviewed-by: default avatarDoug Arnett <dougarnett@chromium.org>
Reviewed-by: default avatarRyan Sturm <ryansturm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#582220}
parent 78aae16b
...@@ -1201,6 +1201,7 @@ jumbo_split_static_library("browser") { ...@@ -1201,6 +1201,7 @@ jumbo_split_static_library("browser") {
"previews/previews_lite_page_decider.h", "previews/previews_lite_page_decider.h",
"previews/previews_lite_page_navigation_throttle.cc", "previews/previews_lite_page_navigation_throttle.cc",
"previews/previews_lite_page_navigation_throttle.h", "previews/previews_lite_page_navigation_throttle.h",
"previews/previews_lite_page_navigation_throttle_manager.h",
"previews/previews_service.cc", "previews/previews_service.cc",
"previews/previews_service.h", "previews/previews_service.h",
"previews/previews_service_factory.cc", "previews/previews_service_factory.cc",
......
...@@ -2,7 +2,12 @@ ...@@ -2,7 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include <map>
#include <string>
#include "base/command_line.h" #include "base/command_line.h"
#include "base/metrics/field_trial_param_associator.h"
#include "base/metrics/field_trial_params.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
...@@ -61,6 +66,7 @@ class PreviewsBrowserTest : public InProcessBrowserTest { ...@@ -61,6 +66,7 @@ class PreviewsBrowserTest : public InProcessBrowserTest {
void SetUpOnMainThread() override { void SetUpOnMainThread() override {
noscript_css_requested_ = false; noscript_css_requested_ = false;
noscript_js_requested_ = false; noscript_js_requested_ = false;
https_url_count_ = 0;
// Set up https server with resource monitor. // Set up https server with resource monitor.
https_server_.reset( https_server_.reset(
...@@ -80,20 +86,28 @@ class PreviewsBrowserTest : public InProcessBrowserTest { ...@@ -80,20 +86,28 @@ class PreviewsBrowserTest : public InProcessBrowserTest {
// Set up http server with resource monitor and redirect handler. // Set up http server with resource monitor and redirect handler.
http_server_.reset( http_server_.reset(
new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTP)); new net::EmbeddedTestServer(net::EmbeddedTestServer::TYPE_HTTP));
http_server_->ServeFilesFromSourceDirectory("chrome/test/data/previews"); http_server_->ServeFilesFromSourceDirectory("chrome/test/data");
http_server_->RegisterRequestMonitor(base::BindRepeating( http_server_->RegisterRequestMonitor(base::BindRepeating(
&PreviewsBrowserTest::MonitorResourceRequest, base::Unretained(this))); &PreviewsBrowserTest::MonitorResourceRequest, base::Unretained(this)));
http_server_->RegisterRequestHandler(base::BindRepeating( http_server_->RegisterRequestHandler(base::BindRepeating(
&PreviewsBrowserTest::HandleRedirectRequest, base::Unretained(this))); &PreviewsBrowserTest::HandleRedirectRequest, base::Unretained(this)));
ASSERT_TRUE(http_server_->Start()); ASSERT_TRUE(http_server_->Start());
http_url_ = http_server_->GetURL("/noscript_test.html"); http_url_ = http_server_->GetURL("/previews/noscript_test.html");
ASSERT_TRUE(http_url_.SchemeIs(url::kHttpScheme)); ASSERT_TRUE(http_url_.SchemeIs(url::kHttpScheme));
redirect_url_ = http_server_->GetURL("/redirect.html"); subframe_url_ = http_server_->GetURL("/iframe_blank.html");
ASSERT_TRUE(subframe_url_.SchemeIs(url::kHttpScheme));
redirect_url_ = http_server_->GetURL("/previews/redirect.html");
ASSERT_TRUE(redirect_url_.SchemeIs(url::kHttpScheme)); ASSERT_TRUE(redirect_url_.SchemeIs(url::kHttpScheme));
} }
void ExecuteScript(const std::string& script) {
EXPECT_TRUE(content::ExecuteScript(
browser()->tab_strip_model()->GetActiveWebContents(), script));
}
void SetUpCommandLine(base::CommandLine* cmd) override { void SetUpCommandLine(base::CommandLine* cmd) override {
cmd->AppendSwitch("enable-spdy-proxy-auth"); cmd->AppendSwitch("enable-spdy-proxy-auth");
cmd->AppendSwitchASCII("force-effective-connection-type", "Slow-2G"); cmd->AppendSwitchASCII("force-effective-connection-type", "Slow-2G");
...@@ -103,12 +117,15 @@ class PreviewsBrowserTest : public InProcessBrowserTest { ...@@ -103,12 +117,15 @@ class PreviewsBrowserTest : public InProcessBrowserTest {
const GURL& https_no_transform_url() const { return https_no_transform_url_; } const GURL& https_no_transform_url() const { return https_no_transform_url_; }
const GURL& http_url() const { return http_url_; } const GURL& http_url() const { return http_url_; }
const GURL& redirect_url() const { return redirect_url_; } const GURL& redirect_url() const { return redirect_url_; }
const GURL& subframe_url() const { return subframe_url_; }
bool noscript_css_requested() const { return noscript_css_requested_; } bool noscript_css_requested() const { return noscript_css_requested_; }
bool noscript_js_requested() const { return noscript_js_requested_; } bool noscript_js_requested() const { return noscript_js_requested_; }
int https_url_count() const { return https_url_count_; }
private: private:
// Called by |https_server_|. // Called by |https_server_|.
void MonitorResourceRequest(const net::test_server::HttpRequest& request) { void MonitorResourceRequest(const net::test_server::HttpRequest& request) {
https_url_count_++;
if (request.GetURL().spec().find("noscript_test.css") != if (request.GetURL().spec().find("noscript_test.css") !=
std::string::npos) { std::string::npos) {
noscript_css_requested_ = true; noscript_css_requested_ = true;
...@@ -135,8 +152,10 @@ class PreviewsBrowserTest : public InProcessBrowserTest { ...@@ -135,8 +152,10 @@ class PreviewsBrowserTest : public InProcessBrowserTest {
GURL https_no_transform_url_; GURL https_no_transform_url_;
GURL http_url_; GURL http_url_;
GURL redirect_url_; GURL redirect_url_;
GURL subframe_url_;
bool noscript_css_requested_; bool noscript_css_requested_;
bool noscript_js_requested_; bool noscript_js_requested_;
int https_url_count_;
}; };
// Loads a webpage that has both script and noscript tags and also requests // Loads a webpage that has both script and noscript tags and also requests
...@@ -329,6 +348,8 @@ IN_PROC_BROWSER_TEST_F(PreviewsOptimizationGuideBrowserTest, ...@@ -329,6 +348,8 @@ IN_PROC_BROWSER_TEST_F(PreviewsOptimizationGuideBrowserTest,
EXPECT_FALSE(noscript_css_requested()); EXPECT_FALSE(noscript_css_requested());
} }
static const std::string kTestPreviewsServer = "https://litepages.test.com/";
// This test class enables LitePageServerPreviews. // This test class enables LitePageServerPreviews.
class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest { class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest {
public: public:
...@@ -337,28 +358,113 @@ class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest { ...@@ -337,28 +358,113 @@ class PreviewsLitePageServerBrowserTest : public PreviewsBrowserTest {
~PreviewsLitePageServerBrowserTest() override {} ~PreviewsLitePageServerBrowserTest() override {}
void SetUp() override { void SetUp() override {
scoped_feature_list_.InitWithFeatures( std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
{previews::features::kPreviews, {
previews::features::kLitePageServerPreviews}, // The trial and group names are dummy values.
{}); scoped_refptr<base::FieldTrial> trial =
base::FieldTrialList::CreateFieldTrial("TrialName1", "GroupName1");
std::map<std::string, std::string> feature_parameters = {
{"previews_host", kTestPreviewsServer}};
ASSERT_TRUE(base::FieldTrialParamAssociator::GetInstance()
->AssociateFieldTrialParams("TrialName1", "GroupName1",
feature_parameters));
feature_list->RegisterFieldTrialOverride(
previews::features::kLitePageServerPreviews.name,
base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get());
}
{
// The trial and group names are dummy values.
scoped_refptr<base::FieldTrial> trial =
base::FieldTrialList::CreateFieldTrial("TrialName3", "GroupName3");
feature_list->RegisterFieldTrialOverride(
previews::features::kPreviews.name,
base::FeatureList::OVERRIDE_ENABLE_FEATURE, trial.get());
}
scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
PreviewsBrowserTest::SetUp(); PreviewsBrowserTest::SetUp();
} }
GURL NavigatedURL() {
return browser()->tab_strip_model()->GetActiveWebContents()->GetURL();
}
void VerifyPreviewLoaded() {
EXPECT_TRUE(NavigatedURL().DomainIs(GURL(kTestPreviewsServer).host()));
}
void VerifyPreviewNotLoaded() {
EXPECT_FALSE(NavigatedURL().DomainIs(GURL(kTestPreviewsServer).host()));
}
private: private:
base::test::ScopedFeatureList scoped_feature_list_; base::test::ScopedFeatureList scoped_feature_list_;
}; };
// Previews InfoBar (which these tests triggers) does not work on Mac. // Previews InfoBar (which these tests trigger) does not work on Mac.
// See crbug.com/782322 for detail. // See crbug.com/782322 for detail.
// Also occasional flakes on win7 (crbug.com/789542). // Also occasional flakes on win7 (crbug.com/789542).
#if defined(OS_ANDROID) || defined(OS_LINUX) #if defined(OS_ANDROID) || defined(OS_LINUX)
#define MAYBE_LitePagePreviewsEnabled LitePagePreviewsEnabled #define MAYBE_LitePagePreviewsTriggering LitePagePreviewsTriggering
#else #else
#define MAYBE_LitePagePreviewsEnabled DISABLED_LitePagePreviewsEnabled #define MAYBE_LitePagePreviewsTriggering DISABLED_LitePagePreviewsTriggering
#endif #endif
// Makes sure that nothing crashes.
IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest, IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerBrowserTest,
MAYBE_LitePagePreviewsEnabled) { MAYBE_LitePagePreviewsTriggering) {
// Verify the preview is not triggered on HTTP pageloads.
ui_test_utils::NavigateToURL(browser(), http_url());
VerifyPreviewNotLoaded();
// Verify the preview is triggered on HTTPS pageloads.
ui_test_utils::NavigateToURL(browser(), https_url());
VerifyPreviewLoaded();
// Verify the preview is triggered when an HTTP page redirects to HTTPS.
ui_test_utils::NavigateToURL(browser(), redirect_url());
VerifyPreviewLoaded();
// Verify the preview is not triggered for POST navigations.
std::string post_data = "helloworld";
NavigateParams params(browser(), https_url(), ui::PAGE_TRANSITION_LINK);
params.window_action = NavigateParams::SHOW_WINDOW;
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
params.is_renderer_initiated = false;
params.uses_post = true;
params.post_data = network::ResourceRequestBody::CreateFromBytes(
post_data.data(), post_data.size());
ui_test_utils::NavigateToURL(&params);
VerifyPreviewNotLoaded();
// Verify the preview is not triggered when navigating to the previews server.
ui_test_utils::NavigateToURL(browser(), GURL(kTestPreviewsServer));
EXPECT_EQ(NavigatedURL(), GURL(kTestPreviewsServer));
// Verify a subframe navigation does not trigger a preview.
const int starting_https_url_count = https_url_count();
ui_test_utils::NavigateToURL(browser(), subframe_url());
ExecuteScript("window.open(\"" + https_url().spec() + "\", \"subframe\")");
EXPECT_EQ(https_url_count(), starting_https_url_count + 1);
}
class PreviewsLitePageServerDataSaverBrowserTest
: public PreviewsLitePageServerBrowserTest {
public:
PreviewsLitePageServerDataSaverBrowserTest() = default;
~PreviewsLitePageServerDataSaverBrowserTest() override = default;
// Overrides the cmd line in PreviewsBrowserTest and leave out the flag to
// enable DataSaver.
void SetUpCommandLine(base::CommandLine* cmd) override {
cmd->AppendSwitchASCII("force-effective-connection-type", "Slow-2G");
}
};
IN_PROC_BROWSER_TEST_F(PreviewsLitePageServerDataSaverBrowserTest,
MAYBE_LitePagePreviewsTriggering) {
// Verify the preview is not triggered on HTTPS pageloads without DataSaver.
ui_test_utils::NavigateToURL(browser(), https_url()); ui_test_utils::NavigateToURL(browser(), https_url());
VerifyPreviewNotLoaded();
} }
...@@ -48,17 +48,12 @@ PreviewsLitePageDecider::MaybeCreateThrottleFor( ...@@ -48,17 +48,12 @@ PreviewsLitePageDecider::MaybeCreateThrottleFor(
previews::params::IsLitePageServerPreviewsEnabled(); previews::params::IsLitePageServerPreviewsEnabled();
if (drp_enabled && preview_enabled) { if (drp_enabled && preview_enabled) {
return PreviewsLitePageNavigationThrottle::MaybeCreateThrottleFor(handle); return std::make_unique<PreviewsLitePageNavigationThrottle>(handle,
decider);
} }
return nullptr; return nullptr;
} }
void PreviewsLitePageDecider::SetRetryAt(base::TimeTicks retry_at) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (retry_at > retry_at_)
retry_at_ = retry_at;
}
bool PreviewsLitePageDecider::IsDataSaverEnabled( bool PreviewsLitePageDecider::IsDataSaverEnabled(
content::NavigationHandle* handle) const { content::NavigationHandle* handle) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
...@@ -73,6 +68,13 @@ bool PreviewsLitePageDecider::IsDataSaverEnabled( ...@@ -73,6 +68,13 @@ bool PreviewsLitePageDecider::IsDataSaverEnabled(
return drp_settings->IsDataReductionProxyEnabled(); return drp_settings->IsDataReductionProxyEnabled();
} }
void PreviewsLitePageDecider::SetServerUnavailableUntil(
base::TimeTicks retry_at) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!retry_at_.has_value() || retry_at > retry_at_)
retry_at_ = retry_at;
}
bool PreviewsLitePageDecider::IsServerUnavailable(base::TimeTicks now) { bool PreviewsLitePageDecider::IsServerUnavailable(base::TimeTicks now) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!retry_at_.has_value()) if (!retry_at_.has_value())
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_ #ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_
#define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_ #define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_DECIDER_H_
#include <map>
#include <memory> #include <memory>
#include "base/gtest_prod_util.h" #include "base/gtest_prod_util.h"
...@@ -12,17 +13,18 @@ ...@@ -12,17 +13,18 @@
#include "base/optional.h" #include "base/optional.h"
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h"
namespace content { namespace content {
class NavigationHandle; class NavigationHandle;
class NavigationThrottle; class NavigationThrottle;
} // namespace content } // namespace content
// This class manages the state for triggering the Lite Page Server Preview.
// This class ensures that the feature is enabled and the // This class ensures that the feature is enabled and the
// current Profile is not incognito before handing off the real legwork of the // current Profile is not incognito before handing off the real legwork of the
// triggering decision to |PreviewsLitePageNavigationThrottle|. // triggering decision to |PreviewsLitePageNavigationThrottle|.
class PreviewsLitePageDecider { class PreviewsLitePageDecider
: public PreviewsLitePageNavigationThrottleManager {
public: public:
PreviewsLitePageDecider(); PreviewsLitePageDecider();
virtual ~PreviewsLitePageDecider(); virtual ~PreviewsLitePageDecider();
...@@ -33,10 +35,6 @@ class PreviewsLitePageDecider { ...@@ -33,10 +35,6 @@ class PreviewsLitePageDecider {
static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor( static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
content::NavigationHandle* handle); content::NavigationHandle* handle);
// Used to notify that the Previews Server should not be sent anymore requests
// until after the given time.
void SetRetryAt(base::TimeTicks retry_at);
protected: protected:
// Virtual for testing. // Virtual for testing.
virtual bool IsDataSaverEnabled(content::NavigationHandle* handle) const; virtual bool IsDataSaverEnabled(content::NavigationHandle* handle) const;
...@@ -44,9 +42,9 @@ class PreviewsLitePageDecider { ...@@ -44,9 +42,9 @@ class PreviewsLitePageDecider {
private: private:
FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageDeciderTest, TestServerUnavailable); FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageDeciderTest, TestServerUnavailable);
// Returns true if a Preview should not be triggered because the server is // PreviewsLitePageNavigationThrottleManager:
// unavailable. void SetServerUnavailableUntil(base::TimeTicks retry_at) override;
bool IsServerUnavailable(base::TimeTicks now); bool IsServerUnavailable(base::TimeTicks now) override;
// The time after which it is ok to send the server more preview requests. // The time after which it is ok to send the server more preview requests.
base::Optional<base::TimeTicks> retry_at_; base::Optional<base::TimeTicks> retry_at_;
......
...@@ -38,7 +38,7 @@ TEST_F(PreviewsLitePageDeciderTest, TestServerUnavailable) { ...@@ -38,7 +38,7 @@ TEST_F(PreviewsLitePageDeciderTest, TestServerUnavailable) {
for (const TestCase& test_case : kTestCases) { for (const TestCase& test_case : kTestCases) {
std::unique_ptr<PreviewsLitePageDecider> decider = std::unique_ptr<PreviewsLitePageDecider> decider =
std::make_unique<PreviewsLitePageDecider>(); std::make_unique<PreviewsLitePageDecider>();
decider->SetRetryAt(test_case.retry_at); decider->SetServerUnavailableUntil(test_case.retry_at);
EXPECT_EQ(decider->IsServerUnavailable(test_case.now), EXPECT_EQ(decider->IsServerUnavailable(test_case.now),
test_case.want_is_unavailable); test_case.want_is_unavailable);
} }
......
...@@ -4,31 +4,144 @@ ...@@ -4,31 +4,144 @@
#include "chrome/browser/previews/previews_lite_page_navigation_throttle.h" #include "chrome/browser/previews/previews_lite_page_navigation_throttle.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "components/base32/base32.h"
#include "components/previews/core/previews_experiments.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/page_navigator.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_user_data.h"
#include "content/public/common/referrer.h"
#include "crypto/sha2.h"
#include "net/base/escape.h"
#include "url/gurl.h"
namespace {
bool IsPreviewsDomain(const GURL& url) {
GURL previews_host = previews::params::GetLitePagePreviewsDomainURL();
return url.DomainIs(previews_host.host()) &&
url.EffectiveIntPort() == previews_host.EffectiveIntPort();
}
} // namespace
class WebContentsLifetimeHelper
: public content::WebContentsUserData<WebContentsLifetimeHelper> {
public:
explicit WebContentsLifetimeHelper(content::WebContents* web_contents)
: web_contents_(web_contents), weak_factory_(this) {}
base::WeakPtr<WebContentsLifetimeHelper> GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void PostNewNavigation(const content::OpenURLParams& url_params) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(url_params.url.is_valid());
DCHECK(url_params.url.SchemeIs(url::kHttpsScheme));
web_contents_->OpenURL(url_params);
}
private:
content::WebContents* web_contents_;
base::WeakPtrFactory<WebContentsLifetimeHelper> weak_factory_;
};
PreviewsLitePageNavigationThrottle::PreviewsLitePageNavigationThrottle( PreviewsLitePageNavigationThrottle::PreviewsLitePageNavigationThrottle(
content::NavigationHandle* handle) content::NavigationHandle* handle,
: content::NavigationThrottle(handle) { PreviewsLitePageNavigationThrottleManager* manager)
: content::NavigationThrottle(handle), manager_(manager) {
DCHECK(manager_);
DCHECK(handle); DCHECK(handle);
DCHECK(handle->GetWebContents());
DCHECK(handle->GetWebContents()->GetBrowserContext());
} }
PreviewsLitePageNavigationThrottle::~PreviewsLitePageNavigationThrottle() = PreviewsLitePageNavigationThrottle::~PreviewsLitePageNavigationThrottle() =
default; default;
// static bool PreviewsLitePageNavigationThrottle::IsEligibleForPreview() const {
std::unique_ptr<content::NavigationThrottle> if (!navigation_handle()->GetURL().SchemeIs(url::kHttpsScheme))
PreviewsLitePageNavigationThrottle::MaybeCreateThrottleFor( return false;
content::NavigationHandle* handle) {
DCHECK(handle); if (navigation_handle()->IsPost())
return nullptr; return false;
if (!navigation_handle()->IsInMainFrame())
return false;
if (IsPreviewsDomain(navigation_handle()->GetURL()))
return false;
return true;
}
GURL PreviewsLitePageNavigationThrottle::GetPreviewsURL() const {
GURL original_url = navigation_handle()->GetURL();
DCHECK(original_url.is_valid());
DCHECK(!IsPreviewsDomain(original_url));
std::string origin_hash = base::ToLowerASCII(base32::Base32Encode(
crypto::SHA256HashString(
original_url.scheme() + "://" + original_url.host() + ":" +
base::IntToString(original_url.EffectiveIntPort())),
base32::Base32EncodePolicy::OMIT_PADDING));
GURL previews_host = previews::params::GetLitePagePreviewsDomainURL();
GURL previews_url = GURL(
previews_host.scheme() + "://" + origin_hash + "." +
previews_host.host() +
(previews_host.has_port() ? (":" + previews_host.port()) : "") + "/p?u=" +
net::EscapeQueryParamValue(original_url.spec(), true /* use_plus */));
DCHECK(previews_url.is_valid());
DCHECK(previews_url.SchemeIs(url::kHttpsScheme));
return previews_url;
} }
content::NavigationThrottle::ThrottleCheckResult content::NavigationThrottle::ThrottleCheckResult
PreviewsLitePageNavigationThrottle::WillStartRequest() { PreviewsLitePageNavigationThrottle::MaybeNavigateToPreview() const {
if (!IsEligibleForPreview()) {
return content::NavigationThrottle::PROCEED; return content::NavigationThrottle::PROCEED;
}
content::OpenURLParams url_params(GetPreviewsURL(),
navigation_handle()->GetReferrer(),
WindowOpenDisposition::CURRENT_TAB,
navigation_handle()->GetPageTransition(),
navigation_handle()->IsRendererInitiated());
// TODO(crbug.com/864652): Add chrome-proxy headers.
url_params.redirect_chain = navigation_handle()->GetRedirectChain();
url_params.frame_tree_node_id = navigation_handle()->GetFrameTreeNodeId();
url_params.user_gesture = navigation_handle()->HasUserGesture();
url_params.started_from_context_menu =
navigation_handle()->WasStartedFromContextMenu();
// The helper class and its weak pointer protect against the WebContents
// dying in-between the PostTask and its execution, resulting in a use after
// free bug. Since the helper is a WebContentsUserData, it will be
// destroyed when the WebContents is and the task will not be executed.
content::WebContents* web_contents = navigation_handle()->GetWebContents();
WebContentsLifetimeHelper::CreateForWebContents(web_contents);
WebContentsLifetimeHelper* helper =
WebContentsLifetimeHelper::FromWebContents(web_contents);
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE,
base::BindOnce(&WebContentsLifetimeHelper::PostNewNavigation,
helper->GetWeakPtr(), url_params));
return content::NavigationThrottle::CANCEL;
}
content::NavigationThrottle::ThrottleCheckResult
PreviewsLitePageNavigationThrottle::WillStartRequest() {
return MaybeNavigateToPreview();
} }
content::NavigationThrottle::ThrottleCheckResult content::NavigationThrottle::ThrottleCheckResult
PreviewsLitePageNavigationThrottle::WillRedirectRequest() { PreviewsLitePageNavigationThrottle::WillRedirectRequest() {
return content::NavigationThrottle::PROCEED; return MaybeNavigateToPreview();
} }
content::NavigationThrottle::ThrottleCheckResult content::NavigationThrottle::ThrottleCheckResult
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_H_ #ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_H_
#define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_H_ #define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_H_
#include "base/gtest_prod_util.h"
#include "chrome/browser/previews/previews_lite_page_navigation_throttle_manager.h"
#include "content/public/browser/navigation_handle.h" #include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.h" #include "content/public/browser/navigation_throttle.h"
...@@ -14,17 +16,34 @@ ...@@ -14,17 +16,34 @@
// new one to the Previews Server. // new one to the Previews Server.
class PreviewsLitePageNavigationThrottle : public content::NavigationThrottle { class PreviewsLitePageNavigationThrottle : public content::NavigationThrottle {
public: public:
explicit PreviewsLitePageNavigationThrottle( PreviewsLitePageNavigationThrottle(
content::NavigationHandle* handle); content::NavigationHandle* handle,
PreviewsLitePageNavigationThrottleManager* manager);
~PreviewsLitePageNavigationThrottle() override; ~PreviewsLitePageNavigationThrottle() override;
// If a Preview is triggered, this method returns an instance of |this|.
// Otherwise it will return a nullptr.
static std::unique_ptr<content::NavigationThrottle> MaybeCreateThrottleFor(
content::NavigationHandle* handle);
private: private:
FRIEND_TEST_ALL_PREFIXES(PreviewsLitePageNavigationThrottleTest,
TestGetPreviewsURL);
// The current effective connection type;
net::EffectiveConnectionType GetECT() const;
// Returns true if the current URL is eligible for the preview. Does not check
// any blacklisting or single bypass.
bool IsEligibleForPreview() const;
// Returns the URL for a preview of the current URL given by the navigation
// handle.
GURL GetPreviewsURL() const;
// This method is called on every request and redirect. It checks all
// eligibility and blacklisting criteria for the current URL and will return
// CANCEL when the preview is triggered and post a new navigation, or will
// return PROCEED if the preview is not triggered.
content::NavigationThrottle::ThrottleCheckResult MaybeNavigateToPreview()
const;
// content::NavigationThrottle implementation: // content::NavigationThrottle implementation:
content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override; content::NavigationThrottle::ThrottleCheckResult WillStartRequest() override;
content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest() content::NavigationThrottle::ThrottleCheckResult WillRedirectRequest()
...@@ -34,6 +53,10 @@ class PreviewsLitePageNavigationThrottle : public content::NavigationThrottle { ...@@ -34,6 +53,10 @@ class PreviewsLitePageNavigationThrottle : public content::NavigationThrottle {
override; override;
const char* GetNameForLogging() override; const char* GetNameForLogging() override;
// The manager class to report state changes to and get state from. Outlives
// |this|.
PreviewsLitePageNavigationThrottleManager* manager_;
DISALLOW_COPY_AND_ASSIGN(PreviewsLitePageNavigationThrottle); DISALLOW_COPY_AND_ASSIGN(PreviewsLitePageNavigationThrottle);
}; };
......
// Copyright 2018 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.
#ifndef CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_MANAGER_H_
#define CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_MANAGER_H_
#include "base/time/time.h"
// This interface specifies the interaction that a
// |PreviewsLitePageNavigationThrottle| has with it's state manager. This class
// tracks the state of the Navigation Throttle since a single instance of the
// navigation throttle can be very short lived, and therefore is not aware of
// any actions taken by its predecessor.
class PreviewsLitePageNavigationThrottleManager {
public:
// Used to notify that the Previews Server should not be sent anymore requests
// until after the given time.
virtual void SetServerUnavailableUntil(base::TimeTicks retry_at) = 0;
// Returns true if a Preview should not be triggered because the server is
// unavailable.
virtual bool IsServerUnavailable(base::TimeTicks now) = 0;
};
#endif // CHROME_BROWSER_PREVIEWS_PREVIEWS_LITE_PAGE_NAVIGATION_THROTTLE_MANAGER_H_
// Copyright 2018 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/previews/previews_lite_page_navigation_throttle.h"
#include <memory>
#include "base/memory/ptr_util.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "chrome/browser/previews/previews_lite_page_decider.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/previews/core/previews_features.h"
#include "content/public/browser/navigation_handle.h"
#include "net/http/http_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace {
const char kTestUrl[] = "https://www.test.com/";
}
class PreviewsLitePageNavigationThrottleTest
: public ChromeRenderViewHostTestHarness {
protected:
PreviewsLitePageNavigationThrottleTest() = default;
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
test_handle_ = content::NavigationHandle::CreateNavigationHandleForTesting(
GURL(kTestUrl), main_rfh());
content::RenderFrameHostTester::For(main_rfh())
->InitializeRenderFrameIfNeeded();
}
void SimulateCommit() {
test_handle_->CallDidCommitNavigationForTesting(test_handle_->GetURL());
}
void SimulateWillProcessResponse() {
std::string headers("HTTP/1.1 200 OK\n\n");
test_handle_->CallWillProcessResponseForTesting(
main_rfh(),
net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size()));
SimulateCommit();
}
void CallDidFinishNavigation() { test_handle_.reset(); }
content::NavigationHandle* handle() { return test_handle_.get(); }
content::NavigationHandle* handle_with_url(std::string url) {
test_handle_ = content::NavigationHandle::CreateNavigationHandleForTesting(
GURL(url), main_rfh());
return test_handle_.get();
}
private:
std::unique_ptr<content::NavigationHandle> test_handle_;
};
TEST_F(PreviewsLitePageNavigationThrottleTest, TestGetPreviewsURL) {
struct TestCase {
std::string previews_host;
std::string original_url;
std::string previews_url;
};
const TestCase kTestCases[]{
// Use https://play.golang.org/p/HUM2HxmUTOW to compute |previews_url|.
{
"https://previews.host.com",
"https://original.host.com/path/path/path?query=yes",
"https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
"previews.host.com/p?u="
"https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
},
{
"https://previews.host.com",
"http://original.host.com/path/path/path?query=yes",
"https://6p7dar4ju6r4ynz7x3pucmlcltuqsf7z5auhvckzln7voglkt56q."
"previews.host.com/p?u="
"http%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
},
{
"https://previews.host.com",
"https://original.host.com:1443/path/path/path?query=yes",
"https://mil6oxtqb4zpsbmutm4d7wrx5nlr6tzlxjp7y44u55zqhzsdzjpq."
"previews.host.com/p?u=https%3A%2F%2Foriginal.host.com%3A1443"
"%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
},
{
"https://previews.host.com:1443",
"http://original.host.com/path/path/path?query=yes",
"https://6p7dar4ju6r4ynz7x3pucmlcltuqsf7z5auhvckzln7voglkt56q."
"previews.host.com:1443/p?u="
"http%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
},
{
"https://previews.host.com:1443",
"https://original.host.com:1443/path/path/path?query=yes",
"https://mil6oxtqb4zpsbmutm4d7wrx5nlr6tzlxjp7y44u55zqhzsdzjpq."
"previews.host.com:1443/p?u=https%3A%2F%2Foriginal.host.com%3A1443"
"%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes",
},
{
"https://previews.host.com",
"https://original.host.com/path/path/path?query=yes#fragment",
"https://shta44dh4bi7rc6fnpjnkrtytwlabygjhk53v2trlot2wddylwua."
"previews.host.com/p?u="
"https%3A%2F%2Foriginal.host.com%2Fpath%2Fpath%2Fpath%3Fquery%3Dyes"
"%23fragment",
},
};
for (const TestCase& test_case : kTestCases) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
previews::features::kLitePageServerPreviews,
{{"previews_host", test_case.previews_host}});
std::unique_ptr<PreviewsLitePageDecider> decider =
std::make_unique<PreviewsLitePageDecider>();
std::unique_ptr<PreviewsLitePageNavigationThrottle> throttle =
std::make_unique<PreviewsLitePageNavigationThrottle>(
handle_with_url(test_case.original_url), decider.get());
EXPECT_EQ(throttle->GetPreviewsURL(), test_case.previews_url);
}
// Boilerplate navigation to keep the test harness happy.
SimulateWillProcessResponse();
CallDidFinishNavigation();
}
...@@ -2616,6 +2616,7 @@ test("unit_tests") { ...@@ -2616,6 +2616,7 @@ test("unit_tests") {
"../browser/previews/previews_infobar_delegate_unittest.cc", "../browser/previews/previews_infobar_delegate_unittest.cc",
"../browser/previews/previews_infobar_tab_helper_unittest.cc", "../browser/previews/previews_infobar_tab_helper_unittest.cc",
"../browser/previews/previews_lite_page_decider_unittest.cc", "../browser/previews/previews_lite_page_decider_unittest.cc",
"../browser/previews/previews_lite_page_navigation_throttle_unittest.cc",
"../browser/previews/previews_service_unittest.cc", "../browser/previews/previews_service_unittest.cc",
"../browser/process_singleton_win_unittest.cc", "../browser/process_singleton_win_unittest.cc",
"../browser/profiles/gaia_info_update_service_unittest.cc", "../browser/profiles/gaia_info_update_service_unittest.cc",
......
...@@ -129,6 +129,18 @@ base::TimeDelta OfflinePreviewFreshnessDuration() { ...@@ -129,6 +129,18 @@ base::TimeDelta OfflinePreviewFreshnessDuration() {
"offline_preview_freshness_duration_in_days", 7)); "offline_preview_freshness_duration_in_days", 7));
} }
GURL GetLitePagePreviewsDomainURL() {
std::string variable_host_str = GetFieldTrialParamValueByFeature(
features::kLitePageServerPreviews, "previews_host");
if (!variable_host_str.empty()) {
GURL variable_host(variable_host_str);
DCHECK(variable_host.is_valid());
DCHECK(variable_host.has_scheme());
return variable_host;
}
return GURL("https://litepages.googlezip.net/");
}
net::EffectiveConnectionType GetECTThresholdForPreview( net::EffectiveConnectionType GetECTThresholdForPreview(
previews::PreviewsType type) { previews::PreviewsType type) {
switch (type) { switch (type) {
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "net/nqe/effective_connection_type.h" #include "net/nqe/effective_connection_type.h"
#include "url/gurl.h"
namespace previews { namespace previews {
...@@ -88,6 +89,9 @@ base::TimeDelta SingleOptOutDuration(); ...@@ -88,6 +89,9 @@ base::TimeDelta SingleOptOutDuration();
// shown as a preview. // shown as a preview.
base::TimeDelta OfflinePreviewFreshnessDuration(); base::TimeDelta OfflinePreviewFreshnessDuration();
// The host for Lite Page server previews.
GURL GetLitePagePreviewsDomainURL();
// The threshold of EffectiveConnectionType above which preview |type| will be // The threshold of EffectiveConnectionType above which preview |type| will be
// triggered. // triggered.
net::EffectiveConnectionType GetECTThresholdForPreview( net::EffectiveConnectionType GetECTThresholdForPreview(
......
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