1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136

content / browser / picture_in_picture / document_picture_in_picture_window_controller_impl.h [blame]

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

#ifndef CONTENT_BROWSER_PICTURE_IN_PICTURE_DOCUMENT_PICTURE_IN_PICTURE_WINDOW_CONTROLLER_IMPL_H_
#define CONTENT_BROWSER_PICTURE_IN_PICTURE_DOCUMENT_PICTURE_IN_PICTURE_WINDOW_CONTROLLER_IMPL_H_

#include <map>
#include <set>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "content/common/content_export.h"
#include "content/public/browser/document_picture_in_picture_window_controller.h"
#include "content/public/browser/media_player_id.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"

namespace content {

class WebContents;
class WebContentsImpl;
enum class PictureInPictureResult;

// DocumentPictureInPictureWindowControllerImpl handles Picture-in-Picture mode
// for HTML Document contents. It is very similar to video Picture-in-Picture
// mode, just using a WebContents view instead of a video element. See the
// content::PictureInPictureWindowControllerImpl documentation for additional
// context.
class CONTENT_EXPORT DocumentPictureInPictureWindowControllerImpl
    : public DocumentPictureInPictureWindowController,
      public WebContentsUserData<DocumentPictureInPictureWindowControllerImpl>,
      public WebContentsObserver {
 public:
  // Gets a reference to the controller associated with |web_contents| and
  // creates one if it does not exist. The returned pointer is guaranteed to be
  // non-null.
  static DocumentPictureInPictureWindowControllerImpl*
  GetOrCreateForWebContents(WebContents* web_contents);

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

  ~DocumentPictureInPictureWindowControllerImpl() override;

  // PictureInPictureWindowController:
  void Show() override;
  void FocusInitiator() override;
  void Close(bool should_pause_video) override;
  void CloseAndFocusInitiator() override;
  void OnWindowDestroyed(bool should_pause_video) override;
  WebContents* GetWebContents() override;
  std::optional<gfx::Rect> GetWindowBounds() override;
  WebContents* GetChildWebContents() override;
  std::optional<url::Origin> GetOrigin() override;

  // DocumentPictureInPictureWindowController:
  void SetChildWebContents(WebContents* child_contents) override;

  // WebContentsObserver:
  void WebContentsDestroyed() override;
  void PrimaryPageChanged(Page&) override;

 private:
  friend class WebContentsUserData<
      DocumentPictureInPictureWindowControllerImpl>;

  // To create an instance, use
  // DocumentPictureInPictureWindowControllerImpl::GetOrCreateForWebContents()
  explicit DocumentPictureInPictureWindowControllerImpl(
      WebContents* web_contents);

  // Internal method to set the states after the pip has been stopped, whether
  // via the page or by the browser.  Notifies the opener that pip has ended.
  // This is the only thing that should clear `child_contents_`, and it should
  // always clear `child_contents_`.
  void NotifyClosedAndStopObserving(bool should_pause_video);

  // Called when the child WebContents discovers that it's being deleted.
  void OnChildContentsDestroyed();

  // Returns the web_contents() as a WebContentsImpl*.
  WebContentsImpl* GetWebContentsImpl();

  // The WebContents for the PiP window. If this is null, then we have already
  // closed / stopped Picture in Picture.
  raw_ptr<WebContents> child_contents_ = nullptr;

  class ChildContentsObserver : public WebContentsObserver {
   public:
    // Will post `force_close_cb` when `web_contents` navigates, or at similar
    // times when the PiP session should end. `contents_destroyed_cb` will be
    // called in-line (not posted) when our WebContents has been destroyed and
    // the pointer should be discarded.
    ChildContentsObserver(WebContents* web_contents,
                          base::OnceClosure force_close_cb,
                          base::OnceClosure contents_destroyed_cb);
    ~ChildContentsObserver() override;

    // Watch for navigations in the child contents, so that we can close the PiP
    // window if it navigates away.  Some navigations (e.g., same-document) are
    // allowed here.
    void DidStartNavigation(NavigationHandle*) override;

    // If the PiP window is destroyed, notify the opener.
    void WebContentsDestroyed() override;

    // The PiP window should never be duplicated.
    void DidCloneToNewWebContents(WebContents*, WebContents*) override;

   private:
    // Called, via post, to request that the pip session end.
    base::OnceClosure force_close_cb_;

    // Called, without posting, when the raw ptr to our WebContents is about to
    // be invalidated.
    base::OnceClosure contents_destroyed_cb_;
  };

  raw_ptr<WebContents> opener_web_contents_ = nullptr;

  // WebContentsObserver to watch for changes in `child_contents_`.
  std::unique_ptr<ChildContentsObserver> child_contents_observer_;

  WEB_CONTENTS_USER_DATA_KEY_DECL();

  base::WeakPtrFactory<DocumentPictureInPictureWindowControllerImpl>
      weak_factory_{this};
};

}  // namespace content

#endif  // CONTENT_BROWSER_PICTURE_IN_PICTURE_DOCUMENT_PICTURE_IN_PICTURE_WINDOW_CONTROLLER_IMPL_H_