1/*
2 * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if ENABLE(VIDEO_TRACK)
29
30#include "TextTrackCueGeneric.h"
31
32#include "CSSPropertyNames.h"
33#include "CSSStyleDeclaration.h"
34#include "CSSValueKeywords.h"
35#include "HTMLNames.h"
36#include "HTMLSpanElement.h"
37#include "InbandTextTrackPrivateClient.h"
38#include "Logging.h"
39#include "RenderObject.h"
40#include "ScriptExecutionContext.h"
41#include "StyleProperties.h"
42#include "TextTrackCue.h"
43#include <wtf/MathExtras.h>
44
45namespace WebCore {
46
47// This default value must be the same as the one specified in mediaControlsApple.css for -webkit-media-controls-closed-captions-container
48const static int DEFAULTCAPTIONFONTSIZE = 10;
49
50class TextTrackCueGenericBoxElement final : public VTTCueBox {
51public:
52    static PassRefPtr<TextTrackCueGenericBoxElement> create(Document& document, TextTrackCueGeneric& cue)
53    {
54        return adoptRef(new TextTrackCueGenericBoxElement(document, cue));
55    }
56
57    virtual void applyCSSProperties(const IntSize&) override;
58
59private:
60    TextTrackCueGenericBoxElement(Document&, VTTCue&);
61};
62
63TextTrackCueGenericBoxElement::TextTrackCueGenericBoxElement(Document& document, VTTCue& cue)
64    : VTTCueBox(document, cue)
65{
66}
67
68void TextTrackCueGenericBoxElement::applyCSSProperties(const IntSize& videoSize)
69{
70    setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute);
71    setInlineStyleProperty(CSSPropertyUnicodeBidi, CSSValueWebkitPlaintext);
72
73    TextTrackCueGeneric* cue = static_cast<TextTrackCueGeneric*>(getCue());
74    RefPtr<HTMLSpanElement> cueElement = cue->element();
75
76    CSSValueID alignment = cue->getCSSAlignment();
77    float size = static_cast<float>(cue->getCSSSize());
78    if (cue->useDefaultPosition()) {
79        setInlineStyleProperty(CSSPropertyBottom, 0, CSSPrimitiveValue::CSS_PX);
80        setInlineStyleProperty(CSSPropertyMarginBottom, 1.0, CSSPrimitiveValue::CSS_PERCENTAGE);
81    } else {
82        setInlineStyleProperty(CSSPropertyLeft, static_cast<float>(cue->position()), CSSPrimitiveValue::CSS_PERCENTAGE);
83        setInlineStyleProperty(CSSPropertyTop, static_cast<float>(cue->line()), CSSPrimitiveValue::CSS_PERCENTAGE);
84
85        double authorFontSize = videoSize.height() * cue->baseFontSizeRelativeToVideoHeight() / 100.0;
86        if (!authorFontSize)
87            authorFontSize = DEFAULTCAPTIONFONTSIZE;
88
89        if (cue->fontSizeMultiplier())
90            authorFontSize *= cue->fontSizeMultiplier() / 100;
91
92        double multiplier = m_fontSizeFromCaptionUserPrefs / authorFontSize;
93        double newCueSize = std::min(size * multiplier, 100.0);
94        if (cue->getWritingDirection() == VTTCue::Horizontal) {
95            setInlineStyleProperty(CSSPropertyWidth, newCueSize, CSSPrimitiveValue::CSS_PERCENTAGE);
96            if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0)
97                setInlineStyleProperty(CSSPropertyLeft, static_cast<double>(cue->position() - (newCueSize - m_cue.getCSSSize()) / 2), CSSPrimitiveValue::CSS_PERCENTAGE);
98        } else {
99            setInlineStyleProperty(CSSPropertyHeight, newCueSize,  CSSPrimitiveValue::CSS_PERCENTAGE);
100            if ((alignment == CSSValueMiddle || alignment == CSSValueCenter) && multiplier != 1.0)
101                setInlineStyleProperty(CSSPropertyTop, static_cast<double>(cue->line() - (newCueSize - m_cue.getCSSSize()) / 2), CSSPrimitiveValue::CSS_PERCENTAGE);
102        }
103    }
104
105    double textPosition = m_cue.position();
106    double maxSize = 100.0;
107
108    if (alignment == CSSValueEnd || alignment == CSSValueRight)
109        maxSize = textPosition;
110    else if (alignment == CSSValueStart || alignment == CSSValueLeft)
111        maxSize = 100.0 - textPosition;
112
113    if (cue->getWritingDirection() == VTTCue::Horizontal) {
114        setInlineStyleProperty(CSSPropertyMinWidth, "-webkit-min-content");
115        setInlineStyleProperty(CSSPropertyMaxWidth, maxSize, CSSPrimitiveValue::CSS_PERCENTAGE);
116    } else {
117        setInlineStyleProperty(CSSPropertyMinHeight, "-webkit-min-content");
118        setInlineStyleProperty(CSSPropertyMaxHeight, maxSize, CSSPrimitiveValue::CSS_PERCENTAGE);
119    }
120
121    if (cue->foregroundColor().isValid())
122        cueElement->setInlineStyleProperty(CSSPropertyColor, cue->foregroundColor().serialized());
123    if (cue->highlightColor().isValid())
124        cueElement->setInlineStyleProperty(CSSPropertyBackgroundColor, cue->highlightColor().serialized());
125
126    if (cue->getWritingDirection() == VTTCue::Horizontal)
127        setInlineStyleProperty(CSSPropertyHeight, CSSValueAuto);
128    else
129        setInlineStyleProperty(CSSPropertyWidth, CSSValueAuto);
130
131    if (cue->baseFontSizeRelativeToVideoHeight())
132        cue->setFontSize(cue->baseFontSizeRelativeToVideoHeight(), videoSize, false);
133
134    if (cue->getAlignment() == VTTCue::Middle)
135        setInlineStyleProperty(CSSPropertyTextAlign, CSSValueCenter);
136    else if (cue->getAlignment() == VTTCue::End)
137        setInlineStyleProperty(CSSPropertyTextAlign, CSSValueEnd);
138    else
139        setInlineStyleProperty(CSSPropertyTextAlign, CSSValueStart);
140
141    if (cue->backgroundColor().isValid())
142        setInlineStyleProperty(CSSPropertyBackgroundColor, cue->backgroundColor().serialized());
143    setInlineStyleProperty(CSSPropertyWebkitWritingMode, cue->getCSSWritingMode(), false);
144    setInlineStyleProperty(CSSPropertyWhiteSpace, CSSValuePreWrap);
145}
146
147TextTrackCueGeneric::TextTrackCueGeneric(ScriptExecutionContext& context, double start, double end, const String& content)
148    : VTTCue(context, start, end, content)
149    , m_baseFontSizeRelativeToVideoHeight(0)
150    , m_fontSizeMultiplier(0)
151    , m_defaultPosition(true)
152{
153}
154
155PassRefPtr<VTTCueBox> TextTrackCueGeneric::createDisplayTree()
156{
157    return TextTrackCueGenericBoxElement::create(ownerDocument(), *this);
158}
159
160void TextTrackCueGeneric::setLine(double line, ExceptionCode& ec)
161{
162    m_defaultPosition = false;
163    VTTCue::setLine(line, ec);
164}
165
166void TextTrackCueGeneric::setPosition(double position, ExceptionCode& ec)
167{
168    m_defaultPosition = false;
169    VTTCue::setPosition(position, ec);
170}
171
172void TextTrackCueGeneric::setFontSize(int fontSize, const IntSize& videoSize, bool important)
173{
174    if (!hasDisplayTree() || !fontSize)
175        return;
176
177    if (important || !baseFontSizeRelativeToVideoHeight()) {
178        VTTCue::setFontSize(fontSize, videoSize, important);
179        return;
180    }
181
182    double size = videoSize.height() * baseFontSizeRelativeToVideoHeight() / 100;
183    if (fontSizeMultiplier())
184        size *= fontSizeMultiplier() / 100;
185    displayTreeInternal()->setInlineStyleProperty(CSSPropertyFontSize, lround(size), CSSPrimitiveValue::CSS_PX);
186
187    LOG(Media, "TextTrackCueGeneric::setFontSize - setting cue font size to %li", lround(size));
188}
189
190bool TextTrackCueGeneric::cueContentsMatch(const TextTrackCue& cue) const
191{
192    // Do call the parent class cueContentsMatch here, because we want to confirm
193    // the content of the two cues are identical (even though the types are not the same).
194    if (!VTTCue::cueContentsMatch(cue))
195        return false;
196
197    const TextTrackCueGeneric* other = static_cast<const TextTrackCueGeneric*>(&cue);
198
199    if (m_baseFontSizeRelativeToVideoHeight != other->baseFontSizeRelativeToVideoHeight())
200        return false;
201    if (m_fontSizeMultiplier != other->fontSizeMultiplier())
202        return false;
203    if (m_fontName != other->fontName())
204        return false;
205    if (m_foregroundColor != other->foregroundColor())
206        return false;
207    if (m_backgroundColor != other->backgroundColor())
208        return false;
209
210    return true;
211}
212
213bool TextTrackCueGeneric::isEqual(const TextTrackCue& cue, TextTrackCue::CueMatchRules match) const
214{
215    // Do not call the parent class isEqual here, because we are not cueType() == VTTCue,
216    // and will fail that equality test.
217    if (!TextTrackCue::isEqual(cue, match))
218        return false;
219
220    if (cue.cueType() != TextTrackCue::Generic)
221        return false;
222
223    return cueContentsMatch(cue);
224}
225
226
227bool TextTrackCueGeneric::doesExtendCue(const TextTrackCue& cue) const
228{
229    if (!cueContentsMatch(cue))
230        return false;
231
232    return VTTCue::doesExtendCue(cue);
233}
234
235bool TextTrackCueGeneric::isOrderedBefore(const TextTrackCue* that) const
236{
237    if (VTTCue::isOrderedBefore(that))
238        return true;
239
240    if (that->cueType() == Generic && startTime() == that->startTime() && endTime() == that->endTime()) {
241        // Further order generic cues by their calculated line value.
242        std::pair<double, double> thisPosition = getPositionCoordinates();
243        std::pair<double, double> thatPosition = toVTTCue(that)->getPositionCoordinates();
244        return thisPosition.second > thatPosition.second || (thisPosition.second == thatPosition.second && thisPosition.first < thatPosition.first);
245    }
246
247    return false;
248}
249
250bool TextTrackCueGeneric::isPositionedAbove(const TextTrackCue* that) const
251{
252    if (that->cueType() == Generic && startTime() == that->startTime() && endTime() == that->endTime()) {
253        // Further order generic cues by their calculated line value.
254        std::pair<double, double> thisPosition = getPositionCoordinates();
255        std::pair<double, double> thatPosition = toVTTCue(that)->getPositionCoordinates();
256        return thisPosition.second > thatPosition.second || (thisPosition.second == thatPosition.second && thisPosition.first < thatPosition.first);
257    }
258
259    if (that->cueType() == Generic)
260        return startTime() > that->startTime();
261
262    return VTTCue::isOrderedBefore(that);
263}
264
265} // namespace WebCore
266
267#endif
268