1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
// 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 "chrome/test/base/ui_test_utils.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(
browser()->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(),
browser()->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
|