Auto Picture-in-Picture should apply only in the scope of the web app

TBR=beaufort.francois@gmail.com

(cherry picked from commit 0a5d0c6838cb1468b26dc2ec93eb26e382106adb)

Bug: 922884
Change-Id: Iab91cfdf83a8022b38a073532f9052732bb44296
Reviewed-on: https://chromium-review.googlesource.com/c/1436357
Auto-Submit: François Beaufort <beaufort.francois@gmail.com>
Commit-Queue: François Beaufort <beaufort.francois@gmail.com>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Reviewed-by: Mounir Lamouri <mlamouri@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#628072}
Reviewed-on: https://chromium-review.googlesource.com/c/1451818
Reviewed-by: François Beaufort <beaufort.francois@gmail.com>
Cr-Commit-Position: refs/branch-heads/3683@{#157}
Cr-Branched-From: e51029943e0a38dd794b73caaf6373d5496ae783-refs/heads/master@{#625896}
diff --git a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
index fb46e4c..f27fa6b 100644
--- a/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
+++ b/chrome/browser/picture_in_picture/picture_in_picture_window_controller_browsertest.cc
@@ -8,6 +8,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/scoped_feature_list.h"
 #include "build/build_config.h"
+#include "chrome/browser/chrome_content_browser_client.h"
 #include "chrome/browser/devtools/devtools_window_testing.h"
 #include "chrome/browser/extensions/browsertest_util.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
@@ -25,8 +26,10 @@
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/overlay_window.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/content_switches.h"
+#include "content/public/common/web_preferences.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "media/base/media_switches.h"
@@ -2009,6 +2012,31 @@
   EXPECT_TRUE(in_picture_in_picture);
 }
 
+namespace {
+
+class ChromeContentBrowserClientOverrideWebAppScope
+    : public ChromeContentBrowserClient {
+ public:
+  ChromeContentBrowserClientOverrideWebAppScope() = default;
+  ~ChromeContentBrowserClientOverrideWebAppScope() override = default;
+
+  void OverrideWebkitPrefs(content::RenderViewHost* rvh,
+                           content::WebPreferences* web_prefs) override {
+    ChromeContentBrowserClient::OverrideWebkitPrefs(rvh, web_prefs);
+
+    web_prefs->web_app_scope = web_app_scope_;
+  }
+
+  void set_web_app_scope(const GURL& web_app_scope) {
+    web_app_scope_ = web_app_scope;
+  }
+
+ private:
+  GURL web_app_scope_;
+};
+
+}  // namespace
+
 class WebAppPictureInPictureWindowControllerBrowserTest
     : public extensions::ExtensionBrowserTest {
  public:
@@ -2045,6 +2073,20 @@
     web_contents_ = app_browser->tab_strip_model()->GetActiveWebContents();
     EXPECT_TRUE(content::WaitForLoadStop(web_contents_));
     ASSERT_NE(nullptr, web_contents_);
+
+    SetWebAppScope(app_url.GetOrigin());
+  }
+
+  void SetWebAppScope(const GURL web_app_scope) {
+    ChromeContentBrowserClientOverrideWebAppScope browser_client_;
+    browser_client_.set_web_app_scope(web_app_scope);
+
+    content::ContentBrowserClient* original_browser_client_ =
+        content::SetBrowserClientForTesting(&browser_client_);
+
+    web_contents_->GetRenderViewHost()->OnWebkitPreferencesChanged();
+
+    content::SetBrowserClientForTesting(original_browser_client_);
   }
 
   content::WebContents* web_contents() { return web_contents_; }
@@ -2083,6 +2125,34 @@
 }
 
 // Show pwa page and check that Auto Picture-in-Picture is not triggered if
+// document is not inside the scope specified in the Web App Manifest.
+IN_PROC_BROWSER_TEST_F(
+    WebAppPictureInPictureWindowControllerBrowserTest,
+    AutoPictureInPictureNotTriggeredIfDocumentNotInWebAppScope) {
+  InstallAndLaunchPWA();
+  SetWebAppScope(GURL("http://www.foobar.com"));
+  bool result = false;
+  ASSERT_TRUE(content::ExecuteScriptAndExtractBool(web_contents(),
+                                                   "playVideo();", &result));
+  ASSERT_TRUE(result);
+  ASSERT_TRUE(content::ExecuteScript(web_contents(),
+                                     "video.autoPictureInPicture = true;"));
+
+  // Hide page and check that the video did not entered
+  // Picture-in-Picture automatically.
+  web_contents()->WasHidden();
+  base::string16 expected_title = base::ASCIIToUTF16("hidden");
+  EXPECT_EQ(
+      expected_title,
+      content::TitleWatcher(web_contents(), expected_title).WaitAndGetTitle());
+
+  bool in_picture_in_picture = false;
+  ASSERT_TRUE(ExecuteScriptAndExtractBool(
+      web_contents(), "isInPictureInPicture();", &in_picture_in_picture));
+  EXPECT_FALSE(in_picture_in_picture);
+}
+
+// Show pwa page and check that Auto Picture-in-Picture is not triggered if
 // video is not playing.
 IN_PROC_BROWSER_TEST_F(WebAppPictureInPictureWindowControllerBrowserTest,
                        AutoPictureInPictureNotTriggeredIfVideoNotPlaying) {
diff --git a/third_party/blink/renderer/core/dom/document.cc b/third_party/blink/renderer/core/dom/document.cc
index 21ef6fd..e5d02bd5 100644
--- a/third_party/blink/renderer/core/dom/document.cc
+++ b/third_party/blink/renderer/core/dom/document.cc
@@ -7894,6 +7894,18 @@
     javascript_url_task_handle_.Cancel();
 }
 
