Diff
Modified: trunk/LayoutTests/ChangeLog (286742 => 286743)
--- trunk/LayoutTests/ChangeLog 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/LayoutTests/ChangeLog 2021-12-08 22:59:49 UTC (rev 286743)
@@ -1,3 +1,20 @@
+2021-12-08 Jer Noble <jer.no...@apple.com>
+
+ [VTT] Fix various issues with complicated rendering of VTT cues
+ https://bugs.webkit.org/show_bug.cgi?id=233901
+
+ Reviewed by Eric Carlson.
+
+ * media/track/captions-webvtt/no-snap-to-lines-overlap.vtt: Added.
+ * media/track/captions-webvtt/snap-to-lines-inline-style.vtt: Added.
+ * media/track/captions-webvtt/snap-to-lines-left-and-right.vtt: Added.
+ * media/track/track-webvtt-no-snap-to-lines-overlap-expected.html: Added.
+ * media/track/track-webvtt-no-snap-to-lines-overlap.html: Added.
+ * media/track/track-webvtt-snap-to-lines-inline-style-expected.html: Added.
+ * media/track/track-webvtt-snap-to-lines-inline-style.html: Added.
+ * media/track/track-webvtt-snap-to-lines-left-right-expected.html: Added.
+ * media/track/track-webvtt-snap-to-lines-left-right.html: Added.
+
2021-12-08 Rob Buis <rb...@igalia.com>
[css-contain] Prevent various kinds of propagation to RenderView
Added: trunk/LayoutTests/media/track/captions-webvtt/no-snap-to-lines-overlap.vtt (0 => 286743)
--- trunk/LayoutTests/media/track/captions-webvtt/no-snap-to-lines-overlap.vtt (rev 0)
+++ trunk/LayoutTests/media/track/captions-webvtt/no-snap-to-lines-overlap.vtt 2021-12-08 22:59:49 UTC (rev 286743)
@@ -0,0 +1,33 @@
+WEBVTT
+
+1
+00:00:00.000 --> 00:00:00.500 align:left line:0%
+Left
+
+2
+00:00:00.000 --> 00:00:00.500 align:right line:0%
+Right
+
+3
+00:00:00.000 --> 00:00:00.500 align:left line:0%
+Below
+
+4
+00:00:00.000 --> 00:00:00.500 align:right line:0%
+Below
+
+1
+00:00:00.000 --> 00:00:00.500 align:left line:100%
+Left
+
+2
+00:00:00.000 --> 00:00:00.500 align:right line:100%
+Right
+
+3
+00:00:00.000 --> 00:00:00.500 align:left line:100%
+Above
+
+4
+00:00:00.000 --> 00:00:00.500 align:right line:100%
+Above
Added: trunk/LayoutTests/media/track/captions-webvtt/snap-to-lines-inline-style.vtt (0 => 286743)
--- trunk/LayoutTests/media/track/captions-webvtt/snap-to-lines-inline-style.vtt (rev 0)
+++ trunk/LayoutTests/media/track/captions-webvtt/snap-to-lines-inline-style.vtt 2021-12-08 22:59:49 UTC (rev 286743)
@@ -0,0 +1,20 @@
+WEBVTT
+
+STYLE
+::cue(.doubleHeight) { font-size: 200%; background-color: black }
+
+1
+00:00:00.000 --> 00:00:00.500 align:left
+<c.doubleHeight>Left</c>
+
+2
+00:00:00.000 --> 00:00:00.500 align:right position:100%
+<c.doubleHeight>Right</c>
+
+3
+00:00:00.000 --> 00:00:00.500 align:left
+<c.doubleHeight>Above</c>
+
+4
+00:00:00.000 --> 00:00:00.500 align:right position:100%
+<c.doubleHeight>Above</c>
Added: trunk/LayoutTests/media/track/captions-webvtt/snap-to-lines-left-and-right.vtt (0 => 286743)
--- trunk/LayoutTests/media/track/captions-webvtt/snap-to-lines-left-and-right.vtt (rev 0)
+++ trunk/LayoutTests/media/track/captions-webvtt/snap-to-lines-left-and-right.vtt 2021-12-08 22:59:49 UTC (rev 286743)
@@ -0,0 +1,17 @@
+WEBVTT
+
+1
+00:00:00.000 --> 00:00:00.500 align:left
+Left
+
+2
+00:00:00.000 --> 00:00:00.500 align:right position:100%
+Right
+
+3
+00:00:00.000 --> 00:00:00.500 align:left
+Above
+
+4
+00:00:00.000 --> 00:00:00.500 align:right position:100%
+Above
Added: trunk/LayoutTests/media/track/track-webvtt-no-snap-to-lines-overlap-expected.html (0 => 286743)
--- trunk/LayoutTests/media/track/track-webvtt-no-snap-to-lines-overlap-expected.html (rev 0)
+++ trunk/LayoutTests/media/track/track-webvtt-no-snap-to-lines-overlap-expected.html 2021-12-08 22:59:49 UTC (rev 286743)
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>track-webvtt-no-snap-to-lines-overlap</title>
+ <style>
+ .video {
+ display: inline-block;
+ position: relative;
+ width: 320px;
+ height: 240px;
+ }
+ html { overflow:hidden }
+ body { margin:0 }
+ .cue-box {
+ position: absolute;
+ font-size: 12px;
+ }
+ .cue {
+ font-family: Ahem, sans-serif;
+ background-color: black;
+ color: green;
+ }
+ </style>
+ <script>var requirePixelDump = true;</script>
+ <script src=""
+ <script src=""
+ <script>
+ window.addEventListener('load', async event => {
+ consoleWrite('RUN(video.querySelector("track").src = ""
+ consoleWrite('RUN(video.textTracks[0].mode = "showing")');
+ consoleWrite('EVENT(load)');
+ consoleWrite('RUN(video.src = "" "w3c/track/webvtt/media/white"))');
+ consoleWrite('EVENT(canplaythrough)');
+ endTest();
+ });
+ </script>
+</head>
+<body>
+ <div class="video">
+ <div class="cue-box" style="left:0; top:0; text-align: left;">
+ <span class="cue">Left</span><br>
+ <span class="cue">Below</span>
+ </div>
+ <div class="cue-box" style="right:0; top:0; text-align: right">
+ <span class="cue">Right</span><br>
+ <span class="cue">Below</span>
+ </div>
+ <div class="cue-box" style="left:0; bottom:0; text-align: left">
+ <span class="cue">Above</span><br>
+ <span class="cue">Left</span>
+ </div>
+ <div class="cue-box" style="right:0; bottom:0; text-align: right">
+ <span class="cue">Above</span><br>
+ <span class="cue">Right</span>
+ </div>
+ </div>
+</body>
+</html>
\ No newline at end of file
Added: trunk/LayoutTests/media/track/track-webvtt-no-snap-to-lines-overlap.html (0 => 286743)
--- trunk/LayoutTests/media/track/track-webvtt-no-snap-to-lines-overlap.html (rev 0)
+++ trunk/LayoutTests/media/track/track-webvtt-no-snap-to-lines-overlap.html 2021-12-08 22:59:49 UTC (rev 286743)
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>track-webvtt-no-snap-to-lines-overlap</title>
+ <style>
+ html { overflow:hidden }
+ body { margin:0 }
+ ::cue {
+ font-family: Ahem, sans-serif;
+ font-size: 12px;
+ background-color: black;
+ color: green;
+ }
+ </style>
+ <script>var requirePixelDump = true;</script>
+ <script src=""
+ <script src=""
+ <script>
+ window.addEventListener('load', async event => {
+ findMediaElement();
+
+ run(`video.querySelector("track").src = ""
+ run(`video.textTracks[0].mode = "showing"`);
+ await waitFor(video.querySelector('track'), 'load');
+
+ run(`video.src = "" "w3c/track/webvtt/media/white")`);
+ await waitFor(video, 'canplaythrough');
+
+ endTest();
+ });
+ </script>
+</head>
+<body>
+ <video>
+ <track id="testTrack" kind="captions" default>
+ </video>
+</body>
+</html>
\ No newline at end of file
Added: trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-inline-style-expected.html (0 => 286743)
--- trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-inline-style-expected.html (rev 0)
+++ trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-inline-style-expected.html 2021-12-08 22:59:49 UTC (rev 286743)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>track-webvtt-snap-to-lines-inline-style</title>
+ <style>
+ .video {
+ display: inline-block;
+ position: relative;
+ width: 320px;
+ height: 240px;
+ }
+ html { overflow:hidden }
+ body { margin:0 }
+ .cue-box {
+ position: absolute;
+ }
+ .cue {
+ font-family: Ahem, sans-serif;
+ background-color: black;
+ color: green;
+ font-size: 24px;
+ }
+ </style>
+ <script>var requirePixelDump = true;</script>
+ <script src=""
+ <script src=""
+ <script>
+ window.addEventListener('load', async event => {
+ consoleWrite('RUN(video.querySelector("track").src = ""
+ consoleWrite('RUN(video.textTracks[0].mode = "showing")');
+ consoleWrite('EVENT(load)');
+ consoleWrite('RUN(video.src = "" "w3c/track/webvtt/media/white"))');
+ consoleWrite('EVENT(canplaythrough)');
+ endTest();
+ });
+ </script>
+</head>
+<body>
+ <div class="video">
+ <div class="cue-box" style="left:0; bottom:0; text-align: left">
+ <span class="cue">Above</span><br>
+ <span class="cue">Left</span>
+ </div>
+ <div class="cue-box" style="right:0; bottom:0; text-align: right">
+ <span class="cue">Above</span><br>
+ <span class="cue">Right</span>
+ </div>
+ </div>
+</body>
+</html>
\ No newline at end of file
Added: trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-inline-style.html (0 => 286743)
--- trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-inline-style.html (rev 0)
+++ trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-inline-style.html 2021-12-08 22:59:49 UTC (rev 286743)
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>track-webvtt-snap-to-lines-inline-style</title>
+ <style>
+ html { overflow:hidden }
+ body { margin:0 }
+ ::cue {
+ font-family: Ahem, sans-serif;
+ font-size: 12px;
+ background-color: black;
+ color: green;
+ }
+ </style>
+ <script>var requirePixelDump = true;</script>
+ <script src=""
+ <script src=""
+ <script>
+ window.addEventListener('load', async event => {
+ findMediaElement();
+
+ run(`video.querySelector("track").src = ""
+ run(`video.textTracks[0].mode = "showing"`);
+ await waitFor(video.querySelector('track'), 'load');
+
+ run(`video.src = "" "w3c/track/webvtt/media/white")`);
+ await waitFor(video, 'canplaythrough');
+
+ endTest();
+ });
+ </script>
+</head>
+<body>
+ <video>
+ <track id="testTrack" src="" kind="captions" default>
+ </video>
+</body>
+</html>
\ No newline at end of file
Added: trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-left-right-expected.html (0 => 286743)
--- trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-left-right-expected.html (rev 0)
+++ trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-left-right-expected.html 2021-12-08 22:59:49 UTC (rev 286743)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>track-webvtt-snap-to-lines-left-right</title>
+ <style>
+ .video {
+ display: inline-block;
+ position: relative;
+ width: 320px;
+ height: 240px;
+ }
+ html { overflow:hidden }
+ body { margin:0 }
+ .cue-box {
+ position: absolute;
+ font-size: 12px;
+ }
+ .cue {
+ font-family: Ahem, sans-serif;
+ background-color: black;
+ color: green;
+ }
+ </style>
+ <script>var requirePixelDump = true;</script>
+ <script src=""
+ <script src=""
+ <script>
+ window.addEventListener('load', async event => {
+ consoleWrite('RUN(video.querySelector("track").src = ""
+ consoleWrite('RUN(video.textTracks[0].mode = "showing")');
+ consoleWrite('EVENT(load)');
+ consoleWrite('RUN(video.src = "" "w3c/track/webvtt/media/white"))');
+ consoleWrite('EVENT(canplaythrough)');
+ endTest();
+ });
+ </script>
+</head>
+<body>
+ <div class="video">
+ <div class="cue-box" style="left:0; bottom:0; text-align: left">
+ <span class="cue">Above</span><br>
+ <span class="cue">Left</span>
+ </div>
+ <div class="cue-box" style="right:0; bottom:0; text-align: right">
+ <span class="cue">Above</span><br>
+ <span class="cue">Right</span>
+ </div>
+ </div>
+</body>
+</html>
\ No newline at end of file
Added: trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-left-right.html (0 => 286743)
--- trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-left-right.html (rev 0)
+++ trunk/LayoutTests/media/track/track-webvtt-snap-to-lines-left-right.html 2021-12-08 22:59:49 UTC (rev 286743)
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>track-webvtt-snap-to-lines-left-right</title>
+ <style>
+ html { overflow:hidden }
+ body { margin:0 }
+ ::cue {
+ font-family: Ahem, sans-serif;
+ font-size: 12px;
+ background-color: black;
+ color: green;
+ }
+ </style>
+ <script>var requirePixelDump = true;</script>
+ <script src=""
+ <script src=""
+ <script>
+ window.addEventListener('load', async event => {
+ findMediaElement();
+
+ run(`video.querySelector("track").src = ""
+ run(`video.textTracks[0].mode = "showing"`);
+ await waitFor(video.querySelector('track'), 'load');
+
+ run(`video.src = "" "w3c/track/webvtt/media/white")`);
+ await waitFor(video, 'canplaythrough');
+
+ endTest();
+ });
+ </script>
+</head>
+<body>
+ <video>
+ <track id="testTrack" kind="captions" default>
+ </video>
+</body>
+</html>
\ No newline at end of file
Modified: trunk/Source/WebCore/ChangeLog (286742 => 286743)
--- trunk/Source/WebCore/ChangeLog 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/Source/WebCore/ChangeLog 2021-12-08 22:59:49 UTC (rev 286743)
@@ -1,5 +1,62 @@
2021-12-08 Jer Noble <jer.no...@apple.com>
+ [VTT] Fix various issues with complicated rendering of VTT cues
+ https://bugs.webkit.org/show_bug.cgi?id=233901
+
+ Reviewed by Eric Carlson.
+
+ Tests: media/track/track-webvtt-no-snap-to-lines-overlap.html
+ media/track/track-webvtt-snap-to-lines-inline-style.html
+ media/track/track-webvtt-snap-to-lines-left-right.html
+
+ When positioning VTT cues, the spec requires UAs to avoid collisions between cues
+ by detecting that two cues overlap each other. However, WebKit's implementation
+ looks for collisions between non-displaying portions of the cues; namely the
+ ::-webkit-media-text-track-display element, which is used to position the displaying
+ portion of the cue. Instead, the UA should look for collisions between the
+ ::-webkit-media-text-track-display-backdrop element, which holds the background
+ of the cue, if present.
+
+ Add a convenience function to retrieve the backdrop element, and another to retrieve
+ the cue itself.
+
+ Add cast macros for RenderVTTCue to allow downcast<>ing. Use this macro to retrieve
+ other cues's backdrop elements for collision avoidance.
+
+ VTTCueBox::applyCSSProperties() had a section for moving cues into place that is entirely
+ unneeded if VTTCue::getCSSPosition() returns the pre-calculated m_displayPosition.
+
+ RenderVTTCue::initializeLayoutParameters() is a careful implementation of the specification,
+ accurately layout out cues' initial position so that the text run is exactly at the
+ bottom of the content box. However, if the cue has a border, padding, or extra height,
+ this will result in the cue being pushed outside the content box. To account for this,
+ adjust the inital layout parameter by the difference between the cue text size and the
+ backdrop element size.
+
+ RenderVTTCue::layout() will create a LayoutStateMaintainer, which modifies the behavior of
+ layout machinery during the layout itself. However, certain child renderers get dramatically
+ incorrect results for absoluteBoundingRect() unless the maintainer explicitly sets disableState.
+
+ * html/track/VTTCue.cpp:
+ (WebCore::VTTCueBox::applyCSSProperties):
+ (WebCore::VTTCue::getCSSPosition const):
+ * html/track/VTTCue.h:
+ * rendering/RenderObject.h:
+ (WebCore::RenderObject::isRenderVTTCue const):
+ * rendering/RenderVTTCue.cpp:
+ (WebCore::RenderVTTCue::layout):
+ (WebCore::RenderVTTCue::initializeLayoutParameters):
+ (WebCore::RenderVTTCue::isOutside const):
+ (WebCore::RenderVTTCue::overlappingObject const):
+ (WebCore::RenderVTTCue::overlappingObjectForRect const):
+ (WebCore::RenderVTTCue::moveIfNecessaryToKeepWithinContainer):
+ (WebCore::RenderVTTCue::findNonOverlappingPosition const):
+ (WebCore::RenderVTTCue::repositionGenericCue):
+ (WebCore::RenderVTTCue::backdropBox const):
+ (WebCore::RenderVTTCue::cueBox const):
+
+2021-12-08 Jer Noble <jer.no...@apple.com>
+
[MSE] Add a Modules/mediasource/README.md file
https://bugs.webkit.org/show_bug.cgi?id=234026
<rdar://problem/86227732>
Modified: trunk/Source/WebCore/html/track/VTTCue.cpp (286742 => 286743)
--- trunk/Source/WebCore/html/track/VTTCue.cpp 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/Source/WebCore/html/track/VTTCue.cpp 2021-12-08 22:59:49 UTC (rev 286743)
@@ -186,14 +186,15 @@
// the 'writing-mode' property must be set to writing-mode
setInlineStyleProperty(CSSPropertyWritingMode, cue->getCSSWritingMode(), false);
- auto position = cue->getCSSPosition();
+ auto& position = cue->getCSSPosition();
// the 'top' property must be set to top,
- setInlineStyleProperty(CSSPropertyTop, position.second, CSSUnitType::CSS_PERCENTAGE);
+ if (position.second)
+ setInlineStyleProperty(CSSPropertyTop, *position.second, CSSUnitType::CSS_PERCENTAGE);
// the 'left' property must be set to left
- if (cue->vertical() == horizontalKeyword())
- setInlineStyleProperty(CSSPropertyLeft, position.first, CSSUnitType::CSS_PERCENTAGE);
+ if (cue->vertical() == horizontalKeyword() && position.first)
+ setInlineStyleProperty(CSSPropertyLeft, *position.first, CSSUnitType::CSS_PERCENTAGE);
else if (cue->vertical() == verticalGrowingRightKeyword()) {
// FIXME: Why use calc to do the math instead of doing the subtraction here?
setInlineStyleProperty(CSSPropertyLeft, makeString("calc(-", videoSize.width(), "px - ", cue->getCSSSize(), "px)"));
@@ -219,15 +220,15 @@
setInlineStyleProperty(CSSPropertyHeight, CSSValueAuto);
setInlineStyleProperty(CSSPropertyMinWidth, "min-content");
setInlineStyleProperty(CSSPropertyMaxWidth, maxSize, CSSUnitType::CSS_PERCENTAGE);
- if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0)
- setInlineStyleProperty(CSSPropertyLeft, static_cast<double>(position.first - (newCueSize - cue->getCSSSize()) / 2), CSSUnitType::CSS_PERCENTAGE);
+ if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0 && position.first)
+ setInlineStyleProperty(CSSPropertyLeft, static_cast<double>(*position.first - (newCueSize - cue->getCSSSize()) / 2), CSSUnitType::CSS_PERCENTAGE);
} else {
setInlineStyleProperty(CSSPropertyWidth, CSSValueAuto);
setInlineStyleProperty(CSSPropertyHeight, newCueSize, CSSUnitType::CSS_PERCENTAGE);
setInlineStyleProperty(CSSPropertyMinHeight, "min-content");
setInlineStyleProperty(CSSPropertyMaxHeight, maxSize, CSSUnitType::CSS_PERCENTAGE);
- if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0)
- setInlineStyleProperty(CSSPropertyTop, static_cast<double>(position.second - (newCueSize - cue->getCSSSize()) / 2), CSSUnitType::CSS_PERCENTAGE);
+ if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0 && position.second)
+ setInlineStyleProperty(CSSPropertyTop, static_cast<double>(*position.second - (newCueSize - cue->getCSSSize()) / 2), CSSUnitType::CSS_PERCENTAGE);
}
// The 'text-align' property on the (root) List of WebVTT Node Objects must
@@ -236,21 +237,8 @@
// alignment:
setInlineStyleProperty(CSSPropertyTextAlign, cue->getCSSAlignment());
- if (!cue->snapToLines()) {
- // 10.13.1 Set up x and y:
- // Note: x and y are set through the CSS left and top above.
-
- // 10.13.2 Position the boxes in boxes such that the point x% along the
- // width of the bounding box of the boxes in boxes is x% of the way
- // across the width of the video's rendering area, and the point y%
- // along the height of the bounding box of the boxes in boxes is y%
- // of the way across the height of the video's rendering area, while
- // maintaining the relative positions of the boxes in boxes to each
- // other.
- setInlineStyleProperty(CSSPropertyTransform, makeString("translate(", -position.first, "%, ", -position.second, "%)"));
-
+ if (!cue->snapToLines())
setInlineStyleProperty(CSSPropertyWhiteSpace, CSSValuePre);
- }
// Make sure shadow or stroke is not clipped.
setInlineStyleProperty(CSSPropertyOverflow, CSSValueVisible);
@@ -384,26 +372,21 @@
VTTCue::LineAndPositionSetting VTTCue::line() const
{
- if (std::isnan(m_linePosition))
+ if (!m_linePosition)
return Auto;
- return m_linePosition;
+ return *m_linePosition;
}
ExceptionOr<void> VTTCue::setLine(const LineAndPositionSetting& position)
{
- double linePosition = 0;
+ std::optional<double> linePosition;
- if (std::holds_alternative<AutoKeyword>(position)) {
- if (std::isnan(m_linePosition))
- return { };
- linePosition = std::numeric_limits<double>::quiet_NaN();
- } else {
+ if (!std::holds_alternative<AutoKeyword>(position))
linePosition = std::get<double>(position);
- if (m_linePosition == linePosition)
- return { };
- }
+ if (m_linePosition == linePosition)
+ return { };
willChange();
m_linePosition = linePosition;
@@ -453,9 +436,9 @@
VTTCue::LineAndPositionSetting VTTCue::position() const
{
- if (textPositionIsAuto())
- return Auto;
- return m_textPosition;
+ if (m_textPosition)
+ return *m_textPosition;
+ return Auto;
}
ExceptionOr<void> VTTCue::setPosition(const LineAndPositionSetting& position)
@@ -465,23 +448,20 @@
// IndexSizeError exception must be thrown. Otherwise, the WebVTT cue
// position must be set to the new value; if the new value is the string
// "auto", then it must be interpreted as the special value auto.
- double textPosition = 0;
- if (std::holds_alternative<AutoKeyword>(position)) {
- if (textPositionIsAuto())
- return { };
- textPosition = std::numeric_limits<double>::quiet_NaN();
- } else {
+ std::optional<double> textPosition;
+
+ // Otherwise, set the text track cue line position to the new value.
+ if (!std::holds_alternative<AutoKeyword>(position)) {
textPosition = std::get<double>(position);
if (!(textPosition >= 0 && textPosition <= 100))
return Exception { IndexSizeError };
-
- // Otherwise, set the text track cue line position to the new value.
- if (m_textPosition == textPosition)
- return { };
}
+ if (m_textPosition == textPosition)
+ return { };
+
willChange();
- m_textPosition = textPosition;
+ m_textPosition = WTFMove(textPosition);
didChange();
return { };
@@ -714,14 +694,14 @@
return m_region->id();
}
-int VTTCue::calculateComputedLinePosition()
+int VTTCue::calculateComputedLinePosition() const
{
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-computed-line-position
// If the text track cue line position is numeric, then that is the text
// track cue computed line position.
- if (!std::isnan(m_linePosition))
- return m_linePosition;
+ if (m_linePosition)
+ return *m_linePosition;
// If the text track cue snap-to-lines flag of the text track cue is not
// set, the text track cue computed line position is the value 100;
@@ -756,11 +736,6 @@
return u_charType(character) == U_PARAGRAPH_SEPARATOR;
}
-bool VTTCue::textPositionIsAuto() const
-{
- return std::isnan(m_textPosition);
-}
-
void VTTCue::determineTextDirection()
{
static NeverDestroyed<const String> rtTag(MAKE_STATIC_STRING_IMPL("rt"));
@@ -810,8 +785,8 @@
// 1. If the position is numeric, then return the value of the position and
// abort these steps. (Otherwise, the position is the special value auto.)
- if (!textPositionIsAuto())
- return m_textPosition;
+ if (m_textPosition)
+ return *m_textPosition;
switch (m_cueAlignment) {
case Start:
@@ -921,18 +896,18 @@
// 10.9 Determine the value of whichever of x-position or y-position is not
// yet calculated for cue as per the appropriate rules from the following
// list:
- if (m_snapToLines && m_displayPosition.second == undefinedPosition && m_writingDirection == Horizontal)
+ if (m_snapToLines && !m_displayPosition.second && m_writingDirection == Horizontal)
m_displayPosition.second = 0;
- if (!m_snapToLines && m_displayPosition.second == undefinedPosition && m_writingDirection == Horizontal)
- m_displayPosition.second = m_computedLinePosition;
+ if (!m_snapToLines && !m_displayPosition.second && m_writingDirection == Horizontal)
+ m_displayPosition.second = *m_computedLinePosition;
- if (m_snapToLines && m_displayPosition.first == undefinedPosition
+ if (m_snapToLines && !m_displayPosition.first
&& (m_writingDirection == VerticalGrowingLeft || m_writingDirection == VerticalGrowingRight))
m_displayPosition.first = 0;
if (!m_snapToLines && (m_writingDirection == VerticalGrowingLeft || m_writingDirection == VerticalGrowingRight))
- m_displayPosition.first = m_computedLinePosition;
+ m_displayPosition.first = *m_computedLinePosition;
}
void VTTCue::markFutureAndPastNodes(ContainerNode* root, const MediaTime& previousTimestamp, const MediaTime& movieTime)
@@ -1076,10 +1051,11 @@
std::pair<double, double> coordinates;
auto textPosition = calculateComputedTextPosition();
+ auto computedLinePosition = m_computedLinePosition ? *m_computedLinePosition : calculateComputedLinePosition();
if (m_writingDirection == Horizontal && m_displayDirection == CSSValueLtr) {
coordinates.first = textPosition;
- coordinates.second = m_computedLinePosition;
+ coordinates.second = computedLinePosition;
return coordinates;
}
@@ -1086,13 +1062,13 @@
if (m_writingDirection == Horizontal && m_displayDirection == CSSValueRtl) {
coordinates.first = 100 - textPosition;
- coordinates.second = m_computedLinePosition;
+ coordinates.second = computedLinePosition;
return coordinates;
}
if (m_writingDirection == VerticalGrowingLeft) {
- coordinates.first = 100 - m_computedLinePosition;
+ coordinates.first = 100 - *m_computedLinePosition;
coordinates.second = textPosition;
return coordinates;
@@ -1099,7 +1075,7 @@
}
if (m_writingDirection == VerticalGrowingRight) {
- coordinates.first = m_computedLinePosition;
+ coordinates.first = computedLinePosition;
coordinates.second = textPosition;
return coordinates;
@@ -1319,14 +1295,6 @@
return m_displaySize;
}
-std::pair<double, double> VTTCue::getCSSPosition() const
-{
- if (!m_snapToLines)
- return getPositionCoordinates();
-
- return m_displayPosition;
-}
-
bool VTTCue::cueContentsMatch(const TextTrackCue& otherTextTrackCue) const
{
auto& other = downcast<VTTCue>(otherTextTrackCue);
@@ -1360,11 +1328,14 @@
object.setString("vertical"_s, vertical());
object.setBoolean("snapToLines"_s, snapToLines());
- object.setDouble("line"_s, m_linePosition);
- if (textPositionIsAuto())
+ if (m_linePosition)
+ object.setString("line"_s, "auto"_s);
+ else
+ object.setDouble("line"_s, *m_linePosition);
+ if (m_textPosition)
+ object.setDouble("position"_s, *m_textPosition);
+ else
object.setString("position"_s, "auto"_s);
- else
- object.setDouble("position"_s, m_textPosition);
object.setInteger("size"_s, m_cueSize);
object.setString("align"_s, align());
}
Modified: trunk/Source/WebCore/html/track/VTTCue.h (286742 => 286743)
--- trunk/Source/WebCore/html/track/VTTCue.h 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/Source/WebCore/html/track/VTTCue.h 2021-12-08 22:59:49 UTC (rev 286743)
@@ -136,10 +136,11 @@
void removeDisplayTree() final;
void markFutureAndPastNodes(ContainerNode*, const MediaTime&, const MediaTime&);
- int calculateComputedLinePosition();
+ int calculateComputedLinePosition() const;
std::pair<double, double> getPositionCoordinates() const;
- std::pair<double, double> getCSSPosition() const;
+ using DisplayPosition = std::pair<std::optional<double>, std::optional<double>>;
+ const DisplayPosition& getCSSPosition() const { return m_displayPosition; };
CSSValueID getCSSAlignment() const;
int getCSSSize() const;
@@ -207,8 +208,6 @@
void parseSettings(const String&);
- bool textPositionIsAuto() const;
-
void determineTextDirection();
void calculateDisplayParameters();
@@ -223,13 +222,11 @@
};
CueSetting settingName(VTTScanner&);
- static constexpr double undefinedPosition = -1;
-
String m_content;
String m_settings;
- double m_linePosition { std::numeric_limits<double>::quiet_NaN() };
- double m_computedLinePosition { std::numeric_limits<double>::quiet_NaN() };
- double m_textPosition { std::numeric_limits<double>::quiet_NaN() };
+ std::optional<double> m_linePosition;
+ std::optional<double> m_computedLinePosition;
+ std::optional<double> m_textPosition;
int m_cueSize { 100 };
WritingDirection m_writingDirection { Horizontal };
@@ -245,7 +242,7 @@
CSSValueID m_displayDirection { CSSValueLtr };
int m_displaySize { 0 };
- std::pair<float, float> m_displayPosition;
+ DisplayPosition m_displayPosition;
MediaTime m_originalStartTime;
Modified: trunk/Source/WebCore/page/CaptionUserPreferences.cpp (286742 => 286743)
--- trunk/Source/WebCore/page/CaptionUserPreferences.cpp 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/Source/WebCore/page/CaptionUserPreferences.cpp 2021-12-08 22:59:49 UTC (rev 286743)
@@ -97,7 +97,7 @@
void CaptionUserPreferences::setCaptionDisplayMode(CaptionUserPreferences::CaptionDisplayMode mode)
{
m_displayMode = mode;
- if (m_testingMode && mode != AlwaysOn) {
+ if (testingMode() && mode != AlwaysOn) {
setUserPrefersCaptions(false);
setUserPrefersSubtitles(false);
}
@@ -176,7 +176,7 @@
Vector<String> CaptionUserPreferences::preferredLanguages() const
{
Vector<String> languages = userPreferredLanguages(ShouldMinimizeLanguages::No);
- if (m_testingMode && !m_userPreferredLanguage.isEmpty())
+ if (testingMode() && !m_userPreferredLanguage.isEmpty())
languages.insert(0, m_userPreferredLanguage);
return languages;
Modified: trunk/Source/WebCore/page/CaptionUserPreferences.h (286742 => 286743)
--- trunk/Source/WebCore/page/CaptionUserPreferences.h 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/Source/WebCore/page/CaptionUserPreferences.h 2021-12-08 22:59:49 UTC (rev 286743)
@@ -36,6 +36,7 @@
namespace WebCore {
+class CaptionUserPreferencesTestingModeToken;
class HTMLMediaElement;
class Page;
class PageGroup;
@@ -98,8 +99,10 @@
void setPrimaryAudioTrackLanguageOverride(const String& language) { m_primaryAudioTrackLanguageOverride = language; }
String primaryAudioTrackLanguageOverride() const;
- virtual bool testingMode() const { return m_testingMode; }
- void setTestingMode(bool override) { m_testingMode = override; }
+ virtual bool testingMode() const { return m_testingModeCount; }
+
+ friend class CaptionUserPreferencesTestingModeToken;
+ UniqueRef<CaptionUserPreferencesTestingModeToken> createTestingModeToken() { return makeUniqueRef<CaptionUserPreferencesTestingModeToken>(*this); }
PageGroup& pageGroup() const { return m_pageGroup; }
@@ -111,6 +114,14 @@
void endBlockingNotifications();
private:
+ void incrementTestingModeCount() { ++m_testingModeCount; }
+ void decrementTestingModeCount()
+ {
+ ASSERT(m_testingModeCount);
+ if (m_testingModeCount)
+ --m_testingModeCount;
+ }
+
void timerFired();
void notify();
Page* currentPage() const;
@@ -123,9 +134,26 @@
String m_captionsStyleSheetOverride;
String m_primaryAudioTrackLanguageOverride;
unsigned m_blockNotificationsCounter { 0 };
- bool m_testingMode { false };
bool m_havePreferences { false };
+ unsigned m_testingModeCount { 0 };
};
+
+class CaptionUserPreferencesTestingModeToken {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ CaptionUserPreferencesTestingModeToken(CaptionUserPreferences& parent)
+ : m_parent(parent)
+ {
+ parent.incrementTestingModeCount();
+ }
+ ~CaptionUserPreferencesTestingModeToken()
+ {
+ if (m_parent)
+ m_parent->decrementTestingModeCount();
+ }
+private:
+ WeakPtr<CaptionUserPreferences> m_parent;
+};
}
Modified: trunk/Source/WebCore/rendering/RenderObject.h (286742 => 286743)
--- trunk/Source/WebCore/rendering/RenderObject.h 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/Source/WebCore/rendering/RenderObject.h 2021-12-08 22:59:49 UTC (rev 286743)
@@ -269,6 +269,7 @@
virtual bool isRenderMultiColumnSpannerPlaceholder() const { return false; }
virtual bool isRenderScrollbarPart() const { return false; }
+ virtual bool isRenderVTTCue() const { return false; }
bool isDocumentElementRenderer() const { return document().documentElement() == &m_node; }
bool isBody() const { return node() && node()->hasTagName(HTMLNames::bodyTag); }
Modified: trunk/Source/WebCore/rendering/RenderVTTCue.cpp (286742 => 286743)
--- trunk/Source/WebCore/rendering/RenderVTTCue.cpp 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/Source/WebCore/rendering/RenderVTTCue.cpp 2021-12-08 22:59:49 UTC (rev 286743)
@@ -60,7 +60,7 @@
if (!m_cue->regionId().isEmpty())
return;
- LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
+ LayoutStateMaintainer statePusher(*this, locationOffset(), true);
if (m_cue->cueType()== TextTrackCue::WebVTT) {
if (m_cue->snapToLines())
@@ -79,12 +79,7 @@
RenderBlock* parentBlock = containingBlock();
- // firstChild() returns the wrapping (backdrop) <div>. The cue object is
- // the <div>'s first child.
- RenderObject& firstChild = *this->firstChild();
- RenderElement& backdropElement = downcast<RenderElement>(firstChild);
-
- firstLineBox = downcast<RenderInline>(*backdropElement.firstChild()).firstLineBox();
+ firstLineBox = cueBox().firstLineBox();
if (!firstLineBox)
firstLineBox = this->firstRootBox();
@@ -92,6 +87,23 @@
// Vertical: Let step be the width of the first line box in boxes.
step = m_cue->getWritingDirection() == VTTCue::Horizontal ? firstLineBox->height() : firstLineBox->width();
+ // Note: the previous rules in initializeLayoutParameters() only account for
+ // the height of the line boxes contained within the cue, and not the cue's height
+ // nor its padding, nor its borders. Ignoring these will lead to errors
+ // in the initial placement of cues, as the resulting placement will result in
+ // the cue always being partially outside its containing block, rather than at
+ // its initial position. Correct the initial position by subtracting from
+ // position the difference between the the logicalHeight of the cue and its
+ // first line box.
+ auto& backdropBox = this->backdropBox();
+ auto lineBoxHeights = firstLineBox->logicalHeight();
+ for (auto nextLineBox = firstLineBox->nextLineBox(); nextLineBox; nextLineBox = nextLineBox->nextLineBox())
+ lineBoxHeights += nextLineBox->logicalHeight();
+
+ auto logicalHeightDelta = backdropBox.logicalHeight() - lineBoxHeights;
+ if (logicalHeightDelta > 0)
+ step += logicalHeightDelta;
+
// 2. If step is zero, then jump to the step labeled done positioning below.
if (!step)
return false;
@@ -146,7 +158,7 @@
bool RenderVTTCue::isOutside() const
{
- return !rectIsWithinContainer(absoluteContentBox());
+ return !rectIsWithinContainer(backdropBox().absoluteBoundingBoxRect());
}
bool RenderVTTCue::rectIsWithinContainer(const IntRect& rect) const
@@ -160,18 +172,20 @@
return overlappingObject();
}
-RenderObject* RenderVTTCue::overlappingObject() const
+RenderVTTCue* RenderVTTCue::overlappingObject() const
{
- return overlappingObjectForRect(absoluteBoundingBoxRect());
+ return overlappingObjectForRect(backdropBox().absoluteBoundingBoxRect());
}
-RenderObject* RenderVTTCue::overlappingObjectForRect(const IntRect& rect) const
+RenderVTTCue* RenderVTTCue::overlappingObjectForRect(const IntRect& rect) const
{
- for (RenderObject* box = previousSibling(); box; box = box->previousSibling()) {
- IntRect boxRect = box->absoluteBoundingBoxRect();
+ for (RenderObject* sibling = previousSibling(); sibling; sibling = sibling->previousSibling()) {
+ auto* previousCue = downcast<RenderVTTCue>(sibling);
+ if (!previousCue)
+ continue;
- if (rect.intersects(boxRect))
- return box;
+ if (rect.intersects(previousCue->backdropBox().absoluteBoundingBoxRect()))
+ return previousCue;
}
return 0;
@@ -243,7 +257,7 @@
void RenderVTTCue::moveIfNecessaryToKeepWithinContainer()
{
IntRect containerRect = containingBlock()->absoluteBoundingBoxRect();
- IntRect cueRect = absoluteBoundingBoxRect();
+ IntRect cueRect = backdropBox().absoluteBoundingBoxRect();
int topOverflow = cueRect.y() - containerRect.y();
int bottomOverflow = containerRect.maxY() - cueRect.maxY();
@@ -274,15 +288,15 @@
{
newX = x();
newY = y();
- IntRect srcRect = absoluteBoundingBoxRect();
+ IntRect srcRect = backdropBox().absoluteBoundingBoxRect();
IntRect destRect = srcRect;
// Move the box up, looking for a non-overlapping position:
- while (RenderObject* box = overlappingObjectForRect(destRect)) {
+ while (RenderVTTCue* cue = overlappingObjectForRect(destRect)) {
if (m_cue->getWritingDirection() == VTTCue::Horizontal)
- destRect.setY(box->absoluteBoundingBoxRect().y() - destRect.height());
+ destRect.setY(cue->backdropBox().absoluteBoundingBoxRect().y() - destRect.height());
else
- destRect.setX(box->absoluteBoundingBoxRect().x() - destRect.width());
+ destRect.setX(cue->backdropBox().absoluteBoundingBoxRect().x() - destRect.width());
}
if (rectIsWithinContainer(destRect)) {
@@ -294,11 +308,11 @@
destRect = srcRect;
// Move the box down, looking for a non-overlapping position:
- while (RenderObject* box = overlappingObjectForRect(destRect)) {
+ while (RenderVTTCue* cue = overlappingObjectForRect(destRect)) {
if (m_cue->getWritingDirection() == VTTCue::Horizontal)
- destRect.setY(box->absoluteBoundingBoxRect().maxY());
+ destRect.setY(cue->backdropBox().absoluteBoundingBoxRect().maxY());
else
- destRect.setX(box->absoluteBoundingBoxRect().maxX());
+ destRect.setX(cue->backdropBox().absoluteBoundingBoxRect().maxX());
}
if (rectIsWithinContainer(destRect)) {
@@ -345,12 +359,7 @@
{
ASSERT(firstChild());
- // firstChild() returns the wrapping (backdrop) <div>. The cue object is
- // the <div>'s first child.
- RenderObject& firstChild = *this->firstChild();
- RenderElement& backdropElement = downcast<RenderElement>(firstChild);
-
- LegacyInlineFlowBox* firstLineBox = downcast<RenderInline>(*backdropElement.firstChild()).firstLineBox();
+ LegacyInlineFlowBox* firstLineBox = cueBox().firstLineBox();
if (downcast<TextTrackCueGeneric>(*m_cue).useDefaultPosition() && firstLineBox) {
LayoutUnit parentWidth = containingBlock()->logicalWidth();
LayoutUnit width { firstLineBox->width() };
@@ -384,6 +393,21 @@
setY(y);
}
+RenderBlockFlow& RenderVTTCue::backdropBox() const
+{
+ ASSERT(firstChild());
+
+ // firstChild() returns the wrapping (backdrop) <div>. The cue object is
+ // the <div>'s first child.
+ RenderObject& firstChild = *this->firstChild();
+ return downcast<RenderBlockFlow>(firstChild);
+}
+
+RenderInline& RenderVTTCue::cueBox() const
+{
+ return downcast<RenderInline>(*backdropBox().firstChild());
+}
+
} // namespace WebCore
#endif
Modified: trunk/Source/WebCore/rendering/RenderVTTCue.h (286742 => 286743)
--- trunk/Source/WebCore/rendering/RenderVTTCue.h 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/Source/WebCore/rendering/RenderVTTCue.h 2021-12-08 22:59:49 UTC (rev 286743)
@@ -42,13 +42,15 @@
RenderVTTCue(VTTCueBox&, RenderStyle&&);
private:
+ bool isRenderVTTCue() const final { return true; }
+
void layout() override;
bool isOutside() const;
bool rectIsWithinContainer(const IntRect&) const;
bool isOverlapping() const;
- RenderObject* overlappingObject() const;
- RenderObject* overlappingObjectForRect(const IntRect&) const;
+ RenderVTTCue* overlappingObject() const;
+ RenderVTTCue* overlappingObjectForRect(const IntRect&) const;
bool shouldSwitchDirection(LegacyInlineFlowBox*, LayoutUnit) const;
void moveBoxesByStep(LayoutUnit);
@@ -62,6 +64,9 @@
void repositionCueSnapToLinesNotSet();
void repositionGenericCue();
+ RenderBlockFlow& backdropBox() const;
+ RenderInline& cueBox() const;
+
VTTCue* m_cue;
FloatPoint m_fallbackPosition;
};
@@ -68,4 +73,6 @@
} // namespace WebCore
+SPECIALIZE_TYPE_TRAITS_RENDER_OBJECT(RenderVTTCue, isRenderVTTCue())
+
#endif // ENABLE(VIDEO)
Modified: trunk/Source/WebCore/testing/Internals.cpp (286742 => 286743)
--- trunk/Source/WebCore/testing/Internals.cpp 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/Source/WebCore/testing/Internals.cpp 2021-12-08 22:59:49 UTC (rev 286743)
@@ -545,10 +545,8 @@
page.mainFrame().loader().clearTestingOverrides();
page.applicationCacheStorage().setDefaultOriginQuota(ApplicationCacheStorage::noQuota());
#if ENABLE(VIDEO)
- page.group().ensureCaptionPreferences().setTestingMode(true);
page.group().ensureCaptionPreferences().setCaptionDisplayMode(CaptionUserPreferences::ForcedOnly);
page.group().ensureCaptionPreferences().setCaptionsStyleSheetOverride(emptyString());
- page.group().ensureCaptionPreferences().setTestingMode(false);
PlatformMediaSessionManager::sharedManager().resetHaveEverRegisteredAsNowPlayingApplicationForTesting();
PlatformMediaSessionManager::sharedManager().resetRestrictions();
PlatformMediaSessionManager::sharedManager().setWillIgnoreSystemInterruptions(true);
@@ -624,14 +622,14 @@
, m_orientationNotifier(0)
#endif
{
-#if ENABLE(VIDEO)
+#if ENABLE(WIRELESS_PLAYBACK_TARGET)
if (document.page())
- document.page()->group().ensureCaptionPreferences().setTestingMode(true);
+ document.page()->setMockMediaPlaybackTargetPickerEnabled(true);
#endif
-#if ENABLE(WIRELESS_PLAYBACK_TARGET)
+#if ENABLE(VIDEO)
if (document.page())
- document.page()->setMockMediaPlaybackTargetPickerEnabled(true);
+ m_testingModeToken = document.page()->group().ensureCaptionPreferences().createTestingModeToken().moveToUniquePtr();
#endif
if (contextDocument() && contextDocument()->frame()) {
Modified: trunk/Source/WebCore/testing/Internals.h (286742 => 286743)
--- trunk/Source/WebCore/testing/Internals.h 2021-12-08 22:57:36 UTC (rev 286742)
+++ trunk/Source/WebCore/testing/Internals.h 2021-12-08 22:59:49 UTC (rev 286743)
@@ -56,6 +56,7 @@
class BaseAudioContext;
class Blob;
class CacheStorageConnection;
+class CaptionUserPreferencesTestingModeToken;
class DOMPointReadOnly;
class DOMRect;
class DOMRectList;
@@ -1262,6 +1263,9 @@
#if ENABLE(MEDIA_SESSION_COORDINATOR)
RefPtr<MockMediaSessionCoordinator> m_mockMediaSessionCoordinator;
#endif
+#if ENABLE(VIDEO)
+ std::unique_ptr<CaptionUserPreferencesTestingModeToken> m_testingModeToken;
+#endif
};
} // namespace WebCore