Surface within DevTools remaining AutoPiP for media playback conditions

This change updates the auto picture in picture tab helper to report,
within DevTools, whenever AutoPip requests are denied.

This change also renames the GetAutoPipReason API to GetAutoPipInfo and
updates it to return additional auto picture in picture information.

Bug: 368058093, 386193409
Change-Id: Ie012d4435be4376515a433e90696174bed7ea580
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6426890
Reviewed-by: Frank Liberato <liberato@chromium.org>
Reviewed-by: Rakina Zata Amni <rakina@chromium.org>
Commit-Queue: Benjamin Keen <bkeen@google.com>
Reviewed-by: Tommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1443742}
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 37d8f59..9755339 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -7194,17 +7194,16 @@
   return content::VideoOverlayWindow::Create(controller);
 }
 
-media::PictureInPictureEventsInfo::AutoPipReason
-ChromeContentBrowserClient::GetAutoPipReason(
+media::PictureInPictureEventsInfo::AutoPipInfo
+ChromeContentBrowserClient::GetAutoPipInfo(
     const content::WebContents& web_contents) const {
 #if BUILDFLAG(IS_ANDROID)
-  return media::PictureInPictureEventsInfo::AutoPipReason::kUnknown;
+  return media::PictureInPictureEventsInfo::AutoPipInfo();
 #else
   auto* auto_pip_tab_helper =
       AutoPictureInPictureTabHelper::FromWebContents(&web_contents);
-  return auto_pip_tab_helper
-             ? auto_pip_tab_helper->GetAutoPipTriggerReason()
-             : media::PictureInPictureEventsInfo::AutoPipReason::kUnknown;
+  return auto_pip_tab_helper ? auto_pip_tab_helper->GetAutoPipInfo()
+                             : media::PictureInPictureEventsInfo::AutoPipInfo();
 #endif  // BUILDFLAG(IS_ANDROID)
 }
 
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index ab03a00..0535ba1 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -809,7 +809,7 @@
   std::unique_ptr<content::VideoOverlayWindow>
   CreateWindowForVideoPictureInPicture(
       content::VideoPictureInPictureWindowController* controller) override;
-  media::PictureInPictureEventsInfo::AutoPipReason GetAutoPipReason(
+  media::PictureInPictureEventsInfo::AutoPipInfo GetAutoPipInfo(
       const content::WebContents& web_contents) const override;
   void RegisterRendererPreferenceWatcher(
       content::BrowserContext* browser_context,
diff --git a/chrome/browser/chrome_content_browser_client_unittest.cc b/chrome/browser/chrome_content_browser_client_unittest.cc
index 13fbc1cd..9abff07 100644
--- a/chrome/browser/chrome_content_browser_client_unittest.cc
+++ b/chrome/browser/chrome_content_browser_client_unittest.cc
@@ -359,7 +359,7 @@
       url::Origin::Create(GURL("c.test"))));
 }
 
-TEST_F(ChromeContentBrowserClientWindowTest, GetAutoPipReason) {
+TEST_F(ChromeContentBrowserClientWindowTest, GetAutoPipInfo_AutoPipReason) {
   ChromeContentBrowserClient client;
 
   const GURL url("https://www.google.com");
@@ -378,18 +378,18 @@
       AutoPictureInPictureTabHelper::FromWebContents(web_contents);
   ASSERT_NE(nullptr, tab_helper);
   EXPECT_EQ(media::PictureInPictureEventsInfo::AutoPipReason::kUnknown,
-            client.GetAutoPipReason(*web_contents));
+            client.GetAutoPipInfo(*web_contents).auto_pip_reason);
 
   tab_helper->set_auto_pip_trigger_reason_for_testing(
       media::PictureInPictureEventsInfo::AutoPipReason::kVideoConferencing);
   EXPECT_EQ(
       media::PictureInPictureEventsInfo::AutoPipReason::kVideoConferencing,
-      client.GetAutoPipReason(*web_contents));
+      client.GetAutoPipInfo(*web_contents).auto_pip_reason);
 
   tab_helper->set_auto_pip_trigger_reason_for_testing(
       media::PictureInPictureEventsInfo::AutoPipReason::kMediaPlayback);
   EXPECT_EQ(media::PictureInPictureEventsInfo::AutoPipReason::kMediaPlayback,
-            client.GetAutoPipReason(*web_contents));
+            client.GetAutoPipInfo(*web_contents).auto_pip_reason);
 }
 
 #endif  // !BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.cc b/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.cc
