chromium/chrome/browser/ash/policy/dlp/dlp_content_manager_ash.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_POLICY_DLP_DLP_CONTENT_MANAGER_ASH_H_
#define CHROME_BROWSER_ASH_POLICY_DLP_DLP_CONTENT_MANAGER_ASH_H_

#include <memory>
#include <optional>
#include <string>
#include <utility>

#include "ash/public/cpp/capture_mode/capture_mode_delegate.h"
#include "base/containers/flat_map.h"
#include "base/functional/callback.h"
#include "base/scoped_observation.h"
#include "base/time/time.h"
#include "chrome/browser/ash/policy/dlp/dlp_window_observer.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_confidential_contents.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_content_manager.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_content_restriction_set.h"
#include "chrome/browser/chromeos/policy/dlp/dlp_rules_manager.h"
#include "chrome/browser/ui/ash/screenshot_area.h"
#include "components/exo/window_properties.h"
#include "content/public/browser/desktop_media_id.h"
#include "content/public/browser/media_stream_request.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "ui/aura/window_observer.h"
#include "ui/wm/public/activation_change_observer.h"
#include "ui/wm/public/activation_client.h"
#include "url/gurl.h"

namespace aura {
class Window;
}  // namespace aura

namespace content {
class WebContents;
}  // namespace content

