910e62b5创建于 1月15日历史提交
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/bluetooth/frame_connected_bluetooth_devices.h"

#include <tuple>

#include "base/memory/raw_ptr.h"
#include "content/browser/bluetooth/web_bluetooth_service_impl.h"
#include "content/test/test_render_view_host.h"
#include "content/test/test_web_contents.h"
#include "device/bluetooth/bluetooth_gatt_connection.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_device.h"
#include "device/bluetooth/test/mock_bluetooth_gatt_connection.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom.h"

namespace content {

typedef testing::NiceMock<device::MockBluetoothAdapter>
    NiceMockBluetoothAdapter;
typedef testing::NiceMock<device::MockBluetoothDevice> NiceMockBluetoothDevice;
typedef testing::NiceMock<device::MockBluetoothGattConnection>
    NiceMockBluetoothGattConnection;

using testing::_;
using testing::Return;
using testing::StrEq;

namespace {

const blink::WebBluetoothDeviceId kDeviceId0("000000000000000000000A==");
constexpr char kDeviceAddress0[] = "0";
constexpr char kDeviceName0[] = "Device0";

const blink::WebBluetoothDeviceId kDeviceId1("111111111111111111111A==");
constexpr char kDeviceAddress1[] = "1";
constexpr char kDeviceName1[] = "Device1";

mojo::AssociatedRemote<blink::mojom::WebBluetoothServerClient>
CreateServerClient() {
  mojo::AssociatedRemote<blink::mojom::WebBluetoothServerClient> client;
  std::ignore = client.BindNewEndpointAndPassDedicatedReceiver();
  return client;
}

}  // namespace

class FrameConnectedBluetoothDevicesTest
    : public RenderViewHostImplTestHarness {
 public:
  FrameConnectedBluetoothDevicesTest()
      : adapter_(new NiceMockBluetoothAdapter()),
        device0_(adapter_.get(),
                 0 /* class */,
                 kDeviceName0,
                 kDeviceAddress0,
                 false /* paired */,
                 false /* connected */),
        device1_(adapter_.get(),
                 0 /* class */,
                 kDeviceName1,
                 kDeviceAddress1,
                 false /* paired */,
                 false /* connected */) {
    ON_CALL(*adapter_, GetDevice(_)).WillByDefault(Return(nullptr));
    ON_CALL(*adapter_, GetDevice(StrEq(kDeviceAddress0)))
        .WillByDefault(Return(&device0_));
    ON_CALL(*adapter_, GetDevice(StrEq(kDeviceAddress1)))
        .WillByDefault(Return(&device1_));
  }

  ~FrameConnectedBluetoothDevicesTest() override {}

  void SetUp() override {
    RenderViewHostImplTestHarness::SetUp();

    // Create subframe to simulate two maps on the same WebContents.
    contents()->GetPrimaryMainFrame()->InitializeRenderFrameIfNeeded();
    contents()->GetPrimaryMainFrame()->SetLastCommittedOriginForTesting(
        url::Origin::Create(GURL("https://blah.com")));
    TestRenderFrameHost* subframe =
        contents()->GetPrimaryMainFrame()->AppendChild("bluetooth_frame");
    subframe->InitializeRenderFrameIfNeeded();

    // Simulate two frames each connected to a bluetooth service.
    service_ptr0_ = WebBluetoothServiceImpl::CreateForTesting(
        contents()->GetPrimaryMainFrame(),
        service0_.BindNewPipeAndPassReceiver());
    map_ptr0_ = service_ptr0_->connected_devices_.get();

    service_ptr1_ = WebBluetoothServiceImpl::CreateForTesting(
        subframe, service1_.BindNewPipeAndPassReceiver());
    map_ptr1_ = service_ptr1_->connected_devices_.get();
  }

  void TearDown() override {
    // This normally happens as part of fixture destruction, but the test
    // fixture has pointers to several `DocumentUserData` that will dangle if
    // not explicitly torn down here.
    DeleteContents();
    RenderViewHostImplTestHarness::TearDown();
  }

  std::unique_ptr<NiceMockBluetoothGattConnection> GetConnection(
      const std::string& address) {
    return std::make_unique<NiceMockBluetoothGattConnection>(adapter_.get(),
                                                             address);
  }

  void ResetService0() {
    // This is a hack; destruction is normally implicitly triggered by
    // navigation or destruction of the frame itself, and not explicitly like
    // this test does.
    map_ptr0_ = nullptr;
    WebBluetoothServiceImpl::DeleteForCurrentDocument(
        &service_ptr0_.ExtractAsDangling()->render_frame_host());
  }

  void ResetService1() {
    // This is a hack; destruction is normally implicitly triggered by
    // navigation or destruction of the frame itself, and not explicitly like
    // this test does.
    map_ptr1_ = nullptr;
    WebBluetoothServiceImpl::DeleteForCurrentDocument(
        &service_ptr1_.ExtractAsDangling()->render_frame_host());
  }

  void DeleteContents() {
    // WebBluetoothServiceImpls are DocumentUserDatas, so null out these fields
    // before destroying the WebContents to avoid dangling pointers.
    service_ptr0_ = nullptr;
    map_ptr0_ = nullptr;
    service_ptr1_ = nullptr;
    map_ptr1_ = nullptr;
    RenderViewHostTestHarness::DeleteContents();
  }

 protected:
  raw_ptr<FrameConnectedBluetoothDevices> map_ptr0_ = nullptr;
  raw_ptr<FrameConnectedBluetoothDevices> map_ptr1_ = nullptr;

 private:
  mojo::Remote<blink::mojom::WebBluetoothService> service0_;
  raw_ptr<WebBluetoothServiceImpl> service_ptr0_ = nullptr;

  mojo::Remote<blink::mojom::WebBluetoothService> service1_;
  raw_ptr<WebBluetoothServiceImpl> service_ptr1_ = nullptr;

  scoped_refptr<NiceMockBluetoothAdapter> adapter_;
  NiceMockBluetoothDevice device0_;
  NiceMockBluetoothDevice device1_;
};

TEST_F(FrameConnectedBluetoothDevicesTest, Insert_Once) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
}