index 04af36a..5bfeadc 100644
--- a/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.cc
+++ b/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.cc
@@ -278,6 +278,10 @@
 void AutoPictureInPictureTabHelper::MaybeEnterAutoPictureInPicture() {
   if (!IsEligibleForAutoPictureInPicture(
           /*should_record_blocking_metrics=*/true)) {
+    if (content::MediaSession* media_session =
+            content::MediaSession::GetIfExists(web_contents())) {
+      media_session->ReportAutoPictureInPictureInfoChanged();
+    }
     return;
   }
   auto_picture_in_picture_activation_time_ =
@@ -560,6 +564,19 @@
   return media::PictureInPictureEventsInfo::AutoPipReason::kUnknown;
 }
 
+media::PictureInPictureEventsInfo::AutoPipInfo
+AutoPictureInPictureTabHelper::GetAutoPipInfo() const {
+  return media::PictureInPictureEventsInfo::AutoPipInfo{
+      .auto_pip_reason = GetAutoPipTriggerReason(),
+      .has_audio_focus = has_audio_focus_,
+      .is_playing = is_playing_,
+      .was_recently_audible = WasRecentlyAudible(),
+      .has_safe_url = has_safe_url_,
+      .meets_media_engagement_conditions = MeetsMediaEngagementConditions(),
+      .blocked_due_to_content_setting = blocked_due_to_content_setting_,
+  };
+}
+
 media::PictureInPictureEventsInfo::AutoPipReason
 AutoPictureInPictureTabHelper::GetAutoPipTriggerReason() const {
   return auto_pip_trigger_reason_;
diff --git a/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.h b/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.h
index d37e4624..90fc365 100644
--- a/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.h
+++ b/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper.h
@@ -159,6 +159,11 @@
   media::PictureInPictureEventsInfo::AutoPipReason GetAutoPipTriggerReason()
       const;
 
+  // Returns information related to auto picture in picture. This information
+  // includes the reason for entering picture in picture automatically, if
+  // known, and various conditions that are used to allow/deny autopip requests.
+  media::PictureInPictureEventsInfo::AutoPipInfo GetAutoPipInfo() const;
+
  private:
   explicit AutoPictureInPictureTabHelper(content::WebContents* web_contents);
   friend class content::WebContentsUserData<AutoPictureInPictureTabHelper>;
diff --git a/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper_browsertest.cc b/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper_browsertest.cc
index c5776fe..0151e5a 100644
--- a/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper_browsertest.cc
+++ b/chrome/browser/picture_in_picture/auto_picture_in_picture_tab_helper_browsertest.cc
@@ -8,6 +8,7 @@
 
 #include "base/files/file_util.h"
 #include "base/path_service.h"
+#include "base/scoped_observation.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/simple_test_tick_clock.h"
 #include "build/build_config.h"
@@ -194,6 +195,45 @@
   std::optional<bool> was_recently_audible_;
 };
 
