blob: 93c70a446f9e701734dc476a3029a7f92eb15f91 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "weblayer/public/js_communication/web_message.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "content/public/common/content_features.h"
#include "net/dns/mock_host_resolver.h"
#include "weblayer/public/js_communication/web_message.h"
#include "weblayer/public/js_communication/web_message_host.h"
#include "weblayer/public/js_communication/web_message_host_factory.h"
#include "weblayer/public/js_communication/web_message_reply_proxy.h"
#include "weblayer/public/navigation.h"
#include "weblayer/public/navigation_controller.h"
#include "weblayer/public/tab.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/weblayer_browser_test.h"
#include "weblayer/test/weblayer_browser_test_utils.h"
namespace weblayer {
namespace {
class WebMessageHostImpl;
WebMessageHostImpl* current_connection = nullptr;
// WebMessageHost implementation that records contents of OnPostMessage().
class WebMessageHostImpl : public WebMessageHost {
public:
WebMessageHostImpl(base::RepeatingClosure quit_closure,
const std::string& origin_string,
bool is_main_frame,
WebMessageReplyProxy* proxy)
: quit_closure_(quit_closure), proxy_(proxy) {
current_connection = this;
}
~WebMessageHostImpl() override {
if (current_connection == this)
current_connection = nullptr;
}
int back_forward_cache_state_changed_call_count() const {
return back_forward_cache_state_changed_call_count_;
}
void WaitForBackForwardStateToBe(bool value) {
if (value == proxy_->IsInBackForwardCache())
return;
expected_back_forward_value_ = value;
state_changed_run_loop_ = std::make_unique<base::RunLoop>();
state_changed_run_loop_->Run();
state_changed_run_loop_.reset();
}
WebMessageReplyProxy* proxy() { return proxy_; }
std::vector<std::u16string>& messages() { return messages_; }
// WebMessageHost:
void OnPostMessage(std::unique_ptr<WebMessage> message) override {
messages_.push_back(std::move(message->message));
if (++call_count_ == 1) {
// First time called, send a message to the page.
std::unique_ptr<WebMessage> m2 = std::make_unique<WebMessage>();
m2->message = u"from c++";
proxy_->PostWebMessage(std::move(m2));
} else {
// On subsequent calls quit.
quit_closure_.Run();
}
}
void OnBackForwardCacheStateChanged() override {
++back_forward_cache_state_changed_call_count_;
if (state_changed_run_loop_ &&
expected_back_forward_value_ == proxy_->IsInBackForwardCache()) {
state_changed_run_loop_->Quit();
}
}
private:
int call_count_ = 0;
int back_forward_cache_state_changed_call_count_ = 0;
base::RepeatingClosure quit_closure_;
raw_ptr<WebMessageReplyProxy> proxy_;
std::vector<std::u16string> messages_;
bool expected_back_forward_value_ = false;
std::unique_ptr<base::RunLoop> state_changed_run_loop_;
};
// WebMessageHostFactory implementation that creates WebMessageHostImpl.
class WebMessageHostFactoryImpl : public WebMessageHostFactory {
public:
explicit WebMessageHostFactoryImpl(base::RepeatingClosure quit_closure)
: quit_closure_(quit_closure) {}
~WebMessageHostFactoryImpl() override = default;
// WebMessageHostFactory:
std::unique_ptr<WebMessageHost> CreateHost(
const std::string& origin_string,
bool is_main_frame,
WebMessageReplyProxy* proxy) override {
return std::make_unique<WebMessageHostImpl>(quit_closure_, origin_string,
is_main_frame, proxy);
}
private:
base::RepeatingClosure quit_closure_;
};
} // namespace
using WebMessageTest = WebLayerBrowserTest;
IN_PROC_BROWSER_TEST_F(WebMessageTest, SendAndReceive) {
EXPECT_TRUE(embedded_test_server()->Start());
base::RunLoop run_loop;
shell()->tab()->AddWebMessageHostFactory(
std::make_unique<WebMessageHostFactoryImpl>(run_loop.QuitClosure()), u"x",
{"*"});
// web_message_test.html posts a message immediately.
shell()->tab()->GetNavigationController()->Navigate(
embedded_test_server()->GetURL("/web_message_test.html"));
run_loop.Run();
// There should be two messages. The one from the page, and the ack triggered
// when WebMessageHostImpl calls PostMessage().
ASSERT_TRUE(current_connection);
ASSERT_EQ(2u, current_connection->messages().size());
EXPECT_EQ(u"from page", current_connection->messages()[0]);
EXPECT_EQ(u"bouncing from c++", current_connection->messages()[1]);
// WebLayer's Page has no functions, verify it can be requested.
current_connection->proxy()->GetPage();
}
// Ensures that a listener removed from a post message works.
IN_PROC_BROWSER_TEST_F(WebMessageTest, RemoveFromReceive) {
EXPECT_TRUE(embedded_test_server()->Start());
base::RunLoop run_loop;
shell()->tab()->AddWebMessageHostFactory(
std::make_unique<WebMessageHostFactoryImpl>(run_loop.QuitClosure()), u"x",
{"*"});
// web_message_test.html posts a message immediately.
shell()->tab()->GetNavigationController()->Navigate(
embedded_test_server()->GetURL("/web_message_test2.html"));
run_loop.Run();
// There should be two messages. The one from the page, and the ack triggered
// when WebMessageHostImpl calls PostMessage().
ASSERT_TRUE(current_connection);
ASSERT_EQ(2u, current_connection->messages().size());
EXPECT_EQ(u"from page", current_connection->messages()[0]);
EXPECT_EQ(u"bouncing from c++", current_connection->messages()[1]);
// WebLayer's Page has no functions, verify it can be requested.
current_connection->proxy()->GetPage();
}
class WebMessageTestWithBfCache : public WebLayerBrowserTest {
public:
// WebLayerBrowserTest:
void SetUp() override {
scoped_feature_list_.InitWithFeaturesAndParameters(
{{features::kBackForwardCache, {{}}},
{features::kBackForwardCacheTimeToLiveControl,
{// Set a very long TTL before expiration (longer than the test
// timeout) so tests that are expecting deletion don't pass when
// they shouldn't.
{"time_to_live_seconds", "3600"}}}},
// Allow BackForwardCache for all devices regardless of their memory.
{features::kBackForwardCacheMemoryControls});
WebLayerBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
WebLayerBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(WebMessageTestWithBfCache, Basic) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url1(embedded_test_server()->GetURL("a.com", "/web_message_test.html"));
base::RunLoop run_loop;
shell()->tab()->AddWebMessageHostFactory(
std::make_unique<WebMessageHostFactoryImpl>(run_loop.QuitClosure()), u"x",
{"http://a.com:" + url1.port()});
auto* navigation_controller = shell()->tab()->GetNavigationController();
navigation_controller->Navigate(url1);
run_loop.Run();
// A WebMessageHostImpl should be created, and it should not be in the cache.
WebMessageHostImpl* web_message_host = current_connection;
ASSERT_TRUE(web_message_host);
EXPECT_FALSE(web_message_host->proxy()->IsInBackForwardCache());
EXPECT_EQ(0, web_message_host->back_forward_cache_state_changed_call_count());
Page* original_page = &(web_message_host->proxy()->GetPage());
// Navigate to a new host. The old page should go into the cache.
OneShotNavigationObserver observer1(shell());
navigation_controller->Navigate(
embedded_test_server()->GetURL("b.com", "/simple_page.html"));
observer1.WaitForNavigation();
EXPECT_TRUE(observer1.completed());
ASSERT_EQ(current_connection, web_message_host);
web_message_host->WaitForBackForwardStateToBe(true);
EXPECT_EQ(1, web_message_host->back_forward_cache_state_changed_call_count());
// Navigate back.
OneShotNavigationObserver observer2(shell());
navigation_controller->GoBack();
observer2.WaitForNavigation();
web_message_host->WaitForBackForwardStateToBe(false);
EXPECT_EQ(2, web_message_host->back_forward_cache_state_changed_call_count());
EXPECT_EQ(original_page, &(web_message_host->proxy()->GetPage()));
}
} // namespace weblayer