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

Unified Diff: third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp

Issue 2549443003: Move <marquee> implementation to HTMLMarqueeElement.cpp (Closed)
Patch Set: Add asserts for didParse 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp
diff --git a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp
index 1da81e76d6b184d3c023742cc8a3cde9eea043b3..b4e536ab825a0c4f68b2c765424866666311b3c9 100644
--- a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp
@@ -22,32 +22,69 @@
#include "core/html/HTMLMarqueeElement.h"
-#include "bindings/core/v8/PrivateScriptRunner.h"
+#include "bindings/core/v8/ExceptionStatePlaceholder.h"
#include "bindings/core/v8/V8HTMLMarqueeElement.h"
+#include "core/CSSPropertyNames.h"
#include "core/HTMLNames.h"
+#include "core/animation/DocumentTimeline.h"
+#include "core/animation/KeyframeEffect.h"
+#include "core/animation/KeyframeEffectModel.h"
+#include "core/animation/KeyframeEffectOptions.h"
+#include "core/animation/StringKeyframe.h"
+#include "core/animation/TimingInput.h"
+#include "core/css/CSSStyleDeclaration.h"
+#include "core/css/StylePropertySet.h"
#include "core/dom/Document.h"
+#include "core/dom/shadow/ShadowRoot.h"
+#include "core/frame/LocalDOMWindow.h"
#include "core/frame/UseCounter.h"
-#include "platform/ScriptForbiddenScope.h"
+#include "core/html/HTMLContentElement.h"
+#include "core/html/HTMLDimension.h"
+#include "core/html/HTMLDivElement.h"
+#include "core/html/HTMLStyleElement.h"
+#include <cstdlib>
namespace blink {
+namespace {
+
+String convertHTMLLengthToCSSLength(const String& htmlLength) {
+ HTMLDimension dimension;
+ parseDimensionValue(htmlLength, dimension);
+ if (dimension.isRelative())
+ return String();
+ CSSPrimitiveValue* cssValue = CSSPrimitiveValue::create(
+ dimension.value(), dimension.isPercentage()
+ ? CSSPrimitiveValue::UnitType::Percentage
+ : CSSPrimitiveValue::UnitType::Pixels);
+ return cssValue->customCSSText();
+}
+
+} // namespace
+
inline HTMLMarqueeElement::HTMLMarqueeElement(Document& document)
: HTMLElement(HTMLNames::marqueeTag, document) {
- if (document.contextDocument()) {
- ScriptForbiddenScope::AllowUserAgentScript script;
- v8::Local<v8::Value> classObject =
- PrivateScriptRunner::installClassIfNeeded(&document,
- "HTMLMarqueeElement");
- RELEASE_ASSERT(!classObject.IsEmpty());
- }
UseCounter::count(document, UseCounter::HTMLMarqueeElement);
+ ShadowRoot* shadow =
+ createShadowRootInternal(ShadowRootType::V0, ASSERT_NO_EXCEPTION);
+ Element* style = HTMLStyleElement::create(document, false);
+ style->setTextContent(
+ ":host { display: inline-block; width: -webkit-fill-available; overflow: "
+ "hidden; text-align: initial; white-space: nowrap; }"
+ ":host([direction=\"up\"]), :host([direction=\"down\"]) { overflow: "
+ "initial; overflow-y: hidden; white-space: initial; }"
+ ":host > div { will-change: transform; }");
+ shadow->appendChild(style);
+
+ Element* mover = HTMLDivElement::create(document);
+ shadow->appendChild(mover);
+
+ mover->appendChild(HTMLContentElement::create(document));
+ m_mover = mover;
}
HTMLMarqueeElement* HTMLMarqueeElement::create(Document& document) {
- HTMLMarqueeElement* marqueeElement = new HTMLMarqueeElement(document);
- V8HTMLMarqueeElement::PrivateScript::createdCallbackMethod(document.frame(),
- marqueeElement);
- return marqueeElement;
+ return new HTMLMarqueeElement(document);
}
void HTMLMarqueeElement::attributeChanged(const QualifiedName& name,
tkent 2016/12/06 22:43:11 Overriding attributeChanged looks weird. Doesn't
esprehn 2016/12/07 00:10:00 Yes see the discussion for follow up patches
@@ -55,31 +92,401 @@ void HTMLMarqueeElement::attributeChanged(const QualifiedName& name,
const AtomicString& newValue,
AttributeModificationReason reason) {
HTMLElement::attributeChanged(name, oldValue, newValue, reason);
- V8HTMLMarqueeElement::PrivateScript::attributeChangedCallbackMethod(
- document().frame(), this, name.toString(), oldValue, newValue);
+ attributeChangedCallback(name, newValue);
}
Node::InsertionNotificationRequest HTMLMarqueeElement::insertedInto(
ContainerNode* insertionPoint) {
HTMLElement::insertedInto(insertionPoint);
+
if (isConnected()) {
- V8HTMLMarqueeElement::PrivateScript::attachedCallbackMethod(
- document().frame(), this);
+ static const QualifiedName* presentationalAttributes[] = {
+ &HTMLNames::bgcolorAttr, &HTMLNames::heightAttr, &HTMLNames::hspaceAttr,
+ &HTMLNames::vspaceAttr, &HTMLNames::widthAttr};
+ for (const auto* attr : presentationalAttributes) {
+ const AtomicString& value = getAttribute(*attr);
tkent 2016/12/06 22:43:11 getAttribute -> fastGetAttribute
esprehn 2016/12/07 00:10:00 We were trying to avoid using private hooks that a
+ if (value.isNull())
+ continue;
+ attributeChangedCallback(*attr, value);
+ }
+
+ start();
}
+
return InsertionDone;
}
void HTMLMarqueeElement::removedFrom(ContainerNode* insertionPoint) {
HTMLElement::removedFrom(insertionPoint);
if (insertionPoint->isConnected()) {
- V8HTMLMarqueeElement::PrivateScript::detachedCallbackMethod(
- insertionPoint->document().frame(), this);
+ stop();
}
}
bool HTMLMarqueeElement::isHorizontal() const {
- AtomicString direction = getAttribute(HTMLNames::directionAttr);
- return direction != "down" && direction != "up";
+ Direction direction = this->direction();
+ return direction != Up && direction != Down;
+}
+
+int HTMLMarqueeElement::scrollAmount() const {
+ bool ok;
+ int scrollAmount = getAttribute(HTMLNames::scrollamountAttr).toInt(&ok);
tkent 2016/12/06 22:43:11 Please do not use AtomicString::toInt(). It isn't
esprehn 2016/12/07 00:10:00 Please fix the toInt() to not have an overflow. :)
adithyas 2016/12/08 15:12:11 I'm not really sure about the overflow bug, I coul
+ if (!ok || scrollAmount < 0)
+ return kDefaultScrollAmount;
+ return scrollAmount;
+}
+
+void HTMLMarqueeElement::setScrollAmount(int value,
+ ExceptionState& exceptionState) {
+ if (value < 0) {
+ exceptionState.throwDOMException(
+ IndexSizeError,
+ "The provided value (" + String::number(value) + ") is negative.");
+ return;
+ }
+ setIntegralAttribute(HTMLNames::scrollamountAttr, value);
+}
+
+int HTMLMarqueeElement::scrollDelay() const {
+ bool ok;
+ int scrollDelay = getAttribute(HTMLNames::scrolldelayAttr).toInt(&ok);
+ if (!ok || scrollDelay < 0)
+ return kDefaultScrollDelayMS;
+ return scrollDelay;
+}
+
+void HTMLMarqueeElement::setScrollDelay(int value,
+ ExceptionState& exceptionState) {
+ if (value < 0) {
+ exceptionState.throwDOMException(
+ IndexSizeError,
+ "The provided value (" + String::number(value) + ") is negative.");
+ return;
+ }
+ setIntegralAttribute(HTMLNames::scrolldelayAttr, value);
+}
+
+int HTMLMarqueeElement::loop() const {
+ bool ok;
+ int loop = getAttribute(HTMLNames::loopAttr).toInt(&ok);
+ if (!ok || loop <= 0)
+ return kDefaultLoopLimit;
+ return loop;
+}
+
+void HTMLMarqueeElement::setLoop(int value, ExceptionState& exceptionState) {
+ if (value <= 0 && value != -1) {
+ exceptionState.throwDOMException(
+ IndexSizeError, "The provided value (" + String::number(value) +
+ ") is neither positive nor -1.");
+ return;
+ }
+ setIntegralAttribute(HTMLNames::loopAttr, value);
+}
+
+void HTMLMarqueeElement::start() {
+ if (m_continueCallbackRequestId)
+ return;
+
+ RequestAnimationFrameCallback* callback =
+ new RequestAnimationFrameCallback(this);
+ m_continueCallbackRequestId = document().requestAnimationFrame(callback);
+}
+
+void HTMLMarqueeElement::stop() {
+ if (m_continueCallbackRequestId) {
+ document().cancelAnimationFrame(m_continueCallbackRequestId);
+ m_continueCallbackRequestId = 0;
+ return;
+ }
+
+ if (m_player)
+ m_player->pause();
+}
+
+void HTMLMarqueeElement::attributeChangedCallback(const QualifiedName& attr,
+ const String& newValue) {
+ if (attr == HTMLNames::bgcolorAttr) {
+ style()->setProperty("background-color", newValue, String(),
+ ASSERT_NO_EXCEPTION);
+ } else if (attr == HTMLNames::heightAttr) {
+ style()->setProperty("height", convertHTMLLengthToCSSLength(newValue),
+ String(), ASSERT_NO_EXCEPTION);
+ } else if (attr == HTMLNames::hspaceAttr) {
+ style()->setProperty("margin-left", convertHTMLLengthToCSSLength(newValue),
+ String(), ASSERT_NO_EXCEPTION);
+ style()->setProperty("margin-right", convertHTMLLengthToCSSLength(newValue),
+ String(), ASSERT_NO_EXCEPTION);
+ } else if (attr == HTMLNames::vspaceAttr) {
+ style()->setProperty("margin-top", convertHTMLLengthToCSSLength(newValue),
+ String(), ASSERT_NO_EXCEPTION);
+ style()->setProperty("margin-bottom",
+ convertHTMLLengthToCSSLength(newValue), String(),
+ ASSERT_NO_EXCEPTION);
+ } else if (attr == HTMLNames::widthAttr) {
+ style()->setProperty("width", convertHTMLLengthToCSSLength(newValue),
+ String(), ASSERT_NO_EXCEPTION);
+ }
+}
+
+void HTMLMarqueeElement::RequestAnimationFrameCallback::handleEvent(double) {
+ m_marquee->m_continueCallbackRequestId = 0;
+ m_marquee->continueAnimation();
+}
+
+void HTMLMarqueeElement::AnimationFinished::handleEvent(
+ ExecutionContext* context,
+ Event* event) {
+ ++m_marquee->m_loopCount;
+ m_marquee->start();
+}
+
+StringKeyframeEffectModel* HTMLMarqueeElement::createEffectModel(
+ AnimationParameters& parameters) {
tkent 2016/12/06 22:43:11 The argument type should be |const AnimationParame
+ StyleSheetContents* styleSheetContents =
+ m_mover->document().elementSheet().contents();
+ MutableStylePropertySet::SetResult setResult;
+
+ RefPtr<StringKeyframe> keyframe1 = StringKeyframe::create();
+ setResult = keyframe1->setCSSPropertyValue(
+ CSSPropertyTransform, parameters.transformBegin, styleSheetContents);
+ DCHECK(setResult.didParse);
+
+ RefPtr<StringKeyframe> keyframe2 = StringKeyframe::create();
+ setResult = keyframe2->setCSSPropertyValue(
+ CSSPropertyTransform, parameters.transformEnd, styleSheetContents);
+ DCHECK(setResult.didParse);
+
+ return StringKeyframeEffectModel::create(
+ {std::move(keyframe1), std::move(keyframe2)},
+ LinearTimingFunction::shared());
+}
+
+void HTMLMarqueeElement::continueAnimation() {
+ if (!shouldContinue())
+ return;
+
+ if (m_player && m_player->playState() == "paused") {
+ m_player->play();
+ return;
+ }
+
+ AnimationParameters parameters = getAnimationParameters();
+ int scrollDelay = this->scrollDelay();
+ int scrollAmount = this->scrollAmount();
+
+ if (scrollDelay < kMinimumScrollDelayMS && !trueSpeed())
+ scrollDelay = kDefaultScrollDelayMS;
+ double duration = 0;
+ if (scrollAmount)
+ duration = parameters.distance * scrollDelay / scrollAmount;
+ if (!duration)
+ return;
+
+ StringKeyframeEffectModel* effectModel = createEffectModel(parameters);
+ Timing timing;
+ timing.fillMode = Timing::FillMode::FORWARDS;
+ TimingInput::setIterationDuration(
+ timing, UnrestrictedDoubleOrString::fromUnrestrictedDouble(duration),
+ ASSERT_NO_EXCEPTION);
+
+ KeyframeEffect* keyframeEffect =
+ KeyframeEffect::create(m_mover, effectModel, timing);
+ Animation* player = m_mover->document().timeline().play(keyframeEffect);
+ player->setId(emptyString());
+ player->setOnfinish(new AnimationFinished(this));
+
+ m_player = player;
+}
+
+bool HTMLMarqueeElement::shouldContinue() {
+ int loopCount = loop();
+
+ // By default, slide loops only once.
+ if (loopCount <= 0 && behavior() == Slide)
+ loopCount = 1;
+
+ if (loopCount <= 0)
+ return true;
+ return m_loopCount < loopCount;
+}
+
+HTMLMarqueeElement::Behavior HTMLMarqueeElement::behavior() const {
+ const AtomicString& behavior = getAttribute(HTMLNames::behaviorAttr);
+ if (behavior == "alternate")
tkent 2016/12/06 22:43:11 Should it be case-sensitive match?
adithyas 2016/12/08 15:12:11 Nope, this is a bug, thanks for catching it.
+ return Alternate;
+ if (behavior == "slide")
+ return Slide;
+ return Scroll;
+}
+
+HTMLMarqueeElement::Direction HTMLMarqueeElement::direction() const {
+ const AtomicString& direction = getAttribute(HTMLNames::directionAttr);
+ if (direction == "down")
+ return Down;
+ if (direction == "up")
+ return Up;
+ if (direction == "right")
+ return Right;
+ return Left;
+}
+
+bool HTMLMarqueeElement::trueSpeed() const {
+ return hasAttribute(HTMLNames::truespeedAttr);
tkent 2016/12/06 22:43:11 hasAttribute -> fastHasAttribute
+}
+
+HTMLMarqueeElement::Metrics HTMLMarqueeElement::getMetrics() {
+ Metrics metrics;
+ CSSStyleDeclaration* marqueeStyle =
+ document().domWindow()->getComputedStyle(this, String());
+ // For marquees that are declared inline, getComputedStyle returns "auto" for
+ // width and height. Setting all the metrics to zero disables animation for
+ // inline marquees.
+ if (marqueeStyle->getPropertyValue("width") == "auto" &&
+ marqueeStyle->getPropertyValue("height") == "auto") {
+ metrics.contentHeight = 0;
+ metrics.contentWidth = 0;
+ metrics.marqueeWidth = 0;
+ metrics.marqueeHeight = 0;
+ return metrics;
+ }
+
+ if (isHorizontal()) {
+ m_mover->style()->setProperty("width", "-webkit-max-content", "important",
+ ASSERT_NO_EXCEPTION);
+ } else {
+ m_mover->style()->setProperty("height", "-webkit-max-content", "important",
+ ASSERT_NO_EXCEPTION);
+ }
+ CSSStyleDeclaration* moverStyle =
+ document().domWindow()->getComputedStyle(m_mover, String());
+
+ metrics.contentWidth = moverStyle->getPropertyValue("width").toDouble();
+ metrics.contentHeight = moverStyle->getPropertyValue("height").toDouble();
+ metrics.marqueeWidth = marqueeStyle->getPropertyValue("width").toDouble();
+ metrics.marqueeHeight = marqueeStyle->getPropertyValue("height").toDouble();
+
+ if (isHorizontal()) {
+ m_mover->style()->setProperty("width", "", "important",
+ ASSERT_NO_EXCEPTION);
+ } else {
+ m_mover->style()->setProperty("height", "", "important",
+ ASSERT_NO_EXCEPTION);
+ }
+
+ return metrics;
+}
+
+HTMLMarqueeElement::AnimationParameters
+HTMLMarqueeElement::getAnimationParameters() {
+ AnimationParameters parameters;
+ Metrics metrics = getMetrics();
+
+ double totalWidth = metrics.marqueeWidth + metrics.contentWidth;
+ double totalHeight = metrics.marqueeHeight + metrics.contentHeight;
+
+ double innerWidth = metrics.marqueeWidth - metrics.contentWidth;
+ double innerHeight = metrics.marqueeHeight - metrics.contentHeight;
+
+ switch (behavior()) {
+ case Alternate:
+ switch (direction()) {
+ case Right:
+ parameters.transformBegin =
+ createTransform(innerWidth >= 0 ? 0 : innerWidth);
+ parameters.transformEnd =
+ createTransform(innerWidth >= 0 ? innerWidth : 0);
+ parameters.distance = std::abs(innerWidth);
+ break;
+ case Up:
+ parameters.transformBegin =
+ createTransform(innerHeight >= 0 ? innerHeight : 0);
+ parameters.transformEnd =
+ createTransform(innerHeight >= 0 ? 0 : innerHeight);
+ parameters.distance = std::abs(innerHeight);
+ break;
+ case Down:
+ parameters.transformBegin =
+ createTransform(innerHeight >= 0 ? 0 : innerHeight);
+ parameters.transformEnd =
+ createTransform(innerHeight >= 0 ? innerHeight : 0);
+ parameters.distance = std::abs(innerHeight);
+ break;
+ case Left:
+ default:
+ parameters.transformBegin =
+ createTransform(innerWidth >= 0 ? innerWidth : 0);
+ parameters.transformEnd =
+ createTransform(innerWidth >= 0 ? 0 : innerWidth);
+ parameters.distance = std::abs(innerWidth);
+ }
+
+ if (m_loopCount % 2)
+ std::swap(parameters.transformBegin, parameters.transformEnd);
+ break;
+ case Slide:
+ switch (direction()) {
+ case Right:
+ parameters.transformBegin = createTransform(-metrics.contentWidth);
+ parameters.transformEnd = createTransform(innerWidth);
+ parameters.distance = metrics.marqueeWidth;
+ break;
+ case Up:
+ parameters.transformBegin = createTransform(metrics.marqueeHeight);
+ parameters.transformEnd = "translateY(0)";
+ parameters.distance = metrics.marqueeHeight;
+ break;
+ case Down:
+ parameters.transformBegin = createTransform(-metrics.contentHeight);
+ parameters.transformEnd = createTransform(innerHeight);
+ parameters.distance = metrics.marqueeHeight;
+ break;
+ case Left:
+ default:
+ parameters.transformBegin = createTransform(metrics.marqueeWidth);
+ parameters.transformEnd = "translateX(0)";
+ parameters.distance = metrics.marqueeWidth;
+ }
+ break;
+ case Scroll:
+ default:
+ switch (direction()) {
+ case Right:
+ parameters.transformBegin = createTransform(-metrics.contentWidth);
+ parameters.transformEnd = createTransform(metrics.marqueeWidth);
+ parameters.distance = totalWidth;
+ break;
+ case Up:
+ parameters.transformBegin = createTransform(metrics.marqueeHeight);
+ parameters.transformEnd = createTransform(-metrics.contentHeight);
+ parameters.distance = totalHeight;
+ break;
+ case Down:
+ parameters.transformBegin = createTransform(-metrics.contentHeight);
+ parameters.transformEnd = createTransform(metrics.marqueeHeight);
+ parameters.distance = totalHeight;
+ break;
+ case Left:
+ default:
+ parameters.transformBegin = createTransform(metrics.marqueeWidth);
+ parameters.transformEnd = createTransform(-metrics.contentWidth);
+ parameters.distance = totalWidth;
+ }
+ break;
+ }
+
+ return parameters;
+}
+
+AtomicString HTMLMarqueeElement::createTransform(double value) const {
+ char axis = isHorizontal() ? 'X' : 'Y';
+ return AtomicString(String::format("translate%c(%fpx)", axis, value));
tkent 2016/12/06 22:43:11 Does this work in French locale, which use ',' for
adithyas 2016/12/08 15:12:11 I don't think it does, thanks for pointing this ou
+}
+
+DEFINE_TRACE(HTMLMarqueeElement) {
+ visitor->trace(m_mover);
+ visitor->trace(m_player);
+ HTMLElement::trace(visitor);
}
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698