TEST_F(FrameConnectedBluetoothDevicesTest, Insert_Twice) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
}

TEST_F(FrameConnectedBluetoothDevicesTest, Insert_TwoDevices) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr0_->Insert(kDeviceId1, GetConnection(kDeviceAddress1),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId1));
}

TEST_F(FrameConnectedBluetoothDevicesTest, Insert_TwoMaps) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr1_->Insert(kDeviceId1, GetConnection(kDeviceAddress1),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
  EXPECT_TRUE(map_ptr1_->IsConnectedToDeviceWithId(kDeviceId1));
}

TEST_F(FrameConnectedBluetoothDevicesTest,
       CloseConnectionId_OneDevice_AddOnce_RemoveOnce) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));

  map_ptr0_->CloseConnectionToDeviceWithId(kDeviceId0);

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
}

TEST_F(FrameConnectedBluetoothDevicesTest,
       CloseConnectionId_OneDevice_AddOnce_RemoveTwice) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));

  map_ptr0_->CloseConnectionToDeviceWithId(kDeviceId0);
  map_ptr0_->CloseConnectionToDeviceWithId(kDeviceId0);

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
}

TEST_F(FrameConnectedBluetoothDevicesTest,
       CloseConnectionId_OneDevice_AddTwice_RemoveOnce) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));

  map_ptr0_->CloseConnectionToDeviceWithId(kDeviceId0);

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
}

TEST_F(FrameConnectedBluetoothDevicesTest,
       CloseConnectionId_OneDevice_AddTwice_RemoveTwice) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));

  map_ptr0_->CloseConnectionToDeviceWithId(kDeviceId0);
  map_ptr0_->CloseConnectionToDeviceWithId(kDeviceId0);

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
}

TEST_F(FrameConnectedBluetoothDevicesTest, CloseConnectionId_TwoDevices) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr0_->Insert(kDeviceId1, GetConnection(kDeviceAddress1),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId1));

  map_ptr0_->CloseConnectionToDeviceWithId(kDeviceId0);

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));

  map_ptr0_->CloseConnectionToDeviceWithId(kDeviceId1);

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId1));
}

TEST_F(FrameConnectedBluetoothDevicesTest, CloseConnectionId_TwoMaps) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr1_->Insert(kDeviceId1, GetConnection(kDeviceAddress1),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
  EXPECT_TRUE(map_ptr1_->IsConnectedToDeviceWithId(kDeviceId1));

  map_ptr0_->CloseConnectionToDeviceWithId(kDeviceId0);

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));

  map_ptr1_->CloseConnectionToDeviceWithId(kDeviceId1);

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr1_->IsConnectedToDeviceWithId(kDeviceId1));
}