+// Helper class to wait for DevTools to receive auto picture in picture events
+// information.
+class AutoPipInfoDevToolsWaiter : public content::DevToolsInspectorLogWatcher::
+                                      DevToolsInspectorLogWatcherObserver {
+ public:
+  explicit AutoPipInfoDevToolsWaiter(
+      content::DevToolsInspectorLogWatcher* log_watcher) {
+    auto_pip_dev_tools_waiter_observation_.Observe(log_watcher);
+  }
+  AutoPipInfoDevToolsWaiter(const AutoPipInfoDevToolsWaiter&) = delete;
+  AutoPipInfoDevToolsWaiter(AutoPipInfoDevToolsWaiter&&) = delete;
+  AutoPipInfoDevToolsWaiter& operator=(const AutoPipInfoDevToolsWaiter&) =
+      delete;
+
+  void WaitUntilDone() {
+    if (auto_pip_event_info_set_) {
+      return;
+    }
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
+  }
+
+ private:
+  void OnLastAutoPipEventInfoSet() override {
+    auto_pip_event_info_set_ = true;
+    if (run_loop_) {
+      run_loop_->Quit();
+    }
+    auto_pip_dev_tools_waiter_observation_.Reset();
+  }
+
+  std::unique_ptr<base::RunLoop> run_loop_;
+  bool auto_pip_event_info_set_ = false;
+  base::ScopedObservation<
+      content::DevToolsInspectorLogWatcher,
+      content::DevToolsInspectorLogWatcher::DevToolsInspectorLogWatcherObserver>
+      auto_pip_dev_tools_waiter_observation_{this};
+};
+
 class AutoPictureInPictureTabHelperBrowserTest : public WebRtcTestBase {
  public:
   AutoPictureInPictureTabHelperBrowserTest() = default;
@@ -679,8 +719,10 @@
 
   media::PictureInPictureEventsInfo::AutoPipReason GetAutoPipReason(
       const content::WebContents& web_contents) {
-    return content::GetContentClientForTesting()->browser()->GetAutoPipReason(
-        web_contents);
+    return content::GetContentClientForTesting()
+        ->browser()
+        ->GetAutoPipInfo(web_contents)
+        .auto_pip_reason;
   }
 
   ukm::TestAutoSetUkmRecorder* ukm_recorder() { return ukm_recorder_.get(); }
@@ -2571,3 +2613,40 @@
 
   CloseBrowserSynchronously(browser());
 }
+
+IN_PROC_BROWSER_TEST_F(AutoPictureInPictureWithVideoPlaybackBrowserTest,
+                       AutoPipInfoRecordedInDevTools) {
+  LoadAutoDocumentPipPage(browser());
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  PlayVideo(web_contents);
+  WaitForAudioFocusGained();
+  WaitForMediaSessionPlaying(web_contents);
+  SetExpectedHasHighEngagement(true);
+  WaitForWasRecentlyAudible(web_contents);
+
+  {
+    // Start watching the DevTools logs and clear the latest media notification.
+    content::DevToolsInspectorLogWatcher log_watcher(
+        web_contents, content::DevToolsInspectorLogWatcher::Domain::Media);
+    log_watcher.ClearLastAutoPictureInPictureEventInfo();
+
+    // Generate media logs.
+    AutoPipInfoDevToolsWaiter pip_devtools_info_waiter(&log_watcher);
+    SwitchToNewTabAndBackAndExpectAutopip(/*should_video_pip=*/false,
+                                          /*should_document_pip=*/true);
+    pip_devtools_info_waiter.WaitUntilDone();
+
+    // Verify that the auto picture in picture information was recorded in the
+    // DevTools media logs.
+    log_watcher.FlushAndStopWatching();
+    ASSERT_FALSE(log_watcher.last_auto_picture_in_picture_event_info().empty());
+    const std::string expected_auto_pip_info =
+        "{\"auto_picture_in_picture_info\":\"{Reason: MediaPlayback, has audio "
+        "focus: true, is_playing: true, was recently audible: true, has safe "
+        "url: true, meets media engagement conditions: true, blocked due to "
+        "content setting: "
+        "false}\",\"event\":\"kAutoPictureInPictureInfoChanged\"}";
+    EXPECT_EQ(expected_auto_pip_info,
+              log_watcher.last_auto_picture_in_picture_event_info());
+  }
+}
diff --git a/content/browser/media/session/media_session_impl.cc b/content/browser/media/session/media_session_impl.cc
index e9be07b..192c4371 100644
--- a/content/browser/media/session/media_session_impl.cc
+++ b/content/browser/media/session/media_session_impl.cc
@@ -1321,7 +1321,7 @@
       media_session::mojom::MediaSessionAction::kEnterPictureInPicture);
   uma_helper_.RecordEnterPictureInPicture(
       MediaSessionUmaHelper::EnterPictureInPictureType::kRegisteredAutomatic);
