зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1504343 - Convert xbl-marquee to UA Widget r=bgrins,bzbarsky
This patch moves the marquee bindings from xbl-marquee.xml to marquee.js and converts them to a UA Widget. The contenteditable bindings are dropped, replaced with a styling rule that will fix the position of the scrolling text. Inline styles have been moved to the stylesheet so usage of display: -moz-box can continue to be parsed. test_bug840098.html is deleted because it is only valid under the context of in-content XBL bindings. Differential Revision: https://phabricator.services.mozilla.com/D10385 --HG-- rename : layout/style/xbl-marquee/xbl-marquee.css => toolkit/content/widgets/marquee.css rename : layout/style/xbl-marquee/xbl-marquee.xml => toolkit/content/widgets/marquee.js extra : moz-landing-system : lando
This commit is contained in:
Родитель
9ca1f4544a
Коммит
a685c05ea5
|
@ -553,7 +553,6 @@ skip-if = toolkit == 'android' || (verify && !debug && (os == 'linux')) #bug 687
|
|||
[test_bug814576.html]
|
||||
[test_bug819051.html]
|
||||
[test_bug820909.html]
|
||||
[test_bug840098.html]
|
||||
[test_bug864595.html]
|
||||
[test_bug868999.html]
|
||||
[test_bug869000.html]
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=840098
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 840098</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=840098">Mozilla Bug 840098</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<div id="foo"></div>
|
||||
</div>
|
||||
<marquee id="m">Hello</marquee>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 840098 **/
|
||||
|
||||
var mar = document.getElementById("m");
|
||||
var anonymousNode = SpecialPowers.wrap(document).getAnonymousNodes(mar)[0];
|
||||
try {
|
||||
SpecialPowers.wrap(document).implementation.createDocument("", "", null).adoptNode(anonymousNode);
|
||||
ok(false, "shouldn't be able to adopt the root of an anonymous subtree");
|
||||
} catch (e) {
|
||||
is(e.name, "NotSupportedError", "threw the correct type of error");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -8,10 +8,12 @@
|
|||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsStyleConsts.h"
|
||||
#include "nsMappedAttributes.h"
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/dom/HTMLMarqueeElementBinding.h"
|
||||
#include "mozilla/dom/CustomEvent.h"
|
||||
// This is to pick up the definition of FunctionStringCallback:
|
||||
#include "mozilla/dom/DataTransferItemBinding.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Marquee)
|
||||
|
||||
|
@ -65,6 +67,43 @@ HTMLMarqueeElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
|
|||
return dom::HTMLMarqueeElement_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLMarqueeElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent)
|
||||
{
|
||||
|
||||
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
|
||||
aBindingParent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (nsContentUtils::IsUAWidgetEnabled() && IsInComposedDoc()) {
|
||||
AttachAndSetUAShadowRoot();
|
||||
AsyncEventDispatcher* dispatcher =
|
||||
new AsyncEventDispatcher(this,
|
||||
NS_LITERAL_STRING("UAWidgetBindToTree"),
|
||||
CanBubble::eYes,
|
||||
ChromeOnlyDispatch::eYes);
|
||||
dispatcher->RunDOMEventWhenSafe();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMarqueeElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
{
|
||||
if (GetShadowRoot() && IsInComposedDoc()) {
|
||||
AsyncEventDispatcher* dispatcher =
|
||||
new AsyncEventDispatcher(this,
|
||||
NS_LITERAL_STRING("UAWidgetUnbindFromTree"),
|
||||
CanBubble::eYes,
|
||||
ChromeOnlyDispatch::eYes);
|
||||
dispatcher->RunDOMEventWhenSafe();
|
||||
}
|
||||
|
||||
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMarqueeElement::SetStartStopCallback(FunctionStringCallback* aCallback)
|
||||
{
|
||||
|
@ -123,6 +162,29 @@ HTMLMarqueeElement::ParseAttribute(int32_t aNamespaceID,
|
|||
aMaybeScriptedPrincipal, aResult);
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLMarqueeElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||
const nsAttrValue* aValue,
|
||||
const nsAttrValue* aOldValue,
|
||||
nsIPrincipal* aMaybeScriptedPrincipal,
|
||||
bool aNotify)
|
||||
{
|
||||
if (nsContentUtils::IsUAWidgetEnabled() &&
|
||||
IsInComposedDoc() &&
|
||||
aNameSpaceID == kNameSpaceID_None &&
|
||||
aName == nsGkAtoms::direction) {
|
||||
AsyncEventDispatcher* dispatcher =
|
||||
new AsyncEventDispatcher(this,
|
||||
NS_LITERAL_STRING("UAWidgetAttributeChanged"),
|
||||
CanBubble::eYes,
|
||||
ChromeOnlyDispatch::eYes);
|
||||
dispatcher->RunDOMEventWhenSafe();
|
||||
}
|
||||
return nsGenericHTMLElement::AfterSetAttr(
|
||||
aNameSpaceID, aName, aValue, aOldValue, aMaybeScriptedPrincipal, aNotify);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HTMLMarqueeElement::MapAttributesIntoRule(const nsMappedAttributes* aAttributes, MappedDeclarations& aDecls)
|
||||
{
|
||||
|
@ -149,10 +211,24 @@ HTMLMarqueeElement::GetAttributeMappingFunction() const
|
|||
return &MapAttributesIntoRule;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMarqueeElement::DispatchEventToShadowRoot(const nsAString& aEventTypeArg)
|
||||
{
|
||||
// Dispatch the event to the UA Widget Shadow Root, make it inaccessible to document.
|
||||
RefPtr<nsINode> shadow = GetShadowRoot();
|
||||
MOZ_ASSERT(shadow);
|
||||
RefPtr<Event> event = new Event(shadow, nullptr, nullptr);
|
||||
event->InitEvent(aEventTypeArg, false, false);
|
||||
event->SetTrusted(true);
|
||||
shadow->DispatchEvent(*event, IgnoreErrors());
|
||||
}
|
||||
|
||||
void
|
||||
HTMLMarqueeElement::Start()
|
||||
{
|
||||
if (mStartStopCallback) {
|
||||
if (GetShadowRoot()) {
|
||||
DispatchEventToShadowRoot(NS_LITERAL_STRING("marquee-start"));
|
||||
} else if (mStartStopCallback) {
|
||||
mStartStopCallback->Call(NS_LITERAL_STRING("start"));
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +236,9 @@ HTMLMarqueeElement::Start()
|
|||
void
|
||||
HTMLMarqueeElement::Stop()
|
||||
{
|
||||
if (mStartStopCallback) {
|
||||
if (GetShadowRoot()) {
|
||||
DispatchEventToShadowRoot(NS_LITERAL_STRING("marquee-stop"));
|
||||
} else if (mStartStopCallback) {
|
||||
mStartStopCallback->Call(NS_LITERAL_STRING("stop"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,11 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLMarqueeElement,
|
||||
nsGenericHTMLElement)
|
||||
|
||||
nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
nsIContent* aBindingParent) override;
|
||||
void UnbindFromTree(bool aDeep = true,
|
||||
bool aNullParent = true) override;
|
||||
|
||||
static const int kDefaultLoop = -1;
|
||||
static const int kDefaultScrollAmount = 6;
|
||||
static const int kDefaultScrollDelayMS = 85;
|
||||
|
@ -135,6 +140,11 @@ public:
|
|||
const nsAString& aValue,
|
||||
nsIPrincipal* aMaybeScriptedPrincipal,
|
||||
nsAttrValue& aResult) override;
|
||||
virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
|
||||
const nsAttrValue* aValue,
|
||||
const nsAttrValue* aOldValue,
|
||||
nsIPrincipal* aMaybeScriptedPrincipal,
|
||||
bool aNotify) override;
|
||||
NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
|
||||
nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override;
|
||||
|
||||
|
@ -149,6 +159,8 @@ private:
|
|||
RefPtr<FunctionStringCallback> mStartStopCallback;
|
||||
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
||||
MappedDeclarations&);
|
||||
|
||||
void DispatchEventToShadowRoot(const nsAString& aEventTypeArg);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -80,12 +80,17 @@ input[contenteditable="true"][type="file"] {
|
|||
}
|
||||
|
||||
/* emulation of non-standard HTML <marquee> tag */
|
||||
marquee:-moz-read-write {
|
||||
-moz-binding: url('chrome://xbl-marquee/content/xbl-marquee.xml#marquee-horizontal-editable');
|
||||
}
|
||||
|
||||
marquee[direction="up"]:-moz-read-write, marquee[direction="down"]:-moz-read-write {
|
||||
@supports not -moz-bool-pref("dom.ua_widget.enabled") {
|
||||
|
||||
marquee:-moz-read-write {
|
||||
-moz-binding: url('chrome://xbl-marquee/content/xbl-marquee.xml#marquee-horizontal-editable');
|
||||
}
|
||||
|
||||
marquee[direction="up"]:-moz-read-write, marquee[direction="down"]:-moz-read-write {
|
||||
-moz-binding: url('chrome://xbl-marquee/content/xbl-marquee.xml#marquee-vertical-editable');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
*|*:-moz-read-write > input[type="hidden"],
|
||||
|
|
|
@ -842,14 +842,24 @@ marquee {
|
|||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
text-align: start;
|
||||
-moz-binding: url('chrome://xbl-marquee/content/xbl-marquee.xml#marquee-horizontal');
|
||||
}
|
||||
|
||||
marquee[direction="up"], marquee[direction="down"] {
|
||||
-moz-binding: url('chrome://xbl-marquee/content/xbl-marquee.xml#marquee-vertical');
|
||||
block-size: 200px;
|
||||
}
|
||||
|
||||
@supports not -moz-bool-pref("dom.ua_widget.enabled") {
|
||||
|
||||
marquee {
|
||||
-moz-binding: url('chrome://xbl-marquee/content/xbl-marquee.xml#marquee-horizontal');
|
||||
}
|
||||
|
||||
marquee[direction="up"], marquee[direction="down"] {
|
||||
-moz-binding: url('chrome://xbl-marquee/content/xbl-marquee.xml#marquee-vertical');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* PRINT ONLY rules follow */
|
||||
@media print {
|
||||
|
||||
|
|
|
@ -61,6 +61,10 @@ class UAWidgetsChild extends ActorChild {
|
|||
case "object":
|
||||
// TODO (pluginProblems)
|
||||
break;
|
||||
case "marquee":
|
||||
uri = "chrome://global/content/elements/marquee.js";
|
||||
widgetName = "MarqueeWidget";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!uri || !widgetName) {
|
||||
|
|
|
@ -99,6 +99,8 @@ toolkit.jar:
|
|||
content/global/elements/notificationbox.js (widgets/notificationbox.js)
|
||||
content/global/elements/progressmeter.js (widgets/progressmeter.js)
|
||||
content/global/elements/radio.js (widgets/radio.js)
|
||||
content/global/elements/marquee.css (widgets/marquee.css)
|
||||
content/global/elements/marquee.js (widgets/marquee.js)
|
||||
content/global/elements/stringbundle.js (widgets/stringbundle.js)
|
||||
content/global/elements/tabbox.js (widgets/tabbox.js)
|
||||
content/global/elements/textbox.js (widgets/textbox.js)
|
||||
|
|
|
@ -48,3 +48,5 @@ As part of the implementation of the Web Platform, it is important to make sure
|
|||
|
||||
* Do not dispatch non-spec compliant events on the UA Widget Shadow Root host element, as event listeners in web content scripts can access them.
|
||||
* The layout and the dimensions of the widget should be ready by the time the constructor returns, since they can be detectable as soon as the content script gets the reference of the host element (i.e. when ``appendChild()`` returns). In order to make this easier we load ``<link>`` elements load chrome stylesheets synchronously when inside a UA Widget Shadow DOM.
|
||||
* There shouldn't be any white-spaces nodes in the Shadow DOM, because UA Widget could be placed inside ``white-space: pre``. See bug 1502205.
|
||||
* CSP will block inline styles in the Shadow DOM. ``<link>`` is the only safe way to load styles.
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
.horizontalContainer {
|
||||
display: -moz-box;
|
||||
overflow: hidden;
|
||||
width: -moz-available;
|
||||
}
|
||||
.horizontalOuterDiv {
|
||||
display: -moz-box;
|
||||
}
|
||||
|
||||
.horizontalInnerDiv {
|
||||
display: table;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.verticalContainer {
|
||||
overflow: hidden;
|
||||
width: -moz-available;
|
||||
}
|
||||
|
||||
/* disable scrolling in contenteditable */
|
||||
:host(:-moz-read-write) .horizontalOuterDiv,
|
||||
:host(:-moz-read-write) .verticalInnerDiv {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
/* PRINT ONLY rules */
|
||||
@media print {
|
||||
.horizontalOuterDiv,
|
||||
.verticalInnerDiv {
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,375 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* This is the class of entry. It will construct the actual implementation
|
||||
* according to the value of the "direction" property.
|
||||
*/
|
||||
this.MarqueeWidget = class {
|
||||
constructor(shadowRoot) {
|
||||
this.shadowRoot = shadowRoot;
|
||||
this.element = shadowRoot.host;
|
||||
|
||||
this.switchImpl();
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback called by UAWidgetsChild wheen the direction property
|
||||
* changes.
|
||||
*/
|
||||
onattributechange() {
|
||||
this.switchImpl();
|
||||
}
|
||||
|
||||
switchImpl() {
|
||||
let newImpl;
|
||||
switch (this.element.direction) {
|
||||
case "up":
|
||||
case "down":
|
||||
newImpl = MarqueeVerticalImplWidget;
|
||||
break;
|
||||
case "left":
|
||||
case "right":
|
||||
newImpl = MarqueeHorizontalImplWidget;
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip if we are asked to load the same implementation.
|
||||
// This can happen if the property is set again w/o value change.
|
||||
if (this.impl && this.impl.constructor == newImpl) {
|
||||
return;
|
||||
}
|
||||
this.destructor();
|
||||
if (newImpl) {
|
||||
this.impl = new newImpl(this.shadowRoot);
|
||||
}
|
||||
}
|
||||
|
||||
destructor() {
|
||||
if (!this.impl) {
|
||||
return;
|
||||
}
|
||||
this.impl.destructor();
|
||||
this.shadowRoot.firstChild.remove();
|
||||
delete this.impl;
|
||||
}
|
||||
};
|
||||
|
||||
this.MarqueeBaseImplWidget = class {
|
||||
constructor(shadowRoot) {
|
||||
this.shadowRoot = shadowRoot;
|
||||
this.element = shadowRoot.host;
|
||||
this.document = this.element.ownerDocument;
|
||||
this.window = this.document.defaultView;
|
||||
|
||||
this.generateContent();
|
||||
|
||||
// Set up state.
|
||||
this._currentDirection = this.element.direction || "left";
|
||||
this._currentLoop = this.element.loop;
|
||||
this.dirsign = 1;
|
||||
this.startAt = 0;
|
||||
this.stopAt = 0;
|
||||
this.newPosition = 0;
|
||||
this.runId = 0;
|
||||
this.originalHeight = 0;
|
||||
this.invalidateCache = true;
|
||||
|
||||
this._mutationObserver = new this.window.MutationObserver(
|
||||
(aMutations) => this._mutationActor(aMutations));
|
||||
this._mutationObserver.observe(this.element, { attributes: true,
|
||||
attributeOldValue: true,
|
||||
attributeFilter: ["loop", "", "behavior",
|
||||
"direction", "width", "height"] });
|
||||
|
||||
// init needs to be run after the page has loaded in order to calculate
|
||||
// the correct height/width
|
||||
if (this.document.readyState == "complete") {
|
||||
this.init();
|
||||
} else {
|
||||
this.window.addEventListener("load", this, { once: true });
|
||||
}
|
||||
|
||||
this.shadowRoot.addEventListener("marquee-start", this);
|
||||
this.shadowRoot.addEventListener("marquee-stop", this);
|
||||
}
|
||||
|
||||
destructor() {
|
||||
this._mutationObserver.disconnect();
|
||||
this.window.clearTimeout(this.runId);
|
||||
|
||||
this.window.removeEventListener("load", this);
|
||||
this.shadowRoot.removeEventListener("marquee-start", this);
|
||||
this.shadowRoot.removeEventListener("marquee-stop", this);
|
||||
}
|
||||
|
||||
handleEvent(aEvent) {
|
||||
if (!aEvent.isTrusted) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aEvent.type) {
|
||||
case "load":
|
||||
this.init();
|
||||
break;
|
||||
case "marquee-start":
|
||||
this.doStart();
|
||||
break;
|
||||
case "marquee-stop":
|
||||
this.doStop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
get outerDiv() {
|
||||
return this.shadowRoot.firstChild;
|
||||
}
|
||||
|
||||
get innerDiv() {
|
||||
return this.shadowRoot.getElementById("innerDiv");
|
||||
}
|
||||
|
||||
get scrollDelayWithTruespeed() {
|
||||
if (this.element.scrollDelay < 60 && !this.element.trueSpeed) {
|
||||
return 60;
|
||||
}
|
||||
return this.element.scrollDelay;
|
||||
}
|
||||
|
||||
doStart() {
|
||||
if (this.runId == 0) {
|
||||
var lambda = () => this._doMove(false);
|
||||
this.runId = this.window.setTimeout(lambda, this.scrollDelayWithTruespeed - this._deltaStartStop);
|
||||
this._deltaStartStop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
doStop() {
|
||||
if (this.runId != 0) {
|
||||
this._deltaStartStop = Date.now() - this._lastMoveDate;
|
||||
this.window.clearTimeout(this.runId);
|
||||
}
|
||||
|
||||
this.runId = 0;
|
||||
}
|
||||
|
||||
_fireEvent(aName, aBubbles, aCancelable) {
|
||||
var e = this.document.createEvent("Events");
|
||||
e.initEvent(aName, aBubbles, aCancelable);
|
||||
this.element.dispatchEvent(e);
|
||||
}
|
||||
|
||||
_doMove(aResetPosition) {
|
||||
this._lastMoveDate = Date.now();
|
||||
|
||||
// invalidateCache is true at first load and whenever an attribute
|
||||
// is changed
|
||||
if (this.invalidateCache) {
|
||||
this.invalidateCache = false; // we only want this to run once every scroll direction change
|
||||
|
||||
var corrvalue = 0;
|
||||
|
||||
switch (this._currentDirection) {
|
||||
case "up": {
|
||||
let height = this.window.getComputedStyle(this.element).height;
|
||||
this.outerDiv.style.height = height;
|
||||
if (this.originalHeight > this.outerDiv.offsetHeight) {
|
||||
corrvalue = this.originalHeight - this.outerDiv.offsetHeight;
|
||||
}
|
||||
this.innerDiv.style.padding = height + " 0";
|
||||
this.dirsign = 1;
|
||||
this.startAt = (this.element.behavior == "alternate") ? (this.originalHeight - corrvalue) : 0;
|
||||
this.stopAt = (this.element.behavior == "alternate" || this.element.behavior == "slide") ?
|
||||
(parseInt(height) + corrvalue) : (this.originalHeight + parseInt(height));
|
||||
}
|
||||
break;
|
||||
|
||||
case "down": {
|
||||
let height = this.window.getComputedStyle(this.element).height;
|
||||
this.outerDiv.style.height = height;
|
||||
if (this.originalHeight > this.outerDiv.offsetHeight) {
|
||||
corrvalue = this.originalHeight - this.outerDiv.offsetHeight;
|
||||
}
|
||||
this.innerDiv.style.padding = height + " 0";
|
||||
this.dirsign = -1;
|
||||
this.startAt = (this.element.behavior == "alternate") ?
|
||||
(parseInt(height) + corrvalue) : (this.originalHeight + parseInt(height));
|
||||
this.stopAt = (this.element.behavior == "alternate" || this.element.behavior == "slide") ?
|
||||
(this.originalHeight - corrvalue) : 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case "right":
|
||||
if (this.innerDiv.offsetWidth > this.outerDiv.offsetWidth) {
|
||||
corrvalue = this.innerDiv.offsetWidth - this.outerDiv.offsetWidth;
|
||||
}
|
||||
this.dirsign = -1;
|
||||
this.stopAt = (this.element.behavior == "alternate" || this.element.behavior == "slide") ?
|
||||
(this.innerDiv.offsetWidth - corrvalue) : 0;
|
||||
this.startAt = this.outerDiv.offsetWidth + ((this.element.behavior == "alternate") ?
|
||||
corrvalue : (this.innerDiv.offsetWidth + this.stopAt));
|
||||
break;
|
||||
|
||||
case "left":
|
||||
default:
|
||||
if (this.innerDiv.offsetWidth > this.outerDiv.offsetWidth) {
|
||||
corrvalue = this.innerDiv.offsetWidth - this.outerDiv.offsetWidth;
|
||||
}
|
||||
this.dirsign = 1;
|
||||
this.startAt = (this.element.behavior == "alternate") ? (this.innerDiv.offsetWidth - corrvalue) : 0;
|
||||
this.stopAt = this.outerDiv.offsetWidth +
|
||||
((this.element.behavior == "alternate" || this.element.behavior == "slide") ?
|
||||
corrvalue : (this.innerDiv.offsetWidth + this.startAt));
|
||||
}
|
||||
|
||||
if (aResetPosition) {
|
||||
this.newPosition = this.startAt;
|
||||
this._fireEvent("start", false, false);
|
||||
}
|
||||
} // end if
|
||||
|
||||
this.newPosition = this.newPosition + (this.dirsign * this.element.scrollAmount);
|
||||
|
||||
if ((this.dirsign == 1 && this.newPosition > this.stopAt) ||
|
||||
(this.dirsign == -1 && this.newPosition < this.stopAt)) {
|
||||
switch (this.element.behavior) {
|
||||
case "alternate":
|
||||
// lets start afresh
|
||||
this.invalidateCache = true;
|
||||
|
||||
// swap direction
|
||||
const swap = {left: "right", down: "up", up: "down", right: "left"};
|
||||
this._currentDirection = swap[this._currentDirection] || "left";
|
||||
this.newPosition = this.stopAt;
|
||||
|
||||
if ((this._currentDirection == "up") || (this._currentDirection == "down")) {
|
||||
this.outerDiv.scrollTop = this.newPosition;
|
||||
} else {
|
||||
this.outerDiv.scrollLeft = this.newPosition;
|
||||
}
|
||||
|
||||
if (this._currentLoop != 1) {
|
||||
this._fireEvent("bounce", false, true);
|
||||
}
|
||||
break;
|
||||
|
||||
case "slide":
|
||||
if (this._currentLoop > 1) {
|
||||
this.newPosition = this.startAt;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
this.newPosition = this.startAt;
|
||||
|
||||
if ((this._currentDirection == "up") || (this._currentDirection == "down")) {
|
||||
this.outerDiv.scrollTop = this.newPosition;
|
||||
} else {
|
||||
this.outerDiv.scrollLeft = this.newPosition;
|
||||
}
|
||||
|
||||
// dispatch start event, even when this._currentLoop == 1, comp. with IE6
|
||||
this._fireEvent("start", false, false);
|
||||
}
|
||||
|
||||
if (this._currentLoop > 1) {
|
||||
this._currentLoop--;
|
||||
} else if (this._currentLoop == 1) {
|
||||
if ((this._currentDirection == "up") || (this._currentDirection == "down")) {
|
||||
this.outerDiv.scrollTop = this.stopAt;
|
||||
} else {
|
||||
this.outerDiv.scrollLeft = this.stopAt;
|
||||
}
|
||||
this.element.stop();
|
||||
this._fireEvent("finish", false, true);
|
||||
return;
|
||||
}
|
||||
} else if ((this._currentDirection == "up") || (this._currentDirection == "down")) {
|
||||
this.outerDiv.scrollTop = this.newPosition;
|
||||
} else {
|
||||
this.outerDiv.scrollLeft = this.newPosition;
|
||||
}
|
||||
|
||||
var myThis = this;
|
||||
var lambda = function myTimeOutFunction() { myThis._doMove(false); };
|
||||
this.runId = this.window.setTimeout(lambda, this.scrollDelayWithTruespeed);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.element.stop();
|
||||
|
||||
if ((this._currentDirection != "up") && (this._currentDirection != "down")) {
|
||||
var width = this.window.getComputedStyle(this.element).width;
|
||||
this.innerDiv.parentNode.style.margin = "0 " + width;
|
||||
|
||||
// XXX Adding the margin sometimes causes the marquee to widen,
|
||||
// see testcase from bug bug 364434:
|
||||
// https://bugzilla.mozilla.org/attachment.cgi?id=249233
|
||||
// Just add a fixed width with current marquee's width for now
|
||||
if (width != this.window.getComputedStyle(this.element).width) {
|
||||
width = this.window.getComputedStyle(this.element).width;
|
||||
this.outerDiv.style.width = width;
|
||||
this.innerDiv.parentNode.style.margin = "0 " + width;
|
||||
}
|
||||
} else {
|
||||
// store the original height before we add padding
|
||||
this.innerDiv.style.padding = 0;
|
||||
this.originalHeight = this.innerDiv.offsetHeight;
|
||||
}
|
||||
|
||||
this._doMove(true);
|
||||
}
|
||||
|
||||
_mutationActor(aMutations) {
|
||||
while (aMutations.length > 0) {
|
||||
var mutation = aMutations.shift();
|
||||
var attrName = mutation.attributeName.toLowerCase();
|
||||
var oldValue = mutation.oldValue;
|
||||
var target = mutation.target;
|
||||
var newValue = target.getAttribute(attrName);
|
||||
|
||||
if (oldValue != newValue) {
|
||||
this.invalidateCache = true;
|
||||
switch (attrName) {
|
||||
case "loop":
|
||||
this._currentLoop = target.loop;
|
||||
break;
|
||||
case "direction":
|
||||
this._currentDirection = target.direction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.MarqueeHorizontalImplWidget = class extends MarqueeBaseImplWidget {
|
||||
// White-space isn't allowed because a marquee could be
|
||||
// inside 'white-space: pre'
|
||||
generateContent() {
|
||||
this.shadowRoot.innerHTML = `<div class="horizontalContainer"
|
||||
><link rel="stylesheet" type="text/css" href="chrome://global/content/elements/marquee.css"
|
||||
/><div class="horizontalOuterDiv"
|
||||
><div id="innerDiv" class="horizontalInnerDiv"
|
||||
><div
|
||||
><slot
|
||||
/></div
|
||||
></div
|
||||
></div
|
||||
></div>`;
|
||||
}
|
||||
};
|
||||
|
||||
this.MarqueeVerticalImplWidget = class extends MarqueeBaseImplWidget {
|
||||
// White-space isn't allowed because a marquee could be
|
||||
// inside 'white-space: pre'
|
||||
generateContent() {
|
||||
this.shadowRoot.innerHTML = `<div class="verticalContainer"
|
||||
><link rel="stylesheet" type="text/css" href="chrome://global/content/elements/marquee.css"
|
||||
/><div id="innerDiv" class="verticalInnerDiv"><slot /></div
|
||||
></div>`;
|
||||
}
|
||||
};
|
Загрузка…
Ссылка в новой задаче