Commit e0fd3165 authored by Francois Beaufort's avatar Francois Beaufort Committed by Commit Bot

[PTZ] Merge camera and ptz permissions from a user perspective

This CL makes some UI changes to how ptz is presented to the user:
- camera is hidden in page info if ptz is granted or blocked
- any change to ptz is reflected on camera
- users see one permission for both camera and ptz when requested
- bubble strings are adjusted when ptz is granted

Screenshots: https://imgur.com/a/tlR4KRr
Test: https://ptz.glitch.me/ w/ Experimental Web Platform Features flag

Change-Id: Ia94c23ae0a79feb6f0744cfd0bc5bc0771604af6
Bug: 934063
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2224214
Commit-Queue: François Beaufort <beaufort.francois@gmail.com>
Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarEvan Stade <estade@chromium.org>
Reviewed-by: default avatarBalazs Engedy <engedy@chromium.org>
Reviewed-by: default avatarAndy Paicu <andypaicu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#776865}
parent 7ace4a6a
......@@ -2644,12 +2644,18 @@ are declared in tools/grit/grit_rule.gni.
<message name="IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION" desc="Radio button choice to allow a site to access microphone and camera, displayed in bubble when a page is accessing microphone and camera.">
Continue allowing <ph name="HOST">$1<ex>mail.google.com</ex></ph> to access your camera and microphone
</message>
<message name="IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_PAN_TILT_ZOOM_NO_ACTION" desc="Radio button choice to allow a site to use and move camera, and use microphone, displayed in bubble when a page is accessing microphone and camera.">
Continue allowing <ph name="HOST">$1<ex>mail.google.com</ex></ph> to use and move your camera, and use your microphone
</message>
<message name="IDS_ALLOWED_MEDIASTREAM_MIC_NO_ACTION" desc="Radio button choice to allow a site to access microphone, displayed in bubble when a page is accessing microphone.">
Continue allowing <ph name="HOST">$1<ex>mail.google.com</ex></ph> to access your microphone
</message>
<message name="IDS_ALLOWED_MEDIASTREAM_CAMERA_NO_ACTION" desc="Radio button choice to allow a site to access camera, displayed in bubble when a page is accessing camera.">
Continue allowing <ph name="HOST">$1<ex>mail.google.com</ex></ph> to access your camera
</message>
<message name="IDS_ALLOWED_CAMERA_PAN_TILT_ZOOM_NO_ACTION" desc="Radio button choice to allow a site to use and move camera, displayed in bubble when a page is accessing camera.">
Continue allowing <ph name="HOST">$1<ex>mail.google.com</ex></ph> to use and move your camera
</message>
<message name="IDS_BLOCKED_POPUPS_EXPLANATORY_TEXT" desc="Explanatory animated text that appears (and then disappears) in the address line when popup is blocked">
Pop-up blocked
</message>
......
5bcf338a371961b6028b999c47497b0e776134e3
\ No newline at end of file
......@@ -40,8 +40,26 @@ void CameraPanTiltZoomPermissionContext::OnContentSettingChanged(
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
const std::string& resource_identifier) {
if (content_type != ContentSettingsType::MEDIASTREAM_CAMERA)
if (content_type != ContentSettingsType::MEDIASTREAM_CAMERA &&
content_type != ContentSettingsType::CAMERA_PAN_TILT_ZOOM) {
return;
}
// Skip if the camera permission is currently being updated to match camera
// PTZ permission as OnContentSettingChanged would have been called again
// causing a reentrancy issue.
if (updating_mediastream_camera_permission_) {
updating_mediastream_camera_permission_ = false;
return;
}
// Skip if the camera PTZ permission is currently being reset when camera
// permission got blocked or reset as OnContentSettingChanged would have been
// called again causing a reentrancy issue.
if (updating_camera_ptz_permission_) {
updating_camera_ptz_permission_ = false;
return;
}
// TODO(crbug.com/1078272): We should not need to deduce the url from the
// primary pattern here. Modify the infrastructure to facilitate this
......@@ -53,6 +71,18 @@ void CameraPanTiltZoomPermissionContext::OnContentSettingChanged(
ContentSetting camera_ptz_setting =
host_content_settings_map_->GetContentSetting(
url, url, content_settings_type(), resource_identifier);
if (content_type == ContentSettingsType::CAMERA_PAN_TILT_ZOOM) {
// Automatically update camera permission to camera PTZ permission as any
// change to camera PTZ should be reflected to camera.
updating_mediastream_camera_permission_ = true;
host_content_settings_map_->SetContentSettingCustomScope(
primary_pattern, secondary_pattern,
ContentSettingsType::MEDIASTREAM_CAMERA, resource_identifier,
camera_ptz_setting);
return;
}
// Don't reset camera PTZ permission if it is already blocked or in a
// "default" state.
if (camera_ptz_setting == CONTENT_SETTING_BLOCK ||
......@@ -67,6 +97,7 @@ void CameraPanTiltZoomPermissionContext::OnContentSettingChanged(
mediastream_camera_setting == CONTENT_SETTING_ASK) {
// Automatically reset camera PTZ permission if camera permission
// gets blocked or reset.
updating_camera_ptz_permission_ = true;
host_content_settings_map_->SetContentSettingCustomScope(
primary_pattern, secondary_pattern,
ContentSettingsType::CAMERA_PAN_TILT_ZOOM, resource_identifier,
......
......@@ -47,6 +47,9 @@ class CameraPanTiltZoomPermissionContext
const std::string& resource_identifier) override;
HostContentSettingsMap* host_content_settings_map_;
bool updating_camera_ptz_permission_ = false;
bool updating_mediastream_camera_permission_ = false;
};
#endif // CHROME_BROWSER_MEDIA_WEBRTC_CAMERA_PAN_TILT_ZOOM_PERMISSION_CONTEXT_H_
......@@ -12,35 +12,23 @@
namespace {
struct ContentSettingTestParams {
const ContentSetting initial_camera_pan_tilt_zoom;
const ContentSetting mediastream_camera;
const ContentSetting expected_camera_pan_tilt_zoom;
} kTestParams[] = {
{CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
CONTENT_SETTING_ASK}, // Granted permission is reset if camera is
// blocked.
{CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK,
CONTENT_SETTING_ASK}, // Granted permission is reset if camera is
// reset.
{CONTENT_SETTING_BLOCK, CONTENT_SETTING_ALLOW,
CONTENT_SETTING_BLOCK}, // Blocked permission is not reset if camera
// is granted.
{CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK,
CONTENT_SETTING_BLOCK}, // Blocked permission is not reset if camera
// is blocked.
struct TestConfig {
const ContentSetting first; // first content setting to be set
const ContentSetting second; // second content setting to be set
const ContentSetting result; // expected resulting content setting
};
} // namespace
// Waits until a camera change is observed in content settings.
class CameraContentSettingsChangeWaiter : public content_settings::Observer {
// Waits until a change is observed for a specific content setting type.
class ContentSettingsChangeWaiter : public content_settings::Observer {
public:
explicit CameraContentSettingsChangeWaiter(Profile* profile)
: profile_(profile) {
explicit ContentSettingsChangeWaiter(Profile* profile,
ContentSettingsType content_type)
: profile_(profile), content_type_(content_type) {
HostContentSettingsMapFactory::GetForProfile(profile)->AddObserver(this);
}
~CameraContentSettingsChangeWaiter() override {
~ContentSettingsChangeWaiter() override {
HostContentSettingsMapFactory::GetForProfile(profile_)->RemoveObserver(
this);
}
......@@ -50,7 +38,7 @@ class CameraContentSettingsChangeWaiter : public content_settings::Observer {
const ContentSettingsPattern& secondary_pattern,
ContentSettingsType content_type,
const std::string& resource_identifier) override {
if (content_type == ContentSettingsType::MEDIASTREAM_CAMERA)
if (content_type == content_type_)
Proceed();
}
......@@ -60,14 +48,15 @@ class CameraContentSettingsChangeWaiter : public content_settings::Observer {
void Proceed() { run_loop_.Quit(); }
Profile* profile_;
ContentSettingsType content_type_;
base::RunLoop run_loop_;
DISALLOW_COPY_AND_ASSIGN(CameraContentSettingsChangeWaiter);
DISALLOW_COPY_AND_ASSIGN(ContentSettingsChangeWaiter);
};
class CameraPanTiltZoomPermissionContextTests
: public ChromeRenderViewHostTestHarness,
public testing::WithParamInterface<ContentSettingTestParams> {
public testing::WithParamInterface<TestConfig> {
public:
CameraPanTiltZoomPermissionContextTests() = default;
......@@ -91,21 +80,113 @@ class CameraPanTiltZoomPermissionContextTests
DISALLOW_COPY_AND_ASSIGN(CameraPanTiltZoomPermissionContextTests);
};
TEST_P(CameraPanTiltZoomPermissionContextTests,
TestResetPermissionOnCameraChange) {
class CameraContentSettingTests
: public CameraPanTiltZoomPermissionContextTests {
public:
CameraContentSettingTests() = default;
};
TEST_P(CameraContentSettingTests, TestResetPermissionOnCameraChange) {
CameraPanTiltZoomPermissionContext permission_context(profile());
CameraContentSettingsChangeWaiter waiter(profile());
ContentSettingsChangeWaiter waiter(profile(),
ContentSettingsType::MEDIASTREAM_CAMERA);
SetContentSetting(ContentSettingsType::CAMERA_PAN_TILT_ZOOM,
GetParam().initial_camera_pan_tilt_zoom);
SetContentSetting(ContentSettingsType::MEDIASTREAM_CAMERA,
GetParam().mediastream_camera);
GetParam().first);
SetContentSetting(ContentSettingsType::MEDIASTREAM_CAMERA, GetParam().second);
waiter.Wait();
EXPECT_EQ(GetParam().expected_camera_pan_tilt_zoom,
EXPECT_EQ(GetParam().result,
GetContentSetting(ContentSettingsType::CAMERA_PAN_TILT_ZOOM));
}
INSTANTIATE_TEST_SUITE_P(ResetPermissionOnCameraChange,
CameraPanTiltZoomPermissionContextTests,
testing::ValuesIn(kTestParams));
INSTANTIATE_TEST_SUITE_P(
ResetPermissionOnCameraChange,
CameraContentSettingTests,
testing::Values(
// Granted camera PTZ permission is reset if camera is blocked.
TestConfig{CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
CONTENT_SETTING_ASK},
// Granted camera PTZ permission is reset if camera is reset.
TestConfig{CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK,
CONTENT_SETTING_ASK},
// Blocked camera PTZ permission is not reset if camera is granted.
TestConfig{CONTENT_SETTING_BLOCK, CONTENT_SETTING_ALLOW,
CONTENT_SETTING_BLOCK},
// Blocked camera PTZ permission is not reset if camera is blocked.
TestConfig{CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK,
CONTENT_SETTING_BLOCK}));
class CameraPanTiltZoomContentSettingTests
: public CameraPanTiltZoomPermissionContextTests {
public:
CameraPanTiltZoomContentSettingTests() = default;
};
TEST_P(CameraPanTiltZoomContentSettingTests,
TestCameraPermissionOnCameraPanTiltZoomChange) {
CameraPanTiltZoomPermissionContext permission_context(profile());
ContentSettingsChangeWaiter waiter(profile(),
ContentSettingsType::CAMERA_PAN_TILT_ZOOM);
SetContentSetting(ContentSettingsType::MEDIASTREAM_CAMERA, GetParam().first);
SetContentSetting(ContentSettingsType::CAMERA_PAN_TILT_ZOOM,
GetParam().second);
waiter.Wait();
EXPECT_EQ(GetParam().result,
GetContentSetting(ContentSettingsType::MEDIASTREAM_CAMERA));
}
INSTANTIATE_TEST_SUITE_P(
CameraPermissionOnCameraPanTiltZoomChange,
CameraPanTiltZoomContentSettingTests,
testing::Values(
// Asked camera permission is blocked if camera PTZ is blocked.
TestConfig{CONTENT_SETTING_ASK, CONTENT_SETTING_BLOCK,
CONTENT_SETTING_BLOCK},
// Asked camera permission is granted if camera PTZ is granted.
TestConfig{CONTENT_SETTING_ASK, CONTENT_SETTING_ALLOW,
CONTENT_SETTING_ALLOW},
// Asked camera permission is unchanged if camera PTZ is reset.
TestConfig{CONTENT_SETTING_ASK, CONTENT_SETTING_DEFAULT,
CONTENT_SETTING_ASK},
// Asked camera permission is unchanged if camera PTZ is ask.
TestConfig{CONTENT_SETTING_ASK, CONTENT_SETTING_ASK,
CONTENT_SETTING_ASK},
// Allowed camera permission is blocked if camera PTZ is blocked.
TestConfig{CONTENT_SETTING_ALLOW, CONTENT_SETTING_BLOCK,
CONTENT_SETTING_BLOCK},
// Allowed camera permission is unchanged if camera PTZ is granted.
TestConfig{CONTENT_SETTING_ALLOW, CONTENT_SETTING_ALLOW,
CONTENT_SETTING_ALLOW},
// Allowed camera permission is ask if camera PTZ is reset.
TestConfig{CONTENT_SETTING_ALLOW, CONTENT_SETTING_DEFAULT,
CONTENT_SETTING_ASK},
// Allowed camera permission is reset if camera PTZ is ask.
TestConfig{CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK,
CONTENT_SETTING_ASK},
// Blocked camera permission is unchanged if camera PTZ is blocked.
TestConfig{CONTENT_SETTING_BLOCK, CONTENT_SETTING_BLOCK,
CONTENT_SETTING_BLOCK},
// Blocked camera permission is allowed if camera PTZ is granted.
TestConfig{CONTENT_SETTING_BLOCK, CONTENT_SETTING_ALLOW,
CONTENT_SETTING_ALLOW},
// Blocked camera permission is ask if camera PTZ is reset.
TestConfig{CONTENT_SETTING_BLOCK, CONTENT_SETTING_DEFAULT,
CONTENT_SETTING_ASK},
// Blocked camera permission is reset if camera PTZ is ask.
TestConfig{CONTENT_SETTING_BLOCK, CONTENT_SETTING_ASK,
CONTENT_SETTING_ASK},
// Default camera permission is blocked if camera PTZ is blocked.
TestConfig{CONTENT_SETTING_DEFAULT, CONTENT_SETTING_BLOCK,
CONTENT_SETTING_BLOCK},
// Default camera permission is allowed if camera PTZ is granted.
TestConfig{CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ALLOW,
CONTENT_SETTING_ALLOW},
// Default camera permission is unchanged if camera PTZ is reset.
TestConfig{CONTENT_SETTING_DEFAULT, CONTENT_SETTING_DEFAULT,
CONTENT_SETTING_ASK},
// Default camera permission is ask if camera PTZ is ask.
TestConfig{CONTENT_SETTING_DEFAULT, CONTENT_SETTING_ASK,
CONTENT_SETTING_ASK}));
......@@ -10,11 +10,13 @@
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/media/webrtc/camera_pan_tilt_zoom_permission_context.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/media/webrtc/media_stream_capture_indicator.h"
#include "chrome/browser/media/webrtc/media_stream_device_permissions.h"
#include "chrome/browser/media/webrtc/permission_bubble_media_access_handler.h"
#include "chrome/browser/media/webrtc/webrtc_browsertest_base.h"
#include "chrome/browser/permissions/permission_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
......@@ -24,6 +26,7 @@
#include "components/content_settings/browser/tab_specific_content_settings.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/permissions/permission_context_base.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_request.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/permission_util.h"
......@@ -106,7 +109,7 @@ class MediaStreamDevicesControllerTest : public WebRtcTestBase {
prefs->SetBoolean(policy_name, access == ACCESS_ALLOWED);
}
// Set the content settings for mic/cam.
// Set the content settings for mic/cam/ptz.
void SetContentSettings(ContentSetting mic_setting,
ContentSetting cam_setting,
ContentSetting ptz_setting) {
......@@ -747,6 +750,19 @@ IN_PROC_BROWSER_TEST_P(MediaStreamDevicesControllerPtzTest, ContentSettings) {
{CONTENT_SETTING_ASK, CONTENT_SETTING_ASK, CONTENT_SETTING_ASK, true},
};
// Prevent automatic camera permission change when camera PTZ gets updated.
CameraPanTiltZoomPermissionContext* camera_pan_tilt_zoom_permission_context =
static_cast<CameraPanTiltZoomPermissionContext*>(
PermissionManagerFactory::GetForProfile(
Profile::FromBrowserContext(
GetWebContents()->GetBrowserContext()))
->GetPermissionContextForTesting(
ContentSettingsType::CAMERA_PAN_TILT_ZOOM));
HostContentSettingsMap* content_settings =
HostContentSettingsMapFactory::GetForProfile(
Profile::FromBrowserContext(GetWebContents()->GetBrowserContext()));
content_settings->RemoveObserver(camera_pan_tilt_zoom_permission_context);
const bool pan_tilt_zoom_supported = IsPanTiltZoomSupported();
for (auto& test : tests) {
SetContentSettings(test.mic, test.cam, test.ptz);
......
......@@ -52,7 +52,9 @@
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_utils.h"
#include "components/permissions/permission_decision_auto_blocker.h"
#include "components/permissions/permission_manager.h"
#include "components/permissions/permission_request_manager.h"
#include "components/permissions/permission_result.h"
#include "components/permissions/permission_uma_util.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/permissions_client.h"
......@@ -1216,14 +1218,28 @@ void ContentSettingMediaStreamBubbleModel::SetRadioGroup() {
CameraAccessed() ? IDS_BLOCKED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION
: IDS_BLOCKED_MEDIASTREAM_MIC_NO_ACTION;
} else {
permissions::PermissionManager* permission_manager =
permissions::PermissionsClient::Get()->GetPermissionManager(
web_contents()->GetBrowserContext());
permissions::PermissionResult pan_tilt_zoom_permission =
permission_manager->GetPermissionStatusForFrame(
ContentSettingsType::CAMERA_PAN_TILT_ZOOM,
web_contents()->GetMainFrame(), url);
bool has_pan_tilt_zoom_permission_granted =
pan_tilt_zoom_permission.content_setting == CONTENT_SETTING_ALLOW;
if (MicrophoneAccessed() && CameraAccessed()) {
radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION;
radio_allow_label_id =
has_pan_tilt_zoom_permission_granted
? IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_PAN_TILT_ZOOM_NO_ACTION
: IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION;
radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_BLOCK;
} else if (MicrophoneAccessed()) {
radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_NO_ACTION;
radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_MIC_BLOCK;
} else {
radio_allow_label_id = IDS_ALLOWED_MEDIASTREAM_CAMERA_NO_ACTION;
radio_allow_label_id = has_pan_tilt_zoom_permission_granted
? IDS_ALLOWED_CAMERA_PAN_TILT_ZOOM_NO_ACTION
: IDS_ALLOWED_MEDIASTREAM_CAMERA_NO_ACTION;
radio_block_label_id = IDS_ALLOWED_MEDIASTREAM_CAMERA_BLOCK;
}
}
......
......@@ -10,6 +10,8 @@
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/content_settings/chrome_content_settings_utils.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h"
#include "chrome/browser/ui/browser_finder.h"
......@@ -17,11 +19,13 @@
#include "chrome/browser/ui/content_settings/fake_owner.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/content_settings/browser/tab_specific_content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "components/url_formatter/elide_url.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
......@@ -153,21 +157,8 @@ class ContentSettingBubbleModelMediaStreamTest : public InProcessBrowserTest {
public:
void ManageMediaStreamSettings(
TabSpecificContentSettings::MicrophoneCameraState state) {
// Open a tab for which we will invoke the media bubble.
GURL url(ui_test_utils::GetTestUrl(
base::FilePath().AppendASCII("content_setting_bubble"),
base::FilePath().AppendASCII("mixed_script.html")));
ui_test_utils::NavigateToURL(browser(), url);
content::WebContents* original_tab = GetActiveTab();
// Create a bubble with the given camera and microphone access state.
TabSpecificContentSettings::FromWebContents(original_tab)->
OnMediaStreamPermissionSet(
original_tab->GetLastCommittedURL(),
state, std::string(), std::string(), std::string(), std::string());
std::unique_ptr<ContentSettingBubbleModel> bubble(
new ContentSettingMediaStreamBubbleModel(
browser()->content_setting_bubble_model_delegate(), original_tab));
content::WebContents* original_tab = OpenTab();
std::unique_ptr<ContentSettingBubbleModel> bubble = ShowBubble(state);
// Click the manage button, which opens in a new tab or window. Wait until
// it loads.
......@@ -177,12 +168,43 @@ class ContentSettingBubbleModelMediaStreamTest : public InProcessBrowserTest {
observer.Wait();
}
std::unique_ptr<ContentSettingBubbleModel> ShowBubble(
TabSpecificContentSettings::MicrophoneCameraState state) {
content::WebContents* web_contents = GetActiveTab();
// Create a bubble with the given camera and microphone access state.
TabSpecificContentSettings::FromWebContents(web_contents)
->OnMediaStreamPermissionSet(web_contents->GetLastCommittedURL(), state,
std::string(), std::string(),
std::string(), std::string());
return std::make_unique<ContentSettingMediaStreamBubbleModel>(
browser()->content_setting_bubble_model_delegate(), web_contents);
}
content::WebContents* GetActiveTab() {
// First, we need to find the active browser window. It should be at
// the same desktop as the browser in which we invoked the bubble.
Browser* active_browser = chrome::FindLastActive();
return active_browser->tab_strip_model()->GetActiveWebContents();
}
content::WebContents* OpenTab() {
// Open a tab for which we will invoke the media bubble.
GURL url(
https_server_->GetURL("/content_setting_bubble/mixed_script.html"));
ui_test_utils::NavigateToURL(browser(), url);
return GetActiveTab();
}
protected:
void SetUpInProcessBrowserTestFixture() override {
https_server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTPS);
https_server_->ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(https_server_->Start());
}
std::unique_ptr<net::EmbeddedTestServer> https_server_;
};
// Tests that clicking on the manage button in the media bubble opens the
......@@ -212,6 +234,61 @@ IN_PROC_BROWSER_TEST_F(ContentSettingBubbleModelMediaStreamTest,
GetActiveTab()->GetLastCommittedURL());
}
// Tests that media bubble content includes camera PTZ when the permission has
// been granted to the website.
IN_PROC_BROWSER_TEST_F(ContentSettingBubbleModelMediaStreamTest,
BubbleContentIncludesCameraPanTiltZoom) {
content::WebContents* web_contents = OpenTab();
GURL url = web_contents->GetLastCommittedURL();
// Do not grant camera PTZ permission to current tab.
HostContentSettingsMapFactory::GetForProfile(browser()->profile())
->SetContentSettingDefaultScope(url, GURL(),
ContentSettingsType::CAMERA_PAN_TILT_ZOOM,
std::string(), CONTENT_SETTING_ASK);
// The mic & camera bubble content does not include camera PTZ.
std::unique_ptr<ContentSettingBubbleModel> mic_and_camera_bubble =
ShowBubble(TabSpecificContentSettings::MICROPHONE_ACCESSED |
TabSpecificContentSettings::CAMERA_ACCESSED);
EXPECT_EQ(mic_and_camera_bubble->bubble_content().radio_group.radio_items[0],
l10n_util::GetStringFUTF16(
IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_NO_ACTION,
url_formatter::FormatUrlForSecurityDisplay(url)));
// The camera bubble content does not include camera PTZ.
std::unique_ptr<ContentSettingBubbleModel> camera_bubble =
ShowBubble(TabSpecificContentSettings::CAMERA_ACCESSED);
EXPECT_EQ(camera_bubble->bubble_content().radio_group.radio_items[0],
l10n_util::GetStringFUTF16(
IDS_ALLOWED_MEDIASTREAM_CAMERA_NO_ACTION,
url_formatter::FormatUrlForSecurityDisplay(url)));
// Grant camera PTZ permission to current tab.
HostContentSettingsMapFactory::GetForProfile(browser()->profile())
->SetContentSettingDefaultScope(url, GURL(),
ContentSettingsType::CAMERA_PAN_TILT_ZOOM,
std::string(), CONTENT_SETTING_ALLOW);
// The mic & camera bubble content includes camera PTZ.
std::unique_ptr<ContentSettingBubbleModel> mic_and_camera_ptz_bubble =
ShowBubble(TabSpecificContentSettings::MICROPHONE_ACCESSED |
TabSpecificContentSettings::CAMERA_ACCESSED);
EXPECT_EQ(
mic_and_camera_ptz_bubble->bubble_content().radio_group.radio_items[0],
l10n_util::GetStringFUTF16(
IDS_ALLOWED_MEDIASTREAM_MIC_AND_CAMERA_PAN_TILT_ZOOM_NO_ACTION,
url_formatter::FormatUrlForSecurityDisplay(url)));
// The camera bubble content includes camera PTZ.
std::unique_ptr<ContentSettingBubbleModel> camera_ptz_bubble =
ShowBubble(TabSpecificContentSettings::CAMERA_ACCESSED);
EXPECT_EQ(camera_ptz_bubble->bubble_content().radio_group.radio_items[0],
l10n_util::GetStringFUTF16(
IDS_ALLOWED_CAMERA_PAN_TILT_ZOOM_NO_ACTION,
url_formatter::FormatUrlForSecurityDisplay(url)));
}
class ContentSettingBubbleModelPopupTest : public InProcessBrowserTest {
protected:
static constexpr int kDisallowButtonIndex = 1;
......
......@@ -45,6 +45,7 @@ PermissionPromptBubbleView::PermissionPromptBubbleView(
base::TimeTicks permission_requested_time)
: browser_(browser),
delegate_(delegate),
visible_requests_(GetVisibleRequests()),
name_or_origin_(GetDisplayNameOrOrigin()),
permission_requested_time_(permission_requested_time) {
// Note that browser_ may be null in unit tests.
......@@ -80,10 +81,12 @@ PermissionPromptBubbleView::PermissionPromptBubbleView(
ChromeLayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_RELATED_CONTROL_VERTICAL)));
for (permissions::PermissionRequest* request : delegate_->Requests())
for (permissions::PermissionRequest* request : visible_requests_)
AddPermissionRequestLine(request);
}
PermissionPromptBubbleView::~PermissionPromptBubbleView() = default;
void PermissionPromptBubbleView::Show() {
DCHECK(browser_->window());
......@@ -104,6 +107,35 @@ void PermissionPromptBubbleView::Show() {
chrome::RecordDialogCreation(chrome::DialogIdentifier::PERMISSIONS);
}
std::vector<permissions::PermissionRequest*>
PermissionPromptBubbleView::GetVisibleRequests() {
std::vector<permissions::PermissionRequest*> visible_requests;
for (permissions::PermissionRequest* request : delegate_->Requests()) {
if (ShouldShowPermissionRequest(request))
visible_requests.push_back(request);
}
return visible_requests;
}
bool PermissionPromptBubbleView::ShouldShowPermissionRequest(
permissions::PermissionRequest* request) {
if (request->GetContentSettingsType() !=
ContentSettingsType::MEDIASTREAM_CAMERA) {
return true;
}
// Hide camera request only if camera PTZ request is present as well.
for (permissions::PermissionRequest* request : delegate_->Requests()) {
if (request->GetContentSettingsType() ==
ContentSettingsType::CAMERA_PAN_TILT_ZOOM) {
return false;
}
}
return true;
}
void PermissionPromptBubbleView::AddPermissionRequestLine(
permissions::PermissionRequest* request) {
ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
......@@ -212,23 +244,23 @@ base::string16 PermissionPromptBubbleView::GetAccessibleWindowTitle() const {
// There are three separate internationalized messages used, one for each
// format of title, to provide for accurate i18n. See https://crbug.com/434574
// for more details.
const std::vector<permissions::PermissionRequest*>& requests =
delegate_->Requests();
DCHECK(!requests.empty());
DCHECK(!visible_requests_.empty());
if (requests.size() == 1) {
if (visible_requests_.size() == 1) {
return l10n_util::GetStringFUTF16(
IDS_PERMISSIONS_BUBBLE_PROMPT_ACCESSIBLE_TITLE_ONE_PERM,
name_or_origin_.name_or_origin, requests[0]->GetMessageTextFragment());
name_or_origin_.name_or_origin,
visible_requests_[0]->GetMessageTextFragment());
}
int template_id =
requests.size() == 2
visible_requests_.size() == 2
? IDS_PERMISSIONS_BUBBLE_PROMPT_ACCESSIBLE_TITLE_TWO_PERMS
: IDS_PERMISSIONS_BUBBLE_PROMPT_ACCESSIBLE_TITLE_TWO_PERMS_MORE;
return l10n_util::GetStringFUTF16(template_id, name_or_origin_.name_or_origin,
requests[0]->GetMessageTextFragment(),
requests[1]->GetMessageTextFragment());
return l10n_util::GetStringFUTF16(
template_id, name_or_origin_.name_or_origin,
visible_requests_[0]->GetMessageTextFragment(),
visible_requests_[1]->GetMessageTextFragment());
}
gfx::Size PermissionPromptBubbleView::CalculatePreferredSize() const {
......@@ -248,10 +280,8 @@ void PermissionPromptBubbleView::ButtonPressed(views::Button* sender,
PermissionPromptBubbleView::DisplayNameOrOrigin
PermissionPromptBubbleView::GetDisplayNameOrOrigin() {
const std::vector<permissions::PermissionRequest*>& requests =
delegate_->Requests();
DCHECK(!requests.empty());
GURL origin_url = requests[0]->GetOrigin();
DCHECK(!visible_requests_.empty());
GURL origin_url = visible_requests_[0]->GetOrigin();
if (origin_url.SchemeIs(extensions::kExtensionScheme)) {
base::string16 extension_name =
......
......@@ -25,6 +25,7 @@ class PermissionPromptBubbleView : public views::ButtonListener,
PermissionPromptBubbleView(Browser* browser,
permissions::PermissionPrompt::Delegate* delegate,
base::TimeTicks permission_requested_time);
~PermissionPromptBubbleView() override;
void Show();
......@@ -54,6 +55,8 @@ class PermissionPromptBubbleView : public views::ButtonListener,
bool is_origin;
};
std::vector<permissions::PermissionRequest*> GetVisibleRequests();
bool ShouldShowPermissionRequest(permissions::PermissionRequest* request);
void AddPermissionRequestLine(permissions::PermissionRequest* request);
// Returns the origin to be displayed in the permission prompt. May return
......@@ -66,6 +69,9 @@ class PermissionPromptBubbleView : public views::ButtonListener,
Browser* const browser_;
permissions::PermissionPrompt::Delegate* const delegate_;
// List of permission requests that should be visible in the bubble.
std::vector<permissions::PermissionRequest*> visible_requests_;
// The requesting domain's name or origin.
const DisplayNameOrOrigin name_or_origin_;
......
......@@ -5,6 +5,7 @@
#include "chrome/browser/ui/views/permission_bubble/permission_prompt_bubble_view.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/views/chrome_views_test_base.h"
#include "components/permissions/permission_util.h"
#include "components/permissions/test/mock_permission_request.h"
#include "ui/base/l10n/l10n_util.h"
......@@ -14,6 +15,18 @@ namespace {
class TestDelegate : public permissions::PermissionPrompt::Delegate {
public:
explicit TestDelegate(const std::vector<ContentSettingsType> content_types) {
std::transform(
content_types.begin(), content_types.end(),
std::back_inserter(requests_), [&](auto& content_type) {
return std::make_unique<permissions::MockPermissionRequest>(
permissions::PermissionUtil::GetPermissionString(content_type),
content_type);
});
std::transform(requests_.begin(), requests_.end(),
std::back_inserter(raw_requests_),
[](auto& req) { return req.get(); });
}
TestDelegate(const GURL& origin, const std::vector<std::string> names) {
std::transform(
names.begin(), names.end(), std::back_inserter(requests_),
......@@ -85,4 +98,18 @@ TEST_F(PermissionPromptBubbleViewTest,
IDS_PERMISSIONS_BUBBLE_PROMPT_THIS_FILE)),
base::UTF16ToUTF8(bubble->GetAccessibleWindowTitle()));
}
TEST_F(PermissionPromptBubbleViewTest,
AccessibleTitleIncludesOnlyVisiblePermissions) {
TestDelegate delegate({ContentSettingsType::MEDIASTREAM_MIC,
ContentSettingsType::MEDIASTREAM_CAMERA,
ContentSettingsType::CAMERA_PAN_TILT_ZOOM});
auto bubble = std::make_unique<PermissionPromptBubbleView>(
nullptr, &delegate, base::TimeTicks::Now());
const auto title = base::UTF16ToUTF8(bubble->GetAccessibleWindowTitle());
EXPECT_PRED_FORMAT2(::testing::IsSubstring, "AudioCapture", title);
EXPECT_PRED_FORMAT2(::testing::IsSubstring, "CameraPanTiltZoom", title);
EXPECT_PRED_FORMAT2(::testing::IsNotSubstring, "VideoCapture", title);
}
} // namespace
......@@ -209,6 +209,21 @@ bool ShouldShowPermission(const PageInfoUI::PermissionInfo& info,
!cmd->HasSwitch(switches::kEnableExperimentalWebPlatformFeatures)) {
return false;
}
// Hide camera if camera PTZ is granted or blocked.
if (info.type == ContentSettingsType::MEDIASTREAM_CAMERA &&
cmd->HasSwitch(switches::kEnableExperimentalWebPlatformFeatures)) {
std::unique_ptr<base::Value> value = content_settings->GetWebsiteSetting(
site_url, site_url, ContentSettingsType::CAMERA_PAN_TILT_ZOOM,
std::string(), nullptr);
DCHECK(value.get());
ContentSetting camera_ptz_setting =
content_settings::ValueToContentSetting(value.get());
if (camera_ptz_setting == CONTENT_SETTING_ALLOW ||
camera_ptz_setting == CONTENT_SETTING_BLOCK) {
return false;
}
}
#endif
// Show the content setting if it has been changed by the user since the last
......
......@@ -581,6 +581,7 @@ const gfx::ImageSkia PageInfoUI::GetPermissionIcon(const PermissionInfo& info,
icon = &vector_icons::kMicIcon;
break;
case ContentSettingsType::MEDIASTREAM_CAMERA:
case ContentSettingsType::CAMERA_PAN_TILT_ZOOM:
icon = &vector_icons::kVideocamIcon;
break;
case ContentSettingsType::AUTOMATIC_DOWNLOADS:
......@@ -628,9 +629,6 @@ const gfx::ImageSkia PageInfoUI::GetPermissionIcon(const PermissionInfo& info,
case ContentSettingsType::AR:
icon = &vector_icons::kVrHeadsetIcon;
break;
case ContentSettingsType::CAMERA_PAN_TILT_ZOOM:
icon = &vector_icons::kCameraPanTiltZoomIcon;
break;
case ContentSettingsType::WINDOW_PLACEMENT:
icon = &vector_icons::kWindowPlacementIcon;
break;
......
......@@ -306,7 +306,7 @@
Augmented reality
</message>
<message name="IDS_PAGE_INFO_TYPE_CAMERA_PAN_TILT_ZOOM" desc="The label used for the Camera PTZ permission controls in the Page Info popup.">
Camera movement
Camera use &amp; movement
</message>
<message name="IDS_PAGE_INFO_TYPE_WINDOW_PLACEMENT" desc="The label used for the Window Placement permission controls in the Page Info popup.">
Window placement
......
4f1296eb6ba9cc54a997e38765ff4cad25662865
\ No newline at end of file
d44f3a6c3d7cc73ea8a7001bedfac50ca27288ac
\ No newline at end of file
......@@ -89,6 +89,7 @@ PermissionRequest::IconId PermissionRequestImpl::GetIconId() const {
case ContentSettingsType::MEDIASTREAM_MIC:
return vector_icons::kMicIcon;
case ContentSettingsType::MEDIASTREAM_CAMERA:
case ContentSettingsType::CAMERA_PAN_TILT_ZOOM:
return vector_icons::kVideocamIcon;
case ContentSettingsType::ACCESSIBILITY_EVENTS:
return vector_icons::kAccessibilityIcon;
......@@ -99,8 +100,6 @@ PermissionRequest::IconId PermissionRequestImpl::GetIconId() const {
return vector_icons::kVrHeadsetIcon;
case ContentSettingsType::STORAGE_ACCESS:
return vector_icons::kCookieIcon;
case ContentSettingsType::CAMERA_PAN_TILT_ZOOM:
return vector_icons::kCameraPanTiltZoomIcon;
case ContentSettingsType::WINDOW_PLACEMENT:
return vector_icons::kWindowPlacementIcon;
default:
......@@ -218,8 +217,7 @@ base::string16 PermissionRequestImpl::GetMessageTextFragment() const {
message_id = IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT;
break;
case ContentSettingsType::CAMERA_PAN_TILT_ZOOM:
message_id =
IDS_MEDIA_CAPTURE_CAMERA_PAN_TILT_ZOOM_ONLY_PERMISSION_FRAGMENT;
message_id = IDS_MEDIA_CAPTURE_CAMERA_PAN_TILT_ZOOM_PERMISSION_FRAGMENT;
break;
case ContentSettingsType::ACCESSIBILITY_EVENTS:
message_id = IDS_ACCESSIBILITY_EVENTS_PERMISSION_FRAGMENT;
......
......@@ -84,8 +84,8 @@
<message name="IDS_MEDIA_CAPTURE_VIDEO_ONLY_PERMISSION_FRAGMENT" desc="Permission fragment shown in the permissions bubble when a web page requests access to the computer's camera.">
Use your camera
</message>
<message name="IDS_MEDIA_CAPTURE_CAMERA_PAN_TILT_ZOOM_ONLY_PERMISSION_FRAGMENT" desc="Permission fragment shown in the permissions prompt when a web page requests access to the computer's camera, including zoom and move.">
Move your camera
<message name="IDS_MEDIA_CAPTURE_CAMERA_PAN_TILT_ZOOM_PERMISSION_FRAGMENT" desc="Permission fragment shown in the permissions prompt when a web page requests access to the computer's camera, including zoom and move.">
Use &amp; move your camera
</message>
<message name="IDS_ACCESSIBILITY_EVENTS_PERMISSION_FRAGMENT" desc="Permission request shown if the user is visiting a site that wants to respond to accessibility events, for example if the user is using a screen reader and the site has custom accessibility features. Follows a prompt: 'This site would like to:'">
Respond to Accessibility Events
......
......@@ -18,7 +18,6 @@ aggregate_vector_icons("components_vector_icons") {
"business.icon",
"call.icon",
"certificate.icon",
"camera_pan_tilt_zoom.icon",
"check_circle.icon",
"close.icon",
"close_rounded.icon",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
CANVAS_DIMENSIONS, 24,
MOVE_TO, 15.54f, 5.54f,
LINE_TO, 13.77f, 7.3f,
LINE_TO, 12, 5.54f,
LINE_TO, 10.23f, 7.3f,
LINE_TO, 8.46f, 5.54f,
LINE_TO, 12, 2,
CLOSE,
R_MOVE_TO, 2.92f, 10,
R_LINE_TO, -1.76f, -1.77f,
LINE_TO, 18.46f, 12,
R_LINE_TO, -1.76f, -1.77f,
R_LINE_TO, 1.76f, -1.77f,
LINE_TO, 22, 12,
CLOSE,
R_MOVE_TO, -10, 2.92f,
R_LINE_TO, 1.77f, -1.76f,
LINE_TO, 12, 18.46f,
R_LINE_TO, 1.77f, -1.76f,
R_LINE_TO, 1.77f, 1.76f,
LINE_TO, 12, 22,
CLOSE,
R_MOVE_TO, -2.92f, -10,
R_LINE_TO, 1.76f, 1.77f,
LINE_TO, 5.54f, 12,
R_LINE_TO, 1.76f, 1.77f,
R_LINE_TO, -1.76f, 1.77f,
LINE_TO, 2, 12,
CLOSE,
CIRCLE, 12, 12, 3
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment