| // Copyright 2021 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/media/capture_handle_manager.h" | 
 |  | 
 | #include "content/test/test_render_view_host.h" | 
 | #include "content/test/test_web_contents.h" | 
 | #include "testing/gmock/include/gmock/gmock.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | namespace { | 
 |  | 
 | using Callback = CaptureHandleManager::DeviceCaptureHandleChangeCallback; | 
 | using blink::MediaStreamDevice; | 
 | using testing::_; | 
 |  | 
 | const std::string kLabel = "noladaleybeceipretsamrehtonatey"; | 
 |  | 
 | MATCHER(IsNullCaptureHandle, "") { | 
 |   static_assert( | 
 |       std::is_same<decltype(arg), const media::mojom::CaptureHandlePtr&>::value, | 
 |       "Matcher applied to incorrect type."); | 
 |  | 
 |   return !arg; | 
 | } | 
 |  | 
 | MATCHER_P2(IsCaptureHandle, expected_origin, expected_handle, "") { | 
 |   if (!arg) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (arg->origin.opaque() != expected_origin.opaque()) { | 
 |     return false;  // One is empty, the other is non-empty. | 
 |   } | 
 |  | 
 |   // Either both are opaque or neither is. We only compare non-opaque origins. | 
 |   if (!expected_origin.opaque()) { | 
 |     if (arg->origin != expected_origin) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return arg->capture_handle == expected_handle; | 
 | } | 
 |  | 
 | media::mojom::CaptureHandlePtr MakeCaptureHandle(url::Origin origin, | 
 |                                                  const std::u16string& handle) { | 
 |   auto capture_handle = media::mojom::CaptureHandle::New(); | 
 |   capture_handle->origin = origin; | 
 |   capture_handle->capture_handle = handle; | 
 |   return capture_handle; | 
 | } | 
 |  | 
 | media::mojom::CaptureHandlePtr MakeCaptureHandle(const std::u16string& handle) { | 
 |   return MakeCaptureHandle(url::Origin(), handle); | 
 | } | 
 |  | 
 | blink::mojom::CaptureHandleConfigPtr MakePermissiveConfigWithHandle( | 
 |     const std::u16string& handle = u"") { | 
 |   auto ptr = blink::mojom::CaptureHandleConfig::New(); | 
 |   ptr->expose_origin = true; | 
 |   ptr->capture_handle = handle; | 
 |   ptr->all_origins_permitted = true; | 
 |   return ptr; | 
 | } | 
 |  | 
 | blink::mojom::CaptureHandleConfigPtr MakeRestrictiveConfigWithHandle( | 
 |     const std::vector<url::Origin> permitted_origins, | 
 |     const std::u16string& handle = u"") { | 
 |   auto ptr = blink::mojom::CaptureHandleConfig::New(); | 
 |   ptr->expose_origin = true; | 
 |   ptr->capture_handle = handle; | 
 |   ptr->all_origins_permitted = false; | 
 |   ptr->permitted_origins = permitted_origins; | 
 |   return ptr; | 
 | } | 
 |  | 
 | class CallbackHelper { | 
 |  public: | 
 |   MOCK_METHOD3(Method, | 
 |                void(const std::string& label, | 
 |                     blink::mojom::MediaStreamType type, | 
 |                     media::mojom::CaptureHandlePtr capture_handle)); | 
 |  | 
 |   Callback AsCallback() { | 
 |     return base::BindRepeating(&CallbackHelper::Method, base::Unretained(this)); | 
 |   } | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | class CaptureHandleManagerTest : public RenderViewHostImplTestHarness { | 
 |  public: | 
 |   std::unique_ptr<TestWebContents> MakeTestWebContents() { | 
 |     scoped_refptr<SiteInstance> instance = | 
 |         SiteInstance::Create(GetBrowserContext()); | 
 |     instance->GetProcess()->Init(); | 
 |  | 
 |     return TestWebContents::Create(GetBrowserContext(), std::move(instance)); | 
 |   } | 
 |  | 
 |   CallbackHelper& MakeCallbackHelper() { | 
 |     CHECK_LT(callback_helper_count_, kMaxCallbackHelpers); | 
 |     return callback_helpers_[++callback_helper_count_]; | 
 |   } | 
 |  | 
 |   MediaStreamDevice& MakeDevice( | 
 |       RenderFrameHost* frame, | 
 |       media::mojom::CaptureHandlePtr capture_handle = nullptr) { | 
 |     CHECK_LT(device_count_, kMaxDevices); | 
 |     auto& device = devices_[++device_count_]; | 
 |  | 
 |     const WebContentsMediaCaptureId id(frame->GetProcess()->GetID(), | 
 |                                        frame->GetRoutingID()); | 
 |     device.id = id.ToString(); | 
 |     device.display_media_info = media::mojom::DisplayMediaInformation::New(); | 
 |     device.display_media_info->capture_handle = std::move(capture_handle); | 
 |  | 
 |     return device; | 
 |   } | 
 |  | 
 |   MediaStreamDevice& MakeDevice( | 
 |       std::unique_ptr<TestWebContents>& web_contents, | 
 |       media::mojom::CaptureHandlePtr capture_handle = nullptr) { | 
 |     return MakeDevice(web_contents->GetPrimaryMainFrame(), | 
 |                       std::move(capture_handle)); | 
 |   } | 
 |  | 
 |  protected: | 
 |   CaptureHandleManager manager_; | 
 |  | 
 |  private: | 
 |   static constexpr size_t kMaxCallbackHelpers = 10; | 
 |   CallbackHelper callback_helpers_[kMaxCallbackHelpers]; | 
 |   size_t callback_helper_count_ = 0; | 
 |  | 
 |   static constexpr size_t kMaxDevices = 10; | 
 |   MediaStreamDevice devices_[kMaxDevices]; | 
 |   size_t device_count_ = 0; | 
 | }; | 
 |  | 
 | TEST_F(CaptureHandleManagerTest, | 
 |        OnTabCaptureStartedProducesNoCallbackIfDeviceHasNoCaptureHandle) { | 
 |   auto captured = MakeTestWebContents(); | 
 |   auto capturer = MakeTestWebContents(); | 
 |  | 
 |   auto& callback_helper = MakeCallbackHelper(); | 
 |   EXPECT_CALL(callback_helper, Method(_, _, _)).Times(0); | 
 |   manager_.OnTabCaptureStarted(kLabel, MakeDevice(captured), | 
 |                                capturer->GetPrimaryMainFrame()->GetGlobalId(), | 
 |                                callback_helper.AsCallback()); | 
 | } | 
 |  | 
 | TEST_F(CaptureHandleManagerTest, | 
 |        OnTabCaptureStartedProducesCallbackIfDeviceHasOldCaptureHandle) { | 
 |   auto captured = MakeTestWebContents(); | 
 |   auto capturer = MakeTestWebContents(); | 
 |  | 
 |   auto& captured_device = MakeDevice(captured, MakeCaptureHandle(u"same")); | 
 |  | 
 |   captured->SetCaptureHandleConfig(MakePermissiveConfigWithHandle(u"same")); | 
 |  | 
 |   auto& callback_helper = MakeCallbackHelper(); | 
 |   EXPECT_CALL(callback_helper, Method(_, _, _)).Times(0); | 
 |   manager_.OnTabCaptureStarted(kLabel, captured_device, | 
 |                                capturer->GetPrimaryMainFrame()->GetGlobalId(), | 
 |                                callback_helper.AsCallback()); | 
 | } | 
 |  | 
 | TEST_F(CaptureHandleManagerTest, | 
 |        OnTabCaptureStartedProducesCallbackIfDeviceHasNewCaptureHandle) { | 
 |   auto captured = MakeTestWebContents(); | 
 |   auto capturer = MakeTestWebContents(); | 
 |  | 
 |   auto& captured_device = MakeDevice(captured, MakeCaptureHandle(u"old")); | 
 |  | 
 |   captured->SetCaptureHandleConfig(MakePermissiveConfigWithHandle(u"new")); | 
 |  | 
 |   auto& callback_helper = MakeCallbackHelper(); | 
 |   EXPECT_CALL(callback_helper, Method(kLabel, captured_device.type, | 
 |                                       IsCaptureHandle(url::Origin(), u"new"))) | 
 |       .Times(1); | 
 |   manager_.OnTabCaptureStarted(kLabel, captured_device, | 
 |                                capturer->GetPrimaryMainFrame()->GetGlobalId(), | 
 |                                callback_helper.AsCallback()); | 
 | } | 
 |  | 
 | TEST_F(CaptureHandleManagerTest, CallbackInvokedWhenCaptureHandleChanges) { | 
 |   auto captured = MakeTestWebContents(); | 
 |   auto capturer = MakeTestWebContents(); | 
 |  | 
 |   auto& captured_device = MakeDevice(captured, MakeCaptureHandle(u"before")); | 
 |   captured->SetCaptureHandleConfig(MakePermissiveConfigWithHandle(u"before")); | 
 |  | 
 |   auto& callback_helper = MakeCallbackHelper(); | 
 |   manager_.OnTabCaptureStarted(kLabel, captured_device, | 
 |                                capturer->GetPrimaryMainFrame()->GetGlobalId(), | 
 |                                callback_helper.AsCallback()); | 
 |  | 
 |   EXPECT_CALL(callback_helper, Method(kLabel, captured_device.type, | 
 |                                       IsCaptureHandle(url::Origin(), u"after"))) | 
 |       .Times(1); | 
 |   captured->SetCaptureHandleConfig(MakePermissiveConfigWithHandle(u"after")); | 
 | } | 
 |  | 
 | TEST_F(CaptureHandleManagerTest, CaptureHandleResetByNavigation) { | 
 |   auto captured = MakeTestWebContents(); | 
 |   auto capturer = MakeTestWebContents(); | 
 |  | 
 |   const GURL kGurl1("https://origin1.com"); | 
 |   const GURL kGurl2("https://origin2.com"); | 
 |   const url::Origin kOrigin1 = url::Origin::Create(kGurl1); | 
 |  | 
 |   captured->NavigateAndCommit(kGurl1); | 
 |   auto& captured_device = | 
 |       MakeDevice(captured, MakeCaptureHandle(kOrigin1, u"handle")); | 
 |   captured->SetCaptureHandleConfig(MakePermissiveConfigWithHandle(u"handle")); | 
 |  | 
 |   auto& callback_helper = MakeCallbackHelper(); | 
 |   manager_.OnTabCaptureStarted(kLabel, captured_device, | 
 |                                capturer->GetPrimaryMainFrame()->GetGlobalId(), | 
 |                                callback_helper.AsCallback()); | 
 |  | 
 |   EXPECT_CALL(callback_helper, | 
 |               Method(kLabel, captured_device.type, IsNullCaptureHandle())) | 
 |       .Times(1); | 
 |   captured->NavigateAndCommit(kGurl2); | 
 | } | 
 |  | 
 | TEST_F(CaptureHandleManagerTest, | 
 |        CallbackNotInvokedWhenConfigDisallowsCapturer) { | 
 |   auto captured = MakeTestWebContents(); | 
 |   auto capturer = MakeTestWebContents(); | 
 |  | 
 |   const GURL kCapturedUrl("https://captured.com"); | 
 |   captured->NavigateAndCommit(kCapturedUrl); | 
 |  | 
 |   const GURL kDisallowedUrl("https://disallowed.com"); | 
 |   capturer->NavigateAndCommit(kDisallowedUrl); | 
 |  | 
 |   const GURL kAllowedUrl("https://allowed.com"); | 
 |   const url::Origin kAllowedOrigin = url::Origin::Create(kAllowedUrl); | 
 |  | 
 |   auto& captured_device = MakeDevice(captured); | 
 |  | 
 |   auto& callback_helper = MakeCallbackHelper(); | 
 |   manager_.OnTabCaptureStarted(kLabel, captured_device, | 
 |                                capturer->GetPrimaryMainFrame()->GetGlobalId(), | 
 |                                callback_helper.AsCallback()); | 
 |  | 
 |   EXPECT_CALL(callback_helper, Method(_, _, _)).Times(0); | 
 |   captured->SetCaptureHandleConfig( | 
 |       MakeRestrictiveConfigWithHandle({kAllowedOrigin}, u"handle")); | 
 | } | 
 |  | 
 | TEST_F(CaptureHandleManagerTest, CallbackInvokedWhenConfigAllowsCapturer) { | 
 |   auto captured = MakeTestWebContents(); | 
 |   auto capturer = MakeTestWebContents(); | 
 |  | 
 |   const GURL kCapturedUrl("https://captured.com"); | 
 |   const url::Origin kCapturedOrigin = url::Origin::Create(kCapturedUrl); | 
 |   captured->NavigateAndCommit(kCapturedUrl); | 
 |  | 
 |   const GURL kAllowedUrl("https://allowed.com"); | 
 |   const url::Origin kAllowedOrigin = url::Origin::Create(kAllowedUrl); | 
 |   capturer->NavigateAndCommit(kAllowedUrl); | 
 |  | 
 |   auto& captured_device = MakeDevice(captured); | 
 |  | 
 |   auto& callback_helper = MakeCallbackHelper(); | 
 |   manager_.OnTabCaptureStarted(kLabel, captured_device, | 
 |                                capturer->GetPrimaryMainFrame()->GetGlobalId(), | 
 |                                callback_helper.AsCallback()); | 
 |  | 
 |   EXPECT_CALL(callback_helper, | 
 |               Method(kLabel, captured_device.type, | 
 |                      IsCaptureHandle(kCapturedOrigin, u"handle"))) | 
 |       .Times(1); | 
 |   captured->SetCaptureHandleConfig( | 
 |       MakeRestrictiveConfigWithHandle({kAllowedOrigin}, u"handle")); | 
 | } | 
 |  | 
 | }  // namespace content |