-  OnAutoPictureInPictureInfoChanged();
+  ReportAutoPictureInPictureInfoChanged();
 }
 
 void MediaSessionImpl::SetAudioSinkId(const std::optional<std::string>& id) {
@@ -1431,6 +1431,21 @@
                      minimum_size_px, desired_size_px, source_icon));
 }
 
+void MediaSessionImpl::ReportAutoPictureInPictureInfoChanged() {
+  ContentClient* content_client = GetContentClient();
+  const auto auto_picture_in_picture_info =
+      media::PictureInPictureEventsInfo::AutoPipInfoToString(
+          content_client->browser()->GetAutoPipInfo(*web_contents()));
+
+  ForAllPlayers(base::BindRepeating(
+      [](std::string_view auto_picture_in_picture_info,
+         const PlayerIdentifier& player) {
+        player.observer->OnAutoPictureInPictureInfoChanged(
+            player.player_id, auto_picture_in_picture_info);
+      },
+      auto_picture_in_picture_info));
+}
+
 void MediaSessionImpl::AbandonSystemAudioFocusIfNeeded() {
   if (audio_focus_state_ == State::INACTIVE || !normal_players_.empty() ||
       !pepper_players_.empty() || !one_shot_players_.empty() ||
@@ -2162,21 +2177,6 @@
   guarding_player_id_.reset();
 }
 
-void MediaSessionImpl::OnAutoPictureInPictureInfoChanged() {
-  ContentClient* content_client = GetContentClient();
-  const auto auto_picture_in_picture_reason =
-      media::PictureInPictureEventsInfo::AutoPipReasonToString(
-          content_client->browser()->GetAutoPipReason(*web_contents()));
-
-  ForAllPlayers(base::BindRepeating(
-      [](std::string_view auto_picture_in_picture_reason,
-         const PlayerIdentifier& player) {
-        player.observer->OnAutoPictureInPictureInfoChanged(
-            player.player_id, auto_picture_in_picture_reason);
-      },
-      auto_picture_in_picture_reason));
-}
-
 void MediaSessionImpl::SetShouldThrottleDurationUpdateForTest(
     bool should_throttle) {
   should_throttle_duration_update_ = should_throttle;
diff --git a/content/browser/media/session/media_session_impl.h b/content/browser/media/session/media_session_impl.h
index 8f5390c..f508bb3 100644
--- a/content/browser/media/session/media_session_impl.h
+++ b/content/browser/media/session/media_session_impl.h
@@ -319,6 +319,10 @@
       int desired_size_px,
       GetMediaImageBitmapCallback callback) override;
 
+  // Called to report to all players that the auto picture in picture
+  // information changed.
+  void ReportAutoPictureInPictureInfoChanged() override;
+
   const base::UnguessableToken& audio_focus_group_id() const {
     return audio_focus_group_id_;
   }
@@ -530,10 +534,6 @@
 
   void ResetDurationUpdateGuard();
 
-  // Called when any of the normal players auto picture in picture information
-  // changes.
-  void OnAutoPictureInPictureInfoChanged();
-
   CONTENT_EXPORT void SetShouldThrottleDurationUpdateForTest(
       bool should_throttle);
 
diff --git a/content/browser/renderer_host/render_frame_host_delegate.cc b/content/browser/renderer_host/render_frame_host_delegate.cc
index cac24d2..94f051a 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.cc
+++ b/content/browser/renderer_host/render_frame_host_delegate.cc
@@ -238,9 +238,9 @@
   return gfx::NativeWindow();
 }
 
