From 64a5754959e10e7dc89e5b6fad4aba5d60270d66 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Fri, 8 Jul 2022 07:40:35 +0000 Subject: [PATCH] chrome: Add support for OnRequestMediaAccessPermission callback (see issue #2582) --- include/capi/cef_permission_handler_capi.h | 10 +- include/cef_permission_handler.h | 6 +- .../browser/alloy/alloy_browser_host_impl.cc | 6 +- libcef/browser/chrome/browser_delegate.h | 9 + .../browser/chrome/chrome_browser_delegate.cc | 14 + .../browser/chrome/chrome_browser_delegate.h | 4 + libcef/browser/media_access_query.cc | 39 ++- libcef/browser/media_access_query.h | 10 +- patch/patches/chrome_browser_browser.patch | 22 +- tests/ceftests/media_access_unittest.cc | 261 +++++++++++++----- 10 files changed, 282 insertions(+), 99 deletions(-) diff --git a/include/capi/cef_permission_handler_capi.h b/include/capi/cef_permission_handler_capi.h index b5b61fd0d..fb5dabf62 100644 --- a/include/capi/cef_permission_handler_capi.h +++ b/include/capi/cef_permission_handler_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=5a39566f586c012271d96c7d42337a30bf98e6b8$ +// $hash=e902fe011f8667b64989e57ad9e72aec74b22015$ // #ifndef CEF_INCLUDE_CAPI_CEF_PERMISSION_HANDLER_CAPI_H_ @@ -106,9 +106,11 @@ typedef struct _cef_permission_handler_t { // combination of values from cef_media_access_permission_types_t that // represent the requested permissions. Return true (1) and call // cef_media_access_callback_t functions either in this function or at a later - // time to continue or cancel the request. Return false (0) to cancel the - // request immediately. This function will not be called if the "--enable- - // media-stream" command-line switch is used to grant all permissions. + // time to continue or cancel the request. Return false (0) to proceed with + // default handling. With the Chrome runtime, default handling will display + // the permission request UI. With the Alloy runtime, default handling will + // deny the request. This function will not be called if the "--enable-media- + // stream" command-line switch is used to grant all permissions. /// int(CEF_CALLBACK* on_request_media_access_permission)( struct _cef_permission_handler_t* self, diff --git a/include/cef_permission_handler.h b/include/cef_permission_handler.h index 09b587308..5d0044619 100644 --- a/include/cef_permission_handler.h +++ b/include/cef_permission_handler.h @@ -92,8 +92,10 @@ class CefPermissionHandler : public virtual CefBaseRefCounted { // combination of values from cef_media_access_permission_types_t that // represent the requested permissions. Return true and call // CefMediaAccessCallback methods either in this method or at a later time to - // continue or cancel the request. Return false to cancel the request - // immediately. This method will not be called if the "--enable-media-stream" + // continue or cancel the request. Return false to proceed with default + // handling. With the Chrome runtime, default handling will display the + // permission request UI. With the Alloy runtime, default handling will deny + // the request. This method will not be called if the "--enable-media-stream" // command-line switch is used to grant all permissions. /// /*--cef()--*/ diff --git a/libcef/browser/alloy/alloy_browser_host_impl.cc b/libcef/browser/alloy/alloy_browser_host_impl.cc index f6802b1de..fb4b0e9d6 100644 --- a/libcef/browser/alloy/alloy_browser_host_impl.cc +++ b/libcef/browser/alloy/alloy_browser_host_impl.cc @@ -1298,8 +1298,10 @@ void AlloyBrowserHostImpl::RequestMediaAccessPermission( content::WebContents* web_contents, const content::MediaStreamRequest& request, content::MediaResponseCallback callback) { - media_access_query::RequestMediaAccessPermission(this, request, - std::move(callback)); + auto returned_callback = media_access_query::RequestMediaAccessPermission( + this, request, std::move(callback), /*default_disallow=*/true); + // Callback should not be returned. + DCHECK(returned_callback.is_null()); } bool AlloyBrowserHostImpl::CheckMediaAccessPermission( diff --git a/libcef/browser/chrome/browser_delegate.h b/libcef/browser/chrome/browser_delegate.h index 4aa4abde8..258cbb30e 100644 --- a/libcef/browser/chrome/browser_delegate.h +++ b/libcef/browser/chrome/browser_delegate.h @@ -55,6 +55,15 @@ class BrowserDelegate : public content::WebContentsDelegate { WindowOpenDisposition disposition) { return false; } + + // Same as RequestMediaAccessPermission but returning |callback| if the + // request is unhandled. + [[nodiscard]] virtual content::MediaResponseCallback + RequestMediaAccessPermissionEx(content::WebContents* web_contents, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback) { + return callback; + } }; } // namespace cef diff --git a/libcef/browser/chrome/chrome_browser_delegate.cc b/libcef/browser/chrome/chrome_browser_delegate.cc index 298e6c9c3..d5b6892f7 100644 --- a/libcef/browser/chrome/chrome_browser_delegate.cc +++ b/libcef/browser/chrome/chrome_browser_delegate.cc @@ -11,6 +11,7 @@ #include "libcef/browser/browser_info_manager.h" #include "libcef/browser/browser_platform_delegate.h" #include "libcef/browser/chrome/chrome_browser_host_impl.h" +#include "libcef/browser/media_access_query.h" #include "libcef/browser/request_context_impl.h" #include "libcef/common/app_manager.h" #include "libcef/common/frame_util.h" @@ -102,6 +103,19 @@ bool ChromeBrowserDelegate::HandleCommand(int command_id, return false; } +content::MediaResponseCallback +ChromeBrowserDelegate::RequestMediaAccessPermissionEx( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback) { + if (auto browser = ChromeBrowserHostImpl::GetBrowserForBrowser(browser_)) { + return media_access_query::RequestMediaAccessPermission( + browser.get(), request, std::move(callback), + /*default_disallow=*/false); + } + return callback; +} + void ChromeBrowserDelegate::WebContentsCreated( content::WebContents* source_contents, int opener_render_process_id, diff --git a/libcef/browser/chrome/chrome_browser_delegate.h b/libcef/browser/chrome/chrome_browser_delegate.h index 0f9d45156..77455f728 100644 --- a/libcef/browser/chrome/chrome_browser_delegate.h +++ b/libcef/browser/chrome/chrome_browser_delegate.h @@ -54,6 +54,10 @@ class ChromeBrowserDelegate : public cef::BrowserDelegate { bool ShowStatusBubble(bool show_by_default) override; bool HandleCommand(int command_id, WindowOpenDisposition disposition) override; + [[nodiscard]] content::MediaResponseCallback RequestMediaAccessPermissionEx( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback) override; // WebContentsDelegate methods: void WebContentsCreated(content::WebContents* source_contents, diff --git a/libcef/browser/media_access_query.cc b/libcef/browser/media_access_query.cc index d9e92a00f..82498f388 100644 --- a/libcef/browser/media_access_query.cc +++ b/libcef/browser/media_access_query.cc @@ -10,6 +10,7 @@ #include "libcef/browser/media_stream_registrar.h" #include "libcef/common/cef_switches.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h" @@ -59,6 +60,10 @@ class CefMediaAccessQuery { return requested_permissions; } + [[nodiscard]] CallbackType DisconnectCallback() { + return std::move(callback_); + } + void ExecuteCallback(uint32_t allowed_permissions) { CEF_REQUIRE_UIT(); @@ -267,6 +272,7 @@ class CefMediaAccessCallbackImpl : public CefMediaAccessCallback { void Cancel() override { Continue(CEF_MEDIA_PERMISSION_NONE); } [[nodiscard]] CallbackType Disconnect() { return std::move(callback_); } + bool IsDisconnected() const { return callback_.is_null(); } private: static void RunNow(CallbackType callback, uint32_t allowed_permissions) { @@ -294,9 +300,11 @@ bool CheckMediaAccessPermission(CefBrowserHostBase* browser, return true; } -void RequestMediaAccessPermission(CefBrowserHostBase* browser, - const content::MediaStreamRequest& request, - content::MediaResponseCallback callback) { +content::MediaResponseCallback RequestMediaAccessPermission( + CefBrowserHostBase* browser, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback, + bool default_disallow) { CEF_REQUIRE_UIT(); CefMediaAccessQuery query(browser, request, std::move(callback)); @@ -304,10 +312,10 @@ void RequestMediaAccessPermission(CefBrowserHostBase* browser, if (CheckCommandLinePermission()) { // Allow all requested permissions. query.ExecuteCallback(query.requested_permissions()); - return; + return base::NullCallback(); } - bool proceed = false; + bool handled = false; if (auto client = browser->GetClient()) { if (auto handler = client->GetPermissionHandler()) { @@ -320,18 +328,29 @@ void RequestMediaAccessPermission(CefBrowserHostBase* browser, request.render_process_id, request.render_frame_id)); if (!frame) frame = browser->GetMainFrame(); - proceed = handler->OnRequestMediaAccessPermission( + handled = handler->OnRequestMediaAccessPermission( browser, frame, request.security_origin.spec(), requested_permissions, callbackImpl.get()); - if (!proceed) + if (!handled) { + LOG_IF(ERROR, callbackImpl->IsDisconnected()) + << "Should return true from OnRequestMediaAccessPermission when " + "executing the callback"; query = callbackImpl->Disconnect(); + } } } - if (!proceed && !query.is_null()) { - // Disallow access by default. - query.ExecuteCallback(CEF_MEDIA_PERMISSION_NONE); + if (!query.is_null()) { + if (default_disallow && !handled) { + // Disallow access by default. + query.ExecuteCallback(CEF_MEDIA_PERMISSION_NONE); + } else { + // Proceed with default handling. + return query.DisconnectCallback(); + } } + + return base::NullCallback(); } } // namespace media_access_query diff --git a/libcef/browser/media_access_query.h b/libcef/browser/media_access_query.h index 70283b438..1af6c2668 100644 --- a/libcef/browser/media_access_query.h +++ b/libcef/browser/media_access_query.h @@ -24,9 +24,13 @@ bool CheckMediaAccessPermission(CefBrowserHostBase* browser, blink::mojom::MediaStreamType type); // Called from WebContentsDelegate::RequestMediaAccessPermission. -void RequestMediaAccessPermission(CefBrowserHostBase* browser, - const content::MediaStreamRequest& request, - content::MediaResponseCallback callback); +// |callback| will be returned if the request is unhandled and +// |default_disallow| is false. +[[nodiscard]] content::MediaResponseCallback RequestMediaAccessPermission( + CefBrowserHostBase* browser, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback, + bool default_disallow); } // namespace media_access_query diff --git a/patch/patches/chrome_browser_browser.patch b/patch/patches/chrome_browser_browser.patch index b265dce42..6f9d02553 100644 --- a/patch/patches/chrome_browser_browser.patch +++ b/patch/patches/chrome_browser_browser.patch @@ -52,7 +52,7 @@ index 583a685cd125a..bef2272570eb1 100644 ] } diff --git chrome/browser/ui/browser.cc chrome/browser/ui/browser.cc -index 63d64acc72318..fd3cb52daf611 100644 +index 63d64acc72318..21f0a0b81f996 100644 --- chrome/browser/ui/browser.cc +++ chrome/browser/ui/browser.cc @@ -264,6 +264,25 @@ @@ -254,7 +254,23 @@ index 63d64acc72318..fd3cb52daf611 100644 } bool Browser::IsFullscreenForTabOrPending(const WebContents* web_contents) { -@@ -2664,13 +2763,20 @@ void Browser::RemoveScheduledUpdatesFor(WebContents* contents) { +@@ -2127,6 +2226,15 @@ void Browser::RequestMediaAccessPermission( + content::WebContents* web_contents, + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback) { ++#if BUILDFLAG(ENABLE_CEF) ++ if (cef_browser_delegate_) { ++ callback = cef_browser_delegate_->RequestMediaAccessPermissionEx( ++ web_contents, request, std::move(callback)); ++ if (callback.is_null()) ++ return; ++ } ++#endif ++ + const extensions::Extension* extension = + GetExtensionForOrigin(profile_, request.security_origin); + MediaCaptureDevicesDispatcher::GetInstance()->ProcessMediaAccessRequest( +@@ -2664,13 +2772,20 @@ void Browser::RemoveScheduledUpdatesFor(WebContents* contents) { // Browser, Getters for UI (private): StatusBubble* Browser::GetStatusBubble() { @@ -276,7 +292,7 @@ index 63d64acc72318..fd3cb52daf611 100644 return window_ ? window_->GetStatusBubble() : nullptr; } -@@ -2797,6 +2903,8 @@ void Browser::SetAsDelegate(WebContents* web_contents, bool set_delegate) { +@@ -2797,6 +2912,8 @@ void Browser::SetAsDelegate(WebContents* web_contents, bool set_delegate) { content_translate_driver->RemoveTranslationObserver(this); BookmarkTabHelper::FromWebContents(web_contents)->RemoveObserver(this); } diff --git a/tests/ceftests/media_access_unittest.cc b/tests/ceftests/media_access_unittest.cc index afa2f2fd8..6321eb1dd 100644 --- a/tests/ceftests/media_access_unittest.cc +++ b/tests/ceftests/media_access_unittest.cc @@ -13,6 +13,7 @@ #include "include/wrapper/cef_stream_resource_handler.h" #include "tests/ceftests/test_handler.h" #include "tests/ceftests/test_suite.h" +#include "tests/ceftests/test_util.h" #include "tests/gtest/include/gtest/gtest.h" #include "tests/shared/browser/client_app_browser.h" @@ -22,6 +23,8 @@ namespace { const char kMediaUrl[] = "https://media-access-test/media.html"; const char kMediaOrigin[] = "https://media-access-test/"; +constexpr char kMediaNavUrl[] = "https://media-access-test/nav.html"; + // Browser-side app delegate. class MediaAccessBrowserTest : public client::ClientAppBrowser::Delegate, public CefPermissionHandler { @@ -44,14 +47,35 @@ class TestSetup { public: TestSetup() {} + // CONFIGURATION + + // Deny the prompt by returning false in OnRequestMediaAccessPermission. bool deny_implicitly = false; + + // Deny the prompt by returning true in OnRequestMediaAccessPermission but + // then never calling CefMediaAccessCallback::Continue. + bool deny_with_navigation = false; + + // Don't synchronously execute the callback in OnRequestMediaAccessPermission. bool continue_async = false; + // RESULTS + + // Method callbacks. TrackCallback got_request; - TrackCallback got_success; + TrackCallback got_change; + + // JS success state. + TrackCallback got_js_success; TrackCallback got_audio; TrackCallback got_video; - TrackCallback got_change; + + // JS error state. + TrackCallback got_js_error; + std::string js_error_str; + + // JS timeout state. + TrackCallback got_js_timeout; }; class MediaAccessTestHandler : public TestHandler, public CefPermissionHandler { @@ -67,26 +91,25 @@ class MediaAccessTestHandler : public TestHandler, public CefPermissionHandler { std::string newUrl = request->GetURL(); if (newUrl.find("tests/exit") != std::string::npos) { if (newUrl.find("SUCCESS") != std::string::npos) { - EXPECT_FALSE(test_setup_->got_success); - test_setup_->got_success.yes(); + EXPECT_FALSE(test_setup_->got_js_success); + test_setup_->got_js_success.yes(); - std::string data_string = newUrl.substr(newUrl.find("&data=") + - std::string("&data=").length()); - std::string data_string_decoded = CefURIDecode( - data_string, false, - static_cast( - UU_SPACES | UU_URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)); - auto obj = CefParseJSON(data_string_decoded, - JSON_PARSER_ALLOW_TRAILING_COMMAS); - CefRefPtr data = obj->GetDictionary(); - const auto got_video = data->GetBool("got_video_track"); - const auto got_audio = data->GetBool("got_audio_track"); - if (got_video) { + auto dict = ParseURLData(newUrl); + if (dict->GetBool("got_video_track")) { test_setup_->got_video.yes(); } - if (got_audio) { + if (dict->GetBool("got_audio_track")) { test_setup_->got_audio.yes(); } + } else if (newUrl.find("ERROR") != std::string::npos) { + EXPECT_FALSE(test_setup_->got_js_error); + test_setup_->got_js_error.yes(); + + auto dict = ParseURLData(newUrl); + test_setup_->js_error_str = dict->GetString("error_str"); + } else if (newUrl.find("TIMEOUT") != std::string::npos) { + EXPECT_FALSE(test_setup_->got_js_timeout); + test_setup_->got_js_timeout.yes(); } DestroyTest(); return RV_CANCEL; @@ -101,7 +124,7 @@ class MediaAccessTestHandler : public TestHandler, public CefPermissionHandler { "" "MEDIA ACCESS TEST"; @@ -137,6 +173,11 @@ class MediaAccessTestHandler : public TestHandler, public CefPermissionHandler { AddResource(kMediaUrl, page, "text/html"); + if (test_setup_->deny_with_navigation) { + AddResource(kMediaNavUrl, "Navigated", + "text/html"); + } + // Create the browser. CreateBrowser(kMediaUrl, request_context); @@ -148,6 +189,16 @@ class MediaAccessTestHandler : public TestHandler, public CefPermissionHandler { return this; } + void OnLoadEnd(CefRefPtr browser, + CefRefPtr frame, + int httpStatusCode) override { + if (test_setup_->deny_with_navigation) { + if (frame->GetURL().ToString() == kMediaNavUrl) { + DestroyTest(); + } + } + } + bool OnRequestMediaAccessPermission( CefRefPtr browser, CefRefPtr frame, @@ -167,6 +218,12 @@ class MediaAccessTestHandler : public TestHandler, public CefPermissionHandler { return false; } + if (test_setup_->deny_with_navigation) { + // Handle the request, but never execute the callback. + callback_ = callback; + return true; + } + if (test_setup_->continue_async) { CefPostTask(TID_UI, base::BindOnce(&CefMediaAccessCallback::Continue, callback, response_)); @@ -186,6 +243,23 @@ class MediaAccessTestHandler : public TestHandler, public CefPermissionHandler { test_setup_->got_change.yes(); } + void DestroyTest() override { + callback_ = nullptr; + + const size_t js_outcome_ct = test_setup_->got_js_success + + test_setup_->got_js_error + + test_setup_->got_js_timeout; + if (test_setup_->deny_with_navigation) { + // Expect no JS outcome. + EXPECT_EQ(0U, js_outcome_ct); + } else { + // Expect a single JS outcome. + EXPECT_EQ(1U, js_outcome_ct); + } + + TestHandler::DestroyTest(); + } + private: bool want_audio_device() const { return request_ & CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE; @@ -213,10 +287,25 @@ class MediaAccessTestHandler : public TestHandler, public CefPermissionHandler { return response_ & CEF_MEDIA_PERMISSION_DESKTOP_VIDEO_CAPTURE; } + CefRefPtr ParseURLData(const std::string& url) { + const std::string& find_str = "&data="; + const std::string& data_string = + url.substr(url.find(find_str) + std::string(find_str).length()); + const std::string& data_string_decoded = CefURIDecode( + data_string, false, + static_cast( + UU_SPACES | UU_URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS)); + auto obj = + CefParseJSON(data_string_decoded, JSON_PARSER_ALLOW_TRAILING_COMMAS); + return obj->GetDictionary(); + } + TestSetup* const test_setup_; const uint32 request_; const uint32 response_; + CefRefPtr callback_; + IMPLEMENT_REFCOUNTING(MediaAccessTestHandler); }; } // namespace @@ -235,9 +324,31 @@ TEST(MediaAccessTest, DeviceFailureWhenReturningFalse) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + if (IsChromeRuntimeEnabled()) { + // Chrome shows a UI prompt, so we time out. + EXPECT_TRUE(test_setup.got_js_timeout); + } else { + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Permission denied", + test_setup.js_error_str.c_str()); + } + EXPECT_FALSE(test_setup.got_change); +} + +TEST(MediaAccessTest, DeviceFailureWhenNoCallback) { + TestSetup test_setup; + test_setup.deny_with_navigation = true; + + CefRefPtr handler = + new MediaAccessTestHandler(&test_setup, + CEF_MEDIA_PERMISSION_DEVICE_AUDIO_CAPTURE | + CEF_MEDIA_PERMISSION_DEVICE_VIDEO_CAPTURE, + CEF_MEDIA_PERMISSION_NONE); + handler->ExecuteTest(); + ReleaseAndWaitForDestructor(handler); + + // No JS result. + EXPECT_TRUE(test_setup.got_request); EXPECT_FALSE(test_setup.got_change); } @@ -253,9 +364,9 @@ TEST(MediaAccessTest, DeviceFailureWhenReturningNoPermission) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Permission denied", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -272,9 +383,9 @@ TEST(MediaAccessTest, DeviceFailureWhenReturningNoPermissionAsync) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Permission denied", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -288,9 +399,9 @@ TEST(MediaAccessTest, DeviceFailureWhenRequestingAudioButReturningVideo) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -304,9 +415,9 @@ TEST(MediaAccessTest, DeviceFailureWhenRequestingVideoButReturningAudio) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -322,9 +433,9 @@ TEST(MediaAccessTest, DevicePartialFailureReturningVideo) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -340,9 +451,9 @@ TEST(MediaAccessTest, DevicePartialFailureReturningAudio) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -358,9 +469,9 @@ TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture1) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -376,9 +487,9 @@ TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture2) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -392,9 +503,9 @@ TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture3) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -408,9 +519,9 @@ TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture4) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -424,9 +535,9 @@ TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture5) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -440,9 +551,9 @@ TEST(MediaAccessTest, DeviceFailureWhenReturningScreenCapture6) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -456,7 +567,7 @@ TEST(MediaAccessTest, DeviceSuccessAudioOnly) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_TRUE(test_setup.got_success); + EXPECT_TRUE(test_setup.got_js_success); EXPECT_TRUE(test_setup.got_audio); EXPECT_FALSE(test_setup.got_video); EXPECT_TRUE(test_setup.got_change); @@ -472,7 +583,7 @@ TEST(MediaAccessTest, DeviceSuccessVideoOnly) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_TRUE(test_setup.got_success); + EXPECT_TRUE(test_setup.got_js_success); EXPECT_FALSE(test_setup.got_audio); EXPECT_TRUE(test_setup.got_video); EXPECT_TRUE(test_setup.got_change); @@ -491,7 +602,7 @@ TEST(MediaAccessTest, DeviceSuccessAudioVideo) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_TRUE(test_setup.got_success); + EXPECT_TRUE(test_setup.got_js_success); EXPECT_TRUE(test_setup.got_audio); EXPECT_TRUE(test_setup.got_video); EXPECT_TRUE(test_setup.got_change); @@ -511,7 +622,7 @@ TEST(MediaAccessTest, DeviceSuccessAudioVideoAsync) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_TRUE(test_setup.got_success); + EXPECT_TRUE(test_setup.got_js_success); EXPECT_TRUE(test_setup.got_audio); EXPECT_TRUE(test_setup.got_video); EXPECT_TRUE(test_setup.got_change); @@ -530,9 +641,9 @@ TEST(MediaAccessTest, DesktopFailureWhenReturningNoPermission) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Permission denied", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -546,9 +657,9 @@ TEST(MediaAccessTest, DesktopFailureWhenRequestingVideoButReturningAudio) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); } @@ -564,7 +675,7 @@ TEST(MediaAccessTest, DesktopPartialSuccessReturningVideo) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_TRUE(test_setup.got_success); + EXPECT_TRUE(test_setup.got_js_success); EXPECT_FALSE(test_setup.got_audio); EXPECT_TRUE(test_setup.got_video); EXPECT_TRUE(test_setup.got_change); @@ -581,9 +692,9 @@ TEST(MediaAccessTest, DesktopPartialFailureReturningAudio) { ReleaseAndWaitForDestructor(handler); EXPECT_TRUE(test_setup.got_request); - EXPECT_FALSE(test_setup.got_success); - EXPECT_FALSE(test_setup.got_audio); - EXPECT_FALSE(test_setup.got_video); + EXPECT_TRUE(test_setup.got_js_error); + EXPECT_STREQ("NotAllowedError: Invalid state", + test_setup.js_error_str.c_str()); EXPECT_FALSE(test_setup.got_change); }