Changeset 286743 in webkit


Ignore:
Timestamp:
Dec 8, 2021 2:59:49 PM (7 months ago)
Author:
jer.noble@apple.com
Message:

[VTT] Fix various issues with complicated rendering of VTT cues
https://bugs.webkit.org/show_bug.cgi?id=233901

Reviewed by Eric Carlson.

Source/WebCore:

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):

LayoutTests:

  • 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.
Location:
trunk
Files:
9 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r286672 r286743  
     12021-12-08  Jer Noble  <jer.noble@apple.com>
     2
     3        [VTT] Fix various issues with complicated rendering of VTT cues
     4        https://bugs.webkit.org/show_bug.cgi?id=233901
     5
     6        Reviewed by Eric Carlson.
     7
     8        * media/track/captions-webvtt/no-snap-to-lines-overlap.vtt: Added.
     9        * media/track/captions-webvtt/snap-to-lines-inline-style.vtt: Added.
     10        * media/track/captions-webvtt/snap-to-lines-left-and-right.vtt: Added.
     11        * media/track/track-webvtt-no-snap-to-lines-overlap-expected.html: Added.
     12        * media/track/track-webvtt-no-snap-to-lines-overlap.html: Added.
     13        * media/track/track-webvtt-snap-to-lines-inline-style-expected.html: Added.
     14        * media/track/track-webvtt-snap-to-lines-inline-style.html: Added.
     15        * media/track/track-webvtt-snap-to-lines-left-right-expected.html: Added.
     16        * media/track/track-webvtt-snap-to-lines-left-right.html: Added.
     17
    1182021-12-08  Rob Buis  <rbuis@igalia.com>
    219
  • trunk/Source/WebCore/ChangeLog

    r286742 r286743  
     12021-12-08  Jer Noble  <jer.noble@apple.com>
     2
     3        [VTT] Fix various issues with complicated rendering of VTT cues
     4        https://bugs.webkit.org/show_bug.cgi?id=233901
     5
     6        Reviewed by Eric Carlson.
     7
     8        Tests: media/track/track-webvtt-no-snap-to-lines-overlap.html
     9               media/track/track-webvtt-snap-to-lines-inline-style.html
     10               media/track/track-webvtt-snap-to-lines-left-right.html
     11
     12        When positioning VTT cues, the spec requires UAs to avoid collisions between cues
     13        by detecting that two cues overlap each other. However, WebKit's implementation
     14        looks for collisions between non-displaying portions of the cues; namely the
     15        ::-webkit-media-text-track-display element, which is used to position the displaying
     16        portion of the cue. Instead, the UA should look for collisions between the
     17        ::-webkit-media-text-track-display-backdrop element, which holds the background
     18        of the cue, if present.
     19
     20        Add a convenience function to retrieve the backdrop element, and another to retrieve
     21        the cue itself.
     22
     23        Add cast macros for RenderVTTCue to allow downcast<>ing. Use this macro to retrieve
     24        other cues's backdrop elements for collision avoidance.
     25
     26        VTTCueBox::applyCSSProperties() had a section for moving cues into place that is entirely
     27        unneeded if VTTCue::getCSSPosition() returns the pre-calculated m_displayPosition.
     28
     29        RenderVTTCue::initializeLayoutParameters() is a careful implementation of the specification,
     30        accurately layout out cues' initial position so that the text run is exactly at the
     31        bottom of the content box. However, if the cue has a border, padding, or extra height,
     32        this will result in the cue being pushed outside the content box. To account for this,
     33        adjust the inital layout parameter by the difference between the cue text size and the
     34        backdrop element size.
     35
     36        RenderVTTCue::layout() will create a LayoutStateMaintainer, which modifies the behavior of
     37        layout machinery during the layout itself. However, certain child renderers get dramatically
     38        incorrect results for absoluteBoundingRect() unless the maintainer explicitly sets disableState.
     39
     40        * html/track/VTTCue.cpp:
     41        (WebCore::VTTCueBox::applyCSSProperties):
     42        (WebCore::VTTCue::getCSSPosition const):
     43        * html/track/VTTCue.h:
     44        * rendering/RenderObject.h:
     45        (WebCore::RenderObject::isRenderVTTCue const):
     46        * rendering/RenderVTTCue.cpp:
     47        (WebCore::RenderVTTCue::layout):
     48        (WebCore::RenderVTTCue::initializeLayoutParameters):
     49        (WebCore::RenderVTTCue::isOutside const):
     50        (WebCore::RenderVTTCue::overlappingObject const):
     51        (WebCore::RenderVTTCue::overlappingObjectForRect const):
     52        (WebCore::RenderVTTCue::moveIfNecessaryToKeepWithinContainer):
     53        (WebCore::RenderVTTCue::findNonOverlappingPosition const):
     54        (WebCore::RenderVTTCue::repositionGenericCue):
     55        (WebCore::RenderVTTCue::backdropBox const):
     56        (WebCore::RenderVTTCue::cueBox const):
     57
    1582021-12-08  Jer Noble  <jer.noble@apple.com>
    259
  • trunk/Source/WebCore/html/track/VTTCue.cpp

    r285195 r286743  
    187187    setInlineStyleProperty(CSSPropertyWritingMode, cue->getCSSWritingMode(), false);
    188188
    189     auto position = cue->getCSSPosition();
     189    auto& position = cue->getCSSPosition();
    190190
    191191    // the 'top' property must be set to top,
    192     setInlineStyleProperty(CSSPropertyTop, position.second, CSSUnitType::CSS_PERCENTAGE);
     192    if (position.second)
     193        setInlineStyleProperty(CSSPropertyTop, *position.second, CSSUnitType::CSS_PERCENTAGE);
    193194
    194195    // the 'left' property must be set to left
    195     if (cue->vertical() == horizontalKeyword())
    196         setInlineStyleProperty(CSSPropertyLeft, position.first, CSSUnitType::CSS_PERCENTAGE);
     196    if (cue->vertical() == horizontalKeyword() && position.first)
     197        setInlineStyleProperty(CSSPropertyLeft, *position.first, CSSUnitType::CSS_PERCENTAGE);
    197198    else if (cue->vertical() == verticalGrowingRightKeyword()) {
    198199        // FIXME: Why use calc to do the math instead of doing the subtraction here?
     
    220221        setInlineStyleProperty(CSSPropertyMinWidth, "min-content");
    221222        setInlineStyleProperty(CSSPropertyMaxWidth, maxSize, CSSUnitType::CSS_PERCENTAGE);
    222         if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0)
    223             setInlineStyleProperty(CSSPropertyLeft, static_cast<double>(position.first - (newCueSize - cue->getCSSSize()) / 2), CSSUnitType::CSS_PERCENTAGE);
     223        if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0 && position.first)
     224            setInlineStyleProperty(CSSPropertyLeft, static_cast<double>(*position.first - (newCueSize - cue->getCSSSize()) / 2), CSSUnitType::CSS_PERCENTAGE);
    224225    } else {
    225226        setInlineStyleProperty(CSSPropertyWidth, CSSValueAuto);
     
    227228        setInlineStyleProperty(CSSPropertyMinHeight, "min-content");
    228229        setInlineStyleProperty(CSSPropertyMaxHeight, maxSize, CSSUnitType::CSS_PERCENTAGE);
    229         if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0)
    230             setInlineStyleProperty(CSSPropertyTop, static_cast<double>(position.second - (newCueSize - cue->getCSSSize()) / 2), CSSUnitType::CSS_PERCENTAGE);
     230        if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0 && position.second)
     231            setInlineStyleProperty(CSSPropertyTop, static_cast<double>(*position.second - (newCueSize - cue->getCSSSize()) / 2), CSSUnitType::CSS_PERCENTAGE);
    231232    }
    232233
     
    237238    setInlineStyleProperty(CSSPropertyTextAlign, cue->getCSSAlignment());
    238239   
    239     if (!cue->snapToLines()) {
    240         // 10.13.1 Set up x and y:
    241         // Note: x and y are set through the CSS left and top above.
    242 
    243         // 10.13.2 Position the boxes in boxes such that the point x% along the
    244         // width of the bounding box of the boxes in boxes is x% of the way
    245         // across the width of the video's rendering area, and the point y%
    246         // along the height of the bounding box of the boxes in boxes is y%
    247         // of the way across the height of the video's rendering area, while
    248         // maintaining the relative positions of the boxes in boxes to each
    249         // other.
    250         setInlineStyleProperty(CSSPropertyTransform, makeString("translate(", -position.first, "%, ", -position.second, "%)"));
    251 
     240    if (!cue->snapToLines())
    252241        setInlineStyleProperty(CSSPropertyWhiteSpace, CSSValuePre);
    253     }
    254242
    255243    // Make sure shadow or stroke is not clipped.
     
    385373VTTCue::LineAndPositionSetting VTTCue::line() const
    386374{
    387     if (std::isnan(m_linePosition))
     375    if (!m_linePosition)
    388376        return Auto;
    389377
    390     return m_linePosition;
     378    return *m_linePosition;
    391379}
    392380
    393381ExceptionOr<void> VTTCue::setLine(const LineAndPositionSetting& position)
    394382{
    395     double linePosition = 0;
    396 
    397     if (std::holds_alternative<AutoKeyword>(position)) {
    398         if (std::isnan(m_linePosition))
    399             return { };
    400         linePosition = std::numeric_limits<double>::quiet_NaN();
    401     } else {
     383    std::optional<double> linePosition;
     384
     385    if (!std::holds_alternative<AutoKeyword>(position))
    402386        linePosition = std::get<double>(position);
    403387
    404         if (m_linePosition == linePosition)
    405             return { };
    406     }
     388    if (m_linePosition == linePosition)
     389        return { };
    407390
    408391    willChange();
     
    454437VTTCue::LineAndPositionSetting VTTCue::position() const
    455438{
    456     if (textPositionIsAuto())
    457         return Auto;
    458     return m_textPosition;
     439    if (m_textPosition)
     440        return *m_textPosition;
     441    return Auto;
    459442}
    460443
     
    466449    // position must be set to the new value; if the new value is the string
    467450    // "auto", then it must be interpreted as the special value auto.
    468     double textPosition = 0;
    469     if (std::holds_alternative<AutoKeyword>(position)) {
    470         if (textPositionIsAuto())
    471             return { };
    472         textPosition = std::numeric_limits<double>::quiet_NaN();
    473     } else {
     451    std::optional<double> textPosition;
     452
     453    // Otherwise, set the text track cue line position to the new value.
     454    if (!std::holds_alternative<AutoKeyword>(position)) {
    474455        textPosition = std::get<double>(position);
    475456        if (!(textPosition >= 0 && textPosition <= 100))
    476457            return Exception { IndexSizeError };
    477 
    478         // Otherwise, set the text track cue line position to the new value.
    479         if (m_textPosition == textPosition)
    480             return { };
    481     }
     458    }
     459
     460    if (m_textPosition == textPosition)
     461        return { };
    482462
    483463    willChange();
    484     m_textPosition = textPosition;
     464    m_textPosition = WTFMove(textPosition);
    485465    didChange();
    486466
     
    715695}
    716696
    717 int VTTCue::calculateComputedLinePosition()
     697int VTTCue::calculateComputedLinePosition() const
    718698{
    719699    // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-computed-line-position
     
    721701    // If the text track cue line position is numeric, then that is the text
    722702    // track cue computed line position.
    723     if (!std::isnan(m_linePosition))
    724         return m_linePosition;
     703    if (m_linePosition)
     704        return *m_linePosition;
    725705
    726706    // If the text track cue snap-to-lines flag of the text track cue is not
     
    755735    // such as U+000A LINE FEED (LF), U+0085 NEXT LINE (NEL), and U+2029 PARAGRAPH SEPARATOR.
    756736    return u_charType(character) == U_PARAGRAPH_SEPARATOR;
    757 }
    758 
    759 bool VTTCue::textPositionIsAuto() const
    760 {
    761     return std::isnan(m_textPosition);
    762737}
    763738
     
    811786    // 1. If the position is numeric, then return the value of the position and
    812787    // abort these steps. (Otherwise, the position is the special value auto.)
    813     if (!textPositionIsAuto())
    814         return m_textPosition;
     788    if (m_textPosition)
     789        return *m_textPosition;
    815790   
    816791    switch (m_cueAlignment) {
     
    922897    // yet calculated for cue as per the appropriate rules from the following
    923898    // list:
    924     if (m_snapToLines && m_displayPosition.second == undefinedPosition && m_writingDirection == Horizontal)
     899    if (m_snapToLines && !m_displayPosition.second && m_writingDirection == Horizontal)
    925900        m_displayPosition.second = 0;
    926901
    927     if (!m_snapToLines && m_displayPosition.second == undefinedPosition && m_writingDirection == Horizontal)
    928         m_displayPosition.second = m_computedLinePosition;
    929 
    930     if (m_snapToLines && m_displayPosition.first == undefinedPosition
     902    if (!m_snapToLines && !m_displayPosition.second && m_writingDirection == Horizontal)
     903        m_displayPosition.second = *m_computedLinePosition;
     904
     905    if (m_snapToLines && !m_displayPosition.first
    931906        && (m_writingDirection == VerticalGrowingLeft || m_writingDirection == VerticalGrowingRight))
    932907        m_displayPosition.first = 0;
    933908
    934909    if (!m_snapToLines && (m_writingDirection == VerticalGrowingLeft || m_writingDirection == VerticalGrowingRight))
    935         m_displayPosition.first = m_computedLinePosition;
     910        m_displayPosition.first = *m_computedLinePosition;
    936911}
    937912   
     
    10771052
    10781053    auto textPosition = calculateComputedTextPosition();
     1054    auto computedLinePosition = m_computedLinePosition ? *m_computedLinePosition : calculateComputedLinePosition();
    10791055   
    10801056    if (m_writingDirection == Horizontal && m_displayDirection == CSSValueLtr) {
    10811057        coordinates.first = textPosition;
    1082         coordinates.second = m_computedLinePosition;
     1058        coordinates.second = computedLinePosition;
    10831059
    10841060        return coordinates;
     
    10871063    if (m_writingDirection == Horizontal && m_displayDirection == CSSValueRtl) {
    10881064        coordinates.first = 100 - textPosition;
    1089         coordinates.second = m_computedLinePosition;
     1065        coordinates.second = computedLinePosition;
    10901066
    10911067        return coordinates;
     
    10931069
    10941070    if (m_writingDirection == VerticalGrowingLeft) {
    1095         coordinates.first = 100 - m_computedLinePosition;
     1071        coordinates.first = 100 - *m_computedLinePosition;
    10961072        coordinates.second = textPosition;
    10971073
     
    11001076
    11011077    if (m_writingDirection == VerticalGrowingRight) {
    1102         coordinates.first = m_computedLinePosition;
     1078        coordinates.first = computedLinePosition;
    11031079        coordinates.second = textPosition;
    11041080
     
    13201296}
    13211297
    1322 std::pair<double, double> VTTCue::getCSSPosition() const
    1323 {
    1324     if (!m_snapToLines)
    1325         return getPositionCoordinates();
    1326 
    1327     return m_displayPosition;
    1328 }
    1329 
    13301298bool VTTCue::cueContentsMatch(const TextTrackCue& otherTextTrackCue) const
    13311299{
     
    13611329    object.setString("vertical"_s, vertical());
    13621330    object.setBoolean("snapToLines"_s, snapToLines());
    1363     object.setDouble("line"_s, m_linePosition);
    1364     if (textPositionIsAuto())
     1331    if (m_linePosition)
     1332        object.setString("line"_s, "auto"_s);
     1333    else
     1334        object.setDouble("line"_s, *m_linePosition);
     1335    if (m_textPosition)
     1336        object.setDouble("position"_s, *m_textPosition);
     1337    else
    13651338        object.setString("position"_s, "auto"_s);
    1366     else
    1367         object.setDouble("position"_s, m_textPosition);
    13681339    object.setInteger("size"_s, m_cueSize);
    13691340    object.setString("align"_s, align());
  • trunk/Source/WebCore/html/track/VTTCue.h

    r284075 r286743  
    137137    void markFutureAndPastNodes(ContainerNode*, const MediaTime&, const MediaTime&);
    138138
    139     int calculateComputedLinePosition();
     139    int calculateComputedLinePosition() const;
    140140    std::pair<double, double> getPositionCoordinates() const;
    141141
    142     std::pair<double, double> getCSSPosition() const;
     142    using DisplayPosition = std::pair<std::optional<double>, std::optional<double>>;
     143    const DisplayPosition& getCSSPosition() const { return m_displayPosition; };
    143144
    144145    CSSValueID getCSSAlignment() const;
     
    207208
    208209    void parseSettings(const String&);
    209 
    210     bool textPositionIsAuto() const;
    211210
    212211    void determineTextDirection();
     
    224223    CueSetting settingName(VTTScanner&);
    225224
    226     static constexpr double undefinedPosition = -1;
    227 
    228225    String m_content;
    229226    String m_settings;
    230     double m_linePosition { std::numeric_limits<double>::quiet_NaN() };
    231     double m_computedLinePosition { std::numeric_limits<double>::quiet_NaN() };
    232     double m_textPosition { std::numeric_limits<double>::quiet_NaN() };
     227    std::optional<double> m_linePosition;
     228    std::optional<double> m_computedLinePosition;
     229    std::optional<double> m_textPosition;
    233230    int m_cueSize { 100 };
    234231
     
    246243    CSSValueID m_displayDirection { CSSValueLtr };
    247244    int m_displaySize { 0 };
    248     std::pair<float, float> m_displayPosition;
     245    DisplayPosition m_displayPosition;
    249246
    250247    MediaTime m_originalStartTime;
  • trunk/Source/WebCore/page/CaptionUserPreferences.cpp

    r284163 r286743  
    9898{
    9999    m_displayMode = mode;
    100     if (m_testingMode && mode != AlwaysOn) {
     100    if (testingMode() && mode != AlwaysOn) {
    101101        setUserPrefersCaptions(false);
    102102        setUserPrefersSubtitles(false);
     
    177177{
    178178    Vector<String> languages = userPreferredLanguages(ShouldMinimizeLanguages::No);
    179     if (m_testingMode && !m_userPreferredLanguage.isEmpty())
     179    if (testingMode() && !m_userPreferredLanguage.isEmpty())
    180180        languages.insert(0, m_userPreferredLanguage);
    181181
  • trunk/Source/WebCore/page/CaptionUserPreferences.h

    r284163 r286743  
    3737namespace WebCore {
    3838
     39class CaptionUserPreferencesTestingModeToken;
    3940class HTMLMediaElement;
    4041class Page;
     
    99100    String primaryAudioTrackLanguageOverride() const;
    100101
    101     virtual bool testingMode() const { return m_testingMode; }
    102     void setTestingMode(bool override) { m_testingMode = override; }
     102    virtual bool testingMode() const { return m_testingModeCount; }
     103
     104    friend class CaptionUserPreferencesTestingModeToken;
     105    UniqueRef<CaptionUserPreferencesTestingModeToken> createTestingModeToken() { return makeUniqueRef<CaptionUserPreferencesTestingModeToken>(*this); }
    103106   
    104107    PageGroup& pageGroup() const { return m_pageGroup; }
     
    112115
    113116private:
     117    void incrementTestingModeCount() { ++m_testingModeCount; }
     118    void decrementTestingModeCount()
     119    {
     120        ASSERT(m_testingModeCount);
     121        if (m_testingModeCount)
     122            --m_testingModeCount;
     123    }
     124
    114125    void timerFired();
    115126    void notify();
     
    124135    String m_primaryAudioTrackLanguageOverride;
    125136    unsigned m_blockNotificationsCounter { 0 };
    126     bool m_testingMode { false };
    127137    bool m_havePreferences { false };
     138    unsigned m_testingModeCount { 0 };
     139};
     140
     141class CaptionUserPreferencesTestingModeToken {
     142    WTF_MAKE_FAST_ALLOCATED;
     143public:
     144    CaptionUserPreferencesTestingModeToken(CaptionUserPreferences& parent)
     145        : m_parent(parent)
     146    {
     147        parent.incrementTestingModeCount();
     148    }
     149    ~CaptionUserPreferencesTestingModeToken()
     150    {
     151        if (m_parent)
     152            m_parent->decrementTestingModeCount();
     153    }
     154private:
     155    WeakPtr<CaptionUserPreferences> m_parent;
    128156};
    129157   
  • trunk/Source/WebCore/rendering/RenderObject.h

    r286672 r286743  
    270270
    271271    virtual bool isRenderScrollbarPart() const { return false; }
     272    virtual bool isRenderVTTCue() const { return false; }
    272273
    273274    bool isDocumentElementRenderer() const { return document().documentElement() == &m_node; }
  • trunk/Source/WebCore/rendering/RenderVTTCue.cpp

    r278405 r286743  
    6161        return;
    6262
    63     LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
     63    LayoutStateMaintainer statePusher(*this, locationOffset(), true);
    6464
    6565    if (m_cue->cueType()== TextTrackCue::WebVTT) {
     
    8080    RenderBlock* parentBlock = containingBlock();
    8181
    82     // firstChild() returns the wrapping (backdrop) <div>. The cue object is
    83     // the <div>'s first child.
    84     RenderObject& firstChild = *this->firstChild();
    85     RenderElement& backdropElement = downcast<RenderElement>(firstChild);
    86    
    87     firstLineBox = downcast<RenderInline>(*backdropElement.firstChild()).firstLineBox();
     82    firstLineBox = cueBox().firstLineBox();
    8883    if (!firstLineBox)
    8984        firstLineBox = this->firstRootBox();
     
    9287    //    Vertical: Let step be the width of the first line box in boxes.
    9388    step = m_cue->getWritingDirection() == VTTCue::Horizontal ? firstLineBox->height() : firstLineBox->width();
     89
     90    // Note: the previous rules in initializeLayoutParameters() only account for
     91    // the height of the line boxes contained within the cue, and not the cue's height
     92    // nor its padding, nor its borders. Ignoring these will lead to errors
     93    // in the initial placement of cues, as the resulting placement will result in
     94    // the cue always being partially outside its containing block, rather than at
     95    // its initial position. Correct the initial position by subtracting from
     96    // position the difference between the the logicalHeight of the cue and its
     97    // first line box.
     98    auto& backdropBox = this->backdropBox();
     99    auto lineBoxHeights = firstLineBox->logicalHeight();
     100    for (auto nextLineBox = firstLineBox->nextLineBox(); nextLineBox; nextLineBox = nextLineBox->nextLineBox())
     101        lineBoxHeights += nextLineBox->logicalHeight();
     102
     103    auto logicalHeightDelta = backdropBox.logicalHeight() - lineBoxHeights;
     104    if (logicalHeightDelta > 0)
     105        step += logicalHeightDelta;
    94106
    95107    // 2. If step is zero, then jump to the step labeled done positioning below.
     
    147159bool RenderVTTCue::isOutside() const
    148160{
    149     return !rectIsWithinContainer(absoluteContentBox());
     161    return !rectIsWithinContainer(backdropBox().absoluteBoundingBoxRect());
    150162}
    151163
     
    161173}
    162174
    163 RenderObject* RenderVTTCue::overlappingObject() const
    164 {
    165     return overlappingObjectForRect(absoluteBoundingBoxRect());
    166 }
    167 
    168 RenderObject* RenderVTTCue::overlappingObjectForRect(const IntRect& rect) const
    169 {
    170     for (RenderObject* box = previousSibling(); box; box = box->previousSibling()) {
    171         IntRect boxRect = box->absoluteBoundingBoxRect();
    172 
    173         if (rect.intersects(boxRect))
    174             return box;
     175RenderVTTCue* RenderVTTCue::overlappingObject() const
     176{
     177    return overlappingObjectForRect(backdropBox().absoluteBoundingBoxRect());
     178}
     179
     180RenderVTTCue* RenderVTTCue::overlappingObjectForRect(const IntRect& rect) const
     181{
     182    for (RenderObject* sibling = previousSibling(); sibling; sibling = sibling->previousSibling()) {
     183        auto* previousCue = downcast<RenderVTTCue>(sibling);
     184        if (!previousCue)
     185            continue;
     186
     187        if (rect.intersects(previousCue->backdropBox().absoluteBoundingBoxRect()))
     188            return previousCue;
    175189    }
    176190
     
    244258{
    245259    IntRect containerRect = containingBlock()->absoluteBoundingBoxRect();
    246     IntRect cueRect = absoluteBoundingBoxRect();
     260    IntRect cueRect = backdropBox().absoluteBoundingBoxRect();
    247261
    248262    int topOverflow = cueRect.y() - containerRect.y();
     
    275289    newX = x();
    276290    newY = y();
    277     IntRect srcRect = absoluteBoundingBoxRect();
     291    IntRect srcRect = backdropBox().absoluteBoundingBoxRect();
    278292    IntRect destRect = srcRect;
    279293
    280294    // Move the box up, looking for a non-overlapping position:
    281     while (RenderObject* box = overlappingObjectForRect(destRect)) {
     295    while (RenderVTTCue* cue = overlappingObjectForRect(destRect)) {
    282296        if (m_cue->getWritingDirection() == VTTCue::Horizontal)
    283             destRect.setY(box->absoluteBoundingBoxRect().y() - destRect.height());
     297            destRect.setY(cue->backdropBox().absoluteBoundingBoxRect().y() - destRect.height());
    284298        else
    285             destRect.setX(box->absoluteBoundingBoxRect().x() - destRect.width());
     299            destRect.setX(cue->backdropBox().absoluteBoundingBoxRect().x() - destRect.width());
    286300    }
    287301
     
    295309
    296310    // Move the box down, looking for a non-overlapping position:
    297     while (RenderObject* box = overlappingObjectForRect(destRect)) {
     311    while (RenderVTTCue* cue = overlappingObjectForRect(destRect)) {
    298312        if (m_cue->getWritingDirection() == VTTCue::Horizontal)
    299             destRect.setY(box->absoluteBoundingBoxRect().maxY());
     313            destRect.setY(cue->backdropBox().absoluteBoundingBoxRect().maxY());
    300314        else
    301             destRect.setX(box->absoluteBoundingBoxRect().maxX());
     315            destRect.setX(cue->backdropBox().absoluteBoundingBoxRect().maxX());
    302316    }
    303317
     
    346360    ASSERT(firstChild());
    347361
    348     // firstChild() returns the wrapping (backdrop) <div>. The cue object is
    349     // the <div>'s first child.
    350     RenderObject& firstChild = *this->firstChild();
    351     RenderElement& backdropElement = downcast<RenderElement>(firstChild);
    352    
    353     LegacyInlineFlowBox* firstLineBox = downcast<RenderInline>(*backdropElement.firstChild()).firstLineBox();
     362    LegacyInlineFlowBox* firstLineBox = cueBox().firstLineBox();
    354363    if (downcast<TextTrackCueGeneric>(*m_cue).useDefaultPosition() && firstLineBox) {
    355364        LayoutUnit parentWidth = containingBlock()->logicalWidth();
     
    385394}
    386395
     396RenderBlockFlow& RenderVTTCue::backdropBox() const
     397{
     398    ASSERT(firstChild());
     399
     400    // firstChild() returns the wrapping (backdrop) <div>. The cue object is
     401    // the <div>'s first child.
     402    RenderObject& firstChild = *this->firstChild();
     403    return downcast<RenderBlockFlow>(firstChild);
     404}
     405
     406RenderInline& RenderVTTCue::cueBox() const
     407{
     408    return downcast<RenderInline>(*backdropBox().firstChild());
     409}
     410
    387411} // namespace WebCore
    388412
  • trunk/Source/WebCore/rendering/RenderVTTCue.h

    r278405 r286743  
    4343
    4444private:
     45    bool isRenderVTTCue() const final { return true; }
     46
    4547    void layout() override;
    4648
     
    4850    bool rectIsWithinContainer(const IntRect&) const;
    4951    bool isOverlapping() const;
    50     RenderObject* overlappingObject() const;
    51     RenderObject* overlappingObjectForRect(const IntRect&) const;
     52    RenderVTTCue* overlappingObject() const;
     53    RenderVTTCue* overlappingObjectForRect(const IntRect&) const;
    5254    bool shouldSwitchDirection(LegacyInlineFlowBox*, LayoutUnit) const;
    5355
     
    6365    void repositionGenericCue();
    6466
     67    RenderBlockFlow& backdropBox() const;
     68    RenderInline& cueBox() const;
     69
    6570    VTTCue* m_cue;
    6671    FloatPoint m_fallbackPosition;
     
    6974} // namespace WebCore
    7075
     76SPECIALIZE_TYPE_TRAITS_RENDER_OBJECT(RenderVTTCue, isRenderVTTCue())
     77
    7178#endif // ENABLE(VIDEO)
  • trunk/Source/WebCore/testing/Internals.cpp

    r286625 r286743  
    546546    page.applicationCacheStorage().setDefaultOriginQuota(ApplicationCacheStorage::noQuota());
    547547#if ENABLE(VIDEO)
    548     page.group().ensureCaptionPreferences().setTestingMode(true);
    549548    page.group().ensureCaptionPreferences().setCaptionDisplayMode(CaptionUserPreferences::ForcedOnly);
    550549    page.group().ensureCaptionPreferences().setCaptionsStyleSheetOverride(emptyString());
    551     page.group().ensureCaptionPreferences().setTestingMode(false);
    552550    PlatformMediaSessionManager::sharedManager().resetHaveEverRegisteredAsNowPlayingApplicationForTesting();
    553551    PlatformMediaSessionManager::sharedManager().resetRestrictions();
     
    625623#endif
    626624{
    627 #if ENABLE(VIDEO)
    628     if (document.page())
    629         document.page()->group().ensureCaptionPreferences().setTestingMode(true);
    630 #endif
    631 
    632625#if ENABLE(WIRELESS_PLAYBACK_TARGET)
    633626    if (document.page())
    634627        document.page()->setMockMediaPlaybackTargetPickerEnabled(true);
     628#endif
     629
     630#if ENABLE(VIDEO)
     631    if (document.page())
     632        m_testingModeToken = document.page()->group().ensureCaptionPreferences().createTestingModeToken().moveToUniquePtr();
    635633#endif
    636634
  • trunk/Source/WebCore/testing/Internals.h

    r286265 r286743  
    5757class Blob;
    5858class CacheStorageConnection;
     59class CaptionUserPreferencesTestingModeToken;
    5960class DOMPointReadOnly;
    6061class DOMRect;
     
    12631264    RefPtr<MockMediaSessionCoordinator> m_mockMediaSessionCoordinator;
    12641265#endif
     1266#if ENABLE(VIDEO)
     1267    std::unique_ptr<CaptionUserPreferencesTestingModeToken> m_testingModeToken;
     1268#endif
    12651269};
    12661270
Note: See TracChangeset for help on using the changeset viewer.