OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. | |
3 * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmo
bile.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 US
A | |
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 "core/rendering/RenderMarquee.h" | |
48 | |
49 #include "core/HTMLNames.h" | |
50 #include "core/html/HTMLMarqueeElement.h" | |
51 #include "core/frame/FrameView.h" | |
52 #include "core/frame/UseCounter.h" | |
53 #include "core/rendering/RenderLayer.h" | |
54 #include "core/rendering/RenderView.h" | |
55 #include "platform/LengthFunctions.h" | |
56 | |
57 namespace blink { | |
58 | |
59 using namespace HTMLNames; | |
60 | |
61 RenderMarquee::RenderMarquee(HTMLMarqueeElement* element) | |
62 : RenderBlockFlow(element) | |
63 , m_currentLoop(0) | |
64 , m_totalLoops(0) | |
65 , m_timer(element, &HTMLMarqueeElement::timerFired) | |
66 , m_start(0) | |
67 , m_end(0) | |
68 , m_speed(0) | |
69 , m_reset(false) | |
70 , m_suspended(false) | |
71 , m_stopped(false) | |
72 , m_direction(MAUTO) | |
73 { | |
74 UseCounter::count(document(), UseCounter::HTMLMarqueeElement); | |
75 } | |
76 | |
77 RenderMarquee::~RenderMarquee() | |
78 { | |
79 } | |
80 | |
81 int RenderMarquee::marqueeSpeed() const | |
82 { | |
83 int result = style()->marqueeSpeed(); | |
84 if (Node* node = this->node()) | |
85 result = std::max(result, toHTMLMarqueeElement(node)->minimumDelay()); | |
86 return result; | |
87 } | |
88 | |
89 EMarqueeDirection RenderMarquee::direction() const | |
90 { | |
91 // FIXME: Support the CSS3 "auto" value for determining the direction of the
marquee. | |
92 // For now just map MAUTO to MBACKWARD | |
93 EMarqueeDirection result = style()->marqueeDirection(); | |
94 TextDirection dir = style()->direction(); | |
95 if (result == MAUTO) | |
96 result = MBACKWARD; | |
97 if (result == MFORWARD) | |
98 result = (dir == LTR) ? MRIGHT : MLEFT; | |
99 if (result == MBACKWARD) | |
100 result = (dir == LTR) ? MLEFT : MRIGHT; | |
101 | |
102 // Now we have the real direction. Next we check to see if the increment is
negative. | |
103 // If so, then we reverse the direction. | |
104 Length increment = style()->marqueeIncrement(); | |
105 if (increment.isNegative()) | |
106 result = static_cast<EMarqueeDirection>(-result); | |
107 | |
108 return result; | |
109 } | |
110 | |
111 bool RenderMarquee::isHorizontal() const | |
112 { | |
113 return direction() == MLEFT || direction() == MRIGHT; | |
114 } | |
115 | |
116 int RenderMarquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge
) | |
117 { | |
118 if (isHorizontal()) { | |
119 bool ltr = style()->isLeftToRightDirection(); | |
120 LayoutUnit clientWidth = this->clientWidth(); | |
121 LayoutUnit contentWidth = ltr ? maxPreferredLogicalWidth() : minPreferre
dLogicalWidth(); | |
122 if (ltr) | |
123 contentWidth += (paddingRight() - borderLeft()); | |
124 else { | |
125 contentWidth = width() - contentWidth; | |
126 contentWidth += (paddingLeft() - borderRight()); | |
127 } | |
128 if (dir == MRIGHT) { | |
129 if (stopAtContentEdge) | |
130 return std::max<LayoutUnit>(0, ltr ? (contentWidth - clientWidth
) : (clientWidth - contentWidth)); | |
131 else | |
132 return ltr ? contentWidth : clientWidth; | |
133 } | |
134 else { | |
135 if (stopAtContentEdge) | |
136 return std::min<LayoutUnit>(0, ltr ? (contentWidth - clientWidth
) : (clientWidth - contentWidth)); | |
137 else | |
138 return ltr ? -clientWidth : -contentWidth; | |
139 } | |
140 } | |
141 else { | |
142 int contentHeight = layoutOverflowRect().maxY() - borderTop() + paddingB
ottom(); | |
143 int clientHeight = this->clientHeight(); | |
144 if (dir == MUP) { | |
145 if (stopAtContentEdge) | |
146 return std::min(contentHeight - clientHeight, 0); | |
147 else | |
148 return -clientHeight; | |
149 } | |
150 else { | |
151 if (stopAtContentEdge) | |
152 return std::max(contentHeight - clientHeight, 0); | |
153 else | |
154 return contentHeight; | |
155 } | |
156 } | |
157 } | |
158 | |
159 void RenderMarquee::start() | |
160 { | |
161 if (m_timer.isActive() || style()->marqueeIncrement().isZero()) | |
162 return; | |
163 | |
164 if (!m_suspended && !m_stopped) { | |
165 if (isHorizontal()) | |
166 layer()->scrollableArea()->scrollToOffset(IntSize(m_start, 0)); | |
167 else | |
168 layer()->scrollableArea()->scrollToOffset(IntSize(0, m_start)); | |
169 } else { | |
170 m_suspended = false; | |
171 m_stopped = false; | |
172 } | |
173 | |
174 m_timer.startRepeating(speed() * 0.001, FROM_HERE); | |
175 } | |
176 | |
177 void RenderMarquee::suspend() | |
178 { | |
179 m_timer.stop(); | |
180 m_suspended = true; | |
181 } | |
182 | |
183 void RenderMarquee::stop() | |
184 { | |
185 m_timer.stop(); | |
186 m_stopped = true; | |
187 } | |
188 | |
189 void RenderMarquee::updateMarqueePosition() | |
190 { | |
191 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops); | |
192 if (activate) { | |
193 EMarqueeBehavior behavior = style()->marqueeBehavior(); | |
194 m_start = computePosition(direction(), behavior == MALTERNATE); | |
195 m_end = computePosition(reverseDirection(), behavior == MALTERNATE || be
havior == MSLIDE); | |
196 if (!m_stopped) { | |
197 // Hits in compositing/overflow/do-not-repaint-if-scrolling-composit
ed-layers.html during layout. | |
198 DisableCompositingQueryAsserts disabler; | |
199 start(); | |
200 } | |
201 } | |
202 } | |
203 | |
204 const char* RenderMarquee::renderName() const | |
205 { | |
206 if (isFloating()) | |
207 return "RenderMarquee (floating)"; | |
208 if (isOutOfFlowPositioned()) | |
209 return "RenderMarquee (positioned)"; | |
210 if (isAnonymous()) | |
211 return "RenderMarquee (generated)"; | |
212 if (isRelPositioned()) | |
213 return "RenderMarquee (relative positioned)"; | |
214 return "RenderMarquee"; | |
215 | |
216 } | |
217 | |
218 void RenderMarquee::styleDidChange(StyleDifference difference, const RenderStyle
* oldStyle) | |
219 { | |
220 RenderBlockFlow::styleDidChange(difference, oldStyle); | |
221 | |
222 RenderStyle* s = style(); | |
223 | |
224 if (m_direction != s->marqueeDirection() || (m_totalLoops != s->marqueeLoopC
ount() && m_currentLoop >= m_totalLoops)) | |
225 m_currentLoop = 0; // When direction changes or our loopCount is a small
er number than our current loop, reset our loop. | |
226 | |
227 m_totalLoops = s->marqueeLoopCount(); | |
228 m_direction = s->marqueeDirection(); | |
229 | |
230 // Hack for WinIE. In WinIE, a value of 0 or lower for the loop count for SL
IDE means to only do | |
231 // one loop. | |
232 if (m_totalLoops <= 0 && s->marqueeBehavior() == MSLIDE) | |
233 m_totalLoops = 1; | |
234 | |
235 // Hack alert: Set the white-space value to nowrap for horizontal marquees w
ith inline children, thus ensuring | |
236 // all the text ends up on one line by default. Limit this hack to the <marq
uee> element to emulate | |
237 // WinIE's behavior. Someone using CSS3 can use white-space: nowrap on their
own to get this effect. | |
238 // Second hack alert: Set the text-align back to auto. WinIE completely igno
res text-align on the | |
239 // marquee element. | |
240 // FIXME: Bring these up with the CSS WG. | |
241 if (isHorizontal() && childrenInline()) { | |
242 s->setWhiteSpace(NOWRAP); | |
243 s->setTextAlign(TASTART); | |
244 } | |
245 | |
246 // Legacy hack - multiple browsers default vertical marquees to 200px tall. | |
247 if (!isHorizontal() && s->height().isAuto()) | |
248 s->setHeight(Length(200, Fixed)); | |
249 | |
250 if (speed() != marqueeSpeed()) { | |
251 m_speed = marqueeSpeed(); | |
252 if (m_timer.isActive()) | |
253 m_timer.startRepeating(speed() * 0.001, FROM_HERE); | |
254 } | |
255 | |
256 // Check the loop count to see if we should now stop. | |
257 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops); | |
258 if (activate && !m_timer.isActive()) | |
259 setNeedsLayoutAndFullPaintInvalidation(); | |
260 else if (!activate && m_timer.isActive()) | |
261 m_timer.stop(); | |
262 } | |
263 | |
264 void RenderMarquee::layoutBlock(bool relayoutChildren) | |
265 { | |
266 RenderBlockFlow::layoutBlock(relayoutChildren); | |
267 | |
268 updateMarqueePosition(); | |
269 } | |
270 | |
271 void RenderMarquee::timerFired() | |
272 { | |
273 // FIXME: Why do we need to check the view and not just the RenderMarquee it
self? | |
274 if (view()->needsLayout()) | |
275 return; | |
276 | |
277 if (m_reset) { | |
278 m_reset = false; | |
279 if (isHorizontal()) | |
280 layer()->scrollableArea()->scrollToXOffset(m_start); | |
281 else | |
282 layer()->scrollableArea()->scrollToYOffset(m_start); | |
283 return; | |
284 } | |
285 | |
286 RenderStyle* s = style(); | |
287 | |
288 int endPoint = m_end; | |
289 int range = m_end - m_start; | |
290 int newPos; | |
291 if (range == 0) | |
292 newPos = m_end; | |
293 else { | |
294 bool addIncrement = direction() == MUP || direction() == MLEFT; | |
295 bool isReversed = s->marqueeBehavior() == MALTERNATE && m_currentLoop %
2; | |
296 if (isReversed) { | |
297 // We're going in the reverse direction. | |
298 endPoint = m_start; | |
299 range = -range; | |
300 addIncrement = !addIncrement; | |
301 } | |
302 bool positive = range > 0; | |
303 int clientSize = (isHorizontal() ? clientWidth() : clientHeight()); | |
304 int increment = abs(intValueForLength(style()->marqueeIncrement(), clien
tSize)); | |
305 int currentPos = (isHorizontal() ? layer()->scrollableArea()->scrollXOff
set() : layer()->scrollableArea()->scrollYOffset()); | |
306 newPos = currentPos + (addIncrement ? increment : -increment); | |
307 if (positive) | |
308 newPos = std::min(newPos, endPoint); | |
309 else | |
310 newPos = std::max(newPos, endPoint); | |
311 } | |
312 | |
313 if (newPos == endPoint) { | |
314 m_currentLoop++; | |
315 if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops) | |
316 m_timer.stop(); | |
317 else if (s->marqueeBehavior() != MALTERNATE) | |
318 m_reset = true; | |
319 } | |
320 | |
321 if (isHorizontal()) | |
322 layer()->scrollableArea()->scrollToXOffset(newPos); | |
323 else | |
324 layer()->scrollableArea()->scrollToYOffset(newPos); | |
325 } | |
326 | |
327 } // namespace blink | |
OLD | NEW |