Video with object-fit: cover can spill outside the box
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 31 Aug 2013 00:14:41 +0000 (00:14 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 31 Aug 2013 00:14:41 +0000 (00:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=52103

Source/WebCore:

Reviewed by Dean Jackson.

object-fit on renderers which use accelerated compositing needs special
treatment.

For directly composited images, and video, GraphicsLayer needs to know
both the size of the content layer, and also a rectangle at which this
should be clipped (because, for the first time, that content layer can be
larger than the renderer's content box).

AVFoundation would always aspect-ratio fit video by default, so plumb
through MediaPlayer a way to override that when object-fit requires it.

Added a LAYER_TREE_INCLUDES_CONTENT_LAYERS enum to the layerTreeAsText()
flags so we can dump content layers for testing.

Tests: compositing/images/direct-image-object-fit.html
       compositing/reflections/direct-image-object-fit-reflected.html
       compositing/video/video-object-fit.html

* page/Frame.h: New LayerTreeFlagsIncludeContentLayers flag.
* platform/graphics/GraphicsLayer.h: New flag.
* platform/graphics/MediaPlayer.cpp:
(WebCore::MediaPlayer::shouldMaintainAspectRatio):
(WebCore::MediaPlayer::setShouldMaintainAspectRatio):
* platform/graphics/MediaPlayer.h:
* platform/graphics/MediaPlayerPrivate.h:
(WebCore::MediaPlayerPrivateInterface::shouldMaintainAspectRatio):
(WebCore::MediaPlayerPrivateInterface::setShouldMaintainAspectRatio):
* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp:
(WebCore::MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation):
(WebCore::MediaPlayerPrivateAVFoundation::setShouldMaintainAspectRatio):
* platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
* platform/graphics/avfoundation/cf/MediaPlayerPrivateAVFoundationCF.cpp:
(WebCore::MediaPlayerPrivateAVFoundationCF::updateVideoLayerGravity):
(WebCore::AVFWrapper::platformLayer):
* platform/graphics/avfoundation/cf/MediaPlayerPrivateAVFoundationCF.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
* platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
(WebCore::MediaPlayerPrivateAVFoundationObjC::createVideoLayer):
(WebCore::MediaPlayerPrivateAVFoundationObjC::updateVideoLayerGravity):
* platform/graphics/ca/GraphicsLayerCA.cpp: We need a new m_contentsClippingLayer to
clip the contents layer, which only gets created when necessary. It has to be cloned
for reflections.
(WebCore::GraphicsLayerCA::willBeDestroyed):
(WebCore::GraphicsLayerCA::setContentsRect):
(WebCore::GraphicsLayerCA::setContentsClippingRect):
(WebCore::GraphicsLayerCA::commitLayerChangesBeforeSublayers):
(WebCore::GraphicsLayerCA::updateSublayerList):
(WebCore::GraphicsLayerCA::updateContentsImage):
(WebCore::GraphicsLayerCA::updateContentsMediaLayer):
(WebCore::GraphicsLayerCA::updateContentsCanvasLayer):
(WebCore::GraphicsLayerCA::updateContentsColorLayer):
(WebCore::GraphicsLayerCA::updateContentsRects):
(WebCore::GraphicsLayerCA::dumpAdditionalProperties):
(WebCore::GraphicsLayerCA::ensureCloneLayers):
(WebCore::GraphicsLayerCA::removeCloneLayers):
(WebCore::GraphicsLayerCA::fetchCloneLayers):
* platform/graphics/ca/GraphicsLayerCA.h:
* rendering/RenderLayerBacking.cpp: Need to push both the contentsRect and
the contentsClippingRect down to the GraphicsLayers. Most of the time they
are the same, unless object-fit makes them different.
(WebCore::RenderLayerBacking::resetContentsRect):
(WebCore::RenderLayerBacking::positionOverflowControlsLayers):
(WebCore::RenderLayerBacking::updateDirectlyCompositedBackgroundColor):
(WebCore::RenderLayerBacking::updateDirectlyCompositedBackgroundImage):
(WebCore::RenderLayerBacking::updateImageContents):
(WebCore::RenderLayerBacking::contentsBox):
* rendering/RenderLayerCompositor.cpp:
(WebCore::RenderLayerCompositor::layerTreeAsText):
* rendering/RenderVideo.cpp:
(WebCore::RenderVideo::updatePlayer):
* testing/Internals.cpp:
(WebCore::Internals::layerTreeAsText):
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

Reviewed by Dean Jackson.

Test cases for directly composited image with object-fit, the same with
reflections, and one with video.

Tests dump content GraphicsLayers, so have platform-specific results.

* compositing/images/direct-image-object-fit-expected.txt: Added.
* compositing/images/direct-image-object-fit.html: Added.
* compositing/reflections/direct-image-object-fit-reflected-expected.txt: Added.
* compositing/reflections/direct-image-object-fit-reflected.html: Added.
* compositing/video/video-object-fit-expected.txt: Added.
* compositing/video/video-object-fit.html: Added.
* media/video-object-fit-change.html: Fixed
* platform/mac/TestExpectations: Unskip two tests.
* platform/mac/compositing/images/direct-image-object-fit-expected.txt: Added.
* platform/mac/compositing/reflections/direct-image-object-fit-reflected-expected.txt: Added.
* platform/mac/compositing/video/video-object-fit-expected.txt: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@154921 268f45cc-cd09-0410-ab3c-d52691b4dbfc

32 files changed:
LayoutTests/ChangeLog
LayoutTests/compositing/images/direct-image-object-fit-expected.txt [new file with mode: 0644]
LayoutTests/compositing/images/direct-image-object-fit.html [new file with mode: 0644]
LayoutTests/compositing/reflections/direct-image-object-fit-reflected-expected.txt [new file with mode: 0644]
LayoutTests/compositing/reflections/direct-image-object-fit-reflected.html [new file with mode: 0644]
LayoutTests/compositing/video/video-object-fit-expected.txt [new file with mode: 0644]
LayoutTests/compositing/video/video-object-fit.html [new file with mode: 0644]
LayoutTests/media/video-object-fit-change.html
LayoutTests/platform/mac/TestExpectations
LayoutTests/platform/mac/compositing/images/direct-image-object-fit-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/compositing/reflections/direct-image-object-fit-reflected-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/compositing/video/video-object-fit-expected.txt [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/page/Frame.h
Source/WebCore/platform/graphics/GraphicsLayer.h
Source/WebCore/platform/graphics/MediaPlayer.cpp
Source/WebCore/platform/graphics/MediaPlayer.h
Source/WebCore/platform/graphics/MediaPlayerPrivate.h
Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp
Source/WebCore/platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h
Source/WebCore/platform/graphics/avfoundation/cf/MediaPlayerPrivateAVFoundationCF.cpp
Source/WebCore/platform/graphics/avfoundation/cf/MediaPlayerPrivateAVFoundationCF.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h
Source/WebCore/platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp
Source/WebCore/platform/graphics/ca/GraphicsLayerCA.h
Source/WebCore/rendering/RenderLayerBacking.cpp
Source/WebCore/rendering/RenderLayerCompositor.cpp
Source/WebCore/rendering/RenderVideo.cpp
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl

index eba8e22..2e9efde 100644 (file)
@@ -1,3 +1,27 @@
+2013-08-30  Simon Fraser  <simon.fraser@apple.com>
+
+        Video with object-fit: cover can spill outside the box
+        https://bugs.webkit.org/show_bug.cgi?id=52103
+
+        Reviewed by Dean Jackson.
+        
+        Test cases for directly composited image with object-fit, the same with
+        reflections, and one with video.
+        
+        Tests dump content GraphicsLayers, so have platform-specific results.
+
+        * compositing/images/direct-image-object-fit-expected.txt: Added.
+        * compositing/images/direct-image-object-fit.html: Added.
+        * compositing/reflections/direct-image-object-fit-reflected-expected.txt: Added.
+        * compositing/reflections/direct-image-object-fit-reflected.html: Added.
+        * compositing/video/video-object-fit-expected.txt: Added.
+        * compositing/video/video-object-fit.html: Added.
+        * media/video-object-fit-change.html: Fixed
+        * platform/mac/TestExpectations: Unskip two tests.
+        * platform/mac/compositing/images/direct-image-object-fit-expected.txt: Added.
+        * platform/mac/compositing/reflections/direct-image-object-fit-reflected-expected.txt: Added.
+        * platform/mac/compositing/video/video-object-fit-expected.txt: Added.
+
 2013-08-30  Oliver Hunt  <oliver@apple.com>
 
         Fix expected output
diff --git a/LayoutTests/compositing/images/direct-image-object-fit-expected.txt b/LayoutTests/compositing/images/direct-image-object-fit-expected.txt
new file mode 100644 (file)
index 0000000..8d7a1a3
--- /dev/null
@@ -0,0 +1,25 @@
+
+(GraphicsLayer
+  (bounds 800.00 600.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 800.00 600.00)
+      (contentsOpaque 1)
+      (children 3
+        (GraphicsLayer
+          (position 58.00 20.00)
+          (bounds 200.00 120.00)
+        )
+        (GraphicsLayer
+          (position 58.00 160.00)
+          (bounds 200.00 120.00)
+        )
+        (GraphicsLayer
+          (position 58.00 300.00)
+          (bounds 200.00 120.00)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/compositing/images/direct-image-object-fit.html b/LayoutTests/compositing/images/direct-image-object-fit.html
new file mode 100644 (file)
index 0000000..c2b227e
--- /dev/null
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        img {
+            display: block;
+            height: 120px;
+            width: 200px;
+            margin: 20px 50px;
+        }
+        .composited {
+            -webkit-transform: translateZ(0);
+        }
+    </style>
+    <script>
+        if (window.testRunner)
+            testRunner.dumpAsText();
+
+        function doTest()
+        {
+            if (window.internals)
+                document.getElementById('results').innerText = window.internals.layerTreeAsText(document, internals.LAYER_TREE_INCLUDES_CONTENT_LAYERS);
+        }
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <img class="composited" src="../../fast/css/resources/circles-landscape.png" style="object-fit: cover"></div>
+    <img class="composited" src="../../fast/css/resources/circles-landscape.png" style="object-fit: contain"></div>
+    <img class="composited" src="../../fast/css/resources/circles-landscape.png" style="object-fit: none"></div>
+<pre id="results"></pre>
+</body>
+</html>
diff --git a/LayoutTests/compositing/reflections/direct-image-object-fit-reflected-expected.txt b/LayoutTests/compositing/reflections/direct-image-object-fit-reflected-expected.txt
new file mode 100644 (file)
index 0000000..3609879
--- /dev/null
@@ -0,0 +1,43 @@
+    
+(GraphicsLayer
+  (bounds 800.00 600.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 800.00 600.00)
+      (contentsOpaque 1)
+      (children 3
+        (GraphicsLayer
+          (position 28.00 28.00)
+          (bounds 200.00 120.00)
+          (replica layer)
+            (GraphicsLayer
+              (bounds 200.00 120.00)
+              (transform [1.00 0.00 0.00 0.00] [0.00 -1.00 0.00 0.00] [0.00 0.00 1.00 0.00] [0.00 140.00 0.00 1.00])
+              (replicated layer)
+            )
+        )
+        (GraphicsLayer
+          (position 272.00 28.00)
+          (bounds 200.00 120.00)
+          (replica layer)
+            (GraphicsLayer
+              (bounds 200.00 120.00)
+              (transform [1.00 0.00 0.00 0.00] [0.00 -1.00 0.00 0.00] [0.00 0.00 1.00 0.00] [0.00 140.00 0.00 1.00])
+              (replicated layer)
+            )
+        )
+        (GraphicsLayer
+          (position 516.00 28.00)
+          (bounds 200.00 120.00)
+          (replica layer)
+            (GraphicsLayer
+              (bounds 200.00 120.00)
+              (transform [1.00 0.00 0.00 0.00] [0.00 -1.00 0.00 0.00] [0.00 0.00 1.00 0.00] [0.00 140.00 0.00 1.00])
+              (replicated layer)
+            )
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/compositing/reflections/direct-image-object-fit-reflected.html b/LayoutTests/compositing/reflections/direct-image-object-fit-reflected.html
new file mode 100644 (file)
index 0000000..ee220c8
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <style>
+        img {
+            height: 120px;
+            width: 200px;
+            margin: 20px;
+            -webkit-box-reflect: below 20px;
+        }
+        
+        img:hover {
+            height: 160px;
+        }
+
+        .composited {
+            -webkit-transform: translateZ(0);
+        }
+    </style>
+    <script>
+        if (window.testRunner)
+            testRunner.dumpAsText();
+
+        function doTest()
+        {
+            if (window.internals)
+                document.getElementById('results').innerText = window.internals.layerTreeAsText(document, internals.LAYER_TREE_INCLUDES_CONTENT_LAYERS);
+        }
+        window.addEventListener('load', doTest, false);
+    </script>
+</head>
+<body>
+    <img class="composited" src="../../fast/css/resources/circles-landscape.png" style="object-fit: cover"></div>
+    <img class="composited" src="../../fast/css/resources/circles-landscape.png" style="object-fit: contain"></div>
+    <img class="composited" src="../../fast/css/resources/circles-landscape.png" style="object-fit: none"></div>
+<pre id="results"></pre>
+</body>
+</html>
diff --git a/LayoutTests/compositing/video/video-object-fit-expected.txt b/LayoutTests/compositing/video/video-object-fit-expected.txt
new file mode 100644 (file)
index 0000000..7c0fe70
--- /dev/null
@@ -0,0 +1,49 @@
+   
+(GraphicsLayer
+  (bounds 785.00 775.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 785.00 775.00)
+      (contentsOpaque 1)
+      (children 6
+        (GraphicsLayer
+          (position 13.00 13.00)
+          (bounds 124.00 204.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+        (GraphicsLayer
+          (position 151.00 13.00)
+          (bounds 124.00 204.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+        (GraphicsLayer
+          (position 289.00 13.00)
+          (bounds 124.00 204.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+        (GraphicsLayer
+          (position 13.00 231.00)
+          (bounds 354.00 304.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+        (GraphicsLayer
+          (position 13.00 569.00)
+          (bounds 404.00 184.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+        (GraphicsLayer
+          (position 431.00 549.00)
+          (bounds 124.00 204.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/compositing/video/video-object-fit.html b/LayoutTests/compositing/video/video-object-fit.html
new file mode 100644 (file)
index 0000000..6fba36a
--- /dev/null
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+  video {
+/*    display: block;*/
+    width: 120px;
+    height: 200px;
+    margin: 5px;
+    border: 2px solid blue;
+    background-color: gray;
+  }
+  
+  .big {
+      height: 300px;
+      width: 350px;
+  }
+  .smaller {
+      height: 180px;
+      width: 400px;
+  }
+</style>
+<script src="../../media/media-file.js"></script>
+<script>
+    if (window.testRunner) {
+        testRunner.waitUntilDone();
+        testRunner.dumpAsText();
+    }
+
+    function init()
+    {
+        setSrcByTagName("video", findMediaFile("video", "../../media/content/test"));
+
+        var totalCount = document.getElementsByTagName('video').length;
+        var count = totalCount;
+        document.addEventListener("canplaythrough", function () {
+            if (!--count) {
+                if (window.testRunner)
+                    setTimeout(function() {
+                        if (window.internals)
+                            document.getElementById('results').innerText = window.internals.layerTreeAsText(document, internals.LAYER_TREE_INCLUDES_CONTENT_LAYERS);
+                        testRunner.notifyDone();
+                    }, totalCount * 150);
+            }
+        }, true);
+
+        if (window.testRunner) {
+            setTimeout(function() {
+                document.body.appendChild(document.createTextNode('FAIL'));
+                if (window.testRunner)
+                    testRunner.notifyDone();
+            }, 1500);
+        }
+    }
+</script>
+
+</head>
+<body onload="init();">
+    <video style="object-fit: fill"></video>
+    <video style="object-fit: cover"></video>
+    <video style="object-fit: contain"></video>
+    <video class="big" style="object-fit: scale-down"></video>
+    <video class="smaller" style="object-fit: scale-down"></video>
+    <video style="object-fit: none"></video>
+<pre id="results"></pre>
+</body>
+</html>
index db5f2d3..1c7da9d 100644 (file)
 
         function init()
         {
-            setSrcByTagName("video", findMediaFile("video", "../../media/content/test"));
+            setSrcByTagName("video", findMediaFile("video", "content/test"));
 
             var totalCount = document.getElementsByTagName('video').length;
             var count = totalCount;
             document.addEventListener("canplaythrough", function () {
                 if (!--count)
-                    setTimeout(function() { changeStyle(); }, 500);
+                    setTimeout(function() { changeStyle(); }, 250);
             }, true);
 
             if (window.testRunner) {
@@ -43,9 +43,8 @@
            video3.style.objectFit = 'fill';
            video4.style.objectFit = 'scale-down';
 
-            if (window.testRunner) {
-                setTimeout(function() { testRunner.notifyDone(); }, 500);
-            }
+        if (window.testRunner)
+            setTimeout(function() { testRunner.notifyDone(); }, 250);
        }
     </script>
 
index 6113066..735dcb4 100644 (file)
@@ -52,9 +52,6 @@ plugins/reloadplugins-and-pages.html
 # This test requires ogg codecs
 media/media-can-play-ogg.html
 
-webkit.org/b/52103 media/video-object-fit-change.html
-webkit.org/b/52103 media/video-object-fit.html
-
 # This test requires flac codec
 media/media-can-play-flac-audio.html
 
diff --git a/LayoutTests/platform/mac/compositing/images/direct-image-object-fit-expected.txt b/LayoutTests/platform/mac/compositing/images/direct-image-object-fit-expected.txt
new file mode 100644 (file)
index 0000000..7d341b8
--- /dev/null
@@ -0,0 +1,30 @@
+
+(GraphicsLayer
+  (bounds 800.00 600.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 800.00 600.00)
+      (contentsOpaque 1)
+      (children 3
+        (GraphicsLayer
+          (position 58.00 20.00)
+          (bounds 200.00 120.00)
+          (contents clipping layer 0.00, 0.00 200.00 x 120.00)
+          (contents layer -20.00, 0.00 240.00 x 120.00)
+        )
+        (GraphicsLayer
+          (position 58.00 160.00)
+          (bounds 200.00 120.00)
+          (contents layer 0.00, 10.00 200.00 x 100.00)
+        )
+        (GraphicsLayer
+          (position 58.00 300.00)
+          (bounds 200.00 120.00)
+          (contents clipping layer 0.00, 0.00 200.00 x 120.00)
+          (contents layer -44.00, -12.00 288.00 x 144.00)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/platform/mac/compositing/reflections/direct-image-object-fit-reflected-expected.txt b/LayoutTests/platform/mac/compositing/reflections/direct-image-object-fit-reflected-expected.txt
new file mode 100644 (file)
index 0000000..06b452d
--- /dev/null
@@ -0,0 +1,48 @@
+    
+(GraphicsLayer
+  (bounds 800.00 600.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 800.00 600.00)
+      (contentsOpaque 1)
+      (children 3
+        (GraphicsLayer
+          (position 28.00 28.00)
+          (bounds 200.00 120.00)
+          (replica layer)
+            (GraphicsLayer
+              (bounds 200.00 120.00)
+              (transform [1.00 0.00 0.00 0.00] [0.00 -1.00 0.00 0.00] [0.00 0.00 1.00 0.00] [0.00 140.00 0.00 1.00])
+              (replicated layer)
+            )
+          (contents clipping layer 0.00, 0.00 200.00 x 120.00)
+          (contents layer -20.00, 0.00 240.00 x 120.00)
+        )
+        (GraphicsLayer
+          (position 272.00 28.00)
+          (bounds 200.00 120.00)
+          (replica layer)
+            (GraphicsLayer
+              (bounds 200.00 120.00)
+              (transform [1.00 0.00 0.00 0.00] [0.00 -1.00 0.00 0.00] [0.00 0.00 1.00 0.00] [0.00 140.00 0.00 1.00])
+              (replicated layer)
+            )
+          (contents layer 0.00, 10.00 200.00 x 100.00)
+        )
+        (GraphicsLayer
+          (position 516.00 28.00)
+          (bounds 200.00 120.00)
+          (replica layer)
+            (GraphicsLayer
+              (bounds 200.00 120.00)
+              (transform [1.00 0.00 0.00 0.00] [0.00 -1.00 0.00 0.00] [0.00 0.00 1.00 0.00] [0.00 140.00 0.00 1.00])
+              (replicated layer)
+            )
+          (contents clipping layer 0.00, 0.00 200.00 x 120.00)
+          (contents layer -44.00, -12.00 288.00 x 144.00)
+        )
+      )
+    )
+  )
+)
+
diff --git a/LayoutTests/platform/mac/compositing/video/video-object-fit-expected.txt b/LayoutTests/platform/mac/compositing/video/video-object-fit-expected.txt
new file mode 100644 (file)
index 0000000..7c0fe70
--- /dev/null
@@ -0,0 +1,49 @@
+   
+(GraphicsLayer
+  (bounds 785.00 775.00)
+  (children 1
+    (GraphicsLayer
+      (bounds 785.00 775.00)
+      (contentsOpaque 1)
+      (children 6
+        (GraphicsLayer
+          (position 13.00 13.00)
+          (bounds 124.00 204.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+        (GraphicsLayer
+          (position 151.00 13.00)
+          (bounds 124.00 204.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+        (GraphicsLayer
+          (position 289.00 13.00)
+          (bounds 124.00 204.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+        (GraphicsLayer
+          (position 13.00 231.00)
+          (bounds 354.00 304.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+        (GraphicsLayer
+          (position 13.00 569.00)
+          (bounds 404.00 184.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+        (GraphicsLayer
+          (position 431.00 549.00)
+          (bounds 124.00 204.00)
+          (contentsOpaque 1)
+          (drawsContent 1)
+        )
+      )
+    )
+  )
+)
+
index 9472d15..078b8bb 100644 (file)
@@ -1,3 +1,85 @@
+2013-08-30  Simon Fraser  <simon.fraser@apple.com>
+
+        Video with object-fit: cover can spill outside the box
+        https://bugs.webkit.org/show_bug.cgi?id=52103
+
+        Reviewed by Dean Jackson.
+        
+        object-fit on renderers which use accelerated compositing needs special
+        treatment.
+        
+        For directly composited images, and video, GraphicsLayer needs to know
+        both the size of the content layer, and also a rectangle at which this
+        should be clipped (because, for the first time, that content layer can be
+        larger than the renderer's content box).
+        
+        AVFoundation would always aspect-ratio fit video by default, so plumb
+        through MediaPlayer a way to override that when object-fit requires it.
+        
+        Added a LAYER_TREE_INCLUDES_CONTENT_LAYERS enum to the layerTreeAsText()
+        flags so we can dump content layers for testing.
+
+        Tests: compositing/images/direct-image-object-fit.html
+               compositing/reflections/direct-image-object-fit-reflected.html
+               compositing/video/video-object-fit.html
+
+        * page/Frame.h: New LayerTreeFlagsIncludeContentLayers flag.
+        * platform/graphics/GraphicsLayer.h: New flag.
+        * platform/graphics/MediaPlayer.cpp:
+        (WebCore::MediaPlayer::shouldMaintainAspectRatio):
+        (WebCore::MediaPlayer::setShouldMaintainAspectRatio):
+        * platform/graphics/MediaPlayer.h:
+        * platform/graphics/MediaPlayerPrivate.h:
+        (WebCore::MediaPlayerPrivateInterface::shouldMaintainAspectRatio):
+        (WebCore::MediaPlayerPrivateInterface::setShouldMaintainAspectRatio):
+        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.cpp:
+        (WebCore::MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation):
+        (WebCore::MediaPlayerPrivateAVFoundation::setShouldMaintainAspectRatio):
+        * platform/graphics/avfoundation/MediaPlayerPrivateAVFoundation.h:
+        * platform/graphics/avfoundation/cf/MediaPlayerPrivateAVFoundationCF.cpp:
+        (WebCore::MediaPlayerPrivateAVFoundationCF::updateVideoLayerGravity):
+        (WebCore::AVFWrapper::platformLayer):
+        * platform/graphics/avfoundation/cf/MediaPlayerPrivateAVFoundationCF.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.h:
+        * platform/graphics/avfoundation/objc/MediaPlayerPrivateAVFoundationObjC.mm:
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::createVideoLayer):
+        (WebCore::MediaPlayerPrivateAVFoundationObjC::updateVideoLayerGravity):
+        * platform/graphics/ca/GraphicsLayerCA.cpp: We need a new m_contentsClippingLayer to
+        clip the contents layer, which only gets created when necessary. It has to be cloned
+        for reflections.
+        (WebCore::GraphicsLayerCA::willBeDestroyed):
+        (WebCore::GraphicsLayerCA::setContentsRect):
+        (WebCore::GraphicsLayerCA::setContentsClippingRect):
+        (WebCore::GraphicsLayerCA::commitLayerChangesBeforeSublayers):
+        (WebCore::GraphicsLayerCA::updateSublayerList):
+        (WebCore::GraphicsLayerCA::updateContentsImage):
+        (WebCore::GraphicsLayerCA::updateContentsMediaLayer):
+        (WebCore::GraphicsLayerCA::updateContentsCanvasLayer):
+        (WebCore::GraphicsLayerCA::updateContentsColorLayer):
+        (WebCore::GraphicsLayerCA::updateContentsRects):
+        (WebCore::GraphicsLayerCA::dumpAdditionalProperties):
+        (WebCore::GraphicsLayerCA::ensureCloneLayers):
+        (WebCore::GraphicsLayerCA::removeCloneLayers):
+        (WebCore::GraphicsLayerCA::fetchCloneLayers):
+        * platform/graphics/ca/GraphicsLayerCA.h:
+        * rendering/RenderLayerBacking.cpp: Need to push both the contentsRect and
+        the contentsClippingRect down to the GraphicsLayers. Most of the time they
+        are the same, unless object-fit makes them different.
+        (WebCore::RenderLayerBacking::resetContentsRect):
+        (WebCore::RenderLayerBacking::positionOverflowControlsLayers):
+        (WebCore::RenderLayerBacking::updateDirectlyCompositedBackgroundColor):
+        (WebCore::RenderLayerBacking::updateDirectlyCompositedBackgroundImage):
+        (WebCore::RenderLayerBacking::updateImageContents):
+        (WebCore::RenderLayerBacking::contentsBox):
+        * rendering/RenderLayerCompositor.cpp:
+        (WebCore::RenderLayerCompositor::layerTreeAsText):
+        * rendering/RenderVideo.cpp:
+        (WebCore::RenderVideo::updatePlayer):
+        * testing/Internals.cpp:
+        (WebCore::Internals::layerTreeAsText):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2013-08-30  Brent Fulgham  <bfulgham@apple.com>
 
         [Windows] Update to incorporate additional suggestions
index b0efe4c..896bb02 100644 (file)
@@ -82,7 +82,8 @@ namespace WebCore {
         LayerTreeFlagsIncludeVisibleRects = 1 << 1,
         LayerTreeFlagsIncludeTileCaches = 1 << 2,
         LayerTreeFlagsIncludeRepaintRects = 1 << 3,
-        LayerTreeFlagsIncludePaintingPhases = 1 << 4
+        LayerTreeFlagsIncludePaintingPhases = 1 << 4,
+        LayerTreeFlagsIncludeContentLayers = 1 << 5
     };
     typedef unsigned LayerTreeFlags;
 
index 152a45a..1429dc2 100644 (file)
@@ -50,7 +50,8 @@ enum LayerTreeAsTextBehaviorFlags {
     LayerTreeAsTextIncludeVisibleRects = 1 << 1,
     LayerTreeAsTextIncludeTileCaches = 1 << 2,
     LayerTreeAsTextIncludeRepaintRects = 1 << 3,
-    LayerTreeAsTextIncludePaintingPhases = 1 << 4
+    LayerTreeAsTextIncludePaintingPhases = 1 << 4,
+    LayerTreeAsTextIncludeContentLayers = 1 << 5
 };
 typedef unsigned LayerTreeAsTextBehavior;
 
index 1263822..4882e88 100644 (file)
@@ -847,6 +847,16 @@ bool MediaPlayer::supportsAcceleratedRendering() const
 }
 #endif // USE(ACCELERATED_COMPOSITING)
 
+bool MediaPlayer::shouldMaintainAspectRatio() const
+{
+    return m_private->shouldMaintainAspectRatio();
+}
+
+void MediaPlayer::setShouldMaintainAspectRatio(bool maintainAspectRatio)
+{
+    m_private->setShouldMaintainAspectRatio(maintainAspectRatio);
+}
+
 bool MediaPlayer::hasSingleSecurityOrigin() const
 {
     return m_private->hasSingleSecurityOrigin();
index 4368150..a1d0471 100644 (file)
@@ -415,6 +415,9 @@ public:
     void acceleratedRenderingStateChanged();
 #endif
 
+    bool shouldMaintainAspectRatio() const;
+    void setShouldMaintainAspectRatio(bool);
+
 #if PLATFORM(WIN) && USE(AVFOUNDATION)
     GraphicsDeviceAdapter* graphicsDeviceAdapter() const;
 #endif
index 323441e..8058bee 100644 (file)
@@ -150,6 +150,9 @@ public:
     virtual void acceleratedRenderingStateChanged() { }
 #endif
 
+    virtual bool shouldMaintainAspectRatio() const { return true; }
+    virtual void setShouldMaintainAspectRatio(bool) { }
+
     virtual bool hasSingleSecurityOrigin() const { return false; }
 
     virtual bool didPassCORSAccessCheck() const { return false; }
index 28cffd2..81f5ba0 100644 (file)
@@ -77,6 +77,7 @@ MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(MediaPlayer* play
     , m_playWhenFramesAvailable(false)
     , m_inbandTrackConfigurationPending(false)
     , m_characteristicsChanged(false)
+    , m_shouldMaintainAspectRatio(true)
     , m_seekCount(0)
 {
     LOG(Media, "MediaPlayerPrivateAVFoundation::MediaPlayerPrivateAVFoundation(%p)", this);
@@ -582,6 +583,15 @@ void MediaPlayerPrivateAVFoundation::acceleratedRenderingStateChanged()
     setUpVideoRendering();
 }
 
+void MediaPlayerPrivateAVFoundation::setShouldMaintainAspectRatio(bool maintainAspectRatio)
+{
+    if (maintainAspectRatio == m_shouldMaintainAspectRatio)
+        return;
+
+    m_shouldMaintainAspectRatio = maintainAspectRatio;
+    updateVideoLayerGravity();
+}
+
 void MediaPlayerPrivateAVFoundation::metadataLoaded()
 {
     m_loadingMetadata = false;
index 30acbf0..cad4097 100644 (file)
@@ -165,6 +165,9 @@ protected:
     virtual bool supportsAcceleratedRendering() const = 0;
     virtual void acceleratedRenderingStateChanged();
 #endif
+    virtual bool shouldMaintainAspectRatio() const OVERRIDE { return m_shouldMaintainAspectRatio; }
+    virtual void setShouldMaintainAspectRatio(bool) OVERRIDE;
+
     virtual MediaPlayer::MovieLoadType movieLoadType() const;
     virtual void prepareForRendering();
     virtual float mediaTimeForTimeValue(float) const = 0;
@@ -228,6 +231,8 @@ protected:
     virtual bool hasContextRenderer() const = 0;
     virtual bool hasLayerRenderer() const = 0;
 
+    virtual void updateVideoLayerGravity() = 0;
+
 protected:
     void updateStates();
 
@@ -308,6 +313,7 @@ private:
     bool m_playWhenFramesAvailable;
     bool m_inbandTrackConfigurationPending;
     bool m_characteristicsChanged;
+    bool m_shouldMaintainAspectRatio;
     size_t m_seekCount;
 };
 
index 12fcb0e..5380f95 100644 (file)
@@ -332,6 +332,11 @@ void MediaPlayerPrivateAVFoundationCF::cancelLoad()
     setDelayCallbacks(false);
 }
 
+void MediaPlayerPrivateAVFoundationCF::updateVideoLayerGravity()
+{
+    // We should call AVCFPlayerLayerSetVideoGravity() here, but it is not yet implemented.
+}
+
 bool MediaPlayerPrivateAVFoundationCF::hasLayerRenderer() const
 {
     return videoLayer(m_avfWrapper);
@@ -1577,6 +1582,7 @@ PlatformLayer* AVFWrapper::platformLayer()
     CACFLayerInsertSublayer(m_videoLayerWrapper->platformLayer(), m_caVideoLayer.get(), 0);
     m_videoLayerWrapper->setAnchorPoint(FloatPoint3D());
     m_videoLayerWrapper->setNeedsLayout();
+    updateVideoLayerGravity();
 
     return m_videoLayerWrapper->platformLayer();
 }
index 4e5dda3..dc618d1 100644 (file)
@@ -98,6 +98,8 @@ private:
     virtual bool hasContextRenderer() const;
     virtual bool hasLayerRenderer() const;
 
+    virtual void updateVideoLayerGravity() OVERRIDE;
+
     virtual void contentsNeedsDisplay();
 
     virtual String languageOfPrimaryAudioTrack() const OVERRIDE;
index f7bf057..b5d3949 100644 (file)
@@ -148,6 +148,8 @@ private:
     virtual bool hasContextRenderer() const;
     virtual bool hasLayerRenderer() const;
 
+    virtual void updateVideoLayerGravity() OVERRIDE;
+
     virtual bool hasSingleSecurityOrigin() const;
     
 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1080
index ad65282..fb0c780 100644 (file)
@@ -98,6 +98,8 @@ SOFT_LINK_POINTER(AVFoundation, AVMediaTypeAudio, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVPlayerItemDidPlayToEndTimeNotification, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVAssetImageGeneratorApertureModeCleanAperture, NSString *)
 SOFT_LINK_POINTER(AVFoundation, AVURLAssetReferenceRestrictionsKey, NSString *)
+SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResizeAspect, NSString *)
+SOFT_LINK_POINTER(AVFoundation, AVLayerVideoGravityResize, NSString *)
 
 SOFT_LINK_CONSTANT(CoreMedia, kCMTimeZero, CMTime)
 
@@ -116,6 +118,8 @@ SOFT_LINK_CONSTANT(CoreMedia, kCMTimeZero, CMTime)
 #define AVPlayerItemDidPlayToEndTimeNotification getAVPlayerItemDidPlayToEndTimeNotification()
 #define AVAssetImageGeneratorApertureModeCleanAperture getAVAssetImageGeneratorApertureModeCleanAperture()
 #define AVURLAssetReferenceRestrictionsKey getAVURLAssetReferenceRestrictionsKey()
+#define AVLayerVideoGravityResizeAspect getAVLayerVideoGravityResizeAspect()
+#define AVLayerVideoGravityResize getAVLayerVideoGravityResize()
 
 #if HAVE(AVFOUNDATION_MEDIA_SELECTION_GROUP)
 typedef AVMediaSelectionGroup AVMediaSelectionGroupType;
@@ -372,6 +376,7 @@ void MediaPlayerPrivateAVFoundationObjC::createVideoLayer()
 #ifndef NDEBUG
         [m_videoLayer.get() setName:@"Video layer"];
 #endif
+        updateVideoLayerGravity();
         LOG(Media, "MediaPlayerPrivateAVFoundationObjC::createVideoLayer(%p) - returning %p", this, m_videoLayer.get());
     }
 }
@@ -987,6 +992,18 @@ float MediaPlayerPrivateAVFoundationObjC::mediaTimeForTimeValue(float timeValue)
     return timeValue;
 }
 
+void MediaPlayerPrivateAVFoundationObjC::updateVideoLayerGravity()
+{
+    if (!m_videoLayer)
+        return;
+
+    [CATransaction begin];
+    [CATransaction setDisableActions:YES];    
+    NSString* gravity = shouldMaintainAspectRatio() ? AVLayerVideoGravityResizeAspect : AVLayerVideoGravityResize;
+    [m_videoLayer.get() setVideoGravity:gravity];
+    [CATransaction commit];
+}
+
 void MediaPlayerPrivateAVFoundationObjC::tracksChanged()
 {
     String primaryAudioTrackLanguage = m_languageOfPrimaryAudioTrack;
index 6f7a965..b063bee 100644 (file)
@@ -305,6 +305,9 @@ void GraphicsLayerCA::willBeDestroyed()
     
     if (m_contentsLayer)
         m_contentsLayer->setOwner(0);
+
+    if (m_contentsClippingLayer)
+        m_contentsClippingLayer->setOwner(0);
         
     if (m_structuralLayer)
         m_structuralLayer->setOwner(0);
@@ -669,7 +672,16 @@ void GraphicsLayerCA::setContentsRect(const IntRect& rect)
         return;
 
     GraphicsLayer::setContentsRect(rect);
-    noteLayerPropertyChanged(ContentsRectChanged);
+    noteLayerPropertyChanged(ContentsRectsChanged);
+}
+
+void GraphicsLayerCA::setContentsClippingRect(const IntRect& rect)
+{
+    if (rect == m_contentsClippingRect)
+        return;
+
+    GraphicsLayer::setContentsClippingRect(rect);
+    noteLayerPropertyChanged(ContentsRectsChanged);
 }
 
 bool GraphicsLayerCA::shouldRepaintOnSizeChange() const
@@ -1214,8 +1226,8 @@ void GraphicsLayerCA::commitLayerChangesBeforeSublayers(CommitState& commitState
     if (m_uncommittedChanges & DirtyRectsChanged)
         repaintLayerDirtyRects();
     
-    if (m_uncommittedChanges & ContentsRectChanged)
-        updateContentsRect();
+    if (m_uncommittedChanges & ContentsRectsChanged) // Needs to happen before ChildrenChanged
+        updateContentsRects();
 
     if (m_uncommittedChanges & MaskLayerChanged)
         updateMaskLayer();
@@ -1297,7 +1309,7 @@ void GraphicsLayerCA::updateSublayerList(bool maxLayerDepthReached)
         // FIXME: add the contents layer in the correct order with negative z-order children.
         // This does not cause visible rendering issues because currently contents layers are only used
         // for replaced elements that don't have children.
-        primaryLayerChildren.append(m_contentsLayer);
+        primaryLayerChildren.append(m_contentsClippingLayer ? m_contentsClippingLayer : m_contentsLayer);
     }
     
     const Vector<GraphicsLayer*>& childLayers = children();
@@ -1722,7 +1734,7 @@ void GraphicsLayerCA::updateContentsImage()
                 it->value->setContents(m_contentsLayer->contents());
         }
         
-        updateContentsRect();
+        updateContentsRects();
     } else {
         // No image.
         // m_contentsLayer will be removed via updateSublayerList.
@@ -1737,7 +1749,7 @@ void GraphicsLayerCA::updateContentsMediaLayer()
 
     // Video layer was set as m_contentsLayer, and will get parented in updateSublayerList().
     setupContentsLayer(m_contentsLayer.get());
-    updateContentsRect();
+    updateContentsRects();
 }
 
 void GraphicsLayerCA::updateContentsCanvasLayer()
@@ -1748,7 +1760,7 @@ void GraphicsLayerCA::updateContentsCanvasLayer()
     // CanvasLayer was set as m_contentsLayer, and will get parented in updateSublayerList().
     setupContentsLayer(m_contentsLayer.get());
     m_contentsLayer->setNeedsDisplay();
-    updateContentsRect();
+    updateContentsRects();
 }
 
 void GraphicsLayerCA::updateContentsColorLayer()
@@ -1758,7 +1770,7 @@ void GraphicsLayerCA::updateContentsColorLayer()
         return;
 
     setupContentsLayer(m_contentsLayer.get());
-    updateContentsRect();
+    updateContentsRects();
     ASSERT(m_contentsSolidColor.isValid());
     m_contentsLayer->setBackgroundColor(m_contentsSolidColor);
 
@@ -1769,22 +1781,74 @@ void GraphicsLayerCA::updateContentsColorLayer()
     }
 }
 
-void GraphicsLayerCA::updateContentsRect()
+void GraphicsLayerCA::updateContentsRects()
 {
     if (!m_contentsLayer)
         return;
 
-    FloatPoint point(m_contentsRect.x(), m_contentsRect.y());
-    FloatRect rect(0, 0, m_contentsRect.width(), m_contentsRect.height());
+    FloatPoint contentOrigin;
+    FloatRect contentBounds(0, 0, m_contentsRect.width(), m_contentsRect.height());
+
+    FloatPoint clippingOrigin;
+    FloatRect clippingBounds;
+    
+    bool gainedOrLostClippingLayer = false;
+    if (!m_contentsClippingRect.contains(m_contentsRect)) {
+        if (!m_contentsClippingLayer) {
+            m_contentsClippingLayer = PlatformCALayer::create(PlatformCALayer::LayerTypeLayer, this);
+            m_contentsClippingLayer->setMasksToBounds(true);
+            m_contentsClippingLayer->setAnchorPoint(FloatPoint());
+#ifndef NDEBUG
+            m_contentsClippingLayer->setName("Contents Clipping");
+#endif
+            m_contentsLayer->removeFromSuperlayer();
+            m_contentsClippingLayer->appendSublayer(m_contentsLayer.get());
+            gainedOrLostClippingLayer = true;
+        }
+    
+        clippingOrigin = m_contentsClippingRect.location();
+        clippingBounds.setSize(m_contentsClippingRect.size());
+
+        contentOrigin = toPoint(m_contentsRect.location() - m_contentsClippingRect.location());
+
+        m_contentsClippingLayer->setPosition(clippingOrigin);
+        m_contentsClippingLayer->setBounds(clippingBounds);
+
+        m_contentsLayer->setPosition(contentOrigin);
+        m_contentsLayer->setBounds(contentBounds);
+    
+    } else {
+        if (m_contentsClippingLayer) {
+            m_contentsLayer->removeFromSuperlayer();
 
-    m_contentsLayer->setPosition(point);
-    m_contentsLayer->setBounds(rect);
+            m_contentsClippingLayer->removeFromSuperlayer();
+            m_contentsClippingLayer->setOwner(0);
+            m_contentsClippingLayer = nullptr;
+            gainedOrLostClippingLayer = true;
+        }
+
+        contentOrigin = m_contentsRect.location();
+    }
+    
+    if (gainedOrLostClippingLayer)
+        noteSublayersChanged();
+
+    m_contentsLayer->setPosition(contentOrigin);
+    m_contentsLayer->setBounds(contentBounds);
 
     if (m_contentsLayerClones) {
         LayerMap::const_iterator end = m_contentsLayerClones->end();
         for (LayerMap::const_iterator it = m_contentsLayerClones->begin(); it != end; ++it) {
-            it->value->setPosition(point);
-            it->value->setBounds(rect);
+            it->value->setPosition(contentOrigin);
+            it->value->setBounds(contentBounds);
+        }
+    }
+
+    if (m_contentsClippingLayerClones) {
+        LayerMap::const_iterator end = m_contentsClippingLayerClones->end();
+        for (LayerMap::const_iterator it = m_contentsClippingLayerClones->begin(); it != end; ++it) {
+            it->value->setPosition(clippingOrigin);
+            it->value->setBounds(clippingBounds);
         }
     }
 }
@@ -2660,6 +2724,20 @@ void GraphicsLayerCA::dumpAdditionalProperties(TextStream& textStream, int inden
         writeIndent(textStream, indent + 1);
         textStream << "(top left tile " << gridExtent.x() << ", " << gridExtent.y() << " tiles grid " << gridExtent.width() << " x " << gridExtent.height() << ")\n";
     }
+    
+    if (behavior & LayerTreeAsTextIncludeContentLayers) {
+        if (m_contentsClippingLayer) {
+            writeIndent(textStream, indent + 1);
+            textStream << "(contents clipping layer " << m_contentsClippingLayer->position().x() << ", " << m_contentsClippingLayer->position().y()
+                << " " << m_contentsClippingLayer->bounds().width() << " x " << m_contentsClippingLayer->bounds().height() << ")\n";
+        }
+
+        if (m_contentsLayer) {
+            writeIndent(textStream, indent + 1);
+            textStream << "(contents layer " << m_contentsLayer->position().x() << ", " << m_contentsLayer->position().y()
+                << " " << m_contentsLayer->bounds().width() << " x " << m_contentsLayer->bounds().height() << ")\n";
+        }
+    }
 }
 
 void GraphicsLayerCA::setDebugBorder(const Color& color, float borderWidth)
@@ -2795,7 +2873,8 @@ PassRefPtr<PlatformCALayer> GraphicsLayerCA::findOrMakeClone(CloneID cloneID, Pl
     return resultLayer;
 }   
 
-void GraphicsLayerCA::ensureCloneLayers(CloneID cloneID, RefPtr<PlatformCALayer>& primaryLayer, RefPtr<PlatformCALayer>& structuralLayer, RefPtr<PlatformCALayer>& contentsLayer, CloneLevel cloneLevel)
+void GraphicsLayerCA::ensureCloneLayers(CloneID cloneID, RefPtr<PlatformCALayer>& primaryLayer, RefPtr<PlatformCALayer>& structuralLayer,
+    RefPtr<PlatformCALayer>& contentsLayer, RefPtr<PlatformCALayer>& contentsClippingLayer, CloneLevel cloneLevel)
 {
     structuralLayer = 0;
     contentsLayer = 0;
@@ -2809,9 +2888,13 @@ void GraphicsLayerCA::ensureCloneLayers(CloneID cloneID, RefPtr<PlatformCALayer>
     if (!m_contentsLayerClones && m_contentsLayer)
         m_contentsLayerClones = adoptPtr(new LayerMap);
 
+    if (!m_contentsClippingLayerClones && m_contentsClippingLayer)
+        m_contentsClippingLayerClones = adoptPtr(new LayerMap);
+
     primaryLayer = findOrMakeClone(cloneID, m_layer.get(), m_layerClones.get(), cloneLevel);
     structuralLayer = findOrMakeClone(cloneID, m_structuralLayer.get(), m_structuralLayerClones.get(), cloneLevel);
     contentsLayer = findOrMakeClone(cloneID, m_contentsLayer.get(), m_contentsLayerClones.get(), cloneLevel);
+    contentsClippingLayer = findOrMakeClone(cloneID, m_contentsClippingLayer.get(), m_contentsClippingLayerClones.get(), cloneLevel);
 }
 
 void GraphicsLayerCA::removeCloneLayers()
@@ -2819,6 +2902,7 @@ void GraphicsLayerCA::removeCloneLayers()
     m_layerClones = nullptr;
     m_structuralLayerClones = nullptr;
     m_contentsLayerClones = nullptr;
+    m_contentsClippingLayerClones = nullptr;
 }
 
 FloatPoint GraphicsLayerCA::positionForCloneRootLayer() const
@@ -2849,7 +2933,8 @@ PassRefPtr<PlatformCALayer> GraphicsLayerCA::fetchCloneLayers(GraphicsLayer* rep
     RefPtr<PlatformCALayer> primaryLayer;
     RefPtr<PlatformCALayer> structuralLayer;
     RefPtr<PlatformCALayer> contentsLayer;
-    ensureCloneLayers(replicaState.cloneID(), primaryLayer, structuralLayer, contentsLayer, cloneLevel);
+    RefPtr<PlatformCALayer> contentsClippingLayer;
+    ensureCloneLayers(replicaState.cloneID(), primaryLayer, structuralLayer, contentsLayer, contentsClippingLayer, cloneLevel);
 
     if (m_maskLayer) {
         RefPtr<PlatformCALayer> maskClone = static_cast<GraphicsLayerCA*>(m_maskLayer)->fetchCloneLayers(replicaRoot, replicaState, IntermediateCloneLevel);
@@ -2882,8 +2967,13 @@ PassRefPtr<PlatformCALayer> GraphicsLayerCA::fetchCloneLayers(GraphicsLayer* rep
         replicaLayer = static_cast<GraphicsLayerCA*>(m_replicaLayer)->fetchCloneLayers(replicaRoot, replicaState, RootCloneLevel);
         replicaState.setBranchType(ReplicaState::ChildBranch);
     }
+
+    if (contentsClippingLayer) {
+        ASSERT(contentsLayer);
+        contentsClippingLayer->appendSublayer(contentsLayer.get());
+    }
     
-    if (replicaLayer || structuralLayer || contentsLayer || childLayers.size() > 0) {
+    if (replicaLayer || structuralLayer || contentsLayer || contentsClippingLayer || childLayers.size() > 0) {
         if (structuralLayer) {
             // Replicas render behind the actual layer content.
             if (replicaLayer)
@@ -2891,6 +2981,11 @@ PassRefPtr<PlatformCALayer> GraphicsLayerCA::fetchCloneLayers(GraphicsLayer* rep
                 
             // Add the primary layer next. Even if we have negative z-order children, the primary layer always comes behind.
             clonalSublayers.append(primaryLayer);
+        } else if (contentsClippingLayer) {
+            // FIXME: add the contents layer in the correct order with negative z-order children.
+            // This does not cause visible rendering issues because currently contents layers are only used
+            // for replaced elements that don't have children.
+            clonalSublayers.append(contentsClippingLayer);
         } else if (contentsLayer) {
             // FIXME: add the contents layer in the correct order with negative z-order children.
             // This does not cause visible rendering issues because currently contents layers are only used
@@ -2919,11 +3014,11 @@ PassRefPtr<PlatformCALayer> GraphicsLayerCA::fetchCloneLayers(GraphicsLayer* rep
     if (structuralLayer) {
         structuralLayer->setSublayers(clonalSublayers);
 
-        if (contentsLayer) {
+        if (contentsClippingLayer || contentsLayer) {
             // If we have a transform layer, then the contents layer is parented in the 
             // primary layer (which is itself a child of the transform layer).
             primaryLayer->removeAllSublayers();
-            primaryLayer->appendSublayer(contentsLayer.get());
+            primaryLayer->appendSublayer(contentsClippingLayer ? contentsClippingLayer.get() : contentsLayer.get());
         }
 
         result = structuralLayer;
index 50e29b6..958eff1 100644 (file)
@@ -105,6 +105,7 @@ public:
     virtual void setContentsNeedsDisplay();
     
     virtual void setContentsRect(const IntRect&);
+    virtual void setContentsClippingRect(const IntRect&) OVERRIDE;
     
     virtual void suspendAnimations(double time);
     virtual void resumeAnimations();
@@ -314,7 +315,8 @@ private:
     PassRefPtr<PlatformCALayer> cloneLayer(PlatformCALayer *, CloneLevel);
     PassRefPtr<PlatformCALayer> findOrMakeClone(CloneID, PlatformCALayer *, LayerMap*, CloneLevel);
 
-    void ensureCloneLayers(CloneID cloneID, RefPtr<PlatformCALayer>& primaryLayer, RefPtr<PlatformCALayer>& structuralLayer, RefPtr<PlatformCALayer>& contentsLayer, CloneLevel cloneLevel);
+    void ensureCloneLayers(CloneID, RefPtr<PlatformCALayer>& primaryLayer, RefPtr<PlatformCALayer>& structuralLayer,
+        RefPtr<PlatformCALayer>& contentsLayer, RefPtr<PlatformCALayer>& contentsClippingLayer, CloneLevel);
 
     bool hasCloneLayers() const { return m_layerClones; }
     void removeCloneLayers();
@@ -340,7 +342,7 @@ private:
     void updateContentsMediaLayer();
     void updateContentsCanvasLayer();
     void updateContentsColorLayer();
-    void updateContentsRect();
+    void updateContentsRects();
     void updateMaskLayer();
     void updateReplicatedLayers();
 
@@ -392,7 +394,7 @@ private:
         ContentsMediaLayerChanged = 1 << 16,
         ContentsCanvasLayerChanged = 1 << 17,
         ContentsColorLayerChanged = 1 << 18,
-        ContentsRectChanged = 1 << 19,
+        ContentsRectsChanged = 1 << 19,
         MaskLayerChanged = 1 << 20,
         ReplicatedLayerChanged = 1 << 21,
         ContentsNeedsDisplay = 1 << 22,
@@ -413,12 +415,14 @@ private:
 
     RefPtr<PlatformCALayer> m_layer; // The main layer
     RefPtr<PlatformCALayer> m_structuralLayer; // A layer used for structural reasons, like preserves-3d or replica-flattening. Is the parent of m_layer.
+    RefPtr<PlatformCALayer> m_contentsClippingLayer; // A layer used to clip inner content
     RefPtr<PlatformCALayer> m_contentsLayer; // A layer used for inner content, like image and video
 
     // References to clones of our layers, for replicated layers.
     OwnPtr<LayerMap> m_layerClones;
     OwnPtr<LayerMap> m_structuralLayerClones;
     OwnPtr<LayerMap> m_contentsLayerClones;
+    OwnPtr<LayerMap> m_contentsClippingLayerClones;
 
 #ifdef VISIBLE_TILE_WASH
     RefPtr<PlatformCALayer> m_visibleTileWashLayer;
index 398aeba..e19422b 100644 (file)
@@ -925,8 +925,15 @@ void RenderLayerBacking::updateInternalHierarchy()
 
 void RenderLayerBacking::resetContentsRect()
 {
-    IntRect rect = pixelSnappedIntRect(contentsBox());
-    m_graphicsLayer->setContentsRect(rect);
+    m_graphicsLayer->setContentsRect(pixelSnappedIntRect(contentsBox()));
+    
+    LayoutRect contentsClippingRect;
+    if (renderer().isBox())
+        contentsClippingRect = toRenderBox(&renderer())->contentBoxRect();
+
+    contentsClippingRect.move(contentOffsetInCompostingLayer());
+    m_graphicsLayer->setContentsClippingRect(pixelSnappedIntRect(contentsClippingRect));
+
     m_graphicsLayer->setContentsTileSize(IntSize());
     m_graphicsLayer->setContentsTilePhase(IntPoint());
 }
@@ -1078,8 +1085,11 @@ void RenderLayerBacking::positionOverflowControlsLayers(const IntSize& offsetFro
         if (hBar) {
             layer->setPosition(hBar->frameRect().location() - offsetFromRoot - offsetFromRenderer);
             layer->setSize(hBar->frameRect().size());
-            if (layer->hasContentsLayer())
-                layer->setContentsRect(IntRect(IntPoint(), hBar->frameRect().size()));
+            if (layer->hasContentsLayer()) {
+                IntRect barRect = IntRect(IntPoint(), hBar->frameRect().size());
+                layer->setContentsRect(barRect);
+                layer->setContentsClippingRect(barRect);
+            }
         }
         layer->setDrawsContent(hBar && !layer->hasContentsLayer());
     }
@@ -1089,8 +1099,11 @@ void RenderLayerBacking::positionOverflowControlsLayers(const IntSize& offsetFro
         if (vBar) {
             layer->setPosition(vBar->frameRect().location() - offsetFromRoot - offsetFromRenderer);
             layer->setSize(vBar->frameRect().size());
-            if (layer->hasContentsLayer())
-                layer->setContentsRect(IntRect(IntPoint(), vBar->frameRect().size()));
+            if (layer->hasContentsLayer()) {
+                IntRect barRect = IntRect(IntPoint(), vBar->frameRect().size());
+                layer->setContentsRect(barRect);
+                layer->setContentsClippingRect(barRect);
+            }
         }
         layer->setDrawsContent(vBar && !layer->hasContentsLayer());
     }
@@ -1383,7 +1396,9 @@ void RenderLayerBacking::updateDirectlyCompositedBackgroundColor(bool isSimpleCo
 
     // An unset (invalid) color will remove the solid color.
     m_graphicsLayer->setContentsToSolidColor(backgroundColor);
-    m_graphicsLayer->setContentsRect(backgroundBox());
+    IntRect contentsRect = backgroundBox();
+    m_graphicsLayer->setContentsRect(contentsRect);
+    m_graphicsLayer->setContentsClippingRect(contentsRect);
     didUpdateContentsRect = true;
 }
 
@@ -1443,6 +1458,7 @@ void RenderLayerBacking::updateDirectlyCompositedBackgroundImage(bool isSimpleCo
     m_graphicsLayer->setContentsTileSize(tileSize);
     m_graphicsLayer->setContentsTilePhase(phase);
     m_graphicsLayer->setContentsRect(destRect);
+    m_graphicsLayer->setContentsClippingRect(destRect);
     m_graphicsLayer->setContentsToImage(image.get());
     didUpdateContentsRect = true;
 }
@@ -1725,6 +1741,11 @@ void RenderLayerBacking::updateImageContents()
 
     // This is a no-op if the layer doesn't have an inner layer for the image.
     m_graphicsLayer->setContentsRect(pixelSnappedIntRect(contentsBox()));
+
+    LayoutRect contentsClippingRect = imageRenderer->contentBoxRect();
+    contentsClippingRect.move(contentOffsetInCompostingLayer());
+    m_graphicsLayer->setContentsClippingRect(pixelSnappedIntRect(contentsClippingRect));
+
     m_graphicsLayer->setContentsToImage(image);
     bool isSimpleContainer = false;
     updateDrawsContent(isSimpleContainer);
@@ -1772,14 +1793,19 @@ LayoutRect RenderLayerBacking::contentsBox() const
     if (!renderer().isBox())
         return LayoutRect();
 
+    RenderBox& renderBox = *toRenderBox(&renderer());
     LayoutRect contentsRect;
 #if ENABLE(VIDEO)
-    if (renderer().isVideo()) {
-        RenderVideo* videoRenderer = toRenderVideo(&renderer());
+    if (renderBox.isVideo()) {
+        RenderVideo* videoRenderer = toRenderVideo(&renderBox);
         contentsRect = videoRenderer->videoBox();
     } else
 #endif
-        contentsRect = toRenderBox(&renderer())->contentBoxRect();
+    if (renderBox.isRenderReplaced()) {
+        RenderReplaced& renderReplaced = *toRenderReplaced(&renderBox);
+        contentsRect = renderReplaced.replacedContentRect(renderBox.intrinsicSize());
+    } else
+        contentsRect = renderBox.contentBoxRect();
 
     contentsRect.move(contentOffsetInCompostingLayer());
     return contentsRect;
index 3967ca2..aaad9f7 100644 (file)
@@ -1390,6 +1390,8 @@ String RenderLayerCompositor::layerTreeAsText(LayerTreeFlags flags)
         layerTreeBehavior |= LayerTreeAsTextIncludeRepaintRects;
     if (flags & LayerTreeFlagsIncludePaintingPhases)
         layerTreeBehavior |= LayerTreeAsTextIncludePaintingPhases;
+    if (flags & LayerTreeFlagsIncludeContentLayers)
+        layerTreeBehavior |= LayerTreeAsTextIncludeContentLayers;
 
     // We skip dumping the scroll and clip layers to keep layerTreeAsText output
     // similar between platforms.
index d8a3a25..9b9b39e 100644 (file)
@@ -246,6 +246,7 @@ void RenderVideo::updatePlayer()
     mediaPlayer->setFrameView(&view().frameView());
     mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height()));
     mediaPlayer->setVisible(true);
+    mediaPlayer->setShouldMaintainAspectRatio(style()->objectFit() != ObjectFitFill);
 }
 
 LayoutUnit RenderVideo::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
index 3efb3b1..ad8adcd 100644 (file)
@@ -1605,6 +1605,8 @@ String Internals::layerTreeAsText(Document* document, unsigned flags, ExceptionC
         layerTreeFlags |= LayerTreeFlagsIncludeRepaintRects;
     if (flags & LAYER_TREE_INCLUDES_PAINTING_PHASES)
         layerTreeFlags |= LayerTreeFlagsIncludePaintingPhases;
+    if (flags & LAYER_TREE_INCLUDES_CONTENT_LAYERS)
+        layerTreeFlags |= LayerTreeFlagsIncludeContentLayers;
 
     return document->frame()->layerTreeAsText(layerTreeFlags);
 }
index d1b4b97..3cd5d0f 100644 (file)
@@ -225,7 +225,8 @@ public:
         LAYER_TREE_INCLUDES_VISIBLE_RECTS = 1,
         LAYER_TREE_INCLUDES_TILE_CACHES = 2,
         LAYER_TREE_INCLUDES_REPAINT_RECTS = 4,
-        LAYER_TREE_INCLUDES_PAINTING_PHASES = 8
+        LAYER_TREE_INCLUDES_PAINTING_PHASES = 8,
+        LAYER_TREE_INCLUDES_CONTENT_LAYERS = 16
     };
     String layerTreeAsText(Document*, unsigned flags, ExceptionCode&) const;
     String layerTreeAsText(Document*, ExceptionCode&) const;
index 98aeff8..56e7889 100644 (file)
     const unsigned short LAYER_TREE_INCLUDES_TILE_CACHES = 2;
     const unsigned short LAYER_TREE_INCLUDES_REPAINT_RECTS = 4;
     const unsigned short LAYER_TREE_INCLUDES_PAINTING_PHASES = 8;
+    const unsigned short LAYER_TREE_INCLUDES_CONTENT_LAYERS = 16;
     [RaisesException] DOMString layerTreeAsText(Document document, optional unsigned short flags);
 
     [RaisesException] DOMString scrollingStateTreeAsText(Document document);