blob: 93ff57be9a7200352de32dbe0d1eb9522a98dd9f [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/functional/bind.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/common/content_paths.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/service_worker_test_helpers.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/script_executor.h"
#include "extensions/browser/script_result_queue.h"
#include "extensions/browser/service_worker/service_worker_test_utils.h"
#include "extensions/test/extension_test_message_listener.h"
#include "extensions/test/result_catcher.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/create_websocket_handler.h"
#include "net/test/embedded_test_server/websocket_connection.h"
#include "net/test/embedded_test_server/websocket_handler.h"
#include "net/test/test_data_directory.h"
namespace extensions {
namespace {
// WebSocketHandler that expects to receive a single text message if "9", and
// once it does, writes "ping" to the WebSocket 9 times, each write 250
// milliseconds after the last one. Used by the
// "ReceivingWebSocketMessagesResetsServiceWorkerIdleTime" test.
class SendMessagesWebSocketHandler : public net::test_server::WebSocketHandler {
public:
explicit SendMessagesWebSocketHandler(
scoped_refptr<net::test_server::WebSocketConnection> connection)
: WebSocketHandler(std::move(connection)) {}
~SendMessagesWebSocketHandler() override = default;
void OnTextMessage(std::string_view message) override {
EXPECT_EQ(message, "9");
EXPECT_EQ(remaining_messages_, 9);
SendMessage();
timer_.Start(FROM_HERE, base::Milliseconds(250),
base::BindRepeating(&SendMessagesWebSocketHandler::SendMessage,
base::Unretained(this)));
}
private:
void SendMessage() {
CHECK_GT(remaining_messages_, 0);
CHECK(connection());
connection()->SendTextMessage("ping");
--remaining_messages_;
if (remaining_messages_ == 0) {
timer_.Stop();
}
}
int remaining_messages_ = 9;
base::RepeatingTimer timer_;
};
} // namespace
class ExtensionWebSocketApiTest : public ExtensionApiTest {
public:
ExtensionWebSocketApiTest() = default;
~ExtensionWebSocketApiTest() override = default;
void SetUpOnMainThread() override {
ExtensionApiTest::SetUpOnMainThread();
ASSERT_TRUE(StartEmbeddedTestServer());
RegisterWebSocketHandler<SendMessagesWebSocketHandler>(
&GetWebSocketServer(), "/send-message-every-quarter-second");
ASSERT_TRUE(StartWebSocketServer());
}
// Runs a specific setup for service worker-based extensions. We open a web
// socket, set the idle timeout for the worker to one second, then wait for
// two seconds of web socket activity. If the worker is still alive and
// responds, it indicates the web socket correctly extended the worker's
// lifetime.
// `test_directory` indicates the path from which to load the extension,
// since different extensions test different kinds of web socket activity.
void RunServiceWorkerWebSocketTest(const char* test_directory);
};
void ExtensionWebSocketApiTest::RunServiceWorkerWebSocketTest(
const char* test_directory) {
ExtensionTestMessageListener socket_ready_listener("socket ready");
service_worker_test_utils::TestServiceWorkerContextObserver observer(
profile());
ResultCatcher catcher;
const Extension* extension =
LoadExtension(test_data_dir_.AppendASCII(test_directory));
ASSERT_TRUE(extension);
const int64_t version_id = observer.WaitForWorkerStarted();
// Open the web socket in the extension.
base::Value open_result = BackgroundScriptExecutor::ExecuteScript(
profile(), extension->id(), "openSocket()",
BackgroundScriptExecutor::ResultCapture::kSendScriptResult);
EXPECT_EQ("open", open_result);
// Tricky: `content::SetServiceWorkerIdleDelay() can only be called when the
// idle timer is already active; that is, when there are no pending events.
// Run until idle to let the result from the BackgroundScriptExecutor fully
// finish, and then set the idle delay to 1s.
base::RunLoop().RunUntilIdle();
// Set idle timeout to 1 second.
content::ServiceWorkerContext* context =
util::GetServiceWorkerContextForExtensionId(extension->id(), profile());
content::SetServiceWorkerIdleDelay(context, version_id, base::Seconds(1));
// Wait for two seconds of web socket activity, after which the socket will
// be closed and the extension will return. If we make it to the two seconds,
// the test succeeded (because the service worker didn't time out, indicating
// the web socket extended its lifetime).
base::Value close_result = BackgroundScriptExecutor::ExecuteScript(
profile(), extension->id(), "perform2SecondsOfWebSocketActivity()",
BackgroundScriptExecutor::ResultCapture::kSendScriptResult);
EXPECT_EQ("closed", close_result);
}
IN_PROC_BROWSER_TEST_F(ExtensionWebSocketApiTest, BasicWebSocketUsage) {
ASSERT_TRUE(RunExtensionTest("websocket")) << message_;
}
// Tests that client-side web socket activity (like sending messages) resets the
// service worker idle timer for service worker-based extensions.
// TODO(devlin): This test uses an echoing web socket, so it has both sending
// and receiving messages. It'd be better if this verified it purely via
// sending messages.
IN_PROC_BROWSER_TEST_F(ExtensionWebSocketApiTest,
SendingWebSocketMessagesResetsServiceWorkerIdleTime) {
RunServiceWorkerWebSocketTest("websocket_idle_timer_send_messages");
}
// Tests that server-initiated web socket activity (incoming messages from the
// server) resets the service worker idle timer for service worker-based
// extensions.
// Regression test for https://cbrug.com/1476142.
IN_PROC_BROWSER_TEST_F(ExtensionWebSocketApiTest,
ReceivingWebSocketMessagesResetsServiceWorkerIdleTime) {
RunServiceWorkerWebSocketTest("websocket_idle_timer_server_pings");
}
} // namespace extensions