mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
fb49d72bb0
- Bug 1252882 - Content-Signature Service - some tests r=keeler,r=fkiefer (7fc1f726a4)
- Bug 1265085 - Replace verification source with a SAN in the content signature verifier interface. r=Cykesiopka,r=fkiefer (0881ba797d)
- Bug 1264706: Move nsILocalCertService, and implementation, to security/manager/ssl in order to alloow use w use elsewhere in gecko. r=dkeeler (6f2200f2a6)
- Bug 1219088 - Clear the session cache when a weak crypto override is revoked. r=keeler (456e2ce3e6)
- Bug 550185 - Ensure nsCertTree::GetCellText returns an initialized value. r=kaie (ee356452a5)
- Bug 1252384 - Remove nsICertTree.isHostPortOverride(). r=dkeele It is unused since the changes in Bug 825583 landed. (61400adad7)
- reorder as Bug 1411458 (600fc338a9)
- bug 1264761 - improve handling of x509 versions in certificate manager r=Cykesiopka (a89237c66d)
- Bug 1263857 - (followup) Disable windows crash reporter on automated tests. r=sfink (bb9581fec9)
- bug 1263221 - improve how PSM handles the visibility of __CERT_AddTempCertToPerm r=chmanchester,mgoodwin (d9fd09d9a6)
- bug 1182742 - allow users to override small key size errors r=rbarnes (b09074987b)
- Bug 1252722 - Fully implement nsNSSShutDownObject everywhere. r=keeler (8c1a8df597)
- Bug 1252722 - Use smart pointers for NSS resources. r=keeler (b2ef34f9d2)
- Bug 1252722 - Ensure arguments of all public methods are checked. r=keeler (7064697e25)
- bug 1242032 - change some pipnss logging output from Debug to Verbose r=Cykesiopka (516c52da9f)
- Bug 1252722 - Improve handling of PK11_* function error codes. r=keeler (b47d13bd7e)
- Bug 1251801 - Ensure arguments of all public methods are checked. r=keeler (ffe77174e7)
- Bug 1127158 - Remove brittle debug only flag math in nsSecureBrowserUIImpl.cpp. r=dkeeler (410d25dc3e)
- Bug 1257246: Update security/manager for eslint 2. r=cykesiopka (bbdcf78264)
- bug 1218515 - flip pinning-test.badssl.com into production mode r=jcj DONTBUILD NPOTB (0d93e78cab)
- Bug 1199624 - Don't use memset and memcmp in files that don't include cstring explicitly. r=briansmith (5a938e056c)
- Bug 1154399 - Part 1: De-templatize and un-inline IntegralValue. r=keeler (bcaa11a646)
- Bug 1154399 - Part 2: Simplify and un-inline OptionalVersion. r=keeler (a5a9bb5e46)
- Bug 1154399 - Part 3: Simplify OptionalExtensions. r=keeler (0d3f613cde)
- Bug 1154399 - Part 4: Simplify certificate parsing in OCSP responses. r=keeler (94aa90a96d)
- Bug 1189020 - Replace |// unnamed namespace| with |// namespace| in mozilla::pkix. r=Cykesiopka (bb016e13ac)
- bug 1255153 - (re)move redundant xpcshell name constraint tests to gtests r=Cykesiopka,jcj (d8597a3bb1)
- bug 1248099 - add extended key usage tests for mozilla::pkix r=Cykesiopka,jcj (f9c11a8ecc)
- Bug 1266298 - Add sys_fchmod to seccomp whitelist r=jld (e16608d738)
- Bug 1268579 - Add inotify_rm_watch to the seccomp-bpf whitelist. r=jld (c20823e237)
- Bug 1176099 - Add hooks for sigprocmask/pthread_sigmask. r=jld r=glandium (55d16a8e41)
- Bug 1176099 - Fix missing NULL check r=luke (2f6e6e3836)
- Bug 1245789 - Use ifdef MOZ_WIDEVINE_EME to prevent compilation when not enabled. r=gerald (40d13ca2d9)
- Bug 1267453 - Amazon Widevine rejects HDCP on MacBook Pro with or without an external display. r=gcp (5366006c54)
- Bug 1268379 - Delay WMF checks in GMPParent - r=jesup (0f6642fdcc)
- bit of Bug 1245789 - Push detection of WMF decoding (e60b0634de)
- missing bit of 1267453 (573b867a62)
- Bug 1243594 (part 3) - leave the utf-8 encoding of the payload to rest.js instead of directly in loop. r=Standard8 (7781df1275)
- Bug 1243594 (part 2) - have rest.js automatically encode the request body as utf-8. r=gfritzsche (fd98dddf43)
- Bug 1246938 - Allow extra headers to be passed via hawk requests. r=markh (3692244524)
- Bug 1239354: Replace old-style generator function with star functions. r=asuth (a7793a89aa)
- Bug 1217982 - Remove for-each from storage/. r=mak (bbff0f0b7b)
- Bug 1223510, part 1 - Always finishTest() in test_nonUnicode.html. r=baku (ba3fb681ad)
- Bug 1223510, part 2 - Make ArchiveReader tests use pushPrefEnv. r=baku (5a4a5f9a7a)
- Bug 1223510, part 3 - Change where generator is created. r=baku Mostly this lets us take advantage of things created during the initial setup. (ee337d83de)
- Bug 1220304 - Part 1 - Make test_XHRSendData.html use SpecialPowers.createFiles(). r=baku (91fc25f3b7)
- Bug 1220304 - Part 2 - Make ArchiveReader tests use SpecialPowers.createFiles(). r=baku (5a0bfc4200)
- Bug 1267966 - Remove the deprecation warning from Attr.ownerElement; r=baku (cfd8910f2f)
- Bug 1269646 - Console API should be NOP after window-inner-destroyed, r=smaug (c4e5959d1f)
- Bug 1263392 - ConsoleCallData::mStatus should be set also when ConsoleCallData is not used in workers, r=smaug (10358e33a1)
- Bug 1268361 - Strip leading '?' in new URLSearchParams(query), r=smaug (a1a5075185)
- Bug 1052139 - Make more objects on the global prototype chain have immutable [[Prototype]], when we enable enforcement of this requirement. r=bz (8e48cfc3d6)
- Bug 1267932 - Update EventSource.webidl, r=ehsan (41532a54d5)
- Bug 1269383 - Remove dom.server-events.enabled pref, r=smaug (21e65b8436)
- Bug 1237077 - Part 3: use createCodebasePrincipal. r=tanvi (7da7268d47)
- Bug 1268721, part 1 - Use early continue in TransferableToIPCTransferable. r=jimm (317ab04f38)
- Bug 1268721, part 2 - Null check first argument to nsContentUtils::GetSurfaceData(). r=jimm (544d181021)
- Bug 1272203 (part 3) - Use NotNull in nsContentUtils::GetSurfaceData(). r=froydnj. (65e488c4f8)
- Bug 1272203 (part 1) - Add mozilla::NotNull to MFBT. r=froydnj. (4653d120fc)
- Bug 964092: don't let DOM DataChannels get GC'd if they have an active callback r=smaug,jib (c9c291f44a)
- Bug 1224186: Implement DOMTokenlist.replace r=baku,Ms2ger (329f4f942e)
- Bug 1265715 - Part 1. Pull Mode out of nsDisplayListBuilder; r=jfkthame (2c7cae0f3a)
- Bug 1265715 - Part 2. Add nsDisplayListBuilderMode parameter into nsLayoutUtils::PaintFrame; r=jfkthame (5ecabbda5d)
- Bug 1264949 - Ensure that the display list does not contain any background-image/background-color display item; r=jfkthame (018a7aec15)
- Bug 1265715 - Part 3. Use nsLayoutUtils::PaintFrame in ClipBackgroundByText; r=jfkthame (182a700fa6)
- Bug 1265715 - Part 4. Fix transform problem; r=jfkthame (37f77bf24b)
- Bug 1265715 - Part 5. bg-clip:text transform reftest; r=jfkthame (44778bcfd7)
- Bug 1267209 - Convert nsLayoutUtils::PaintFrame flags to be an enum class. r=jfkthame (4f304b84f1)
- Bug 1265280 - Temporary debugging code to crash with a useful abort message. r=khuey (43d532166f)
- Bug 1232939 - Ensure the opaque region of a fixed background layer is correctly clipped. r=mstange (cc2118e0a0)
- Bug 735857 - Treat background-attachment:fixed as background-attachment:scroll if it's on a non-root element affected by a transform. r=mstange (273d62aabf)
- Bug 735857 - Factor out a helper function nsLayoutUtils::IsTransformed(). r=mstange (61528fafef)
- Bug 1263286 - Move base-uri CSP check into SetBaseURIUsingFirstBaseWithHref. r=bz (03114b2fcb)
- Bug 1227327 - Allow specifying a background rect for background dislay items. r=mattwoodrow (410ef269eb)
- Bug 1227327 - Make fieldset frames build nsDisplayBackgroundImage items. r=mattwoodrow (9a3a8953b4)
- Fix temporary debugging patch for bug 1265280 so we'll hit the condition. r=khuey (fd30f8f0b4)
- Bug 1265715 - followup - Correct dirty region; r=me (38fc76e698)
- Bug 550426 - Use background-position-x/y in ActiveLayerTracker. r=dbaron (4f154a39dd)
- Bug 1266131 part 1 - [css-grid] 'order' doesn't apply to grid-aligned abs.pos. descendants (anymore). r=dholbert (ba8aa18fea)
- Bug 1266131 part 2 - [css-grid] Remove unused nsDisplayList::SortByCSSOrder() function. r=dholbert (b676c48a26)
- Bug 550426 - Add support for {background,mask}-position-{x,y}, most of the style system changes. r=dbaron (3739a8ec58)
- Bug 550426 - Add support for {background,mask}-position-{x,y}, StyleAnimation changes. r=dbaron (0fd2f97a60)
- Bug 852754 - Part 4: Reduce max downscaling allowed to <3. r=mstange (399b851221)
- Bug 1266868, part 1 - Fix nsCSSValue::Array leaks in the StyleAnimationValue code. r=dholbert (c6fc4f7d9c)
- Bug 1266868, part 2 - Fix leaks of the values passed to nsCSSValue::.SetPairValue in the StyleAnimationValue code. r=dholbert (e5a1ff8603)
- Bug 1266868, part 3 - Avoid Maybe::ref() where not necessary. r=dholbert (8dd435fd5e)
- Back out bug 1164227, because bug 1236043 fixes the original problem in a better way. (6b734f0718)
- Bug 1267524 Part 1 - Use member initializer list for nsStyleOutline. r=heycam (d7cabb2ea8)
- Bug 1267524 Part 2 - Use member initializer list for nsStyleXUL. r=heycam (82107506a6)
- Bug 1267524 Part 3 - Use member initializer list for nsStyleColumn. r=heycam (80318b0056)
- Bug 1267524 Part 4.1 - Add Reset() and rewrite methods for nsStyleSVGPaint. r=heycam (02ba8762cb)
- Bug 1267524 Part 4.2 - Use member initializer list for nsStyleSVG. r=heycam (5531ed4a93)
- Bug 1267524 Part 5 - Use member initializer list for nsStyleSVGReset. r=heycam (4a72005b1b)
- Bug 1267524 Part 6 - Use member initializer list for nsStylePosition. r=heycam (cfd6a8b640)
- Bug 1267524 Part 7 - Use member initializer list for nsStyleTable. r=heycam (510678ed8b)
- Bug 1267524 Part 8 - Use member initializer list for nsStyleTableBorder. r=heycam (c1617af193)
- Bug 1267524 Part 9 - Use member initializer list for nsStyleColor. r=heycam (753afba9f8)
- Bug 1267524 Part 10 - Use member initializer list for nsStyleDisplay. r=heycam (a6cc7ce52b)
- Bug 1267524 Part 11 - Use member initializer list for nsStyleVisibility. r=heycam (c93d75480e)
- Bug 1267524 Part 12 - Use member initializer list for nsStyleContent. r=heycam (ccc17aa74a)
- Bug 1267524 Part 13 - Use member initializer list for nsStyleTextReset. r=heycam (fc8b6ae837)
- Bug 1267524 Part 14 - Use member initializer list for nsStyleText. r=heycam (d73abb7d32)
- Bug 1267524 Part 15 - Use member initializer list for nsStyleUserInterface. r=heycam (3964558f27)
- Bug 1267524 Part 16 - Use member initializer list for nsStyleUIReset. r=heycam (3154cbc7d2)
- Bug 1267524 Part 17 - Use member initializer list for nsStyleVariables. r=heycam (3d286d2299)
- Bug 1267524 Part 18 - Remove "void" from zero argument functions. r=heycam (57b1a87c19)
- Bug 1267524 Part 19 - Move nsStyleCoord members to initializer list. r=heycam (385231a406)
- Bug 1227327 - Invalidate table parts and MathML frames when background-position changes on them. r=dbaron (088fad2be7)
- Bug 1268290: stylo: Pass SheetParsingMode to Servo, r=bholley (f05d51b7b1)
- Bug 1267833 - Pass the RawServoStyleSet to Servo_GetComputedValuesForAnonymousBox. r=heycam (c4870e2005)
- Bug 1268392 - Make Servo_GetComputedValues take a node rather than an element. r=bholley (61230bdc1f)
- Bug 1268390 - Part 1: Factor out most of nsStyleSet::AddDocStyleSheet for re-use. r=bholley (5bd89657a1)
- Bug 1268390 - Part 2: Add bindings for Servo_InsertStyleSheetBefore. r=bholley (f83ea77d37)
- Bug 1268404 - Part 1: Split out ResolveStyleForText from ResolveStyleForNonElement and pass in the text node. r=bholley (dc40bbc9dc)
- Bug 1268404 - Part 2: Implement ServoStyleSet::ResolveStyleForText. r=bholley (0c6bffbd4b)
- Bug 1268748 - Implement {Resolve,Probe}PseudoElementStyle. r=heycam (cd674703d6)
- Bug 1267560 - Get style structs from ServoComputedValues rather than the rule node, when using the Servo-backed style system. r=bholley (62784ed0ee)
- Bug 1268290 followup: remove stray semicolon on a CLOSED TREE. (609540fab1)
- Bug 1268390 - Part 3: Add support for doc style sheets in ServoStyleSet. r=bholley (787cee0d54)
- Bug 1267564 - Implement a couple of Servo-backed style object methods. r=bholley (b27b0f78a0)
- Bug 1250820 - Part 1: Define scoped enum for CSSPseudoClass::Type. r=heycam (c2992f4c01)
- Bug 1250820 - Part 2: Replace nsCSSPseudoClasses::Type with CSSPseudoClassType. r=heycam (8102ab491b)
- Bug 1250820 - Part 3: Replace notPseudo with negation. r=heycam (94f4b95650)
- Bug 1250820 - Part 4: Add MAX to CSSPseudoClassType. r=heycam (85acf2bc45)
- Bug 1206961 - Use channel->AsyncOpen2() for imageLoader; Remove security checks from callsites (r=bz) (0d5b91ca12)
- Bug 1134163 - Part1.Modify animationstart event timing in order to fire event after end of pending task. r=birtles (e2c333fb8d)
- Bug 1134163 - Part2 - Modify animation tests which rely on animationstart timing. r=birtles (fb780f4298)
- Bug 1067769 - Part 1: Avoid doing RequestRestyle and mutation batch for null target. r=birtles (c3a0c1a1ef)
- Bug 1067769 - Part 2: Support nullable target in KeyframeEffect(ReadOnly) constructor. r=birtles (e8ac02ebf0)
- Bug 1067769 - Part 3: Test for KeyframeEffectReadOnly with null target. r=birtles (9ee7fc48c3)
- Bug 1067769 - Part 4: Add some simple tests for document.getAnimation() in wpt. r=birtles (77c18ad32a)
- Bug 1067769 - Part 5: Support setting KeyframeEffect.target webidl interface. r=smaug (cb450cd6f7)
- Bug 1067769 - Part 6: Rename NonOwningAnimationTarget.h to AnimationTarget.h. r=birtles (45083b4141)
- Bug 1067769 - Part 7: Define OwningAnimationTarget and use it. r=birtles (0a716665aa)
- Bug 1067769 - Part 8: Add ConvertTarget function. r=birtles (2663246043)
- Bug 1067769 - Part 9: Wrap RequestRestyle and UnregisterTarget. r=birtles (1deb75c7e0)
- Bug 1067769 - Part 10: Implement SetTarget(). r=birtles (0823f6da17)
- Bug 1067769 - Part 11: Implement animation mutation observer while setting the target. r=birtles (8224724c49)
- Bug 1067769 - Part 12: Use Maybe<OwningAnimationTarget> in KeyframeEffect(ReadOnly) constructors. r=birtles (e057c15804)
- Bug 1067769 - Part 13: Test for setting the target in basic cases. r=birtles (b6a638a268)
- Bug 1067769 - Part 14: Test for our animation mutation observer. r=birtles (5381522d25)
- Bug 1264067 - [css-grid] 'fr' min-sizing is now invalid. r=dholbert (abc7d63364)
- Bug 550426 - Add support for {background,mask}-position-{x,y}, computed style additions. r=dbaron (0dea650527)
- Bug 1266948 - text-decoration-color: currentcolor should not use value from -webkit-text-fill-color; r=jfkthame (9b36b2f493)
- Bug 1271590 - Rename timespecadd to moz_timespecadd. r=jandem (4b417dabae)
- Bug 550426 - Use background-position-x/y when detecting scroll-linked effects. r=dbaron (478331b348)
- Bug 1227327 - Use regular background drawing for XUL groupbox frames. r=mattwoodrow (db42359656)
- Bug 1260329 - Properly escape the frameTable when running |dmd.py --clamp-contents|. r=mccr8. (b40a5a0f49)
- Bug 1148544 - Update tests to work with new way of handling user agent overrides. r=jchen (2cede65d5b)
- Bug 1262326 - Make test_user_agent_overrides.html work in e10s r=nwgh (81c4d7ba00)
- Bug 1180107: Factor out logic for determining whether a flex item's main size could influence cross size. r=mats (94b89305ea)
- Bug 1267471 - Check the snap info when comparing scroll metadata for equality. rs=botond (e5a40f0387)
- Bug 1257288 - Improve the APZ gtest infrastructure to make writing multi-FrameMetrics tests easier. r=kats (b4b898abc2)
- Bug 1256344 - Add a gtest to catch scenarios where the long-press block is interrupted by a non-touch block. r=botond (84982b1ba7)
- Bug 1265510 - Add a gtest for interrupting a scroll snap. r=botond (5f33cdadea)
- Bug 1246290 - Add a simple gtest to exercise the force-disabled-APZ codepaths. r=botond (be91113c70)
- Bug 1267470 - Move more fields from FrameMetrics to ScrollMetadata. r=kats (259f44ab15)
- Bug 1030952 part 4: For flex items with an aspect ratio, stomp on reflow state's main size *and cross size* in final reflow. r=mats (3f02ed9761)
- Bug 550426 - In PropertySupportsVariant, add {background,mask}-position-{x,y} to the list of properties that are parsed by functions. r=dbaron (b350dd9ec4)
- Bug 1258609: Initialize nsICanvasRenderingContextInternal with a DrawTarget instead of a gfxASurface. r=jrmuizel (236656c82d)
- Merge remote-tracking branch 'upstream/dev' into winbuild (c0659b547d)
- [mfbt] NotNull: VC2013 fix (86139057b8)
- layout: put back array initializations back to function body, fix VC2013 build. (3ac23f6474)
1087 lines
38 KiB
C++
1087 lines
38 KiB
C++
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
|
/* 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/. */
|
|
|
|
#include "nsAnimationManager.h"
|
|
#include "nsTransitionManager.h"
|
|
#include "mozilla/dom/CSSAnimationBinding.h"
|
|
|
|
#include "mozilla/EffectCompositor.h"
|
|
#include "mozilla/EffectSet.h"
|
|
#include "mozilla/MemoryReporting.h"
|
|
#include "mozilla/StyleAnimationValue.h"
|
|
#include "mozilla/dom/DocumentTimeline.h"
|
|
#include "mozilla/dom/KeyframeEffect.h"
|
|
|
|
#include "nsPresContext.h"
|
|
#include "nsStyleSet.h"
|
|
#include "nsStyleChangeList.h"
|
|
#include "nsCSSRules.h"
|
|
#include "mozilla/RestyleManager.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "nsIFrame.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsDOMMutationObserver.h"
|
|
#include <algorithm> // std::stable_sort
|
|
#include <math.h>
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::css;
|
|
using mozilla::dom::Animation;
|
|
using mozilla::dom::AnimationPlayState;
|
|
using mozilla::dom::KeyframeEffectReadOnly;
|
|
using mozilla::dom::CSSAnimation;
|
|
|
|
namespace {
|
|
|
|
// Pair of an event message and elapsed time used when determining the set of
|
|
// events to queue.
|
|
typedef Pair<EventMessage, StickyTimeDuration> EventPair;
|
|
|
|
} // anonymous namespace
|
|
|
|
////////////////////////// CSSAnimation ////////////////////////////
|
|
|
|
JSObject*
|
|
CSSAnimation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
{
|
|
return dom::CSSAnimationBinding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
mozilla::dom::Promise*
|
|
CSSAnimation::GetReady(ErrorResult& aRv)
|
|
{
|
|
FlushStyle();
|
|
return Animation::GetReady(aRv);
|
|
}
|
|
|
|
void
|
|
CSSAnimation::Play(ErrorResult &aRv, LimitBehavior aLimitBehavior)
|
|
{
|
|
mPauseShouldStick = false;
|
|
Animation::Play(aRv, aLimitBehavior);
|
|
}
|
|
|
|
void
|
|
CSSAnimation::Pause(ErrorResult& aRv)
|
|
{
|
|
mPauseShouldStick = true;
|
|
Animation::Pause(aRv);
|
|
}
|
|
|
|
AnimationPlayState
|
|
CSSAnimation::PlayStateFromJS() const
|
|
{
|
|
// Flush style to ensure that any properties controlling animation state
|
|
// (e.g. animation-play-state) are fully updated.
|
|
FlushStyle();
|
|
return Animation::PlayStateFromJS();
|
|
}
|
|
|
|
void
|
|
CSSAnimation::PlayFromJS(ErrorResult& aRv)
|
|
{
|
|
// Note that flushing style below might trigger calls to
|
|
// PlayFromStyle()/PauseFromStyle() on this object.
|
|
FlushStyle();
|
|
Animation::PlayFromJS(aRv);
|
|
}
|
|
|
|
void
|
|
CSSAnimation::PlayFromStyle()
|
|
{
|
|
mIsStylePaused = false;
|
|
if (!mPauseShouldStick) {
|
|
ErrorResult rv;
|
|
DoPlay(rv, Animation::LimitBehavior::Continue);
|
|
// play() should not throw when LimitBehavior is Continue
|
|
MOZ_ASSERT(!rv.Failed(), "Unexpected exception playing animation");
|
|
}
|
|
}
|
|
|
|
void
|
|
CSSAnimation::PauseFromStyle()
|
|
{
|
|
// Check if the pause state is being overridden
|
|
if (mIsStylePaused) {
|
|
return;
|
|
}
|
|
|
|
mIsStylePaused = true;
|
|
ErrorResult rv;
|
|
DoPause(rv);
|
|
// pause() should only throw when *all* of the following conditions are true:
|
|
// - we are in the idle state, and
|
|
// - we have a negative playback rate, and
|
|
// - we have an infinitely repeating animation
|
|
// The first two conditions will never happen under regular style processing
|
|
// but could happen if an author made modifications to the Animation object
|
|
// and then updated animation-play-state. It's an unusual case and there's
|
|
// no obvious way to pass on the exception information so we just silently
|
|
// fail for now.
|
|
if (rv.Failed()) {
|
|
NS_WARNING("Unexpected exception pausing animation - silently failing");
|
|
}
|
|
}
|
|
|
|
void
|
|
CSSAnimation::Tick()
|
|
{
|
|
Animation::Tick();
|
|
QueueEvents();
|
|
}
|
|
|
|
bool
|
|
CSSAnimation::HasLowerCompositeOrderThan(const CSSAnimation& aOther) const
|
|
{
|
|
MOZ_ASSERT(IsTiedToMarkup() && aOther.IsTiedToMarkup(),
|
|
"Should only be called for CSS animations that are sorted "
|
|
"as CSS animations (i.e. tied to CSS markup)");
|
|
|
|
// 0. Object-equality case
|
|
if (&aOther == this) {
|
|
return false;
|
|
}
|
|
|
|
// 1. Sort by document order
|
|
if (!mOwningElement.Equals(aOther.mOwningElement)) {
|
|
return mOwningElement.LessThan(aOther.mOwningElement);
|
|
}
|
|
|
|
// 2. (Same element and pseudo): Sort by position in animation-name
|
|
return mAnimationIndex < aOther.mAnimationIndex;
|
|
}
|
|
|
|
void
|
|
CSSAnimation::QueueEvents()
|
|
{
|
|
if (!mEffect) {
|
|
return;
|
|
}
|
|
|
|
// If the animation is pending, we ignore animation events until we finish
|
|
// pending.
|
|
if (mPendingState != PendingState::NotPending) {
|
|
return;
|
|
}
|
|
|
|
// CSS animations dispatch events at their owning element. This allows
|
|
// script to repurpose a CSS animation to target a different element,
|
|
// to use a group effect (which has no obvious "target element"), or
|
|
// to remove the animation effect altogether whilst still getting
|
|
// animation events.
|
|
//
|
|
// It does mean, however, that for a CSS animation that has no owning
|
|
// element (e.g. it was created using the CSSAnimation constructor or
|
|
// disassociated from CSS) no events are fired. If it becomes desirable
|
|
// for these animations to still fire events we should spec the concept
|
|
// of the "original owning element" or "event target" and allow script
|
|
// to set it when creating a CSSAnimation object.
|
|
if (!mOwningElement.IsSet()) {
|
|
return;
|
|
}
|
|
|
|
dom::Element* owningElement;
|
|
CSSPseudoElementType owningPseudoType;
|
|
mOwningElement.GetElement(owningElement, owningPseudoType);
|
|
MOZ_ASSERT(owningElement, "Owning element should be set");
|
|
|
|
// Get the nsAnimationManager so we can queue events on it
|
|
nsPresContext* presContext = mOwningElement.GetRenderedPresContext();
|
|
if (!presContext) {
|
|
return;
|
|
}
|
|
nsAnimationManager* manager = presContext->AnimationManager();
|
|
|
|
ComputedTiming computedTiming = mEffect->GetComputedTiming();
|
|
|
|
if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Null) {
|
|
return; // do nothing
|
|
}
|
|
|
|
// Note that script can change the start time, so we have to handle moving
|
|
// backwards through the animation as well as forwards. An 'animationstart'
|
|
// is dispatched if we enter the active phase (regardless if that is from
|
|
// before or after the animation's active phase). An 'animationend' is
|
|
// dispatched if we leave the active phase (regardless if that is to before
|
|
// or after the animation's active phase).
|
|
|
|
bool wasActive = mPreviousPhaseOrIteration != PREVIOUS_PHASE_BEFORE &&
|
|
mPreviousPhaseOrIteration != PREVIOUS_PHASE_AFTER;
|
|
bool isActive =
|
|
computedTiming.mPhase == ComputedTiming::AnimationPhase::Active;
|
|
bool isSameIteration =
|
|
computedTiming.mCurrentIteration == mPreviousPhaseOrIteration;
|
|
bool skippedActivePhase =
|
|
(mPreviousPhaseOrIteration == PREVIOUS_PHASE_BEFORE &&
|
|
computedTiming.mPhase == ComputedTiming::AnimationPhase::After) ||
|
|
(mPreviousPhaseOrIteration == PREVIOUS_PHASE_AFTER &&
|
|
computedTiming.mPhase == ComputedTiming::AnimationPhase::Before);
|
|
bool skippedFirstIteration =
|
|
isActive &&
|
|
mPreviousPhaseOrIteration == PREVIOUS_PHASE_BEFORE &&
|
|
computedTiming.mCurrentIteration > 0;
|
|
|
|
MOZ_ASSERT(!skippedActivePhase || (!isActive && !wasActive),
|
|
"skippedActivePhase only makes sense if we were & are inactive");
|
|
|
|
if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Before) {
|
|
mPreviousPhaseOrIteration = PREVIOUS_PHASE_BEFORE;
|
|
} else if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Active) {
|
|
mPreviousPhaseOrIteration = computedTiming.mCurrentIteration;
|
|
} else if (computedTiming.mPhase == ComputedTiming::AnimationPhase::After) {
|
|
mPreviousPhaseOrIteration = PREVIOUS_PHASE_AFTER;
|
|
}
|
|
|
|
AutoTArray<EventPair, 2> events;
|
|
StickyTimeDuration initialAdvance = StickyTimeDuration(InitialAdvance());
|
|
StickyTimeDuration iterationStart = computedTiming.mDuration *
|
|
computedTiming.mCurrentIteration;
|
|
const StickyTimeDuration& activeDuration = computedTiming.mActiveDuration;
|
|
|
|
if (skippedFirstIteration) {
|
|
// Notify animationstart and animationiteration in same tick.
|
|
events.AppendElement(EventPair(eAnimationStart, initialAdvance));
|
|
events.AppendElement(EventPair(eAnimationIteration,
|
|
std::max(iterationStart, initialAdvance)));
|
|
} else if (!wasActive && isActive) {
|
|
events.AppendElement(EventPair(eAnimationStart, initialAdvance));
|
|
} else if (wasActive && !isActive) {
|
|
events.AppendElement(EventPair(eAnimationEnd, activeDuration));
|
|
} else if (wasActive && isActive && !isSameIteration) {
|
|
events.AppendElement(EventPair(eAnimationIteration, iterationStart));
|
|
} else if (skippedActivePhase) {
|
|
events.AppendElement(EventPair(eAnimationStart,
|
|
std::min(initialAdvance, activeDuration)));
|
|
events.AppendElement(EventPair(eAnimationEnd, activeDuration));
|
|
} else {
|
|
return; // No events need to be sent
|
|
}
|
|
|
|
for (const EventPair& pair : events){
|
|
manager->QueueEvent(
|
|
AnimationEventInfo(owningElement, owningPseudoType,
|
|
pair.first(), mAnimationName,
|
|
pair.second(),
|
|
ElapsedTimeToTimeStamp(pair.second()),
|
|
this));
|
|
}
|
|
}
|
|
|
|
void
|
|
CSSAnimation::UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag)
|
|
{
|
|
if (mNeedsNewAnimationIndexWhenRun &&
|
|
PlayState() != AnimationPlayState::Idle) {
|
|
mAnimationIndex = sNextAnimationIndex++;
|
|
mNeedsNewAnimationIndexWhenRun = false;
|
|
}
|
|
|
|
Animation::UpdateTiming(aSeekFlag, aSyncNotifyFlag);
|
|
}
|
|
|
|
TimeStamp
|
|
CSSAnimation::ElapsedTimeToTimeStamp(const StickyTimeDuration&
|
|
aElapsedTime) const
|
|
{
|
|
return AnimationTimeToTimeStamp(aElapsedTime +
|
|
mEffect->SpecifiedTiming().mDelay);
|
|
}
|
|
|
|
////////////////////////// nsAnimationManager ////////////////////////////
|
|
|
|
NS_IMPL_CYCLE_COLLECTION(nsAnimationManager, mEventDispatcher)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsAnimationManager, AddRef)
|
|
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsAnimationManager, Release)
|
|
|
|
// Find the matching animation by |aName| in the old list
|
|
// of animations and remove the matched animation from the list.
|
|
static already_AddRefed<CSSAnimation>
|
|
PopExistingAnimation(const nsAString& aName,
|
|
nsAnimationManager::CSSAnimationCollection* aCollection)
|
|
{
|
|
if (!aCollection) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Animations are stored in reverse order to how they appear in the
|
|
// animation-name property. However, we want to match animations beginning
|
|
// from the end of the animation-name list, so we iterate *forwards*
|
|
// through the collection.
|
|
for (size_t idx = 0, length = aCollection->mAnimations.Length();
|
|
idx != length; ++ idx) {
|
|
CSSAnimation* cssAnim = aCollection->mAnimations[idx];
|
|
if (cssAnim->AnimationName() == aName) {
|
|
RefPtr<CSSAnimation> match = cssAnim;
|
|
aCollection->mAnimations.RemoveElementAt(idx);
|
|
return match.forget();
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static void
|
|
UpdateOldAnimationPropertiesWithNew(
|
|
CSSAnimation& aOld,
|
|
TimingParams& aNewTiming,
|
|
nsTArray<Keyframe>& aNewFrames,
|
|
bool aNewIsStylePaused,
|
|
nsStyleContext* aStyleContext)
|
|
{
|
|
bool animationChanged = false;
|
|
|
|
// Update the old from the new so we can keep the original object
|
|
// identity (and any expando properties attached to it).
|
|
if (aOld.GetEffect()) {
|
|
KeyframeEffectReadOnly* oldEffect = aOld.GetEffect();
|
|
animationChanged =
|
|
oldEffect->SpecifiedTiming() != aNewTiming;
|
|
oldEffect->SetSpecifiedTiming(aNewTiming);
|
|
oldEffect->SetFrames(Move(aNewFrames), aStyleContext);
|
|
}
|
|
|
|
// Handle changes in play state. If the animation is idle, however,
|
|
// changes to animation-play-state should *not* restart it.
|
|
if (aOld.PlayState() != AnimationPlayState::Idle) {
|
|
// CSSAnimation takes care of override behavior so that,
|
|
// for example, if the author has called pause(), that will
|
|
// override the animation-play-state.
|
|
// (We should check aNew->IsStylePaused() but that requires
|
|
// downcasting to CSSAnimation and we happen to know that
|
|
// aNew will only ever be paused by calling PauseFromStyle
|
|
// making IsPausedOrPausing synonymous in this case.)
|
|
if (!aOld.IsStylePaused() && aNewIsStylePaused) {
|
|
aOld.PauseFromStyle();
|
|
animationChanged = true;
|
|
} else if (aOld.IsStylePaused() && !aNewIsStylePaused) {
|
|
aOld.PlayFromStyle();
|
|
animationChanged = true;
|
|
}
|
|
}
|
|
|
|
// Updating the effect timing above might already have caused the
|
|
// animation to become irrelevant so only add a changed record if
|
|
// the animation is still relevant.
|
|
if (animationChanged && aOld.IsRelevant()) {
|
|
nsNodeUtils::AnimationChanged(&aOld);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsAnimationManager::UpdateAnimations(nsStyleContext* aStyleContext,
|
|
mozilla::dom::Element* aElement)
|
|
{
|
|
MOZ_ASSERT(mPresContext->IsDynamic(),
|
|
"Should not update animations for print or print preview");
|
|
MOZ_ASSERT(aElement->IsInComposedDoc(),
|
|
"Should not update animations that are not attached to the "
|
|
"document tree");
|
|
|
|
// Everything that causes our animation data to change triggers a
|
|
// style change, which in turn triggers a non-animation restyle.
|
|
// Likewise, when we initially construct frames, we're not in a
|
|
// style change, but also not in an animation restyle.
|
|
|
|
const nsStyleDisplay* disp = aStyleContext->StyleDisplay();
|
|
CSSAnimationCollection* collection =
|
|
CSSAnimationCollection::GetAnimationCollection(aElement,
|
|
aStyleContext->
|
|
GetPseudoType());
|
|
if (!collection &&
|
|
disp->mAnimationNameCount == 1 &&
|
|
disp->mAnimations[0].GetName().IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
nsAutoAnimationMutationBatch mb(aElement->OwnerDoc());
|
|
|
|
// Build the updated animations list, extracting matching animations from
|
|
// the existing collection as we go.
|
|
OwningCSSAnimationPtrArray newAnimations;
|
|
if (!aStyleContext->IsInDisplayNoneSubtree()) {
|
|
BuildAnimations(aStyleContext, aElement, collection, newAnimations);
|
|
}
|
|
|
|
if (newAnimations.IsEmpty()) {
|
|
if (collection) {
|
|
collection->Destroy();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (collection) {
|
|
EffectSet* effectSet =
|
|
EffectSet::GetEffectSet(aElement, aStyleContext->GetPseudoType());
|
|
if (effectSet) {
|
|
effectSet->UpdateAnimationGeneration(mPresContext);
|
|
}
|
|
} else {
|
|
bool createdCollection = false;
|
|
collection =
|
|
CSSAnimationCollection::GetOrCreateAnimationCollection(
|
|
aElement, aStyleContext->GetPseudoType(), &createdCollection);
|
|
if (!collection) {
|
|
MOZ_ASSERT(!createdCollection, "outparam should agree with return value");
|
|
NS_WARNING("allocating collection failed");
|
|
return;
|
|
}
|
|
|
|
if (createdCollection) {
|
|
AddElementCollection(collection);
|
|
}
|
|
}
|
|
collection->mAnimations.SwapElements(newAnimations);
|
|
|
|
// Cancel removed animations
|
|
for (size_t newAnimIdx = newAnimations.Length(); newAnimIdx-- != 0; ) {
|
|
newAnimations[newAnimIdx]->CancelFromStyle();
|
|
}
|
|
|
|
EffectCompositor::UpdateCascadeResults(aElement,
|
|
aStyleContext->GetPseudoType(),
|
|
aStyleContext);
|
|
|
|
mPresContext->EffectCompositor()->
|
|
MaybeUpdateAnimationRule(aElement,
|
|
aStyleContext->GetPseudoType(),
|
|
EffectCompositor::CascadeLevel::Animations);
|
|
|
|
// We don't actually dispatch the pending events now. We'll either
|
|
// dispatch them the next time we get a refresh driver notification
|
|
// or the next time somebody calls
|
|
// nsPresShell::FlushPendingNotifications.
|
|
if (mEventDispatcher.HasQueuedEvents()) {
|
|
mPresContext->Document()->SetNeedStyleFlush();
|
|
}
|
|
}
|
|
|
|
void
|
|
nsAnimationManager::StopAnimationsForElement(
|
|
mozilla::dom::Element* aElement,
|
|
mozilla::CSSPseudoElementType aPseudoType)
|
|
{
|
|
MOZ_ASSERT(aElement);
|
|
CSSAnimationCollection* collection =
|
|
CSSAnimationCollection::GetAnimationCollection(aElement, aPseudoType);
|
|
if (!collection) {
|
|
return;
|
|
}
|
|
|
|
nsAutoAnimationMutationBatch mb(aElement->OwnerDoc());
|
|
collection->Destroy();
|
|
}
|
|
|
|
class ResolvedStyleCache {
|
|
public:
|
|
ResolvedStyleCache() : mCache() {}
|
|
nsStyleContext* Get(nsPresContext *aPresContext,
|
|
nsStyleContext *aParentStyleContext,
|
|
Declaration* aKeyframeDeclaration);
|
|
|
|
private:
|
|
nsRefPtrHashtable<nsPtrHashKey<Declaration>, nsStyleContext> mCache;
|
|
};
|
|
|
|
nsStyleContext*
|
|
ResolvedStyleCache::Get(nsPresContext *aPresContext,
|
|
nsStyleContext *aParentStyleContext,
|
|
Declaration* aKeyframeDeclaration)
|
|
{
|
|
// FIXME (spec): The css3-animations spec isn't very clear about how
|
|
// properties are resolved when they have values that depend on other
|
|
// properties (e.g., values in 'em'). I presume that they're resolved
|
|
// relative to the other styles of the element. The question is
|
|
// whether they are resolved relative to other animations: I assume
|
|
// that they're not, since that would prevent us from caching a lot of
|
|
// data that we'd really like to cache (in particular, the
|
|
// StyleAnimationValue values in AnimationPropertySegment).
|
|
nsStyleContext *result = mCache.GetWeak(aKeyframeDeclaration);
|
|
if (!result) {
|
|
aKeyframeDeclaration->SetImmutable();
|
|
// The spec says that !important declarations should just be ignored
|
|
MOZ_ASSERT(!aKeyframeDeclaration->HasImportantData(),
|
|
"Keyframe rule has !important data");
|
|
|
|
nsCOMArray<nsIStyleRule> rules;
|
|
rules.AppendObject(aKeyframeDeclaration);
|
|
MOZ_ASSERT(aPresContext->StyleSet()->IsGecko(),
|
|
"ServoStyleSet should not use nsAnimationManager for "
|
|
"animations");
|
|
RefPtr<nsStyleContext> resultStrong = aPresContext->StyleSet()->AsGecko()->
|
|
ResolveStyleByAddingRules(aParentStyleContext, rules);
|
|
mCache.Put(aKeyframeDeclaration, resultStrong);
|
|
result = resultStrong;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
class MOZ_STACK_CLASS CSSAnimationBuilder final {
|
|
public:
|
|
CSSAnimationBuilder(nsStyleContext* aStyleContext,
|
|
dom::Element* aTarget,
|
|
nsAnimationManager::CSSAnimationCollection* aCollection)
|
|
: mStyleContext(aStyleContext)
|
|
, mTarget(aTarget)
|
|
, mCollection(aCollection)
|
|
{
|
|
MOZ_ASSERT(aStyleContext);
|
|
MOZ_ASSERT(aTarget);
|
|
mTimeline = mTarget->OwnerDoc()->Timeline();
|
|
}
|
|
|
|
// Returns a new animation set up with given StyleAnimation and
|
|
// keyframe rules.
|
|
// Or returns an existing animation matching StyleAnimation's name updated
|
|
// with the new StyleAnimation and keyframe rules.
|
|
already_AddRefed<CSSAnimation>
|
|
Build(nsPresContext* aPresContext,
|
|
const StyleAnimation& aSrc,
|
|
const nsCSSKeyframesRule* aRule);
|
|
|
|
private:
|
|
nsTArray<Keyframe> BuildAnimationFrames(nsPresContext* aPresContext,
|
|
const StyleAnimation& aSrc,
|
|
const nsCSSKeyframesRule* aRule);
|
|
Maybe<ComputedTimingFunction> GetKeyframeTimingFunction(
|
|
nsPresContext* aPresContext,
|
|
nsCSSKeyframeRule* aKeyframeRule,
|
|
const Maybe<ComputedTimingFunction>& aInheritedTimingFunction);
|
|
nsTArray<PropertyValuePair> GetKeyframePropertyValues(
|
|
nsPresContext* aPresContext,
|
|
nsCSSKeyframeRule* aKeyframeRule,
|
|
nsCSSCompressedDataBlock* aDataBlock,
|
|
nsCSSPropertySet& aAnimatedProperties);
|
|
void FillInMissingKeyframeValues(
|
|
nsPresContext* aPresContext,
|
|
nsCSSPropertySet aAnimatedProperties,
|
|
nsCSSPropertySet aPropertiesSetAtStart,
|
|
nsCSSPropertySet aPropertiesSetAtEnd,
|
|
const Maybe<ComputedTimingFunction>& aInheritedTimingFunction,
|
|
nsTArray<Keyframe>& aKeyframes);
|
|
void AppendProperty(nsPresContext* aPresContext,
|
|
nsCSSProperty aProperty,
|
|
nsTArray<PropertyValuePair>& aPropertyValues);
|
|
void GetComputedValue(nsPresContext* aPresContext,
|
|
nsCSSProperty aProperty,
|
|
nsCSSValue& aResult);
|
|
|
|
static TimingParams TimingParamsFrom(
|
|
const StyleAnimation& aStyleAnimation)
|
|
{
|
|
TimingParams timing;
|
|
|
|
timing.mDuration.emplace(StickyTimeDuration::FromMilliseconds(
|
|
aStyleAnimation.GetDuration()));
|
|
timing.mDelay = TimeDuration::FromMilliseconds(aStyleAnimation.GetDelay());
|
|
timing.mIterations = aStyleAnimation.GetIterationCount();
|
|
MOZ_ASSERT(timing.mIterations >= 0.0 && !IsNaN(timing.mIterations),
|
|
"mIterations should be nonnegative & finite, as ensured by "
|
|
"CSSParser");
|
|
timing.mDirection = aStyleAnimation.GetDirection();
|
|
timing.mFill = aStyleAnimation.GetFillMode();
|
|
|
|
return timing;
|
|
}
|
|
|
|
RefPtr<nsStyleContext> mStyleContext;
|
|
RefPtr<dom::Element> mTarget;
|
|
RefPtr<dom::DocumentTimeline> mTimeline;
|
|
|
|
ResolvedStyleCache mResolvedStyles;
|
|
RefPtr<nsStyleContext> mStyleWithoutAnimation;
|
|
// Existing collection, nullptr if the target element has no animations.
|
|
nsAnimationManager::CSSAnimationCollection* mCollection;
|
|
};
|
|
|
|
static Maybe<ComputedTimingFunction>
|
|
ConvertTimingFunction(const nsTimingFunction& aTimingFunction);
|
|
|
|
already_AddRefed<CSSAnimation>
|
|
CSSAnimationBuilder::Build(nsPresContext* aPresContext,
|
|
const StyleAnimation& aSrc,
|
|
const nsCSSKeyframesRule* aRule)
|
|
{
|
|
MOZ_ASSERT(aPresContext);
|
|
MOZ_ASSERT(aRule);
|
|
|
|
TimingParams timing = TimingParamsFrom(aSrc);
|
|
|
|
nsTArray<Keyframe> keyframes =
|
|
BuildAnimationFrames(aPresContext, aSrc, aRule);
|
|
|
|
bool isStylePaused =
|
|
aSrc.GetPlayState() == NS_STYLE_ANIMATION_PLAY_STATE_PAUSED;
|
|
|
|
// Find the matching animation with animation name in the old list
|
|
// of animations and remove the matched animation from the list.
|
|
RefPtr<CSSAnimation> oldAnim =
|
|
PopExistingAnimation(aSrc.GetName(), mCollection);
|
|
|
|
if (oldAnim) {
|
|
// Copy over the start times and (if still paused) pause starts
|
|
// for each animation (matching on name only) that was also in the
|
|
// old list of animations.
|
|
// This means that we honor dynamic changes, which isn't what the
|
|
// spec says to do, but WebKit seems to honor at least some of
|
|
// them. See
|
|
// http://lists.w3.org/Archives/Public/www-style/2011Apr/0079.html
|
|
// In order to honor what the spec said, we'd copy more data over.
|
|
UpdateOldAnimationPropertiesWithNew(*oldAnim,
|
|
timing,
|
|
keyframes,
|
|
isStylePaused,
|
|
mStyleContext);
|
|
return oldAnim.forget();
|
|
}
|
|
|
|
// mTarget is non-null here, so we emplace it directly.
|
|
Maybe<OwningAnimationTarget> target;
|
|
target.emplace(mTarget, mStyleContext->GetPseudoType());
|
|
RefPtr<KeyframeEffectReadOnly> effect =
|
|
new KeyframeEffectReadOnly(aPresContext->Document(), target, timing);
|
|
|
|
effect->SetFrames(Move(keyframes), mStyleContext);
|
|
|
|
RefPtr<CSSAnimation> animation =
|
|
new CSSAnimation(aPresContext->Document()->GetScopeObject(),
|
|
aSrc.GetName());
|
|
animation->SetOwningElement(
|
|
OwningElementRef(*mTarget, mStyleContext->GetPseudoType()));
|
|
|
|
animation->SetTimeline(mTimeline);
|
|
animation->SetEffect(effect);
|
|
|
|
if (isStylePaused) {
|
|
animation->PauseFromStyle();
|
|
} else {
|
|
animation->PlayFromStyle();
|
|
}
|
|
|
|
return animation.forget();
|
|
}
|
|
|
|
nsTArray<Keyframe>
|
|
CSSAnimationBuilder::BuildAnimationFrames(nsPresContext* aPresContext,
|
|
const StyleAnimation& aSrc,
|
|
const nsCSSKeyframesRule* aRule)
|
|
{
|
|
// Ideally we'd like to build up a set of Keyframe objects that more-or-less
|
|
// reflects the keyframes as-specified in the @keyframes rule(s). However,
|
|
// that proves to be difficult because the way CSS declarations are processed
|
|
// differs from how we are able to represent keyframes as JavaScript objects.
|
|
//
|
|
// For example, in CSS the following rules differ in meaning:
|
|
//
|
|
// { margin: 10px; margin-left: 20px }
|
|
// { margin-left: 20px; margin: 10px }
|
|
//
|
|
// However, in JavaScript, since the order in which object properties are
|
|
// enumerated is not defined, Web Animations defines that shorthands are
|
|
// applied first and longhands are layered on top regardless of the order
|
|
// in which they are specified. As a result, we would need to represent the
|
|
// above as:
|
|
//
|
|
// { margin: '10px', marginLeft: '20px' }
|
|
// { margin: '10px' }
|
|
//
|
|
// Similarly, redundant declarations are permitted by CSS but not in
|
|
// JavaScript. As such,
|
|
//
|
|
// { margin-left: 20px; margin-left: 30px }
|
|
//
|
|
// would be represented as:
|
|
//
|
|
// { marginLeft: '30px' }
|
|
//
|
|
// In effect, we would need to manually apply the rules for CSS declaration
|
|
// processing in order to maintain the closest possibly mapping
|
|
// to the source and even then, the mapping would be unclear in some
|
|
// cases. Furthermore, @keyframes are defined to cascade so any
|
|
// correspondance to the source would be further obscured once we represent
|
|
// the result as a single array.
|
|
//
|
|
// Until there is specified behavior for preserving shorthands we simply
|
|
// expand all shorthands, apply regular declaration processing, then go and
|
|
// pick up the last value specified for each property at each offset.
|
|
|
|
// FIXME: There is a pending spec change to make multiple @keyframes
|
|
// rules with the same name cascade but we don't support that yet.
|
|
|
|
Maybe<ComputedTimingFunction> inheritedTimingFunction =
|
|
ConvertTimingFunction(aSrc.GetTimingFunction());
|
|
|
|
// First, make up Keyframe objects for each rule
|
|
nsTArray<Keyframe> keyframes;
|
|
nsCSSPropertySet animatedProperties;
|
|
|
|
for (auto ruleIdx = 0, ruleEnd = aRule->StyleRuleCount();
|
|
ruleIdx != ruleEnd; ++ruleIdx) {
|
|
css::Rule* cssRule = aRule->GetStyleRuleAt(ruleIdx);
|
|
MOZ_ASSERT(cssRule, "must have rule");
|
|
MOZ_ASSERT(cssRule->GetType() == css::Rule::KEYFRAME_RULE,
|
|
"must be keyframe rule");
|
|
nsCSSKeyframeRule* keyframeRule = static_cast<nsCSSKeyframeRule*>(cssRule);
|
|
nsCSSCompressedDataBlock* dataBlock =
|
|
keyframeRule->Declaration()->GetNormalBlock();
|
|
|
|
const nsTArray<float>& keys = keyframeRule->GetKeys();
|
|
for (float key : keys) {
|
|
if (key < 0.0f || key > 1.0f) {
|
|
continue;
|
|
}
|
|
|
|
Keyframe keyframe;
|
|
keyframe.mOffset.emplace(key);
|
|
keyframe.mTimingFunction =
|
|
GetKeyframeTimingFunction(aPresContext, keyframeRule,
|
|
inheritedTimingFunction);
|
|
keyframe.mPropertyValues =
|
|
GetKeyframePropertyValues(aPresContext, keyframeRule, dataBlock,
|
|
animatedProperties);
|
|
|
|
keyframes.AppendElement(Move(keyframe));
|
|
}
|
|
}
|
|
|
|
// Next, stable sort by offset
|
|
std::stable_sort(keyframes.begin(), keyframes.end(),
|
|
[](const Keyframe& a, const Keyframe& b)
|
|
{
|
|
return a.mOffset < b.mOffset;
|
|
});
|
|
|
|
// Then walk backwards through the keyframes and drop overridden properties.
|
|
nsCSSPropertySet propertiesSetAtCurrentOffset;
|
|
nsCSSPropertySet propertiesSetAtStart;
|
|
nsCSSPropertySet propertiesSetAtEnd;
|
|
double currentOffset = -1.0;
|
|
for (size_t keyframeIdx = keyframes.Length();
|
|
keyframeIdx > 0;
|
|
--keyframeIdx) {
|
|
Keyframe& keyframe = keyframes[keyframeIdx - 1];
|
|
MOZ_ASSERT(keyframe.mOffset, "Should have filled in the offset");
|
|
|
|
if (keyframe.mOffset.value() != currentOffset) {
|
|
propertiesSetAtCurrentOffset.Empty();
|
|
currentOffset = keyframe.mOffset.value();
|
|
}
|
|
|
|
// Get the set of properties from this keyframe that have not
|
|
// already been set at this offset.
|
|
nsTArray<PropertyValuePair> uniquePropertyValues;
|
|
uniquePropertyValues.SetCapacity(keyframe.mPropertyValues.Length());
|
|
for (const PropertyValuePair& pair : keyframe.mPropertyValues) {
|
|
if (!propertiesSetAtCurrentOffset.HasProperty(pair.mProperty)) {
|
|
uniquePropertyValues.AppendElement(pair);
|
|
propertiesSetAtCurrentOffset.AddProperty(pair.mProperty);
|
|
|
|
if (currentOffset == 0.0) {
|
|
propertiesSetAtStart.AddProperty(pair.mProperty);
|
|
} else if (currentOffset == 1.0) {
|
|
propertiesSetAtEnd.AddProperty(pair.mProperty);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we have a keyframe at the same offset with the same timing
|
|
// function we should merge our (unique) values into it.
|
|
// Otherwise, we should update the existing keyframe with only the
|
|
// unique properties.
|
|
//
|
|
// Bug 1216843: We should also match composite modes here.
|
|
Keyframe* existingKeyframe = nullptr;
|
|
// Don't bother searching for an existing keyframe if we don't
|
|
// have anything to contribute to it.
|
|
if (!uniquePropertyValues.IsEmpty()) {
|
|
for (size_t i = keyframeIdx; i < keyframes.Length(); i++) {
|
|
Keyframe& kf = keyframes[i];
|
|
if (kf.mOffset.value() != currentOffset) {
|
|
break;
|
|
}
|
|
if (kf.mTimingFunction == keyframe.mTimingFunction) {
|
|
existingKeyframe = &kf;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (existingKeyframe) {
|
|
existingKeyframe->
|
|
mPropertyValues.AppendElements(Move(uniquePropertyValues));
|
|
keyframe.mPropertyValues.Clear();
|
|
} else {
|
|
keyframe.mPropertyValues.SwapElements(uniquePropertyValues);
|
|
}
|
|
|
|
// Check for a now-empty keyframe
|
|
if (keyframe.mPropertyValues.IsEmpty()) {
|
|
keyframes.RemoveElementAt(keyframeIdx - 1);
|
|
// existingKeyframe might dangle now
|
|
}
|
|
}
|
|
|
|
// Finally, we need to look for any animated properties that have an
|
|
// implicit 'to' or 'from' value and fill in the appropriate keyframe
|
|
// with the current computed style.
|
|
FillInMissingKeyframeValues(aPresContext, animatedProperties,
|
|
propertiesSetAtStart, propertiesSetAtEnd,
|
|
inheritedTimingFunction, keyframes);
|
|
|
|
return keyframes;
|
|
}
|
|
|
|
Maybe<ComputedTimingFunction>
|
|
CSSAnimationBuilder::GetKeyframeTimingFunction(
|
|
nsPresContext* aPresContext,
|
|
nsCSSKeyframeRule* aKeyframeRule,
|
|
const Maybe<ComputedTimingFunction>& aInheritedTimingFunction)
|
|
{
|
|
Maybe<ComputedTimingFunction> result;
|
|
|
|
if (aKeyframeRule->Declaration() &&
|
|
aKeyframeRule->Declaration()->HasProperty(
|
|
eCSSProperty_animation_timing_function)) {
|
|
RefPtr<nsStyleContext> keyframeRuleContext =
|
|
mResolvedStyles.Get(aPresContext, mStyleContext,
|
|
aKeyframeRule->Declaration());
|
|
const nsTimingFunction& tf = keyframeRuleContext->StyleDisplay()->
|
|
mAnimations[0].GetTimingFunction();
|
|
result = ConvertTimingFunction(tf);
|
|
} else {
|
|
result = aInheritedTimingFunction;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static Maybe<ComputedTimingFunction>
|
|
ConvertTimingFunction(const nsTimingFunction& aTimingFunction)
|
|
{
|
|
Maybe<ComputedTimingFunction> result;
|
|
|
|
if (aTimingFunction.mType != nsTimingFunction::Type::Linear) {
|
|
result.emplace();
|
|
result->Init(aTimingFunction);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
nsTArray<PropertyValuePair>
|
|
CSSAnimationBuilder::GetKeyframePropertyValues(
|
|
nsPresContext* aPresContext,
|
|
nsCSSKeyframeRule* aKeyframeRule,
|
|
nsCSSCompressedDataBlock* aDataBlock,
|
|
nsCSSPropertySet& aAnimatedProperties)
|
|
{
|
|
nsTArray<PropertyValuePair> result;
|
|
|
|
for (nsCSSProperty prop = nsCSSProperty(0);
|
|
prop < eCSSProperty_COUNT_no_shorthands;
|
|
prop = nsCSSProperty(prop + 1)) {
|
|
if (nsCSSProps::kAnimTypeTable[prop] == eStyleAnimType_None ||
|
|
!aKeyframeRule->Declaration()->HasNonImportantValueFor(prop)) {
|
|
continue;
|
|
}
|
|
|
|
PropertyValuePair pair;
|
|
pair.mProperty = prop;
|
|
pair.mValue = *aDataBlock->ValueFor(prop);
|
|
|
|
MOZ_ASSERT(pair.mValue.GetUnit() != eCSSUnit_Null,
|
|
"Not expecting to read invalid properties");
|
|
|
|
result.AppendElement(Move(pair));
|
|
aAnimatedProperties.AddProperty(prop);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Utility function to walk through |aIter| to find the Keyframe with
|
|
// matching offset and timing function but stopping as soon as the offset
|
|
// differs from |aOffset| (i.e. it assumes a sorted iterator).
|
|
//
|
|
// If a matching Keyframe is found,
|
|
// Returns true and sets |aIndex| to the index of the matching Keyframe
|
|
// within |aIter|.
|
|
//
|
|
// If no matching Keyframe is found,
|
|
// Returns false and sets |aIndex| to the index in the iterator of the
|
|
// first Keyframe with an offset differing to |aOffset| or, if the end
|
|
// of the iterator is reached, sets |aIndex| to the index after the last
|
|
// Keyframe.
|
|
template <class IterType>
|
|
static bool
|
|
FindMatchingKeyframe(
|
|
IterType&& aIter,
|
|
double aOffset,
|
|
const Maybe<ComputedTimingFunction>& aTimingFunctionToMatch,
|
|
size_t& aIndex)
|
|
{
|
|
aIndex = 0;
|
|
for (Keyframe& keyframe : aIter) {
|
|
if (keyframe.mOffset.value() != aOffset) {
|
|
break;
|
|
}
|
|
if (keyframe.mTimingFunction == aTimingFunctionToMatch) {
|
|
return true;
|
|
}
|
|
++aIndex;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
CSSAnimationBuilder::FillInMissingKeyframeValues(
|
|
nsPresContext* aPresContext,
|
|
nsCSSPropertySet aAnimatedProperties,
|
|
nsCSSPropertySet aPropertiesSetAtStart,
|
|
nsCSSPropertySet aPropertiesSetAtEnd,
|
|
const Maybe<ComputedTimingFunction>& aInheritedTimingFunction,
|
|
nsTArray<Keyframe>& aKeyframes)
|
|
{
|
|
static const size_t kNotSet = static_cast<size_t>(-1);
|
|
|
|
// Find/create the keyframe to add start values to
|
|
size_t startKeyframeIndex = kNotSet;
|
|
if (!aAnimatedProperties.Equals(aPropertiesSetAtStart) &&
|
|
!FindMatchingKeyframe(aKeyframes, 0.0, aInheritedTimingFunction,
|
|
startKeyframeIndex)) {
|
|
Keyframe newKeyframe;
|
|
newKeyframe.mOffset.emplace(0.0);
|
|
newKeyframe.mTimingFunction = aInheritedTimingFunction;
|
|
aKeyframes.InsertElementAt(startKeyframeIndex, Move(newKeyframe));
|
|
}
|
|
|
|
// Find/create the keyframe to add end values to
|
|
size_t endKeyframeIndex = kNotSet;
|
|
if (!aAnimatedProperties.Equals(aPropertiesSetAtEnd)) {
|
|
if (!FindMatchingKeyframe(Reversed(aKeyframes), 1.0,
|
|
aInheritedTimingFunction, endKeyframeIndex)) {
|
|
Keyframe newKeyframe;
|
|
newKeyframe.mOffset.emplace(1.0);
|
|
newKeyframe.mTimingFunction = aInheritedTimingFunction;
|
|
aKeyframes.AppendElement(Move(newKeyframe));
|
|
endKeyframeIndex = aKeyframes.Length() - 1;
|
|
} else {
|
|
// endKeyframeIndex is currently a count from the end of the array
|
|
// so we need to reverse it.
|
|
endKeyframeIndex = aKeyframes.Length() - 1 - endKeyframeIndex;
|
|
}
|
|
}
|
|
|
|
if (startKeyframeIndex == kNotSet && endKeyframeIndex == kNotSet) {
|
|
return;
|
|
}
|
|
|
|
// Now that we have finished manipulating aKeyframes, it is safe to
|
|
// take pointers to its elements.
|
|
Keyframe* startKeyframe = startKeyframeIndex == kNotSet
|
|
? nullptr : &aKeyframes[startKeyframeIndex];
|
|
Keyframe* endKeyframe = endKeyframeIndex == kNotSet
|
|
? nullptr : &aKeyframes[endKeyframeIndex];
|
|
|
|
// Iterate through all properties and fill-in missing values
|
|
for (nsCSSProperty prop = nsCSSProperty(0);
|
|
prop < eCSSProperty_COUNT_no_shorthands;
|
|
prop = nsCSSProperty(prop + 1)) {
|
|
if (!aAnimatedProperties.HasProperty(prop)) {
|
|
continue;
|
|
}
|
|
|
|
if (startKeyframe && !aPropertiesSetAtStart.HasProperty(prop)) {
|
|
AppendProperty(aPresContext, prop, startKeyframe->mPropertyValues);
|
|
}
|
|
if (endKeyframe && !aPropertiesSetAtEnd.HasProperty(prop)) {
|
|
AppendProperty(aPresContext, prop, endKeyframe->mPropertyValues);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
CSSAnimationBuilder::AppendProperty(
|
|
nsPresContext* aPresContext,
|
|
nsCSSProperty aProperty,
|
|
nsTArray<PropertyValuePair>& aPropertyValues)
|
|
{
|
|
PropertyValuePair propertyValue;
|
|
propertyValue.mProperty = aProperty;
|
|
GetComputedValue(aPresContext, aProperty, propertyValue.mValue);
|
|
|
|
aPropertyValues.AppendElement(Move(propertyValue));
|
|
}
|
|
|
|
void
|
|
CSSAnimationBuilder::GetComputedValue(nsPresContext* aPresContext,
|
|
nsCSSProperty aProperty,
|
|
nsCSSValue& aResult)
|
|
{
|
|
StyleAnimationValue computedValue;
|
|
|
|
if (!mStyleWithoutAnimation) {
|
|
MOZ_ASSERT(aPresContext->StyleSet()->IsGecko(),
|
|
"ServoStyleSet should not use nsAnimationManager for "
|
|
"animations");
|
|
mStyleWithoutAnimation = aPresContext->StyleSet()->AsGecko()->
|
|
ResolveStyleWithoutAnimation(mTarget, mStyleContext,
|
|
eRestyle_AllHintsWithAnimations);
|
|
}
|
|
|
|
if (CommonAnimationManager<CSSAnimation>::ExtractComputedValueForTransition(
|
|
aProperty, mStyleWithoutAnimation, computedValue) &&
|
|
StyleAnimationValue::UncomputeValue(
|
|
aProperty, Move(computedValue), aResult)) {
|
|
// If we hit this assertion or the MOZ_ASSERT_UNREACHABLE below, it
|
|
// probably means we are fetching a value from the computed style that
|
|
// we don't know how to represent as a StyleAnimationValue.
|
|
MOZ_ASSERT(aResult.GetUnit() != eCSSUnit_Null,
|
|
"Got null computed value");
|
|
return;
|
|
}
|
|
|
|
MOZ_ASSERT_UNREACHABLE("Failed to get computed value");
|
|
aResult.Reset();
|
|
}
|
|
|
|
void
|
|
nsAnimationManager::BuildAnimations(nsStyleContext* aStyleContext,
|
|
dom::Element* aTarget,
|
|
CSSAnimationCollection* aCollection,
|
|
OwningCSSAnimationPtrArray& aAnimations)
|
|
{
|
|
MOZ_ASSERT(aAnimations.IsEmpty(), "expect empty array");
|
|
|
|
const nsStyleDisplay *disp = aStyleContext->StyleDisplay();
|
|
|
|
CSSAnimationBuilder builder(aStyleContext, aTarget, aCollection);
|
|
|
|
for (size_t animIdx = disp->mAnimationNameCount; animIdx-- != 0;) {
|
|
const StyleAnimation& src = disp->mAnimations[animIdx];
|
|
|
|
// CSS Animations whose animation-name does not match a @keyframes rule do
|
|
// not generate animation events. This includes when the animation-name is
|
|
// "none" which is represented by an empty name in the StyleAnimation.
|
|
// Since such animations neither affect style nor dispatch events, we do
|
|
// not generate a corresponding CSSAnimation for them.
|
|
MOZ_ASSERT(mPresContext->StyleSet()->IsGecko(),
|
|
"ServoStyleSet should not use nsAnimationManager for "
|
|
"animations");
|
|
nsCSSKeyframesRule* rule =
|
|
src.GetName().IsEmpty()
|
|
? nullptr
|
|
: mPresContext->StyleSet()->AsGecko()->KeyframesRuleForName(src.GetName());
|
|
if (!rule) {
|
|
continue;
|
|
}
|
|
|
|
RefPtr<CSSAnimation> dest = builder.Build(mPresContext, src, rule);
|
|
dest->SetAnimationIndex(static_cast<uint64_t>(animIdx));
|
|
aAnimations.AppendElement(dest);
|
|
}
|
|
}
|
|
|