blob: 1a82fb459c723a6db41ff3b90eb10c80c551ba7b [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "base/strings/stringprintf.h"
#include "chrome/browser/bluetooth/chrome_bluetooth_delegate.h"
#include "chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
namespace {
// Espruino devices like Pixl.js present a Nordic UART service that
// provides serial port-like access to the Espruino REPL.
// https://www.espruino.com/BLE+UART
constexpr std::string_view kNordicUartServiceUuid =
"6e400001-b5a3-f393-e0a9-e50e24dcca9e";
// The chooser that will select the first device shown in the chooser.
class BluetoothRealTargetTestChooser : public content::BluetoothChooser {
public:
explicit BluetoothRealTargetTestChooser(
content::BluetoothChooser::EventHandler event_handler)
: event_handler_(std::move(event_handler)) {}
BluetoothRealTargetTestChooser(const BluetoothRealTargetTestChooser&) =
delete;
BluetoothRealTargetTestChooser& operator=(
const BluetoothRealTargetTestChooser&) = delete;
~BluetoothRealTargetTestChooser() override = default;
// content::BluetoothChooser implementation:
void AddOrUpdateDevice(const std::string& device_id,
bool should_update_name,
const std::u16string& device_name,
bool is_gatt_connected,
bool is_paired,
int signal_strength_level) override {
event_handler_.Run(content::BluetoothChooserEvent::SELECTED, device_id);
}
private:
content::BluetoothChooser::EventHandler event_handler_;
};
class BluetoothRealTargetTestDelegate : public ChromeBluetoothDelegate {
public:
BluetoothRealTargetTestDelegate()
: ChromeBluetoothDelegate(
std::make_unique<ChromeBluetoothDelegateImplClient>()) {}
~BluetoothRealTargetTestDelegate() override = default;
std::unique_ptr<content::BluetoothChooser> RunBluetoothChooser(
content::RenderFrameHost* frame,
const content::BluetoothChooser::EventHandler& event_handler) override {
return std::make_unique<BluetoothRealTargetTestChooser>(event_handler);
}
};
class TestBluetoothContentBrowserClient : public content::ContentBrowserClient {
public:
TestBluetoothContentBrowserClient()
: delegate_(std::make_unique<BluetoothRealTargetTestDelegate>()) {}
~TestBluetoothContentBrowserClient() override = default;
content::BluetoothDelegate* GetBluetoothDelegate() override {
return delegate_.get();
}
void SetAsBrowserClient() {
original_content_browser_client_ =
content::SetBrowserClientForTesting(this);
}
void UnsetAsBrowserClient() {
content::SetBrowserClientForTesting(original_content_browser_client_);
delegate_.reset();
}
private:
std::unique_ptr<BluetoothRealTargetTestDelegate> delegate_;
raw_ptr<content::ContentBrowserClient> original_content_browser_client_ =
nullptr;
};
class BluetoothRealTargetTest : public InProcessBrowserTest {
public:
void SetUpOnMainThread() override {
embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data");
ASSERT_TRUE(embedded_test_server()->Start());
test_content_browser_client_.SetAsBrowserClient();
GURL url = embedded_test_server()->GetURL("localhost", "/simple_page.html");
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
content::RenderFrameHost* render_frame_host = browser()
->tab_strip_model()
->GetActiveWebContents()
->GetPrimaryMainFrame();
EXPECT_EQ(url.DeprecatedGetOriginAsURL(),
render_frame_host->GetLastCommittedOrigin().GetURL());
}
void TearDownOnMainThread() override {
test_content_browser_client_.UnsetAsBrowserClient();
}
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
#if BUILDFLAG(IS_LINUX)
// TODO(41229108): Remove this switch once Web Bluetooth is
// supported on Linux.
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
#endif
}
private:
TestBluetoothContentBrowserClient test_content_browser_client_;
};
IN_PROC_BROWSER_TEST_F(BluetoothRealTargetTest, ConnectAndDisconnectDevice) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
auto test_script = base::StringPrintf(
R"((async () => {
let device = await navigator.bluetooth.requestDevice({
filters: [
{namePrefix: "Pixl.js"},
],
optionalServices: ["%s"]
});
let server = await device.gatt.connect();
await server.getPrimaryServices();
let result = server.connected;
await server.disconnect();
return result;
})())",
kNordicUartServiceUuid);
EXPECT_EQ(true, EvalJs(web_contents, test_script));
}
} // namespace