+bool Document::IsInWebAppScope() const {
+  if (!GetSettings())
+    return false;
+
+  const String& web_app_scope = GetSettings()->GetWebAppScope();
+  if (web_app_scope.IsNull() || web_app_scope.IsEmpty())
+    return false;
+
+  DCHECK_EQ(KURL(web_app_scope).GetString(), web_app_scope);
+  return Url().GetString().StartsWith(web_app_scope);
+}
+
 void Document::SendViolationReport(
     mojom::blink::CSPViolationParamsPtr violation_params) {
   std::unique_ptr<SourceLocation> source_location = SourceLocation::Create(
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h
index f71aae0..f0f7511 100644
--- a/third_party/blink/renderer/core/dom/document.h
+++ b/third_party/blink/renderer/core/dom/document.h
@@ -1515,6 +1515,11 @@
   void ProcessJavaScriptUrl(const KURL&, ContentSecurityPolicyDisposition);
   void CancelPendingJavaScriptUrl();
 
+  // Returns whether the document is inside the scope specified in the Web App
+  // Manifest. If the document doesn't run in a context of a Web App or has no
+  // associated Web App Manifest, it will return false.
+  bool IsInWebAppScope() const;
+
  protected:
   void DidUpdateSecurityOrigin() final;
 
diff --git a/third_party/blink/renderer/core/html/media/autoplay_policy.cc b/third_party/blink/renderer/core/html/media/autoplay_policy.cc
index ac0f420..b4b4209 100644
--- a/third_party/blink/renderer/core/html/media/autoplay_policy.cc
+++ b/third_party/blink/renderer/core/html/media/autoplay_policy.cc
@@ -43,20 +43,6 @@
   return frame && frame->IsCrossOriginSubframe();
 }
 
-// Returns whether |document| is whitelisted for autoplay. If true, the user
-// gesture lock will be initilized as false, indicating that the element is
-// allowed to autoplay unmuted without user gesture.
-bool IsDocumentWhitelisted(const Document& document) {
-  DCHECK(document.GetSettings());
-
-  const String& web_app_scope = document.GetSettings()->GetWebAppScope();
-  if (web_app_scope.IsNull() || web_app_scope.IsEmpty())
-    return false;
-
-  DCHECK_EQ(KURL(web_app_scope).GetString(), web_app_scope);
-  return document.Url().GetString().StartsWith(web_app_scope);
-}
-
 // Return true if and only if the document settings specifies media playback
 // requires user gesture on the element.
 bool ComputeLockPendingUserGestureRequired(const Document& document) {
@@ -86,7 +72,7 @@
   if (!document.GetSettings())
     return Type::kNoUserGestureRequired;
 
-  if (IsDocumentWhitelisted(document))
+  if (document.IsInWebAppScope())
     return Type::kNoUserGestureRequired;
 
   if (DocumentHasUserExceptionFlag(document))
diff --git a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
index c6ed468..6d722e0 100644
--- a/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
+++ b/third_party/blink/renderer/modules/picture_in_picture/picture_in_picture_controller_impl.cc
@@ -241,7 +241,6 @@
   DCHECK(GetSupplementable());
 
   // Auto Picture-in-Picture is allowed only in a PWA window.
-  // TODO(crbug.com/922884) It should apply in the scope of the manifest.
   if (!GetSupplementable()->GetFrame() ||
       !GetSupplementable()->GetFrame()->View() ||
       GetSupplementable()->GetFrame()->View()->DisplayMode() ==
@@ -249,6 +248,10 @@
     return;
   }
 
+  // Auto Picture-in-Picture is allowed only in the scope of a PWA.
+  if (!GetSupplementable()->IsInWebAppScope())
+    return;
+
   // If page becomes visible and Picture-in-Picture element has entered
   // automatically Picture-in-Picture and is still eligible to Auto
   // Picture-in-Picture, exit Picture-in-Picture.