-media::PictureInPictureEventsInfo::AutoPipReason
-RenderFrameHostDelegate::GetAutoPipReason() const {
-  return media::PictureInPictureEventsInfo::AutoPipReason::kUnknown;
+media::PictureInPictureEventsInfo::AutoPipInfo
+RenderFrameHostDelegate::GetAutoPipInfo() const {
+  return media::PictureInPictureEventsInfo::AutoPipInfo();
 }
 
 }  // namespace content
diff --git a/content/browser/renderer_host/render_frame_host_delegate.h b/content/browser/renderer_host/render_frame_host_delegate.h
index ca5a5eb82..fb913eb 100644
--- a/content/browser/renderer_host/render_frame_host_delegate.h
+++ b/content/browser/renderer_host/render_frame_host_delegate.h
@@ -782,9 +782,8 @@
   // Returns the top-level native window for the associated WebContents.
   virtual gfx::NativeWindow GetOwnerNativeWindow();
 
-  // Gets the delegate reason for entering picture in picture automatically.
-  virtual media::PictureInPictureEventsInfo::AutoPipReason GetAutoPipReason()
-      const;
+  // Gets the delegate auto picture in picture information.
+  virtual media::PictureInPictureEventsInfo::AutoPipInfo GetAutoPipInfo() const;
 
  protected:
   virtual ~RenderFrameHostDelegate() = default;
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 270f3af..40a53cb9 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -18587,7 +18587,7 @@
         if (rfh == nullptr) {
           return media::PictureInPictureEventsInfo::AutoPipReason::kUnknown;
         }
-        return rfh->delegate()->GetAutoPipReason();
+        return rfh->delegate()->GetAutoPipInfo().auto_pip_reason;
       },
       weak_ptr_factory_.GetWeakPtr());
 }
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index b7fc242..a8e0d2d0 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -7254,9 +7254,9 @@
   return GetTopLevelNativeWindow();
 }
 
