File: extension_websocket_apitest.cc

package info (click to toggle)
chromium 140.0.7339.185-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,193,740 kB
  • sloc: cpp: 35,093,945; ansic: 7,161,670; javascript: 4,199,694; python: 1,441,797; asm: 949,904; xml: 747,515; pascal: 187,748; perl: 88,691; sh: 88,248; objc: 79,953; sql: 52,714; cs: 44,599; fortran: 24,137; makefile: 22,114; tcl: 15,277; php: 13,980; yacc: 9,000; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (160 lines) | stat: -rw-r--r-- 6,616 bytes parent folder | download | duplicates (5)
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