Update ANGLE
[WebKit-https.git] / Source / WebCore / rendering / RenderMarquee.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
4  *
5  * Portions are Copyright (C) 1998 Netscape Communications Corporation.
6  *
7  * Other contributors:
8  *   Robert O'Callahan <roc+@cs.cmu.edu>
9  *   David Baron <dbaron@fas.harvard.edu>
10  *   Christian Biesinger <cbiesinger@web.de>
11  *   Randall Jesup <rjesup@wgate.com>
12  *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
13  *   Josh Soref <timeless@mac.com>
14  *   Boris Zbarsky <bzbarsky@mit.edu>
15  *
16  * This library is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU Lesser General Public
18  * License as published by the Free Software Foundation; either
19  * version 2.1 of the License, or (at your option) any later version.
20  *
21  * This library is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
24  * Lesser General Public License for more details.
25  *
26  * You should have received a copy of the GNU Lesser General Public
27  * License along with this library; if not, write to the Free Software
28  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
29  *
30  * Alternatively, the contents of this file may be used under the terms
31  * of either the Mozilla Public License Version 1.1, found at
32  * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
33  * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
34  * (the "GPL"), in which case the provisions of the MPL or the GPL are
35  * applicable instead of those above.  If you wish to allow use of your
36  * version of this file only under the terms of one of those two
37  * licenses (the MPL or the GPL) and not to allow others to use your
38  * version of this file under the LGPL, indicate your decision by
39  * deletingthe provisions above and replace them with the notice and
40  * other provisions required by the MPL or the GPL, as the case may be.
41  * If you do not delete the provisions above, a recipient may use your
42  * version of this file under any of the LGPL, the MPL or the GPL.
43  */
44
45 #include "config.h"
46
47 #include "RenderMarquee.h"
48
49 #include "FrameView.h"
50 #include "HTMLMarqueeElement.h"
51 #include "HTMLNames.h"
52 #include "RenderLayer.h"
53 #include "RenderView.h"
54
55 namespace WebCore {
56
57 using namespace HTMLNames;
58
59 RenderMarquee::RenderMarquee(RenderLayer* layer)
60     : m_layer(layer)
61     , m_timer(*this, &RenderMarquee::timerFired)
62 {
63     layer->setConstrainsScrollingToContentEdge(false);
64 }
65
66 RenderMarquee::~RenderMarquee() = default;
67
68 int RenderMarquee::marqueeSpeed() const
69 {
70     int result = m_layer->renderer().style().marqueeSpeed();
71     Element* element = m_layer->renderer().element();
72     if (is<HTMLMarqueeElement>(element))
73         result = std::max(result, downcast<HTMLMarqueeElement>(*element).minimumDelay());
74     return result;
75 }
76
77 static MarqueeDirection reverseDirection(MarqueeDirection direction)
78 {
79     switch (direction) {
80     case MarqueeDirection::Auto:
81         return MarqueeDirection::Auto;
82     case MarqueeDirection::Left:
83         return MarqueeDirection::Right;
84     case MarqueeDirection::Right:
85         return MarqueeDirection::Left;
86     case MarqueeDirection::Up:
87         return MarqueeDirection::Down;
88     case MarqueeDirection::Down:
89         return MarqueeDirection::Up;
90     case MarqueeDirection::Backward:
91         return MarqueeDirection::Forward;
92     case MarqueeDirection::Forward:
93         return MarqueeDirection::Backward;
94     }
95     return MarqueeDirection::Auto;
96 }
97
98 MarqueeDirection RenderMarquee::direction() const
99 {
100     // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee.
101     // For now just map MarqueeDirection::Auto to MarqueeDirection::Backward
102     MarqueeDirection result = m_layer->renderer().style().marqueeDirection();
103     TextDirection dir = m_layer->renderer().style().direction();
104     if (result == MarqueeDirection::Auto)
105         result = MarqueeDirection::Backward;
106     if (result == MarqueeDirection::Forward)
107         result = (dir == TextDirection::LTR) ? MarqueeDirection::Right : MarqueeDirection::Left;
108     if (result == MarqueeDirection::Backward)
109         result = (dir == TextDirection::LTR) ? MarqueeDirection::Left : MarqueeDirection::Right;
110     
111     // Now we have the real direction.  Next we check to see if the increment is negative.
112     // If so, then we reverse the direction.
113     Length increment = m_layer->renderer().style().marqueeIncrement();
114     if (increment.isNegative())
115         result = reverseDirection(result);
116     
117     return result;
118 }
119
120 bool RenderMarquee::isHorizontal() const
121 {
122     return direction() == MarqueeDirection::Left || direction() == MarqueeDirection::Right;
123 }
124
125 int RenderMarquee::computePosition(MarqueeDirection dir, bool stopAtContentEdge)
126 {
127     RenderBox* box = m_layer->renderBox();
128     ASSERT(box);
129     auto& boxStyle = box->style();
130     if (isHorizontal()) {
131         bool ltr = boxStyle.isLeftToRightDirection();
132         LayoutUnit clientWidth = box->clientWidth();
133         LayoutUnit contentWidth = ltr ? box->maxPreferredLogicalWidth() : box->minPreferredLogicalWidth();
134         if (ltr)
135             contentWidth += (box->paddingRight() - box->borderLeft());
136         else {
137             contentWidth = box->width() - contentWidth;
138             contentWidth += (box->paddingLeft() - box->borderRight());
139         }
140         if (dir == MarqueeDirection::Right) {
141             if (stopAtContentEdge)
142                 return std::max<LayoutUnit>(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
143
144             return ltr ? contentWidth : clientWidth;
145         }
146
147         if (stopAtContentEdge)
148             return std::min<LayoutUnit>(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
149
150         return ltr ? -clientWidth : -contentWidth;
151     }
152
153     // Vertical
154     int contentHeight = box->layoutOverflowRect().maxY() - box->borderTop() + box->paddingBottom();
155     int clientHeight = roundToInt(box->clientHeight());
156     if (dir == MarqueeDirection::Up) {
157         if (stopAtContentEdge)
158             return std::min(contentHeight - clientHeight, 0);
159
160         return -clientHeight;
161     }
162
163     if (stopAtContentEdge)
164         return std::max(contentHeight - clientHeight, 0);
165
166     return contentHeight;
167 }
168
169 void RenderMarquee::start()
170 {
171     if (m_timer.isActive() || m_layer->renderer().style().marqueeIncrement().isZero())
172         return;
173
174     if (!m_suspended && !m_stopped) {
175         if (isHorizontal())
176             m_layer->scrollToOffset(ScrollOffset(m_start, 0), ScrollType::Programmatic, ScrollClamping::Unclamped);
177         else
178             m_layer->scrollToOffset(ScrollOffset(0, m_start), ScrollType::Programmatic, ScrollClamping::Unclamped);
179     } else {
180         m_suspended = false;
181         m_stopped = false;
182     }
183
184     m_timer.startRepeating(1_ms * speed());
185 }
186
187 void RenderMarquee::suspend()
188 {
189     m_timer.stop();
190     m_suspended = true;
191 }
192
193 void RenderMarquee::stop()
194 {
195     m_timer.stop();
196     m_stopped = true;
197 }
198
199 void RenderMarquee::updateMarqueePosition()
200 {
201     bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
202     if (activate) {
203         MarqueeBehavior behavior = m_layer->renderer().style().marqueeBehavior();
204         m_start = computePosition(direction(), behavior == MarqueeBehavior::Alternate);
205         m_end = computePosition(reverseDirection(direction()), behavior == MarqueeBehavior::Alternate || behavior == MarqueeBehavior::Slide);
206         if (!m_stopped)
207             start();
208     }
209 }
210
211 void RenderMarquee::updateMarqueeStyle()
212 {
213     auto& style = m_layer->renderer().style();
214     
215     if (m_direction != style.marqueeDirection() || (m_totalLoops != style.marqueeLoopCount() && m_currentLoop >= m_totalLoops))
216         m_currentLoop = 0; // When direction changes or our loopCount is a smaller number than our current loop, reset our loop.
217     
218     m_totalLoops = style.marqueeLoopCount();
219     m_direction = style.marqueeDirection();
220     
221     if (m_layer->renderer().isHTMLMarquee()) {
222         // Hack for WinIE.  In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do
223         // one loop.
224         if (m_totalLoops <= 0 && style.marqueeBehavior() == MarqueeBehavior::Slide)
225             m_totalLoops = 1;
226     }
227     
228     if (speed() != marqueeSpeed()) {
229         m_speed = marqueeSpeed();
230         if (m_timer.isActive())
231             m_timer.startRepeating(1_ms * speed());
232     }
233     
234     // Check the loop count to see if we should now stop.
235     bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
236     if (activate && !m_timer.isActive())
237         m_layer->renderer().setNeedsLayout();
238     else if (!activate && m_timer.isActive())
239         m_timer.stop();
240 }
241
242 void RenderMarquee::timerFired()
243 {
244     if (m_layer->renderer().view().needsLayout())
245         return;
246     
247     if (m_reset) {
248         m_reset = false;
249         if (isHorizontal())
250             m_layer->scrollToXOffset(m_start);
251         else
252             m_layer->scrollToYOffset(m_start);
253         return;
254     }
255     
256     const RenderStyle& style = m_layer->renderer().style();
257     
258     int endPoint = m_end;
259     int range = m_end - m_start;
260     int newPos;
261     if (range == 0)
262         newPos = m_end;
263     else {  
264         bool addIncrement = direction() == MarqueeDirection::Up || direction() == MarqueeDirection::Left;
265         bool isReversed = style.marqueeBehavior() == MarqueeBehavior::Alternate && m_currentLoop % 2;
266         if (isReversed) {
267             // We're going in the reverse direction.
268             endPoint = m_start;
269             range = -range;
270             addIncrement = !addIncrement;
271         }
272         bool positive = range > 0;
273         int clientSize = (isHorizontal() ? roundToInt(m_layer->renderBox()->clientWidth()) : roundToInt(m_layer->renderBox()->clientHeight()));
274         int increment = abs(intValueForLength(m_layer->renderer().style().marqueeIncrement(), clientSize));
275         int currentPos = (isHorizontal() ? m_layer->scrollOffset().x() : m_layer->scrollOffset().y());
276         newPos =  currentPos + (addIncrement ? increment : -increment);
277         if (positive)
278             newPos = std::min(newPos, endPoint);
279         else
280             newPos = std::max(newPos, endPoint);
281     }
282
283     if (newPos == endPoint) {
284         m_currentLoop++;
285         if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops)
286             m_timer.stop();
287         else if (style.marqueeBehavior() != MarqueeBehavior::Alternate)
288             m_reset = true;
289     }
290     
291     if (isHorizontal())
292         m_layer->scrollToXOffset(newPos);
293     else
294         m_layer->scrollToYOffset(newPos);
295 }
296
297 } // namespace WebCore