910e62b5创建于 1月15日历史提交
// Copyright 2015 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_UI_EXCLUSIVE_ACCESS_POINTER_LOCK_CONTROLLER_H_
#define CHROME_BROWSER_UI_EXCLUSIVE_ACCESS_POINTER_LOCK_CONTROLLER_H_

#include <set>
#include <utility>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_bubble_hide_callback.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_controller_base.h"
#include "components/content_settings/core/common/content_settings.h"
#include "content/public/browser/global_routing_id.h"

// This class implements the mouse pointer lock behavior.
class PointerLockController : public ExclusiveAccessControllerBase {
 public:
  explicit PointerLockController(ExclusiveAccessManager* manager);

  PointerLockController(const PointerLockController&) = delete;
  PointerLockController& operator=(const PointerLockController&) = delete;

  ~PointerLockController() override;

  // Returns true if the mouse pointer is locked.
  bool IsPointerLocked() const;

  // Returns true if the mouse pointer was locked and no notification should be
  // displayed to the user. This is the case when a notice has already been
  // displayed to the user, and the application voluntarily unlocks, then
  // re-locks the pointer (a duplicate notification should not be given). See
  // content::PointerLockDispatcher::LockPointer.
  bool IsPointerLockedSilently() const;

  void RequestToLockPointer(content::WebContents* web_contents,
                            bool user_gesture,
                            bool last_unlocked_by_target);

  // Returns true if we are waiting for the user to make a selection on the
  // pointer lock permission request dialog.
  bool IsWaitingForPointerLockPrompt(content::WebContents* web_contents);

  // Override from ExclusiveAccessControllerBase
  bool HandleUserPressedEscape() override;
  void HandleUserHeldEscape() override;
  void HandleUserReleasedEscapeEarly() override;
  bool RequiresPressAndHoldEscToExit() const override;
  void ExitExclusiveAccessToPreviousState() override;

  void UnlockPointer();

  void set_bubble_hide_callback_for_test(
      ExclusiveAccessBubbleHideCallbackForTest callback) {
    bubble_hide_callback_for_test_ = std::move(callback);
  }

  void set_lock_state_callback_for_test(base::OnceClosure callback) {
    lock_state_callback_for_test_ = std::move(callback);
  }

 private:
  friend class ExclusiveAccessTest;

  enum PointerLockState {
    POINTERLOCK_UNLOCKED,
    // Pointer has been locked.
    POINTERLOCK_LOCKED,
    // Pointer has been locked silently, with no notification to user.
    POINTERLOCK_LOCKED_SILENTLY
  };

  void LockPointer(base::WeakPtr<content::WebContents> web_contents,
                   content::GlobalRenderFrameHostId rfh_id,
                   bool last_unlocked_by_target);
  void RejectRequestToLockPointer(
      base::WeakPtr<content::WebContents> web_contents,
      content::GlobalRenderFrameHostId rfh_id);

  void ExitExclusiveAccessIfNecessary() override;
  void NotifyTabExclusiveAccessLost() override;

  void OnBubbleHidden(base::WeakPtr<content::WebContents>,
                      ExclusiveAccessBubbleHideReason);

  bool ShouldSuppressBubbleReshowForStateChange();

  // Returns true if the RenderFrameHost identified by `rfh_id` is waiting
  // for the user to make a selection on the pointer lock prompt.
  bool IsWaitingForPointerLockPromptHelper(
      content::GlobalRenderFrameHostId rfh_id);

  PointerLockState pointer_lock_state_;

  // Optionally a WebContents instance that is granted permission to silently
  // lock the mouse pointer. This is granted only if the WebContents instance
  // has previously locked and displayed the permission bubble until the bubble
  // time out has expired. https://crbug.com/725370
  raw_ptr<content::WebContents, AcrossTasksDanglingUntriaged>
      web_contents_granted_silent_pointer_lock_permission_ = nullptr;

  // If true, does not call into the WebContents to lock the mouse pointer. Just
  // assumes that it works. This may be necessary when calling
  // Browser::RequestToLockPointer in tests, because the proper signal will not
  // have been passed to the RenderViewHost.
  bool fake_pointer_lock_for_test_;

  // If set, |bubble_hide_callback_for_test_| will be called during
  // |OnBubbleHidden()|.
  ExclusiveAccessBubbleHideCallbackForTest bubble_hide_callback_for_test_;

  // Called when the page requests (successfully or not) or loses pointer lock.
  base::OnceClosure lock_state_callback_for_test_;

  // Timestamp when the user last successfully escaped from a lock request.
  base::TimeTicks last_user_escape_time_;

  // Set of RenderFrameHost IDs waiting for pointer lock permission prompt
  // selection by the user.
  std::set<content::GlobalRenderFrameHostId>
      hosts_waiting_for_pointer_lock_permission_prompt_;

  base::WeakPtrFactory<PointerLockController> weak_ptr_factory_{this};
};

#endif  // CHROME_BROWSER_UI_EXCLUSIVE_ACCESS_POINTER_LOCK_CONTROLLER_H_