Implement object-fit CSS property
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 30 Aug 2013 00:23:23 +0000 (00:23 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 30 Aug 2013 00:23:23 +0000 (00:23 +0000)
https://bugs.webkit.org/show_bug.cgi?id=52040

Source/WebCore:

Reviewed by Antti Koivisto, Sam Weinig.

Merge object-fit patch from Blink r156535, which started as a patch
by me.

Since then, the spec has gone to CR. This patch is an
implementation of object-fit as described in
http://www.w3.org/TR/2012/CR-css3-images-20120417/#object-fit

Object-fit is used to maintain the aspect ratio of replaced content
within its content box. All object-fit values but the initial one
('fill') will always ensure that the aspect ratio is retained, in
different ways (fit inside the content box, cover the content box, or
use intrinsic size). Painting is always clipped against the content
box, regardless of the 'overflow' property.

Tests: fast/css/object-fit/object-fit-canvas.html
       fast/css/object-fit/object-fit-embed.html
       fast/css/object-fit/object-fit-grow-landscape.html
       fast/css/object-fit/object-fit-grow-portrait.html
       fast/css/object-fit/object-fit-img-svg.html
       fast/css/object-fit/object-fit-img-svg2.html
       fast/css/object-fit/object-fit-img.html
       fast/css/object-fit/object-fit-input-image.html
       fast/css/object-fit/object-fit-object.html
       fast/css/object-fit/object-fit-shrink.html
       fast/css/object-fit/object-fit-video-poster.html
       fast/css/parsing-object-fit.html
       http/tests/css/object-fit-delayed-img-svg.html
       media/video-object-fit-change.html
       media/video-object-fit.html

* css/CSSComputedStyleDeclaration.cpp:
(WebCore::ComputedStyleExtractor::propertyValue):
* css/CSSParser.cpp:
(WebCore::isValidKeywordPropertyAndValue):
(WebCore::isKeywordPropertyID):
(WebCore::CSSParser::parseValue):
* css/CSSPrimitiveValueMappings.h:
(WebCore::CSSPrimitiveValue::CSSPrimitiveValue):
(WebCore::CSSPrimitiveValue::operator EObjectFit):
* css/CSSProperty.cpp:
(WebCore::CSSProperty::isInheritedProperty):
* css/CSSPropertyNames.in:
* css/CSSValueKeywords.in:
* css/DeprecatedStyleBuilder.cpp:
(WebCore::DeprecatedStyleBuilder::DeprecatedStyleBuilder):
* css/StyleResolver.cpp:
(WebCore::StyleResolver::applyProperty):
* css/html.css:
(video): Set object-fit to 'contain'. This is how VIDEO elements
work, apparently.
* loader/cache/CachedImage.cpp:
(WebCore::CachedImage::imageSizeForRenderer):
* loader/cache/CachedImage.h:
* platform/graphics/LayoutSize.h:
(WebCore::fitLayoutSizeToAspectRatio): New function to grow or shrink
in one dimension to fit to the aspect ratio.
* rendering/RenderHTMLCanvas.cpp:
(WebCore::RenderHTMLCanvas::paintReplaced): Apply object-fit and
clip if necessary.
* rendering/RenderImage.cpp:
(WebCore::RenderImage::updateInnerContentRect):
(WebCore::RenderImage::imageDimensionsChanged): Update intrinsic
size properly, and recalculate the inner content rectangle (the
exact area occupied by the replaced content) again if appropriate.
(WebCore::RenderImage::paintReplaced): Apply object-fit and clip
if necessary.
(WebCore::RenderImage::foregroundIsKnownToBeOpaqueInRect):
object-fit may leave parts of the content box empty, in which case
it won't be fully obscured.
(WebCore::RenderImage::layout):
* rendering/RenderImage.h:
* rendering/RenderImageResource.cpp:
(WebCore::RenderImageResource::intrinsicSize): Need this to
differentiate between intrinsic and extrinsic size for SVG images.
* rendering/RenderImageResource.h:
* rendering/RenderImageResourceStyleImage.h:
* rendering/RenderReplaced.cpp:
(WebCore::RenderReplaced::replacedContentRect): Return the
rectangle occupied by the replaced content. This will be identical
to the content box if object-fit is 'fill', but will typically be
something else for other values.
* rendering/RenderReplaced.h:
* rendering/RenderVideo.cpp:
(WebCore::RenderVideo::videoBox): Not much left to do here, with
the new RenderReplaced::replacedContentRect() method in place.
(WebCore::RenderVideo::paintReplaced): Apply object-fit and clip
if necessary.
* rendering/style/RenderStyle.cpp:
(WebCore::RenderStyle::changeRequiresRepaint):
* rendering/style/RenderStyle.h:
* rendering/style/RenderStyleConstants.h:
* rendering/style/StyleRareNonInheritedData.cpp:
(WebCore::StyleRareNonInheritedData::StyleRareNonInheritedData):
(WebCore::StyleRareNonInheritedData::operator==):
* rendering/style/StyleRareNonInheritedData.h:

LayoutTests:

Reviewed by Antti Koivisto, Sam Weinig.

Tests for object-fit.

* fast/css/object-fit/object-fit-canvas-expected.html: Added.
* fast/css/object-fit/object-fit-canvas.html: Added.
* fast/css/object-fit/object-fit-embed-expected.html: Added.
* fast/css/object-fit/object-fit-embed.html: Added.
* fast/css/object-fit/object-fit-grow-landscape-expected.html: Added.
* fast/css/object-fit/object-fit-grow-landscape.html: Added.
* fast/css/object-fit/object-fit-grow-portrait-expected.html: Added.
* fast/css/object-fit/object-fit-grow-portrait.html: Added.
* fast/css/object-fit/object-fit-img-expected.html: Added.
* fast/css/object-fit/object-fit-img-svg-expected.html: Added.
* fast/css/object-fit/object-fit-img-svg.html: Added.
* fast/css/object-fit/object-fit-img-svg2-expected.html: Added.
* fast/css/object-fit/object-fit-img-svg2.html: Added.
* fast/css/object-fit/object-fit-img.html: Added.
* fast/css/object-fit/object-fit-input-image-expected.html: Added.
* fast/css/object-fit/object-fit-input-image.html: Added.
* fast/css/object-fit/object-fit-object-expected.html: Added.
* fast/css/object-fit/object-fit-object.html: Added.
* fast/css/object-fit/object-fit-shrink-expected.html: Added.
* fast/css/object-fit/object-fit-shrink.html: Added.
* fast/css/object-fit/object-fit-video-poster-expected.html: Added.
* fast/css/object-fit/object-fit-video-poster.html: Added.
* fast/css/parsing-object-fit-expected.txt: Added.
* fast/css/parsing-object-fit.html: Added.
* fast/css/resources/circle.svg: Added.
* fast/css/resources/circle2.svg: Added.
* fast/css/resources/circles-landscape-small.png: Added.
* fast/css/resources/circles-landscape.png: Added.
* fast/css/resources/circles-portrait-small.png: Added.
* fast/css/resources/circles-portrait.png: Added.
* http/tests/css/object-fit-delayed-img-svg-expected.html: Added.
* http/tests/css/object-fit-delayed-img-svg.html: Added.
* media/video-object-fit-change-expected.html: Added.
* media/video-object-fit-change.html: Added.
* media/video-object-fit-expected.html: Added.
* media/video-object-fit.html: Added.
* platform/mac/TestExpectations:

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

69 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/css/object-fit/object-fit-canvas-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-canvas.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-embed-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-embed.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-grow-landscape-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-grow-landscape.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-grow-portrait-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-grow-portrait.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-img-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-img-svg-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-img-svg.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-img-svg2-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-img-svg2.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-img.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-input-image-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-input-image.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-object-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-object.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-shrink-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-shrink.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-video-poster-expected.html [new file with mode: 0644]
LayoutTests/fast/css/object-fit/object-fit-video-poster.html [new file with mode: 0644]
LayoutTests/fast/css/parsing-object-fit-expected.txt [new file with mode: 0644]
LayoutTests/fast/css/parsing-object-fit.html [new file with mode: 0644]
LayoutTests/fast/css/resources/circle.svg [new file with mode: 0644]
LayoutTests/fast/css/resources/circle2.svg [new file with mode: 0644]
LayoutTests/fast/css/resources/circles-landscape-small.png [new file with mode: 0644]
LayoutTests/fast/css/resources/circles-landscape.png [new file with mode: 0644]
LayoutTests/fast/css/resources/circles-portrait-small.png [new file with mode: 0644]
LayoutTests/fast/css/resources/circles-portrait.png [new file with mode: 0644]
LayoutTests/http/tests/css/object-fit-delayed-img-svg-expected.html [new file with mode: 0644]
LayoutTests/http/tests/css/object-fit-delayed-img-svg.html [new file with mode: 0644]
LayoutTests/media/video-object-fit-change-expected.html [new file with mode: 0644]
LayoutTests/media/video-object-fit-change.html [new file with mode: 0644]
LayoutTests/media/video-object-fit-expected.html [new file with mode: 0644]
LayoutTests/media/video-object-fit.html [new file with mode: 0644]
LayoutTests/platform/mac/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/css/CSSComputedStyleDeclaration.cpp
Source/WebCore/css/CSSParser.cpp
Source/WebCore/css/CSSPrimitiveValueMappings.h
Source/WebCore/css/CSSProperty.cpp
Source/WebCore/css/CSSPropertyNames.in
Source/WebCore/css/CSSValueKeywords.in
Source/WebCore/css/DeprecatedStyleBuilder.cpp
Source/WebCore/css/StyleResolver.cpp
Source/WebCore/css/html.css
Source/WebCore/loader/cache/CachedImage.cpp
Source/WebCore/loader/cache/CachedImage.h
Source/WebCore/platform/graphics/GraphicsLayer.h
Source/WebCore/platform/graphics/LayoutRect.cpp
Source/WebCore/platform/graphics/LayoutRect.h
Source/WebCore/platform/graphics/LayoutSize.h
Source/WebCore/rendering/RenderHTMLCanvas.cpp
Source/WebCore/rendering/RenderImage.cpp
Source/WebCore/rendering/RenderImage.h
Source/WebCore/rendering/RenderImageResource.cpp
Source/WebCore/rendering/RenderImageResource.h
Source/WebCore/rendering/RenderImageResourceStyleImage.h
Source/WebCore/rendering/RenderObject.h
Source/WebCore/rendering/RenderReplaced.cpp
Source/WebCore/rendering/RenderReplaced.h
Source/WebCore/rendering/RenderVideo.cpp
Source/WebCore/rendering/style/RenderStyle.cpp
Source/WebCore/rendering/style/RenderStyle.h
Source/WebCore/rendering/style/RenderStyleConstants.h
Source/WebCore/rendering/style/StyleRareNonInheritedData.cpp
Source/WebCore/rendering/style/StyleRareNonInheritedData.h

index 3d0d5ae..3e66000 100644 (file)
@@ -1,3 +1,50 @@
+2013-08-26  Simon Fraser  <simon.fraser@apple.com>
+
+        Implement object-fit CSS property
+        https://bugs.webkit.org/show_bug.cgi?id=52040
+
+        Reviewed by Antti Koivisto, Sam Weinig.
+
+        Tests for object-fit.
+
+        * fast/css/object-fit/object-fit-canvas-expected.html: Added.
+        * fast/css/object-fit/object-fit-canvas.html: Added.
+        * fast/css/object-fit/object-fit-embed-expected.html: Added.
+        * fast/css/object-fit/object-fit-embed.html: Added.
+        * fast/css/object-fit/object-fit-grow-landscape-expected.html: Added.
+        * fast/css/object-fit/object-fit-grow-landscape.html: Added.
+        * fast/css/object-fit/object-fit-grow-portrait-expected.html: Added.
+        * fast/css/object-fit/object-fit-grow-portrait.html: Added.
+        * fast/css/object-fit/object-fit-img-expected.html: Added.
+        * fast/css/object-fit/object-fit-img-svg-expected.html: Added.
+        * fast/css/object-fit/object-fit-img-svg.html: Added.
+        * fast/css/object-fit/object-fit-img-svg2-expected.html: Added.
+        * fast/css/object-fit/object-fit-img-svg2.html: Added.
+        * fast/css/object-fit/object-fit-img.html: Added.
+        * fast/css/object-fit/object-fit-input-image-expected.html: Added.
+        * fast/css/object-fit/object-fit-input-image.html: Added.
+        * fast/css/object-fit/object-fit-object-expected.html: Added.
+        * fast/css/object-fit/object-fit-object.html: Added.
+        * fast/css/object-fit/object-fit-shrink-expected.html: Added.
+        * fast/css/object-fit/object-fit-shrink.html: Added.
+        * fast/css/object-fit/object-fit-video-poster-expected.html: Added.
+        * fast/css/object-fit/object-fit-video-poster.html: Added.
+        * fast/css/parsing-object-fit-expected.txt: Added.
+        * fast/css/parsing-object-fit.html: Added.
+        * fast/css/resources/circle.svg: Added.
+        * fast/css/resources/circle2.svg: Added.
+        * fast/css/resources/circles-landscape-small.png: Added.
+        * fast/css/resources/circles-landscape.png: Added.
+        * fast/css/resources/circles-portrait-small.png: Added.
+        * fast/css/resources/circles-portrait.png: Added.
+        * http/tests/css/object-fit-delayed-img-svg-expected.html: Added.
+        * http/tests/css/object-fit-delayed-img-svg.html: Added.
+        * media/video-object-fit-change-expected.html: Added.
+        * media/video-object-fit-change.html: Added.
+        * media/video-object-fit-expected.html: Added.
+        * media/video-object-fit.html: Added.
+        * platform/mac/TestExpectations:
+
 2013-08-29  Tim Horton  <timothy_horton@apple.com>
 
         SVG clipping, masking, and gradients-on-text do not respect the device scale factor
diff --git a/LayoutTests/fast/css/object-fit/object-fit-canvas-expected.html b/LayoutTests/fast/css/object-fit/object-fit-canvas-expected.html
new file mode 100644 (file)
index 0000000..de86af6
--- /dev/null
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on canvas</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group > * > * { display: block; }
+      .group > *:nth-child(1) * { width:100%; height:100%; }
+      .group > *:nth-child(2) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(3) * { height:100%; margin-left:-50%; }
+      .group > *:nth-child(4) * { width:288px; margin-left:-108px; margin-top:-36px; }
+      .group > *:nth-child(5) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(6) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(7) * { width:100%; height:100%; }
+    </style>
+    <script type="text/javascript" charset="utf-8">
+      if (window.testRunner)
+        testRunner.waitUntilDone();
+
+      var circlesImage;
+
+      function paintCanvas(canvas)
+      {
+        var ctx = canvas.getContext('2d');
+        ctx.drawImage(circlesImage, 0, 0);
+      }
+
+      function init()
+      {
+        circlesImage = new Image()
+        circlesImage.addEventListener('load', imageLoaded, false);
+        circlesImage.src = "../resources/circles-landscape.png";
+      }
+
+      function imageLoaded()
+      {
+        var canvases = document.getElementsByTagName('canvas');
+        for (var i = 0; i < canvases.length; ++i)
+          paintCanvas(canvases[i])
+        if (window.testRunner)
+          testRunner.notifyDone();
+      }
+
+      window.addEventListener('load', init, false);
+    </script>
+  </head>
+  <body>
+
+    <div class="group">
+      <div><canvas width="288" height="144"></canvas></div>
+      <div><canvas width="288" height="144"></canvas></div>
+      <div><canvas width="288" height="144"></canvas></div>
+      <div><canvas width="288" height="144"></canvas></div>
+      <div><canvas width="288" height="144"></canvas></div>
+      <div><canvas width="288" height="144"></canvas></div>
+      <div><canvas width="288" height="144"></canvas></div>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-canvas.html b/LayoutTests/fast/css/object-fit/object-fit-canvas.html
new file mode 100644 (file)
index 0000000..c85eec1
--- /dev/null
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on canvas</title>
+    <style type="text/css">
+      img, embed, object, input, canvas, video {
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group { object-fit: contain; }
+      .group > *:nth-child(1) { object-fit: fill; }
+      .group > *:nth-child(2) { object-fit: contain; }
+      .group > *:nth-child(3) { object-fit: cover; }
+      .group > *:nth-child(4) { object-fit: none; }
+      .group > *:nth-child(5) { object-fit: scale-down; }
+      .group > *:nth-child(6) { object-fit: inherit; }
+      .group > *:nth-child(7) { }
+    </style>
+    <script type="text/javascript" charset="utf-8">
+      if (window.testRunner)
+        testRunner.waitUntilDone();
+
+      var circlesImage;
+
+      function paintCanvas(canvas)
+      {
+        var ctx = canvas.getContext('2d');
+        ctx.drawImage(circlesImage, 0, 0);
+      }
+
+      function init()
+      {
+        circlesImage = new Image()
+        circlesImage.addEventListener('load', imageLoaded, false);
+        circlesImage.src = "../resources/circles-landscape.png";
+      }
+
+      function imageLoaded()
+      {
+        var canvases = document.getElementsByTagName('canvas');
+        for (var i = 0; i < canvases.length; ++i)
+          paintCanvas(canvases[i])
+        if (window.testRunner)
+          testRunner.notifyDone();
+      }
+
+      window.addEventListener('load', init, false);
+  </script>
+  </head>
+  <body>
+
+    <div class="group">
+      <canvas width="288" height="144"></canvas>
+      <canvas width="288" height="144"></canvas>
+      <canvas width="288" height="144"></canvas>
+      <canvas width="288" height="144"></canvas>
+      <canvas width="288" height="144"></canvas>
+      <canvas width="288" height="144"></canvas>
+      <canvas width="288" height="144"></canvas>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-embed-expected.html b/LayoutTests/fast/css/object-fit/object-fit-embed-expected.html
new file mode 100644 (file)
index 0000000..d4d7309
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on embeds</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group > * > * { display: block; }
+      .group > *:nth-child(1) * { width:100%; height:100%; }
+      .group > *:nth-child(2) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(3) * { height:100%; margin-left:-50%; }
+      .group > *:nth-child(4) * { width:288px; margin-left:-108px; margin-top:-36px; }
+      .group > *:nth-child(5) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(6) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(7) * { width:100%; height:100%; }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <div><embed src="../resources/circles-landscape.png"></embed></div>
+      <div><embed src="../resources/circles-landscape.png"></embed></div>
+      <div><embed src="../resources/circles-landscape.png"></embed></div>
+      <div><embed src="../resources/circles-landscape.png"></embed></div>
+      <div><embed src="../resources/circles-landscape.png"></embed></div>
+      <div><embed src="../resources/circles-landscape.png"></embed></div>
+      <div><embed src="../resources/circles-landscape.png"></embed></div>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-embed.html b/LayoutTests/fast/css/object-fit/object-fit-embed.html
new file mode 100644 (file)
index 0000000..d84dd77
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on embeds</title>
+    <style type="text/css">
+      img, embed, object, input, canvas, video {
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group { object-fit: contain; }
+      .group > *:nth-child(1) { object-fit: fill; }
+      .group > *:nth-child(2) { object-fit: contain; }
+      .group > *:nth-child(3) { object-fit: cover; }
+      .group > *:nth-child(4) { object-fit: none; }
+      .group > *:nth-child(5) { object-fit: scale-down; }
+      .group > *:nth-child(6) { object-fit: inherit; }
+      .group > *:nth-child(7) { }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <embed src="../resources/circles-landscape.png"></embed>
+      <embed src="../resources/circles-landscape.png"></embed>
+      <embed src="../resources/circles-landscape.png"></embed>
+      <embed src="../resources/circles-landscape.png"></embed>
+      <embed src="../resources/circles-landscape.png"></embed>
+      <embed src="../resources/circles-landscape.png"></embed>
+      <embed src="../resources/circles-landscape.png"></embed>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-grow-landscape-expected.html b/LayoutTests/fast/css/object-fit/object-fit-grow-landscape-expected.html
new file mode 100644 (file)
index 0000000..5bc2c94
--- /dev/null
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit, landscape images smaller than content box</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        width: 144px;
+        height: 144px;
+        margin: 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group > * > * { display: block; }
+      .group.landscape > *:nth-child(1) > * { width:100%; height:100%; }
+      .group.landscape > *:nth-child(2) > * { width:100%; margin-top:25%; }
+      .group.landscape > *:nth-child(3) > * { height:100%; margin-left:-50%; }
+      .group.landscape > *:nth-child(4) > * { margin-left: 36px; margin-top:54px; }
+      .group.landscape > *:nth-child(5) > * { margin-left: 36px; margin-top:54px; }
+    </style>
+  </head>
+  <body>
+
+    <!-- Small images, should be scaled up when allowed. -->
+    <div class="group landscape">
+      <div><img src="../resources/circles-landscape-small.png"></div>
+      <div><img src="../resources/circles-landscape-small.png"></div>
+      <div><img src="../resources/circles-landscape-small.png"></div>
+      <div><img src="../resources/circles-landscape-small.png"></div>
+      <div><img src="../resources/circles-landscape-small.png"></div>
+    </div>
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-grow-landscape.html b/LayoutTests/fast/css/object-fit/object-fit-grow-landscape.html
new file mode 100644 (file)
index 0000000..ff3fc07
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit, landscape images smaller than content box</title>
+    <style type="text/css">
+      img {
+        display: inline-block;
+        width: 144px;
+        height: 144px;
+        margin: 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+    </style>
+  </head>
+  <body>
+
+    <!-- Small images, should be scaled up when allowed. -->
+    <img style="object-fit: fill;" src="../resources/circles-landscape-small.png">
+    <img style="object-fit: contain;" src="../resources/circles-landscape-small.png">
+    <img style="object-fit: cover;" src="../resources/circles-landscape-small.png">
+    <img style="object-fit: none;" src="../resources/circles-landscape-small.png">
+    <img style="object-fit: scale-down;" src="../resources/circles-landscape-small.png">
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-grow-portrait-expected.html b/LayoutTests/fast/css/object-fit/object-fit-grow-portrait-expected.html
new file mode 100644 (file)
index 0000000..56e025d
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit, portrait images smaller than content box</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        height: 144px;
+        width: 144px;
+        margin: 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+      div > * > * { display:block; }
+      .group.portrait > *:nth-child(1) > * { width:100%; height:100%; }
+      .group.portrait > *:nth-child(2) > * { height:100%; margin-left:25%; }
+      .group.portrait > *:nth-child(3) > * { width:100%; margin-top:-50%; }
+      .group.portrait > *:nth-child(4) > * { margin-top:36px; margin-left:54px; }
+      .group.portrait > *:nth-child(5) > * { margin-top:36px; margin-left:54px; }
+    </style>
+  </head>
+  <body>
+
+    <!-- Small images, should be scaled up when allowed. -->
+    <div class="group portrait">
+      <div><img src="../resources/circles-portrait-small.png"></div>
+      <div><img src="../resources/circles-portrait-small.png"></div>
+      <div><img src="../resources/circles-portrait-small.png"></div>
+      <div><img src="../resources/circles-portrait-small.png"></div>
+      <div><img src="../resources/circles-portrait-small.png"></div>
+    </div>
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-grow-portrait.html b/LayoutTests/fast/css/object-fit/object-fit-grow-portrait.html
new file mode 100644 (file)
index 0000000..72ec9b9
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit, portrait images smaller than content box</title>
+    <style type="text/css">
+      img {
+        display: inline-block;
+        height: 144px;
+        width: 144px;
+        margin: 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+    </style>
+  </head>
+  <body>
+
+    <!-- Small images, should be scaled up when allowed. -->
+    <img style="object-fit: fill;" src="../resources/circles-portrait-small.png">
+    <img style="object-fit: contain;" src="../resources/circles-portrait-small.png">
+    <img style="object-fit: cover;" src="../resources/circles-portrait-small.png">
+    <img style="object-fit: none;" src="../resources/circles-portrait-small.png">
+    <img style="object-fit: scale-down;" src="../resources/circles-portrait-small.png">
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-img-expected.html b/LayoutTests/fast/css/object-fit/object-fit-img-expected.html
new file mode 100644 (file)
index 0000000..ba0d429
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on images</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group > * > * { display: block; }
+      .group > *:nth-child(1) * { width:100%; height:100%; }
+      .group > *:nth-child(2) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(3) * { height:100%; margin-left:-50%; }
+      .group > *:nth-child(4) * { width:288px; margin-left:-108px; margin-top:-36px; }
+      .group > *:nth-child(5) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(6) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(7) * { width:100%; height:100%; }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <div><img src="../resources/circles-landscape.png"></div>
+      <div><img src="../resources/circles-landscape.png"></div>
+      <div><img src="../resources/circles-landscape.png"></div>
+      <div><img src="../resources/circles-landscape.png"></div>
+      <div><img src="../resources/circles-landscape.png"></div>
+      <div><img src="../resources/circles-landscape.png"></div>
+      <div><img src="../resources/circles-landscape.png"></div>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-img-svg-expected.html b/LayoutTests/fast/css/object-fit/object-fit-img-svg-expected.html
new file mode 100644 (file)
index 0000000..b487904
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on SVG images</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        height: 72px;
+        width: 144px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group > * > * { display: block; }
+      .group > *:nth-child(1) * { height:100%; width:100%; }
+      .group > *:nth-child(2) * { height:100%; margin-left:36px; }
+      .group > *:nth-child(3) * { width:100%; margin-top:-36px; }
+      .group > *:nth-child(4) * { height:200px; margin-top:-64px; margin-left:-28px; }
+      .group > *:nth-child(5) * { height:100%; margin-left:36px; }
+      .group > *:nth-child(6) * { height:100%; margin-left:36px; }
+      .group > *:nth-child(7) * { height:100%; width:100%; }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <div><img src="../resources/circle.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle.svg" type="image/svg+xml"></div>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-img-svg.html b/LayoutTests/fast/css/object-fit/object-fit-img-svg.html
new file mode 100644 (file)
index 0000000..d87c3d6
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on SVG images</title>
+    <style type="text/css">
+      img, embed, object, input, canvas, video {
+        height: 72px;
+        width: 144px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group { object-fit: contain; }
+      .group > *:nth-child(1) { object-fit: fill; }
+      .group > *:nth-child(2) { object-fit: contain; }
+      .group > *:nth-child(3) { object-fit: cover; }
+      .group > *:nth-child(4) { object-fit: none; }
+      .group > *:nth-child(5) { object-fit: scale-down; }
+      .group > *:nth-child(6) { object-fit: inherit; }
+      .group > *:nth-child(7) { }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <img src="../resources/circle.svg" type="image/svg+xml">
+      <img src="../resources/circle.svg" type="image/svg+xml">
+      <img src="../resources/circle.svg" type="image/svg+xml">
+      <img src="../resources/circle.svg" type="image/svg+xml">
+      <img src="../resources/circle.svg" type="image/svg+xml">
+      <img src="../resources/circle.svg" type="image/svg+xml">
+      <img src="../resources/circle.svg" type="image/svg+xml">
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-img-svg2-expected.html b/LayoutTests/fast/css/object-fit/object-fit-img-svg2-expected.html
new file mode 100644 (file)
index 0000000..ffa7bbf
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on SVG images, no forced aspect ratio retention</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        height: 72px;
+        width: 144px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group > * > * { display: block; }
+      .group > *:nth-child(1) * { height:100%; width:100%; }
+      .group > *:nth-child(2) * { height:100%; margin-left:36px; }
+      .group > *:nth-child(3) * { width:100%; margin-top:-36px; }
+      .group > *:nth-child(4) * { height:200px; margin-top:-64px; margin-left:-28px; }
+      .group > *:nth-child(5) * { height:100%; margin-left:36px; }
+      .group > *:nth-child(6) * { height:100%; margin-left:36px; }
+      .group > *:nth-child(7) * { height:100%; width:100%; }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <div><img src="../resources/circle2.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle2.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle2.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle2.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle2.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle2.svg" type="image/svg+xml"></div>
+      <div><img src="../resources/circle2.svg" type="image/svg+xml"></div>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-img-svg2.html b/LayoutTests/fast/css/object-fit/object-fit-img-svg2.html
new file mode 100644 (file)
index 0000000..5a99099
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on SVG images, no forced aspect ratio retention</title>
+    <style type="text/css">
+      img, embed, object, input, canvas, video {
+        height: 72px;
+        width: 144px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group { object-fit: contain; }
+      .group > *:nth-child(1) { object-fit: fill; }
+      .group > *:nth-child(2) { object-fit: contain; }
+      .group > *:nth-child(3) { object-fit: cover; }
+      .group > *:nth-child(4) { object-fit: none; }
+      .group > *:nth-child(5) { object-fit: scale-down; }
+      .group > *:nth-child(6) { object-fit: inherit; }
+      .group > *:nth-child(7) { }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <img src="../resources/circle2.svg" type="image/svg+xml">
+      <img src="../resources/circle2.svg" type="image/svg+xml">
+      <img src="../resources/circle2.svg" type="image/svg+xml">
+      <img src="../resources/circle2.svg" type="image/svg+xml">
+      <img src="../resources/circle2.svg" type="image/svg+xml">
+      <img src="../resources/circle2.svg" type="image/svg+xml">
+      <img src="../resources/circle2.svg" type="image/svg+xml">
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-img.html b/LayoutTests/fast/css/object-fit/object-fit-img.html
new file mode 100644 (file)
index 0000000..0080abf
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on images</title>
+    <style type="text/css">
+      img, embed, object, input, canvas, video {
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group { object-fit: contain; }
+      .group > *:nth-child(1) { object-fit: fill; }
+      .group > *:nth-child(2) { object-fit: contain; }
+      .group > *:nth-child(3) { object-fit: cover; }
+      .group > *:nth-child(4) { object-fit: none; }
+      .group > *:nth-child(5) { object-fit: scale-down; }
+      .group > *:nth-child(6) { object-fit: inherit; }
+      .group > *:nth-child(7) { }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <img src="../resources/circles-landscape.png">
+      <img src="../resources/circles-landscape.png">
+      <img src="../resources/circles-landscape.png">
+      <img src="../resources/circles-landscape.png">
+      <img src="../resources/circles-landscape.png">
+      <img src="../resources/circles-landscape.png">
+      <img src="../resources/circles-landscape.png">
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-input-image-expected.html b/LayoutTests/fast/css/object-fit/object-fit-input-image-expected.html
new file mode 100644 (file)
index 0000000..0592918
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on input images</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group > * > * { display: block; }
+      .group > *:nth-child(1) * { width:100%; height:100%; }
+      .group > *:nth-child(2) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(3) * { height:100%; margin-left:-50%; }
+      .group > *:nth-child(4) * { width:288px; margin-left:-108px; margin-top:-36px; }
+      .group > *:nth-child(5) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(6) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(7) * { width:100%; height:100%; }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <div><input type="image" src="../resources/circles-landscape.png"></div>
+      <div><input type="image" src="../resources/circles-landscape.png"></div>
+      <div><input type="image" src="../resources/circles-landscape.png"></div>
+      <div><input type="image" src="../resources/circles-landscape.png"></div>
+      <div><input type="image" src="../resources/circles-landscape.png"></div>
+      <div><input type="image" src="../resources/circles-landscape.png"></div>
+      <div><input type="image" src="../resources/circles-landscape.png"></div>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-input-image.html b/LayoutTests/fast/css/object-fit/object-fit-input-image.html
new file mode 100644 (file)
index 0000000..c6b84b0
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on input images</title>
+    <style type="text/css">
+      img, embed, object, input, canvas, video {
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group { object-fit: contain; }
+      .group > *:nth-child(1) { object-fit: fill; }
+      .group > *:nth-child(2) { object-fit: contain; }
+      .group > *:nth-child(3) { object-fit: cover; }
+      .group > *:nth-child(4) { object-fit: none; }
+      .group > *:nth-child(5) { object-fit: scale-down; }
+      .group > *:nth-child(6) { object-fit: inherit; }
+      .group > *:nth-child(7) { }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <input type="image" src="../resources/circles-landscape.png">
+      <input type="image" src="../resources/circles-landscape.png">
+      <input type="image" src="../resources/circles-landscape.png">
+      <input type="image" src="../resources/circles-landscape.png">
+      <input type="image" src="../resources/circles-landscape.png">
+      <input type="image" src="../resources/circles-landscape.png">
+      <input type="image" src="../resources/circles-landscape.png">
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-object-expected.html b/LayoutTests/fast/css/object-fit/object-fit-object-expected.html
new file mode 100644 (file)
index 0000000..2185c92
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on objects</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group > * > * { display: block; }
+      .group > *:nth-child(1) * { width:100%; height:100%; }
+      .group > *:nth-child(2) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(3) * { height:100%; margin-left:-50%; }
+      .group > *:nth-child(4) * { width:288px; margin-left:-108px; margin-top:-36px; }
+      .group > *:nth-child(5) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(6) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(7) * { width:100%; height:100%; }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <div><object data="../resources/circles-landscape.png"></object></div>
+      <div><object data="../resources/circles-landscape.png"></object></div>
+      <div><object data="../resources/circles-landscape.png"></object></div>
+      <div><object data="../resources/circles-landscape.png"></object></div>
+      <div><object data="../resources/circles-landscape.png"></object></div>
+      <div><object data="../resources/circles-landscape.png"></object></div>
+      <div><object data="../resources/circles-landscape.png"></object></div>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-object.html b/LayoutTests/fast/css/object-fit/object-fit-object.html
new file mode 100644 (file)
index 0000000..777c55d
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on objects</title>
+    <style type="text/css">
+      img, embed, object, input, canvas, video {
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group { object-fit: contain; }
+      .group > *:nth-child(1) { object-fit: fill; }
+      .group > *:nth-child(2) { object-fit: contain; }
+      .group > *:nth-child(3) { object-fit: cover; }
+      .group > *:nth-child(4) { object-fit: none; }
+      .group > *:nth-child(5) { object-fit: scale-down; }
+      .group > *:nth-child(6) { object-fit: inherit; }
+      .group > *:nth-child(7) { }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <object data="../resources/circles-landscape.png"></object>
+      <object data="../resources/circles-landscape.png"></object>
+      <object data="../resources/circles-landscape.png"></object>
+      <object data="../resources/circles-landscape.png"></object>
+      <object data="../resources/circles-landscape.png"></object>
+      <object data="../resources/circles-landscape.png"></object>
+      <object data="../resources/circles-landscape.png"></object>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-shrink-expected.html b/LayoutTests/fast/css/object-fit/object-fit-shrink-expected.html
new file mode 100644 (file)
index 0000000..5ff8832
--- /dev/null
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit, images larger than content box</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        height: 72px;
+        width: 72px;
+        margin: 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+      div > * > * { display:block; }
+      .group.landscape > *:nth-child(1) > * { width:100%; height:100%; }
+      .group.landscape > *:nth-child(2) > * { width:100%; margin-top:25%; }
+      .group.landscape > *:nth-child(3) > * { height:100%; margin-left:-50%; }
+      .group.landscape > *:nth-child(4) > * { width:288px; margin-left:-108px; margin-top:-36px; }
+      .group.landscape > *:nth-child(5) > * { width:100%; margin-top:25%; }
+      .group.portrait > *:nth-child(1) > * { width:100%; height:100%; }
+      .group.portrait > *:nth-child(2) > * { height:100%; margin-left:25%; }
+      .group.portrait > *:nth-child(3) > * { width:100%; margin-top:-50%; }
+      .group.portrait > *:nth-child(4) > * { height:288px; margin-top:-108px; margin-left:-36px; }
+      .group.portrait > *:nth-child(5) > * { height:100%; margin-left:25%; }
+    </style>
+  </head>
+  <body>
+
+    <!-- Large images, should be scaled down when allowed. -->
+    <div class="group landscape">
+      <div><img src="../resources/circles-landscape.png"></div>
+      <div><img src="../resources/circles-landscape.png"></div>
+      <div><img src="../resources/circles-landscape.png"></div>
+      <div><img src="../resources/circles-landscape.png"></div>
+      <div><img src="../resources/circles-landscape.png"></div>
+    </div>
+
+    <br>
+
+    <div class="group portrait">
+      <div><img src="../resources/circles-portrait.png"></div>
+      <div><img src="../resources/circles-portrait.png"></div>
+      <div><img src="../resources/circles-portrait.png"></div>
+      <div><img src="../resources/circles-portrait.png"></div>
+      <div><img src="../resources/circles-portrait.png"></div>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-shrink.html b/LayoutTests/fast/css/object-fit/object-fit-shrink.html
new file mode 100644 (file)
index 0000000..c7c7da2
--- /dev/null
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <style type="text/css">
+      img {
+        display: inline-block;
+        height: 72px;
+        width: 72px;
+        margin: 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+    </style>
+  </head>
+  <body>
+
+    <!-- Large images, should be scaled down when allowed. -->
+    <img style="object-fit: fill;" src="../resources/circles-landscape.png">
+    <img style="object-fit: contain;" src="../resources/circles-landscape.png">
+    <img style="object-fit: cover;" src="../resources/circles-landscape.png">
+    <img style="object-fit: none;" src="../resources/circles-landscape.png">
+    <img style="object-fit: scale-down;" src="../resources/circles-landscape.png">
+    <br>
+    <br>
+
+    <img style="object-fit: fill;" src="../resources/circles-portrait.png">
+    <img style="object-fit: contain;" src="../resources/circles-portrait.png">
+    <img style="object-fit: cover;" src="../resources/circles-portrait.png">
+    <img style="object-fit: none;" src="../resources/circles-portrait.png">
+    <img style="object-fit: scale-down;" src="../resources/circles-portrait.png">
+    <br>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-video-poster-expected.html b/LayoutTests/fast/css/object-fit/object-fit-video-poster-expected.html
new file mode 100644 (file)
index 0000000..f166351
--- /dev/null
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on video poster</title>
+    <style type="text/css">
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group > * > * { display: block; }
+      .group > *:nth-child(1) * { width:100%; height:100%; }
+      .group > *:nth-child(2) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(3) * { height:100%; margin-left:-50%; }
+      .group > *:nth-child(4) * { width:288px; margin-left:-108px; margin-top:-36px; }
+      .group > *:nth-child(5) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(6) * { width:100%; margin-top:25%; }
+      .group > *:nth-child(7) * { width:100%; height:100%; }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <!-- Note about the first video here: There's no way to unlock a video from its
+          aspect ratio without using object-fit:fill, so we have to use it here as well
+          as in the test file. In order to actually being able to detect failures, use a
+          different background color. -->
+      <div style="background:salmon;"><video poster="../resources/circles-landscape.png" style="object-fit:fill;"></video></div>
+      <div><video poster="../resources/circles-landscape.png"></video></div>
+      <div><video poster="../resources/circles-landscape.png"></video></div>
+      <div><video poster="../resources/circles-landscape.png"></video></div>
+      <div><video poster="../resources/circles-landscape.png"></video></div>
+      <div><video poster="../resources/circles-landscape.png"></video></div>
+      <div><video poster="../resources/circles-landscape.png" style="width:100%; height:auto; margin:25% 0 0;"></video></div> <!-- default is contain for video -->
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/object-fit/object-fit-video-poster.html b/LayoutTests/fast/css/object-fit/object-fit-video-poster.html
new file mode 100644 (file)
index 0000000..b592173
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on video poster</title>
+    <style type="text/css">
+      img, embed, object, input, canvas, video {
+        width: 72px;
+        height: 72px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group { object-fit: contain; }
+      .group > *:nth-child(1) { object-fit: fill; }
+      .group > *:nth-child(2) { object-fit: contain; }
+      .group > *:nth-child(3) { object-fit: cover; }
+      .group > *:nth-child(4) { object-fit: none; }
+      .group > *:nth-child(5) { object-fit: scale-down; }
+      .group > *:nth-child(6) { object-fit: inherit; }
+      .group > *:nth-child(7) { }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <video poster="../resources/circles-landscape.png"></video>
+      <video poster="../resources/circles-landscape.png"></video>
+      <video poster="../resources/circles-landscape.png"></video>
+      <video poster="../resources/circles-landscape.png"></video>
+      <video poster="../resources/circles-landscape.png"></video>
+      <video poster="../resources/circles-landscape.png"></video>
+      <video poster="../resources/circles-landscape.png"></video>
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/fast/css/parsing-object-fit-expected.txt b/LayoutTests/fast/css/parsing-object-fit-expected.txt
new file mode 100644 (file)
index 0000000..daa25de
--- /dev/null
@@ -0,0 +1,21 @@
+This tests checks that all of the input values for object-fit parse correctly.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS testComputedStyle(";") is "fill"
+PASS test("object-fit: inherit;") is "inherit"
+PASS test("object-fit: initial;") is "initial"
+PASS test("object-fit: fill;") is "fill"
+PASS test("object-fit: contain;") is "contain"
+PASS test("object-fit: cover;") is "cover"
+PASS test("object-fit: none;") is "none"
+PASS test("object-fit: scale-down;") is "scale-down"
+PASS test("object-fit: fill contain;") is null
+PASS test("object-fit: bananas;") is null
+PASS test("object-fit: 23px;") is null
+PASS test("object-fit: 20%;") is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/css/parsing-object-fit.html b/LayoutTests/fast/css/parsing-object-fit.html
new file mode 100644 (file)
index 0000000..4c590ad
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script src="../js/resources/js-test-pre.js"></script>
+    </head>
+    <body>
+        <script>
+            description("This tests checks that all of the input values for object-fit parse correctly.");
+
+            function test(value)
+            {
+                var div = document.createElement("div");
+                div.setAttribute("style", value);
+                document.body.appendChild(div);
+
+                var result = div.style.getPropertyValue("object-fit");
+                document.body.removeChild(div);
+                return result;
+            }
+
+            function testComputedStyle(value)
+            {
+                var div = document.createElement("div");
+                div.setAttribute("style", value);
+                document.body.appendChild(div);
+
+                var result = window.getComputedStyle(div).objectFit;
+                document.body.removeChild(div);
+                return result;
+            }
+
+            shouldBe('testComputedStyle(";")', '"fill"');
+            shouldBe('test("object-fit: inherit;")', '"inherit"');
+            shouldBe('test("object-fit: initial;")', '"initial"');
+            shouldBe('test("object-fit: fill;")', '"fill"');
+            shouldBe('test("object-fit: contain;")', '"contain"');
+            shouldBe('test("object-fit: cover;")', '"cover"');
+            shouldBe('test("object-fit: none;")', '"none"');
+            shouldBe('test("object-fit: scale-down;")', '"scale-down"');
+
+            shouldBeNull('test("object-fit: fill contain;")');
+            shouldBeNull('test("object-fit: bananas;")');
+            shouldBeNull('test("object-fit: 23px;")');
+            shouldBeNull('test("object-fit: 20%;")');
+        </script>
+        <script src="../js/resources/js-test-post.js"></script>
+    </body>
+</html>
diff --git a/LayoutTests/fast/css/resources/circle.svg b/LayoutTests/fast/css/resources/circle.svg
new file mode 100644 (file)
index 0000000..209b9f4
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+              "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
+    <circle cx="50%" cy="50%" r="80" style="fill:blue;" />
+</svg>
diff --git a/LayoutTests/fast/css/resources/circle2.svg b/LayoutTests/fast/css/resources/circle2.svg
new file mode 100644 (file)
index 0000000..dfa3a90
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+              "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" preserveAspectRatio="none">
+    <circle cx="50%" cy="50%" r="80" style="fill:blue;" />
+</svg>
diff --git a/LayoutTests/fast/css/resources/circles-landscape-small.png b/LayoutTests/fast/css/resources/circles-landscape-small.png
new file mode 100644 (file)
index 0000000..b060d0b
Binary files /dev/null and b/LayoutTests/fast/css/resources/circles-landscape-small.png differ
diff --git a/LayoutTests/fast/css/resources/circles-landscape.png b/LayoutTests/fast/css/resources/circles-landscape.png
new file mode 100644 (file)
index 0000000..2efe877
Binary files /dev/null and b/LayoutTests/fast/css/resources/circles-landscape.png differ
diff --git a/LayoutTests/fast/css/resources/circles-portrait-small.png b/LayoutTests/fast/css/resources/circles-portrait-small.png
new file mode 100644 (file)
index 0000000..708682a
Binary files /dev/null and b/LayoutTests/fast/css/resources/circles-portrait-small.png differ
diff --git a/LayoutTests/fast/css/resources/circles-portrait.png b/LayoutTests/fast/css/resources/circles-portrait.png
new file mode 100644 (file)
index 0000000..1eccdf7
Binary files /dev/null and b/LayoutTests/fast/css/resources/circles-portrait.png differ
diff --git a/LayoutTests/http/tests/css/object-fit-delayed-img-svg-expected.html b/LayoutTests/http/tests/css/object-fit-delayed-img-svg-expected.html
new file mode 100644 (file)
index 0000000..a5b7285
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on SVG images that load slowly</title>
+    <style type="text/css">
+      img, embed, object, input, canvas, video {
+        height: 72px;
+        width: 144px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group { object-fit: contain; }
+      .group > *:nth-child(1) { object-fit: fill; }
+      .group > *:nth-child(2) { object-fit: contain; }
+      .group > *:nth-child(3) { object-fit: cover; }
+      .group > *:nth-child(4) { object-fit: none; }
+      .group > *:nth-child(5) { object-fit: scale-down; }
+      .group > *:nth-child(6) { object-fit: inherit; }
+      .group > *:nth-child(7) { }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <img src="resources/circle.svg" type="image/svg+xml">
+      <img src="resources/circle.svg" type="image/svg+xml">
+      <img src="resources/circle.svg" type="image/svg+xml">
+      <img src="resources/circle.svg" type="image/svg+xml">
+      <img src="resources/circle.svg" type="image/svg+xml">
+      <img src="resources/circle.svg" type="image/svg+xml">
+      <img src="resources/circle.svg" type="image/svg+xml">
+    </div>
+
+  </body>
+</html>
diff --git a/LayoutTests/http/tests/css/object-fit-delayed-img-svg.html b/LayoutTests/http/tests/css/object-fit-delayed-img-svg.html
new file mode 100644 (file)
index 0000000..6c638b5
--- /dev/null
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+
+<html>
+  <head>
+    <title>object-fit on SVG images that load slowly</title>
+    <style type="text/css">
+      img, embed, object, input, canvas, video {
+        height: 72px;
+        width: 144px;
+        margin: 2px 10px;
+        border: 1px solid black;
+        background-color: gray;
+      }
+
+      .group { object-fit: contain; }
+      .group > *:nth-child(1) { object-fit: fill; }
+      .group > *:nth-child(2) { object-fit: contain; }
+      .group > *:nth-child(3) { object-fit: cover; }
+      .group > *:nth-child(4) { object-fit: none; }
+      .group > *:nth-child(5) { object-fit: scale-down; }
+      .group > *:nth-child(6) { object-fit: inherit; }
+      .group > *:nth-child(7) { }
+    </style>
+  </head>
+  <body>
+
+    <div class="group">
+      <img id="hest" src="resources/delayedCircle.php" type="image/svg+xml">
+      <img src="resources/delayedCircle.php" type="image/svg+xml">
+      <img src="resources/delayedCircle.php" type="image/svg+xml">
+      <img src="resources/delayedCircle.php" type="image/svg+xml">
+      <img src="resources/delayedCircle.php" type="image/svg+xml">
+      <img src="resources/delayedCircle.php" type="image/svg+xml">
+      <img src="resources/delayedCircle.php" type="image/svg+xml">
+    </div>
+
+    <script>
+        document.body.offsetLeft; // Lay out now!
+    </script>
+
+  </body>
+</html>
diff --git a/LayoutTests/media/video-object-fit-change-expected.html b/LayoutTests/media/video-object-fit-change-expected.html
new file mode 100644 (file)
index 0000000..84b9d0e
--- /dev/null
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>changing object-fit values on video elements</title>
+    <style>
+      video {
+        width: 120px;
+        height: 120px;
+        border: 1px solid blue;
+        background-color: gray;
+        margin: 10px;
+      }
+    </style>
+    <script src=media-file.js></script>
+    <script>
+        if (window.testRunner)
+          testRunner.waitUntilDone();
+
+        function init()
+        {
+            setSrcByTagName("video", findMediaFile("video", "content/test"));
+
+            var totalCount = document.getElementsByTagName('video').length;
+            var count = totalCount;
+
+            if (window.testRunner) {
+                document.addEventListener("canplaythrough", function () {
+                    if (!--count)
+                        setTimeout(function() { testRunner.notifyDone(); }, 500);
+                }, true);
+
+                setTimeout(function() {
+                    document.body.appendChild(document.createTextNode('FAILED TOO'));
+                    if (window.testRunner)
+                        testRunner.notifyDone();
+                }, 1500);
+            }
+        }
+    </script>
+
+  </head>
+  <body onload="init();">
+    <video id="video1" style="object-fit:contain;"></video>
+    <video id="video2" style="object-fit:cover;"></video>
+    <video id="video3" style="object-fit:fill;"></video>
+    <video id="video4" style="object-fit:scale-down;"></video>
+  </body>
+</html>
diff --git a/LayoutTests/media/video-object-fit-change.html b/LayoutTests/media/video-object-fit-change.html
new file mode 100644 (file)
index 0000000..db5f2d3
--- /dev/null
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>changing object-fit values on video elements</title>
+    <style>
+      video {
+        width: 120px;
+        height: 120px;
+        border: 1px solid blue;
+        background-color: gray;
+        margin: 10px;
+      }
+    </style>
+    <script src="../media/media-file.js"></script>
+    <script>
+        if (window.testRunner)
+          testRunner.waitUntilDone();
+
+        function init()
+        {
+            setSrcByTagName("video", findMediaFile("video", "../../media/content/test"));
+
+            var totalCount = document.getElementsByTagName('video').length;
+            var count = totalCount;
+            document.addEventListener("canplaythrough", function () {
+                if (!--count)
+                    setTimeout(function() { changeStyle(); }, 500);
+            }, true);
+
+            if (window.testRunner) {
+                setTimeout(function() {
+                    document.body.appendChild(document.createTextNode('FAIL'));
+                    if (window.testRunner)
+                        testRunner.notifyDone();
+                }, 1500);
+            }
+        }
+
+       function changeStyle()
+       {
+           video1.style.objectFit = 'contain';
+           video2.style.objectFit = 'cover';
+           video3.style.objectFit = 'fill';
+           video4.style.objectFit = 'scale-down';
+
+            if (window.testRunner) {
+                setTimeout(function() { testRunner.notifyDone(); }, 500);
+            }
+       }
+    </script>
+
+  </head>
+  <body onload="init();">
+    <video id="video1" style="object-fit:fill;"></video>
+    <video id="video2" style="object-fit:contain;"></video>
+    <video id="video3" style="object-fit:cover;"></video>
+    <video id="video4" style="object-fit:none;"></video>
+  </body>
+</html>
diff --git a/LayoutTests/media/video-object-fit-expected.html b/LayoutTests/media/video-object-fit-expected.html
new file mode 100644 (file)
index 0000000..7b1f561
--- /dev/null
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>object-fit, video</title>
+    <style>
+      .group > div {
+        display: inline-block;
+        overflow: hidden;
+        width: 120px;
+        height: 120px;
+        border: 1px solid blue;
+        background-color: gray;
+        margin: 10px;
+      }
+
+      .group > * > * { display: block; }
+      .group > *:nth-child(1) > * { width:100%; height:100%; }
+      .group > *:nth-child(2) > * { width:100%; margin-top:15px; }
+      .group > *:nth-child(3) > * { height:100%; margin-left:-20px; }
+      .group > *:nth-child(4) > * { margin-left:-100px; margin-top:-60px; }
+      .group > *:nth-child(5) > * { width:100%; margin-top:15px; }
+    </style>
+    <script src=media-file.js></script>
+    <script>
+        if (window.testRunner)
+          testRunner.waitUntilDone();
+
+        function init()
+        {
+            setSrcByTagName("video", findMediaFile("video", "content/test"));
+
+            var totalCount = document.getElementsByTagName('video').length;
+            var count = totalCount;
+            document.addEventListener("canplaythrough", function () {
+                if (!--count) {
+                    if (window.testRunner)
+                        setTimeout(function() { 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();">
+    <div class="group">
+      <!-- Note about the first video here: There's no way to unlock a video from its
+          aspect ratio without using object-fit:fill, so we have to use it here as well
+          as in the test file. In order to actually being able to detect failures, use a
+          different background color. -->
+      <div style="background:salmon;"><video style="object-fit:fill;"></video></div>
+      <div><video></video></div>
+      <div><video></video></div>
+      <div><video></video></div>
+      <div><video></video></div>
+    </div>
+  </body>
+</html>
diff --git a/LayoutTests/media/video-object-fit.html b/LayoutTests/media/video-object-fit.html
new file mode 100644 (file)
index 0000000..a0b385e
--- /dev/null
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>object-fit, video</title>
+    <style>
+      video {
+        width: 120px;
+        height: 120px;
+        border: 1px solid blue;
+        background-color: gray;
+        margin: 10px;
+      }
+    </style>
+    <script src=media-file.js></script>
+    <script>
+        if (window.testRunner)
+          testRunner.waitUntilDone();
+
+        function init()
+        {
+            setSrcByTagName("video", findMediaFile("video", "content/test"));
+
+            var totalCount = document.getElementsByTagName('video').length;
+            var count = totalCount;
+            document.addEventListener("canplaythrough", function () {
+                if (!--count) {
+                    if (window.testRunner)
+                        setTimeout(function() { 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: contain"></video>
+    <video style="object-fit: cover"></video>
+    <video style="object-fit: none"></video>
+    <video style="object-fit: scale-down"></video>
+  </body>
+</html>
index 8dbeee1..481269b 100644 (file)
@@ -52,6 +52,9 @@ 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
 
@@ -379,6 +382,9 @@ webkit.org/b/61487 http/tests/media/video-cross-site.html [ Failure ]
 # Styled scope element is not yet enabled.
 webkit.org/b/49142 fast/css/style-scoped
 
+# fails because of antialiasing differences.
+fast/css/object-fit/object-fit-canvas.html [ Pass ImageOnlyFailure ]
+
 # CSS Regions tests for region styling and scoped styles
 fast/regions/style-scoped-in-flow-override-container-style.html
 fast/regions/style-scoped-in-flow-override-region-styling-multiple-regions.html
index 6b61e52..bf60008 100644 (file)
@@ -1,3 +1,106 @@
+2013-08-26  Simon Fraser  <simon.fraser@apple.com>
+
+        Implement object-fit CSS property
+        https://bugs.webkit.org/show_bug.cgi?id=52040
+
+        Reviewed by Antti Koivisto, Sam Weinig.
+        
+        Merge object-fit patch from Blink r156535, which started as a patch
+        by me.
+
+        Since then, the spec has gone to CR. This patch is an
+        implementation of object-fit as described in
+        http://www.w3.org/TR/2012/CR-css3-images-20120417/#object-fit
+
+        Object-fit is used to maintain the aspect ratio of replaced content
+        within its content box. All object-fit values but the initial one
+        ('fill') will always ensure that the aspect ratio is retained, in
+        different ways (fit inside the content box, cover the content box, or
+        use intrinsic size). Painting is always clipped against the content
+        box, regardless of the 'overflow' property.
+
+        Tests: fast/css/object-fit/object-fit-canvas.html
+               fast/css/object-fit/object-fit-embed.html
+               fast/css/object-fit/object-fit-grow-landscape.html
+               fast/css/object-fit/object-fit-grow-portrait.html
+               fast/css/object-fit/object-fit-img-svg.html
+               fast/css/object-fit/object-fit-img-svg2.html
+               fast/css/object-fit/object-fit-img.html
+               fast/css/object-fit/object-fit-input-image.html
+               fast/css/object-fit/object-fit-object.html
+               fast/css/object-fit/object-fit-shrink.html
+               fast/css/object-fit/object-fit-video-poster.html
+               fast/css/parsing-object-fit.html
+               http/tests/css/object-fit-delayed-img-svg.html
+               media/video-object-fit-change.html
+               media/video-object-fit.html
+
+        * css/CSSComputedStyleDeclaration.cpp:
+        (WebCore::ComputedStyleExtractor::propertyValue):
+        * css/CSSParser.cpp:
+        (WebCore::isValidKeywordPropertyAndValue):
+        (WebCore::isKeywordPropertyID):
+        (WebCore::CSSParser::parseValue):
+        * css/CSSPrimitiveValueMappings.h:
+        (WebCore::CSSPrimitiveValue::CSSPrimitiveValue):
+        (WebCore::CSSPrimitiveValue::operator EObjectFit):
+        * css/CSSProperty.cpp:
+        (WebCore::CSSProperty::isInheritedProperty):
+        * css/CSSPropertyNames.in:
+        * css/CSSValueKeywords.in:
+        * css/DeprecatedStyleBuilder.cpp:
+        (WebCore::DeprecatedStyleBuilder::DeprecatedStyleBuilder):
+        * css/StyleResolver.cpp:
+        (WebCore::StyleResolver::applyProperty):
+        * css/html.css:
+        (video): Set object-fit to 'contain'. This is how VIDEO elements
+        work, apparently.
+        * loader/cache/CachedImage.cpp:
+        (WebCore::CachedImage::imageSizeForRenderer):
+        * loader/cache/CachedImage.h:
+        * platform/graphics/LayoutSize.h:
+        (WebCore::fitLayoutSizeToAspectRatio): New function to grow or shrink
+        in one dimension to fit to the aspect ratio.
+        * rendering/RenderHTMLCanvas.cpp:
+        (WebCore::RenderHTMLCanvas::paintReplaced): Apply object-fit and
+        clip if necessary.
+        * rendering/RenderImage.cpp:
+        (WebCore::RenderImage::updateInnerContentRect):
+        (WebCore::RenderImage::imageDimensionsChanged): Update intrinsic
+        size properly, and recalculate the inner content rectangle (the
+        exact area occupied by the replaced content) again if appropriate.
+        (WebCore::RenderImage::paintReplaced): Apply object-fit and clip
+        if necessary.
+        (WebCore::RenderImage::foregroundIsKnownToBeOpaqueInRect):
+        object-fit may leave parts of the content box empty, in which case
+        it won't be fully obscured.
+        (WebCore::RenderImage::layout):
+        * rendering/RenderImage.h:
+        * rendering/RenderImageResource.cpp:
+        (WebCore::RenderImageResource::intrinsicSize): Need this to
+        differentiate between intrinsic and extrinsic size for SVG images.
+        * rendering/RenderImageResource.h:
+        * rendering/RenderImageResourceStyleImage.h:
+        * rendering/RenderReplaced.cpp:
+        (WebCore::RenderReplaced::replacedContentRect): Return the
+        rectangle occupied by the replaced content. This will be identical
+        to the content box if object-fit is 'fill', but will typically be
+        something else for other values.
+        * rendering/RenderReplaced.h:
+        * rendering/RenderVideo.cpp:
+        (WebCore::RenderVideo::videoBox): Not much left to do here, with
+        the new RenderReplaced::replacedContentRect() method in place.
+        (WebCore::RenderVideo::paintReplaced): Apply object-fit and clip
+        if necessary.
+        * rendering/style/RenderStyle.cpp:
+        (WebCore::RenderStyle::changeRequiresRepaint):
+        * rendering/style/RenderStyle.h:
+        * rendering/style/RenderStyleConstants.h:
+        * rendering/style/StyleRareNonInheritedData.cpp:
+        (WebCore::StyleRareNonInheritedData::StyleRareNonInheritedData):
+        (WebCore::StyleRareNonInheritedData::operator==):
+        * rendering/style/StyleRareNonInheritedData.h:
+
 2013-08-29  Tim Horton  <timothy_horton@apple.com>
 
         SVG clipping, masking, and gradients-on-text do not respect the device scale factor
index cd97bd3..783581b 100644 (file)
@@ -2241,6 +2241,8 @@ PassRefPtr<CSSValue> ComputedStyleExtractor::propertyValue(CSSPropertyID propert
             if (style->minWidth().isAuto())
                 return zoomAdjustedPixelValue(0, style.get());
             return zoomAdjustedPixelValueForLength(style->minWidth(), style.get());
+        case CSSPropertyObjectFit:
+            return cssValuePool().createValue(style->objectFit());
         case CSSPropertyOpacity:
             return cssValuePool().createValue(style->opacity(), CSSPrimitiveValue::CSS_NUMBER);
         case CSSPropertyOrphans:
index 7eba00f..878f8ad 100644 (file)
@@ -738,6 +738,10 @@ static inline bool isValidKeywordPropertyAndValue(CSSPropertyID propertyId, int
         if ((valueID >= CSSValueDisc && valueID <= CSSValueKatakanaIroha) || valueID == CSSValueNone)
             return true;
         break;
+    case CSSPropertyObjectFit:
+        if (valueID == CSSValueFill || valueID == CSSValueContain || valueID == CSSValueCover || valueID == CSSValueNone || valueID == CSSValueScaleDown)
+            return true;
+        break;
     case CSSPropertyOutlineStyle: // (<border-style> except hidden) | auto | inherit
         if (valueID == CSSValueAuto || valueID == CSSValueNone || (valueID >= CSSValueInset && valueID <= CSSValueDouble))
             return true;
@@ -1075,6 +1079,7 @@ static inline bool isKeywordPropertyID(CSSPropertyID propertyId)
     case CSSPropertyImageRendering:
     case CSSPropertyListStylePosition:
     case CSSPropertyListStyleType:
+    case CSSPropertyObjectFit:
     case CSSPropertyOutlineStyle:
     case CSSPropertyOverflowWrap:
     case CSSPropertyOverflowX:
@@ -3031,6 +3036,7 @@ bool CSSParser::parseValue(CSSPropertyID propId, bool important)
     case CSSPropertyImageRendering:
     case CSSPropertyListStylePosition:
     case CSSPropertyListStyleType:
+    case CSSPropertyObjectFit:
     case CSSPropertyOutlineStyle:
     case CSSPropertyOverflowWrap:
     case CSSPropertyOverflowX:
index 13ea188..79adfa5 100644 (file)
@@ -3569,6 +3569,50 @@ template<> inline CSSPrimitiveValue::operator FontDescription::Kerning() const
     return FontDescription::AutoKerning;
 }
 
+template<> inline CSSPrimitiveValue::CSSPrimitiveValue(ObjectFit fit)
+    : CSSValue(PrimitiveClass)
+{
+    m_primitiveUnitType = CSS_VALUE_ID;
+    switch (fit) {
+    case ObjectFitFill:
+        m_value.valueID = CSSValueFill;
+        break;
+    case ObjectFitContain:
+        m_value.valueID = CSSValueContain;
+        break;
+    case ObjectFitCover:
+        m_value.valueID = CSSValueCover;
+        break;
+    case ObjectFitNone:
+        m_value.valueID = CSSValueNone;
+        break;
+    case ObjectFitScaleDown:
+        m_value.valueID = CSSValueScaleDown;
+        break;
+    }
+}
+
+template<> inline CSSPrimitiveValue::operator ObjectFit() const
+{
+    ASSERT(isValueID());
+
+    switch (m_value.valueID) {
+    case CSSValueFill:
+        return ObjectFitFill;
+    case CSSValueContain:
+        return ObjectFitContain;
+    case CSSValueCover:
+        return ObjectFitCover;
+    case CSSValueNone:
+        return ObjectFitNone;
+    case CSSValueScaleDown:
+        return ObjectFitScaleDown;
+    default:
+        ASSERT_NOT_REACHED();
+        return ObjectFitFill;
+    }
+}
+
 template<> inline CSSPrimitiveValue::CSSPrimitiveValue(FontSmoothingMode smoothing)
     : CSSValue(PrimitiveClass)
 {
index aa62b8d..a6e0a58 100644 (file)
@@ -462,6 +462,7 @@ bool CSSProperty::isInheritedProperty(CSSPropertyID propertyID)
     case CSSPropertyMaxWidth:
     case CSSPropertyMinHeight:
     case CSSPropertyMinWidth:
+    case CSSPropertyObjectFit:
     case CSSPropertyOpacity:
     case CSSPropertyOutline:
     case CSSPropertyOutlineColor:
index 5c94db6..7d87802 100644 (file)
@@ -136,6 +136,7 @@ min-width
 mix
 parameters
 #endif
+object-fit
 opacity
 // Honor -webkit-opacity as a synonym for opacity. This was the only syntax that worked in Safari 1.1,
 // and may be in use on some websites and widgets.
index 423ac5e..0b4c92a 100644 (file)
@@ -1009,6 +1009,13 @@ saturation
 color
 luminosity
 
+// object-fit
+// fill
+// contain
+// cover
+// none
+scale-down
+
 #if defined(ENABLE_CSS_IMAGE_RESOLUTION) && ENABLE_CSS_IMAGE_RESOLUTION
 from-image
 snap
index 07314e1..049a56d 100644 (file)
@@ -2166,6 +2166,7 @@ DeprecatedStyleBuilder::DeprecatedStyleBuilder()
     setPropertyHandler(CSSPropertyMaxWidth, ApplyPropertyLength<&RenderStyle::maxWidth, &RenderStyle::setMaxWidth, &RenderStyle::initialMaxSize, AutoEnabled, LegacyIntrinsicEnabled, IntrinsicEnabled, NoneEnabled, UndefinedEnabled>::createHandler());
     setPropertyHandler(CSSPropertyMinHeight, ApplyPropertyLength<&RenderStyle::minHeight, &RenderStyle::setMinHeight, &RenderStyle::initialMinSize, AutoEnabled, LegacyIntrinsicEnabled, IntrinsicDisabled>::createHandler());
     setPropertyHandler(CSSPropertyMinWidth, ApplyPropertyLength<&RenderStyle::minWidth, &RenderStyle::setMinWidth, &RenderStyle::initialMinSize, AutoEnabled, LegacyIntrinsicEnabled, IntrinsicEnabled>::createHandler());
+    setPropertyHandler(CSSPropertyObjectFit, ApplyPropertyDefault<ObjectFit, &RenderStyle::objectFit, ObjectFit, &RenderStyle::setObjectFit, ObjectFit, &RenderStyle::initialObjectFit>::createHandler());
     setPropertyHandler(CSSPropertyOpacity, ApplyPropertyDefault<float, &RenderStyle::opacity, float, &RenderStyle::setOpacity, float, &RenderStyle::initialOpacity>::createHandler());
     setPropertyHandler(CSSPropertyOrphans, ApplyPropertyAuto<short, &RenderStyle::orphans, &RenderStyle::setOrphans, &RenderStyle::hasAutoOrphans, &RenderStyle::setHasAutoOrphans>::createHandler());
     setPropertyHandler(CSSPropertyOutlineColor, ApplyPropertyColor<NoInheritFromParent, &RenderStyle::outlineColor, &RenderStyle::setOutlineColor, &RenderStyle::setVisitedLinkOutlineColor, &RenderStyle::color>::createHandler());
index 441d859..ca79fbe 100644 (file)
@@ -2812,6 +2812,7 @@ void StyleResolver::applyProperty(CSSPropertyID id, CSSValue* value)
     case CSSPropertyMaxWidth:
     case CSSPropertyMinHeight:
     case CSSPropertyMinWidth:
+    case CSSPropertyObjectFit:
     case CSSPropertyOpacity:
     case CSSPropertyOrphans:
     case CSSPropertyOutlineColor:
index a0e08e5..7bc55b2 100644 (file)
@@ -146,6 +146,10 @@ map {
     display: inline
 }
 
+video {
+    object-fit: contain;
+}
+
 /* heading elements */
 
 h1 {
index 69e4262..f69af20 100644 (file)
@@ -245,7 +245,7 @@ bool CachedImage::imageHasRelativeHeight() const
     return false;
 }
 
-LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier)
+LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float multiplier, SizeType sizeType)
 {
     ASSERT(!isPurgeable());
 
@@ -257,7 +257,7 @@ LayoutSize CachedImage::imageSizeForRenderer(const RenderObject* renderer, float
     if (m_image->isBitmapImage() && (renderer && renderer->shouldRespectImageOrientation() == RespectImageOrientation))
         imageSize = static_cast<BitmapImage*>(m_image.get())->sizeRespectingOrientation();
 #if ENABLE(SVG)
-    else if (m_image->isSVGImage()) {
+    else if (m_image->isSVGImage() && sizeType == UsedSize) {
         imageSize = m_svgImageCache->imageSizeForRenderer(renderer);
     }
 #endif
index 7edd5b5..431b740 100644 (file)
@@ -67,8 +67,12 @@ public:
     virtual void addDataBuffer(ResourceBuffer*) OVERRIDE;
     virtual void finishLoading(ResourceBuffer*) OVERRIDE;
 
+    enum SizeType {
+        UsedSize,
+        IntrinsicSize
+    };
     // This method takes a zoom multiplier that can be used to increase the natural size of the image by the zoom.
-    LayoutSize imageSizeForRenderer(const RenderObject*, float multiplier); // returns the size of the complete image.
+    LayoutSize imageSizeForRenderer(const RenderObject*, float multiplier, SizeType = UsedSize); // returns the size of the complete image.
     void computeIntrinsicDimensions(Length& intrinsicWidth, Length& intrinsicHeight, FloatSize& intrinsicRatio);
 
     static void resumeAnimatingImagesForLoader(CachedResourceLoader*);
index 588fb55..7e400f5 100644 (file)
@@ -360,7 +360,10 @@ public:
     // Set that the position/size of the contents (image or video).
     IntRect contentsRect() const { return m_contentsRect; }
     virtual void setContentsRect(const IntRect& r) { m_contentsRect = r; }
-    
+
+    IntRect contentsClippingRect() const { return m_contentsClippingRect; }
+    virtual void setContentsClippingRect(const IntRect& r) { m_contentsClippingRect = r; }
+
     // Transitions are identified by a special animation name that cannot clash with a keyframe identifier.
     static String animationNameForTransition(AnimatedPropertyID);
     
@@ -568,6 +571,7 @@ protected:
     FloatPoint m_replicatedLayerPosition; // For a replica layer, the position of the replica.
 
     IntRect m_contentsRect;
+    IntRect m_contentsClippingRect;
     IntPoint m_contentsTilePhase;
     IntSize m_contentsTileSize;
 
index 42566aa..1a5b989 100644 (file)
@@ -113,6 +113,12 @@ void LayoutRect::scale(float s)
     m_size.scale(s);
 }
 
+void LayoutRect::scale(float xScale, float yScale)
+{
+    m_location.scale(xScale, yScale);
+    m_size.scale(xScale, yScale);
+}
+
 LayoutRect unionRect(const Vector<LayoutRect>& rects)
 {
     LayoutRect result;
index 38fb1f0..d2f8cd1 100644 (file)
@@ -160,6 +160,7 @@ public:
     }
     void inflate(LayoutUnit d) { inflateX(d); inflateY(d); }
     void scale(float s);
+    void scale(float xScale, float yScale);
 
     LayoutRect transposedRect() const { return LayoutRect(m_location.transposedPoint(), m_size.transposedSize()); }
 
index 2e45f9d..f2d09ee 100644 (file)
@@ -39,6 +39,11 @@ namespace WebCore {
 
 class LayoutPoint;
 
+enum AspectRatioFit {
+    AspectRatioFitShrink,
+    AspectRatioFitGrow
+};
+
 class LayoutSize {
 public:
     LayoutSize() : m_width(0), m_height(0) { }
@@ -114,6 +119,17 @@ public:
 
     operator FloatSize() const { return FloatSize(m_width, m_height); }
 
+    LayoutSize fitToAspectRatio(const LayoutSize& aspectRatio, AspectRatioFit fit) const
+    {
+        float heightScale = height().toFloat() / aspectRatio.height().toFloat();
+        float widthScale = width().toFloat() / aspectRatio.width().toFloat();
+
+        if ((widthScale > heightScale) != (fit == AspectRatioFitGrow))
+            return LayoutSize(height() * aspectRatio.width() / aspectRatio.height(), height());
+
+        return LayoutSize(width(), width() * aspectRatio.height() / aspectRatio.width());
+    }
+
 private:
     LayoutUnit m_width, m_height;
 };
index 7c75f9e..698daf7 100644 (file)
@@ -59,16 +59,26 @@ bool RenderHTMLCanvas::requiresLayer() const
 
 void RenderHTMLCanvas::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
 {
-    LayoutRect rect = contentBoxRect();
-    rect.moveBy(paintOffset);
+    GraphicsContext* context = paintInfo.context;
+
+    LayoutRect contentRect = contentBoxRect();
+    contentRect.moveBy(paintOffset);
+    LayoutRect paintRect = replacedContentRect(intrinsicSize());
+    paintRect.moveBy(paintOffset);
+
+    // Not allowed to overflow the content box.
+    bool clip = !contentRect.contains(paintRect);
+    GraphicsContextStateSaver stateSaver(*paintInfo.context, clip);
+    if (clip)
+        paintInfo.context->clip(pixelSnappedIntRect(contentRect));
 
     if (Page* page = frame().page()) {
         if (paintInfo.phase == PaintPhaseForeground)
-            page->addRelevantRepaintedObject(this, rect);
+            page->addRelevantRepaintedObject(this, intersection(paintRect, contentRect));
     }
 
     bool useLowQualityScale = style()->imageRendering() == ImageRenderingCrispEdges || style()->imageRendering() == ImageRenderingOptimizeSpeed;
-    static_cast<HTMLCanvasElement*>(node())->paint(paintInfo.context, rect, useLowQualityScale);
+    static_cast<HTMLCanvasElement*>(node())->paint(context, paintRect, useLowQualityScale);
 }
 
 void RenderHTMLCanvas::canvasSizeChanged()
index accddc6..e0ee0f5 100644 (file)
@@ -206,6 +206,15 @@ bool RenderImage::updateIntrinsicSizeIfNeeded(const LayoutSize& newSize, bool im
     return true;
 }
 
+void RenderImage::updateInnerContentRect()
+{
+    // Propagate container size to image resource.
+    LayoutRect paintRect = replacedContentRect(intrinsicSize());
+    IntSize containerSize(paintRect.width(), paintRect.height());
+    if (!containerSize.isEmpty())
+        m_imageResource->setContainerSizeForRenderer(containerSize);
+}
+
 void RenderImage::imageDimensionsChanged(bool imageSizeChanged, const IntRect* rect)
 {
 #if ENABLE(CSS_IMAGE_RESOLUTION)
@@ -214,9 +223,9 @@ void RenderImage::imageDimensionsChanged(bool imageSizeChanged, const IntRect* r
         scale = roundForImpreciseConversion<int>(scale);
     if (scale <= 0)
         scale = 1;
-    bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(m_imageResource->imageSize(style()->effectiveZoom() / scale), imageSizeChanged);
+    bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(m_imageResource->intrinsicSize(style()->effectiveZoom() / scale), imageSizeChanged);
 #else
-    bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(m_imageResource->imageSize(style()->effectiveZoom()), imageSizeChanged);
+    bool intrinsicSizeChanged = updateIntrinsicSizeIfNeeded(m_imageResource->intrinsicSize(style()->effectiveZoom()), imageSizeChanged);
 #endif
 
     // In the case of generated image content using :before/:after/content, we might not be
@@ -255,6 +264,17 @@ void RenderImage::imageDimensionsChanged(bool imageSizeChanged, const IntRect* r
             if (!selfNeedsLayout())
                 setNeedsLayout(true);
         }
+
+        if (everHadLayout() && !selfNeedsLayout()) {
+            // The inner content rectangle is calculated during layout, but may need an update now
+            // (unless the box has already been scheduled for layout). In order to calculate it, we
+            // may need values from the containing block, though, so make sure that we're not too
+            // early. It may be that layout hasn't even taken place once yet.
+
+            // FIXME: we should not have to trigger another call to setContainerSizeForRenderer()
+            // from here, since it's already being done during layout.
+            updateInnerContentRect();
+        }
     }
 
     if (shouldRepaint) {
@@ -394,18 +414,25 @@ void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOf
             paintCustomHighlight(toPoint(paintOffset - location()), style()->highlight(), true);
 #endif
 
-        LayoutSize contentSize(cWidth, cHeight);
-        LayoutPoint contentLocation = paintOffset;
-        contentLocation.move(leftBorder + leftPad, topBorder + topPad);
-        paintIntoRect(context, LayoutRect(contentLocation, contentSize));
+        LayoutRect contentRect = contentBoxRect();
+        contentRect.moveBy(paintOffset);
+        LayoutRect paintRect = replacedContentRect(intrinsicSize());
+        paintRect.moveBy(paintOffset);
+        bool clip = !contentRect.contains(paintRect);
+        GraphicsContextStateSaver stateSaver(*context, clip);
+        if (clip)
+            context->clip(contentRect);
+
+        paintIntoRect(context, paintRect);
         
         if (cachedImage() && page && paintInfo.phase == PaintPhaseForeground) {
             // For now, count images as unpainted if they are still progressively loading. We may want 
             // to refine this in the future to account for the portion of the image that has painted.
+            LayoutRect visibleRect = intersection(paintRect, contentRect);
             if (cachedImage()->isLoading())
-                page->addRelevantUnpaintedObject(this, LayoutRect(contentLocation, contentSize));
+                page->addRelevantUnpaintedObject(this, visibleRect);
             else
-                page->addRelevantRepaintedObject(this, LayoutRect(contentLocation, contentSize));
+                page->addRelevantRepaintedObject(this, visibleRect);
         }
     }
 }
@@ -509,6 +536,10 @@ bool RenderImage::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect,
     // Background shows in padding area.
     if ((backgroundClip == BorderFillBox || backgroundClip == PaddingFillBox) && style()->hasPadding())
         return false;
+    // Object-fit may leave parts of the content box empty.
+    ObjectFit objectFit = style()->objectFit();
+    if (objectFit != ObjectFitFill && objectFit != ObjectFitCover)
+        return false;
     // Check for image with alpha.
     return m_imageResource->cachedImage() && m_imageResource->cachedImage()->currentFrameKnownToBeOpaque(this);
 }
@@ -574,11 +605,7 @@ void RenderImage::layout()
 {
     StackStats::LayoutCheckPoint layoutCheckPoint;
     RenderReplaced::layout();
-
-    // Propagate container size to image resource.
-    IntSize containerSize(contentWidth(), contentHeight());
-    if (!containerSize.isEmpty())
-        m_imageResource->setContainerSizeForRenderer(containerSize);
+    updateInnerContentRect();
 }
 
 void RenderImage::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const
index b77e42d..578be1d 100644 (file)
@@ -101,6 +101,8 @@ private:
     IntSize imageSizeForError(CachedImage*) const;
     void imageDimensionsChanged(bool imageSizeChanged, const IntRect* = 0);
     bool updateIntrinsicSizeIfNeeded(const LayoutSize&, bool imageSizeChanged);
+    // Update the size of the image to be rendered. Object-fit may cause this to be different from the CSS box's content rect.
+    void updateInnerContentRect();
 
     void paintAreaElementFocusRing(PaintInfo&);
 
index a0b46d3..934225e 100644 (file)
@@ -131,4 +131,9 @@ LayoutSize RenderImageResource::imageSize(float multiplier) const
     return m_cachedImage ? m_cachedImage->imageSizeForRenderer(m_renderer, multiplier) : LayoutSize();
 }
 
+LayoutSize RenderImageResource::intrinsicSize(float multiplier) const
+{
+    return m_cachedImage ? m_cachedImage->imageSizeForRenderer(m_renderer, multiplier, CachedImage::IntrinsicSize) : LayoutSize();
+}
+
 } // namespace WebCore
index 28dbd31..2f7fb9a 100644 (file)
@@ -63,6 +63,7 @@ public:
     virtual bool imageHasRelativeHeight() const;
 
     virtual LayoutSize imageSize(float multiplier) const;
+    virtual LayoutSize intrinsicSize(float multiplier) const;
 
     virtual WrappedImagePtr imagePtr() const { return m_cachedImage.get(); }
 
index 6263be1..008ccfa 100644 (file)
@@ -55,6 +55,7 @@ public:
     virtual bool imageHasRelativeHeight() const { return m_styleImage->imageHasRelativeHeight(); }
 
     virtual LayoutSize imageSize(float multiplier) const OVERRIDE { return m_styleImage->imageSize(m_renderer, multiplier); }
+    virtual LayoutSize intrinsicSize(float multiplier) const OVERRIDE { return m_styleImage->imageSize(m_renderer, multiplier); }
 
     virtual WrappedImagePtr imagePtr() const { return m_styleImage->data(); }
 
index f30436a..0261427 100644 (file)
@@ -356,6 +356,7 @@ public:
     virtual bool isRenderInline() const { return false; }
     virtual bool isRenderPart() const { return false; }
     virtual bool isRenderRegion() const { return false; }
+    virtual bool isRenderReplaced() const { return false; }
     virtual bool isRenderView() const { return false; }
     virtual bool isReplica() const { return false; }
 
index 23930d9..dfc2cbb 100644 (file)
@@ -298,6 +298,41 @@ void RenderReplaced::computeAspectRatioInformationForRenderBox(RenderBox* conten
     }
 }
 
+LayoutRect RenderReplaced::replacedContentRect(const LayoutSize& intrinsicSize) const
+{
+    LayoutRect contentRect = contentBoxRect();
+    ObjectFit objectFit = style()->objectFit();
+    if (objectFit == ObjectFitFill)
+        return contentRect;
+
+    if (!intrinsicSize.width() || !intrinsicSize.height())
+        return contentRect;
+
+    LayoutRect finalRect = contentRect;
+    switch (objectFit) {
+    case ObjectFitContain:
+    case ObjectFitScaleDown:
+    case ObjectFitCover:
+        finalRect.setSize(finalRect.size().fitToAspectRatio(intrinsicSize, objectFit == ObjectFitCover ? AspectRatioFitGrow : AspectRatioFitShrink));
+        if (objectFit != ObjectFitScaleDown || finalRect.width() <= intrinsicSize.width())
+            break;
+        // fall through
+    case ObjectFitNone:
+        finalRect.setSize(intrinsicSize);
+        break;
+    case ObjectFitFill:
+        ASSERT_NOT_REACHED();
+    }
+
+    // FIXME: This is where object-position should be taken into account, but since it's not
+    // implemented yet, assume the initial value of "50% 50%".
+    LayoutUnit xOffset = (contentRect.width() - finalRect.width()) / 2;
+    LayoutUnit yOffset = (contentRect.height() - finalRect.height()) / 2;
+    finalRect.move(xOffset, yOffset);
+
+    return finalRect;
+}
+
 void RenderReplaced::computeIntrinsicRatioInformation(FloatSize& intrinsicSize, double& intrinsicRatio, bool& isPercentageIntrinsicSize) const
 {
     // If there's an embeddedContentBox() of a remote, referenced document available, this code-path should never be used.
index 456c76b..4f01dff 100644 (file)
@@ -35,10 +35,14 @@ public:
     virtual LayoutUnit computeReplacedLogicalWidth(ShouldComputePreferred  = ComputeActual) const OVERRIDE;
     virtual LayoutUnit computeReplacedLogicalHeight() const;
 
+    LayoutRect replacedContentRect(const LayoutSize& intrinsicSize) const;
+
     bool hasReplacedLogicalWidth() const;
     bool hasReplacedLogicalHeight() const;
 
 protected:
+    virtual bool isRenderReplaced() const OVERRIDE FINAL { return true; }
+
     virtual void willBeDestroyed();
 
     virtual void layout();
@@ -85,6 +89,21 @@ private:
     mutable LayoutSize m_intrinsicSize;
 };
 
+inline RenderReplaced* toRenderReplaced(RenderObject* object)
+{
+    ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderReplaced());
+    return static_cast<RenderReplaced*>(object);
+}
+
+inline const RenderReplaced* toRenderReplaced(const RenderObject* object)
+{
+    ASSERT_WITH_SECURITY_IMPLICATION(!object || object->isRenderReplaced());
+    return static_cast<const RenderReplaced*>(object);
+}
+
+// This will catch anyone doing an unnecessary cast.
+void toRenderReplaced(const RenderReplaced*);
+
 }
 
 #endif
index 9a652eb..118a222 100644 (file)
@@ -151,35 +151,12 @@ void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
 
 IntRect RenderVideo::videoBox() const
 {
-    if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage())
-        return IntRect();
+    LayoutSize intrinsicSize = this->intrinsicSize();
 
-    LayoutSize elementSize;
     if (videoElement()->shouldDisplayPosterImage())
-        elementSize = m_cachedImageSize;
-    else
-        elementSize = intrinsicSize();
-
-    IntRect contentRect = pixelSnappedIntRect(contentBoxRect());
-    if (elementSize.isEmpty() || contentRect.isEmpty())
-        return IntRect();
-
-    LayoutRect renderBox = contentRect;
-    LayoutUnit ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width();
-    if (ratio > 0) {
-        LayoutUnit newWidth = renderBox.height() * elementSize.width() / elementSize.height();
-        // Just fill the whole area if the difference is one pixel or less (in both sides)
-        if (renderBox.width() - newWidth > 2)
-            renderBox.setWidth(newWidth);
-        renderBox.move((contentRect.width() - renderBox.width()) / 2, 0);
-    } else if (ratio < 0) {
-        LayoutUnit newHeight = renderBox.width() * elementSize.height() / elementSize.width();
-        if (renderBox.height() - newHeight > 2)
-            renderBox.setHeight(newHeight);
-        renderBox.move(0, (contentRect.height() - renderBox.height()) / 2);
-    }
+        intrinsicSize = m_cachedImageSize;
 
-    return pixelSnappedIntRect(renderBox);
+    return pixelSnappedIntRect(replacedContentRect(intrinsicSize));
 }
 
 bool RenderVideo::shouldDisplayVideo() const
@@ -211,12 +188,20 @@ void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOf
     if (page && paintInfo.phase == PaintPhaseForeground)
         page->addRelevantRepaintedObject(this, rect);
 
+    LayoutRect contentRect = contentBoxRect();
+    contentRect.moveBy(paintOffset);
+    GraphicsContext* context = paintInfo.context;
+    bool clip = !contentRect.contains(rect);
+    GraphicsContextStateSaver stateSaver(*context, clip);
+    if (clip)
+        context->clip(contentRect);
+
     if (displayingPoster)
-        paintIntoRect(paintInfo.context, rect);
+        paintIntoRect(context, rect);
     else if (view().frameView().paintBehavior() & PaintBehaviorFlattenCompositingLayers)
-        mediaPlayer->paintCurrentFrameInContext(paintInfo.context, pixelSnappedIntRect(rect));
+        mediaPlayer->paintCurrentFrameInContext(context, pixelSnappedIntRect(rect));
     else
-        mediaPlayer->paint(paintInfo.context, pixelSnappedIntRect(rect));
+        mediaPlayer->paint(context, pixelSnappedIntRect(rect));
 }
 
 void RenderVideo::layout()
@@ -239,6 +224,9 @@ void RenderVideo::updateFromElement()
 
 void RenderVideo::updatePlayer()
 {
+    if (documentBeingDestroyed())
+        return;
+
     updateIntrinsicSize();
 
     MediaPlayer* mediaPlayer = mediaElement()->player();
index 6dcd815..0d78a50 100644 (file)
@@ -701,6 +701,7 @@ bool RenderStyle::changeRequiresRepaint(const RenderStyle* other, unsigned&) con
         || rareInheritedData->userSelect != other->rareInheritedData->userSelect
         || rareNonInheritedData->userDrag != other->rareNonInheritedData->userDrag
         || rareNonInheritedData->m_borderFit != other->rareNonInheritedData->m_borderFit
+        || rareNonInheritedData->m_objectFit != other->rareNonInheritedData->m_objectFit
         || rareInheritedData->m_imageRendering != other->rareInheritedData->m_imageRendering)
         return true;
 
index 330686a..b26f1fe 100644 (file)
@@ -863,6 +863,8 @@ public:
     RubyPosition rubyPosition() const { return static_cast<RubyPosition>(rareInheritedData->m_rubyPosition); }
 
     TextOrientation textOrientation() const { return static_cast<TextOrientation>(rareInheritedData->m_textOrientation); }
+
+    ObjectFit objectFit() const { return static_cast<ObjectFit>(rareNonInheritedData->m_objectFit); }
     
     // Return true if any transform related property (currently transform, transformStyle3D or perspective) 
     // indicates that we are transforming
@@ -1370,6 +1372,8 @@ public:
     void setTextEmphasisPosition(TextEmphasisPosition position) { SET_VAR(rareInheritedData, textEmphasisPosition, position); }
     bool setTextOrientation(TextOrientation);
 
+    void setObjectFit(ObjectFit fit) { SET_VAR(rareNonInheritedData, m_objectFit, fit); }
+
     void setRubyPosition(RubyPosition position) { SET_VAR(rareInheritedData, m_rubyPosition, position); }
 
 #if ENABLE(CSS_FILTERS)
@@ -1591,6 +1595,7 @@ public:
     static WritingMode initialWritingMode() { return TopToBottomWritingMode; }
     static TextCombine initialTextCombine() { return TextCombineNone; }
     static TextOrientation initialTextOrientation() { return TextOrientationVerticalRight; }
+    static ObjectFit initialObjectFit() { return ObjectFitFill; }
     static EDisplay initialDisplay() { return INLINE; }
     static EEmptyCell initialEmptyCells() { return SHOW; }
     static EFloat initialFloating() { return NoFloat; }
index a5a79d6..e0f3972 100644 (file)
@@ -209,6 +209,11 @@ enum EUserSelect {
     SELECT_NONE, SELECT_TEXT, SELECT_ALL
 };
 
+// CSS3 Image Values
+enum ObjectFit {
+    ObjectFitFill, ObjectFitContain, ObjectFitCover, ObjectFitNone, ObjectFitScaleDown
+};
+
 // Word Break Values. Matches WinIE, rather than CSS3
 
 enum EWordBreak {
index a1855e0..55d4430 100644 (file)
@@ -87,6 +87,7 @@ StyleRareNonInheritedData::StyleRareNonInheritedData()
 #if ENABLE(CSS_COMPOSITING)
     , m_effectiveBlendMode(RenderStyle::initialBlendMode())
 #endif
+    , m_objectFit(RenderStyle::initialObjectFit())
 {
     m_maskBoxImage.setMaskDefaults();
 }
@@ -172,6 +173,7 @@ StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonInherited
 #if ENABLE(CSS_COMPOSITING)
     , m_effectiveBlendMode(o.m_effectiveBlendMode)
 #endif
+    , m_objectFit(o.m_objectFit)
 {
 }
 
@@ -262,7 +264,8 @@ bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) c
 #if ENABLE(CSS_COMPOSITING)
         && m_effectiveBlendMode == o.m_effectiveBlendMode
 #endif
-        && m_hasAspectRatio == o.m_hasAspectRatio;
+        && m_hasAspectRatio == o.m_hasAspectRatio
+        && m_objectFit == o.m_objectFit;
 }
 
 bool StyleRareNonInheritedData::contentDataEquivalent(const StyleRareNonInheritedData& o) const
index 2a00fa3..d0fb016 100644 (file)
@@ -201,6 +201,8 @@ public:
     unsigned m_effectiveBlendMode: 5; // EBlendMode
 #endif
 
+    unsigned m_objectFit : 3; // ObjectFit
+
 private:
     StyleRareNonInheritedData();
     StyleRareNonInheritedData(const StyleRareNonInheritedData&);