usb: Add DCHECKs for UsbChooserContext observers
This CL updates UsbChooserContext to require that DeviceObservers
stop observing when the UsbChooserContext is about to be destroyed.
Change-Id: I848bec9c8d5649ed17477f1e9ec767405f13ea29
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3759869
Commit-Queue: Matt Reynolds <[email protected]>
Reviewed-by: Reilly Grant <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1030860}
diff --git a/chrome/browser/usb/chrome_usb_delegate.cc b/chrome/browser/usb/chrome_usb_delegate.cc
index 7aedd97..f61b9a75 100644
--- a/chrome/browser/usb/chrome_usb_delegate.cc
+++ b/chrome/browser/usb/chrome_usb_delegate.cc
@@ -225,6 +225,11 @@
observer.OnDeviceManagerConnectionError();
}
+void ChromeUsbDelegate::OnBrowserContextShutdown() {
+ device_observation_.Reset();
+ permission_observation_.Reset();
+}
+
bool ChromeUsbDelegate::HasDevicePermission(
RenderFrameHost& frame,
const device::mojom::UsbDeviceInfo& device) {
diff --git a/chrome/browser/usb/chrome_usb_delegate.h b/chrome/browser/usb/chrome_usb_delegate.h
index 047cc23..09d3fd1 100644
--- a/chrome/browser/usb/chrome_usb_delegate.h
+++ b/chrome/browser/usb/chrome_usb_delegate.h
@@ -68,6 +68,7 @@
void OnDeviceAdded(const device::mojom::UsbDeviceInfo&) override;
void OnDeviceRemoved(const device::mojom::UsbDeviceInfo&) override;
void OnDeviceManagerConnectionError() override;
+ void OnBrowserContextShutdown() override;
private:
base::ScopedObservation<UsbChooserContext,
diff --git a/chrome/browser/usb/usb_chooser_context.cc b/chrome/browser/usb/usb_chooser_context.cc
index 72cace0..84e48e4d 100644
--- a/chrome/browser/usb/usb_chooser_context.cc
+++ b/chrome/browser/usb/usb_chooser_context.cc
@@ -237,6 +237,11 @@
UsbChooserContext::~UsbChooserContext() {
OnDeviceManagerConnectionError();
+ for (auto& observer : device_observer_list_) {
+ observer.OnBrowserContextShutdown();
+ DCHECK(!device_observer_list_.HasObserver(&observer));
+ }
+ DCHECK(permission_observer_list_.empty());
}
std::vector<std::unique_ptr<permissions::ObjectPermissionContextBase::Object>>
diff --git a/chrome/browser/usb/usb_chooser_context.h b/chrome/browser/usb/usb_chooser_context.h
index 725ce009..55fd657 100644
--- a/chrome/browser/usb/usb_chooser_context.h
+++ b/chrome/browser/usb/usb_chooser_context.h
@@ -45,6 +45,10 @@
virtual void OnDeviceAdded(const device::mojom::UsbDeviceInfo&);
virtual void OnDeviceRemoved(const device::mojom::UsbDeviceInfo&);
virtual void OnDeviceManagerConnectionError();
+
+ // Called when the BrowserContext is shutting down. Observers must remove
+ // themselves before returning.
+ virtual void OnBrowserContextShutdown() = 0;
};
static base::Value DeviceInfoToValue(
diff --git a/chrome/browser/usb/usb_chooser_context_mock_device_observer.h b/chrome/browser/usb/usb_chooser_context_mock_device_observer.h
index 6adb667..c697a6f 100644
--- a/chrome/browser/usb/usb_chooser_context_mock_device_observer.h
+++ b/chrome/browser/usb/usb_chooser_context_mock_device_observer.h
@@ -17,6 +17,7 @@
MOCK_METHOD1(OnDeviceAdded, void(const device::mojom::UsbDeviceInfo&));
MOCK_METHOD1(OnDeviceRemoved, void(const device::mojom::UsbDeviceInfo&));
MOCK_METHOD0(OnDeviceManagerConnectionError, void());
+ MOCK_METHOD0(OnBrowserContextShutdown, void());
};
#endif // CHROME_BROWSER_USB_USB_CHOOSER_CONTEXT_MOCK_DEVICE_OBSERVER_H_
diff --git a/chrome/browser/usb/usb_chooser_context_unittest.cc b/chrome/browser/usb/usb_chooser_context_unittest.cc
index 92fbdd2..ce3bf946a3 100644
--- a/chrome/browser/usb/usb_chooser_context_unittest.cc
+++ b/chrome/browser/usb/usb_chooser_context_unittest.cc
@@ -5,11 +5,13 @@
#include <vector>
#include "base/callback_helpers.h"
+#include "base/containers/flat_map.h"
#include "base/json/json_reader.h"
#include "base/no_destructor.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
+#include "base/test/test_future.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
@@ -30,7 +32,8 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-using device::mojom::UsbDeviceInfoPtr;
+using ::base::test::TestFuture;
+using ::device::mojom::UsbDeviceInfoPtr;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::NiceMock;
@@ -48,15 +51,19 @@
public:
UsbChooserContextTest() {}
~UsbChooserContextTest() override {
- // When UsbChooserContext is destroyed, OnDeviceManagerConnectionError
- // should be called on the observers and OnPermissionRevoked will be called
- // for any ephemeral device permissions that are active.
- EXPECT_CALL(mock_device_observer_, OnDeviceManagerConnectionError())
- .Times(AnyNumber());
- EXPECT_CALL(mock_permission_observer_, OnPermissionRevoked(_))
- .Times(AnyNumber());
- EXPECT_CALL(mock_permission_observer_, OnObjectPermissionChanged(_, _))
- .Times(AnyNumber());
+ // When UsbChooserContext is destroyed, OnDeviceManagerConnectionError will
+ // be called for each device observer.
+ for (const auto& entry : mock_device_observers_) {
+ EXPECT_CALL(*entry.second, OnDeviceManagerConnectionError)
+ .Times(AnyNumber());
+ }
+
+ // OnPermissionRevoked and OnObjectPermissionChanged will be called for any
+ // ephemeral device permissions that are active.
+ for (const auto& entry : mock_permission_observers_) {
+ EXPECT_CALL(*entry.second, OnPermissionRevoked).Times(AnyNumber());
+ EXPECT_CALL(*entry.second, OnObjectPermissionChanged).Times(AnyNumber());
+ }
}
protected:
@@ -71,21 +78,42 @@
// Call GetDevices once to make sure the connection with DeviceManager has
// been set up, so that it can be notified when device is removed.
- chooser_context->GetDevices(base::DoNothing());
- base::RunLoop().RunUntilIdle();
+ TestFuture<std::vector<device::mojom::UsbDeviceInfoPtr>> devices_future;
+ chooser_context->GetDevices(devices_future.GetCallback());
+ EXPECT_TRUE(devices_future.Wait());
// Add observers
+ EXPECT_FALSE(base::Contains(mock_permission_observers_, profile));
+ EXPECT_FALSE(base::Contains(mock_device_observers_, profile));
+ mock_permission_observers_.emplace(
+ profile,
+ std::make_unique<NiceMock<permissions::MockPermissionObserver>>());
+ mock_device_observers_.emplace(profile,
+ std::make_unique<MockDeviceObserver>());
+ NiceMock<permissions::MockPermissionObserver>* permission_observer =
+ mock_permission_observers_[profile].get();
chooser_context->permissions::ObjectPermissionContextBase::AddObserver(
- &mock_permission_observer_);
- chooser_context->AddObserver(&mock_device_observer_);
+ permission_observer);
+ MockDeviceObserver* device_observer = mock_device_observers_[profile].get();
+ chooser_context->AddObserver(device_observer);
+ EXPECT_CALL(*device_observer, OnBrowserContextShutdown)
+ .WillOnce([chooser_context, permission_observer, device_observer]() {
+ chooser_context
+ ->permissions::ObjectPermissionContextBase::RemoveObserver(
+ permission_observer);
+ chooser_context->RemoveObserver(device_observer);
+ });
return chooser_context;
}
device::FakeUsbDeviceManager device_manager_;
// Mock observers
- NiceMock<permissions::MockPermissionObserver> mock_permission_observer_;
- MockDeviceObserver mock_device_observer_;
+ base::flat_map<Profile*,
+ std::unique_ptr<NiceMock<permissions::MockPermissionObserver>>>
+ mock_permission_observers_;
+ base::flat_map<Profile*, std::unique_ptr<MockDeviceObserver>>
+ mock_device_observers_;
private:
content::BrowserTaskEnvironment task_environment_;
@@ -108,7 +136,7 @@
object.SetStringKey(kSerialNumberKey, "123ABC");
EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
@@ -127,11 +155,12 @@
EXPECT_EQ(object, all_origin_objects[0]->value);
EXPECT_FALSE(all_origin_objects[0]->incognito);
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
- EXPECT_CALL(mock_permission_observer_, OnPermissionRevoked(origin));
+ EXPECT_CALL(*mock_permission_observers_[profile()],
+ OnPermissionRevoked(origin));
store->RevokeObjectPermission(origin, objects[0]->value);
EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
@@ -160,7 +189,7 @@
object.SetIntKey(kProductIdKey, device_info->product_id);
EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
@@ -181,11 +210,12 @@
EXPECT_EQ(object, all_origin_objects[0]->value);
EXPECT_FALSE(all_origin_objects[0]->incognito);
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
- EXPECT_CALL(mock_permission_observer_, OnPermissionRevoked(origin));
+ EXPECT_CALL(*mock_permission_observers_[profile()],
+ OnPermissionRevoked(origin));
store->RevokeObjectPermission(origin, objects[0]->value);
EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
@@ -205,7 +235,7 @@
UsbChooserContext* store = GetChooserContext(profile());
EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
@@ -221,7 +251,7 @@
store->GetAllGrantedObjects();
EXPECT_EQ(1u, all_origin_objects.size());
- EXPECT_CALL(mock_device_observer_, OnDeviceRemoved(_));
+ EXPECT_CALL(*mock_device_observers_[profile()], OnDeviceRemoved(_));
device_manager_.RemoveDevice(device_info->guid);
base::RunLoop().RunUntilIdle();
@@ -250,7 +280,7 @@
UsbChooserContext* store = GetChooserContext(profile());
EXPECT_FALSE(store->HasDevicePermission(origin, *device_info));
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
@@ -266,11 +296,11 @@
store->GetAllGrantedObjects();
EXPECT_EQ(1u, all_origin_objects.size());
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
- EXPECT_CALL(mock_device_observer_, OnDeviceRemoved(_));
+ EXPECT_CALL(*mock_device_observers_[profile()], OnDeviceRemoved(_));
device_manager_.RemoveDevice(device_info->guid);
base::RunLoop().RunUntilIdle();
@@ -298,10 +328,11 @@
UsbDeviceInfoPtr device_info_2 =
device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "");
UsbChooserContext* store = GetChooserContext(profile());
- UsbChooserContext* incognito_store = GetChooserContext(
- profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true));
+ auto* otr_profile =
+ profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true);
+ UsbChooserContext* incognito_store = GetChooserContext(otr_profile);
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
@@ -310,7 +341,7 @@
EXPECT_TRUE(store->HasDevicePermission(origin, *device_info_1));
EXPECT_FALSE(incognito_store->HasDevicePermission(origin, *device_info_1));
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[otr_profile],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
@@ -356,7 +387,7 @@
kFooUrl, kFooUrl, ContentSettingsType::USB_GUARD, CONTENT_SETTING_BLOCK);
auto* store = GetChooserContext(profile());
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA))
@@ -975,7 +1006,7 @@
auto* store = GetChooserContext(profile());
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA))
@@ -1046,7 +1077,7 @@
UsbDeviceInfoPtr persistent_device_info = device_manager_.CreateAndAddDevice(
6353, 5678, "Specific", "Product", "123ABC");
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
@@ -1103,7 +1134,7 @@
profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls,
*base::JSONReader::ReadDeprecated(kPolicySetting));
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
@@ -1156,7 +1187,7 @@
profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls,
*base::JSONReader::ReadDeprecated(kPolicySetting));
- EXPECT_CALL(mock_permission_observer_,
+ EXPECT_CALL(*mock_permission_observers_[profile()],
OnObjectPermissionChanged(
absl::make_optional(ContentSettingsType::USB_GUARD),
ContentSettingsType::USB_CHOOSER_DATA));
diff --git a/chrome/browser/usb/usb_chooser_controller.cc b/chrome/browser/usb/usb_chooser_controller.cc
index 00578e1..a735644 100644
--- a/chrome/browser/usb/usb_chooser_controller.cc
+++ b/chrome/browser/usb/usb_chooser_controller.cc
@@ -234,7 +234,7 @@
}
}
-void UsbChooserController::OnDeviceManagerConnectionError() {
+void UsbChooserController::OnBrowserContextShutdown() {
observation_.Reset();
}
diff --git a/chrome/browser/usb/usb_chooser_controller.h b/chrome/browser/usb/usb_chooser_controller.h
index 515933e5..e6d9631 100644
--- a/chrome/browser/usb/usb_chooser_controller.h
+++ b/chrome/browser/usb/usb_chooser_controller.h
@@ -55,7 +55,7 @@
void OnDeviceAdded(const device::mojom::UsbDeviceInfo& device_info) override;
void OnDeviceRemoved(
const device::mojom::UsbDeviceInfo& device_info) override;
- void OnDeviceManagerConnectionError() override;
+ void OnBrowserContextShutdown() override;
private:
void GotUsbDeviceList(std::vector<device::mojom::UsbDeviceInfoPtr> devices);