-media::PictureInPictureEventsInfo::AutoPipReason
-WebContentsImpl::GetAutoPipReason() const {
-  return GetContentClient()->browser()->GetAutoPipReason(*this);
+media::PictureInPictureEventsInfo::AutoPipInfo WebContentsImpl::GetAutoPipInfo()
+    const {
+  return GetContentClient()->browser()->GetAutoPipInfo(*this);
 }
 
 void WebContentsImpl::NotifyChangedNavigationState(
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index c6cb37c5..08f0e968 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -948,7 +948,7 @@
   void OnFirstContentfulPaintInPrimaryMainFrame() override;
   gfx::NativeWindow GetOwnerNativeWindow() override;
 
-  media::PictureInPictureEventsInfo::AutoPipReason GetAutoPipReason()
+  media::PictureInPictureEventsInfo::AutoPipInfo GetAutoPipInfo()
       const override;
 
   // RenderViewHostDelegate ----------------------------------------------------
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 78b3dcf..08653c1 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -1352,9 +1352,9 @@
   return nullptr;
 }
 
-media::PictureInPictureEventsInfo::AutoPipReason
-ContentBrowserClient::GetAutoPipReason(const WebContents& web_contents) const {
-  return media::PictureInPictureEventsInfo::AutoPipReason::kUnknown;
+media::PictureInPictureEventsInfo::AutoPipInfo
+ContentBrowserClient::GetAutoPipInfo(const WebContents& web_contents) const {
+  return media::PictureInPictureEventsInfo::AutoPipInfo();
 }
 
 void ContentBrowserClient::RegisterRendererPreferenceWatcher(
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index dca438ed..0b9ef23b 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -2477,9 +2477,9 @@
   CreateWindowForVideoPictureInPicture(
       VideoPictureInPictureWindowController* controller);
 
-  // Returns the reason for entering picture in picture automatically. This is
-  // recorded in metrics.
-  virtual media::PictureInPictureEventsInfo::AutoPipReason GetAutoPipReason(
+  // Returns information related to auto picture in picture. The auto picture in
+  // picture reason is recorded in metrics.
+  virtual media::PictureInPictureEventsInfo::AutoPipInfo GetAutoPipInfo(
       const WebContents& web_contents) const;
 
   // Registers the watcher to observe updates in RendererPreferences.
diff --git a/content/public/browser/media_session.h b/content/public/browser/media_session.h
index 90d0093..a2c99f8 100644
--- a/content/public/browser/media_session.h
+++ b/content/public/browser/media_session.h
@@ -73,6 +73,10 @@
   // service, if the routed service exists, nullptr otherwise.
   virtual RenderFrameHost* GetRoutedFrame() = 0;
 
+  // Report to all players that information related to automatic picture in
+  // picture has changed.
+  virtual void ReportAutoPictureInPictureInfoChanged() = 0;
+
   // media_session.mojom.MediaSession overrides -------------------------------
 
   // Suspend the media session.
diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc
index 49366da..794ceb2a 100644
--- a/content/public/test/browser_test_utils.cc
+++ b/content/public/test/browser_test_utils.cc
@@ -3873,6 +3873,30 @@
 
   if (notification->find("Media.") != std::string::npos) {
     last_media_notification_ = *notification;
+
+    if (*notification == "Media.playerEventsAdded") {
+      bool last_auto_pip_event_info_set = false;
+      const base::Value::List* events =
+          parsed_message.FindListByDottedPath("params.events");
+      if (events) {
+        for (const base::Value& event : *events) {
+          const auto* dict = event.GetIfDict();
+          if (!dict) {
+            continue;
+          }
+          const std::string* text = dict->FindString("value");
+          if ((text != nullptr) &&
+              ((*text).find("auto_picture_in_picture_info") !=
+               std::string::npos)) {
+            last_auto_picture_in_picture_event_info_ = *text;
+            last_auto_pip_event_info_set = true;
+          }
+        }
+      }
+      if (last_auto_pip_event_info_set) {
+        NotifyLastAutoPipEventInfoSet();
+      }
+    }
   }
 }
 
@@ -3895,6 +3919,12 @@
   run_loop_disable_log_.Run();
 }
 
+void DevToolsInspectorLogWatcher::NotifyLastAutoPipEventInfoSet() {
+  for (DevToolsInspectorLogWatcherObserver& obs : observers_) {
+    obs.OnLastAutoPipEventInfoSet();
+  }
+}
+
 namespace {
 mojo::Remote<blink::mojom::FileSystemManager> GetFileSystemManager(
     RenderProcessHost* rph,
diff --git a/content/public/test/browser_test_utils.h b/content/public/test/browser_test_utils.h
index 643d3e5..d20676d 100644
--- a/content/public/test/browser_test_utils.h
+++ b/content/public/test/browser_test_utils.h
@@ -21,6 +21,8 @@
 #include "base/memory/raw_ptr.h"
 #include "base/memory/raw_ptr_exclusion.h"
 #include "base/memory/weak_ptr.h"
+#include "base/observer_list.h"
+#include "base/observer_list_types.h"
 #include "base/process/process.h"
 #include "base/run_loop.h"
 #include "base/scoped_observation.h"
@@ -2136,11 +2138,34 @@
   std::string last_media_notification() { return last_media_notification_; }
   void ClearLastMediaNotification() { last_media_notification_.clear(); }
 
+  std::string last_auto_picture_in_picture_event_info() {
+    return last_auto_picture_in_picture_event_info_;
+  }
+  void ClearLastAutoPictureInPictureEventInfo() {
+    last_auto_picture_in_picture_event_info_.clear();
+  }
+
   // DevToolsAgentHostClient:
   void DispatchProtocolMessage(DevToolsAgentHost* host,
                                base::span<const uint8_t> message) override;
   void AgentHostClosed(DevToolsAgentHost* host) override;
 
+  class DevToolsInspectorLogWatcherObserver : public base::CheckedObserver {
+   public:
+    virtual void OnLastAutoPipEventInfoSet() = 0;
+  };
+
+  void AddObserver(DevToolsInspectorLogWatcherObserver* observer) {
+    observers_.AddObserver(observer);
+  }
+  void RemoveObserver(DevToolsInspectorLogWatcherObserver* observer) {
+    observers_.RemoveObserver(observer);
+  }
+
+  // Notifies observers that the last auto picture in picture event information
+  // was set.
+  void NotifyLastAutoPipEventInfoSet();
+
  private:
   scoped_refptr<DevToolsAgentHost> host_;
   base::RunLoop run_loop_enable_log_;
@@ -2149,6 +2174,8 @@
   GURL last_url_;
   Domain domain_;
   std::string last_media_notification_;
+  std::string last_auto_picture_in_picture_event_info_;
+  base::ObserverList<DevToolsInspectorLogWatcherObserver> observers_;
 };
 
 // Static methods that simulates Mojo methods as if they were called by a
diff --git a/content/public/test/mock_media_session.h b/content/public/test/mock_media_session.h
index 6988439..9e3b304 100644
--- a/content/public/test/mock_media_session.h
+++ b/content/public/test/mock_media_session.h
@@ -55,6 +55,7 @@
                int desired_size_px,
                GetMediaImageBitmapCallback callback),
               (override));
+  MOCK_METHOD(void, ReportAutoPictureInPictureInfoChanged, (), (override));
   MOCK_METHOD(void, SeekTo, (base::TimeDelta seek_time), (override));
   MOCK_METHOD(void, ScrubTo, (base::TimeDelta seek_time), (override));
   MOCK_METHOD(void, EnterPictureInPicture, (), (override));
diff --git a/media/base/picture_in_picture_events_info.cc b/media/base/picture_in_picture_events_info.cc
index 4922bfb..a0bcc79 100644
--- a/media/base/picture_in_picture_events_info.cc
+++ b/media/base/picture_in_picture_events_info.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/notreached.h"
+#include "base/strings/stringprintf.h"
 
 namespace media {
 
@@ -30,4 +31,20 @@
                << static_cast<int>(auto_pip_reason);
 }
 
+// static
+std::string PictureInPictureEventsInfo::AutoPipInfoToString(
+    AutoPipInfo auto_pip_info) {
+  return base::StringPrintf(
+      "{Reason: %s, has audio focus: %s, is_playing: %s, was recently audible: "
+      "%s, has safe url: %s, meets media engagement conditions: %s, blocked "
+      "due to content setting: %s}",
+      AutoPipReasonToString(auto_pip_info.auto_pip_reason),
+      auto_pip_info.has_audio_focus ? "true" : "false",
+      auto_pip_info.is_playing ? "true" : "false",
+      auto_pip_info.was_recently_audible ? "true" : "false",
+      auto_pip_info.has_safe_url ? "true" : "false",
+      auto_pip_info.meets_media_engagement_conditions ? "true" : "false",
+      auto_pip_info.blocked_due_to_content_setting ? "true" : "false");
+}
+
 }  // namespace media
diff --git a/media/base/picture_in_picture_events_info.h b/media/base/picture_in_picture_events_info.h
index 5cb534dd..dc4e5ffa 100644
--- a/media/base/picture_in_picture_events_info.h
+++ b/media/base/picture_in_picture_events_info.h
@@ -35,9 +35,20 @@
     kMaxValue = kMediaPlayback,
   };
 
+  struct MEDIA_EXPORT AutoPipInfo {
+    AutoPipReason auto_pip_reason = AutoPipReason::kUnknown;
+    bool has_audio_focus = false;
+    bool is_playing = false;
+    bool was_recently_audible = false;
+    bool has_safe_url = false;
+    bool meets_media_engagement_conditions = false;
+    bool blocked_due_to_content_setting = false;
+  };
+
   using AutoPipReasonCallback = base::RepeatingCallback<AutoPipReason(void)>;
 
   static std::string AutoPipReasonToString(AutoPipReason auto_pip_reason);
+  static std::string AutoPipInfoToString(AutoPipInfo auto_pip_info);
 };
 }  // namespace media