Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(74)

Side by Side Diff: third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp

Issue 2549443003: Move <marquee> implementation to HTMLMarqueeElement.cpp (Closed)
Patch Set: Address feedback Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * Copyright (C) 2003, 2007, 2010 Apple Inc. All rights reserved. 4 * Copyright (C) 2003, 2007, 2010 Apple Inc. All rights reserved.
5 * 5 *
6 * This library is free software; you can redistribute it and/or 6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public 7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either 8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version. 9 * version 2 of the License, or (at your option) any later version.
10 * 10 *
11 * This library is distributed in the hope that it will be useful, 11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details. 14 * Library General Public License for more details.
15 * 15 *
16 * You should have received a copy of the GNU Library General Public License 16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to 17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA. 19 * Boston, MA 02110-1301, USA.
20 * 20 *
21 */ 21 */
22 22
23 #include "core/html/HTMLMarqueeElement.h" 23 #include "core/html/HTMLMarqueeElement.h"
24 24
25 #include "bindings/core/v8/PrivateScriptRunner.h" 25 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
26 #include "bindings/core/v8/V8HTMLMarqueeElement.h" 26 #include "bindings/core/v8/V8HTMLMarqueeElement.h"
27 #include "core/CSSPropertyNames.h"
27 #include "core/HTMLNames.h" 28 #include "core/HTMLNames.h"
29 #include "core/animation/DocumentTimeline.h"
30 #include "core/animation/KeyframeEffect.h"
31 #include "core/animation/KeyframeEffectModel.h"
32 #include "core/animation/KeyframeEffectOptions.h"
33 #include "core/animation/StringKeyframe.h"
34 #include "core/animation/TimingInput.h"
35 #include "core/css/CSSStyleDeclaration.h"
28 #include "core/dom/Document.h" 36 #include "core/dom/Document.h"
37 #include "core/dom/shadow/ShadowRoot.h"
38 #include "core/frame/LocalDOMWindow.h"
29 #include "core/frame/UseCounter.h" 39 #include "core/frame/UseCounter.h"
30 #include "platform/ScriptForbiddenScope.h" 40 #include "core/html/HTMLContentElement.h"
41 #include "core/html/HTMLDimension.h"
42 #include "core/html/HTMLDivElement.h"
43 #include "core/html/HTMLStyleElement.h"
44 #include <cstdlib>
31 45
32 namespace blink { 46 namespace blink {
33 47
48 namespace {
49
50 String convertHTMLLengthToCSSLength(const String& htmlLength) {
51 HTMLDimension dimension;
52 parseDimensionValue(htmlLength, dimension);
53 if (dimension.isRelative())
54 return String();
55 CSSPrimitiveValue* cssValue = CSSPrimitiveValue::create(
56 dimension.value(), dimension.isPercentage()
57 ? CSSPrimitiveValue::UnitType::Percentage
58 : CSSPrimitiveValue::UnitType::Pixels);
59 return cssValue->customCSSText();
60 }
61
62 } // namespace
63
34 inline HTMLMarqueeElement::HTMLMarqueeElement(Document& document) 64 inline HTMLMarqueeElement::HTMLMarqueeElement(Document& document)
35 : HTMLElement(HTMLNames::marqueeTag, document) { 65 : HTMLElement(HTMLNames::marqueeTag, document) {
36 if (document.contextDocument()) {
37 ScriptForbiddenScope::AllowUserAgentScript script;
38 v8::Local<v8::Value> classObject =
39 PrivateScriptRunner::installClassIfNeeded(&document,
40 "HTMLMarqueeElement");
41 RELEASE_ASSERT(!classObject.IsEmpty());
42 }
43 UseCounter::count(document, UseCounter::HTMLMarqueeElement); 66 UseCounter::count(document, UseCounter::HTMLMarqueeElement);
67 ShadowRoot* shadow =
68 createShadowRootInternal(ShadowRootType::V0, ASSERT_NO_EXCEPTION);
69 Element* style = HTMLStyleElement::create(document, false);
70 style->setTextContent(
71 ":host { display: inline-block; width: -webkit-fill-available; overflow: "
72 "hidden; text-align: initial; white-space: nowrap; }"
73 ":host([direction=\"up\"]), :host([direction=\"down\"]) { overflow: "
74 "initial; overflow-y: hidden; white-space: initial; }"
75 ":host > div { will-change: transform; }");
76 shadow->appendChild(style);
77
78 Element* mover = HTMLDivElement::create(document);
79 shadow->appendChild(mover);
80
81 mover->appendChild(HTMLContentElement::create(document));
82 m_mover = mover;
44 } 83 }
45 84
46 HTMLMarqueeElement* HTMLMarqueeElement::create(Document& document) { 85 HTMLMarqueeElement* HTMLMarqueeElement::create(Document& document) {
47 HTMLMarqueeElement* marqueeElement = new HTMLMarqueeElement(document); 86 return new HTMLMarqueeElement(document);
48 V8HTMLMarqueeElement::PrivateScript::createdCallbackMethod(document.frame(),
49 marqueeElement);
50 return marqueeElement;
51 } 87 }
52 88
53 void HTMLMarqueeElement::attributeChanged(const QualifiedName& name, 89 void HTMLMarqueeElement::attributeChanged(const QualifiedName& name,
54 const AtomicString& oldValue, 90 const AtomicString& oldValue,
55 const AtomicString& newValue, 91 const AtomicString& newValue,
56 AttributeModificationReason reason) { 92 AttributeModificationReason reason) {
57 HTMLElement::attributeChanged(name, oldValue, newValue, reason); 93 HTMLElement::attributeChanged(name, oldValue, newValue, reason);
58 V8HTMLMarqueeElement::PrivateScript::attributeChangedCallbackMethod( 94 attributeChangedCallback(name, newValue);
59 document().frame(), this, name.toString(), oldValue, newValue);
60 } 95 }
61 96
62 Node::InsertionNotificationRequest HTMLMarqueeElement::insertedInto( 97 Node::InsertionNotificationRequest HTMLMarqueeElement::insertedInto(
63 ContainerNode* insertionPoint) { 98 ContainerNode* insertionPoint) {
64 HTMLElement::insertedInto(insertionPoint); 99 HTMLElement::insertedInto(insertionPoint);
100
65 if (isConnected()) { 101 if (isConnected()) {
66 V8HTMLMarqueeElement::PrivateScript::attachedCallbackMethod( 102 static const QualifiedName* presentationalAttributes[] = {
67 document().frame(), this); 103 &HTMLNames::bgcolorAttr, &HTMLNames::heightAttr, &HTMLNames::hspaceAttr,
104 &HTMLNames::vspaceAttr, &HTMLNames::widthAttr};
105 for (const auto* attr : presentationalAttributes) {
106 initializeAttribute(*attr);
107 }
108
109 start();
68 } 110 }
69 return InsertionDone; 111 return InsertionDone;
70 } 112 }
71 113
72 void HTMLMarqueeElement::removedFrom(ContainerNode* insertionPoint) { 114 void HTMLMarqueeElement::removedFrom(ContainerNode* insertionPoint) {
73 HTMLElement::removedFrom(insertionPoint); 115 HTMLElement::removedFrom(insertionPoint);
74 if (insertionPoint->isConnected()) { 116 if (insertionPoint->isConnected()) {
75 V8HTMLMarqueeElement::PrivateScript::detachedCallbackMethod( 117 stop();
76 insertionPoint->document().frame(), this);
77 } 118 }
78 } 119 }
79 120
80 bool HTMLMarqueeElement::isHorizontal() const { 121 bool HTMLMarqueeElement::isHorizontal() const {
81 AtomicString direction = getAttribute(HTMLNames::directionAttr); 122 Direction direction = this->direction();
82 return direction != "down" && direction != "up"; 123 return direction != Up && direction != Down;
124 }
125
126 int HTMLMarqueeElement::scrollAmount() {
127 bool ok;
128 int scrollAmount = getAttribute(HTMLNames::scrollamountAttr).toInt(&ok);
129 if (!ok || scrollAmount < 0)
130 return kDefaultScrollAmount;
131 return scrollAmount;
132 }
133
134 void HTMLMarqueeElement::setScrollAmount(int value,
135 ExceptionState& exceptionState) {
136 if (value < 0) {
137 exceptionState.throwDOMException(
138 IndexSizeError,
139 "The provided value (" + String::number(value) + ") is negative.");
140 return;
141 }
142 setIntegralAttribute(HTMLNames::scrollamountAttr, value);
143 }
144
145 int HTMLMarqueeElement::scrollDelay() {
146 bool ok;
147 int scrollDelay = getAttribute(HTMLNames::scrolldelayAttr).toInt(&ok);
148 if (!ok || scrollDelay < 0)
149 return kDefaultScrollDelayMS;
150 return scrollDelay;
151 }
152
153 void HTMLMarqueeElement::setScrollDelay(int value,
154 ExceptionState& exceptionState) {
155 if (value < 0) {
156 exceptionState.throwDOMException(
157 IndexSizeError,
158 "The provided value (" + String::number(value) + ") is negative.");
159 return;
160 }
161 setIntegralAttribute(HTMLNames::scrolldelayAttr, value);
162 }
163
164 int HTMLMarqueeElement::loop() {
165 bool ok;
166 int loop = getAttribute(HTMLNames::loopAttr).toInt(&ok);
167 if (!ok || loop <= 0)
168 return kDefaultLoopLimit;
169 return loop;
170 }
171
172 void HTMLMarqueeElement::setLoop(int value, ExceptionState& exceptionState) {
173 if (value <= 0 && value != -1) {
174 exceptionState.throwDOMException(
175 IndexSizeError, "The provided value (" + String::number(value) +
176 ") is neither positive nor -1.");
177 return;
178 }
179 setIntegralAttribute(HTMLNames::loopAttr, value);
180 }
181
182 void HTMLMarqueeElement::start() {
183 if (m_continueCallbackRequestId)
184 return;
185
186 RequestAnimationFrameCallback* callback =
187 new RequestAnimationFrameCallback(this);
188 m_continueCallbackRequestId = document().requestAnimationFrame(callback);
189 }
190
191 void HTMLMarqueeElement::stop() {
192 if (m_continueCallbackRequestId) {
193 document().cancelAnimationFrame(m_continueCallbackRequestId);
194 m_continueCallbackRequestId = 0;
195 return;
196 }
197
198 if (m_player)
199 m_player->pause();
200 }
201
202 void HTMLMarqueeElement::initializeAttribute(const QualifiedName& attr) {
203 const AtomicString& value = getAttribute(attr);
204 if (value.isNull())
205 return;
206 attributeChangedCallback(attr, value);
207 }
208
209 void HTMLMarqueeElement::attributeChangedCallback(const QualifiedName& attr,
210 const String& newValue) {
211 if (attr == HTMLNames::bgcolorAttr) {
212 style()->setProperty("background-color", newValue, String(),
213 ASSERT_NO_EXCEPTION);
214 } else if (attr == HTMLNames::heightAttr) {
215 style()->setProperty("height", convertHTMLLengthToCSSLength(newValue),
216 String(), ASSERT_NO_EXCEPTION);
217 } else if (attr == HTMLNames::hspaceAttr) {
218 style()->setProperty("margin-left", convertHTMLLengthToCSSLength(newValue),
219 String(), ASSERT_NO_EXCEPTION);
220 style()->setProperty("margin-right", convertHTMLLengthToCSSLength(newValue),
221 String(), ASSERT_NO_EXCEPTION);
222 } else if (attr == HTMLNames::vspaceAttr) {
223 style()->setProperty("margin-top", convertHTMLLengthToCSSLength(newValue),
224 String(), ASSERT_NO_EXCEPTION);
225 style()->setProperty("margin-bottom",
226 convertHTMLLengthToCSSLength(newValue), String(),
227 ASSERT_NO_EXCEPTION);
228 } else if (attr == HTMLNames::widthAttr) {
229 style()->setProperty("width", convertHTMLLengthToCSSLength(newValue),
230 String(), ASSERT_NO_EXCEPTION);
231 }
232 }
233
234 void HTMLMarqueeElement::RequestAnimationFrameCallback::handleEvent(double) {
235 m_marquee->m_continueCallbackRequestId = 0;
236 m_marquee->continueAnimation();
237 }
238
239 void HTMLMarqueeElement::AnimationFinished::handleEvent(
240 ExecutionContext* context,
241 Event* event) {
242 ++m_marquee->m_loopCount;
243 m_marquee->start();
244 }
245
246 StringKeyframeEffectModel* HTMLMarqueeElement::createEffectModel(
247 AnimationParameters& parameters) {
248 StyleSheetContents* styleSheetContents =
249 m_mover->document().elementSheet().contents();
250
251 RefPtr<StringKeyframe> keyframe1 = StringKeyframe::create();
252 keyframe1->setCSSPropertyValue(CSSPropertyTransform,
253 parameters.transformBegin, styleSheetContents);
254
255 RefPtr<StringKeyframe> keyframe2 = StringKeyframe::create();
256 keyframe2->setCSSPropertyValue(CSSPropertyTransform, parameters.transformEnd,
257 styleSheetContents);
258
259 return StringKeyframeEffectModel::create(
260 {std::move(keyframe1), std::move(keyframe2)},
261 LinearTimingFunction::shared());
262 }
263
264 void HTMLMarqueeElement::continueAnimation() {
265 if (!shouldContinue())
266 return;
267
268 if (m_player && m_player->playState() == "paused") {
269 m_player->play();
270 return;
271 }
272
273 AnimationParameters parameters = getAnimationParameters();
274 int scrollDelay = this->scrollDelay();
275 int scrollAmount = this->scrollAmount();
276
277 if (scrollDelay < kMinimumScrollDelayMS && !trueSpeed())
278 scrollDelay = kDefaultScrollDelayMS;
279 double duration = 0;
280 if (scrollAmount)
281 duration = parameters.distance * scrollDelay / scrollAmount;
282 if (!duration)
283 return;
284
285 StringKeyframeEffectModel* effectModel = createEffectModel(parameters);
286 Timing timing;
287 timing.fillMode = Timing::FillMode::FORWARDS;
288 TimingInput::setIterationDuration(
289 timing, UnrestrictedDoubleOrString::fromUnrestrictedDouble(duration),
290 ASSERT_NO_EXCEPTION);
291
292 KeyframeEffect* keyframeEffect =
293 KeyframeEffect::create(m_mover, effectModel, timing);
294 Animation* player = m_mover->document().timeline().play(keyframeEffect);
295 player->setId(emptyString());
296 player->setOnfinish(new AnimationFinished(this));
297
298 m_player = player;
299 }
300
301 bool HTMLMarqueeElement::shouldContinue() {
302 int lp = loop();
303
304 // By default, slide loops only once.
305 if (lp <= 0 && behavior() == Slide)
306 lp = 1;
307
308 if (lp <= 0)
309 return true;
310 return m_loopCount < lp;
311 }
312
313 HTMLMarqueeElement::Behavior HTMLMarqueeElement::behavior() const {
314 const AtomicString& bhvr = getAttribute(HTMLNames::behaviorAttr);
jbroman 2016/12/03 23:47:31 "bhvr" and "lp" (above) are a bit too terse for Bl
adithyas 2016/12/05 16:44:27 Fixed.
315 if (bhvr == "alternate")
316 return Alternate;
317 if (bhvr == "slide")
318 return Slide;
319 return Scroll;
320 }
321
322 HTMLMarqueeElement::Direction HTMLMarqueeElement::direction() const {
323 const AtomicString& dir = getAttribute(HTMLNames::directionAttr);
haraken 2016/12/05 02:10:53 direction
adithyas 2016/12/05 16:44:27 Fixed.
324 if (dir == "down")
325 return Down;
326 if (dir == "up")
327 return Up;
328 if (dir == "right")
329 return Right;
330 return Left;
331 }
332
333 bool HTMLMarqueeElement::trueSpeed() const {
334 return hasAttribute(HTMLNames::truespeedAttr);
335 }
336
337 HTMLMarqueeElement::Metrics HTMLMarqueeElement::getMetrics() {
338 Metrics metrics;
339 CSSStyleDeclaration* marqueeStyle =
340 document().domWindow()->getComputedStyle(this, String());
341 // For marquees that are declared inline, getComputedStyle returns "auto" for
342 // width and height. Setting all the metrics to zero disables animation for
343 // inline marquees.
344 if (marqueeStyle->getPropertyValue("width") == "auto" &&
345 marqueeStyle->getPropertyValue("height") == "auto") {
346 metrics.contentHeight = 0;
347 metrics.contentWidth = 0;
348 metrics.marqueeWidth = 0;
349 metrics.marqueeHeight = 0;
350 return metrics;
351 }
352
353 if (isHorizontal()) {
354 m_mover->style()->setProperty("width", "-webkit-max-content", "important",
355 ASSERT_NO_EXCEPTION);
356 } else {
357 m_mover->style()->setProperty("height", "-webkit-max-content", "important",
358 ASSERT_NO_EXCEPTION);
359 }
360 CSSStyleDeclaration* moverStyle =
361 document().domWindow()->getComputedStyle(m_mover, String());
362
363 metrics.contentWidth = moverStyle->getPropertyValue("width").toDouble();
364 metrics.contentHeight = moverStyle->getPropertyValue("height").toDouble();
365 metrics.marqueeWidth = marqueeStyle->getPropertyValue("width").toDouble();
366 metrics.marqueeHeight = marqueeStyle->getPropertyValue("height").toDouble();
367
368 if (isHorizontal()) {
369 m_mover->style()->setProperty("width", "", "important",
370 ASSERT_NO_EXCEPTION);
371 } else {
372 m_mover->style()->setProperty("height", "", "important",
373 ASSERT_NO_EXCEPTION);
374 }
375
376 return metrics;
377 }
378
379 HTMLMarqueeElement::AnimationParameters
380 HTMLMarqueeElement::getAnimationParameters() {
381 AnimationParameters parameters;
382 Metrics metrics = getMetrics();
383
384 double totalWidth = metrics.marqueeWidth + metrics.contentWidth;
385 double totalHeight = metrics.marqueeHeight + metrics.contentHeight;
386
387 double innerWidth = metrics.marqueeWidth - metrics.contentWidth;
388 double innerHeight = metrics.marqueeHeight - metrics.contentHeight;
389
390 switch (behavior()) {
391 case Alternate:
392 switch (direction()) {
393 case Right:
394 parameters.transformBegin =
395 createTransform(false, innerWidth >= 0 ? 0 : innerWidth);
396 parameters.transformEnd =
397 createTransform(false, innerWidth >= 0 ? innerWidth : 0);
398 parameters.distance = std::abs(innerWidth);
399 break;
400 case Up:
401 parameters.transformBegin =
402 createTransform(false, innerHeight >= 0 ? innerHeight : 0);
403 parameters.transformEnd =
404 createTransform(false, innerHeight >= 0 ? 0 : innerHeight);
405 parameters.distance = std::abs(innerHeight);
406 break;
407 case Down:
408 parameters.transformBegin =
409 createTransform(false, innerHeight >= 0 ? 0 : innerHeight);
410 parameters.transformEnd =
411 createTransform(false, innerHeight >= 0 ? innerHeight : 0);
412 parameters.distance = std::abs(innerHeight);
413 break;
414 case Left:
415 default:
416 parameters.transformBegin =
417 createTransform(false, innerWidth >= 0 ? innerWidth : 0);
418 parameters.transformEnd =
419 createTransform(false, innerWidth >= 0 ? 0 : innerWidth);
420 parameters.distance = std::abs(innerWidth);
421 }
422
423 if (m_loopCount % 2) {
424 AtomicString transform = parameters.transformBegin;
jbroman 2016/12/03 23:47:31 nit: more concisely (and IMHO, clearly): std::swa
adithyas 2016/12/05 16:44:27 Fixed, thanks.
425 parameters.transformBegin = parameters.transformEnd;
426 parameters.transformEnd = transform;
427 }
428
429 break;
430 case Slide:
431 switch (direction()) {
432 case Right:
433 parameters.transformBegin =
434 createTransform(true, metrics.contentWidth);
435 parameters.transformEnd = createTransform(false, innerWidth);
436 parameters.distance = metrics.marqueeWidth;
437 break;
438 case Up:
439 parameters.transformBegin =
440 createTransform(false, metrics.marqueeHeight);
441 parameters.transformEnd = "translateY(0)";
442 parameters.distance = metrics.marqueeHeight;
443 break;
444 case Down:
445 parameters.transformBegin =
446 createTransform(true, metrics.contentHeight);
447 parameters.transformEnd = createTransform(false, innerHeight);
448 parameters.distance = metrics.marqueeHeight;
449 break;
450 case Left:
451 default:
452 parameters.transformBegin =
453 createTransform(false, metrics.marqueeWidth);
454 parameters.transformEnd = "translateX(0)";
455 parameters.distance = metrics.marqueeWidth;
456 }
457 break;
458 case Scroll:
459 default:
460 switch (direction()) {
461 case Right:
462 parameters.transformBegin =
463 createTransform(true, metrics.contentWidth);
464 parameters.transformEnd =
465 createTransform(false, metrics.marqueeWidth);
466 parameters.distance = totalWidth;
467 break;
468 case Up:
469 parameters.transformBegin =
470 createTransform(false, metrics.marqueeHeight);
471 parameters.transformEnd =
472 createTransform(true, metrics.contentHeight);
473 parameters.distance = totalHeight;
474 break;
475 case Down:
476 parameters.transformBegin =
477 createTransform(true, metrics.contentHeight);
478 parameters.transformEnd =
479 createTransform(false, metrics.marqueeHeight);
480 parameters.distance = totalHeight;
481 break;
482 case Left:
483 default:
484 parameters.transformBegin =
485 createTransform(false, metrics.marqueeWidth);
486 parameters.transformEnd = createTransform(true, metrics.contentWidth);
487 parameters.distance = totalWidth;
488 }
489 break;
490 }
491
492 return parameters;
493 }
494
495 AtomicString HTMLMarqueeElement::createTransform(bool isNegative,
jbroman 2016/12/03 23:47:31 Why the |isNegative| parameter, instead of simple
adithyas 2016/12/05 16:44:27 I have no idea why I did something so convoluted,
496 double value) const {
497 char axis = isHorizontal() ? 'X' : 'Y';
498 if (isNegative)
499 return AtomicString(String::format("translate%c(-%fpx)", axis, value));
jbroman 2016/12/03 23:47:31 If you change the other thing to String, you can r
adithyas 2016/12/05 16:44:27 Fixed.
500 return AtomicString(String::format("translate%c(%fpx)", axis, value));
501 }
502
503 DEFINE_TRACE(HTMLMarqueeElement) {
504 visitor->trace(m_mover);
505 visitor->trace(m_player);
506 HTMLElement::trace(visitor);
83 } 507 }
84 508
85 } // namespace blink 509 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698