TEST_F(FrameConnectedBluetoothDevicesTest,
       CloseConnectionAddress_OneDevice_AddOnce_RemoveOnce) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));

  EXPECT_EQ(
      map_ptr0_->CloseConnectionToDeviceWithAddress(kDeviceAddress0).value(),
      kDeviceId0);

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
}

TEST_F(FrameConnectedBluetoothDevicesTest,
       CloseConnectionAddress_OneDevice_AddOnce_RemoveTwice) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));

  EXPECT_EQ(
      map_ptr0_->CloseConnectionToDeviceWithAddress(kDeviceAddress0).value(),
      kDeviceId0);
  EXPECT_FALSE(map_ptr0_->CloseConnectionToDeviceWithAddress(kDeviceAddress0));

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
}

TEST_F(FrameConnectedBluetoothDevicesTest,
       CloseConnectionAddress_OneDevice_AddTwice_RemoveOnce) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));

  EXPECT_EQ(
      map_ptr0_->CloseConnectionToDeviceWithAddress(kDeviceAddress0).value(),
      kDeviceId0);

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
}

TEST_F(FrameConnectedBluetoothDevicesTest,
       CloseConnectionAddress_OneDevice_AddTwice_RemoveTwice) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));

  EXPECT_EQ(
      map_ptr0_->CloseConnectionToDeviceWithAddress(kDeviceAddress0).value(),
      kDeviceId0);
  EXPECT_FALSE(map_ptr0_->CloseConnectionToDeviceWithAddress(kDeviceAddress0));

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
}

TEST_F(FrameConnectedBluetoothDevicesTest, CloseConnectionAddress_TwoDevices) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr0_->Insert(kDeviceId1, GetConnection(kDeviceAddress1),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId1));

  EXPECT_EQ(
      map_ptr0_->CloseConnectionToDeviceWithAddress(kDeviceAddress0).value(),
      kDeviceId0);

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId1));

  EXPECT_EQ(
      map_ptr0_->CloseConnectionToDeviceWithAddress(kDeviceAddress1).value(),
      kDeviceId1);

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId1));
}

TEST_F(FrameConnectedBluetoothDevicesTest, CloseConnectionAddress_TwoMaps) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr1_->Insert(kDeviceId1, GetConnection(kDeviceAddress1),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_TRUE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
  EXPECT_TRUE(map_ptr1_->IsConnectedToDeviceWithId(kDeviceId1));

  EXPECT_EQ(
      map_ptr0_->CloseConnectionToDeviceWithAddress(kDeviceAddress0).value(),
      kDeviceId0);

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr0_->IsConnectedToDeviceWithId(kDeviceId0));
  EXPECT_TRUE(map_ptr1_->IsConnectedToDeviceWithId(kDeviceId1));

  EXPECT_EQ(
      map_ptr1_->CloseConnectionToDeviceWithAddress(kDeviceAddress1).value(),
      kDeviceId1);

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
  EXPECT_FALSE(map_ptr1_->IsConnectedToDeviceWithId(kDeviceId1));
}

TEST_F(FrameConnectedBluetoothDevicesTest, Destruction_MultipleDevices) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr0_->Insert(kDeviceId1, GetConnection(kDeviceAddress1),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));

  ResetService0();

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
}

TEST_F(FrameConnectedBluetoothDevicesTest, Destruction_MultipleMaps) {
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr0_->Insert(kDeviceId1, GetConnection(kDeviceAddress1),
                    CreateServerClient());

  map_ptr1_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());
  map_ptr1_->Insert(kDeviceId1, GetConnection(kDeviceAddress1),
                    CreateServerClient());

  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));

  ResetService0();

  // WebContents should still be connected because of map_ptr1_.
  EXPECT_TRUE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));

  ResetService1();

  EXPECT_FALSE(contents()->IsCapabilityActive(
      WebContentsCapabilityType::kBluetoothConnected));
}

TEST_F(FrameConnectedBluetoothDevicesTest,
       DestroyedByWebContentsImplDestruction) {
  // Tests that we don't crash when FrameConnectedBluetoothDevices contains
  // at least one device, and it is destroyed while WebContentsImpl is being
  // destroyed.
  map_ptr0_->Insert(kDeviceId0, GetConnection(kDeviceAddress0),
                    CreateServerClient());

  DeleteContents();
}

}  // namespace content