tor-browser

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

window_capturer_mac.mm (7856B)


      1 /*
      2 *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
      3 *
      4 *  Use of this source code is governed by a BSD-style license
      5 *  that can be found in the LICENSE file in the root of the source
      6 *  tree. An additional intellectual property rights grant can be found
      7 *  in the file PATENTS.  All contributing project authors may
      8 *  be found in the AUTHORS file in the root of the source tree.
      9 */
     10 
     11 #include <ApplicationServices/ApplicationServices.h>
     12 #include <Cocoa/Cocoa.h>
     13 #include <CoreFoundation/CoreFoundation.h>
     14 
     15 #include <utility>
     16 
     17 #include "api/scoped_refptr.h"
     18 #include "modules/desktop_capture/desktop_capture_options.h"
     19 #include "modules/desktop_capture/desktop_capturer.h"
     20 #include "modules/desktop_capture/desktop_frame.h"
     21 #include "modules/desktop_capture/mac/desktop_configuration.h"
     22 #include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
     23 #include "modules/desktop_capture/mac/desktop_frame_cgimage.h"
     24 #include "modules/desktop_capture/mac/window_list_utils.h"
     25 #include "modules/desktop_capture/window_finder_mac.h"
     26 #include "rtc_base/checks.h"
     27 #include "rtc_base/logging.h"
     28 #include "rtc_base/trace_event.h"
     29 
     30 namespace webrtc {
     31 
     32 namespace {
     33 
     34 // Returns true if the window exists.
     35 bool IsWindowValid(CGWindowID id) {
     36  CFArrayRef window_id_array =
     37      CFArrayCreate(nullptr, reinterpret_cast<const void**>(&id), 1, nullptr);
     38  CFArrayRef window_array =
     39      CGWindowListCreateDescriptionFromArray(window_id_array);
     40  bool valid = window_array && CFArrayGetCount(window_array);
     41  CFRelease(window_id_array);
     42  CFRelease(window_array);
     43 
     44  return valid;
     45 }
     46 
     47 class WindowCapturerMac : public DesktopCapturer {
     48 public:
     49  explicit WindowCapturerMac(
     50      webrtc::scoped_refptr<FullScreenWindowDetector>
     51          full_screen_window_detector,
     52      webrtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor);
     53  ~WindowCapturerMac() override;
     54 
     55  WindowCapturerMac(const WindowCapturerMac&) = delete;
     56  WindowCapturerMac& operator=(const WindowCapturerMac&) = delete;
     57 
     58  // DesktopCapturer interface.
     59  void Start(Callback* callback) override;
     60  void CaptureFrame() override;
     61  bool GetSourceList(SourceList* sources) override;
     62  bool SelectSource(SourceId id) override;
     63  bool FocusOnSelectedSource() override;
     64  bool IsOccluded(const DesktopVector& pos) override;
     65 
     66 private:
     67  Callback* callback_ = nullptr;
     68 
     69  // The window being captured.
     70  CGWindowID window_id_ = 0;
     71 
     72  webrtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector_;
     73 
     74  const webrtc::scoped_refptr<DesktopConfigurationMonitor>
     75      configuration_monitor_;
     76 
     77  WindowFinderMac window_finder_;
     78 
     79  // Used to make sure that we only log the usage of fullscreen detection once.
     80  bool fullscreen_usage_logged_ = false;
     81 };
     82 
     83 WindowCapturerMac::WindowCapturerMac(
     84    webrtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector,
     85    webrtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor)
     86    : full_screen_window_detector_(std::move(full_screen_window_detector)),
     87      configuration_monitor_(std::move(configuration_monitor)),
     88      window_finder_(configuration_monitor_) {}
     89 
     90 WindowCapturerMac::~WindowCapturerMac() {}
     91 
     92 bool WindowCapturerMac::GetSourceList(SourceList* sources) {
     93  return webrtc::GetWindowList(sources, true, true);
     94 }
     95 
     96 bool WindowCapturerMac::SelectSource(SourceId id) {
     97  if (!IsWindowValid(id)) return false;
     98  window_id_ = id;
     99  return true;
    100 }
    101 
    102 bool WindowCapturerMac::FocusOnSelectedSource() {
    103  if (!window_id_) return false;
    104 
    105  CGWindowID ids[1];
    106  ids[0] = window_id_;
    107  CFArrayRef window_id_array =
    108      CFArrayCreate(nullptr, reinterpret_cast<const void**>(&ids), 1, nullptr);
    109 
    110  CFArrayRef window_array =
    111      CGWindowListCreateDescriptionFromArray(window_id_array);
    112  if (!window_array || 0 == CFArrayGetCount(window_array)) {
    113    // Could not find the window. It might have been closed.
    114    RTC_LOG(LS_INFO) << "Window not found";
    115    CFRelease(window_id_array);
    116    return false;
    117  }
    118 
    119  CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
    120      CFArrayGetValueAtIndex(window_array, 0));
    121  CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
    122      CFDictionaryGetValue(window, kCGWindowOwnerPID));
    123 
    124  int pid;
    125  CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
    126 
    127  // TODO(jiayl): this will bring the process main window to the front. We
    128  // should find a way to bring only the window to the front.
    129  bool result = [[NSRunningApplication
    130      runningApplicationWithProcessIdentifier:pid] activateWithOptions:0];
    131 
    132  CFRelease(window_id_array);
    133  CFRelease(window_array);
    134  return result;
    135 }
    136 
    137 bool WindowCapturerMac::IsOccluded(const DesktopVector& pos) {
    138  DesktopVector sys_pos = pos;
    139  if (configuration_monitor_) {
    140    auto configuration = configuration_monitor_->desktop_configuration();
    141    sys_pos = pos.add(configuration.bounds.top_left());
    142  }
    143  return window_finder_.GetWindowUnderPoint(sys_pos) != window_id_;
    144 }
    145 
    146 void WindowCapturerMac::Start(Callback* callback) {
    147  RTC_DCHECK(!callback_);
    148  RTC_DCHECK(callback);
    149 
    150  callback_ = callback;
    151 }
    152 
    153 void WindowCapturerMac::CaptureFrame() {
    154  TRACE_EVENT0("webrtc", "WindowCapturerMac::CaptureFrame");
    155 
    156  if (!IsWindowValid(window_id_)) {
    157    RTC_LOG(LS_ERROR) << "The window is not valid any longer.";
    158    callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
    159    return;
    160  }
    161 
    162  CGWindowID on_screen_window = window_id_;
    163  if (full_screen_window_detector_) {
    164    full_screen_window_detector_->UpdateWindowListIfNeeded(
    165        window_id_, [](DesktopCapturer::SourceList* sources) {
    166          // Not using webrtc::GetWindowList(sources, true, false)
    167          // as it doesn't allow to have in the result window with
    168          // empty title along with titled window owned by the same pid.
    169          return webrtc::GetWindowList(
    170              [sources](CFDictionaryRef window) {
    171                WindowId window_id = GetWindowId(window);
    172                int pid = GetWindowOwnerPid(window);
    173                if (window_id != kNullWindowId) {
    174                  sources->push_back(DesktopCapturer::Source{
    175                      window_id, pid, GetWindowTitle(window)});
    176                }
    177                return true;
    178              },
    179              true,
    180              false);
    181        });
    182 
    183    CGWindowID full_screen_window =
    184        full_screen_window_detector_->FindFullScreenWindow(window_id_);
    185 
    186    if (full_screen_window != kCGNullWindowID) {
    187      // If this is the first time this happens, report to UMA that the feature
    188      // is active.
    189      if (!fullscreen_usage_logged_) {
    190        LogDesktopCapturerFullscreenDetectorUsage();
    191        fullscreen_usage_logged_ = true;
    192      }
    193      on_screen_window = full_screen_window;
    194    }
    195  }
    196 
    197  std::unique_ptr<DesktopFrame> frame =
    198      DesktopFrameCGImage::CreateForWindow(on_screen_window);
    199  if (!frame) {
    200    RTC_LOG(LS_WARNING) << "Temporarily failed to capture window.";
    201    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
    202    return;
    203  }
    204 
    205  frame->mutable_updated_region()->SetRect(
    206      DesktopRect::MakeSize(frame->size()));
    207  frame->set_top_left(GetWindowBounds(on_screen_window).top_left());
    208 
    209  float scale_factor = GetWindowScaleFactor(window_id_, frame->size());
    210  frame->set_dpi(
    211      DesktopVector(kStandardDPI * scale_factor, kStandardDPI * scale_factor));
    212 
    213  callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
    214 }
    215 
    216 }  // namespace
    217 
    218 // static
    219 std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
    220    const DesktopCaptureOptions& options) {
    221  RTC_LOG(LS_INFO) << "video capture: DesktopCapturer::CreateRawWindowCapturer "
    222                      "creates DesktopCapturer of type WindowCapturerMac";
    223  return std::unique_ptr<DesktopCapturer>(new WindowCapturerMac(
    224      options.full_screen_window_detector(), options.configuration_monitor()));
    225 }
    226 
    227 }  // namespace webrtc