namespace policy {

// System-wide class that tracks the set of currently known confidential
// WebContents and whether any of them are currently visible.
// If any confidential WebContents is visible, the corresponding restrictions
// will be enforced according to the current enterprise policy.
class DlpContentManagerAsh : public DlpContentManager,
                             public DlpWindowObserver::Delegate,
                             public wm::ActivationChangeObserver {
 public:
  DlpContentManagerAsh(const DlpContentManagerAsh&) = delete;
  DlpContentManagerAsh& operator=(const DlpContentManagerAsh&) = delete;

  // Creates the instance if not yet created.
  // There will always be a single instance created on the first access.
  static DlpContentManagerAsh* Get();

  // DlpWindowObserver::Delegate overrides:
  void OnWindowOcclusionChanged(aura::Window* window) override;
  void OnWindowDestroying(aura::Window* window) override;
  void OnWindowTitleChanged(aura::Window* window) override;

  // Returns which restrictions are applied to the WebContents which are
  // currently visible.
  DlpContentRestrictionSet GetOnScreenPresentRestrictions() const;

  // Checks whether screenshots of |area| are restricted or not advised.
  // Depending on the result, calls |callback| and passes an indicator whether
  // to proceed or not.
  void CheckScreenshotRestriction(
      const ScreenshotArea& area,
      ash::OnCaptureModeDlpRestrictionChecked callback);

  // Called when video capturing for |area| is started.
  void OnVideoCaptureStarted(const ScreenshotArea& area);

  // Called when video capturing is stopped. Calls |callback| with an indicator
  // whether to proceed or not, based on DLP restrictions and potentially
  // confidential content captured.
  void CheckStoppedVideoCapture(
      ash::OnCaptureModeDlpRestrictionChecked callback);

  // Called when screenshot is taken for |area|.
  void OnImageCapture(const ScreenshotArea& area);

  // Checks whether initiation of capture mode is restricted or not advised
  // based on the currently visible content. Depending on the result, calls
  // |callback| and passes an indicator whether to proceed or not.
  void CheckCaptureModeInitRestriction(
      ash::OnCaptureModeDlpRestrictionChecked callback);

  // DlpContentManager overrides:
  void CheckScreenShareRestriction(const content::DesktopMediaID& media_id,
                                   const std::u16string& application_title,
                                   WarningCallback callback) override;
  void OnScreenShareStarted(
      const std::string& label,
      std::vector<content::DesktopMediaID> screen_share_ids,
      const std::u16string& application_title,
      base::RepeatingClosure stop_callback,
      content::MediaStreamUI::StateChangeCallback state_change_callback,
      content::MediaStreamUI::SourceCallback source_callback) override;
  void OnScreenShareStopped(const std::string& label,
                            const content::DesktopMediaID& media_id) override;

  // Called when an updated restrictions are received for Lacros window.
  void OnWindowRestrictionChanged(mojo::ReceiverId receiver_id,
                                  const std::string& window_id,
                                  const DlpContentRestrictionSet& restrictions);

  // ActivationChangeObserver:
  void OnWindowActivated(wm::ActivationChangeObserver::ActivationReason reason,
                         aura::Window* gained_active,
                         aura::Window* lost_active) override;

  // Clean pending restrictions for a receiver.
  void CleanPendingRestrictions(mojo::ReceiverId receiver_id);

 private:
  friend class DlpContentManagerTestHelper;
  friend class DlpContentObserver;
  friend class DlpContentTabHelper;

  // Structure to keep track of a running video capture.
  struct VideoCaptureInfo {
    explicit VideoCaptureInfo(const ScreenshotArea& area);

    const ScreenshotArea area;
    DlpConfidentialContents confidential_contents;
    // Contents reported during a video capture, after the start of a capture.
    DlpConfidentialContents reported_confidential_contents;
    // Flag that indicates that there was some content with warn level
    // restriction captured. Used to indicate that the warn UMA should be
    // logged, even if no warning is shown.
    bool had_warning_restriction = false;
  };

  DlpContentManagerAsh();
  ~DlpContentManagerAsh() override;

  // DlpContentManager overrides:
  void OnConfidentialityChanged(
      content::WebContents* web_contents,
      const DlpContentRestrictionSet& restriction_set) override;
  void OnVisibilityChanged(content::WebContents* web_contents) override;
  void RemoveFromConfidential(content::WebContents* web_contents) override;
  ConfidentialContentsInfo GetScreenShareConfidentialContentsInfo(
      const content::DesktopMediaID& media_id,
      content::WebContents* web_contents) const override;
  void TabLocationMaybeChanged(content::WebContents* web_contents) override;

  // Updates |on_screen_restrictions_| and calls
  // OnScreenRestrictionsChanged() if needed.
  void MaybeChangeOnScreenRestrictions();

  // Called when the restrictions for currently visible content changes.
  void OnScreenRestrictionsChanged(
      const DlpContentRestrictionSet& added_restrictions,
      const DlpContentRestrictionSet& removed_restrictions);

  // Removes PrivacyScreen enforcement after delay if it's still not enforced.
  void MaybeRemovePrivacyScreenEnforcement() const;

  // Returns the information about contents that are currently visible on
  // screen, for which the highest level |restriction| is enforced.
  ConfidentialContentsInfo GetConfidentialContentsOnScreen(
      DlpContentRestriction restriction) const;

  // Returns level, url, and information about visible confidential contents of
  // |restriction| that is currently enforced for |area|.
  ConfidentialContentsInfo GetAreaConfidentialContentsInfo(
      const ScreenshotArea& area,
      DlpContentRestriction restriction) const;

  // Checks and stops the running video capture if restricted content appeared
  // in the corresponding areas.
  void CheckRunningVideoCapture();

  // Get the delay before switching privacy screen off.
  static base::TimeDelta GetPrivacyScreenOffDelayForTesting();

  // Helper method for async check of the restriction level, based on which
  // calls |callback| with an indicator whether to proceed or not.
  void CheckScreenCaptureRestriction(
      ConfidentialContentsInfo info,
      ash::OnCaptureModeDlpRestrictionChecked callback);

  // Map of window observers for the current confidential WebContents.
  base::flat_map<content::WebContents*, std::unique_ptr<DlpWindowObserver>>
      web_contents_window_observers_;

  // Map from currently known Lacros Windows to their restrictions.
  base::flat_map<aura::Window*, DlpContentRestrictionSet> confidential_windows_;

  // Map of observers for currently known Lacros Windows.
  base::flat_map<aura::Window*, std::unique_ptr<DlpWindowObserver>>
      window_observers_;
  // Map of observers for Lacros surfaces that are being notified for visibility
  // changes.
  base::flat_map<aura::Window*, std::unique_ptr<DlpWindowObserver>>
      surface_observers_;

  // Set of restriction applied to the currently visible content.
  DlpContentRestrictionSet on_screen_restrictions_;

  // Information about the currently running video capture area if any.
  std::optional<VideoCaptureInfo> running_video_capture_info_;

  // Cache for restrictions, which are sent to ash with a window id before the
  // id is known to ash. The window is (considered to be) invisible, so the
  // restrictions are not applied as long as they are in the cache. The id of
  // the receiver is also saved.

  // Restrictions might be sent from lacros to ash before the window is known to
  // ash. The pending restrictions are saved here until the window gets active
  // in wayland / ash.
  std::map<std::string, std::pair<mojo::ReceiverId, DlpContentRestrictionSet>>
      pending_restrictions_;

  // Map to save all windows of a receiver with pending restrictions.
  std::map<mojo::ReceiverId, std::set<std::string>> pending_restrictions_owner_;

  base::ScopedObservation<::wm::ActivationClient, wm::ActivationChangeObserver>
      window_activation_observation_{this};
};

}  // namespace policy

#endif  // CHROME_BROWSER_ASH_POLICY_DLP_DLP_CONTENT_MANAGER_ASH_H_