Entering/Exiting Picture-in-Picture mode through webkitSetPresentationMode() does not fire events (enterpictureinpicture and leavepictureinpicture) defined in the spec
https://bugs.webkit.org/show_bug.cgi?id=203989

Patch by Peng Liu <peng.liu6@apple.com> on 2019-11-08
Reviewed by Eric Carlson.

Source/WebCore:

Instantiate a HTMLVideoElementPictureInPicture object when we create a new video element
so that the events defined in the spec will be fired no matter which interface is used
to enter/exit the Picture-in-Picture mode.

Test: media/picture-in-picture/picture-in-picture-events.html

* Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp:
(WebCore::HTMLVideoElementPictureInPicture::providePictureInPictureTo):
* Modules/pictureinpicture/HTMLVideoElementPictureInPicture.h:
* WebCore.xcodeproj/project.pbxproj:
* html/HTMLVideoElement.cpp:
(WebCore::HTMLVideoElement::create):

LayoutTests:

Move all layout tests for Picture-in-Picture to folder media/picture-in-picture.
Add a layout test to verify the events defined in the spec are fired when we call the webkit prefixed API.
Update TestExpectations files since webkit.org/b/203614 has been fixed.

* TestExpectations:
* media/picture-in-picture/picture-in-picture-api-element-attributes-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-element-attributes-expected.txt.
* media/picture-in-picture/picture-in-picture-api-element-attributes.html: Renamed from LayoutTests/media/picture-in-picture-api-element-attributes.html.
* media/picture-in-picture/picture-in-picture-api-enter-pip-1-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-1-expected.txt.
* media/picture-in-picture/picture-in-picture-api-enter-pip-1.html: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-1.html.
* media/picture-in-picture/picture-in-picture-api-enter-pip-2-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-2-expected.txt.
* media/picture-in-picture/picture-in-picture-api-enter-pip-2.html: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-2.html.
* media/picture-in-picture/picture-in-picture-api-enter-pip-3-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-3-expected.txt.
* media/picture-in-picture/picture-in-picture-api-enter-pip-3.html: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-3.html.
* media/picture-in-picture/picture-in-picture-api-enter-pip-4-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-4-expected.txt.
* media/picture-in-picture/picture-in-picture-api-enter-pip-4.html: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-4.html.
* media/picture-in-picture/picture-in-picture-api-events-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-pip-events-expected.txt.
* media/picture-in-picture/picture-in-picture-api-events.html: Renamed from LayoutTests/media/picture-in-picture-api-pip-events.html.
* media/picture-in-picture/picture-in-picture-api-exit-pip-1-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-exit-pip-1-expected.txt.
* media/picture-in-picture/picture-in-picture-api-exit-pip-1.html: Renamed from LayoutTests/media/picture-in-picture-api-exit-pip-1.html.
* media/picture-in-picture/picture-in-picture-api-exit-pip-2-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-exit-pip-2-expected.txt.
* media/picture-in-picture/picture-in-picture-api-exit-pip-2.html: Renamed from LayoutTests/media/picture-in-picture-api-exit-pip-2.html.
* media/picture-in-picture/picture-in-picture-api-pip-window-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-pip-window-expected.txt.
* media/picture-in-picture/picture-in-picture-api-pip-window.html: Renamed from LayoutTests/media/picture-in-picture-api-pip-window.html.
* media/picture-in-picture/picture-in-picture-events-expected.txt: Added.
* media/picture-in-picture/picture-in-picture-events.html: Added.
* media/picture-in-picture/picture-in-picture-interruption-expected.txt: Renamed from LayoutTests/media/picture-in-picture-interruption-expected.txt.
* media/picture-in-picture/picture-in-picture-interruption.html: Renamed from LayoutTests/media/picture-in-picture-interruption.html.
* platform/gtk/TestExpectations:
* platform/ios/TestExpectations:
* platform/ipad/TestExpectations:
* platform/mac-wk2/TestExpectations:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@252276 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 1838510..2e29081 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,42 @@
+2019-11-08  Peng Liu  <peng.liu6@apple.com>
+
+        Entering/Exiting Picture-in-Picture mode through webkitSetPresentationMode() does not fire events (enterpictureinpicture and leavepictureinpicture) defined in the spec
+        https://bugs.webkit.org/show_bug.cgi?id=203989
+
+        Reviewed by Eric Carlson.
+
+        Move all layout tests for Picture-in-Picture to folder media/picture-in-picture.
+        Add a layout test to verify the events defined in the spec are fired when we call the webkit prefixed API.
+        Update TestExpectations files since webkit.org/b/203614 has been fixed.
+
+        * TestExpectations:
+        * media/picture-in-picture/picture-in-picture-api-element-attributes-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-element-attributes-expected.txt.
+        * media/picture-in-picture/picture-in-picture-api-element-attributes.html: Renamed from LayoutTests/media/picture-in-picture-api-element-attributes.html.
+        * media/picture-in-picture/picture-in-picture-api-enter-pip-1-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-1-expected.txt.
+        * media/picture-in-picture/picture-in-picture-api-enter-pip-1.html: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-1.html.
+        * media/picture-in-picture/picture-in-picture-api-enter-pip-2-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-2-expected.txt.
+        * media/picture-in-picture/picture-in-picture-api-enter-pip-2.html: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-2.html.
+        * media/picture-in-picture/picture-in-picture-api-enter-pip-3-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-3-expected.txt.
+        * media/picture-in-picture/picture-in-picture-api-enter-pip-3.html: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-3.html.
+        * media/picture-in-picture/picture-in-picture-api-enter-pip-4-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-4-expected.txt.
+        * media/picture-in-picture/picture-in-picture-api-enter-pip-4.html: Renamed from LayoutTests/media/picture-in-picture-api-enter-pip-4.html.
+        * media/picture-in-picture/picture-in-picture-api-events-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-pip-events-expected.txt.
+        * media/picture-in-picture/picture-in-picture-api-events.html: Renamed from LayoutTests/media/picture-in-picture-api-pip-events.html.
+        * media/picture-in-picture/picture-in-picture-api-exit-pip-1-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-exit-pip-1-expected.txt.
+        * media/picture-in-picture/picture-in-picture-api-exit-pip-1.html: Renamed from LayoutTests/media/picture-in-picture-api-exit-pip-1.html.
+        * media/picture-in-picture/picture-in-picture-api-exit-pip-2-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-exit-pip-2-expected.txt.
+        * media/picture-in-picture/picture-in-picture-api-exit-pip-2.html: Renamed from LayoutTests/media/picture-in-picture-api-exit-pip-2.html.
+        * media/picture-in-picture/picture-in-picture-api-pip-window-expected.txt: Renamed from LayoutTests/media/picture-in-picture-api-pip-window-expected.txt.
+        * media/picture-in-picture/picture-in-picture-api-pip-window.html: Renamed from LayoutTests/media/picture-in-picture-api-pip-window.html.
+        * media/picture-in-picture/picture-in-picture-events-expected.txt: Added.
+        * media/picture-in-picture/picture-in-picture-events.html: Added.
+        * media/picture-in-picture/picture-in-picture-interruption-expected.txt: Renamed from LayoutTests/media/picture-in-picture-interruption-expected.txt.
+        * media/picture-in-picture/picture-in-picture-interruption.html: Renamed from LayoutTests/media/picture-in-picture-interruption.html.
+        * platform/gtk/TestExpectations:
+        * platform/ios/TestExpectations:
+        * platform/ipad/TestExpectations:
+        * platform/mac-wk2/TestExpectations:
+
 2019-11-08  Chris Dumez  <cdumez@apple.com>
 
         Make DeferredPromise behave nicely with regards to the back/forward cache
diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations
index cadc06b..6644a41 100644
--- a/LayoutTests/TestExpectations
+++ b/LayoutTests/TestExpectations
@@ -197,15 +197,7 @@
 webkit.org/b/202617 imported/w3c/web-platform-tests/picture-in-picture [ Skip ]
 
 # PiP API tests are only relevant on mac and iPad
-media/picture-in-picture-api-element-attributes.html [ Skip ]
-media/picture-in-picture-api-enter-pip-1.html [ Skip ]
-media/picture-in-picture-api-enter-pip-2.html [ Skip ]
-media/picture-in-picture-api-enter-pip-3.html [ Skip ]
-media/picture-in-picture-api-enter-pip-4.html [ Skip ]
-media/picture-in-picture-api-exit-pip-1.html [ Skip ]
-media/picture-in-picture-api-exit-pip-2.html [ Skip ]
-media/picture-in-picture-api-pip-events.html [ Skip ]
-media/picture-in-picture-api-pip-window.html [ Skip ]
+media/picture-in-picture [ Skip ]
 
 # Shared Workers are unsupported
 imported/w3c/web-platform-tests/secure-contexts/basic-shared-worker.html [ Skip ]
diff --git a/LayoutTests/media/picture-in-picture-api-pip-events.html b/LayoutTests/media/picture-in-picture-api-pip-events.html
deleted file mode 100644
index fcee347..0000000
--- a/LayoutTests/media/picture-in-picture-api-pip-events.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-    <script src="video-test.js"></script>
-    <script src="media-file.js"></script>
-    <script>
-        window.addEventListener('load', async event => {
-            findMediaElement();
-
-            run('internals.settings.setAllowsPictureInPictureMediaPlayback(true)');
-            run('internals.setPictureInPictureAPITestEnabled(video, true)');
-
-            run('video.src = findMediaFile("video", "content/test")');
-            await waitFor(video, 'canplaythrough');
-
-            runWithKeyDown(function() { video.requestPictureInPicture(); });
-            await waitFor(video, 'enterpictureinpicture');
-            document.exitPictureInPicture();
-            await waitFor(video, 'leavepictureinpicture');
-
-            endTest();
-        });
-    </script>
-</head>
-<body>
-    <div>This tests that events are fired correctly when a video element enters and exits the Picture-in-Picture mode.</div>
-    <video controls></video>
-</body>
-</html>
diff --git a/LayoutTests/media/picture-in-picture-api-element-attributes-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-api-element-attributes-expected.txt
similarity index 100%
rename from LayoutTests/media/picture-in-picture-api-element-attributes-expected.txt
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-element-attributes-expected.txt
diff --git a/LayoutTests/media/picture-in-picture-api-element-attributes.html b/LayoutTests/media/picture-in-picture/picture-in-picture-api-element-attributes.html
similarity index 93%
rename from LayoutTests/media/picture-in-picture-api-element-attributes.html
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-element-attributes.html
index 8c01719..1fb689a 100644
--- a/LayoutTests/media/picture-in-picture-api-element-attributes.html
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-element-attributes.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <script src="video-test.js"></script>
-    <script src="media-file.js"></script>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
     <script>
         window.addEventListener('load', async event => {
             findMediaElement();
diff --git a/LayoutTests/media/picture-in-picture-api-enter-pip-1-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-1-expected.txt
similarity index 82%
rename from LayoutTests/media/picture-in-picture-api-enter-pip-1-expected.txt
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-1-expected.txt
index a1c782b..5125e53 100644
--- a/LayoutTests/media/picture-in-picture-api-enter-pip-1-expected.txt
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-1-expected.txt
@@ -2,7 +2,7 @@
 
 RUN(internals.settings.setAllowsPictureInPictureMediaPlayback(true))
 RUN(internals.setPictureInPictureAPITestEnabled(video, true))
-RUN(video.src = findMediaFile("video", "content/test"))
+RUN(video.src = findMediaFile("video", "../content/test"))
 EVENT(canplaythrough)
 EXPECTED (error.name == 'NotAllowedError') OK
 END OF TEST
diff --git a/LayoutTests/media/picture-in-picture-api-enter-pip-1.html b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-1.html
similarity index 83%
rename from LayoutTests/media/picture-in-picture-api-enter-pip-1.html
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-1.html
index ef84ab1..79d8fdf 100644
--- a/LayoutTests/media/picture-in-picture-api-enter-pip-1.html
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-1.html
@@ -1,15 +1,15 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <script src="video-test.js"></script>
-    <script src="media-file.js"></script>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
     <script>
         window.addEventListener('load', async event => {
             findMediaElement();
 
             run('internals.settings.setAllowsPictureInPictureMediaPlayback(true)');
             run('internals.setPictureInPictureAPITestEnabled(video, true)');
-            run('video.src = findMediaFile("video", "content/test")');
+            run('video.src = findMediaFile("video", "../content/test")');
             await waitFor(video, 'canplaythrough');
 
             video.requestPictureInPicture()
diff --git a/LayoutTests/media/picture-in-picture-api-enter-pip-2-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-2-expected.txt
similarity index 100%
rename from LayoutTests/media/picture-in-picture-api-enter-pip-2-expected.txt
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-2-expected.txt
diff --git a/LayoutTests/media/picture-in-picture-api-enter-pip-2.html b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-2.html
similarity index 90%
rename from LayoutTests/media/picture-in-picture-api-enter-pip-2.html
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-2.html
index 68a5052..93cf8fd 100644
--- a/LayoutTests/media/picture-in-picture-api-enter-pip-2.html
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-2.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <script src="video-test.js"></script>
-    <script src="media-file.js"></script>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
     <script>
         window.addEventListener('load', async event => {
             findMediaElement();
diff --git a/LayoutTests/media/picture-in-picture-api-enter-pip-3-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-3-expected.txt
similarity index 83%
rename from LayoutTests/media/picture-in-picture-api-enter-pip-3-expected.txt
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-3-expected.txt
index 09ad5f3..8fc9c7f 100644
--- a/LayoutTests/media/picture-in-picture-api-enter-pip-3-expected.txt
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-3-expected.txt
@@ -2,7 +2,7 @@
 
 RUN(internals.settings.setAllowsPictureInPictureMediaPlayback(true))
 RUN(internals.setPictureInPictureAPITestEnabled(video, true))
-RUN(video.src = findMediaFile("audio", "content/test"))
+RUN(video.src = findMediaFile("audio", "../content/test"))
 EVENT(canplaythrough)
 EXPECTED (error.name == 'InvalidStateError') OK
 END OF TEST
diff --git a/LayoutTests/media/picture-in-picture-api-enter-pip-3.html b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-3.html
similarity index 85%
rename from LayoutTests/media/picture-in-picture-api-enter-pip-3.html
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-3.html
index b8adc6f..7b82ec9 100644
--- a/LayoutTests/media/picture-in-picture-api-enter-pip-3.html
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-3.html
@@ -1,15 +1,15 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <script src="video-test.js"></script>
-    <script src="media-file.js"></script>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
     <script>
         window.addEventListener('load', async event => {
             findMediaElement();
 
             run('internals.settings.setAllowsPictureInPictureMediaPlayback(true)');
             run('internals.setPictureInPictureAPITestEnabled(video, true)');
-            run('video.src = findMediaFile("audio", "content/test")');
+            run('video.src = findMediaFile("audio", "../content/test")');
             await waitFor(video, 'canplaythrough');
             runWithKeyDown(function() {
                 video.requestPictureInPicture()
diff --git a/LayoutTests/media/picture-in-picture-api-enter-pip-4-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-4-expected.txt
similarity index 79%
rename from LayoutTests/media/picture-in-picture-api-enter-pip-4-expected.txt
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-4-expected.txt
index 41ab9cd..fea02a5 100644
--- a/LayoutTests/media/picture-in-picture-api-enter-pip-4-expected.txt
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-4-expected.txt
@@ -2,7 +2,7 @@
 
 RUN(internals.settings.setAllowsPictureInPictureMediaPlayback(true))
 RUN(internals.setPictureInPictureAPITestEnabled(video, true))
-RUN(video.src = findMediaFile("video", "content/test"))
+RUN(video.src = findMediaFile("video", "../content/test"))
 EVENT(canplaythrough)
 END OF TEST
 
diff --git a/LayoutTests/media/picture-in-picture-api-enter-pip-4.html b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-4.html
similarity index 85%
rename from LayoutTests/media/picture-in-picture-api-enter-pip-4.html
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-4.html
index 6fa7209..f1bb07b 100644
--- a/LayoutTests/media/picture-in-picture-api-enter-pip-4.html
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-enter-pip-4.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <script src="video-test.js"></script>
-    <script src="media-file.js"></script>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
     <script>
         window.addEventListener('load', async event => {
             findMediaElement();
@@ -10,7 +10,7 @@
             run('internals.settings.setAllowsPictureInPictureMediaPlayback(true)');
             run('internals.setPictureInPictureAPITestEnabled(video, true)');
 
-            run('video.src = findMediaFile("video", "content/test")');
+            run('video.src = findMediaFile("video", "../content/test")');
             await waitFor(video, 'canplaythrough');
             runWithKeyDown(function() {
                 video.requestPictureInPicture()
diff --git a/LayoutTests/media/picture-in-picture-api-pip-events-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-api-events-expected.txt
similarity index 71%
rename from LayoutTests/media/picture-in-picture-api-pip-events-expected.txt
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-events-expected.txt
index ee42d53..9881594 100644
--- a/LayoutTests/media/picture-in-picture-api-pip-events-expected.txt
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-events-expected.txt
@@ -2,9 +2,11 @@
 
 RUN(internals.settings.setAllowsPictureInPictureMediaPlayback(true))
 RUN(internals.setPictureInPictureAPITestEnabled(video, true))
-RUN(video.src = findMediaFile("video", "content/test"))
+RUN(video.src = findMediaFile("video", "../content/test"))
 EVENT(canplaythrough)
 EVENT(enterpictureinpicture)
+EXPECTED (pipWindow.width > '0') OK
+EXPECTED (pipWindow.height > '0') OK
 EVENT(leavepictureinpicture)
 END OF TEST
 
diff --git a/LayoutTests/media/picture-in-picture/picture-in-picture-api-events.html b/LayoutTests/media/picture-in-picture/picture-in-picture-api-events.html
new file mode 100644
index 0000000..de46830
--- /dev/null
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-events.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
+    <script>
+        window.addEventListener('load', async event => {
+            findMediaElement();
+
+            run('internals.settings.setAllowsPictureInPictureMediaPlayback(true)');
+            run('internals.setPictureInPictureAPITestEnabled(video, true)');
+
+            run('video.src = findMediaFile("video", "../content/test")');
+            await waitFor(video, 'canplaythrough');
+
+            runWithKeyDown(function() { video.requestPictureInPicture(); });
+            waitForEventOnce('enterpictureinpicture', event => {
+                window.pipWindow = event.pictureInPictureWindow;
+                testExpected('pipWindow.width', 0, '>');
+                testExpected('pipWindow.height', 0, '>');
+
+                document.exitPictureInPicture();
+                waitForEventAndEnd('leavepictureinpicture');
+            });
+        });
+    </script>
+</head>
+<body>
+    <div>This tests that events are fired correctly when a video element enters and exits the Picture-in-Picture mode.</div>
+    <video controls></video>
+</body>
+</html>
diff --git a/LayoutTests/media/picture-in-picture-api-exit-pip-1-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-1-expected.txt
similarity index 82%
rename from LayoutTests/media/picture-in-picture-api-exit-pip-1-expected.txt
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-1-expected.txt
index 05b9919..5c1eabc 100644
--- a/LayoutTests/media/picture-in-picture-api-exit-pip-1-expected.txt
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-1-expected.txt
@@ -2,7 +2,7 @@
 
 RUN(internals.settings.setAllowsPictureInPictureMediaPlayback(true))
 RUN(internals.setPictureInPictureAPITestEnabled(video, true))
-RUN(video.src = findMediaFile("video", "content/test"))
+RUN(video.src = findMediaFile("video", "../content/test"))
 EVENT(canplaythrough)
 EVENT(enterpictureinpicture)
 END OF TEST
diff --git a/LayoutTests/media/picture-in-picture-api-exit-pip-1.html b/LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-1.html
similarity index 83%
rename from LayoutTests/media/picture-in-picture-api-exit-pip-1.html
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-1.html
index 4169554..bf58866 100644
--- a/LayoutTests/media/picture-in-picture-api-exit-pip-1.html
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-1.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <script src="video-test.js"></script>
-    <script src="media-file.js"></script>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
     <script>
         window.addEventListener('load', async event => {
             findMediaElement();
@@ -10,7 +10,7 @@
             run('internals.settings.setAllowsPictureInPictureMediaPlayback(true)');
             run('internals.setPictureInPictureAPITestEnabled(video, true)');
 
-            run('video.src = findMediaFile("video", "content/test")');
+            run('video.src = findMediaFile("video", "../content/test")');
             await waitFor(video, 'canplaythrough');
 
             runWithKeyDown(function() { video.requestPictureInPicture() });
diff --git a/LayoutTests/media/picture-in-picture-api-exit-pip-2-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-2-expected.txt
similarity index 100%
rename from LayoutTests/media/picture-in-picture-api-exit-pip-2-expected.txt
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-2-expected.txt
diff --git a/LayoutTests/media/picture-in-picture-api-exit-pip-2.html b/LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-2.html
similarity index 90%
rename from LayoutTests/media/picture-in-picture-api-exit-pip-2.html
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-2.html
index 36c3b4e..4cdc407 100644
--- a/LayoutTests/media/picture-in-picture-api-exit-pip-2.html
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-exit-pip-2.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <script src="video-test.js"></script>
-    <script src="media-file.js"></script>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
     <script>
         window.addEventListener('load', async event => {
             findMediaElement();
diff --git a/LayoutTests/media/picture-in-picture-api-pip-window-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-api-pip-window-expected.txt
similarity index 85%
rename from LayoutTests/media/picture-in-picture-api-pip-window-expected.txt
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-pip-window-expected.txt
index 19a5dee..4e68a7d 100644
--- a/LayoutTests/media/picture-in-picture-api-pip-window-expected.txt
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-pip-window-expected.txt
@@ -2,7 +2,7 @@
 
 RUN(internals.settings.setAllowsPictureInPictureMediaPlayback(true))
 RUN(internals.setPictureInPictureAPITestEnabled(video, true))
-RUN(video.src = findMediaFile("video", "content/test"))
+RUN(video.src = findMediaFile("video", "../content/test"))
 EVENT(canplaythrough)
 EXPECTED (pipWindow.width > '0') OK
 EXPECTED (pipWindow.height > '0') OK
diff --git a/LayoutTests/media/picture-in-picture-api-pip-window.html b/LayoutTests/media/picture-in-picture/picture-in-picture-api-pip-window.html
similarity index 87%
rename from LayoutTests/media/picture-in-picture-api-pip-window.html
rename to LayoutTests/media/picture-in-picture/picture-in-picture-api-pip-window.html
index c0ff1d3..cc7e624 100644
--- a/LayoutTests/media/picture-in-picture-api-pip-window.html
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-api-pip-window.html
@@ -1,8 +1,8 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <script src="video-test.js"></script>
-    <script src="media-file.js"></script>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
     <script>
         window.addEventListener('load', async event => {
             findMediaElement();
@@ -10,7 +10,7 @@
             run('internals.settings.setAllowsPictureInPictureMediaPlayback(true)');
             run('internals.setPictureInPictureAPITestEnabled(video, true)');
 
-            run('video.src = findMediaFile("video", "content/test")');
+            run('video.src = findMediaFile("video", "../content/test")');
             await waitFor(video, 'canplaythrough');
 
             runWithKeyDown(function() {
diff --git a/LayoutTests/media/picture-in-picture/picture-in-picture-events-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-events-expected.txt
new file mode 100644
index 0000000..be2ea40
--- /dev/null
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-events-expected.txt
@@ -0,0 +1,12 @@
+This tests that events are fired correctly when we use the webkit prefixed API to enters and exits the Picture-in-Picture mode.
+
+RUN(internals.settings.setAllowsPictureInPictureMediaPlayback(true))
+RUN(internals.setPictureInPictureAPITestEnabled(video, true))
+RUN(video.src = findMediaFile("video", "../content/test"))
+EVENT(canplaythrough)
+EVENT(enterpictureinpicture)
+EXPECTED (pipWindow.width > '0') OK
+EXPECTED (pipWindow.height > '0') OK
+EVENT(leavepictureinpicture)
+END OF TEST
+
diff --git a/LayoutTests/media/picture-in-picture/picture-in-picture-events.html b/LayoutTests/media/picture-in-picture/picture-in-picture-events.html
new file mode 100644
index 0000000..0ea0911
--- /dev/null
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-events.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
+    <script>
+        window.addEventListener('load', async event => {
+            findMediaElement();
+
+            run('internals.settings.setAllowsPictureInPictureMediaPlayback(true)');
+            run('internals.setPictureInPictureAPITestEnabled(video, true)');
+
+            run('video.src = findMediaFile("video", "../content/test")');
+            await waitFor(video, 'canplaythrough');
+
+            runWithKeyDown(function() { video.webkitSetPresentationMode('picture-in-picture'); });
+            waitForEventOnce('enterpictureinpicture', event => {
+                window.pipWindow = event.pictureInPictureWindow;
+                testExpected('pipWindow.width', 0, '>');
+                testExpected('pipWindow.height', 0, '>');
+
+                video.webkitSetPresentationMode('inline');
+                waitForEventAndEnd('leavepictureinpicture');
+            });
+        });
+    </script>
+</head>
+<body>
+    <div>This tests that events are fired correctly when we use the webkit prefixed API to enters and exits the Picture-in-Picture mode.</div>
+    <video controls></video>
+</body>
+</html>
diff --git a/LayoutTests/media/picture-in-picture-interruption-expected.txt b/LayoutTests/media/picture-in-picture/picture-in-picture-interruption-expected.txt
similarity index 89%
rename from LayoutTests/media/picture-in-picture-interruption-expected.txt
rename to LayoutTests/media/picture-in-picture/picture-in-picture-interruption-expected.txt
index 1079d0b..332f1f3 100644
--- a/LayoutTests/media/picture-in-picture-interruption-expected.txt
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-interruption-expected.txt
@@ -1,7 +1,7 @@
 This tests that video is in the correct media session state after ending a background interruption while pipped.
 
 RUN(internals.settings.setAllowsPictureInPictureMediaPlayback(true))
-RUN(video.src = findMediaFile("video", "content/test"))
+RUN(video.src = findMediaFile("video", "../content/test"))
 EVENT(canplaythrough)
 RUN(video.play())
 EVENT(playing)
diff --git a/LayoutTests/media/picture-in-picture-interruption.html b/LayoutTests/media/picture-in-picture/picture-in-picture-interruption.html
similarity index 86%
rename from LayoutTests/media/picture-in-picture-interruption.html
rename to LayoutTests/media/picture-in-picture/picture-in-picture-interruption.html
index aac6530..60922a6 100644
--- a/LayoutTests/media/picture-in-picture-interruption.html
+++ b/LayoutTests/media/picture-in-picture/picture-in-picture-interruption.html
@@ -1,15 +1,15 @@
 <!DOCTYPE html>
 <html>
 <head>
-    <script src="video-test.js"></script>
-    <script src="media-file.js"></script>
+    <script src="../video-test.js"></script>
+    <script src="../media-file.js"></script>
     <script>
         window.addEventListener('load', async event => {
             findMediaElement();
 
             run('internals.settings.setAllowsPictureInPictureMediaPlayback(true)');
 
-            run('video.src = findMediaFile("video", "content/test")');
+            run('video.src = findMediaFile("video", "../content/test")');
             await waitFor(video, 'canplaythrough');
 
             run('video.play()');
diff --git a/LayoutTests/platform/gtk/TestExpectations b/LayoutTests/platform/gtk/TestExpectations
index d2287a1..f189cea 100644
--- a/LayoutTests/platform/gtk/TestExpectations
+++ b/LayoutTests/platform/gtk/TestExpectations
@@ -2478,7 +2478,7 @@
 
 webkit.org/b/187266 imported/w3c/web-platform-tests/html/semantics/embedded-content/media-elements/track/track-element/track-api-texttracks.html [ Pass Timeout ]
 
-webkit.org/b/187273 media/picture-in-picture-interruption.html [ Timeout ]
+webkit.org/b/187273 media/picture-in-picture/picture-in-picture-interruption.html [ Timeout ]
 
 webkit.org/b/188103 imported/w3c/web-platform-tests/mathml/presentation-markup/fractions/frac-parameters-1.html [ Failure Pass ]
 webkit.org/b/188103 imported/w3c/web-platform-tests/mathml/presentation-markup/fractions/frac-parameters-2.html [ Failure Pass ]
diff --git a/LayoutTests/platform/ios/TestExpectations b/LayoutTests/platform/ios/TestExpectations
index 82ddad6..b28fcef 100644
--- a/LayoutTests/platform/ios/TestExpectations
+++ b/LayoutTests/platform/ios/TestExpectations
@@ -991,7 +991,7 @@
 http/tests/resourceLoadStatistics/prevalent-resource-handled-keydown-database.html [ Skip ]
 media/audio-playback-volume-changes-with-restrictions-and-user-gestures.html [ Skip ]
 media/audio-playback-volume-changes-with-restrictions.html [ Skip ]
-media/picture-in-picture-interruption.html [ Skip ]
+media/picture-in-picture/picture-in-picture-interruption.html [ Skip ]
 scrollbars/scrolling-backward-by-page-accounting-bottom-fixed-elements-on-keyboard-spacebar.html [ Skip ]
 scrollbars/scrolling-backward-by-page-on-keyboard-spacebar.html [ Skip ]
 scrollbars/scrolling-by-page-accounting-oversized-fixed-elements-on-keyboard-spacebar.html [ Skip ]
diff --git a/LayoutTests/platform/ipad/TestExpectations b/LayoutTests/platform/ipad/TestExpectations
index e33e3ed..a985170 100644
--- a/LayoutTests/platform/ipad/TestExpectations
+++ b/LayoutTests/platform/ipad/TestExpectations
@@ -67,12 +67,4 @@
 webkit.org/b/203264 editing/pasteboard/smart-paste-paragraph-002.html [ Pass ]
 webkit.org/b/203264 editing/pasteboard/smart-paste-paragraph-004.html [ Pass ]
 
-media/picture-in-picture-api-element-attributes.html [ Pass ]
-media/picture-in-picture-api-enter-pip-1.html [ Pass ]
-media/picture-in-picture-api-enter-pip-2.html [ Pass ]
-media/picture-in-picture-api-enter-pip-3.html [ Pass ]
-webkit.org/b/203614 media/picture-in-picture-api-enter-pip-4.html [ Pass Timeout ]
-webkit.org/b/203614 media/picture-in-picture-api-exit-pip-1.html [ Pass Timeout ]
-media/picture-in-picture-api-exit-pip-2.html [ Pass ]
-webkit.org/b/203614 media/picture-in-picture-api-pip-events.html [ Pass Timeout ]
-webkit.org/b/203614 media/picture-in-picture-api-pip-window.html [ Pass Timeout ]
\ No newline at end of file
+media/picture-in-picture [ Pass ]
\ No newline at end of file
diff --git a/LayoutTests/platform/mac-wk2/TestExpectations b/LayoutTests/platform/mac-wk2/TestExpectations
index 86ca787..181ef92 100644
--- a/LayoutTests/platform/mac-wk2/TestExpectations
+++ b/LayoutTests/platform/mac-wk2/TestExpectations
@@ -541,15 +541,7 @@
 webkit.org/b/172998 [ Sierra+ ] media/fullscreen-api-enabled-media-with-presentation-mode.html [ Failure ]
 webkit.org/b/173199 [ Sierra+ ] media/navigate-with-pip-should-not-crash.html [ Pass Failure ]
 [ Sierra+ ] media/pip-video-going-into-fullscreen.html [ Pass ]
-[ Sierra+ ] media/picture-in-picture-api-element-attributes.html [ Pass ]
-[ Sierra+ ] media/picture-in-picture-api-enter-pip-1.html [ Pass ]
-[ Sierra+ ] media/picture-in-picture-api-enter-pip-2.html [ Pass ]
-[ Sierra+ ] media/picture-in-picture-api-enter-pip-3.html [ Pass ]
-webkit.org/b/203614 [ Sierra+ ] media/picture-in-picture-api-enter-pip-4.html [ Pass Timeout ]
-webkit.org/b/203614 [ Sierra+ ] media/picture-in-picture-api-exit-pip-1.html [ Pass Timeout ]
-[ Sierra+ ] media/picture-in-picture-api-exit-pip-2.html [ Pass ]
-webkit.org/b/203614 [ Sierra+ ] media/picture-in-picture-api-pip-events.html [ Pass Timeout ]
-webkit.org/b/203614 [ Sierra+ ] media/picture-in-picture-api-pip-window.html [ Pass Timeout ]
+[ Sierra+ ] media/picture-in-picture [ Pass ]
 
 # RTL Scrollbars are enabled on Sierra WebKit2.
 webkit.org/b/179455 [ Sierra+ ] fast/scrolling/rtl-scrollbars.html [ Pass ImageOnlyFailure ]
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 1a4252f..9f6fbb3 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,23 @@
+2019-11-08  Peng Liu  <peng.liu6@apple.com>
+
+        Entering/Exiting Picture-in-Picture mode through webkitSetPresentationMode() does not fire events (enterpictureinpicture and leavepictureinpicture) defined in the spec
+        https://bugs.webkit.org/show_bug.cgi?id=203989
+
+        Reviewed by Eric Carlson.
+
+        Instantiate a HTMLVideoElementPictureInPicture object when we create a new video element
+        so that the events defined in the spec will be fired no matter which interface is used
+        to enter/exit the Picture-in-Picture mode.
+
+        Test: media/picture-in-picture/picture-in-picture-events.html
+
+        * Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp:
+        (WebCore::HTMLVideoElementPictureInPicture::providePictureInPictureTo):
+        * Modules/pictureinpicture/HTMLVideoElementPictureInPicture.h:
+        * WebCore.xcodeproj/project.pbxproj:
+        * html/HTMLVideoElement.cpp:
+        (WebCore::HTMLVideoElement::create):
+
 2019-11-08  Chris Dumez  <cdumez@apple.com>
 
         Make DeferredPromise behave nicely with regards to the back/forward cache
diff --git a/Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp b/Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp
index 1b37df7..7b24df5 100644
--- a/Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp
+++ b/Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.cpp
@@ -71,6 +71,11 @@
     return supplement;
 }
 
+void HTMLVideoElementPictureInPicture::providePictureInPictureTo(HTMLVideoElement& videoElement)
+{
+    provideTo(&videoElement, supplementName(), makeUnique<HTMLVideoElementPictureInPicture>(videoElement));
+}
+
 void HTMLVideoElementPictureInPicture::requestPictureInPicture(HTMLVideoElement& videoElement, Ref<DeferredPromise>&& promise)
 {
     if (!supportsPictureInPicture()) {
diff --git a/Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.h b/Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.h
index e4f0624..69fbb2d 100644
--- a/Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.h
+++ b/Source/WebCore/Modules/pictureinpicture/HTMLVideoElementPictureInPicture.h
@@ -50,6 +50,7 @@
 public:
     HTMLVideoElementPictureInPicture(HTMLVideoElement&);
     static HTMLVideoElementPictureInPicture* from(HTMLVideoElement&);
+    static void providePictureInPictureTo(HTMLVideoElement&);
     virtual ~HTMLVideoElementPictureInPicture();
 
     static void requestPictureInPicture(HTMLVideoElement&, Ref<DeferredPromise>&&);
diff --git a/Source/WebCore/WebCore.xcodeproj/project.pbxproj b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
index 2ce5a97..afadee5 100644
--- a/Source/WebCore/WebCore.xcodeproj/project.pbxproj
+++ b/Source/WebCore/WebCore.xcodeproj/project.pbxproj
@@ -626,6 +626,8 @@
 		1CCD81502231F83E0065FC2B /* WebCoreResourceHandleAsOperationQueueDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = E152551416FD234F003D7ADB /* WebCoreResourceHandleAsOperationQueueDelegate.mm */; };
 		1CCDF5BE1990332400BCEBAD /* SVGToOTFFontConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 1CCDF5BC1990332400BCEBAD /* SVGToOTFFontConversion.h */; };
 		1CFAE3230A6D6A3F0032593D /* libobjc.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CFAE3220A6D6A3F0032593D /* libobjc.dylib */; };
+		1D0026A42374D62400CA6CDF /* JSPictureInPictureWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D0026A22374D62300CA6CDF /* JSPictureInPictureWindow.h */; };
+		1D0026AA2374F9EA00CA6CDF /* JSEnterPictureInPictureEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D0026A82374F9D900CA6CDF /* JSEnterPictureInPictureEvent.h */; };
 		1D2C82B7236A3F6A0055D6C5 /* PictureInPictureSupport.h in Headers */ = {isa = PBXBuildFile; fileRef = 1D2C82B6236A3F6A0055D6C5 /* PictureInPictureSupport.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		1D2F8E03234474EF00993B68 /* DocumentPictureInPicture.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DEF06DD233D2E1C00EE228D /* DocumentPictureInPicture.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		1D2F8E042344751600993B68 /* EnterPictureInPictureEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = 1DEF06CA233C3D0B00EE228D /* EnterPictureInPictureEvent.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -6469,6 +6471,10 @@
 		1CECB3C721F59C8700F44542 /* WHLSLNativeTypeWriter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WHLSLNativeTypeWriter.h; sourceTree = "<group>"; };
 		1CF0BFD42298706800ED2074 /* TextSizeAdjustment.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = TextSizeAdjustment.cpp; sourceTree = "<group>"; };
 		1CFAE3220A6D6A3F0032593D /* libobjc.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libobjc.dylib; path = /usr/lib/libobjc.dylib; sourceTree = "<absolute>"; };
+		1D0026A22374D62300CA6CDF /* JSPictureInPictureWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPictureInPictureWindow.h; sourceTree = "<group>"; };
+		1D0026A32374D62400CA6CDF /* JSPictureInPictureWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPictureInPictureWindow.cpp; sourceTree = "<group>"; };
+		1D0026A82374F9D900CA6CDF /* JSEnterPictureInPictureEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JSEnterPictureInPictureEvent.h; path = JSEnterPictureInPictureEvent.h; sourceTree = "<group>"; };
+		1D0026A92374F9D900CA6CDF /* JSEnterPictureInPictureEvent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JSEnterPictureInPictureEvent.cpp; path = JSEnterPictureInPictureEvent.cpp; sourceTree = "<group>"; };
 		1D2C82B6236A3F6A0055D6C5 /* PictureInPictureSupport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PictureInPictureSupport.h; sourceTree = "<group>"; };
 		1DBC1B552347B3D200B901AF /* PictureInPictureObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PictureInPictureObserver.h; sourceTree = "<group>"; };
 		1DC553FD211BA12A004B780E /* NavigatorShare.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NavigatorShare.idl; sourceTree = "<group>"; };
@@ -16772,6 +16778,7 @@
 				33503C9C10179A9A003B47E1 /* Notifications */,
 				1AC226020DB69EA70089B669 /* Offline */,
 				A1CC564A1F4613BB00A4555B /* PaymentRequest */,
+				1D00269A2374B9F800CA6CDF /* PictureInPicture */,
 				A9D247F90D757E4100FDF959 /* Plugins */,
 				89F60B17157F6A020075E157 /* Quota */,
 				BC9854460CD3DA5F00069BC1 /* Ranges */,
@@ -17477,6 +17484,17 @@
 			path = Metal;
 			sourceTree = "<group>";
 		};
+		1D00269A2374B9F800CA6CDF /* PictureInPicture */ = {
+			isa = PBXGroup;
+			children = (
+				1D0026A92374F9D900CA6CDF /* JSEnterPictureInPictureEvent.cpp */,
+				1D0026A82374F9D900CA6CDF /* JSEnterPictureInPictureEvent.h */,
+				1D0026A32374D62400CA6CDF /* JSPictureInPictureWindow.cpp */,
+				1D0026A22374D62300CA6CDF /* JSPictureInPictureWindow.h */,
+			);
+			name = PictureInPicture;
+			sourceTree = "<group>";
+		};
 		1DEF06A2233C32DB00EE228D /* pictureinpicture */ = {
 			isa = PBXGroup;
 			children = (
@@ -30451,6 +30469,7 @@
 				71495DDB202B06F100ADFD34 /* JSEffectTiming.h in Headers */,
 				65DF31FA09D1CC60000BE325 /* JSElement.h in Headers */,
 				ADEC78F818EE5308001315C2 /* JSElementCustom.h in Headers */,
+				1D0026AA2374F9EA00CA6CDF /* JSEnterPictureInPictureEvent.h in Headers */,
 				83EE598F1F50958E003E8B30 /* JSErrorCallback.h in Headers */,
 				2ECF7ADD10162B3800427DE7 /* JSErrorEvent.h in Headers */,
 				F3D461491161D53200CA0D09 /* JSErrorHandler.h in Headers */,
@@ -30717,6 +30736,7 @@
 				CB38FD5B1CD2325B00592A3F /* JSPerformanceResourceTiming.h in Headers */,
 				8A9A588811E84F37008ACFD1 /* JSPerformanceTiming.h in Headers */,
 				FDEA6247152102FC00479DF0 /* JSPeriodicWave.h in Headers */,
+				1D0026A42374D62400CA6CDF /* JSPictureInPictureWindow.h in Headers */,
 				712BE4891FE86875002031CC /* JSPlaybackDirection.h in Headers */,
 				93B70D6C09EB0C7C009D8468 /* JSPluginElementFunctions.h in Headers */,
 				5189F01E10B37BD900F3C739 /* JSPopStateEvent.h in Headers */,
diff --git a/Source/WebCore/html/HTMLVideoElement.cpp b/Source/WebCore/html/HTMLVideoElement.cpp
index edad9fb..244260c 100644
--- a/Source/WebCore/html/HTMLVideoElement.cpp
+++ b/Source/WebCore/html/HTMLVideoElement.cpp
@@ -48,10 +48,14 @@
 #include <wtf/text/TextStream.h>
 
 #if ENABLE(VIDEO_PRESENTATION_MODE)
-#include "PictureInPictureObserver.h"
 #include "VideoFullscreenModel.h"
 #endif
 
+#if ENABLE(PICTURE_IN_PICTURE_API)
+#include "HTMLVideoElementPictureInPicture.h"
+#include "PictureInPictureObserver.h"
+#endif
+
 namespace WebCore {
 
 WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLVideoElement);
@@ -69,6 +73,11 @@
 Ref<HTMLVideoElement> HTMLVideoElement::create(const QualifiedName& tagName, Document& document, bool createdByParser)
 {
     auto videoElement = adoptRef(*new HTMLVideoElement(tagName, document, createdByParser));
+
+#if ENABLE(PICTURE_IN_PICTURE_API)
+    HTMLVideoElementPictureInPicture::providePictureInPictureTo(videoElement);
+#endif
+
     videoElement->finishInitialization();
     videoElement->suspendIfNeeded();
     return videoElement;