blob: 41f517ad9859d0e556d26fc39d1035f73b6e098f [file] [log] [blame]
// 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 <array>
#include <memory>
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/policy/policy_test_utils.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/test/mock_permission_prompt_factory.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/webrtc/media_stream_devices_controller.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
using testing::_;
namespace policy {
class MediaStreamDevicesControllerBrowserTest
: public PolicyTest,
public testing::WithParamInterface<bool> {
public:
void SetUpOnMainThread() override {
PolicyTest::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Start());
request_url_ = embedded_test_server()->GetURL("/simple.html");
request_pattern_ = request_url_.DeprecatedGetOriginAsURL().spec();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), request_url_));
// Testing both the new (PermissionManager) and old code-paths is not simple
// since we are already using WithParamInterface. We only test whichever one
// is enabled in chrome_features.cc since we won't keep the old path around
// for long once we flip the flag.
permissions::PermissionRequestManager* manager =
permissions::PermissionRequestManager::FromWebContents(
browser()->tab_strip_model()->GetActiveWebContents());
prompt_factory_ =
std::make_unique<permissions::MockPermissionPromptFactory>(manager);
prompt_factory_->set_response_type(
permissions::PermissionRequestManager::ACCEPT_ALL);
}
void TearDownOnMainThread() override { prompt_factory_.reset(); }
content::MediaStreamRequest CreateRequest(
blink::mojom::MediaStreamType audio_request_type,
blink::mojom::MediaStreamType video_request_type) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(request_url_,
web_contents->GetPrimaryMainFrame()->GetLastCommittedURL());
int render_process_id =
web_contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID();
int render_frame_id = web_contents->GetPrimaryMainFrame()->GetRoutingID();
return content::MediaStreamRequest(
render_process_id, render_frame_id, 0,
url::Origin::Create(request_url_), false, blink::MEDIA_GENERATE_STREAM,
/*requested_audio_device_ids=*/{}, /*requested_video_device_ids=*/{},
audio_request_type, video_request_type,
/*disable_local_echo=*/false,
/*request_pan_tilt_zoom_permission=*/false,
/*captured_surface_control_active=*/false);
}
// Configure a given policy map. The |policy_name| is the name of either the
// audio or video capture allow policy and must never be nullptr.
// |allowlist_policy| and |allow_rule| are optional. If nullptr, no allowlist
// policy is set. If non-nullptr, the allowlist policy is set to contain
// either the |allow_rule| (if non-nullptr) or an "allow all" wildcard.
void ConfigurePolicyMap(PolicyMap* policies,
const char* policy_name,
const char* allowlist_policy,
const char* allow_rule) {
policies->Set(policy_name, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, base::Value(policy_value_), nullptr);
if (allowlist_policy) {
// Add an entry to the allowlist that allows the specified URL regardless
// of the setting of kAudioCapturedAllowed.
base::Value::List list;
if (allow_rule) {
list.Append(allow_rule);
request_url_allowed_via_allowlist_ = true;
} else {
list.Append(ContentSettingsPattern::Wildcard().ToString());
// We should ignore all wildcard entries in the allowlist, so even
// though we've added an entry, it should be ignored and our expectation
// is that the request has not been allowed via the allowlist.
request_url_allowed_via_allowlist_ = false;
}
policies->Set(allowlist_policy, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
POLICY_SOURCE_CLOUD, base::Value(std::move(list)), nullptr);
}
}
void Accept(const blink::mojom::StreamDevicesSet& stream_devices_set,
blink::mojom::MediaStreamRequestResult result,
bool blocked_by_permissions_policy,
ContentSetting audio_setting,
ContentSetting video_setting) {
if (result == blink::mojom::MediaStreamRequestResult::OK) {
ASSERT_EQ(stream_devices_set.stream_devices.size(), 1u);
const blink::mojom::StreamDevices& devices =
*stream_devices_set.stream_devices[0];
if (policy_value_ || request_url_allowed_via_allowlist_) {
ASSERT_NE(devices.audio_device.has_value(),
devices.video_device.has_value());
if (devices.audio_device.has_value()) {
ASSERT_EQ("fake_dev", devices.audio_device.value().id);
} else if (devices.video_device.has_value()) {
ASSERT_EQ("fake_dev", devices.video_device.value().id);
}
} else {
ASSERT_FALSE(devices.audio_device.has_value());
ASSERT_FALSE(devices.video_device.has_value());
}
} else {
ASSERT_EQ(0u, stream_devices_set.stream_devices.size());
}
}
void FinishAudioTest(std::string requested_device_id) {
content::MediaStreamRequest request(
CreateRequest(blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE,
blink::mojom::MediaStreamType::NO_SERVICE));
request.requested_audio_device_ids = {requested_device_id};
// TODO(raymes): Test MEDIA_DEVICE_OPEN (Pepper) which grants both webcam
// and microphone permissions at the same time.
webrtc::MediaStreamDevicesController::RequestPermissions(
request, MediaCaptureDevicesDispatcher::GetInstance(),
base::BindOnce(&MediaStreamDevicesControllerBrowserTest::Accept,
base::Unretained(this)));
quit_closure_.Run();
}
void FinishVideoTest(std::string requested_device_id) {
content::MediaStreamRequest request(
CreateRequest(blink::mojom::MediaStreamType::NO_SERVICE,
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE));
request.requested_video_device_ids = {requested_device_id};
// TODO(raymes): Test MEDIA_DEVICE_OPEN (Pepper) which grants both webcam
// and microphone permissions at the same time.
webrtc::MediaStreamDevicesController::RequestPermissions(
request, MediaCaptureDevicesDispatcher::GetInstance(),
base::BindOnce(&MediaStreamDevicesControllerBrowserTest::Accept,
base::Unretained(this)));
quit_closure_.Run();
}
std::unique_ptr<permissions::MockPermissionPromptFactory> prompt_factory_;
bool policy_value_ = GetParam();
bool request_url_allowed_via_allowlist_ = false;
GURL request_url_;
std::string request_pattern_;
base::RepeatingClosure quit_closure_;
};
IN_PROC_BROWSER_TEST_P(MediaStreamDevicesControllerBrowserTest,
AudioCaptureAllowed) {
blink::MediaStreamDevices audio_devices;
blink::MediaStreamDevice fake_audio_device(
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, "fake_dev",
"Fake Audio Device");
audio_devices.push_back(fake_audio_device);
PolicyMap policies;
ConfigurePolicyMap(&policies, key::kAudioCaptureAllowed, nullptr, nullptr);
UpdateProviderPolicy(policies);
content::GetIOThreadTaskRunner({})->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
&MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices,
base::Unretained(MediaCaptureDevicesDispatcher::GetInstance()),
audio_devices),
base::BindOnce(&MediaStreamDevicesControllerBrowserTest::FinishAudioTest,
base::Unretained(this), fake_audio_device.id));
base::RunLoop loop;
quit_closure_ = loop.QuitWhenIdleClosure();
loop.Run();
}
IN_PROC_BROWSER_TEST_P(MediaStreamDevicesControllerBrowserTest,
AudioCaptureAllowedUrls) {
blink::MediaStreamDevices audio_devices;
blink::MediaStreamDevice fake_audio_device(
blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE, "fake_dev",
"Fake Audio Device");
audio_devices.push_back(fake_audio_device);
auto allow_pattern = std::to_array<const char*>({
request_pattern_.c_str(),
// This will set an allow-all policy allowlist. Since we do not allow
// setting an allow-all entry in the allowlist, this entry should be
// ignored and therefore the request should be denied.
nullptr,
});
for (size_t i = 0; i < std::size(allow_pattern); ++i) {
PolicyMap policies;
ConfigurePolicyMap(&policies, key::kAudioCaptureAllowed,
key::kAudioCaptureAllowedUrls, allow_pattern[i]);
UpdateProviderPolicy(policies);
content::GetIOThreadTaskRunner({})->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
&MediaCaptureDevicesDispatcher::SetTestAudioCaptureDevices,
base::Unretained(MediaCaptureDevicesDispatcher::GetInstance()),
audio_devices),
base::BindOnce(
&MediaStreamDevicesControllerBrowserTest::FinishAudioTest,
base::Unretained(this), fake_audio_device.id));
base::RunLoop loop;
quit_closure_ = loop.QuitWhenIdleClosure();
loop.Run();
}
}
IN_PROC_BROWSER_TEST_P(MediaStreamDevicesControllerBrowserTest,
VideoCaptureAllowed) {
blink::MediaStreamDevices video_devices;
blink::MediaStreamDevice fake_video_device(
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, "fake_dev",
"Fake Video Device");
video_devices.push_back(fake_video_device);
PolicyMap policies;
ConfigurePolicyMap(&policies, key::kVideoCaptureAllowed, nullptr, nullptr);
UpdateProviderPolicy(policies);
content::GetIOThreadTaskRunner({})->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
&MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices,
base::Unretained(MediaCaptureDevicesDispatcher::GetInstance()),
video_devices),
base::BindOnce(&MediaStreamDevicesControllerBrowserTest::FinishVideoTest,
base::Unretained(this), std::move(fake_video_device.id)));
base::RunLoop loop;
quit_closure_ = loop.QuitWhenIdleClosure();
loop.Run();
}
IN_PROC_BROWSER_TEST_P(MediaStreamDevicesControllerBrowserTest,
VideoCaptureAllowedUrls) {
blink::MediaStreamDevices video_devices;
blink::MediaStreamDevice fake_video_device(
blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE, "fake_dev",
"Fake Video Device");
video_devices.push_back(fake_video_device);
auto allow_pattern = std::to_array<const char*>({
request_pattern_.c_str(),
// This will set an allow-all policy allowlist. Since we do not allow
// setting an allow-all entry in the allowlist, this entry should be
// ignored and therefore the request should be denied.
nullptr,
});
for (size_t i = 0; i < std::size(allow_pattern); ++i) {
PolicyMap policies;
ConfigurePolicyMap(&policies, key::kVideoCaptureAllowed,
key::kVideoCaptureAllowedUrls, allow_pattern[i]);
UpdateProviderPolicy(policies);
content::GetIOThreadTaskRunner({})->PostTaskAndReply(
FROM_HERE,
base::BindOnce(
&MediaCaptureDevicesDispatcher::SetTestVideoCaptureDevices,
base::Unretained(MediaCaptureDevicesDispatcher::GetInstance()),
video_devices),
base::BindOnce(
&MediaStreamDevicesControllerBrowserTest::FinishVideoTest,
base::Unretained(this), fake_video_device.id));
base::RunLoop loop;
quit_closure_ = loop.QuitWhenIdleClosure();
loop.Run();
}
}
INSTANTIATE_TEST_SUITE_P(MediaStreamDevicesControllerBrowserTestInstance,
MediaStreamDevicesControllerBrowserTest,
testing::Bool());
} // namespace policy