tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 463085f5019080a08eac1a27209d6b12ecc7cfb2
parent dae7b8956a8266711a3a4d90abfd4ffe9010cc4d
Author: Michael Froman <mfroman@mozilla.com>
Date:   Wed,  8 Oct 2025 17:28:17 -0500

Bug 1993083 - Vendor libwebrtc from e7b2586e3b

Upstream commit: https://webrtc.googlesource.com/src/+/e7b2586e3b58f1fe83d047fa07cb8af27fa86e35
    Introduce FullScreenWindowDetector for Window.Graphics.Capture API

    This change defines a FullScreenWindowDetector for window captures while
    using WGC on Windows OS. FullScreenWindowDetector detects if an
    application exists in a full screen mode and if it does, then it tells
    the capturer to capture the full screen version of the application.
    Currently this works only for PowerPoint.

    This feature is protected behind a finch feature which will be landed in
    a follow-up CL in Chromium.

    Bug: chromium:409473386
    Change-Id: I0f2eeaa5dfb00e76f08ab3a264df193cc643042e
    Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/397640
    Reviewed-by: Harald Alvestrand <hta@webrtc.org>
    Commit-Queue: Palak Agarwal <agpalak@google.com>
    Cr-Commit-Position: refs/heads/main@{#45044}

Diffstat:
Mthird_party/libwebrtc/README.mozilla.last-vendor | 4++--
Mthird_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.cc | 18+++++++++++++++++-
Mthird_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.h | 15++++++++++++++-
Mthird_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc | 13+++++++++++--
Mthird_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler_unittest.cc | 17+++++++++++++++++
Mthird_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc | 94++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mthird_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h | 14++++++++++++++
Mthird_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 216 insertions(+), 24 deletions(-)

diff --git a/third_party/libwebrtc/README.mozilla.last-vendor b/third_party/libwebrtc/README.mozilla.last-vendor @@ -1,4 +1,4 @@ # ./mach python dom/media/webrtc/third_party_build/vendor-libwebrtc.py --from-local /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc --commit mozpatches libwebrtc -libwebrtc updated from /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-08T22:26:51.407018+00:00. +libwebrtc updated from /home/mfroman/mozilla/elm/.moz-fast-forward/moz-libwebrtc commit mozpatches on 2025-10-08T22:28:09.782321+00:00. # base of lastest vendoring -08837f6e91 +e7b2586e3b diff --git a/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.cc b/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.cc @@ -17,6 +17,10 @@ #include "modules/desktop_capture/full_screen_application_handler.h" #include "rtc_base/time_utils.h" +#if defined(WEBRTC_WIN) +#include "modules/desktop_capture/win/full_screen_win_application_handler.h" +#endif + namespace webrtc { FullScreenWindowDetector::FullScreenWindowDetector( @@ -38,7 +42,9 @@ DesktopCapturer::SourceId FullScreenWindowDetector::FindFullScreenWindow( void FullScreenWindowDetector::UpdateWindowListIfNeeded( DesktopCapturer::SourceId original_source_id, FunctionView<bool(DesktopCapturer::SourceList*)> get_sources) { - const bool skip_update = previous_source_id_ != original_source_id; + // Don't skip update if app_handler_ exists. + const bool skip_update = + !app_handler_ && (previous_source_id_ != original_source_id); previous_source_id_ = original_source_id; // Here is an attempt to avoid redundant creating application handler in case @@ -89,4 +95,14 @@ void FullScreenWindowDetector::CreateApplicationHandlerIfNeeded( } } +void FullScreenWindowDetector::CreateFullScreenApplicationHandlerForTest( + DesktopCapturer::SourceId source_id) { + if (app_handler_) { + return; + } +#if defined(WEBRTC_WIN) + app_handler_ = std::make_unique<FullScreenPowerPointHandler>(source_id); +#endif +} + } // namespace webrtc diff --git a/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.h b/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.h @@ -62,14 +62,21 @@ class FullScreenWindowDetector static scoped_refptr<FullScreenWindowDetector> CreateFullScreenWindowDetector(); void SetUseHeuristicFullscreenPowerPointWindows( - bool use_heuristic_fullscreen_powerpoint_windows) { + bool use_heuristic_fullscreen_powerpoint_windows, + bool use_heuristic_for_wgc = false) { use_heuristic_fullscreen_powerpoint_windows_ = use_heuristic_fullscreen_powerpoint_windows; + use_heuristic_for_wgc_ = use_heuristic_for_wgc; if (app_handler_) { app_handler_->SetUseHeuristicFullscreenPowerPointWindows( use_heuristic_fullscreen_powerpoint_windows); } } + bool UseHeuristicForWGC() { return use_heuristic_for_wgc_; } + + // Used for tests. + void CreateFullScreenApplicationHandlerForTest( + DesktopCapturer::SourceId source_id); protected: std::unique_ptr<FullScreenApplicationHandler> app_handler_; @@ -85,6 +92,12 @@ class FullScreenWindowDetector // available in stable for some milestones. bool use_heuristic_fullscreen_powerpoint_windows_ = true; + // `use_heuristic_for_wgc_` implements the finch experiment for + // the usage of FullScreenPowerPointHandler class for WGC API. + // TODO(crbug.com/409473386): Remove `use_heuristic_for_wgc_` once + // the feature has been rolled out to Stable for some milestones. + bool use_heuristic_for_wgc_ = false; + int64_t last_update_time_ms_; DesktopCapturer::SourceId previous_source_id_; diff --git a/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc b/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc @@ -124,19 +124,28 @@ DesktopCapturer::SourceId FullScreenPowerPointHandler::FindFullScreenWindow( return 0; } + DesktopCapturer::SourceId full_screen_slide_show_id = 0; const std::string original_document_title = GetDocumentTitleFromEditor(original_window); for (const auto& source : powerpoint_windows) { HWND window = reinterpret_cast<HWND>(source.id); + // If another PowerPoint editor window with the same title exists, then we + // don't use the heuristic as we don't know which editor has opened the + // slide show. + if (GetWindowType(window) == WindowType::kEditor && + GetDocumentTitleFromEditor(window) == original_document_title) { + return 0; + } + // Looking for fullscreen slide show window for the corresponding editor // document. if (GetWindowType(window) == WindowType::kSlideShow && GetDocumentTitleFromSlideShow(window) == original_document_title) { - return source.id; + full_screen_slide_show_id = source.id; } } - return 0; + return full_screen_slide_show_id; } FullScreenPowerPointHandler::WindowType diff --git a/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler_unittest.cc @@ -257,6 +257,7 @@ TEST_F(FullScreenWinApplicationHandlerTest, EXPECT_EQ(FindFullScreenWindow(), correct_slide_show); } +// TODO(crbug.com/409473386): Add DestroyTestWindow to clean the tests. TEST_F(FullScreenWinApplicationHandlerTest, FullScreenWindowsFoundWhenMultipleEditorsAndSlideShowsExist) { std::vector<WindowInfo> editors = { @@ -290,4 +291,20 @@ TEST_F(FullScreenWinApplicationHandlerTest, } } +TEST_F(FullScreenWinApplicationHandlerTest, + FullScreenWindowNotFoundWhenTwoEditorsWithSameTitleExist) { + const WCHAR* editor_title = L"My - Title - PowerPoint"; + CreateEditorWindow(editor_title); + WindowInfo second_editor_window_info = + CreateTestWindow(editor_title, /*window_class=*/L"PPTFrameClass"); + EXPECT_NE(editor_window_info_.hwnd, second_editor_window_info.hwnd); + HWND slide_show = + CreateSlideShowWindow(L"PowerPoint Slide Show - [My - Title]"); + + EXPECT_NE(FindFullScreenWindow(), slide_show); + EXPECT_EQ(FindFullScreenWindow(), reinterpret_cast<HWND>(0)); + + DestroyTestWindow(second_editor_window_info); +} + } // namespace webrtc diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc @@ -115,14 +115,15 @@ bool IsWgcSupported(CaptureType capture_type) { // There is a bug in `CreateForMonitor` that causes a crash if there are no // active displays. The crash was fixed in Win11, but we are still unable // to capture screens without an active display. - if (capture_type == CaptureType::kScreen) + if (capture_type == CaptureType::kScreen) { return false; - + } // There is a bug in the DWM (Desktop Window Manager) that prevents it from // providing image data if there are no displays attached. This was fixed in // Windows 11. - if (rtc_win::GetVersion() < rtc_win::Version::VERSION_WIN11) + if (rtc_win::GetVersion() < rtc_win::Version::VERSION_WIN11) { return false; + } } // A bug in the WGC API `CreateForMonitor` prevents capturing the entire @@ -134,8 +135,9 @@ bool IsWgcSupported(CaptureType capture_type) { return false; } - if (!ResolveCoreWinRTDelayload()) + if (!ResolveCoreWinRTDelayload()) { return false; + } // We need to check if the WGC APIs are present on the system. Certain SKUs // of Windows ship without these APIs. @@ -145,32 +147,37 @@ bool IsWgcSupported(CaptureType capture_type) { ABI::Windows::Foundation::Metadata::IApiInformationStatics, RuntimeClass_Windows_Foundation_Metadata_ApiInformation>( &api_info_statics); - if (FAILED(hr)) + if (FAILED(hr)) { return false; + } HSTRING api_contract; hr = CreateHstring(kApiContract, wcslen(kApiContract), &api_contract); - if (FAILED(hr)) + if (FAILED(hr)) { return false; + } boolean is_api_present; hr = api_info_statics->IsApiContractPresentByMajor( api_contract, kRequiredApiContractVersion, &is_api_present); DeleteHstring(api_contract); - if (FAILED(hr) || !is_api_present) + if (FAILED(hr) || !is_api_present) { return false; + } HSTRING wgc_session_type; hr = CreateHstring(kWgcSessionType, wcslen(kWgcSessionType), &wgc_session_type); - if (FAILED(hr)) + if (FAILED(hr)) { return false; + } boolean is_type_present; hr = api_info_statics->IsTypePresent(wgc_session_type, &is_type_present); DeleteHstring(wgc_session_type); - if (FAILED(hr) || !is_type_present) + if (FAILED(hr) || !is_type_present) { return false; + } // If the APIs are present, we need to check that they are supported. ComPtr<WGC::IGraphicsCaptureSessionStatics> capture_session_statics; @@ -178,13 +185,15 @@ bool IsWgcSupported(CaptureType capture_type) { WGC::IGraphicsCaptureSessionStatics, RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureSession>( &capture_session_statics); - if (FAILED(hr)) + if (FAILED(hr)) { return false; + } boolean is_supported; hr = capture_session_statics->IsSupported(&is_supported); - if (FAILED(hr) || !is_supported) + if (FAILED(hr) || !is_supported) { return false; + } return true; } @@ -197,9 +206,11 @@ WgcCapturerWin::WgcCapturerWin( : options_(options), source_factory_(std::move(source_factory)), source_enumerator_(std::move(source_enumerator)), - allow_delayed_capturable_check_(allow_delayed_capturable_check) { - if (!core_messaging_library_) + allow_delayed_capturable_check_(allow_delayed_capturable_check), + full_screen_window_detector_(options.full_screen_window_detector()) { + if (!core_messaging_library_) { core_messaging_library_ = LoadLibraryW(kCoreMessagingDll); + } if (core_messaging_library_) { create_dispatcher_queue_controller_func_ = @@ -210,8 +221,9 @@ WgcCapturerWin::WgcCapturerWin( } WgcCapturerWin::~WgcCapturerWin() { - if (core_messaging_library_) + if (core_messaging_library_) { FreeLibrary(core_messaging_library_); + } } // static @@ -238,16 +250,41 @@ bool WgcCapturerWin::GetSourceList(SourceList* sources) { } bool WgcCapturerWin::SelectSource(DesktopCapturer::SourceId id) { - capture_source_ = source_factory_->CreateCaptureSource(id); - if (allow_delayed_capturable_check_) + selected_source_id_ = id; + + // Use `full_screen_window_detector_` to check if there is a corresponding + // full screen window for the `selected_source_id_`. + const DesktopCapturer::SourceId full_screen_source_id = + full_screen_window_detector_ && + full_screen_window_detector_->UseHeuristicForWGC() + ? full_screen_window_detector_->FindFullScreenWindow(id) + : 0; + + // `capture_id` represents the SourceId used to create the `capture_source_`, + // which is the module responsible for capturing the frames. + auto capture_id = full_screen_source_id ? full_screen_source_id : id; + if (capture_id != id && !fullscreen_usage_logged_) { + // Log the usage of FullScreenDetector only once and only if it's + // successful. + fullscreen_usage_logged_ = true; + LogDesktopCapturerFullscreenDetectorUsage(); + } + + if (!capture_source_ || capture_source_->GetSourceId() != capture_id) { + capture_source_ = source_factory_->CreateCaptureSource(capture_id); + } + + if (allow_delayed_capturable_check_) { return true; + } return capture_source_->IsCapturable(); } bool WgcCapturerWin::FocusOnSelectedSource() { - if (!capture_source_) + if (!capture_source_) { return false; + } return capture_source_->FocusOnSource(); } @@ -309,6 +346,15 @@ void WgcCapturerWin::CaptureFrame() { return; } + // Feed the actual list of windows into full screen window detector. + if (full_screen_window_detector_) { + full_screen_window_detector_->UpdateWindowListIfNeeded( + selected_source_id_, [this](DesktopCapturer::SourceList* sources) { + return GetSourceList(sources); + }); + SelectSource(selected_source_id_); + } + HRESULT hr; if (!dispatcher_queue_created_) { // Set the apartment type to NONE because this thread should already be COM @@ -407,10 +453,22 @@ void WgcCapturerWin::CaptureFrame() { bool WgcCapturerWin::IsSourceBeingCaptured(DesktopCapturer::SourceId id) { std::map<DesktopCapturer::SourceId, WgcCaptureSession>::iterator session_iter = ongoing_captures_.find(id); - if (session_iter == ongoing_captures_.end()) + if (session_iter == ongoing_captures_.end()) { return false; + } return session_iter->second.IsCaptureStarted(); } +void WgcCapturerWin::SetUpFullScreenDetectorForTest( + bool use_heuristic, + DesktopCapturer::SourceId source_id) { + if (full_screen_window_detector_) { + full_screen_window_detector_->SetUseHeuristicFullscreenPowerPointWindows( + /*use_heuristic_fullscreen_powerpoint_windows=*/true, use_heuristic); + full_screen_window_detector_->CreateFullScreenApplicationHandlerForTest( + source_id); + } +} + } // namespace webrtc diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h @@ -111,6 +111,8 @@ class WgcCapturerWin : public DesktopCapturer { // Used in WgcCapturerTests. bool IsSourceBeingCaptured(SourceId id); + void SetUpFullScreenDetectorForTest(bool use_heuristic, + DesktopCapturer::SourceId source_id); private: typedef HRESULT(WINAPI* CreateDispatcherQueueControllerFunc)( @@ -144,6 +146,11 @@ class WgcCapturerWin : public DesktopCapturer { // if the source is capturable and it creates the GraphicsCaptureItem for us. std::unique_ptr<WgcCaptureSource> capture_source_; + // DesktopCapturer::SourceId of the source that was selected by the user. It + // might not necessarily correspond to the `capture_source_` when the + // `full_screen_window_detector_` is active. + DesktopCapturer::SourceId selected_source_id_; + // A map of all the sources we are capturing and the associated // WgcCaptureSession. Frames for the current source (indicated via // SelectSource) will be retrieved from the appropriate session when @@ -164,6 +171,13 @@ class WgcCapturerWin : public DesktopCapturer { // A Direct3D11 device that is shared amongst the WgcCaptureSessions, who // require one to perform the capture. Microsoft::WRL::ComPtr<::ID3D11Device> d3d11_device_; + + // This allows us to find full screen windows for applications in some + // specific cases. + scoped_refptr<FullScreenWindowDetector> full_screen_window_detector_; + + // Used to make sure that we only log the usage of fullscreen detection once. + bool fullscreen_usage_logged_ = false; }; } // namespace webrtc diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc @@ -54,6 +54,9 @@ constexpr int kSourceClosed = 1; constexpr char kCaptureTimeHistogram[] = "WebRTC.DesktopCapture.Win.WgcCapturerFrameTime"; +constexpr char kCaptureFullscreenDetectorHistogram[] = + "WebRTC.Screenshare.DesktopCapturerFullscreenDetector"; + // The capturer keeps `kNumBuffers` in its frame pool, so we need to request // that many frames to clear those out. The next frame will have the new size // (if the size has changed) so we will resize the frame pool at this point. @@ -437,6 +440,68 @@ TEST_F(WgcCapturerWindowTest, FocusOnWindow) { DestroyTestWindow(window_info_); } +TEST_F(WgcCapturerWindowTest, FullScreenWindowDetector) { + capturer_ = WgcCapturerWin::CreateRawWindowCapturer( + DesktopCaptureOptions::CreateDefault()); + + WindowInfo editor_info = CreateTestWindow( + L"My - Title - PowerPoint", /*height=*/240, /*width=*/320, + /*extended_styles=*/0, /*window_class=*/L"PPTFrameClass"); + WindowInfo slide_show_info = CreateTestWindow( + L"PowerPoint Slide Show - [My - Title]", /*height=*/240, /*width=*/320, + /*extended_styles=*/0, /*window_class=*/L"screenClass"); + + auto* wgc_capturer = static_cast<WgcCapturerWin*>(capturer_.get()); + wgc_capturer->SetUpFullScreenDetectorForTest( + /*use_heuristic=*/true, + reinterpret_cast<DesktopCapturer::SourceId>(editor_info.hwnd)); + + EXPECT_TRUE(capturer_->SelectSource( + reinterpret_cast<DesktopCapturer::SourceId>(editor_info.hwnd))); + capturer_->Start(this); + DoCapture(); + + EXPECT_FALSE(wgc_capturer->IsSourceBeingCaptured( + reinterpret_cast<DesktopCapturer::SourceId>(editor_info.hwnd))); + EXPECT_TRUE(wgc_capturer->IsSourceBeingCaptured( + reinterpret_cast<DesktopCapturer::SourceId>(slide_show_info.hwnd))); + EXPECT_EQ(metrics::NumEvents(kCaptureFullscreenDetectorHistogram, true), 1); + + DestroyTestWindow(editor_info); + DestroyTestWindow(slide_show_info); +} + +TEST_F(WgcCapturerWindowTest, FullScreenWindowDetectorDoesNotWorkByDefault) { + capturer_ = WgcCapturerWin::CreateRawWindowCapturer( + DesktopCaptureOptions::CreateDefault()); + + WindowInfo editor_info = CreateTestWindow( + L"My - Title - PowerPoint", /*height=*/240, /*width=*/320, + /*extended_styles=*/0, /*window_class=*/L"PPTFrameClass"); + WindowInfo slide_show_info = CreateTestWindow( + L"PowerPoint Slide Show - [My - Title]", /*height=*/240, /*width=*/320, + /*extended_styles=*/0, /*window_class=*/L"screenClass"); + + auto* wgc_capturer = static_cast<WgcCapturerWin*>(capturer_.get()); + // The default behavior on WGC capturer of `use_heuristic` is false. + wgc_capturer->SetUpFullScreenDetectorForTest( + /*use_heuristic=*/false, + reinterpret_cast<DesktopCapturer::SourceId>(editor_info.hwnd)); + + EXPECT_TRUE(capturer_->SelectSource( + reinterpret_cast<DesktopCapturer::SourceId>(editor_info.hwnd))); + capturer_->Start(this); + DoCapture(); + + EXPECT_TRUE(wgc_capturer->IsSourceBeingCaptured( + reinterpret_cast<DesktopCapturer::SourceId>(editor_info.hwnd))); + EXPECT_FALSE(wgc_capturer->IsSourceBeingCaptured( + reinterpret_cast<DesktopCapturer::SourceId>(slide_show_info.hwnd))); + EXPECT_EQ(metrics::NumEvents(kCaptureFullscreenDetectorHistogram, true), 0); + + DestroyTestWindow(editor_info); + DestroyTestWindow(slide_show_info); +} TEST_F(WgcCapturerWindowTest, SelectMinimizedWindow) { SetUpForWindowCapture(); MinimizeTestWindow(reinterpret_cast<HWND>(source_id_));