Files
palemoon27/layout/style/StyleAnimationValue.cpp
T
roytam1 c6e1bfdcf2 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1261578 - Part 1. Correct text color in selection range; r=jfkthame (6895ebe313)
- Bug 1261578 - Part 2. Correct text decoration color; r=jfkthame (348ba5a946)
- Bug 1261578 - Part 3. Correct MathML text color; r=jfkthame (99dfb99af4)
- Bug 1261578 - Part 4. Correct text overflow color; r=jfkthame (96318becf6)
- Bug 1261578 - Part 5. web-platform-test reftest; r=jfkthame (781bba0317)
- Bug 1110460, part 1 - Add operator= to nsCSSValuePair. r=dholbert (7d836b786b)
- Bug 1110460, part 2 - Move AddPositions() further up StyleAnimationValue.cpp and add asserts. r=dholbert (5f00295917)
- Bug 1110460, part 3 - Factor out a AddCSSValuePair helper in StyleAnimationValue.cpp. r=dholbert (9947835334)
- Bug 1110460, part 4 - Fix MOZ_ASSERT bug in nsComputedDOMStyle.cpp. r=dholbert (5403c0c377)
- Bug 1110460, part 5 - Factor out a nsStyleBasicShape::GetShapeTypeName method. r=dholbert (d27623d42f)
- Bug 1110460, part 6 - Make StyleAnimationValue support css::URLValue backed nsCSSValue objects. r=dholbert (dc006d1cae)
- Bug 1110460, part 7 - Factor out code for adding two nsCSSValuePairList lists. r=dholbert (f27f780005)
- Bug 1260655 - Allow StyleAnimationValue::UncomputeValue to produce values whose storage is independent of the passed-in computed value; r=heycam (abcc77ede5)
- Bug 1110460, part 8 - Support CSS animation of clip-path basic shapes. r=dholbert (cfe5bf883b)
- Bug 1238243 - Reftests involving possibly-ignorable hangul choseong filler. r=xidorn Bug 1238243 followup - Mark test 1238243-2.html as random on OS X 10.6, due to dependency on available fonts. Bug 1239564 - Add reftest for mask layer composition. r=roc (bb3bf56d52)
- Bug 1260543 - Treat currentcolor as computed value which is not interpolatable with actual color for text-emphasis-color and -webkit-text-fill-color. r=birtles (7b90f37e2f)
- Bug 1260543 followup - Fix the function name in test_transitions_per_property.html. DONTBUILD (1362d30c6a)
- Bug 1248708 - Part1: parse and compute -webkit-text-stroke property. r=heycam (c77332031d)
- Bug 1248708 - Part2.1: use mfbt/TypedEnumBits.h for DrawMode. r=jwatt (55bfbdf5dd)
- Bug 1266101, part 1 - Remove various bits of dead code related to painting SVG text. r=heycam (c5140a911c)
- Bug 1266101, part 2 - Remove most of the GLYPH_STROKE/GLYPH_STROKE_UNDERNEATH checking code. r=heycam (6d8f17bc19)
- Bug 1248708 - Part2.2: render -webkit-text-stroke property. r=jfkthame (bccc308ddc)
- Bug 1248708 - Part3: add reftests. r=jfkthame (0fc0864749)
- Bug 1248708 followup: Use MOZ_ARRAY_LENGTH instead of ArrayLength for expression used at compile time, to fix MSVC 2013 bustage. (trivial/bustage-fix, no review) (25cecd7585)
- Bug 1266341 - Pass the right kind of enum constants for script codes. r=masayuki (723e784a37)
- Bug 1245866 - A better buffer size check in nsIDNService::IDNA2008ToUnicode, r=jfkthame (683db7d93c)
- Bug 1233610 - IDN service should return NS_ERROR_MALFORMED_URI instead of NS_ERROR_FAILURE r=mcmanus (d70d4425ca)
- Bug 1266391 - Introduce an enum class mozilla::unicode::Script, and use this instead of bare integers to specify script codes for better type checking. r=masayuki (363a23049f)
- fix 1389436 backport (a8f96f6b33)
- Bug 1258228 - create special child accessibles in owner's constructor, part1, r=marcoz (2bbb819f76)
- Bug 1258228 - create special child accessibles in owner's constructor, part2, r=marcoz (5e20a7761a)
- Bug 1258228 - create special child accessibles in owner's constructor, part3, r=marcoz (6574efb1e7)
- Bug 1258228 - create special child accessibles in owner's constructor, part4, r=marcoz (15420c5a12)
- Bug 1188818 - DataTransfer must deal with nsIFile of directories, r=jwatt (715a857dd1)
- Bug 1126295 - Move TestAtoms.cpp to gtest and enable it; r=froydnj (9275ebeb3c)
- Bug 1257128 (part 1) - Remove nsIAtom.equalsUTF8. r=froydnj. (17238a04e6)
- Bug 1257128 (part 2) - Remove nsIAtomService.get{,Permanent}AtomUTF8. r=froydnj. (f4131f1024)
- Bug 1257128 (part 3) - Remove unused createAtom() function in a test. r=tbsaunde. (e082c1bc17)
- Bug 1257128 (part 4) - Remove unused nsIAtomService references in toolkit/. r=froydnj. (5ddae71ae8)
- Bug 1257402 - Remove NS_NewPermanentAtom() and nsIAtomService.getPermanentAtom(). r=froydnj,kmag. (4b9ad7894e)
- Bug 1145395 - Pass Element& to nsHTMLEditor::RemoveElementIfNoStyleOrIdOrClass; r=ehsan (83080cb024)
- Bug 1140105 - Can't query for a specific font face when the selection is collapsed. r=ehsan (b534c04e7f)
- Bug 1145395 - Introduce an overload of nsHTMLEditor::RemoveStyleInside that takes nsIContent&; r=ehsan (c5cb3bfb1d)
- Bug 1170326 - nsHTMLCSSUtils::ParseLength should check if the input is an empty string, r=ehsan (1950f67c9f)
- Bug 1260871 - Remove do_GetAtom() and rename NS_NewAtom() as NS_Atomize(). r=erahm. (984bc596bf)
- Bug 1254128 - Make the Mozilla .lldbinit use the full path for topsrcdir. r=sparky (20856a94b4)
- Bug 1259659 - rename from InternalUIEvent.detail to InternalUIEvent.mDetail. r=masayuki (37165bd487)
- Bug 1249664 - Make stateful frames responsible for their own keys r=dbaron (03a4365aa1)
- Bug 1232338 - part 1, Add better mouse support to InputData MouseInput r=kats (8be234631e)
- Bug 1259655 - part 1 Rename WidgetEventTime::time to WidgetEventTime::mTime r=masayuki (cd679b408e)
- Bug 1235908 - measure total event wait and process time. r=smaug (4364242809)
- Bug 1259655 - part 2 Rename WidgetEventTime::timeStamp to WidgetEventTime::mTimeStamp r=masayuki (94a507904d)
- Bug 1263785 - Kill off the deprecated nsINode::GetCurrentDoc. r=baku (670e175017)
- Bug 1245748 - Remove no-longer-needed code for directly setting up properties in KeyframeEffect(ReadOnly) constructor; r=heycam (166ed7697c)
- Bug 1259285 - Part1 - Move CSS/Web Animations-specific visibility handling. r=birtles (13a4ac2b94)
- Bug 1259285 - Part2 - Add web-platform test for visibility handling. r=birtles (b7cee58cf7)
- Bug 1260655 - Add KeyframeEffectReadOnly::SetFrames; r=heycam (7da27828da)
- Bug 1260655 - Wrap lines in keyframe-effect/constructor.html to 80 chars; r=whitespace-only (32b6412db2)
- Bug 1260655 - Update keyframe-effect/constructor.html to no longer refer to PropertyIndexedKeyframes or Keyframe; r=heycam (dbe0acf873)
- Bug 1260655 - Return the stored Keyframe objects from GetFrames, when available ; r=heycam (5b8cbc1773)
- Bug 1245748 - Add a Move constructor to Keyframe; r=heycam (eeabc9396a)
- Bug 1260655 - Add an assignment operator to Keyframe that takes an rvalue reference; r=heycam (e81f1f6d7c)
- Bug 1260655 - Add methods to CSSAnimationBuilder to build a set of Keyframe objects; r=heycam (93e2a7b3aa)
- Bug 1260655 - Use CSSAnimationBuilder::BuildAnimationFrames to set up CSS animations using Keyframe objects; r=heycam (4df3ad20cd)
- Bug 1260655 - Drop some no-longer-needed code for setting up CSS animations using AnimationProperty objects; r=heycam (e1d8e020aa)
- Bug 1260655 - Add a copy constructor and copy assignment operator to Keyframe; r=heycam (d9a1ca5d24)
- Bug 1249564 - Part 1: Assign the parent object of AnimationEffectTiming(ReadOnly). r=birtles,bz (298bdcf084)
- Bug 1249564 - Part 2: Cycle collect AnimationEffectTimingReadOnly. r=birtles (25d959d057)
- Bug 1263063 - Part 1: Remove unnecessary clamping of TimingParams::mIterationStart, since it's guaranteed to be nonnegative. r=dholbert (35362749d7)
- Bug 1263063 - Part 2: Add an assertion to ensure that iteration count is nonnegative and finite. r=dholbert (7df13df139)
- Bug 1263063 - Part 3: Change logic in KeyFrameEffect to assume that TimingParams' iteration-count has already been validated as nonnegative & finite. r=dholbert (6dd3f0d984)
- Bug 1263063 - Part 4: Move ActiveDuration() into TimingParams. r=dholbert (6760bdeaa7)
- Bug 1263063 - Part 5: Introduce TimingParams::EndTime(). r=dholbert (af9a4ddd95)
- Bug 1260983 - Allow creating animations with a target element not bound to a document; r=heycam (7d71619cfb)
- Bug 1244591 - Part 1: Implement KeyframeEffect.setFrames r=birtles,smaug (e090fa68b6)
- Bug 1244591 - Part 2: Extract useful keyframes tests to a new file r=birtles (780797fe53)
- Bug 1244591 - Part 3: Add web-platform tests for KeyframeEffect.setFrames r=birtles (997d26427b)
- Bug 1259878 - remove unnecessary isSome(). r=birtles (cb21268a0a)
- Bug 1248532 - Part 1: steps-start does not produce correct value at the beginning of the interval. r=birtles (9131923815)
- Bug 1248532 - Part 2: Add tests for step-start. r=birtles (04ae44375d)
- Bug 1248532 - Part 3: add a test for step-end with iterationStart. r=birtles (769827746b)
- Bug 1266257 - Revise timing model calculations to use fraction-based approach; r=hiro (c1de969ef0)
- Bug 1260572 - Replace AnimValuesStyleRule::AddEmptyValue with an overload of AddValue that takes an rvalue reference; r=heycam (08e01a4cdc)
- Bug 1260572 - Use 50% switch behavior if StyleAnimationValue::Interpolate fails; r=heycam (e67754e770)
- Bug 1260976 - Remove the old AnimationProperty-based GetFrames; r=heycam (efc88117e6)
- Bug 1259248. Add an ArrayView class. r=botond (5bb9e22500)
- Bug 1265648 - Remove the global nsTextFrameTextRunCache, as it no longer serves any useful purpose. r=mats (3ffbf42634)
- Bug 1238243 - Don't filter out Hangul jamo fillers as 'ignorable', because the font may require them to provide advance width. r=xidorn (9fc2f82853)
- Bug 1265648 followup - Use an array of UniquePtr<> for mTextRunsToDelete. r=mats (8745a7566e)
- Bug 1263956 - Adopt the new harfbuzz API for char-to-glyph mapping functions. r=jrmuizel (1f061d7b0f)
- Bug 1265568 - Don't require component alpha layers for text with -moz-osx-font-smoothing:grayscale. r=jfkthame,mattwoodrow (f819591273)
- Bug 1199547 - Backout workaround from bug 722676 as it causes issues on at least OSX 10.10 and 10.11. r=mstange (c96fb3a69d)
- Bug 1265953, part 1 - Convert most of the cocoa widget code from nsAutoPtr to UniquePtr. r=mstange (74aa93eaf9)
- Bug 1249915 - Add ability to synthesize native touch events on GTK for mochitests. r=karlt (b734a366fb)
- Bug 1259670 Rename WidgetPluginEvent::retargetToFocusedDocument to WidgetPluginEvent::mRetargetToFocusedDocument r=smaug (24c12b3bb4)
- Bug 1249915 - Fix synthesized touch injection code on Windows to not apply the scale factor twice. r=jimm (b590300aa3)
- Bug 1250505 - Convert SynthesizeNativeTouchPoint and SynthesizeNativeTouchTap to take a LayoutDeviceIntPoint instead of a ScreenPoint. r=njn (8ddd066519)
- Bug 1256731 - patch 1 - Don't apply theme-dpi scaling to metrics of window border elements, because Windows doesn't respect per-monitor dpi scaling when it draws them. r=emk (582676e33e)
- Bug 1247935 - Part 1: double-buffer nsShmImage. r=jrmuizel (2392a1e916)
- Bug 1247935 - Part 2: use shared pixmaps with XShm for nsShmImage. r=jrmuizel (b3dd105fcc)
- Bug 1265953, part 2 - Convert much of the rest of the widget code from nsAutoPtr to UniquePtr. r=mstange (e0c1c49ef8)
- Bug 1265953, part 3 - Convert more widget code from nsAutoPtr to UniquePtr. r=mstange (17e1be57d2)
- Bug 1265953, part 4 - Convert nsMenuX::LoadSubMenu and nsMenuX::AddMenu to UniquePtr. r=mstange (f4ea50b802)
- Bug 1259664 part.1 Rename WidgetWheelEvent::deltaX to WidgetWheelEvent::mDeltaX r=smaug (8c7ef861eb)
- Bug 1259664 part.2 Rename WidgetWheelEvent::deltaY to WidgetWheelEvent::mDeltaY r=smaug (03529452fb)
- Bug 1259664 part.3 Rename WidgetWheelEvent::deltaZ to WidgetWheelEvent::mDeltaZ r=smaug (0d664b2900)
- Bug 1259664 part.4 Rename WidgetWheelEvent::deltaMode to WidgetWheelEvent::mDeltaMode r=smaug (dc8283ee70)
- Bug 1253041 - Don't apply user wheel prefs more than once. r=smaug (2386a9da14)
- Bug 1259664 part.5 Rename WidgetWheelEvent::customizedByUserPrefs to WidgetWheelEvent::mCustomizedByUserPrefs r=smaug (3b16757c1e)
- Bug 1258820 - making root scrollable element not scroll if mouse wheel is used on xul dropdown r=masayuki (58ab750981)
- Bug 1250050 Add a pref to disable supporting mouse wheel of windowless plugins on Windows r=smaug (1560cfcb35)
- Bug 1256162, use last drag target for dragexit event when comparing to a remote browser, r=smaug (2d03394037)
- Bug 1110030 - part1 - add flags:IsSynthesizedByTIP and InputMethodAppState in WidgetKeyboardEvent. r=masayuki (c8f7802ee0)
- Bug 1110030 - part2 - Prevent PostHandleKeyboardEvent if it's handling by IME. r=masayuki (29fab0d4fe)
- Bug 1110030 - part3 - Interface between PresShell and HardwareKeyHandler. r=masayuki, r=smaug (cb152d6b4b)
- Bug 1110030 - part4 - HardwareKeyHandler component. r=masayuki, r=smaug (9e372e8f19)
- Bug 1110030 - part5 - Expose KeyboardEventInit dictionary. r=masayuki, r=smaug (3c38bf2f37)
- Bug 1110030 - part6 - Expose DefaultPreventedBy*. r=masayuki, r=smaug (5c94c067be)
- Bug 1110030 - part7 - Interface between HardwareKeyHandler and Input Method App. r=masayuki, r=smaug (f33b7f5ad5)
- Bug 1244546 part 1 - Apply proper unit conversion for SynthesizeNativeMouseEvent. r=karlt (8596ff8068)
- Bug 1244546 part 2 - Align the center point for pointerlock to meet widget's requirement. r=smaug (c447b8c770)
- Bug 1259664 part.6 Rename WidgetWheelEvent::mayHaveMomentum to WidgetWheelEvent::mMayHaveMomentum r=smaug (ef95784e26)
- Bug 1259664 part.7 Rename WidgetWheelEvent::isMomentum to WidgetWheelEvent::mIsMomentum r=smaug (983c8dc9e3)
- Bug 1259664 part.8 Rename WidgetWheelEvent::scrollType to WidgetWheelEvent::mScrollType r=smaug (c6731aee2d)
- Bug 1259664 part.9 Rename WidgetWheelEvent::lineOrPageDeltaX to WidgetWheelEvent::mLineOrPageDeltaX r=smaug (9c6d3d7f6b)
- Bug 1259664 part.10 Rename WidgetWheelEvent::lineOrPageDeltaY to WidgetWheelEvent::mLineOrPageDeltaY r=smaug (8a0d549eee)
- Bug 1259664 part.11 Rename WidgetWheelEvent::overflowDeltaX to WidgetWheelEvent::mOverflowDeltaX r=smaug (6200509b12)
- Bug 1259664 part.12 Rename WidgetWheelEvent::overflowDeltaY to Widget¦WheelEvent::mOverflowDeltaY r=smaug (3827fc7cef)
- Bug 1259664 part.13 Reorder the definition of members of WidgetWheelEvent r=smaug (ea69305e49)
- Bug 1243628 - Allow for presshell to have been destroyed and disconnected from prescontext during event dispatch. r=dholbert (b98e7ed1a2)
- Bug 1256952, send a dragexit at remote process when leaving the remote frame, r=smaug (1037c6a425)
- Bug 1261818 - don't pass nsAutoCStrings into nsBaseWidget debugging methods; r=karlt (36b2b66795)
- Bug 1250560 - Fix crash in HandleTouchEvent. r=roc (9586890edf)
- Bug 1121468 - Go to NoActionState after receiving release on LongTapState. r=roc (4e05f9ad70)
- Bug 1248847 - Assert AccessibleCaretEventHub mRefCnt > 1 in all its entry points. r=mats (89ced3c3e2)
- Bug 1251915 - Correct AccessibleCaretEventHub coding style by clang-format. r=roc (12d5b507d7)
- Bug 1259668 Rename WidgetTouchEvent::touches to WidgetTouchEvent::mTouches r=smaug (9c53824e17)
- Bug 1259657 Rename WidgetGUIEvent::widget to WidgetGUIEvent::mWidget r=smaug (8707f57a28)
- Bug 1246477 - Fix carets not updated by scroll events in LongTapState. r=roc (ba6f12001c)
- Bug 1259662 part.1 Rename WidgetDragEvent::dataTransfer to WidgetDragEvent::mDataTransfer r=smaug (79ffebe3ae)
- Bug 1259662 part.2 Rename WidgetDragEvent::userCancelled to WidgetDragEvent::mUserCancelled r=smaug (4b7ee565e7)
- Bug 1241008 - Add preference to show or hide selection bars. r=mtseng (aefd4c430d)
- Bug 1242349 - Provide unique AccessibleCaret pref for JS selection changes, r=TYLin (e964cb16cf)
- Bug 1240917 - Do not show caret in empty input on Fennec. r=roc (d73c6bd94d)
- Bug 1121468 - Show carets when long-pressing on selection highlight. r=roc (bf134067de)
- Bug 1246064 - Support long press to show AccessibleCaret on empty input for Fennec. r=roc (90791443cb)
- Bug 1246918 - Fix carets missing after scrolling down in selection mode on Fennec. r=roc (bc0915ad70)
- Bug 1246918 - Handle PresShell gone after FlushLayout(). r=roc (b2f18c9a03)
- Bug 1251915 - Ignore handling eTouchCancel events. r=roc (a04c3ad8eb)
- Bug 1121468 - Use auto* to explicit declare 'self' as a pointer. r=roc (d83a6020e3)
- Bug 1251346 - Fennec should not generate touch events from mouse events. r=kats (d8077748fc)
- Bug 1259656 part.1 Rename WidgetEvent::refPoint to WidgetEvent::mRefPoint r=smaug (fa66825fc9)
- Bug 1259656 part.2 Rename WidgetEvent::lastRefPoint to WidgetEvent::mLastRefPoint r=smaug (c964d62185)
- Bug 1259656 part.3 Rename WidgetEvent::userType to WidgetEvent::mSpecifiedEventType r=smaug (218ae50355)
- Bug 1259656 part.4 Rename WidgetEvent::typeString to WidgetEvent::mSpecifiedEventTypeString r=smaug (0192c890c9)
- Bug 1259656 part.5 Rename WidgetEvent::target to WidgetEvent::mTarget r=smaug (031356f40d)
- Bug 1259656 part.6 Rename WidgetEvent::currentTarget to WidgetEvent::mCurrentTarget r=smaug (451810f6d3)
- Bug 1259656 part.7 Rename WidgetEvent::originalTarget to WidgetEvent::mOriginalTarget r=smaug (643379c9cb)
- Bug 1259673 rename from InternalClipboardEvent.clipboardData to InternalClipboardEvent.mClipboardData. r=masayuki (5ad3c180fe)
- Bug 1259674 Part 1 rename InternalFocusEvent::fromRaise to InternalFocusEvent::mFrameRaise r=masayuki (400f1ba6e9)
- Bug 1259674 Part 2 rename InternalFocusEvent::isRefocus to InternalFocusEvent::mIsRefocus r=masayuki (93b5799c31)
- Bug 1259674 Part 3 rename InternalFocusEvent::relatedTarget to InternalFocusEvent::mRelatedTarget r=masayuki (2382b8de82)
- Bug 1256589 part.7 Add PropagationStopped() to WidgetEvent r=smaug (09325f188d)
- Bug 1259663 - Clean up WidgetMouseScrollEvent. r=masayuki (48e1389e22)
- Bug 1263782 - Kill off the deprecated nsINode::IsInDoc(). r=baku (ae80ec21f7)
- Bug 1223751 - Assume all frames are visible in subdocuments of a top level content document that's assuming all frames are visible. r=tn (3b4f99b2fc)
- Bug 1223747 - Don't assume all frames are visible in XUL documents. r=tn (7f26104ff8)
- Bug 1180267 - Switch Fennec over to using the MobileViewportManager for computing the CSS viewport. r=snorp (b85a3a5fb7)
- put back some SPS stuff (960414c383)
- Bug 1151152 - Change behavior when pointer is dragged out of the document. r=smaug (64779386f0)
- Bug 1263787 - Kill off the deprecated nsINode::GetCrossShadowCurrentDoc. r=baku (ed490173ff)
- Bug 1261933 - Continue unlocking pointer even if the widget has gone. r=smaug (93a781887a)
- Bug 1259296 - Do not scroll snap on the main thread for wheel events handled by APZ. r=kats (f72a7fef66)
- Bug 1208371 - Pass parent window to DOMMediaStream through constructor. r=roc (1c2cc7e7c7)
- Bug 1208371 - Make AudioCaptureStream startable. r=padenot (15849ae011)
- Bug 1208371 - Move OnTracksAvailableCallback out of DOMMediaStream. r=roc (a772c949e0)
- Bug 1208371 - Remove unused MediaManager::NotifyMediaStreamTrackEnded. r=jib (9157aa056e)
- Bug 1208371 - Introduce MediaStreamTrack logs. r=roc,jib (72ff4d4c59)
- Bug 1208371 - Track original track in MediaStreamTrack clones. r=jib (39e9ae1200)
- Bug 1208371 - Un-nest MediaEngineSource::PhotoCallback. r=roc (b1e0b48012)
- Bug 1253333: Don't cause a second MediaManager to be created if there's a race with NotifyFinished r=jib (d22d6d01e5)
- Bug 1251357 - fix regression where last MediaStreamTrack.stop did not turn off the camera light. r=jesup (11b4880c02)
- Bug 1208371 - Add a MediaStreamTrackSource interface. r=roc (bb1880524b)
- Bug 1208371 - Add MediaStreamTrackSourceGetter interface. r=roc (c57a1c6a74)
- Bug 1208371 - Let MediaStreamTracks know their TrackID at the source. r=roc (62739d72bf)
- Bug 1259236 - throw NotSupportedError on pc.addTrack of track in constructed stream. r=bwc (8b828e80e4)
- Bug 1208371 - Let FindOwnedDOMTrack operate on input stream. r=roc (26c24ad3c7)
- Bug 1208371 - Add some MediaStreamTrack helper methods. r=roc (1627cd36f8)
- Bug 1208371 - Count the users of a MediaStream to ease Destroy() resonsibility. r=roc (613d446b39)
- bug 1223670 assert that connected streams have the same graph r=padenot (17ea987392)
- Bug 1208371 - Add convenience method for checking if TrackID is explicit. r=roc (32c9d5fe45)
- Bug 1208371 - Allow MediaInputPorts mapped to a destination TrackID. r=roc (71d3c66c4d)
- Bug 1208371 - Remove obsolete SetTrackEnabled() from DOMMediaStream r=roc (cbcf54a342)
- Bug 1208371 - Add MediaStreamTrack::Graph(). r=jib (044b348267)
- Bug 1208371 - Add DOMMediaStream::GetTrackById/GetOwnedTrackById. r=jib (6f58360808)
- Bug 1208371 - Add a generic PrincipalChangeObserver interface. r=mt (79dfb91a89)
- Bug 1208371 - Turn DOMMediaStream::PrincipalChangeObserver into PrincipalChangeObserver<DOMMediaStream>. r=mt (41fdd835de)
- Bug 1208371 - Add principal APIs to MediaStreamTrack. r=mt (a8aab0e0d9)
- Bug 1208371 - Create MediaStreamTrackSource::Sink that forwards principal changes from the source. r=mt (d9fefd4e3c)
- Bug 1208371 - Let DOMMediaStream base its principal on the tracks it contains. r=mt (0c046c7c20)
- Bug 1208371 - Add an interface DecoderPrincipalChangeObserver to HTMLMediaElement. r=roc (ecde0ddae0)
- Bug 1208371 - Make HTMLMediaElement::CaptureStream pass its principal to MediaStreamTrack. r=mt (03ccd4ab12)
- Bug 1208371 - Make MediaManager pass its principal to MediaStreamTrack for gUM. r=mt (5a4b1eb0bc)
- Bug 1208371 - Make ImageCapture listen to principal changes of MediaStreamTrack instead. r=mt (d29fc5c36e)
- Bug 1208371 - Break PCImpl::SetRemoteDescription into smaller pieces. r=mt,bwc (e4337cc413)
- Bug 1208371 - Make PeerConnectionImpl pass its principal to MediaStreamTrack through a new RemoteTrackSource. r=mt (9a7c744a19)
- Bug 1208371 - Make remaining DOMMediaStream principal sources use MediaStreamTrack. r=mt (b30919c681)
- Bug 1208371 - Remove some no longer necessary principal methods on DOMMediaStream. r=mt (5279935d2b)
- Bug 1208371 - Add a MediaStreamTrackListener to MediaStreamGraph. r=roc (5d7048793e)
- Bug 1208371 - Move ImageCapture to a MediaStreamTrackListener. r=roc (98d081b034)
- Bug 1208371 - Make it possible to look up stream id by track in PeerConnectionImpl. r=jib (c65d07cb55)
- Bug 1208371 - Fix DOMMediaStream::OwnsTrack. r=roc (092dad3654)
- Bug 1208371 - Remove MediaStreamTrack::GetStream. r=jib (a78f873d89)
- Bug 1208371 - Route ApplyConstraints through MediaStreamTrackSource. r=jib (32dfd76245)
- Bug 1208371 - Kill nsDOMUserMediaStream with fire. r=jib (9550ad61be)
- Bug 1208371 - Make it possible to block tracks in a MediaInputPort initally. r=roc (39fdd2322f)
- Bug 1208371 - Implement MediaStreamTrack::Clone(). r=smaug,jib,roc (1995b87f10)
- Bug 1208371 - Implement DOMMediaStream::Clone() r=smaug,jib,roc (d445dffd30)
- Bug 1208371 - Various cleanups in DOMMediaStream/MediaStreamTrack. r=jib (a174781a1b)
- Bug 1208371 - Forward input stream and track id on regular track changes for union streams. r=roc (36ed98f3e4)
- Bug 1208371 - Move track.stop() helpers to MediaStreamPlayback. r=jib (123bcd8192)
- Bug 1240478 - Add test for video size on 'loadedmetadata'. r=jesup,jib Bug 1208371 - Test DOMMediaStream::Clone(). r=jib (5ff908fcad)
- Bug 1208371 - Test DOMMediaStream::Clone(). r=jib (445556e6b5)
- Bug 1208371 - Rename CreateOwnDOMTrack/CreateClonedDOMTrack to CreateDOMTrack/CloneDOMTrack. r=jib (41c71b073c)
- Bug 1208371 - Resolve ambiguous symbol MediaStreamTrack. r=bwc (f998354d96)
- Bug 1208371 - Test MediaStreamTrack::Clone(). r=jib (34865902b0)
- Bug 1208371 - Add CORSMode methods to MediaStreamTrack. r=mt (2244b06567)
- Bug 1208371 - Add convenience method for forwarding a track to an MSG-stream. r=roc (f21675220b)
- bug 1223670 throw not supported when creating a node from a stream with different channel r=baku (55e1d67356)
- Bug 1208371 - Lock MediaStreamAudioSourceNode onto the first AudioStreamTrack in mInputStream at time of construction. r=mt,padenot (77640fa752)
- Bug 1208371 - Move HTMLMediaElement::CaptureStream to forward CORSMode changes through MediaStreamTrackSource. r=mt (aa533e8945)
- Bug 1208371 - Remove CORSMode methods from DOMMediaStream. r=mt (41447ccea1)
- Bug 1208371 - Change HTMLMediaElement video sinks to check principal for video only tracks. r=mt (b8168de792)
- Bug 1208371 - Remove ref counting from DOMMediaStream::TrackListener. r=roc (fe275597ca)
- Bug 1208371 - Update tests to accomodate the fact that MediaStreamAudioSourceNodes lock onto the first audio track. r=padenot (ee97d9f274)
- bug 916387 remove workarounds in tests r=padenot (0285f2b98a)
- Bug 1208371 - Modify WebAudio source nodes tests to wait for tracks befoure measuring nr of samples. r=padenot (bb3afcdd12)
- Bug 1208371 - Make PeerIdentity RefCounted. r=mt (74320ca464)
- Bug 1208371 - Add GetPeerIdentity() to MediaStreamTrackSource. r=mt (77bffa988f)
- Bug 1208371 - Add PeerIdentity to LocalTrackSource for gUM tracks. r=mt (4b4163b66d)
- Bug 1208371 - Move PeerConnection to use PeerIdentity on MediaStreamTrack. r=mt (30b5ab4b5d)
- Bug 1208371 - Remove PeerIdentity from DOMMediaStream. r=mt (0d6b66614e)
- Bug 1208371 - Add convenience method for checking track forwarding to MediaStreamTrack. r=roc (d9eb5210e0)
- Bug 1208371 - Remove test_mediarecorder_record_nosrc.html. r=roc (5d7b83b51c)
- Bug 1208371 - Inline MediaRecorder::Session::SetupStreams. r=roc (1f8463266e)
- Bug 1153690 - Release the Session and MediaRecorder objects correctly if there is no ExtractRunnable running. r=roc (424117e697)
- Bug 1257318: Move MediaRecorder to use DirectListeners wherever possible. r=roc (1d6336641b)
- Bug 1208371 - Check principal per track instead of per stream in MediaRecorder. r=roc (946080d5e7)
- Bug 1219711 - Add mochitest for track disabling over a peer connection. r=jib (ad345e05e2)
- Bug 1166832 - Add test to verify audio (using AudioStreamAnalyser) after renegotiation. r=bwc (ad11535ee3)
- Bug 1250934: Make tests use the correct audio frequency when fake devices are used r=drno (1b8cb18b8f)
- Bug 1182426 - Test that we can record CanvasCaptureMediaStreams. r=roc (e65a5dce61)
- Bug 1219711 - Refactor captureStream_common.js to accept generic pixel testing method. r=jib (e55194c8f5)
- Bug 1223696 - Make canvas captureStream helper resilient to exceptions when there's no video. r=roc (215711b89d)
- Bug 1223696. Don't destroy VideoFrameContainer when we reach MetadataLoaded without a video track. r=jwwang (2c83a23b24)
- Bug 1166832 - Add test for canvas capture on multiple streams. r=bwc (592db27fc4)
- Bug 1224029: ensure video elements that may be disabled (black) are scaled r=mattwoodrow (bfef6af17d)
- Bug 1208371 - Simplify track disabling test code. r=jib (771d560f40)
- Bug 1208371 - Test disabling track across peerconnection not affecting clones. r=jib (31913dd9e6)
- Bug 1208371 - Add DirectTrackLister to MediaStreamGraph and MediaStreamTrack. r=roc,jesup (ae86375502)
- Bug 1208371 - Switch MediaPipeline to use direct listeners on tracks. r=jesup,bwc (aa32ce43e1)
- Bug 1208371 - Let PeerConnection consume principals from tracks instead of streams. r=mt (825d6775d0)
- Bug 1257318: Make recorder.pause()/resume() work with DirectListeners r=padenot (65ff6bff4e)
- Bug 1208371 - Move FindTrack from SourceMediaStream to MediaStream. r=roc (5725a863ae)
- Bug 1208371 - Switch MediaStreamTrack to enable/disable tracks on owned stream. r=jesup (ea7445369d)
- Bug 1208371 - Make GraphDriver callback log calls verbose. r=padenot (a502ceea55)
- Bug 1208371 - Do not require DOMLocalMediaStream for gUM (audioCapture). r=jib (c27ee1c308)
- Bug 1208371 - Add a track getter to gUM for fake tracks. r=jib (4958d2b392)
- Bug 1208371 - Remove unnecessary fakeTracks:true from test_streams_individual_pause.html. r=jib (2437b932b2)
- Bug 1208371 - Remove debug canvas from addTrackRemoveTrack test to help android perf. r=padenot (38b1ad91e8)
- Bug 1208371 - Remove debug canvas from peerConnection_trackDisabling test to help android perf. r=padenot (a8b5a62ad6)
- Bug 1208371 - Enable test_peerConnection_trackDisabling.html on android 4.3 debug. r=jib (19e14785f3)
- Bug 1208371 - Remove debug canvas from mediaStreamConstructors test to help android perf. r=padenot (b7e3280b68)
- Bug 1208371 - Add disconnect method to test helper AudioStreamAnalyser to improve performance on slow devices. r=padenot (2a18d693a7)
- Bug 1208371 - Guard LoadManagerSingleton with a WeakPtr. r=pkerr (0f6d425cf4)
- Bug 1208371 - Clear output canvas on each drawImage(). r=jib (45731c0c9a)
- Bug 1208371 - Wait for "loadedmetadata" before setting up nodes in test_mediaElementAudioSourceNodeCrossOrigin.html. r=padenot (8e9398fe88)
- Bug 1181051 - Remove BOM from dom/media/test/test*.html. r=kinetik (c98b2c9b96)
- Bug 1070110 - Ensure the testcase will receive at least 2 blob. r=jwwang (028cb09b08)
- Bug 1208371 - Improve reliability of test_mediarecorder_record_timeslice.html. r=jwwang (c89bd3fe0b)
- Bug 1251494 - Remove remaining references to MOZILLA_XPCOMRT_API from dom. r=jesup (5b8e7ef603)
- Bug 1234230: Don't pass null images for video encoding, and don't reencode the same image r=roc (eb09c07d8d)
- Bug 1258567 - per comment 3, ensure volume is only applied once to the AudioSegment. r=kinetik. (a7e184e483)
- Bug 1250934: Modify MediaEngine shutdown to allow neutering the AudioDataListener r=padenot (70f4831508)
- Bug 1208371 - Add PrincipalHandle to MediaChunks. r=mt,jesup (0662c26a97)
- Bug 1208371 - Add NotifyPrincipalHandleChanged to MediaStreamTrackListener. r=mt,jesup (54d896b1ff)
- Bug 1208371 - Add a PrincipalHandleListener to MediaStreamTrack. r=mt,jesup (9b4d2e0996)
- Bug 1256520 - use SyncRunnable to create DecodedStreamData synchronously to ensure the creation and destruction of DecodedStreamData happen in order. r=kikuo. (7d28eaebba)
- Bug 1237482 - Remove MediaDecoderStateMachine::mStreamSink. r=kikuo. (609efed70f)
- Bug 1208371 - Hook up DecodedStream with PrincipalHandle. r=mt,jwwang (9290efb216)
- Bug 1208371 - Hook up MediaPipeline with PrincipalHandle. r=mt,bwc (b00ed70ddc)
- Bug 1208371 - Ensure DOMMediaStream principals reflect what could reside in their playback streams. r=mt,jesup (bd3e32ef16)
- Bug 1208371 - Ensure a media element's ImageContainer is protected when playing a stream. r=mt,jesup (7914faa5dc)
- Bug 1208371 - Forward declare MediaStreamTrack in MediaPipeline.h. r=bwc (4c88b79137)
- Bug 1208371 - Clean up unnecessary virtuals in MediaPipeline. r=bwc (b7112f4b34)
- Bug 1208371 - Move MediaStreamTrack's label to MediaStreamTrackSource. r=jib (045bfcc738)
- Bug 1259590 - Remove B2G ACL code. r=khuey (e13223c0f6)
- Bug 1265452 followup - Make mGlyphExtentsArray an array of UniquePtr. r=jrmuizel (d12ce46244)
- Bug 1250540 - have media.navigator.permission.disabled no longer bypass media.getusermedia.screensharing.allowed_domains. r=jesup (e55aa02580)
- Bug 1247806 - Fix HSTS redirect check in WebSocketChannel, r=mcmanus (4e44becf9e)
- Bug 1251530: Use 'MakeUnique' instead of 'new' to populate UniquePtr variable in AddCSSValuePair. r=jwatt (286bf2289a)
- Bug 1264787: Make nsCSSValue's AdoptListValue & AdoptPairListValue methods take a UniquePtr. r=heycam (0f7dc7164f)
- Bug 1151243 part 3 - [css-grid] Add a generic nsHTMLReflowState::STATIC_POS_IS_CB_ORIGIN flag to place the static-position at the CB origin, and make nsAbsoluteContainingBlock use it in Grid containers where the placeholder is a child too. r=dholbert (7bde498cc4)
- Bug 1151243 part 4 - Some code cleanup in nsHTMLReflowState::CalculateHypotheticalPosition, and make a few methods 'const' (idempotent patch). r=dholbert (e430f9b99d)
- Bug 1250540 - fix string-parsing regression that made screen share whitelisting stop working. r=jesup (f8af7c3053)
- Bug 1265641: Move AEC tail length and delay-agnostic settings to getUserMedia r=padenot (3feaf716ee)
- Bug 950936 - Introduce ui.popup.disable_autohide pref to ease debugging popups. r=neil (bc28dcd531)
- bug 1211892 read -unico-border-gradient of early Unico versions for ThreeDHighlight and ThreeDShadow r=acomminos (39c83b1865)
- bug 1257695 disable GTK3 deprecation warnings in widget/gtk r=acomminos (1ad2ba0271)
- Bug 1258989 - Switch gtk3drawing to a C++ file, r=karlt (55b8ea3cdf)
- Bug 1259433 - remove gtk_widget_style_get() from gtk3drawing, r=karlt a=kwierso (a9fc049140)
- Bug 1266680 - Rename BlockTrackId to BlockSourceTrackId. r=jesup (5066bbf870)
- Bug 1262808: Refactor |PrincipalID| to |PrincipalHandle| in Gonk media code, r=rjesup (49ec99e852)
- Bug 1144096 part 1 - [css-grid] Refactor nsGridContainerFrame state and methods. r=dholbert (502fb7d424)
- Bug 1144096 part 2 - [css-grid] Make GridItemInfo::mFrame available also in non-DEBUG builds since we'll need it to support fragmentation. r=dholbert (fe0b396741)
- Bug 1144096 part 3 - [css-grid] Remove CellMap::ClearOccupied() since it's not needed anymore. r=dholbert (7aeacdccab)
- Bug 1144096 part 4 - [css-grid] Move more local nsGridContainerFrame classes into .cpp file. r=dholbert (896fc410ac)
- Bug 1144096 part 5 - [css-grid] Create a couple of Grid container frame bits. r=dholbert (abd007fd90)
- Bug 1144096 part 6 - [css-grid] Add support for creating Grid container continuations and deal with overflow containers. r=dholbert (3f02728f24)
- Bug 1144096 part 7 - [css-grid] Don't create PageBreakFrames inside a Grid container. The container will handle forced breaks on its items. r=dholbert (5a0c64369c)
- Bug 1144096 part 8 - [css-grid] Add a new state flag, eBreakBefore, to record where breaks occur between tracks. r=dholbert (5f5d8761c6)
- Bug 1144096 part 9 - [css-grid] Create a SharedGridData object owned by the first-in-flow Grid container to share state between its continuations. r=dholbert (7e510f0bf0)
- Bug 1144096 part 10 - [css-grid] Add a few helper methods to do a break before a row, and resize a row. r=dholbert (d3fec771a6)
- Bug 1144096 part 11 - [css-grid] Add a GetNearestFragmentainer() method that collects some data from the nearest enclosing fragmentainer needed for fragmentation. r=dholbert (20d4736a1c)
- Bug 1144096 part 12 - [css-grid] Collect and merge child frames we need for reflow. r=dholbert (3b5c619f34)
- Bug 1144096 part 13 - [css-grid] Refactor ReflowChildren() by separating out the code that reflows normal flow children (grid items and placeholders) into a new method ReflowInFlowChild(). r=dholbert (ba8a1edc7b)
- Bug 1144096 part 14 - [css-grid] Make ReflowInFlowChild() deal with a constrained available block-size. r=dholbert (292df4167a)
- Bug 1144096 part 15 - [css-grid] Compute our pre-reflow logical skip sides and cache the result of ComputedLogicalBorderPadding() with that applied. r=dholbert (0c7f59389e)
- Bug 1144096 part 16 - [css-grid] Implement fragmentation. r=dholbert (af8b8aac41)
- Bug 1144096 part 17 - [css-grid] Add helper methods that add a sorted list of child frames to the Overflow and ExcessOverflowContainers child lists. r=dholbert (a6c80c9e9c)
- Bug 1144096 part 18 - [css-grid] Fix a couple of bugs in how we handle child existing continuations when pushing/pulling children. r=dholbert (b8a62fab65)
- Bug 1144096 part 19 - [css-grid] Sanity check the initial child lists we get from the frame constructor (DEBUG only). r=dholbert (f56955c3ec)
- Bug 1144096 part 20 - [css-grid] Sanity check our child lists before starting a Reflow (DEBUG only). r=dholbert (d89c771238)
- Bug 1144096 part 21 - [css-grid] Deal with dynamically inserted/appended/removed child frames. r=dholbert (a07ecc06e2)
- Bug 1144096 part 22 - [css-grid] Check NS_INLINE_IS_BREAK_BEFORE before checking other completion status. r=dholbert (731dcd0ac5)
- Bug 1144096 part 23 - [css-grid] A grid container fragment that is an overflow container can't be INCOMPLETE, only OVERFLOW_INCOMPLETE and it should always have zero BSize. r=dholbert (afcd6c1741)
- Bug 1144096 part 24 - [css-grid] Move the child frame merging code at the start of ReflowOverflowContainerChildren into a new method: DrainExcessOverflowContainersList. Make both take a param so that we can override how the OC child lists are merged together (normally just an Append; MergeSortedFrameLists for Grid). r=dholbert (95323b0a56)
- Bug 1144096 part 25 - [css-grid] Enable fragmentation to occur by reporting our actual reflow status. r=dholbert (767c389238)
- Bug 1144096 part 26 - [css-grid] Fragmentation reftests. (07e62f8fd6)
- Bug 1244006 - Use const instead of MOZ_CONSTEXPR to avoid startup crash; r=dbaron (39b0bb32fb)
- Bug 1053986 - Fix comment referring to a GetBorder method that no longer exists. r=dholbert (2cde22a42c)
- Bug 1264784 - part 1 - call nsIFrame::{GetScreenRect,GetView} instead of their *External counterparts; r=dholbert (a2cd84b7a2)
- Bug 963238: Support isTypeSupported() in MediaRecorder, and throw on invalid mimetypes at construction r=cpearce,khuey (5a3709e45f)
- Bug 963238: Fix compilation errors. r=me (09b541337f)
- Bug 1157654 - 1. Do not call MediaRecorder.stop immediately after MediaRecorder.start, because we want to receive an onerror callback. 2. Make the NofityError async. r=roc (ad56918042)
- Bug 1128448 - 1. Close the pref media.encoder.omx.enabled for newer android version. 2. Modify testcase and MR to eusure no timing issue. r=roc (9dfff44a2c)
- Bug 1197669 - Part3 - Ensure the start event comes before any blobs. r=jwwang (d4e950fa49)
- Bug 1225327 - fix |mEncoder| null-dereference. r=jwwang (5ffce18e33)
- Bug 1260702: Don't crash on input sources from WebAudio in MediaRecorder r=padenot (28bcb80a03)
- Bug 1018299 - Throw security error if principal check fails in MediaRecorder::Start(). r=jib, r=mt (1130168c8d)
- Bug 1261007 - Part 1 - Force to send video sample into encoder if we got the same video sample more than 1 seconds. Enable testcases. r=jolin (5698b39b45)
- Bug 1182426 - Test that changing video resolution of a recorded stream throws an error. r=roc (edaf49f2cf)
- Bug 1261007 - Part 2 - fix test_mediarecorder_webm_support. r=rillian (16ef931949)
- Bug 1250054. Part 1 - implement MediaDecoderReaderWrapper. r=jya. (4b78b4335d)
- Bug 1242841 - Make MDSM::mDecodedAudioEndTime zero-based. r=kikuo. (c6c756d554)
- Bug 1242843 - Make MDSM::mDecodedVideoEndTime zero-based. r=kikuo. (c194178323)
- Bug 1242783. Part 1 - per comment 11, increase mAmpleAudioThresholdUsecs to 2s to avoid audio underrun when BT is connected. r=kinetik. (49567f587e)
- Bug 1242783. Part 2 - per comment 12, buffer only 1s when audio is captured. r=kinetik. (e0f0cd9826)
- Bug 1230641: P1. Use UYVY (YUV422) format in decoders. r=mattwoodrow (17d4de398c)
- Bug 1230641: P2. Increase the video queue size on mac. r=cpearce (e575c85f44)
- Bug 1230641 - Make our NSOpenGLContext opaque when in fullscreen mode. r=mstange (51da9e1e70)
- Bug 1257094 - Remove MediaDecoderStateMachine::DECODER_STATE_DECODING_NONE. r=cpearce. (f0993582c7)
- Bug 1252344. Part 1 - remove unnecessary checks from MediaDecoderStateMachine::NeedToDecodeVideo(). r=bechen. (1c9b7aadf4)
- Bug 1252344. Part 2 - remove unnecessary checks from MediaDecoderStateMachine::NeedToDecodeAudio(). r=bechen. (6896d7d1f6)
- Bug 1252360 - remove some check from NeedToDecodeAudio(). r=bechen. (1c064a9985)
- Bug 1252762 - Decode at most one audio/video sample before finishing seeking. r=cpearce. (73ec7691df)
- Bug 1253928 - adjust the time passed to RequestVideoData() by the start time to avoid incorrectly skipping key frames. r=jya. (1bc203d8e6)
- Bug 1252766 - Remove MediaDecoderStateMachine::mDecodeToSeekTarget which is never read. r=kaku. (aac1fe8018)
- Bug 1251460 - MDSM now waits on a promise to enqueue first frame loaded. r=jya (0e7e67e006)
- Bug 1257013 - Part 3: Stop checking for corrupt frames in MediaDecoderStateMachine since we no longer produce them. r=ajones (fbbbabafbb)
2024-04-24 23:53:16 +08:00

4533 lines
162 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
/* Utilities for animation of computed style values */
#include "mozilla/ArrayUtils.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/RuleNodeCacheConditions.h"
#include "mozilla/StyleAnimationValue.h"
#include "mozilla/UniquePtr.h"
#include "nsStyleTransformMatrix.h"
#include "nsCOMArray.h"
#include "nsIStyleRule.h"
#include "mozilla/css/StyleRule.h"
#include "nsString.h"
#include "nsStyleContext.h"
#include "nsStyleSet.h"
#include "nsComputedDOMStyle.h"
#include "nsCSSParser.h"
#include "nsCSSPseudoElements.h"
#include "mozilla/css/Declaration.h"
#include "mozilla/dom/Element.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Likely.h"
#include "gfxMatrix.h"
#include "gfxQuaternion.h"
#include "nsIDocument.h"
#include "nsIFrame.h"
#include "gfx2DGlue.h"
using namespace mozilla;
using namespace mozilla::gfx;
// HELPER METHODS
// --------------
/*
* Given two units, this method returns a common unit that they can both be
* converted into, if possible. This is intended to facilitate
* interpolation, distance-computation, and addition between "similar" units.
*
* The ordering of the arguments should not affect the output of this method.
*
* If there's no sensible common unit, this method returns eUnit_Null.
*
* @param aFirstUnit One unit to resolve.
* @param aFirstUnit The other unit to resolve.
* @return A "common" unit that both source units can be converted into, or
* eUnit_Null if that's not possible.
*/
static
StyleAnimationValue::Unit
GetCommonUnit(nsCSSProperty aProperty,
StyleAnimationValue::Unit aFirstUnit,
StyleAnimationValue::Unit aSecondUnit)
{
if (aFirstUnit != aSecondUnit) {
if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) &&
(aFirstUnit == StyleAnimationValue::eUnit_Coord ||
aFirstUnit == StyleAnimationValue::eUnit_Percent ||
aFirstUnit == StyleAnimationValue::eUnit_Calc) &&
(aSecondUnit == StyleAnimationValue::eUnit_Coord ||
aSecondUnit == StyleAnimationValue::eUnit_Percent ||
aSecondUnit == StyleAnimationValue::eUnit_Calc)) {
// We can use calc() as the common unit.
return StyleAnimationValue::eUnit_Calc;
}
return StyleAnimationValue::eUnit_Null;
}
return aFirstUnit;
}
static
nsCSSUnit
GetCommonUnit(nsCSSProperty aProperty,
nsCSSUnit aFirstUnit,
nsCSSUnit aSecondUnit)
{
if (aFirstUnit != aSecondUnit) {
if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_STORES_CALC) &&
(aFirstUnit == eCSSUnit_Pixel ||
aFirstUnit == eCSSUnit_Percent ||
aFirstUnit == eCSSUnit_Calc) &&
(aSecondUnit == eCSSUnit_Pixel ||
aSecondUnit == eCSSUnit_Percent ||
aSecondUnit == eCSSUnit_Calc)) {
// We can use calc() as the common unit.
return eCSSUnit_Calc;
}
return eCSSUnit_Null;
}
return aFirstUnit;
}
static nsCSSKeyword
ToPrimitive(nsCSSKeyword aKeyword)
{
switch (aKeyword) {
case eCSSKeyword_translatex:
case eCSSKeyword_translatey:
case eCSSKeyword_translatez:
case eCSSKeyword_translate:
return eCSSKeyword_translate3d;
case eCSSKeyword_scalex:
case eCSSKeyword_scaley:
case eCSSKeyword_scalez:
case eCSSKeyword_scale:
return eCSSKeyword_scale3d;
default:
return aKeyword;
}
}
static already_AddRefed<nsCSSValue::Array>
AppendFunction(nsCSSKeyword aTransformFunction)
{
uint32_t nargs;
switch (aTransformFunction) {
case eCSSKeyword_matrix3d:
nargs = 16;
break;
case eCSSKeyword_matrix:
nargs = 6;
break;
case eCSSKeyword_rotate3d:
nargs = 4;
break;
case eCSSKeyword_interpolatematrix:
case eCSSKeyword_translate3d:
case eCSSKeyword_scale3d:
nargs = 3;
break;
case eCSSKeyword_translate:
case eCSSKeyword_skew:
case eCSSKeyword_scale:
nargs = 2;
break;
default:
NS_ERROR("must be a transform function");
MOZ_FALLTHROUGH;
case eCSSKeyword_translatex:
case eCSSKeyword_translatey:
case eCSSKeyword_translatez:
case eCSSKeyword_scalex:
case eCSSKeyword_scaley:
case eCSSKeyword_scalez:
case eCSSKeyword_skewx:
case eCSSKeyword_skewy:
case eCSSKeyword_rotate:
case eCSSKeyword_rotatex:
case eCSSKeyword_rotatey:
case eCSSKeyword_rotatez:
case eCSSKeyword_perspective:
nargs = 1;
break;
}
RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(nargs + 1);
arr->Item(0).SetIntValue(aTransformFunction, eCSSUnit_Enumerated);
return arr.forget();
}
static already_AddRefed<nsCSSValue::Array>
ToPrimitive(nsCSSValue::Array* aArray)
{
nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(aArray);
nsCSSKeyword primitive = ToPrimitive(tfunc);
RefPtr<nsCSSValue::Array> arr = AppendFunction(primitive);
// FIXME: This would produce fewer calc() expressions if the
// zero were of compatible type (length vs. percent) when
// needed.
nsCSSValue zero(0.0f, eCSSUnit_Pixel);
nsCSSValue one(1.0f, eCSSUnit_Number);
switch(tfunc) {
case eCSSKeyword_translate:
{
MOZ_ASSERT(aArray->Count() == 2 || aArray->Count() == 3,
"unexpected count");
arr->Item(1) = aArray->Item(1);
arr->Item(2) = aArray->Count() == 3 ? aArray->Item(2) : zero;
arr->Item(3) = zero;
break;
}
case eCSSKeyword_translatex:
{
MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
arr->Item(1) = aArray->Item(1);
arr->Item(2) = zero;
arr->Item(3) = zero;
break;
}
case eCSSKeyword_translatey:
{
MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
arr->Item(1) = zero;
arr->Item(2) = aArray->Item(1);
arr->Item(3) = zero;
break;
}
case eCSSKeyword_translatez:
{
MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
arr->Item(1) = zero;
arr->Item(2) = zero;
arr->Item(3) = aArray->Item(1);
break;
}
case eCSSKeyword_scale:
{
MOZ_ASSERT(aArray->Count() == 2 || aArray->Count() == 3,
"unexpected count");
arr->Item(1) = aArray->Item(1);
arr->Item(2) = aArray->Count() == 3 ? aArray->Item(2) : aArray->Item(1);
arr->Item(3) = one;
break;
}
case eCSSKeyword_scalex:
{
MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
arr->Item(1) = aArray->Item(1);
arr->Item(2) = one;
arr->Item(3) = one;
break;
}
case eCSSKeyword_scaley:
{
MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
arr->Item(1) = one;
arr->Item(2) = aArray->Item(1);
arr->Item(3) = one;
break;
}
case eCSSKeyword_scalez:
{
MOZ_ASSERT(aArray->Count() == 2, "unexpected count");
arr->Item(1) = one;
arr->Item(2) = one;
arr->Item(3) = aArray->Item(1);
break;
}
default:
arr = aArray;
}
return arr.forget();
}
inline void
nscoordToCSSValue(nscoord aCoord, nsCSSValue& aCSSValue)
{
aCSSValue.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(aCoord),
eCSSUnit_Pixel);
}
static void
AppendCSSShadowValue(const nsCSSShadowItem *aShadow,
nsCSSValueList **&aResultTail)
{
MOZ_ASSERT(aShadow, "shadow expected");
// X, Y, Radius, Spread, Color, Inset
RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(6);
nscoordToCSSValue(aShadow->mXOffset, arr->Item(0));
nscoordToCSSValue(aShadow->mYOffset, arr->Item(1));
nscoordToCSSValue(aShadow->mRadius, arr->Item(2));
// NOTE: This code sometimes stores mSpread: 0 even when
// the parser would be required to leave it null.
nscoordToCSSValue(aShadow->mSpread, arr->Item(3));
if (aShadow->mHasColor) {
arr->Item(4).SetColorValue(aShadow->mColor);
}
if (aShadow->mInset) {
arr->Item(5).SetIntValue(NS_STYLE_BOX_SHADOW_INSET,
eCSSUnit_Enumerated);
}
nsCSSValueList *resultItem = new nsCSSValueList;
resultItem->mValue.SetArrayValue(arr, eCSSUnit_Array);
*aResultTail = resultItem;
aResultTail = &resultItem->mNext;
}
// Like nsStyleCoord::CalcValue, but with length in float pixels instead
// of nscoord.
struct PixelCalcValue
{
float mLength, mPercent;
bool mHasPercent;
};
// Requires a canonical calc() value that we generated.
static PixelCalcValue
ExtractCalcValueInternal(const nsCSSValue& aValue)
{
MOZ_ASSERT(aValue.GetUnit() == eCSSUnit_Calc, "unexpected unit");
nsCSSValue::Array *arr = aValue.GetArrayValue();
MOZ_ASSERT(arr->Count() == 1, "unexpected length");
const nsCSSValue &topval = arr->Item(0);
PixelCalcValue result;
if (topval.GetUnit() == eCSSUnit_Pixel) {
result.mLength = topval.GetFloatValue();
result.mPercent = 0.0f;
result.mHasPercent = false;
} else {
MOZ_ASSERT(topval.GetUnit() == eCSSUnit_Calc_Plus,
"unexpected unit");
nsCSSValue::Array *arr2 = topval.GetArrayValue();
const nsCSSValue &len = arr2->Item(0);
const nsCSSValue &pct = arr2->Item(1);
MOZ_ASSERT(len.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
MOZ_ASSERT(pct.GetUnit() == eCSSUnit_Percent, "unexpected unit");
result.mLength = len.GetFloatValue();
result.mPercent = pct.GetPercentValue();
result.mHasPercent = true;
}
return result;
}
// Requires a canonical calc() value that we generated.
static PixelCalcValue
ExtractCalcValue(const StyleAnimationValue& aValue)
{
PixelCalcValue result;
if (aValue.GetUnit() == StyleAnimationValue::eUnit_Coord) {
result.mLength =
nsPresContext::AppUnitsToFloatCSSPixels(aValue.GetCoordValue());
result.mPercent = 0.0f;
result.mHasPercent = false;
return result;
}
if (aValue.GetUnit() == StyleAnimationValue::eUnit_Percent) {
result.mLength = 0.0f;
result.mPercent = aValue.GetPercentValue();
result.mHasPercent = true;
return result;
}
MOZ_ASSERT(aValue.GetUnit() == StyleAnimationValue::eUnit_Calc,
"unexpected unit");
nsCSSValue *val = aValue.GetCSSValueValue();
return ExtractCalcValueInternal(*val);
}
static PixelCalcValue
ExtractCalcValue(const nsCSSValue& aValue)
{
PixelCalcValue result;
if (aValue.GetUnit() == eCSSUnit_Pixel) {
result.mLength = aValue.GetFloatValue();
result.mPercent = 0.0f;
result.mHasPercent = false;
return result;
}
if (aValue.GetUnit() == eCSSUnit_Percent) {
result.mLength = 0.0f;
result.mPercent = aValue.GetPercentValue();
result.mHasPercent = true;
return result;
}
return ExtractCalcValueInternal(aValue);
}
static void
SetCalcValue(const nsStyleCoord::CalcValue* aCalc, nsCSSValue& aValue)
{
RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
if (!aCalc->mHasPercent) {
nscoordToCSSValue(aCalc->mLength, arr->Item(0));
} else {
nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
nscoordToCSSValue(aCalc->mLength, arr2->Item(0));
arr2->Item(1).SetPercentValue(aCalc->mPercent);
}
aValue.SetArrayValue(arr, eCSSUnit_Calc);
}
static void
SetCalcValue(const PixelCalcValue& aCalc, nsCSSValue& aValue)
{
RefPtr<nsCSSValue::Array> arr = nsCSSValue::Array::Create(1);
if (!aCalc.mHasPercent) {
arr->Item(0).SetFloatValue(aCalc.mLength, eCSSUnit_Pixel);
} else {
nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
arr2->Item(0).SetFloatValue(aCalc.mLength, eCSSUnit_Pixel);
arr2->Item(1).SetPercentValue(aCalc.mPercent);
}
aValue.SetArrayValue(arr, eCSSUnit_Calc);
}
static already_AddRefed<nsStringBuffer>
GetURIAsUtf16StringBuffer(nsIURI* aUri)
{
nsAutoCString utf8String;
nsresult rv = aUri->GetSpec(utf8String);
NS_ENSURE_SUCCESS(rv, nullptr);
return nsCSSValue::BufferFromString(NS_ConvertUTF8toUTF16(utf8String));
}
double
CalcPositionSquareDistance(const nsCSSValue& aPos1,
const nsCSSValue& aPos2)
{
NS_ASSERTION(aPos1.GetUnit() == eCSSUnit_Array &&
aPos2.GetUnit() == eCSSUnit_Array,
"Expected two arrays");
PixelCalcValue calcVal[4];
nsCSSValue::Array* posArray = aPos1.GetArrayValue();
MOZ_ASSERT(posArray->Count() == 4, "Invalid position value");
NS_ASSERTION(posArray->Item(0).GetUnit() == eCSSUnit_Null &&
posArray->Item(2).GetUnit() == eCSSUnit_Null,
"Invalid list used");
for (int i = 0; i < 2; ++i) {
MOZ_ASSERT(posArray->Item(i*2+1).GetUnit() != eCSSUnit_Null,
"Invalid position value");
calcVal[i] = ExtractCalcValue(posArray->Item(i*2+1));
}
posArray = aPos2.GetArrayValue();
MOZ_ASSERT(posArray->Count() == 4, "Invalid position value");
NS_ASSERTION(posArray->Item(0).GetUnit() == eCSSUnit_Null &&
posArray->Item(2).GetUnit() == eCSSUnit_Null,
"Invalid list used");
for (int i = 0; i < 2; ++i) {
MOZ_ASSERT(posArray->Item(i*2+1).GetUnit() != eCSSUnit_Null,
"Invalid position value");
calcVal[i+2] = ExtractCalcValue(posArray->Item(i*2+1));
}
double squareDistance = 0.0;
for (int i = 0; i < 2; ++i) {
float difflen = calcVal[i+2].mLength - calcVal[i].mLength;
float diffpct = calcVal[i+2].mPercent - calcVal[i].mPercent;
squareDistance += difflen * difflen + diffpct * diffpct;
}
return squareDistance;
}
// CLASS METHODS
// -------------
bool
StyleAnimationValue::ComputeDistance(nsCSSProperty aProperty,
const StyleAnimationValue& aStartValue,
const StyleAnimationValue& aEndValue,
double& aDistance)
{
Unit commonUnit =
GetCommonUnit(aProperty, aStartValue.GetUnit(), aEndValue.GetUnit());
switch (commonUnit) {
case eUnit_Null:
case eUnit_Auto:
case eUnit_None:
case eUnit_Normal:
case eUnit_UnparsedString:
case eUnit_URL:
case eUnit_CurrentColor:
return false;
case eUnit_Enumerated:
switch (aProperty) {
case eCSSProperty_font_stretch: {
// just like eUnit_Integer.
int32_t startInt = aStartValue.GetIntValue();
int32_t endInt = aEndValue.GetIntValue();
aDistance = Abs(endInt - startInt);
return true;
}
default:
return false;
}
case eUnit_Visibility: {
int32_t startEnum = aStartValue.GetIntValue();
int32_t endEnum = aEndValue.GetIntValue();
if (startEnum == endEnum) {
aDistance = 0;
return true;
}
if ((startEnum == NS_STYLE_VISIBILITY_VISIBLE) ==
(endEnum == NS_STYLE_VISIBILITY_VISIBLE)) {
return false;
}
aDistance = 1;
return true;
}
case eUnit_Integer: {
int32_t startInt = aStartValue.GetIntValue();
int32_t endInt = aEndValue.GetIntValue();
aDistance = Abs(double(endInt) - double(startInt));
return true;
}
case eUnit_Coord: {
nscoord startCoord = aStartValue.GetCoordValue();
nscoord endCoord = aEndValue.GetCoordValue();
aDistance = Abs(double(endCoord) - double(startCoord));
return true;
}
case eUnit_Percent: {
float startPct = aStartValue.GetPercentValue();
float endPct = aEndValue.GetPercentValue();
aDistance = Abs(double(endPct) - double(startPct));
return true;
}
case eUnit_Float: {
float startFloat = aStartValue.GetFloatValue();
float endFloat = aEndValue.GetFloatValue();
aDistance = Abs(double(endFloat) - double(startFloat));
return true;
}
case eUnit_Color: {
// http://www.w3.org/TR/smil-animation/#animateColorElement says
// that we should use Euclidean RGB cube distance. However, we
// have to extend that to RGBA. For now, we'll just use the
// Euclidean distance in the (part of the) 4-cube of premultiplied
// colors.
// FIXME (spec): The CSS transitions spec doesn't say whether
// colors are premultiplied, but things work better when they are,
// so use premultiplication. Spec issue is still open per
// http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
nscolor startColor = aStartValue.GetColorValue();
nscolor endColor = aEndValue.GetColorValue();
// Get a color component on a 0-1 scale, which is much easier to
// deal with when working with alpha.
#define GET_COMPONENT(component_, color_) \
(NS_GET_##component_(color_) * (1.0 / 255.0))
double startA = GET_COMPONENT(A, startColor);
double startR = GET_COMPONENT(R, startColor) * startA;
double startG = GET_COMPONENT(G, startColor) * startA;
double startB = GET_COMPONENT(B, startColor) * startA;
double endA = GET_COMPONENT(A, endColor);
double endR = GET_COMPONENT(R, endColor) * endA;
double endG = GET_COMPONENT(G, endColor) * endA;
double endB = GET_COMPONENT(B, endColor) * endA;
#undef GET_COMPONENT
double diffA = startA - endA;
double diffR = startR - endR;
double diffG = startG - endG;
double diffB = startB - endB;
aDistance = sqrt(diffA * diffA + diffR * diffR +
diffG * diffG + diffB * diffB);
return true;
}
case eUnit_Calc: {
PixelCalcValue v1 = ExtractCalcValue(aStartValue);
PixelCalcValue v2 = ExtractCalcValue(aEndValue);
float difflen = v2.mLength - v1.mLength;
float diffpct = v2.mPercent - v1.mPercent;
aDistance = sqrt(difflen * difflen + diffpct * diffpct);
return true;
}
case eUnit_ObjectPosition: {
const nsCSSValue* position1 = aStartValue.GetCSSValueValue();
const nsCSSValue* position2 = aEndValue.GetCSSValueValue();
double squareDistance =
CalcPositionSquareDistance(*position1,
*position2);
aDistance = sqrt(squareDistance);
return true;
}
case eUnit_CSSValuePair: {
const nsCSSValuePair *pair1 = aStartValue.GetCSSValuePairValue();
const nsCSSValuePair *pair2 = aEndValue.GetCSSValuePairValue();
nsCSSUnit unit[2];
unit[0] = GetCommonUnit(aProperty, pair1->mXValue.GetUnit(),
pair2->mXValue.GetUnit());
unit[1] = GetCommonUnit(aProperty, pair1->mYValue.GetUnit(),
pair2->mYValue.GetUnit());
if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
unit[0] == eCSSUnit_URL || unit[0] == eCSSUnit_Enumerated) {
return false;
}
double squareDistance = 0.0;
static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
&nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
};
for (uint32_t i = 0; i < 2; ++i) {
nsCSSValue nsCSSValuePair::*member = pairValues[i];
double diffsquared;
switch (unit[i]) {
case eCSSUnit_Pixel: {
float diff = (pair1->*member).GetFloatValue() -
(pair2->*member).GetFloatValue();
diffsquared = diff * diff;
break;
}
case eCSSUnit_Percent: {
float diff = (pair1->*member).GetPercentValue() -
(pair2->*member).GetPercentValue();
diffsquared = diff * diff;
break;
}
case eCSSUnit_Calc: {
PixelCalcValue v1 = ExtractCalcValue(pair1->*member);
PixelCalcValue v2 = ExtractCalcValue(pair2->*member);
float difflen = v2.mLength - v1.mLength;
float diffpct = v2.mPercent - v1.mPercent;
diffsquared = difflen * difflen + diffpct * diffpct;
break;
}
default:
MOZ_ASSERT(false, "unexpected unit");
return false;
}
squareDistance += diffsquared;
}
aDistance = sqrt(squareDistance);
return true;
}
case eUnit_CSSValueTriplet: {
const nsCSSValueTriplet *triplet1 = aStartValue.GetCSSValueTripletValue();
const nsCSSValueTriplet *triplet2 = aEndValue.GetCSSValueTripletValue();
nsCSSUnit unit[3];
unit[0] = GetCommonUnit(aProperty, triplet1->mXValue.GetUnit(),
triplet2->mXValue.GetUnit());
unit[1] = GetCommonUnit(aProperty, triplet1->mYValue.GetUnit(),
triplet2->mYValue.GetUnit());
unit[2] = GetCommonUnit(aProperty, triplet1->mZValue.GetUnit(),
triplet2->mZValue.GetUnit());
if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
unit[2] == eCSSUnit_Null) {
return false;
}
double squareDistance = 0.0;
static nsCSSValue nsCSSValueTriplet::* const pairValues[3] = {
&nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
};
for (uint32_t i = 0; i < 3; ++i) {
nsCSSValue nsCSSValueTriplet::*member = pairValues[i];
double diffsquared;
switch (unit[i]) {
case eCSSUnit_Pixel: {
float diff = (triplet1->*member).GetFloatValue() -
(triplet2->*member).GetFloatValue();
diffsquared = diff * diff;
break;
}
case eCSSUnit_Percent: {
float diff = (triplet1->*member).GetPercentValue() -
(triplet2->*member).GetPercentValue();
diffsquared = diff * diff;
break;
}
case eCSSUnit_Calc: {
PixelCalcValue v1 = ExtractCalcValue(triplet1->*member);
PixelCalcValue v2 = ExtractCalcValue(triplet2->*member);
float difflen = v2.mLength - v1.mLength;
float diffpct = v2.mPercent - v1.mPercent;
diffsquared = difflen * difflen + diffpct * diffpct;
break;
}
case eCSSUnit_Null:
diffsquared = 0;
break;
default:
MOZ_ASSERT(false, "unexpected unit");
return false;
}
squareDistance += diffsquared;
}
aDistance = sqrt(squareDistance);
return true;
}
case eUnit_CSSRect: {
const nsCSSRect *rect1 = aStartValue.GetCSSRectValue();
const nsCSSRect *rect2 = aEndValue.GetCSSRectValue();
if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
// At least until we have calc()
return false;
}
double squareDistance = 0.0;
for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) {
nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
MOZ_ASSERT((rect1->*member).GetUnit() == (rect2->*member).GetUnit(),
"should have returned above");
double diff;
switch ((rect1->*member).GetUnit()) {
case eCSSUnit_Pixel:
diff = (rect1->*member).GetFloatValue() -
(rect2->*member).GetFloatValue();
break;
case eCSSUnit_Auto:
diff = 0;
break;
default:
MOZ_ASSERT(false, "unexpected unit");
return false;
}
squareDistance += diff * diff;
}
aDistance = sqrt(squareDistance);
return true;
}
case eUnit_Dasharray: {
// NOTE: This produces results on substantially different scales
// for length values and percentage values, which might even be
// mixed in the same property value. This means the result isn't
// particularly useful for paced animation.
// Call AddWeighted to make us lists of the same length.
StyleAnimationValue normValue1, normValue2;
if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
normValue1) ||
!AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
normValue2)) {
return false;
}
double squareDistance = 0.0;
const nsCSSValueList *list1 = normValue1.GetCSSValueListValue();
const nsCSSValueList *list2 = normValue2.GetCSSValueListValue();
MOZ_ASSERT(!list1 == !list2, "lists should be same length");
while (list1) {
const nsCSSValue &val1 = list1->mValue;
const nsCSSValue &val2 = list2->mValue;
MOZ_ASSERT(val1.GetUnit() == val2.GetUnit(),
"unit match should be assured by AddWeighted");
double diff;
switch (val1.GetUnit()) {
case eCSSUnit_Percent:
diff = val1.GetPercentValue() - val2.GetPercentValue();
break;
case eCSSUnit_Number:
diff = val1.GetFloatValue() - val2.GetFloatValue();
break;
default:
MOZ_ASSERT(false, "unexpected unit");
return false;
}
squareDistance += diff * diff;
list1 = list1->mNext;
list2 = list2->mNext;
MOZ_ASSERT(!list1 == !list2, "lists should be same length");
}
aDistance = sqrt(squareDistance);
return true;
}
case eUnit_Shadow: {
// Call AddWeighted to make us lists of the same length.
StyleAnimationValue normValue1, normValue2;
if (!AddWeighted(aProperty, 1.0, aStartValue, 0.0, aEndValue,
normValue1) ||
!AddWeighted(aProperty, 0.0, aStartValue, 1.0, aEndValue,
normValue2)) {
return false;
}
const nsCSSValueList *shadow1 = normValue1.GetCSSValueListValue();
const nsCSSValueList *shadow2 = normValue2.GetCSSValueListValue();
double squareDistance = 0.0;
MOZ_ASSERT(!shadow1 == !shadow2, "lists should be same length");
while (shadow1) {
nsCSSValue::Array *array1 = shadow1->mValue.GetArrayValue();
nsCSSValue::Array *array2 = shadow2->mValue.GetArrayValue();
for (size_t i = 0; i < 4; ++i) {
MOZ_ASSERT(array1->Item(i).GetUnit() == eCSSUnit_Pixel,
"unexpected unit");
MOZ_ASSERT(array2->Item(i).GetUnit() == eCSSUnit_Pixel,
"unexpected unit");
double diff = array1->Item(i).GetFloatValue() -
array2->Item(i).GetFloatValue();
squareDistance += diff * diff;
}
const nsCSSValue &color1 = array1->Item(4);
const nsCSSValue &color2 = array2->Item(4);
#ifdef DEBUG
{
const nsCSSValue &inset1 = array1->Item(5);
const nsCSSValue &inset2 = array2->Item(5);
// There are only two possible states of the inset value:
// (1) GetUnit() == eCSSUnit_Null
// (2) GetUnit() == eCSSUnit_Enumerated &&
// GetIntValue() == NS_STYLE_BOX_SHADOW_INSET
MOZ_ASSERT(((color1.IsNumericColorUnit() &&
color2.IsNumericColorUnit()) ||
(color1.GetUnit() == color2.GetUnit())) &&
inset1 == inset2,
"AddWeighted should have failed");
}
#endif
if (color1.GetUnit() != eCSSUnit_Null) {
StyleAnimationValue color1Value
(color1.GetColorValue(), StyleAnimationValue::ColorConstructor);
StyleAnimationValue color2Value
(color2.GetColorValue(), StyleAnimationValue::ColorConstructor);
double colorDistance;
#ifdef DEBUG
bool ok =
#endif
StyleAnimationValue::ComputeDistance(eCSSProperty_color,
color1Value, color2Value,
colorDistance);
MOZ_ASSERT(ok, "should not fail");
squareDistance += colorDistance * colorDistance;
}
shadow1 = shadow1->mNext;
shadow2 = shadow2->mNext;
MOZ_ASSERT(!shadow1 == !shadow2, "lists should be same length");
}
aDistance = sqrt(squareDistance);
return true;
}
// The CSS Shapes spec doesn't define paced animations for shape functions.
case eUnit_Shape: {
return false;
}
case eUnit_Filter:
// FIXME: Support paced animations for filter function interpolation.
case eUnit_Transform: {
return false;
}
case eUnit_BackgroundPosition: {
const nsCSSValueList *position1 = aStartValue.GetCSSValueListValue();
const nsCSSValueList *position2 = aEndValue.GetCSSValueListValue();
double squareDistance = 0.0;
MOZ_ASSERT(!position1 == !position2, "lists should be same length");
while (position1 && position2) {
squareDistance += CalcPositionSquareDistance(position1->mValue,
position2->mValue);
position1 = position1->mNext;
position2 = position2->mNext;
}
// fail if lists differ in length.
if (position1 || position2) {
return false;
}
aDistance = sqrt(squareDistance);
return true;
}
case eUnit_CSSValuePairList: {
const nsCSSValuePairList *list1 = aStartValue.GetCSSValuePairListValue();
const nsCSSValuePairList *list2 = aEndValue.GetCSSValuePairListValue();
double squareDistance = 0.0;
do {
static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
&nsCSSValuePairList::mXValue,
&nsCSSValuePairList::mYValue,
};
for (uint32_t i = 0; i < ArrayLength(pairListValues); ++i) {
const nsCSSValue &v1 = list1->*(pairListValues[i]);
const nsCSSValue &v2 = list2->*(pairListValues[i]);
nsCSSUnit unit =
GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
if (unit == eCSSUnit_Null) {
return false;
}
double diffsquared = 0.0;
switch (unit) {
case eCSSUnit_Pixel: {
float diff = v1.GetFloatValue() - v2.GetFloatValue();
diffsquared = diff * diff;
break;
}
case eCSSUnit_Percent: {
float diff = v1.GetPercentValue() - v2.GetPercentValue();
diffsquared = diff * diff;
break;
}
case eCSSUnit_Calc: {
PixelCalcValue val1 = ExtractCalcValue(v1);
PixelCalcValue val2 = ExtractCalcValue(v2);
float difflen = val2.mLength - val1.mLength;
float diffpct = val2.mPercent - val1.mPercent;
diffsquared = difflen * difflen + diffpct * diffpct;
break;
}
default:
if (v1 != v2) {
return false;
}
break;
}
squareDistance += diffsquared;
}
list1 = list1->mNext;
list2 = list2->mNext;
} while (list1 && list2);
if (list1 || list2) {
// We can't interpolate lists of different lengths.
return false;
}
aDistance = sqrt(squareDistance);
return true;
}
}
MOZ_ASSERT(false, "Can't compute distance using the given common unit");
return false;
}
#define MAX_PACKED_COLOR_COMPONENT 255
inline uint8_t ClampColor(double aColor)
{
if (aColor >= MAX_PACKED_COLOR_COMPONENT)
return MAX_PACKED_COLOR_COMPONENT;
if (aColor <= 0.0)
return 0;
return NSToIntRound(aColor);
}
// Ensure that a float/double value isn't NaN by returning zero instead
// (NaN doesn't have a sign) as a general restriction for floating point
// values in RestrictValue.
template<typename T>
MOZ_ALWAYS_INLINE T
EnsureNotNan(T aValue)
{
return aValue;
}
template<>
MOZ_ALWAYS_INLINE float
EnsureNotNan(float aValue)
{
// This would benefit from a MOZ_FLOAT_IS_NaN if we had one.
return MOZ_LIKELY(!mozilla::IsNaN(aValue)) ? aValue : 0;
}
template<>
MOZ_ALWAYS_INLINE double
EnsureNotNan(double aValue)
{
return MOZ_LIKELY(!mozilla::IsNaN(aValue)) ? aValue : 0;
}
template <typename T>
T
RestrictValue(uint32_t aRestrictions, T aValue)
{
T result = EnsureNotNan(aValue);
switch (aRestrictions) {
case 0:
break;
case CSS_PROPERTY_VALUE_NONNEGATIVE:
if (result < 0) {
result = 0;
}
break;
case CSS_PROPERTY_VALUE_AT_LEAST_ONE:
if (result < 1) {
result = 1;
}
break;
default:
MOZ_ASSERT(false, "bad value restriction");
break;
}
return result;
}
template <typename T>
T
RestrictValue(nsCSSProperty aProperty, T aValue)
{
return RestrictValue(nsCSSProps::ValueRestrictions(aProperty), aValue);
}
static inline void
AddCSSValuePixel(double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
{
MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Pixel, "unexpected unit");
aResult.SetFloatValue(RestrictValue(aValueRestrictions,
aCoeff1 * aValue1.GetFloatValue() +
aCoeff2 * aValue2.GetFloatValue()),
eCSSUnit_Pixel);
}
static inline void
AddCSSValueNumber(double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
{
MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
aResult.SetFloatValue(RestrictValue(aValueRestrictions,
aCoeff1 * aValue1.GetFloatValue() +
aCoeff2 * aValue2.GetFloatValue()),
eCSSUnit_Number);
}
static inline void
AddCSSValuePercent(double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValue &aResult, uint32_t aValueRestrictions = 0)
{
MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Percent, "unexpected unit");
MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Percent, "unexpected unit");
aResult.SetPercentValue(RestrictValue(aValueRestrictions,
aCoeff1 * aValue1.GetPercentValue() +
aCoeff2 * aValue2.GetPercentValue()));
}
// Add two canonical-form calc values (eUnit_Calc) to make another
// canonical-form calc value.
static void
AddCSSValueCanonicalCalc(double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValue &aResult)
{
PixelCalcValue v1 = ExtractCalcValue(aValue1);
PixelCalcValue v2 = ExtractCalcValue(aValue2);
PixelCalcValue result;
result.mLength = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
result.mPercent = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
result.mHasPercent = v1.mHasPercent || v2.mHasPercent;
MOZ_ASSERT(result.mHasPercent || result.mPercent == 0.0f,
"can't have a nonzero percentage part without having percentages");
SetCalcValue(result, aResult);
}
static void
AddCSSValueAngle(double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValue &aResult)
{
if (aValue1.GetUnit() == aValue2.GetUnit()) {
// To avoid floating point error, if the units match, maintain the unit.
aResult.SetFloatValue(aCoeff1 * aValue1.GetFloatValue() +
aCoeff2 * aValue2.GetFloatValue(),
aValue1.GetUnit());
} else {
aResult.SetFloatValue(aCoeff1 * aValue1.GetAngleValueInRadians() +
aCoeff2 * aValue2.GetAngleValueInRadians(),
eCSSUnit_Radian);
}
}
static bool
AddCSSValuePixelPercentCalc(const uint32_t aValueRestrictions,
const nsCSSUnit aCommonUnit,
double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValue &aResult)
{
switch (aCommonUnit) {
case eCSSUnit_Pixel:
AddCSSValuePixel(aCoeff1, aValue1,
aCoeff2, aValue2,
aResult, aValueRestrictions);
break;
case eCSSUnit_Percent:
AddCSSValuePercent(aCoeff1, aValue1,
aCoeff2, aValue2,
aResult, aValueRestrictions);
break;
case eCSSUnit_Calc:
AddCSSValueCanonicalCalc(aCoeff1, aValue1,
aCoeff2, aValue2,
aResult);
break;
default:
return false;
}
return true;
}
static inline float
GetNumberOrPercent(const nsCSSValue &aValue)
{
nsCSSUnit unit = aValue.GetUnit();
MOZ_ASSERT(unit == eCSSUnit_Number || unit == eCSSUnit_Percent,
"unexpected unit");
return (unit == eCSSUnit_Number) ?
aValue.GetFloatValue() : aValue.GetPercentValue();
}
static inline void
AddCSSValuePercentNumber(const uint32_t aValueRestrictions,
double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValue &aResult, float aInitialVal)
{
float n1 = GetNumberOrPercent(aValue1);
float n2 = GetNumberOrPercent(aValue2);
// Rather than interpolating aValue1 and aValue2 directly, we
// interpolate their *distances from aInitialVal* (the initial value,
// which is either 1 or 0 for "filter" functions). This matters in
// cases where aInitialVal is nonzero and the coefficients don't add
// up to 1. For example, if initialVal is 1, aCoeff1 is 0.5, and
// aCoeff2 is 0, then we'll return the value halfway between 1 and
// aValue1, rather than the value halfway between 0 and aValue1.
// Note that we do something similar in AddTransformScale().
float result = (n1 - aInitialVal) * aCoeff1 + (n2 - aInitialVal) * aCoeff2;
aResult.SetFloatValue(RestrictValue(aValueRestrictions, result + aInitialVal),
eCSSUnit_Number);
}
static bool
AddShadowItems(double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValueList **&aResultTail)
{
// X, Y, Radius, Spread, Color, Inset
MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Array,
"wrong unit");
MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Array,
"wrong unit");
nsCSSValue::Array *array1 = aValue1.GetArrayValue();
nsCSSValue::Array *array2 = aValue2.GetArrayValue();
RefPtr<nsCSSValue::Array> resultArray = nsCSSValue::Array::Create(6);
for (size_t i = 0; i < 4; ++i) {
AddCSSValuePixel(aCoeff1, array1->Item(i), aCoeff2, array2->Item(i),
resultArray->Item(i),
// blur radius must be nonnegative
(i == 2) ? CSS_PROPERTY_VALUE_NONNEGATIVE : 0);
}
const nsCSSValue& color1 = array1->Item(4);
const nsCSSValue& color2 = array2->Item(4);
const nsCSSValue& inset1 = array1->Item(5);
const nsCSSValue& inset2 = array2->Item(5);
if (color1.GetUnit() != color2.GetUnit() ||
inset1.GetUnit() != inset2.GetUnit()) {
// We don't know how to animate between color and no-color, or
// between inset and not-inset.
return false;
}
if (color1.GetUnit() != eCSSUnit_Null) {
StyleAnimationValue color1Value
(color1.GetColorValue(), StyleAnimationValue::ColorConstructor);
StyleAnimationValue color2Value
(color2.GetColorValue(), StyleAnimationValue::ColorConstructor);
StyleAnimationValue resultColorValue;
#ifdef DEBUG
bool ok =
#endif
StyleAnimationValue::AddWeighted(eCSSProperty_color,
aCoeff1, color1Value,
aCoeff2, color2Value,
resultColorValue);
MOZ_ASSERT(ok, "should not fail");
resultArray->Item(4).SetColorValue(resultColorValue.GetColorValue());
}
MOZ_ASSERT(inset1 == inset2, "should match");
resultArray->Item(5) = inset1;
nsCSSValueList *resultItem = new nsCSSValueList;
resultItem->mValue.SetArrayValue(resultArray, eCSSUnit_Array);
*aResultTail = resultItem;
aResultTail = &resultItem->mNext;
return true;
}
static void
AddTransformTranslate(double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValue &aResult)
{
MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Percent ||
aValue1.GetUnit() == eCSSUnit_Pixel ||
aValue1.IsCalcUnit(),
"unexpected unit");
MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Percent ||
aValue2.GetUnit() == eCSSUnit_Pixel ||
aValue2.IsCalcUnit(),
"unexpected unit");
if (aValue1.GetUnit() != aValue2.GetUnit() || aValue1.IsCalcUnit()) {
// different units; create a calc() expression
AddCSSValueCanonicalCalc(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
} else if (aValue1.GetUnit() == eCSSUnit_Percent) {
// both percent
AddCSSValuePercent(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
} else {
// both pixels
AddCSSValuePixel(aCoeff1, aValue1, aCoeff2, aValue2, aResult);
}
}
static void
AddTransformScale(double aCoeff1, const nsCSSValue &aValue1,
double aCoeff2, const nsCSSValue &aValue2,
nsCSSValue &aResult)
{
// Handle scale, and the two matrix components where identity is 1, by
// subtracting 1, multiplying by the coefficients, and then adding 1
// back. This gets the right AddWeighted behavior and gets us the
// interpolation-against-identity behavior for free.
MOZ_ASSERT(aValue1.GetUnit() == eCSSUnit_Number, "unexpected unit");
MOZ_ASSERT(aValue2.GetUnit() == eCSSUnit_Number, "unexpected unit");
float v1 = aValue1.GetFloatValue() - 1.0f,
v2 = aValue2.GetFloatValue() - 1.0f;
float result = v1 * aCoeff1 + v2 * aCoeff2;
aResult.SetFloatValue(result + 1.0f, eCSSUnit_Number);
}
/* static */ already_AddRefed<nsCSSValue::Array>
StyleAnimationValue::AppendTransformFunction(nsCSSKeyword aTransformFunction,
nsCSSValueList**& aListTail)
{
RefPtr<nsCSSValue::Array> arr = AppendFunction(aTransformFunction);
nsCSSValueList *item = new nsCSSValueList;
item->mValue.SetArrayValue(arr, eCSSUnit_Function);
*aListTail = item;
aListTail = &item->mNext;
return arr.forget();
}
/*
* The relevant section of the transitions specification:
* http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
* defers all of the details to the 2-D and 3-D transforms specifications.
* For the 2-D transforms specification (all that's relevant for us, right
* now), the relevant section is:
* http://dev.w3.org/csswg/css3-2d-transforms/#animation
* This, in turn, refers to the unmatrix program in Graphics Gems,
* available from http://tog.acm.org/resources/GraphicsGems/ , and in
* particular as the file GraphicsGems/gemsii/unmatrix.c
* in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
*
* The unmatrix reference is for general 3-D transform matrices (any of the
* 16 components can have any value).
*
* For CSS 2-D transforms, we have a 2-D matrix with the bottom row constant:
*
* [ A C E ]
* [ B D F ]
* [ 0 0 1 ]
*
* For that case, I believe the algorithm in unmatrix reduces to:
*
* (1) If A * D - B * C == 0, the matrix is singular. Fail.
*
* (2) Set translation components (Tx and Ty) to the translation parts of
* the matrix (E and F) and then ignore them for the rest of the time.
* (For us, E and F each actually consist of three constants: a
* length, a multiplier for the width, and a multiplier for the
* height. This actually requires its own decomposition, but I'll
* keep that separate.)
*
* (3) Let the X scale (Sx) be sqrt(A^2 + B^2). Then divide both A and B
* by it.
*
* (4) Let the XY shear (K) be A * C + B * D. From C, subtract A times
* the XY shear. From D, subtract B times the XY shear.
*
* (5) Let the Y scale (Sy) be sqrt(C^2 + D^2). Divide C, D, and the XY
* shear (K) by it.
*
* (6) At this point, A * D - B * C is either 1 or -1. If it is -1,
* negate the XY shear (K), the X scale (Sx), and A, B, C, and D.
* (Alternatively, we could negate the XY shear (K) and the Y scale
* (Sy).)
*
* (7) Let the rotation be R = atan2(B, A).
*
* Then the resulting decomposed transformation is:
*
* translate(Tx, Ty) rotate(R) skewX(atan(K)) scale(Sx, Sy)
*
* An interesting result of this is that all of the simple transform
* functions (i.e., all functions other than matrix()), in isolation,
* decompose back to themselves except for:
* 'skewY(φ)', which is 'matrix(1, tan(φ), 0, 1, 0, 0)', which decomposes
* to 'rotate(φ) skewX(φ) scale(sec(φ), cos(φ))' since (ignoring the
* alternate sign possibilities that would get fixed in step 6):
* In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
* Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
* In step 4, the XY shear is sin(φ).
* Thus, after step 4, C = -cos(φ)sin(φ) and D = 1 - sin²(φ) = cos²(φ).
* Thus, in step 5, the Y scale is sqrt(cos²(φ)(sin²(φ) + cos²(φ)) = cos(φ).
* Thus, after step 5, C = -sin(φ), D = cos(φ), and the XY shear is tan(φ).
* Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
* In step 7, the rotation is thus φ.
*
* skew(θ, φ), which is matrix(1, tan(φ), tan(θ), 1, 0, 0), which decomposes
* to 'rotate(φ) skewX(θ + φ) scale(sec(φ), cos(φ))' since (ignoring
* the alternate sign possibilities that would get fixed in step 6):
* In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
* Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
* In step 4, the XY shear is cos(φ)tan(θ) + sin(φ).
* Thus, after step 4,
* C = tan(θ) - cos(φ)(cos(φ)tan(θ) + sin(φ)) = tan(θ)sin²(φ) - cos(φ)sin(φ)
* D = 1 - sin(φ)(cos(φ)tan(θ) + sin(φ)) = cos²(φ) - sin(φ)cos(φ)tan(θ)
* Thus, in step 5, the Y scale is sqrt(C² + D²) =
* sqrt(tan²(θ)(sin⁴(φ) + sin²(φ)cos²(φ)) -
* 2 tan(θ)(sin³(φ)cos(φ) + sin(φ)cos³(φ)) +
* (sin²(φ)cos²(φ) + cos⁴(φ))) =
* sqrt(tan²(θ)sin²(φ) - 2 tan(θ)sin(φ)cos(φ) + cos²(φ)) =
* cos(φ) - tan(θ)sin(φ) (taking the negative of the obvious solution so
* we avoid flipping in step 6).
* After step 5, C = -sin(φ) and D = cos(φ), and the XY shear is
* (cos(φ)tan(θ) + sin(φ)) / (cos(φ) - tan(θ)sin(φ)) =
* (dividing both numerator and denominator by cos(φ))
* (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)) = tan(θ + φ).
* (See http://en.wikipedia.org/wiki/List_of_trigonometric_identities .)
* Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
* In step 7, the rotation is thus φ.
*
* To check this result, we can multiply things back together:
*
* [ cos(φ) -sin(φ) ] [ 1 tan(θ + φ) ] [ sec(φ) 0 ]
* [ sin(φ) cos(φ) ] [ 0 1 ] [ 0 cos(φ) ]
*
* [ cos(φ) cos(φ)tan(θ + φ) - sin(φ) ] [ sec(φ) 0 ]
* [ sin(φ) sin(φ)tan(θ + φ) + cos(φ) ] [ 0 cos(φ) ]
*
* but since tan(θ + φ) = (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)),
* cos(φ)tan(θ + φ) - sin(φ)
* = cos(φ)(tan(θ) + tan(φ)) - sin(φ) + sin(φ)tan(θ)tan(φ)
* = cos(φ)tan(θ) + sin(φ) - sin(φ) + sin(φ)tan(θ)tan(φ)
* = cos(φ)tan(θ) + sin(φ)tan(θ)tan(φ)
* = tan(θ) (cos(φ) + sin(φ)tan(φ))
* = tan(θ) sec(φ) (cos²(φ) + sin²(φ))
* = tan(θ) sec(φ)
* and
* sin(φ)tan(θ + φ) + cos(φ)
* = sin(φ)(tan(θ) + tan(φ)) + cos(φ) - cos(φ)tan(θ)tan(φ)
* = tan(θ) (sin(φ) - sin(φ)) + sin(φ)tan(φ) + cos(φ)
* = sec(φ) (sin²(φ) + cos²(φ))
* = sec(φ)
* so the above is:
* [ cos(φ) tan(θ) sec(φ) ] [ sec(φ) 0 ]
* [ sin(φ) sec(φ) ] [ 0 cos(φ) ]
*
* [ 1 tan(θ) ]
* [ tan(φ) 1 ]
*/
/*
* Decompose2DMatrix implements the above decomposition algorithm.
*/
#define XYSHEAR 0
#define XZSHEAR 1
#define YZSHEAR 2
static bool
Decompose2DMatrix(const Matrix &aMatrix, Point3D &aScale,
float aShear[3], gfxQuaternion &aRotate,
Point3D &aTranslate)
{
float A = aMatrix._11,
B = aMatrix._12,
C = aMatrix._21,
D = aMatrix._22;
if (A * D == B * C) {
// singular matrix
return false;
}
float scaleX = sqrt(A * A + B * B);
A /= scaleX;
B /= scaleX;
float XYshear = A * C + B * D;
C -= A * XYshear;
D -= B * XYshear;
float scaleY = sqrt(C * C + D * D);
C /= scaleY;
D /= scaleY;
XYshear /= scaleY;
// A*D - B*C should now be 1 or -1
NS_ASSERTION(0.99 < Abs(A*D - B*C) && Abs(A*D - B*C) < 1.01,
"determinant should now be 1 or -1");
if (A * D < B * C) {
A = -A;
B = -B;
C = -C;
D = -D;
XYshear = -XYshear;
scaleX = -scaleX;
}
float rotate = atan2f(B, A);
aRotate = gfxQuaternion(0, 0, sin(rotate/2), cos(rotate/2));
aShear[XYSHEAR] = XYshear;
aScale.x = scaleX;
aScale.y = scaleY;
aTranslate.x = aMatrix._31;
aTranslate.y = aMatrix._32;
return true;
}
/**
* Implementation of the unmatrix algorithm, specified by:
*
* http://dev.w3.org/csswg/css3-2d-transforms/#unmatrix
*
* This, in turn, refers to the unmatrix program in Graphics Gems,
* available from http://tog.acm.org/resources/GraphicsGems/ , and in
* particular as the file GraphicsGems/gemsii/unmatrix.c
* in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
*/
static bool
Decompose3DMatrix(const Matrix4x4 &aMatrix, Point3D &aScale,
float aShear[3], gfxQuaternion &aRotate,
Point3D &aTranslate, Point4D &aPerspective)
{
Matrix4x4 local = aMatrix;
if (local[3][3] == 0) {
return false;
}
/* Normalize the matrix */
local.Normalize();
/**
* perspective is used to solve for perspective, but it also provides
* an easy way to test for singularity of the upper 3x3 component.
*/
Matrix4x4 perspective = local;
Point4D empty(0, 0, 0, 1);
perspective.SetTransposedVector(3, empty);
if (perspective.Determinant() == 0.0) {
return false;
}
/* First, isolate perspective. */
if (local[0][3] != 0 || local[1][3] != 0 ||
local[2][3] != 0) {
/* aPerspective is the right hand side of the equation. */
aPerspective = local.TransposedVector(3);
/**
* Solve the equation by inverting perspective and multiplying
* aPerspective by the inverse.
*/
perspective.Invert();
aPerspective = perspective.TransposeTransform4D(aPerspective);
/* Clear the perspective partition */
local.SetTransposedVector(3, empty);
} else {
aPerspective = Point4D(0, 0, 0, 1);
}
/* Next take care of translation */
for (int i = 0; i < 3; i++) {
aTranslate[i] = local[3][i];
local[3][i] = 0;
}
/* Now get scale and shear. */
/* Compute X scale factor and normalize first row. */
aScale.x = local[0].Length();
local[0] /= aScale.x;
/* Compute XY shear factor and make 2nd local orthogonal to 1st. */
aShear[XYSHEAR] = local[0].DotProduct(local[1]);
local[1] -= local[0] * aShear[XYSHEAR];
/* Now, compute Y scale and normalize 2nd local. */
aScale.y = local[1].Length();
local[1] /= aScale.y;
aShear[XYSHEAR] /= aScale.y;
/* Compute XZ and YZ shears, make 3rd local orthogonal */
aShear[XZSHEAR] = local[0].DotProduct(local[2]);
local[2] -= local[0] * aShear[XZSHEAR];
aShear[YZSHEAR] = local[1].DotProduct(local[2]);
local[2] -= local[1] * aShear[YZSHEAR];
/* Next, get Z scale and normalize 3rd local. */
aScale.z = local[2].Length();
local[2] /= aScale.z;
aShear[XZSHEAR] /= aScale.z;
aShear[YZSHEAR] /= aScale.z;
/**
* At this point, the matrix (in locals) is orthonormal.
* Check for a coordinate system flip. If the determinant
* is -1, then negate the matrix and the scaling factors.
*/
if (local[0].DotProduct(local[1].CrossProduct(local[2])) < 0) {
aScale *= -1;
for (int i = 0; i < 3; i++) {
local[i] *= -1;
}
}
/* Now, get the rotations out */
aRotate = gfxQuaternion(local);
return true;
}
template<typename T>
T InterpolateNumerically(const T& aOne, const T& aTwo, double aCoeff)
{
return aOne + (aTwo - aOne) * aCoeff;
}
/* static */ Matrix4x4
StyleAnimationValue::InterpolateTransformMatrix(const Matrix4x4 &aMatrix1,
const Matrix4x4 &aMatrix2,
double aProgress)
{
// Decompose both matrices
// TODO: What do we do if one of these returns false (singular matrix)
Point3D scale1(1, 1, 1), translate1;
Point4D perspective1(0, 0, 0, 1);
gfxQuaternion rotate1;
float shear1[3] = { 0.0f, 0.0f, 0.0f};
Point3D scale2(1, 1, 1), translate2;
Point4D perspective2(0, 0, 0, 1);
gfxQuaternion rotate2;
float shear2[3] = { 0.0f, 0.0f, 0.0f};
Matrix matrix2d1, matrix2d2;
if (aMatrix1.Is2D(&matrix2d1) && aMatrix2.Is2D(&matrix2d2)) {
Decompose2DMatrix(matrix2d1, scale1, shear1, rotate1, translate1);
Decompose2DMatrix(matrix2d2, scale2, shear2, rotate2, translate2);
} else {
Decompose3DMatrix(aMatrix1, scale1, shear1,
rotate1, translate1, perspective1);
Decompose3DMatrix(aMatrix2, scale2, shear2,
rotate2, translate2, perspective2);
}
// Interpolate each of the pieces
Matrix4x4 result;
Point4D perspective =
InterpolateNumerically(perspective1, perspective2, aProgress);
result.SetTransposedVector(3, perspective);
Point3D translate =
InterpolateNumerically(translate1, translate2, aProgress);
result.PreTranslate(translate.x, translate.y, translate.z);
gfxQuaternion q3 = rotate1.Slerp(rotate2, aProgress);
Matrix4x4 rotate = q3.ToMatrix();
if (!rotate.IsIdentity()) {
result = rotate * result;
}
// TODO: Would it be better to interpolate these as angles? How do we convert back to angles?
float yzshear =
InterpolateNumerically(shear1[YZSHEAR], shear2[YZSHEAR], aProgress);
if (yzshear != 0.0) {
result.SkewYZ(yzshear);
}
float xzshear =
InterpolateNumerically(shear1[XZSHEAR], shear2[XZSHEAR], aProgress);
if (xzshear != 0.0) {
result.SkewXZ(xzshear);
}
float xyshear =
InterpolateNumerically(shear1[XYSHEAR], shear2[XYSHEAR], aProgress);
if (xyshear != 0.0) {
result.SkewXY(xyshear);
}
Point3D scale =
InterpolateNumerically(scale1, scale2, aProgress);
if (scale != Point3D(1.0, 1.0, 1.0)) {
result.PreScale(scale.x, scale.y, scale.z);
}
return result;
}
static nsCSSValueList*
AddDifferentTransformLists(double aCoeff1, const nsCSSValueList* aList1,
double aCoeff2, const nsCSSValueList* aList2)
{
nsAutoPtr<nsCSSValueList> result;
nsCSSValueList **resultTail = getter_Transfers(result);
RefPtr<nsCSSValue::Array> arr;
arr =
StyleAnimationValue::AppendTransformFunction(eCSSKeyword_interpolatematrix,
resultTail);
// FIXME: We should change the other transform code to also only
// take a single progress value, as having values that don't
// sum to 1 doesn't make sense for these.
if (aList1 == aList2) {
arr->Item(1).Reset();
} else {
aList1->CloneInto(arr->Item(1).SetListValue());
}
aList2->CloneInto(arr->Item(2).SetListValue());
arr->Item(3).SetPercentValue(aCoeff2);
return result.forget();
}
static bool
TransformFunctionsMatch(nsCSSKeyword func1, nsCSSKeyword func2)
{
return ToPrimitive(func1) == ToPrimitive(func2);
}
static bool
AddFilterFunctionImpl(double aCoeff1, const nsCSSValueList* aList1,
double aCoeff2, const nsCSSValueList* aList2,
nsCSSValueList**& aResultTail)
{
// AddFilterFunction should be our only caller, and it should ensure that both
// args are non-null.
MOZ_ASSERT(aList1, "expected filter list");
MOZ_ASSERT(aList2, "expected filter list");
MOZ_ASSERT(aList1->mValue.GetUnit() == eCSSUnit_Function,
"expected function");
MOZ_ASSERT(aList2->mValue.GetUnit() == eCSSUnit_Function,
"expected function");
RefPtr<nsCSSValue::Array> a1 = aList1->mValue.GetArrayValue(),
a2 = aList2->mValue.GetArrayValue();
nsCSSKeyword filterFunction = a1->Item(0).GetKeywordValue();
if (filterFunction != a2->Item(0).GetKeywordValue())
return false; // Can't add two filters of different types.
nsAutoPtr<nsCSSValueList> resultListEntry(new nsCSSValueList);
nsCSSValue::Array* result =
resultListEntry->mValue.InitFunction(filterFunction, 1);
// "hue-rotate" is the only filter-function that accepts negative values, and
// we don't use this "restrictions" variable in its clause below.
const uint32_t restrictions = CSS_PROPERTY_VALUE_NONNEGATIVE;
const nsCSSValue& funcArg1 = a1->Item(1);
const nsCSSValue& funcArg2 = a2->Item(1);
nsCSSValue& resultArg = result->Item(1);
float initialVal = 1.0f;
switch (filterFunction) {
case eCSSKeyword_blur: {
nsCSSUnit unit;
if (funcArg1.GetUnit() == funcArg2.GetUnit()) {
unit = funcArg1.GetUnit();
} else {
// If units differ, we'll just combine them with calc().
unit = eCSSUnit_Calc;
}
if (!AddCSSValuePixelPercentCalc(restrictions,
unit,
aCoeff1, funcArg1,
aCoeff2, funcArg2,
resultArg)) {
return false;
}
break;
}
case eCSSKeyword_grayscale:
case eCSSKeyword_invert:
case eCSSKeyword_sepia:
initialVal = 0.0f;
MOZ_FALLTHROUGH;
case eCSSKeyword_brightness:
case eCSSKeyword_contrast:
case eCSSKeyword_opacity:
case eCSSKeyword_saturate:
AddCSSValuePercentNumber(restrictions,
aCoeff1, funcArg1,
aCoeff2, funcArg2,
resultArg,
initialVal);
break;
case eCSSKeyword_hue_rotate:
AddCSSValueAngle(aCoeff1, funcArg1,
aCoeff2, funcArg2,
resultArg);
break;
case eCSSKeyword_drop_shadow: {
nsCSSValueList* resultShadow = resultArg.SetListValue();
nsAutoPtr<nsCSSValueList> shadowValue;
nsCSSValueList **shadowTail = getter_Transfers(shadowValue);
MOZ_ASSERT(!funcArg1.GetListValue()->mNext &&
!funcArg2.GetListValue()->mNext,
"drop-shadow filter func doesn't support lists");
if (!AddShadowItems(aCoeff1, funcArg1.GetListValue()->mValue,
aCoeff2, funcArg2.GetListValue()->mValue,
shadowTail)) {
return false;
}
*resultShadow = *shadowValue;
break;
}
default:
MOZ_ASSERT(false, "unknown filter function");
return false;
}
*aResultTail = resultListEntry.forget();
aResultTail = &(*aResultTail)->mNext;
return true;
}
static bool
AddFilterFunction(double aCoeff1, const nsCSSValueList* aList1,
double aCoeff2, const nsCSSValueList* aList2,
nsCSSValueList**& aResultTail)
{
MOZ_ASSERT(aList1 || aList2,
"one function list item must not be null");
// Note that one of our arguments could be null, indicating that
// it's the initial value. Rather than adding special null-handling
// logic, we just check for null values and replace them with
// 0 * the other value. That way, AddFilterFunctionImpl can assume
// its args are non-null.
if (!aList1) {
return AddFilterFunctionImpl(aCoeff2, aList2, 0, aList2, aResultTail);
}
if (!aList2) {
return AddFilterFunctionImpl(aCoeff1, aList1, 0, aList1, aResultTail);
}
return AddFilterFunctionImpl(aCoeff1, aList1, aCoeff2, aList2, aResultTail);
}
static inline uint32_t
ShapeArgumentCount(nsCSSKeyword aShapeFunction)
{
switch (aShapeFunction) {
case eCSSKeyword_circle:
return 2; // radius and center point
case eCSSKeyword_polygon:
return 2; // fill rule and a list of points
case eCSSKeyword_ellipse:
return 3; // two radii and center point
case eCSSKeyword_inset:
return 5; // four edge offsets and a list of corner radii
default:
MOZ_ASSERT_UNREACHABLE("Unknown shape type");
return 0;
}
}
static void
AddPositions(double aCoeff1, const nsCSSValue& aPos1,
double aCoeff2, const nsCSSValue& aPos2,
nsCSSValue& aResultPos)
{
MOZ_ASSERT(aPos1.GetUnit() == eCSSUnit_Array &&
aPos2.GetUnit() == eCSSUnit_Array,
"Args should be CSS <position>s, encoded as arrays");
const nsCSSValue::Array* posArray1 = aPos1.GetArrayValue();
const nsCSSValue::Array* posArray2 = aPos2.GetArrayValue();
MOZ_ASSERT(posArray1->Count() == 4 && posArray2->Count() == 4,
"CSSParserImpl::ParsePositionValue creates an array of length "
"4 - how did we get here?");
nsCSSValue::Array* resultPosArray = nsCSSValue::Array::Create(4);
aResultPos.SetArrayValue(resultPosArray, eCSSUnit_Array);
// Only iterate over elements 1 and 3. The <position> is 'uncomputed' to
// only those elements. See also the comment in SetPositionValue.
for (size_t i = 1; i < 4; i += 2) {
const nsCSSValue& v1 = posArray1->Item(i);
const nsCSSValue& v2 = posArray2->Item(i);
nsCSSValue& vr = resultPosArray->Item(i);
AddCSSValueCanonicalCalc(aCoeff1, v1,
aCoeff2, v2, vr);
}
}
static UniquePtr<nsCSSValuePair>
AddCSSValuePair(nsCSSProperty aProperty, uint32_t aRestrictions,
double aCoeff1, const nsCSSValuePair* aPair1,
double aCoeff2, const nsCSSValuePair* aPair2)
{
MOZ_ASSERT(aPair1, "expected pair");
MOZ_ASSERT(aPair2, "expected pair");
UniquePtr<nsCSSValuePair> result;
nsCSSUnit unit[2];
unit[0] = GetCommonUnit(aProperty, aPair1->mXValue.GetUnit(),
aPair2->mXValue.GetUnit());
unit[1] = GetCommonUnit(aProperty, aPair1->mYValue.GetUnit(),
aPair2->mYValue.GetUnit());
if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
unit[0] == eCSSUnit_URL || unit[0] == eCSSUnit_Enumerated) {
return result; // nullptr (returning |result| for RVO)
}
result = MakeUnique<nsCSSValuePair>();
static nsCSSValue nsCSSValuePair::* const pairValues[2] = {
&nsCSSValuePair::mXValue, &nsCSSValuePair::mYValue
};
for (uint32_t i = 0; i < 2; ++i) {
nsCSSValue nsCSSValuePair::*member = pairValues[i];
if (!AddCSSValuePixelPercentCalc(aRestrictions, unit[i],
aCoeff1, aPair1->*member,
aCoeff2, aPair2->*member,
result.get()->*member) ) {
MOZ_ASSERT(false, "unexpected unit");
result.reset();
return result; // nullptr (returning |result| for RVO)
}
}
return result;
}
static UniquePtr<nsCSSValuePairList>
AddCSSValuePairList(nsCSSProperty aProperty,
double aCoeff1, const nsCSSValuePairList* aList1,
double aCoeff2, const nsCSSValuePairList* aList2)
{
MOZ_ASSERT(aList1, "Can't add a null list");
MOZ_ASSERT(aList2, "Can't add a null list");
auto result = MakeUnique<nsCSSValuePairList>();
nsCSSValuePairList* resultPtr = result.get();
do {
static nsCSSValue nsCSSValuePairList::* const pairListValues[] = {
&nsCSSValuePairList::mXValue,
&nsCSSValuePairList::mYValue,
};
uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
for (uint32_t i = 0; i < ArrayLength(pairListValues); ++i) {
const nsCSSValue& v1 = aList1->*(pairListValues[i]);
const nsCSSValue& v2 = aList2->*(pairListValues[i]);
nsCSSValue& vr = resultPtr->*(pairListValues[i]);
nsCSSUnit unit =
GetCommonUnit(aProperty, v1.GetUnit(), v2.GetUnit());
if (unit == eCSSUnit_Null) {
return nullptr;
}
if (!AddCSSValuePixelPercentCalc(restrictions, unit,
aCoeff1, v1,
aCoeff2, v2, vr)) {
if (v1 != v2) {
return nullptr;
}
vr = v1;
}
}
aList1 = aList1->mNext;
aList2 = aList2->mNext;
if (!aList1 || !aList2) {
break;
}
resultPtr->mNext = new nsCSSValuePairList;
resultPtr = resultPtr->mNext;
} while (aList1 && aList2);
if (aList1 || aList2) {
return nullptr; // We can't interpolate lists of different lengths
}
return result;
}
static already_AddRefed<nsCSSValue::Array>
AddShapeFunction(nsCSSProperty aProperty,
double aCoeff1, const nsCSSValue::Array* aArray1,
double aCoeff2, const nsCSSValue::Array* aArray2)
{
MOZ_ASSERT(aArray1 && aArray1->Count() == 2, "expected shape function");
MOZ_ASSERT(aArray2 && aArray2->Count() == 2, "expected shape function");
MOZ_ASSERT(aArray1->Item(0).GetUnit() == eCSSUnit_Function,
"expected function");
MOZ_ASSERT(aArray2->Item(0).GetUnit() == eCSSUnit_Function,
"expected function");
MOZ_ASSERT(aArray1->Item(1).GetUnit() == eCSSUnit_Enumerated,
"expected geometry-box");
MOZ_ASSERT(aArray2->Item(1).GetUnit() == eCSSUnit_Enumerated,
"expected geometry-box");
MOZ_ASSERT(aArray1->Item(1).GetIntValue() == aArray2->Item(1).GetIntValue(),
"expected matching geometry-box values");
const nsCSSValue::Array* func1 = aArray1->Item(0).GetArrayValue();
const nsCSSValue::Array* func2 = aArray2->Item(0).GetArrayValue();
nsCSSKeyword shapeFuncName = func1->Item(0).GetKeywordValue();
if (shapeFuncName != func2->Item(0).GetKeywordValue()) {
return nullptr; // Can't add two shapes of different types.
}
RefPtr<nsCSSValue::Array> result = nsCSSValue::Array::Create(2);
nsCSSValue::Array* resultFuncArgs =
result->Item(0).InitFunction(shapeFuncName,
ShapeArgumentCount(shapeFuncName));
switch (shapeFuncName) {
case eCSSKeyword_ellipse:
// Add ellipses' |ry| values (but fail if we encounter an enum):
if (!AddCSSValuePixelPercentCalc(CSS_PROPERTY_VALUE_NONNEGATIVE,
GetCommonUnit(aProperty,
func1->Item(2).GetUnit(),
func2->Item(2).GetUnit()),
aCoeff1, func1->Item(2),
aCoeff2, func2->Item(2),
resultFuncArgs->Item(2))) {
return nullptr;
}
MOZ_FALLTHROUGH; // to handle rx and center point
case eCSSKeyword_circle: {
// Add circles' |r| (or ellipses' |rx|) values:
if (!AddCSSValuePixelPercentCalc(CSS_PROPERTY_VALUE_NONNEGATIVE,
GetCommonUnit(aProperty,
func1->Item(1).GetUnit(),
func2->Item(1).GetUnit()),
aCoeff1, func1->Item(1),
aCoeff2, func2->Item(1),
resultFuncArgs->Item(1))) {
return nullptr;
}
// Add center points (defined as a <position>).
size_t posIndex = shapeFuncName == eCSSKeyword_circle ? 2 : 3;
AddPositions(aCoeff1, func1->Item(posIndex),
aCoeff2, func2->Item(posIndex),
resultFuncArgs->Item(posIndex));
break;
}
case eCSSKeyword_polygon: {
// Add polygons' corresponding points (if the fill rule matches):
int32_t fillRule = func1->Item(1).GetIntValue();
if (fillRule != func2->Item(1).GetIntValue()) {
return nullptr; // can't interpolate between different fill rules
}
resultFuncArgs->Item(1).SetIntValue(fillRule, eCSSUnit_Enumerated);
const nsCSSValuePairList* points1 = func1->Item(2).GetPairListValue();
const nsCSSValuePairList* points2 = func2->Item(2).GetPairListValue();
UniquePtr<nsCSSValuePairList> resultPoints =
AddCSSValuePairList(aProperty, aCoeff1, points1, aCoeff2, points2);
if (!resultPoints) {
return nullptr;
}
resultFuncArgs->Item(2).AdoptPairListValue(Move(resultPoints));
break;
}
case eCSSKeyword_inset: {
MOZ_ASSERT(func1->Count() == 6 && func2->Count() == 6,
"Update for CSSParserImpl::ParseInsetFunction changes");
// Items 1-4 are respectively the top, right, bottom and left offsets
// from the reference box.
for (size_t i = 1; i <= 4; ++i) {
if (!AddCSSValuePixelPercentCalc(CSS_PROPERTY_VALUE_NONNEGATIVE,
GetCommonUnit(aProperty,
func1->Item(i).GetUnit(),
func2->Item(i).GetUnit()),
aCoeff1, func1->Item(i),
aCoeff2, func2->Item(i),
resultFuncArgs->Item(i))) {
return nullptr;
}
}
// Item 5 contains the radii of the rounded corners for the inset
// rectangle.
MOZ_ASSERT(func1->Item(5).GetUnit() == eCSSUnit_Array &&
func2->Item(5).GetUnit() == eCSSUnit_Array,
"Expected two arrays");
const nsCSSValue::Array* radii1 = func1->Item(5).GetArrayValue();
const nsCSSValue::Array* radii2 = func2->Item(5).GetArrayValue();
MOZ_ASSERT(radii1->Count() == 4 && radii2->Count() == 4);
nsCSSValue::Array* resultRadii = nsCSSValue::Array::Create(4);
resultFuncArgs->Item(5).SetArrayValue(resultRadii, eCSSUnit_Array);
// We use an arbitrary border-radius property here to get the appropriate
// restrictions for radii since this is a <border-radius> value.
uint32_t restrictions =
nsCSSProps::ValueRestrictions(eCSSProperty_border_top_left_radius);
for (size_t i = 0; i < 4; ++i) {
const nsCSSValuePair& pair1 = radii1->Item(i).GetPairValue();
const nsCSSValuePair& pair2 = radii2->Item(i).GetPairValue();
UniquePtr<nsCSSValuePair>
pairResult(AddCSSValuePair(aProperty, restrictions,
aCoeff1, &pair1,
aCoeff2, &pair2));
if (!pairResult) {
return nullptr;
}
resultRadii->Item(i).SetPairValue(pairResult.release());
}
break;
}
default:
MOZ_ASSERT_UNREACHABLE("Unknown shape type");
return nullptr;
}
// set the geometry-box value
result->Item(1).SetIntValue(aArray1->Item(1).GetIntValue(),
eCSSUnit_Enumerated);
return result.forget();
}
static nsCSSValueList*
AddTransformLists(double aCoeff1, const nsCSSValueList* aList1,
double aCoeff2, const nsCSSValueList* aList2)
{
nsAutoPtr<nsCSSValueList> result;
nsCSSValueList **resultTail = getter_Transfers(result);
do {
RefPtr<nsCSSValue::Array> a1 = ToPrimitive(aList1->mValue.GetArrayValue()),
a2 = ToPrimitive(aList2->mValue.GetArrayValue());
MOZ_ASSERT(
TransformFunctionsMatch(nsStyleTransformMatrix::TransformFunctionOf(a1),
nsStyleTransformMatrix::TransformFunctionOf(a2)),
"transform function mismatch");
MOZ_ASSERT(!*resultTail,
"resultTail isn't pointing to the tail (may leak)");
nsCSSKeyword tfunc = nsStyleTransformMatrix::TransformFunctionOf(a1);
RefPtr<nsCSSValue::Array> arr;
if (tfunc != eCSSKeyword_matrix &&
tfunc != eCSSKeyword_matrix3d &&
tfunc != eCSSKeyword_interpolatematrix &&
tfunc != eCSSKeyword_rotate3d &&
tfunc != eCSSKeyword_perspective) {
arr = StyleAnimationValue::AppendTransformFunction(tfunc, resultTail);
}
switch (tfunc) {
case eCSSKeyword_translate3d: {
MOZ_ASSERT(a1->Count() == 4, "unexpected count");
MOZ_ASSERT(a2->Count() == 4, "unexpected count");
AddTransformTranslate(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
arr->Item(1));
AddTransformTranslate(aCoeff1, a1->Item(2), aCoeff2, a2->Item(2),
arr->Item(2));
AddTransformTranslate(aCoeff1, a1->Item(3), aCoeff2, a2->Item(3),
arr->Item(3));
break;
}
case eCSSKeyword_scale3d: {
MOZ_ASSERT(a1->Count() == 4, "unexpected count");
MOZ_ASSERT(a2->Count() == 4, "unexpected count");
AddTransformScale(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
arr->Item(1));
AddTransformScale(aCoeff1, a1->Item(2), aCoeff2, a2->Item(2),
arr->Item(2));
AddTransformScale(aCoeff1, a1->Item(3), aCoeff2, a2->Item(3),
arr->Item(3));
break;
}
// It would probably be nicer to animate skew in tangent space
// rather than angle space. However, it's easy to specify
// skews with infinite tangents, and behavior changes pretty
// drastically when crossing such skews (since the direction of
// animation flips), so interop is probably more important here.
case eCSSKeyword_skew: {
MOZ_ASSERT(a1->Count() == 2 || a1->Count() == 3,
"unexpected count");
MOZ_ASSERT(a2->Count() == 2 || a2->Count() == 3,
"unexpected count");
nsCSSValue zero(0.0f, eCSSUnit_Radian);
// Add Y component of skew.
AddCSSValueAngle(aCoeff1,
a1->Count() == 3 ? a1->Item(2) : zero,
aCoeff2,
a2->Count() == 3 ? a2->Item(2) : zero,
arr->Item(2));
// Add X component of skew (which can be merged with case below
// in non-DEBUG).
AddCSSValueAngle(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
arr->Item(1));
break;
}
case eCSSKeyword_skewx:
case eCSSKeyword_skewy:
case eCSSKeyword_rotate:
case eCSSKeyword_rotatex:
case eCSSKeyword_rotatey:
case eCSSKeyword_rotatez: {
MOZ_ASSERT(a1->Count() == 2, "unexpected count");
MOZ_ASSERT(a2->Count() == 2, "unexpected count");
AddCSSValueAngle(aCoeff1, a1->Item(1), aCoeff2, a2->Item(1),
arr->Item(1));
break;
}
case eCSSKeyword_rotate3d: {
Point3D vector1(a1->Item(1).GetFloatValue(),
a1->Item(2).GetFloatValue(),
a1->Item(3).GetFloatValue());
vector1.Normalize();
Point3D vector2(a2->Item(1).GetFloatValue(),
a2->Item(2).GetFloatValue(),
a2->Item(3).GetFloatValue());
vector2.Normalize();
// Handle rotate3d with matched (normalized) vectors,
// otherwise fallthrough to the next switch statement
// and do matrix decomposition.
if (vector1 == vector2) {
// We skipped appending a transform function above for rotate3d,
// so do it now.
arr = StyleAnimationValue::AppendTransformFunction(tfunc, resultTail);
arr->Item(1).SetFloatValue(vector1.x, eCSSUnit_Number);
arr->Item(2).SetFloatValue(vector1.y, eCSSUnit_Number);
arr->Item(3).SetFloatValue(vector1.z, eCSSUnit_Number);
AddCSSValueAngle(aCoeff1, a1->Item(4), aCoeff2, a2->Item(4),
arr->Item(4));
break;
}
MOZ_FALLTHROUGH;
}
case eCSSKeyword_matrix:
case eCSSKeyword_matrix3d:
case eCSSKeyword_interpolatematrix:
case eCSSKeyword_perspective: {
// FIXME: If the matrix contains only numbers then we could decompose
// here.
// Construct temporary lists with only this item in them.
nsCSSValueList tempList1, tempList2;
tempList1.mValue = aList1->mValue;
tempList2.mValue = aList2->mValue;
if (aList1 == aList2) {
*resultTail =
AddDifferentTransformLists(aCoeff1, &tempList1, aCoeff2, &tempList1);
} else {
*resultTail =
AddDifferentTransformLists(aCoeff1, &tempList1, aCoeff2, &tempList2);
}
// Now advance resultTail to point to the new tail slot.
while (*resultTail) {
resultTail = &(*resultTail)->mNext;
}
break;
}
default:
MOZ_ASSERT(false, "unknown transform function");
}
aList1 = aList1->mNext;
aList2 = aList2->mNext;
} while (aList1);
MOZ_ASSERT(!aList2, "list length mismatch");
MOZ_ASSERT(!*resultTail,
"resultTail isn't pointing to the tail");
return result.forget();
}
bool
StyleAnimationValue::AddWeighted(nsCSSProperty aProperty,
double aCoeff1,
const StyleAnimationValue& aValue1,
double aCoeff2,
const StyleAnimationValue& aValue2,
StyleAnimationValue& aResultValue)
{
Unit commonUnit =
GetCommonUnit(aProperty, aValue1.GetUnit(), aValue2.GetUnit());
// Maybe need a followup method to convert the inputs into the common
// unit-type, if they don't already match it. (Or would it make sense to do
// that in GetCommonUnit? in which case maybe ConvertToCommonUnit would be
// better.)
switch (commonUnit) {
case eUnit_Null:
case eUnit_Auto:
case eUnit_None:
case eUnit_Normal:
case eUnit_UnparsedString:
case eUnit_URL:
case eUnit_CurrentColor:
return false;
case eUnit_Enumerated:
switch (aProperty) {
case eCSSProperty_font_stretch: {
// Animate just like eUnit_Integer.
int32_t result = floor(aCoeff1 * double(aValue1.GetIntValue()) +
aCoeff2 * double(aValue2.GetIntValue()));
if (result < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED) {
result = NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED;
} else if (result > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) {
result = NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED;
}
aResultValue.SetIntValue(result, eUnit_Enumerated);
return true;
}
default:
return false;
}
case eUnit_Visibility: {
int32_t enum1 = aValue1.GetIntValue();
int32_t enum2 = aValue2.GetIntValue();
if (enum1 == enum2) {
aResultValue.SetIntValue(enum1, eUnit_Visibility);
return true;
}
if ((enum1 == NS_STYLE_VISIBILITY_VISIBLE) ==
(enum2 == NS_STYLE_VISIBILITY_VISIBLE)) {
return false;
}
int32_t val1 = enum1 == NS_STYLE_VISIBILITY_VISIBLE;
int32_t val2 = enum2 == NS_STYLE_VISIBILITY_VISIBLE;
double interp = aCoeff1 * val1 + aCoeff2 * val2;
int32_t result = interp > 0.0 ? NS_STYLE_VISIBILITY_VISIBLE
: (val1 ? enum2 : enum1);
aResultValue.SetIntValue(result, eUnit_Visibility);
return true;
}
case eUnit_Integer: {
// http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
// says we should use floor
int32_t result = floor(aCoeff1 * double(aValue1.GetIntValue()) +
aCoeff2 * double(aValue2.GetIntValue()));
if (aProperty == eCSSProperty_font_weight) {
if (result < 100) {
result = 100;
} else if (result > 900) {
result = 900;
}
result -= result % 100;
} else {
result = RestrictValue(aProperty, result);
}
aResultValue.SetIntValue(result, eUnit_Integer);
return true;
}
case eUnit_Coord: {
aResultValue.SetCoordValue(RestrictValue(aProperty, NSToCoordRound(
aCoeff1 * aValue1.GetCoordValue() +
aCoeff2 * aValue2.GetCoordValue())));
return true;
}
case eUnit_Percent: {
aResultValue.SetPercentValue(RestrictValue(aProperty,
aCoeff1 * aValue1.GetPercentValue() +
aCoeff2 * aValue2.GetPercentValue()));
return true;
}
case eUnit_Float: {
aResultValue.SetFloatValue(RestrictValue(aProperty,
aCoeff1 * aValue1.GetFloatValue() +
aCoeff2 * aValue2.GetFloatValue()));
return true;
}
case eUnit_Color: {
nscolor color1 = aValue1.GetColorValue();
nscolor color2 = aValue2.GetColorValue();
// FIXME (spec): The CSS transitions spec doesn't say whether
// colors are premultiplied, but things work better when they are,
// so use premultiplication. Spec issue is still open per
// http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
// To save some math, scale the alpha down to a 0-1 scale, but
// leave the color components on a 0-255 scale.
double A1 = NS_GET_A(color1) * (1.0 / 255.0);
double R1 = NS_GET_R(color1) * A1;
double G1 = NS_GET_G(color1) * A1;
double B1 = NS_GET_B(color1) * A1;
double A2 = NS_GET_A(color2) * (1.0 / 255.0);
double R2 = NS_GET_R(color2) * A2;
double G2 = NS_GET_G(color2) * A2;
double B2 = NS_GET_B(color2) * A2;
double Aresf = (A1 * aCoeff1 + A2 * aCoeff2);
nscolor resultColor;
if (Aresf <= 0.0) {
resultColor = NS_RGBA(0, 0, 0, 0);
} else {
if (Aresf > 1.0) {
Aresf = 1.0;
}
double factor = 1.0 / Aresf;
uint8_t Ares = NSToIntRound(Aresf * 255.0);
uint8_t Rres = ClampColor((R1 * aCoeff1 + R2 * aCoeff2) * factor);
uint8_t Gres = ClampColor((G1 * aCoeff1 + G2 * aCoeff2) * factor);
uint8_t Bres = ClampColor((B1 * aCoeff1 + B2 * aCoeff2) * factor);
resultColor = NS_RGBA(Rres, Gres, Bres, Ares);
}
aResultValue.SetColorValue(resultColor);
return true;
}
case eUnit_Calc: {
PixelCalcValue v1 = ExtractCalcValue(aValue1);
PixelCalcValue v2 = ExtractCalcValue(aValue2);
double len = aCoeff1 * v1.mLength + aCoeff2 * v2.mLength;
double pct = aCoeff1 * v1.mPercent + aCoeff2 * v2.mPercent;
bool hasPct = (aCoeff1 != 0.0 && v1.mHasPercent) ||
(aCoeff2 != 0.0 && v2.mHasPercent);
nsCSSValue *val = new nsCSSValue();
nsCSSValue::Array *arr = nsCSSValue::Array::Create(1);
val->SetArrayValue(arr, eCSSUnit_Calc);
if (hasPct) {
nsCSSValue::Array *arr2 = nsCSSValue::Array::Create(2);
arr2->Item(0).SetFloatValue(len, eCSSUnit_Pixel);
arr2->Item(1).SetPercentValue(pct);
arr->Item(0).SetArrayValue(arr2, eCSSUnit_Calc_Plus);
} else {
arr->Item(0).SetFloatValue(len, eCSSUnit_Pixel);
}
aResultValue.SetAndAdoptCSSValueValue(val, eUnit_Calc);
return true;
}
case eUnit_ObjectPosition: {
const nsCSSValue* position1 = aValue1.GetCSSValueValue();
const nsCSSValue* position2 = aValue2.GetCSSValueValue();
nsAutoPtr<nsCSSValue> result(new nsCSSValue);
AddPositions(aCoeff1, *position1,
aCoeff2, *position2, *result);
aResultValue.SetAndAdoptCSSValueValue(result.forget(),
eUnit_ObjectPosition);
return true;
}
case eUnit_CSSValuePair: {
uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
UniquePtr<nsCSSValuePair> result(
AddCSSValuePair(aProperty, restrictions,
aCoeff1, aValue1.GetCSSValuePairValue(),
aCoeff2, aValue2.GetCSSValuePairValue()));
if (!result) {
return false;
}
aResultValue.SetAndAdoptCSSValuePairValue(result.release(),
eUnit_CSSValuePair);
return true;
}
case eUnit_CSSValueTriplet: {
nsCSSValueTriplet triplet1(*aValue1.GetCSSValueTripletValue());
nsCSSValueTriplet triplet2(*aValue2.GetCSSValueTripletValue());
nsCSSUnit unit[3];
unit[0] = GetCommonUnit(aProperty, triplet1.mXValue.GetUnit(),
triplet2.mXValue.GetUnit());
unit[1] = GetCommonUnit(aProperty, triplet1.mYValue.GetUnit(),
triplet2.mYValue.GetUnit());
unit[2] = GetCommonUnit(aProperty, triplet1.mZValue.GetUnit(),
triplet2.mZValue.GetUnit());
if (unit[0] == eCSSUnit_Null || unit[1] == eCSSUnit_Null ||
unit[2] == eCSSUnit_Null) {
return false;
}
nsAutoPtr<nsCSSValueTriplet> result(new nsCSSValueTriplet);
static nsCSSValue nsCSSValueTriplet::* const tripletValues[3] = {
&nsCSSValueTriplet::mXValue, &nsCSSValueTriplet::mYValue, &nsCSSValueTriplet::mZValue
};
uint32_t restrictions = nsCSSProps::ValueRestrictions(aProperty);
for (uint32_t i = 0; i < 3; ++i) {
nsCSSValue nsCSSValueTriplet::*member = tripletValues[i];
if (!AddCSSValuePixelPercentCalc(restrictions, unit[i],
aCoeff1, &triplet1->*member,
aCoeff2, &triplet2->*member,
result->*member) ) {
MOZ_ASSERT(false, "unexpected unit");
return false;
}
}
aResultValue.SetAndAdoptCSSValueTripletValue(result.forget(),
eUnit_CSSValueTriplet);
return true;
}
case eUnit_CSSRect: {
MOZ_ASSERT(nsCSSProps::ValueRestrictions(aProperty) == 0,
"must add code for handling value restrictions");
const nsCSSRect *rect1 = aValue1.GetCSSRectValue();
const nsCSSRect *rect2 = aValue2.GetCSSRectValue();
if (rect1->mTop.GetUnit() != rect2->mTop.GetUnit() ||
rect1->mRight.GetUnit() != rect2->mRight.GetUnit() ||
rect1->mBottom.GetUnit() != rect2->mBottom.GetUnit() ||
rect1->mLeft.GetUnit() != rect2->mLeft.GetUnit()) {
// At least until we have calc()
return false;
}
nsAutoPtr<nsCSSRect> result(new nsCSSRect);
for (uint32_t i = 0; i < ArrayLength(nsCSSRect::sides); ++i) {
nsCSSValue nsCSSRect::*member = nsCSSRect::sides[i];
MOZ_ASSERT((rect1->*member).GetUnit() == (rect2->*member).GetUnit(),
"should have returned above");
switch ((rect1->*member).GetUnit()) {
case eCSSUnit_Pixel:
AddCSSValuePixel(aCoeff1, rect1->*member, aCoeff2, rect2->*member,
result->*member);
break;
case eCSSUnit_Auto:
if (float(aCoeff1 + aCoeff2) != 1.0f) {
// Interpolating between two auto values makes sense;
// adding in other ratios does not.
return false;
}
(result->*member).SetAutoValue();
break;
default:
MOZ_ASSERT(false, "unexpected unit");
return false;
}
}
aResultValue.SetAndAdoptCSSRectValue(result.forget(), eUnit_CSSRect);
return true;
}
case eUnit_Dasharray: {
const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
uint32_t len1 = 0, len2 = 0;
for (const nsCSSValueList *v = list1; v; v = v->mNext) {
++len1;
}
for (const nsCSSValueList *v = list2; v; v = v->mNext) {
++len2;
}
MOZ_ASSERT(len1 > 0 && len2 > 0, "unexpected length");
if (list1->mValue.GetUnit() == eCSSUnit_None ||
list2->mValue.GetUnit() == eCSSUnit_None) {
// One of our values is "none". Can't do addition with that.
MOZ_ASSERT(
(list1->mValue.GetUnit() != eCSSUnit_None || len1 == 1) &&
(list2->mValue.GetUnit() != eCSSUnit_None || len2 == 1),
"multi-value valuelist with 'none' as first element");
return false;
}
nsAutoPtr<nsCSSValueList> result;
nsCSSValueList **resultTail = getter_Transfers(result);
for (uint32_t i = 0, i_end = EuclidLCM<uint32_t>(len1, len2); i != i_end; ++i) {
const nsCSSValue &v1 = list1->mValue;
const nsCSSValue &v2 = list2->mValue;
MOZ_ASSERT(v1.GetUnit() == eCSSUnit_Number ||
v1.GetUnit() == eCSSUnit_Percent, "unexpected");
MOZ_ASSERT(v2.GetUnit() == eCSSUnit_Number ||
v2.GetUnit() == eCSSUnit_Percent, "unexpected");
if (v1.GetUnit() != v2.GetUnit()) {
// Can't animate between lengths and percentages (until calc()).
return false;
}
nsCSSValueList *item = new nsCSSValueList;
*resultTail = item;
resultTail = &item->mNext;
if (v1.GetUnit() == eCSSUnit_Number) {
AddCSSValueNumber(aCoeff1, v1, aCoeff2, v2, item->mValue,
CSS_PROPERTY_VALUE_NONNEGATIVE);
} else {
AddCSSValuePercent(aCoeff1, v1, aCoeff2, v2, item->mValue,
CSS_PROPERTY_VALUE_NONNEGATIVE);
}
list1 = list1->mNext;
if (!list1) {
list1 = aValue1.GetCSSValueListValue();
}
list2 = list2->mNext;
if (!list2) {
list2 = aValue2.GetCSSValueListValue();
}
}
aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
eUnit_Dasharray);
return true;
}
case eUnit_Shadow: {
// This is implemented according to:
// http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
// and the third item in the summary of:
// http://lists.w3.org/Archives/Public/www-style/2009Jul/0050.html
const nsCSSValueList *shadow1 = aValue1.GetCSSValueListValue();
const nsCSSValueList *shadow2 = aValue2.GetCSSValueListValue();
nsAutoPtr<nsCSSValueList> result;
nsCSSValueList **resultTail = getter_Transfers(result);
while (shadow1 && shadow2) {
if (!AddShadowItems(aCoeff1, shadow1->mValue,
aCoeff2, shadow2->mValue,
resultTail)) {
return false;
}
shadow1 = shadow1->mNext;
shadow2 = shadow2->mNext;
}
if (shadow1 || shadow2) {
const nsCSSValueList *longShadow;
double longCoeff;
if (shadow1) {
longShadow = shadow1;
longCoeff = aCoeff1;
} else {
longShadow = shadow2;
longCoeff = aCoeff2;
}
while (longShadow) {
// Passing coefficients that add to less than 1 produces the
// desired result of interpolating "0 0 0 transparent" with
// the current shadow.
if (!AddShadowItems(longCoeff, longShadow->mValue,
0.0, longShadow->mValue,
resultTail)) {
return false;
}
longShadow = longShadow->mNext;
}
}
aResultValue.SetAndAdoptCSSValueListValue(result.forget(), eUnit_Shadow);
return true;
}
case eUnit_Shape: {
RefPtr<nsCSSValue::Array> result =
AddShapeFunction(aProperty,
aCoeff1, aValue1.GetCSSValueArrayValue(),
aCoeff2, aValue2.GetCSSValueArrayValue());
if (!result) {
return false;
}
aResultValue.SetAndAdoptCSSValueArrayValue(result.forget().take(),
eUnit_Shape);
return true;
}
case eUnit_Filter: {
const nsCSSValueList *list1 = aValue1.GetCSSValueListValue();
const nsCSSValueList *list2 = aValue2.GetCSSValueListValue();
nsAutoPtr<nsCSSValueList> result;
nsCSSValueList **resultTail = getter_Transfers(result);
while (list1 || list2) {
MOZ_ASSERT(!*resultTail,
"resultTail isn't pointing to the tail (may leak)");
if ((list1 && list1->mValue.GetUnit() != eCSSUnit_Function) ||
(list2 && list2->mValue.GetUnit() != eCSSUnit_Function)) {
// If we don't have filter-functions, we must have filter-URLs, which
// we can't add or interpolate.
return false;
}
if (!AddFilterFunction(aCoeff1, list1, aCoeff2, list2, resultTail)) {
// filter function mismatch
return false;
}
// move to next list items
if (list1) {
list1 = list1->mNext;
}
if (list2) {
list2 = list2->mNext;
}
}
MOZ_ASSERT(!*resultTail,
"resultTail isn't pointing to the tail (may leak)");
aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
eUnit_Filter);
return true;
}
case eUnit_Transform: {
const nsCSSValueList* list1 = aValue1.GetCSSValueSharedListValue()->mHead;
const nsCSSValueList* list2 = aValue2.GetCSSValueSharedListValue()->mHead;
MOZ_ASSERT(list1);
MOZ_ASSERT(list2);
// We want to avoid the matrix decomposition when we can, since
// avoiding it can produce better results both for compound
// transforms and for skew and skewY (see below). We can do this
// in two cases:
// (1) if one of the transforms is 'none'
// (2) if the lists have the same length and the transform
// functions match
nsAutoPtr<nsCSSValueList> result;
if (list1->mValue.GetUnit() == eCSSUnit_None) {
if (list2->mValue.GetUnit() == eCSSUnit_None) {
result = new nsCSSValueList;
if (result) {
result->mValue.SetNoneValue();
}
} else {
result = AddTransformLists(0, list2, aCoeff2, list2);
}
} else {
if (list2->mValue.GetUnit() == eCSSUnit_None) {
result = AddTransformLists(0, list1, aCoeff1, list1);
} else {
bool match = true;
{
const nsCSSValueList *item1 = list1, *item2 = list2;
do {
nsCSSKeyword func1 = nsStyleTransformMatrix::TransformFunctionOf(
item1->mValue.GetArrayValue());
nsCSSKeyword func2 = nsStyleTransformMatrix::TransformFunctionOf(
item2->mValue.GetArrayValue());
if (!TransformFunctionsMatch(func1, func2)) {
break;
}
item1 = item1->mNext;
item2 = item2->mNext;
} while (item1 && item2);
if (item1 || item2) {
// Either |break| above or length mismatch.
match = false;
}
}
if (match) {
result = AddTransformLists(aCoeff1, list1, aCoeff2, list2);
} else {
result = AddDifferentTransformLists(aCoeff1, list1, aCoeff2, list2);
}
}
}
aResultValue.SetTransformValue(new nsCSSValueSharedList(result.forget()));
return true;
}
case eUnit_BackgroundPosition: {
const nsCSSValueList *position1 = aValue1.GetCSSValueListValue();
const nsCSSValueList *position2 = aValue2.GetCSSValueListValue();
nsAutoPtr<nsCSSValueList> result;
nsCSSValueList **resultTail = getter_Transfers(result);
while (position1 && position2) {
nsCSSValueList *item = new nsCSSValueList;
*resultTail = item;
resultTail = &item->mNext;
AddPositions(aCoeff1, position1->mValue,
aCoeff2, position2->mValue, item->mValue);
position1 = position1->mNext;
position2 = position2->mNext;
}
// Check for different lengths
if (position1 || position2) {
return false;
}
aResultValue.SetAndAdoptCSSValueListValue(result.forget(),
eUnit_BackgroundPosition);
return true;
}
case eUnit_CSSValuePairList: {
const nsCSSValuePairList *list1 = aValue1.GetCSSValuePairListValue();
const nsCSSValuePairList *list2 = aValue2.GetCSSValuePairListValue();
UniquePtr<nsCSSValuePairList> result =
AddCSSValuePairList(aProperty, aCoeff1, list1, aCoeff2, list2);
if (!result) {
return false;
}
aResultValue.SetAndAdoptCSSValuePairListValue(result.release());
return true;
}
}
MOZ_ASSERT(false, "Can't interpolate using the given common unit");
return false;
}
already_AddRefed<css::StyleRule>
BuildStyleRule(nsCSSProperty aProperty,
dom::Element* aTargetElement,
const nsAString& aSpecifiedValue,
bool aUseSVGMode)
{
// Set up an empty CSS Declaration
RefPtr<css::Declaration> declaration(new css::Declaration());
declaration->InitializeEmpty();
bool changed; // ignored, but needed as outparam for ParseProperty
nsIDocument* doc = aTargetElement->OwnerDoc();
nsCOMPtr<nsIURI> baseURI = aTargetElement->GetBaseURI();
nsCSSParser parser(doc->CSSLoader());
nsCSSProperty propertyToCheck = nsCSSProps::IsShorthand(aProperty) ?
nsCSSProps::SubpropertyEntryFor(aProperty)[0] : aProperty;
// Get a parser, parse the property, and check for CSS parsing errors.
// If this fails, we bail out and delete the declaration.
parser.ParseProperty(aProperty, aSpecifiedValue, doc->GetDocumentURI(),
baseURI, aTargetElement->NodePrincipal(), declaration,
&changed, false, aUseSVGMode);
// check whether property parsed without CSS parsing errors
if (!declaration->HasNonImportantValueFor(propertyToCheck)) {
return nullptr;
}
RefPtr<css::StyleRule> rule = new css::StyleRule(nullptr,
declaration,
0, 0);
return rule.forget();
}
already_AddRefed<css::StyleRule>
BuildStyleRule(nsCSSProperty aProperty,
dom::Element* aTargetElement,
const nsCSSValue& aSpecifiedValue,
bool aUseSVGMode)
{
MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
"Should be a longhand property");
// Check if longhand failed to parse correctly.
if (aSpecifiedValue.GetUnit() == eCSSUnit_Null) {
return nullptr;
}
// Set up an empty CSS Declaration
RefPtr<css::Declaration> declaration(new css::Declaration());
declaration->InitializeEmpty();
// Add our longhand value
nsCSSExpandedDataBlock block;
declaration->ExpandTo(&block);
block.AddLonghandProperty(aProperty, aSpecifiedValue);
declaration->ValueAppended(aProperty);
declaration->CompressFrom(&block);
RefPtr<css::StyleRule> rule = new css::StyleRule(nullptr, declaration, 0, 0);
return rule.forget();
}
static bool
ComputeValuesFromStyleRule(nsCSSProperty aProperty,
nsCSSProps::EnabledState aEnabledState,
dom::Element* aTargetElement,
nsStyleContext* aStyleContext,
css::StyleRule* aStyleRule,
nsTArray<PropertyStyleAnimationValuePair>& aValues,
bool* aIsContextSensitive)
{
MOZ_ASSERT(aStyleContext);
if (!nsCSSProps::IsEnabled(aProperty, aEnabledState)) {
return false;
}
MOZ_ASSERT(aStyleContext->PresContext()->StyleSet()->IsGecko(),
"ServoStyleSet should not use StyleAnimationValue for animations");
nsStyleSet* styleSet = aStyleContext->PresContext()->StyleSet()->AsGecko();
RefPtr<nsStyleContext> tmpStyleContext;
if (aIsContextSensitive) {
MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty),
"to correctly set aIsContextSensitive for shorthand properties, "
"this code must be adjusted");
nsCOMArray<nsIStyleRule> ruleArray;
ruleArray.AppendObject(styleSet->InitialStyleRule());
css::Declaration* declaration = aStyleRule->GetDeclaration();
ruleArray.AppendObject(declaration);
declaration->SetImmutable();
tmpStyleContext =
styleSet->ResolveStyleByAddingRules(aStyleContext, ruleArray);
if (!tmpStyleContext) {
return false;
}
// Force walk of rule tree
nsStyleStructID sid = nsCSSProps::kSIDTable[aProperty];
tmpStyleContext->StyleData(sid);
// The rule node will have unconditional cached style data if the value is
// not context-sensitive. So if there's nothing cached, it's not context
// sensitive.
*aIsContextSensitive =
!tmpStyleContext->RuleNode()->NodeHasCachedUnconditionalData(sid);
}
// If we're not concerned whether the property is context sensitive then just
// add the rule to a new temporary style context alongside the target
// element's style context.
// Also, if we previously discovered that this property IS context-sensitive
// then we need to throw the temporary style context out since the property's
// value may have been biased by the 'initial' values supplied.
if (!aIsContextSensitive || *aIsContextSensitive) {
nsCOMArray<nsIStyleRule> ruleArray;
css::Declaration* declaration = aStyleRule->GetDeclaration();
ruleArray.AppendObject(declaration);
declaration->SetImmutable();
tmpStyleContext =
styleSet->ResolveStyleByAddingRules(aStyleContext, ruleArray);
if (!tmpStyleContext) {
return false;
}
}
// Extract computed value of our property (or all longhand components, if
// aProperty is a shorthand) from the temporary style rule
if (nsCSSProps::IsShorthand(aProperty)) {
CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty, aEnabledState) {
if (nsCSSProps::kAnimTypeTable[*p] == eStyleAnimType_None) {
// Skip non-animatable component longhands.
continue;
}
PropertyStyleAnimationValuePair* pair = aValues.AppendElement();
pair->mProperty = *p;
if (!StyleAnimationValue::ExtractComputedValue(*p, tmpStyleContext,
pair->mValue)) {
return false;
}
}
return true;
} else {
PropertyStyleAnimationValuePair* pair = aValues.AppendElement();
pair->mProperty = aProperty;
return StyleAnimationValue::ExtractComputedValue(aProperty, tmpStyleContext,
pair->mValue);
}
}
/* static */ bool
StyleAnimationValue::ComputeValue(nsCSSProperty aProperty,
dom::Element* aTargetElement,
nsStyleContext* aStyleContext,
const nsAString& aSpecifiedValue,
bool aUseSVGMode,
StyleAnimationValue& aComputedValue,
bool* aIsContextSensitive)
{
MOZ_ASSERT(aTargetElement, "null target element");
// Parse specified value into a temporary css::StyleRule
// Note: BuildStyleRule needs an element's OwnerDoc, BaseURI, and Principal.
// If it is a pseudo element, use its parent element's OwnerDoc, BaseURI,
// and Principal.
RefPtr<css::StyleRule> styleRule =
BuildStyleRule(aProperty, aTargetElement, aSpecifiedValue, aUseSVGMode);
if (!styleRule) {
return false;
}
if (nsCSSProps::IsShorthand(aProperty) ||
nsCSSProps::kAnimTypeTable[aProperty] == eStyleAnimType_None) {
// Just capture the specified value
aComputedValue.SetUnparsedStringValue(nsString(aSpecifiedValue));
if (aIsContextSensitive) {
// Since we're just returning the string as-is, aComputedValue isn't going
// to change depending on the context
*aIsContextSensitive = false;
}
return true;
}
AutoTArray<PropertyStyleAnimationValuePair,1> values;
bool ok = ComputeValuesFromStyleRule(aProperty,
nsCSSProps::eIgnoreEnabledState,
aTargetElement, aStyleContext, styleRule,
values, aIsContextSensitive);
if (!ok) {
return false;
}
MOZ_ASSERT(values.Length() == 1);
MOZ_ASSERT(values[0].mProperty == aProperty);
aComputedValue = values[0].mValue;
return true;
}
template <class T>
bool
ComputeValuesFromSpecifiedValue(
nsCSSProperty aProperty,
nsCSSProps::EnabledState aEnabledState,
dom::Element* aTargetElement,
nsStyleContext* aStyleContext,
T& aSpecifiedValue,
bool aUseSVGMode,
nsTArray<PropertyStyleAnimationValuePair>& aResult)
{
MOZ_ASSERT(aTargetElement, "null target element");
// Parse specified value into a temporary css::StyleRule
// Note: BuildStyleRule needs an element's OwnerDoc, BaseURI, and Principal.
// If it is a pseudo element, use its parent element's OwnerDoc, BaseURI,
// and Principal.
RefPtr<css::StyleRule> styleRule =
BuildStyleRule(aProperty, aTargetElement, aSpecifiedValue, aUseSVGMode);
if (!styleRule) {
return false;
}
aResult.Clear();
return ComputeValuesFromStyleRule(aProperty, aEnabledState, aTargetElement,
aStyleContext, styleRule, aResult,
/* aIsContextSensitive */ nullptr);
}
/* static */ bool
StyleAnimationValue::ComputeValues(
nsCSSProperty aProperty,
nsCSSProps::EnabledState aEnabledState,
dom::Element* aTargetElement,
nsStyleContext* aStyleContext,
const nsAString& aSpecifiedValue,
bool aUseSVGMode,
nsTArray<PropertyStyleAnimationValuePair>& aResult)
{
return ComputeValuesFromSpecifiedValue(aProperty, aEnabledState,
aTargetElement, aStyleContext,
aSpecifiedValue, aUseSVGMode,
aResult);
}
/* static */ bool
StyleAnimationValue::ComputeValues(
nsCSSProperty aProperty,
nsCSSProps::EnabledState aEnabledState,
dom::Element* aTargetElement,
nsStyleContext* aStyleContext,
const nsCSSValue& aSpecifiedValue,
bool aUseSVGMode,
nsTArray<PropertyStyleAnimationValuePair>& aResult)
{
return ComputeValuesFromSpecifiedValue(aProperty, aEnabledState,
aTargetElement, aStyleContext,
aSpecifiedValue, aUseSVGMode,
aResult);
}
bool
StyleAnimationValue::UncomputeValue(nsCSSProperty aProperty,
const StyleAnimationValue& aComputedValue,
nsCSSValue& aSpecifiedValue)
{
Unit unit = aComputedValue.GetUnit();
switch (unit) {
case eUnit_Normal:
aSpecifiedValue.SetNormalValue();
break;
case eUnit_Auto:
aSpecifiedValue.SetAutoValue();
break;
case eUnit_None:
aSpecifiedValue.SetNoneValue();
break;
case eUnit_Enumerated:
case eUnit_Visibility:
aSpecifiedValue.
SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Enumerated);
break;
case eUnit_Integer:
aSpecifiedValue.
SetIntValue(aComputedValue.GetIntValue(), eCSSUnit_Integer);
break;
case eUnit_Coord:
nscoordToCSSValue(aComputedValue.GetCoordValue(), aSpecifiedValue);
break;
case eUnit_Percent:
aSpecifiedValue.SetPercentValue(aComputedValue.GetPercentValue());
break;
case eUnit_Float:
aSpecifiedValue.
SetFloatValue(aComputedValue.GetFloatValue(), eCSSUnit_Number);
break;
case eUnit_Color:
// colors can be alone, or part of a paint server
aSpecifiedValue.SetColorValue(aComputedValue.GetColorValue());
break;
case eUnit_CurrentColor:
aSpecifiedValue.SetIntValue(NS_COLOR_CURRENTCOLOR, eCSSUnit_EnumColor);
break;
case eUnit_Calc:
case eUnit_ObjectPosition:
case eUnit_URL: {
nsCSSValue* val = aComputedValue.GetCSSValueValue();
// Sanity-check that the underlying unit in the nsCSSValue is what we
// expect for our StyleAnimationValue::Unit:
MOZ_ASSERT((unit == eUnit_Calc && val->GetUnit() == eCSSUnit_Calc) ||
(unit == eUnit_ObjectPosition &&
val->GetUnit() == eCSSUnit_Array) ||
(unit == eUnit_URL && val->GetUnit() == eCSSUnit_URL),
"unexpected unit");
aSpecifiedValue = *val;
break;
}
case eUnit_CSSValuePair: {
// Rule node processing expects pair values to be collapsed to a
// single value if both halves would be equal, for most but not
// all properties. At present, all animatable properties that
// use pairs do expect collapsing.
const nsCSSValuePair* pair = aComputedValue.GetCSSValuePairValue();
if (pair->mXValue == pair->mYValue) {
aSpecifiedValue = pair->mXValue;
} else {
aSpecifiedValue.SetPairValue(pair);
}
} break;
case eUnit_CSSValueTriplet: {
// Rule node processing expects triplet values to be collapsed to a
// single value if both halves would be equal, for most but not
// all properties. At present, all animatable properties that
// use pairs do expect collapsing.
const nsCSSValueTriplet* triplet = aComputedValue.GetCSSValueTripletValue();
if (triplet->mXValue == triplet->mYValue && triplet->mYValue == triplet->mZValue) {
aSpecifiedValue = triplet->mXValue;
} else {
aSpecifiedValue.SetTripletValue(triplet);
}
} break;
case eUnit_CSSRect: {
nsCSSRect& rect = aSpecifiedValue.SetRectValue();
rect = *aComputedValue.GetCSSRectValue();
} break;
case eUnit_Dasharray:
case eUnit_Shadow:
case eUnit_Filter:
case eUnit_BackgroundPosition:
{
nsCSSValueList* computedList = aComputedValue.GetCSSValueListValue();
if (computedList) {
aSpecifiedValue.SetDependentListValue(computedList);
} else {
aSpecifiedValue.SetNoneValue();
}
}
break;
case eUnit_Shape: {
nsCSSValue::Array* computedArray = aComputedValue.GetCSSValueArrayValue();
aSpecifiedValue.SetArrayValue(computedArray, eCSSUnit_Array);
break;
}
case eUnit_Transform:
aSpecifiedValue.
SetSharedListValue(aComputedValue.GetCSSValueSharedListValue());
break;
case eUnit_CSSValuePairList:
aSpecifiedValue.
SetDependentPairListValue(aComputedValue.GetCSSValuePairListValue());
break;
default:
return false;
}
return true;
}
bool
StyleAnimationValue::UncomputeValue(nsCSSProperty aProperty,
StyleAnimationValue&& aComputedValue,
nsCSSValue& aSpecifiedValue)
{
Unit unit = aComputedValue.GetUnit();
switch (unit) {
case eUnit_Dasharray:
case eUnit_Shadow:
case eUnit_Filter:
case eUnit_BackgroundPosition:
{
UniquePtr<nsCSSValueList> computedList =
aComputedValue.TakeCSSValueListValue();
if (computedList) {
aSpecifiedValue.AdoptListValue(Move(computedList));
} else {
aSpecifiedValue.SetNoneValue();
}
}
break;
case eUnit_CSSValuePairList:
{
UniquePtr<nsCSSValuePairList> computedList =
aComputedValue.TakeCSSValuePairListValue();
MOZ_ASSERT(computedList, "Pair list should never be null");
aSpecifiedValue.AdoptPairListValue(Move(computedList));
}
break;
default:
return UncomputeValue(aProperty, aComputedValue, aSpecifiedValue);
}
return true;
}
bool
StyleAnimationValue::UncomputeValue(nsCSSProperty aProperty,
const StyleAnimationValue& aComputedValue,
nsAString& aSpecifiedValue)
{
aSpecifiedValue.Truncate(); // Clear outparam, if it's not already empty
if (aComputedValue.GetUnit() == eUnit_UnparsedString) {
aComputedValue.GetStringValue(aSpecifiedValue);
return true;
}
nsCSSValue val;
if (!StyleAnimationValue::UncomputeValue(aProperty, aComputedValue, val)) {
return false;
}
val.AppendToString(aProperty, aSpecifiedValue, nsCSSValue::eNormalized);
return true;
}
inline const void*
StyleDataAtOffset(const void* aStyleStruct, ptrdiff_t aOffset)
{
return reinterpret_cast<const char*>(aStyleStruct) + aOffset;
}
inline void*
StyleDataAtOffset(void* aStyleStruct, ptrdiff_t aOffset)
{
return reinterpret_cast<char*>(aStyleStruct) + aOffset;
}
static void
SetCurrentOrActualColor(bool aIsForeground, nscolor aActualColor,
StyleAnimationValue& aComputedValue)
{
if (aIsForeground) {
aComputedValue.SetCurrentColorValue();
} else {
aComputedValue.SetColorValue(aActualColor);
}
}
static void
ExtractBorderColor(nsStyleContext* aStyleContext, const void* aStyleBorder,
mozilla::css::Side aSide,
StyleAnimationValue& aComputedValue)
{
nscolor color;
bool foreground;
static_cast<const nsStyleBorder*>(aStyleBorder)->
GetBorderColor(aSide, color, foreground);
if (foreground) {
// FIXME: should add test for this
color = aStyleContext->StyleColor()->mColor;
}
aComputedValue.SetColorValue(color);
}
static bool
StyleCoordToValue(const nsStyleCoord& aCoord, StyleAnimationValue& aValue)
{
switch (aCoord.GetUnit()) {
case eStyleUnit_Normal:
aValue.SetNormalValue();
break;
case eStyleUnit_Auto:
aValue.SetAutoValue();
break;
case eStyleUnit_None:
aValue.SetNoneValue();
break;
case eStyleUnit_Percent:
aValue.SetPercentValue(aCoord.GetPercentValue());
break;
case eStyleUnit_Factor:
aValue.SetFloatValue(aCoord.GetFactorValue());
break;
case eStyleUnit_Coord:
aValue.SetCoordValue(aCoord.GetCoordValue());
break;
case eStyleUnit_Enumerated:
aValue.SetIntValue(aCoord.GetIntValue(),
StyleAnimationValue::eUnit_Enumerated);
break;
case eStyleUnit_Integer:
aValue.SetIntValue(aCoord.GetIntValue(),
StyleAnimationValue::eUnit_Integer);
break;
case eStyleUnit_Calc: {
nsAutoPtr<nsCSSValue> val(new nsCSSValue);
SetCalcValue(aCoord.GetCalcValue(), *val);
aValue.SetAndAdoptCSSValueValue(val.forget(),
StyleAnimationValue::eUnit_Calc);
break;
}
default:
return false;
}
return true;
}
static bool
StyleCoordToCSSValue(const nsStyleCoord& aCoord, nsCSSValue& aCSSValue)
{
switch (aCoord.GetUnit()) {
case eStyleUnit_Coord:
nscoordToCSSValue(aCoord.GetCoordValue(), aCSSValue);
break;
case eStyleUnit_Factor:
aCSSValue.SetFloatValue(aCoord.GetFactorValue(), eCSSUnit_Number);
break;
case eStyleUnit_Percent:
aCSSValue.SetPercentValue(aCoord.GetPercentValue());
break;
case eStyleUnit_Calc:
SetCalcValue(aCoord.GetCalcValue(), aCSSValue);
break;
case eStyleUnit_Degree:
aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Degree);
break;
case eStyleUnit_Grad:
aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Grad);
break;
case eStyleUnit_Radian:
aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Radian);
break;
case eStyleUnit_Turn:
aCSSValue.SetFloatValue(aCoord.GetAngleValue(), eCSSUnit_Turn);
break;
default:
MOZ_ASSERT(false, "unexpected unit");
return false;
}
return true;
}
static void
SetPositionValue(const nsStyleImageLayers::Position& aPos, nsCSSValue& aCSSValue)
{
RefPtr<nsCSSValue::Array> posArray = nsCSSValue::Array::Create(4);
aCSSValue.SetArrayValue(posArray.get(), eCSSUnit_Array);
// NOTE: Array entries #0 and #2 here are intentionally left untouched, with
// eCSSUnit_Null. The purpose of these entries in our specified-style
// <position> representation is to store edge names. But for values
// extracted from computed style (which is what we're dealing with here),
// we'll just have a normalized "x,y" position, with no edge names needed.
nsCSSValue& xValue = posArray->Item(1);
nsCSSValue& yValue = posArray->Item(3);
SetCalcValue(&aPos.mXPosition, xValue);
SetCalcValue(&aPos.mYPosition, yValue);
}
/*
* Assign |aOutput = aInput|, except with any non-pixel lengths
* replaced with the equivalent in pixels, and any non-canonical calc()
* expressions replaced with canonical ones.
*/
static void
SubstitutePixelValues(nsStyleContext* aStyleContext,
const nsCSSValue& aInput, nsCSSValue& aOutput)
{
if (aInput.IsCalcUnit()) {
RuleNodeCacheConditions conditions;
nsRuleNode::ComputedCalc c =
nsRuleNode::SpecifiedCalcToComputedCalc(aInput, aStyleContext,
aStyleContext->PresContext(),
conditions);
nsStyleCoord::CalcValue c2;
c2.mLength = c.mLength;
c2.mPercent = c.mPercent;
c2.mHasPercent = true; // doesn't matter for transform translate
SetCalcValue(&c2, aOutput);
} else if (aInput.UnitHasArrayValue()) {
const nsCSSValue::Array *inputArray = aInput.GetArrayValue();
RefPtr<nsCSSValue::Array> outputArray =
nsCSSValue::Array::Create(inputArray->Count());
for (size_t i = 0, i_end = inputArray->Count(); i < i_end; ++i) {
SubstitutePixelValues(aStyleContext,
inputArray->Item(i), outputArray->Item(i));
}
aOutput.SetArrayValue(outputArray, aInput.GetUnit());
} else if (aInput.IsLengthUnit() &&
aInput.GetUnit() != eCSSUnit_Pixel) {
RuleNodeCacheConditions conditions;
nscoord len = nsRuleNode::CalcLength(aInput, aStyleContext,
aStyleContext->PresContext(),
conditions);
aOutput.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(len),
eCSSUnit_Pixel);
} else {
aOutput = aInput;
}
}
static void
ExtractImageLayerPositionList(const nsStyleImageLayers& aLayer,
StyleAnimationValue& aComputedValue)
{
MOZ_ASSERT(aLayer.mPositionCount > 0, "unexpected count");
nsAutoPtr<nsCSSValueList> result;
nsCSSValueList **resultTail = getter_Transfers(result);
for (uint32_t i = 0, i_end = aLayer.mPositionCount; i != i_end; ++i) {
nsCSSValueList *item = new nsCSSValueList;
*resultTail = item;
resultTail = &item->mNext;
SetPositionValue(aLayer.mLayers[i].mPosition, item->mValue);
}
aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
StyleAnimationValue::eUnit_BackgroundPosition);
}
static void
ExtractImageLayerSizePairList(const nsStyleImageLayers& aLayer,
StyleAnimationValue& aComputedValue)
{
MOZ_ASSERT(aLayer.mSizeCount > 0, "unexpected count");
nsAutoPtr<nsCSSValuePairList> result;
nsCSSValuePairList **resultTail = getter_Transfers(result);
for (uint32_t i = 0, i_end = aLayer.mSizeCount; i != i_end; ++i) {
nsCSSValuePairList *item = new nsCSSValuePairList;
*resultTail = item;
resultTail = &item->mNext;
const nsStyleImageLayers::Size &size = aLayer.mLayers[i].mSize;
switch (size.mWidthType) {
case nsStyleImageLayers::Size::eContain:
case nsStyleImageLayers::Size::eCover:
item->mXValue.SetIntValue(size.mWidthType,
eCSSUnit_Enumerated);
break;
case nsStyleImageLayers::Size::eAuto:
item->mXValue.SetAutoValue();
break;
case nsStyleImageLayers::Size::eLengthPercentage:
// XXXbz is there a good reason we can't just
// SetCalcValue(&size.mWidth, item->mXValue) here?
if (!size.mWidth.mHasPercent &&
// negative values must have come from calc()
size.mWidth.mLength >= 0) {
MOZ_ASSERT(size.mWidth.mPercent == 0.0f,
"Shouldn't have mPercent");
nscoordToCSSValue(size.mWidth.mLength, item->mXValue);
} else if (size.mWidth.mLength == 0 &&
// negative values must have come from calc()
size.mWidth.mPercent >= 0.0f) {
item->mXValue.SetPercentValue(size.mWidth.mPercent);
} else {
SetCalcValue(&size.mWidth, item->mXValue);
}
break;
}
switch (size.mHeightType) {
case nsStyleImageLayers::Size::eContain:
case nsStyleImageLayers::Size::eCover:
// leave it null
break;
case nsStyleImageLayers::Size::eAuto:
item->mYValue.SetAutoValue();
break;
case nsStyleImageLayers::Size::eLengthPercentage:
// XXXbz is there a good reason we can't just
// SetCalcValue(&size.mHeight, item->mYValue) here?
if (!size.mHeight.mHasPercent &&
// negative values must have come from calc()
size.mHeight.mLength >= 0) {
MOZ_ASSERT(size.mHeight.mPercent == 0.0f,
"Shouldn't have mPercent");
nscoordToCSSValue(size.mHeight.mLength, item->mYValue);
} else if (size.mHeight.mLength == 0 &&
// negative values must have come from calc()
size.mHeight.mPercent >= 0.0f) {
item->mYValue.SetPercentValue(size.mHeight.mPercent);
} else {
SetCalcValue(&size.mHeight, item->mYValue);
}
break;
}
}
aComputedValue.SetAndAdoptCSSValuePairListValue(result.forget());
}
static bool
StyleClipBasicShapeToCSSArray(const nsStyleClipPath& aClipPath,
nsCSSValue::Array* aResult)
{
MOZ_ASSERT(aResult->Count() == 2,
"Expected array to be presized for a function and the sizing-box");
const nsStyleBasicShape* shape = aClipPath.GetBasicShape();
nsCSSKeyword functionName = shape->GetShapeTypeName();
RefPtr<nsCSSValue::Array> functionArray;
switch (shape->GetShapeType()) {
case nsStyleBasicShape::Type::eCircle:
case nsStyleBasicShape::Type::eEllipse: {
const nsTArray<nsStyleCoord>& coords = shape->Coordinates();
MOZ_ASSERT(coords.Length() == ShapeArgumentCount(functionName) - 1,
"Unexpected radii count");
// The "+1" is for the center point:
functionArray = aResult->Item(0).InitFunction(functionName,
coords.Length() + 1);
for (size_t i = 0; i < coords.Length(); ++i) {
if (coords[i].GetUnit() == eStyleUnit_Enumerated) {
functionArray->Item(i + 1).SetIntValue(coords[i].GetIntValue(),
eCSSUnit_Enumerated);
} else if (!StyleCoordToCSSValue(coords[i],
functionArray->Item(i + 1))) {
return false;
}
}
// Set functionArray's last item to the circle or ellipse's center point:
SetPositionValue(shape->GetPosition(),
functionArray->Item(functionArray->Count() - 1));
break;
}
case nsStyleBasicShape::Type::ePolygon: {
functionArray =
aResult->Item(0).InitFunction(functionName,
ShapeArgumentCount(functionName));
functionArray->Item(1).SetIntValue(shape->GetFillRule(),
eCSSUnit_Enumerated);
nsCSSValuePairList* list = functionArray->Item(2).SetPairListValue();
const nsTArray<nsStyleCoord>& coords = shape->Coordinates();
MOZ_ASSERT((coords.Length() % 2) == 0);
for (size_t i = 0; i < coords.Length(); i += 2) {
if (i > 0) {
list->mNext = new nsCSSValuePairList;
list = list->mNext;
}
if (!StyleCoordToCSSValue(coords[i], list->mXValue) ||
!StyleCoordToCSSValue(coords[i + 1], list->mYValue)) {
return false;
}
}
break;
}
case nsStyleBasicShape::Type::eInset: {
const nsTArray<nsStyleCoord>& coords = shape->Coordinates();
MOZ_ASSERT(coords.Length() == ShapeArgumentCount(functionName) - 1,
"Unexpected offset count");
functionArray =
aResult->Item(0).InitFunction(functionName, coords.Length() + 1);
for (size_t i = 0; i < coords.Length(); ++i) {
if (!StyleCoordToCSSValue(coords[i], functionArray->Item(i + 1))) {
return false;
}
}
RefPtr<nsCSSValue::Array> radiusArray = nsCSSValue::Array::Create(4);
const nsStyleCorners& radii = shape->GetRadius();
NS_FOR_CSS_FULL_CORNERS(corner) {
auto pair = MakeUnique<nsCSSValuePair>();
if (!StyleCoordToCSSValue(radii.Get(NS_FULL_TO_HALF_CORNER(corner, false)),
pair->mXValue) ||
!StyleCoordToCSSValue(radii.Get(NS_FULL_TO_HALF_CORNER(corner, true)),
pair->mYValue)) {
return false;
}
radiusArray->Item(corner).SetPairValue(pair.release());
}
// Set the last item in functionArray to the radius array:
functionArray->Item(functionArray->Count() - 1).
SetArrayValue(radiusArray, eCSSUnit_Array);
break;
}
default:
MOZ_ASSERT_UNREACHABLE("Unknown shape type");
return false;
}
aResult->Item(1).SetIntValue(aClipPath.GetSizingBox(), eCSSUnit_Enumerated);
return true;
}
bool
StyleAnimationValue::ExtractComputedValue(nsCSSProperty aProperty,
nsStyleContext* aStyleContext,
StyleAnimationValue& aComputedValue)
{
MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_no_shorthands,
"bad property");
const void* styleStruct =
aStyleContext->StyleData(nsCSSProps::kSIDTable[aProperty]);
ptrdiff_t ssOffset = nsCSSProps::kStyleStructOffsetTable[aProperty];
nsStyleAnimType animType = nsCSSProps::kAnimTypeTable[aProperty];
MOZ_ASSERT(0 <= ssOffset || animType == eStyleAnimType_Custom,
"must be dealing with animatable property");
switch (animType) {
case eStyleAnimType_Custom:
switch (aProperty) {
// For border-width, ignore the border-image business (which
// only exists until we update our implementation to the current
// spec) and use GetComputedBorder
#define BORDER_WIDTH_CASE(prop_, side_) \
case prop_: \
aComputedValue.SetCoordValue( \
static_cast<const nsStyleBorder*>(styleStruct)-> \
GetComputedBorder().side_); \
break;
BORDER_WIDTH_CASE(eCSSProperty_border_bottom_width, bottom)
BORDER_WIDTH_CASE(eCSSProperty_border_left_width, left)
BORDER_WIDTH_CASE(eCSSProperty_border_right_width, right)
BORDER_WIDTH_CASE(eCSSProperty_border_top_width, top)
#undef BORDER_WIDTH_CASE
case eCSSProperty_column_rule_width:
aComputedValue.SetCoordValue(
static_cast<const nsStyleColumn*>(styleStruct)->
GetComputedColumnRuleWidth());
break;
case eCSSProperty_border_bottom_color:
ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_BOTTOM,
aComputedValue);
break;
case eCSSProperty_border_left_color:
ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_LEFT,
aComputedValue);
break;
case eCSSProperty_border_right_color:
ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_RIGHT,
aComputedValue);
break;
case eCSSProperty_border_top_color:
ExtractBorderColor(aStyleContext, styleStruct, NS_SIDE_TOP,
aComputedValue);
break;
case eCSSProperty_outline_color: {
const nsStyleOutline *styleOutline =
static_cast<const nsStyleOutline*>(styleStruct);
nscolor color;
if (!styleOutline->GetOutlineColor(color))
color = aStyleContext->StyleColor()->mColor;
aComputedValue.SetColorValue(color);
break;
}
case eCSSProperty_column_rule_color: {
const nsStyleColumn *styleColumn =
static_cast<const nsStyleColumn*>(styleStruct);
nscolor color;
if (styleColumn->mColumnRuleColorIsForeground) {
color = aStyleContext->StyleColor()->mColor;
} else {
color = styleColumn->mColumnRuleColor;
}
aComputedValue.SetColorValue(color);
break;
}
case eCSSProperty_column_count: {
const nsStyleColumn *styleColumn =
static_cast<const nsStyleColumn*>(styleStruct);
if (styleColumn->mColumnCount == NS_STYLE_COLUMN_COUNT_AUTO) {
aComputedValue.SetAutoValue();
} else {
aComputedValue.SetIntValue(styleColumn->mColumnCount,
eUnit_Integer);
}
break;
}
case eCSSProperty_order: {
const nsStylePosition *stylePosition =
static_cast<const nsStylePosition*>(styleStruct);
aComputedValue.SetIntValue(stylePosition->mOrder,
eUnit_Integer);
break;
}
case eCSSProperty_text_decoration_color: {
const nsStyleTextReset *styleTextReset =
static_cast<const nsStyleTextReset*>(styleStruct);
nscolor color;
bool isForeground;
styleTextReset->GetDecorationColor(color, isForeground);
if (isForeground) {
color = aStyleContext->GetTextFillColor();
}
aComputedValue.SetColorValue(color);
break;
}
case eCSSProperty_text_decoration_style: {
uint8_t decorationStyle =
static_cast<const nsStyleTextReset*>(styleStruct)->
GetDecorationStyle();
aComputedValue.SetIntValue(decorationStyle, eUnit_Enumerated);
break;
}
case eCSSProperty_text_emphasis_color: {
auto styleText = static_cast<const nsStyleText*>(styleStruct);
SetCurrentOrActualColor(styleText->mTextEmphasisColorForeground,
styleText->mTextEmphasisColor,
aComputedValue);
break;
}
case eCSSProperty__webkit_text_fill_color: {
auto styleText = static_cast<const nsStyleText*>(styleStruct);
SetCurrentOrActualColor(styleText->mWebkitTextFillColorForeground,
styleText->mWebkitTextFillColor,
aComputedValue);
break;
}
case eCSSProperty__webkit_text_stroke_color: {
auto styleText = static_cast<const nsStyleText*>(styleStruct);
SetCurrentOrActualColor(styleText->mWebkitTextStrokeColorForeground,
styleText->mWebkitTextStrokeColor,
aComputedValue);
break;
}
case eCSSProperty_border_spacing: {
const nsStyleTableBorder *styleTableBorder =
static_cast<const nsStyleTableBorder*>(styleStruct);
nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
nscoordToCSSValue(styleTableBorder->mBorderSpacingCol, pair->mXValue);
nscoordToCSSValue(styleTableBorder->mBorderSpacingRow, pair->mYValue);
aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
eUnit_CSSValuePair);
break;
}
case eCSSProperty_transform_origin: {
const nsStyleDisplay *styleDisplay =
static_cast<const nsStyleDisplay*>(styleStruct);
nsAutoPtr<nsCSSValueTriplet> triplet(new nsCSSValueTriplet);
if (!StyleCoordToCSSValue(styleDisplay->mTransformOrigin[0],
triplet->mXValue) ||
!StyleCoordToCSSValue(styleDisplay->mTransformOrigin[1],
triplet->mYValue) ||
!StyleCoordToCSSValue(styleDisplay->mTransformOrigin[2],
triplet->mZValue)) {
return false;
}
aComputedValue.SetAndAdoptCSSValueTripletValue(triplet.forget(),
eUnit_CSSValueTriplet);
break;
}
case eCSSProperty_perspective_origin: {
const nsStyleDisplay *styleDisplay =
static_cast<const nsStyleDisplay*>(styleStruct);
nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
if (!StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[0],
pair->mXValue) ||
!StyleCoordToCSSValue(styleDisplay->mPerspectiveOrigin[1],
pair->mYValue)) {
return false;
}
aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
eUnit_CSSValuePair);
break;
}
case eCSSProperty_stroke_dasharray: {
const nsStyleSVG *svg = static_cast<const nsStyleSVG*>(styleStruct);
MOZ_ASSERT((svg->mStrokeDasharray != nullptr) ==
(svg->mStrokeDasharrayLength != 0),
"pointer/length mismatch");
nsAutoPtr<nsCSSValueList> result;
if (svg->mStrokeDasharray) {
MOZ_ASSERT(svg->mStrokeDasharrayLength > 0,
"non-null list should have positive length");
nsCSSValueList **resultTail = getter_Transfers(result);
for (uint32_t i = 0, i_end = svg->mStrokeDasharrayLength;
i != i_end; ++i) {
nsCSSValueList *item = new nsCSSValueList;
*resultTail = item;
resultTail = &item->mNext;
const nsStyleCoord &coord = svg->mStrokeDasharray[i];
nsCSSValue &value = item->mValue;
switch (coord.GetUnit()) {
case eStyleUnit_Coord:
// Number means the same thing as length; we want to
// animate them the same way. Normalize both to number
// since it has more accuracy (float vs nscoord).
value.SetFloatValue(nsPresContext::
AppUnitsToFloatCSSPixels(coord.GetCoordValue()),
eCSSUnit_Number);
break;
case eStyleUnit_Factor:
value.SetFloatValue(coord.GetFactorValue(),
eCSSUnit_Number);
break;
case eStyleUnit_Percent:
value.SetPercentValue(coord.GetPercentValue());
break;
default:
MOZ_ASSERT(false, "unexpected unit");
return false;
}
}
} else {
result = new nsCSSValueList;
result->mValue.SetNoneValue();
}
aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
eUnit_Dasharray);
break;
}
case eCSSProperty_font_stretch: {
int16_t stretch =
static_cast<const nsStyleFont*>(styleStruct)->mFont.stretch;
static_assert(NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED == -4 &&
NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED == 4,
"font stretch constants not as expected");
if (stretch < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED ||
stretch > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) {
return false;
}
aComputedValue.SetIntValue(stretch, eUnit_Enumerated);
return true;
}
case eCSSProperty_font_weight: {
uint16_t weight =
static_cast<const nsStyleFont*>(styleStruct)->mFont.weight;
if (weight % 100 != 0) {
return false;
}
aComputedValue.SetIntValue(weight, eUnit_Integer);
return true;
}
case eCSSProperty_image_region: {
const nsStyleList *list =
static_cast<const nsStyleList*>(styleStruct);
const nsRect &srect = list->mImageRegion;
if (srect.IsEmpty()) {
aComputedValue.SetAutoValue();
break;
}
nsCSSRect *vrect = new nsCSSRect;
nscoordToCSSValue(srect.x, vrect->mLeft);
nscoordToCSSValue(srect.y, vrect->mTop);
nscoordToCSSValue(srect.XMost(), vrect->mRight);
nscoordToCSSValue(srect.YMost(), vrect->mBottom);
aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
break;
}
case eCSSProperty_clip: {
const nsStyleEffects* effects =
static_cast<const nsStyleEffects*>(styleStruct);
if (!(effects->mClipFlags & NS_STYLE_CLIP_RECT)) {
aComputedValue.SetAutoValue();
} else {
nsCSSRect *vrect = new nsCSSRect;
const nsRect &srect = effects->mClip;
if (effects->mClipFlags & NS_STYLE_CLIP_TOP_AUTO) {
vrect->mTop.SetAutoValue();
} else {
nscoordToCSSValue(srect.y, vrect->mTop);
}
if (effects->mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO) {
vrect->mRight.SetAutoValue();
} else {
nscoordToCSSValue(srect.XMost(), vrect->mRight);
}
if (effects->mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO) {
vrect->mBottom.SetAutoValue();
} else {
nscoordToCSSValue(srect.YMost(), vrect->mBottom);
}
if (effects->mClipFlags & NS_STYLE_CLIP_LEFT_AUTO) {
vrect->mLeft.SetAutoValue();
} else {
nscoordToCSSValue(srect.x, vrect->mLeft);
}
aComputedValue.SetAndAdoptCSSRectValue(vrect, eUnit_CSSRect);
}
break;
}
case eCSSProperty_object_position: {
const nsStylePosition* stylePos =
static_cast<const nsStylePosition*>(styleStruct);
nsAutoPtr<nsCSSValue> val(new nsCSSValue);
SetPositionValue(stylePos->mObjectPosition, *val);
aComputedValue.SetAndAdoptCSSValueValue(val.forget(),
eUnit_ObjectPosition);
break;
}
case eCSSProperty_background_position: {
const nsStyleImageLayers& layers =
static_cast<const nsStyleBackground*>(styleStruct)->mImage;
ExtractImageLayerPositionList(layers, aComputedValue);
break;
}
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
case eCSSProperty_mask_position: {
const nsStyleImageLayers& layers =
static_cast<const nsStyleSVGReset*>(styleStruct)->mMask;
ExtractImageLayerPositionList(layers, aComputedValue);
break;
}
#endif
case eCSSProperty_background_size: {
const nsStyleImageLayers& layers =
static_cast<const nsStyleBackground*>(styleStruct)->mImage;
ExtractImageLayerSizePairList(layers, aComputedValue);
break;
}
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
case eCSSProperty_mask_size: {
const nsStyleImageLayers& layers =
static_cast<const nsStyleSVGReset*>(styleStruct)->mMask;
ExtractImageLayerSizePairList(layers, aComputedValue);
break;
}
#endif
case eCSSProperty_clip_path: {
const nsStyleSVGReset* svgReset =
static_cast<const nsStyleSVGReset*>(styleStruct);
const nsStyleClipPath& clipPath = svgReset->mClipPath;
const int32_t type = clipPath.GetType();
if (type == NS_STYLE_CLIP_PATH_URL) {
nsIDocument* doc = aStyleContext->PresContext()->Document();
RefPtr<nsStringBuffer> uriAsStringBuffer =
GetURIAsUtf16StringBuffer(clipPath.GetURL());
RefPtr<mozilla::css::URLValue> url =
new mozilla::css::URLValue(clipPath.GetURL(),
uriAsStringBuffer,
doc->GetDocumentURI(),
doc->NodePrincipal());
auto result = MakeUnique<nsCSSValue>();
result->SetURLValue(url);
aComputedValue.SetAndAdoptCSSValueValue(result.release(), eUnit_URL);
} else if (type == NS_STYLE_CLIP_PATH_BOX) {
aComputedValue.SetIntValue(clipPath.GetSizingBox(), eUnit_Enumerated);
} else if (type == NS_STYLE_CLIP_PATH_SHAPE) {
RefPtr<nsCSSValue::Array> result = nsCSSValue::Array::Create(2);
if (!StyleClipBasicShapeToCSSArray(clipPath, result)) {
return false;
}
aComputedValue.SetAndAdoptCSSValueArrayValue(result.forget().take(),
eUnit_Shape);
} else {
MOZ_ASSERT(type == NS_STYLE_CLIP_PATH_NONE, "unknown type");
aComputedValue.SetNoneValue();
}
break;
}
case eCSSProperty_filter: {
const nsStyleEffects* effects =
static_cast<const nsStyleEffects*>(styleStruct);
const nsTArray<nsStyleFilter>& filters = effects->mFilters;
nsAutoPtr<nsCSSValueList> result;
nsCSSValueList **resultTail = getter_Transfers(result);
for (uint32_t i = 0; i < filters.Length(); ++i) {
nsCSSValueList *item = new nsCSSValueList;
*resultTail = item;
resultTail = &item->mNext;
const nsStyleFilter& filter = filters[i];
int32_t type = filter.GetType();
if (type == NS_STYLE_FILTER_URL) {
nsIDocument* doc = aStyleContext->PresContext()->Document();
RefPtr<nsStringBuffer> uriAsStringBuffer =
GetURIAsUtf16StringBuffer(filter.GetURL());
RefPtr<mozilla::css::URLValue> url =
new mozilla::css::URLValue(filter.GetURL(),
uriAsStringBuffer,
doc->GetDocumentURI(),
doc->NodePrincipal());
item->mValue.SetURLValue(url);
} else {
nsCSSKeyword functionName =
nsCSSProps::ValueToKeywordEnum(type,
nsCSSProps::kFilterFunctionKTable);
nsCSSValue::Array* filterArray =
item->mValue.InitFunction(functionName, 1);
if (type >= NS_STYLE_FILTER_BLUR && type <= NS_STYLE_FILTER_HUE_ROTATE) {
if (!StyleCoordToCSSValue(
filter.GetFilterParameter(),
filterArray->Item(1))) {
return false;
}
} else if (type == NS_STYLE_FILTER_DROP_SHADOW) {
nsCSSValueList* shadowResult = filterArray->Item(1).SetListValue();
nsAutoPtr<nsCSSValueList> tmpShadowValue;
nsCSSValueList **tmpShadowResultTail = getter_Transfers(tmpShadowValue);
nsCSSShadowArray* shadowArray = filter.GetDropShadow();
MOZ_ASSERT(shadowArray->Length() == 1,
"expected exactly one shadow");
AppendCSSShadowValue(shadowArray->ShadowAt(0), tmpShadowResultTail);
*shadowResult = *tmpShadowValue;
} else {
// We checked all possible nsStyleFilter types but
// NS_STYLE_FILTER_NULL before. We should never enter this path.
NS_NOTREACHED("no other filter functions defined");
return false;
}
}
}
aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
eUnit_Filter);
break;
}
case eCSSProperty_transform: {
const nsStyleDisplay *display =
static_cast<const nsStyleDisplay*>(styleStruct);
nsAutoPtr<nsCSSValueList> result;
if (display->mSpecifiedTransform) {
// Clone, and convert all lengths (not percents) to pixels.
nsCSSValueList **resultTail = getter_Transfers(result);
for (const nsCSSValueList *l = display->mSpecifiedTransform->mHead;
l; l = l->mNext) {
nsCSSValueList *clone = new nsCSSValueList;
*resultTail = clone;
resultTail = &clone->mNext;
SubstitutePixelValues(aStyleContext, l->mValue, clone->mValue);
}
} else {
result = new nsCSSValueList();
result->mValue.SetNoneValue();
}
aComputedValue.SetTransformValue(
new nsCSSValueSharedList(result.forget()));
break;
}
default:
MOZ_ASSERT(false, "missing property implementation");
return false;
};
return true;
case eStyleAnimType_Coord: {
const nsStyleCoord& coord = *static_cast<const nsStyleCoord*>(
StyleDataAtOffset(styleStruct, ssOffset));
if (nsCSSProps::PropHasFlags(aProperty, CSS_PROPERTY_NUMBERS_ARE_PIXELS) &&
coord.GetUnit() == eStyleUnit_Coord) {
// For SVG properties where number means the same thing as length,
// we want to animate them the same way. Normalize both to number
// since it has more accuracy (float vs nscoord).
aComputedValue.SetFloatValue(nsPresContext::
AppUnitsToFloatCSSPixels(coord.GetCoordValue()));
return true;
}
return StyleCoordToValue(coord, aComputedValue);
}
case eStyleAnimType_Sides_Top:
case eStyleAnimType_Sides_Right:
case eStyleAnimType_Sides_Bottom:
case eStyleAnimType_Sides_Left: {
static_assert(
NS_SIDE_TOP == eStyleAnimType_Sides_Top -eStyleAnimType_Sides_Top &&
NS_SIDE_RIGHT == eStyleAnimType_Sides_Right -eStyleAnimType_Sides_Top &&
NS_SIDE_BOTTOM == eStyleAnimType_Sides_Bottom-eStyleAnimType_Sides_Top &&
NS_SIDE_LEFT == eStyleAnimType_Sides_Left -eStyleAnimType_Sides_Top,
"box side constants out of sync with animation side constants");
const nsStyleCoord &coord = static_cast<const nsStyleSides*>(
StyleDataAtOffset(styleStruct, ssOffset))->
Get(mozilla::css::Side(animType - eStyleAnimType_Sides_Top));
return StyleCoordToValue(coord, aComputedValue);
}
case eStyleAnimType_Corner_TopLeft:
case eStyleAnimType_Corner_TopRight:
case eStyleAnimType_Corner_BottomRight:
case eStyleAnimType_Corner_BottomLeft: {
static_assert(
NS_CORNER_TOP_LEFT == eStyleAnimType_Corner_TopLeft -
eStyleAnimType_Corner_TopLeft &&
NS_CORNER_TOP_RIGHT == eStyleAnimType_Corner_TopRight -
eStyleAnimType_Corner_TopLeft &&
NS_CORNER_BOTTOM_RIGHT == eStyleAnimType_Corner_BottomRight -
eStyleAnimType_Corner_TopLeft &&
NS_CORNER_BOTTOM_LEFT == eStyleAnimType_Corner_BottomLeft -
eStyleAnimType_Corner_TopLeft,
"box corner constants out of sync with animation corner constants");
const nsStyleCorners *corners = static_cast<const nsStyleCorners*>(
StyleDataAtOffset(styleStruct, ssOffset));
uint8_t fullCorner = animType - eStyleAnimType_Corner_TopLeft;
const nsStyleCoord &horiz =
corners->Get(NS_FULL_TO_HALF_CORNER(fullCorner, false));
const nsStyleCoord &vert =
corners->Get(NS_FULL_TO_HALF_CORNER(fullCorner, true));
nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
if (!StyleCoordToCSSValue(horiz, pair->mXValue) ||
!StyleCoordToCSSValue(vert, pair->mYValue)) {
return false;
}
aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
eUnit_CSSValuePair);
return true;
}
case eStyleAnimType_nscoord:
aComputedValue.SetCoordValue(*static_cast<const nscoord*>(
StyleDataAtOffset(styleStruct, ssOffset)));
return true;
case eStyleAnimType_EnumU8:
aComputedValue.SetIntValue(*static_cast<const uint8_t*>(
StyleDataAtOffset(styleStruct, ssOffset)), eUnit_Enumerated);
if (aProperty == eCSSProperty_visibility) {
aComputedValue.SetIntValue(aComputedValue.GetIntValue(),
eUnit_Visibility);
}
return true;
case eStyleAnimType_float:
aComputedValue.SetFloatValue(*static_cast<const float*>(
StyleDataAtOffset(styleStruct, ssOffset)));
if (aProperty == eCSSProperty_font_size_adjust &&
aComputedValue.GetFloatValue() == -1.0f) {
// In nsStyleFont, we set mFont.sizeAdjust to -1.0 to represent
// font-size-adjust: none. Here, we have to treat this as a keyword
// instead of a float value, to make sure we don't end up doing
// interpolation with it.
aComputedValue.SetNoneValue();
}
return true;
case eStyleAnimType_Color:
aComputedValue.SetColorValue(*static_cast<const nscolor*>(
StyleDataAtOffset(styleStruct, ssOffset)));
return true;
case eStyleAnimType_PaintServer: {
const nsStyleSVGPaint &paint = *static_cast<const nsStyleSVGPaint*>(
StyleDataAtOffset(styleStruct, ssOffset));
if (paint.mType == eStyleSVGPaintType_Color) {
aComputedValue.SetColorValue(paint.mPaint.mColor);
return true;
}
if (paint.mType == eStyleSVGPaintType_Server) {
if (!paint.mPaint.mPaintServer) {
NS_WARNING("Null paint server");
return false;
}
nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
RefPtr<nsStringBuffer> uriAsStringBuffer =
GetURIAsUtf16StringBuffer(paint.mPaint.mPaintServer);
NS_ENSURE_TRUE(!!uriAsStringBuffer, false);
nsIDocument* doc = aStyleContext->PresContext()->Document();
RefPtr<mozilla::css::URLValue> url =
new mozilla::css::URLValue(paint.mPaint.mPaintServer,
uriAsStringBuffer,
doc->GetDocumentURI(),
doc->NodePrincipal());
pair->mXValue.SetURLValue(url);
pair->mYValue.SetColorValue(paint.mFallbackColor);
aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
eUnit_CSSValuePair);
return true;
}
if (paint.mType == eStyleSVGPaintType_ContextFill ||
paint.mType == eStyleSVGPaintType_ContextStroke) {
nsAutoPtr<nsCSSValuePair> pair(new nsCSSValuePair);
pair->mXValue.SetIntValue(paint.mType == eStyleSVGPaintType_ContextFill ?
NS_COLOR_CONTEXT_FILL : NS_COLOR_CONTEXT_STROKE,
eCSSUnit_Enumerated);
pair->mYValue.SetColorValue(paint.mFallbackColor);
aComputedValue.SetAndAdoptCSSValuePairValue(pair.forget(),
eUnit_CSSValuePair);
return true;
}
MOZ_ASSERT(paint.mType == eStyleSVGPaintType_None,
"Unexpected SVG paint type");
aComputedValue.SetNoneValue();
return true;
}
case eStyleAnimType_Shadow: {
const nsCSSShadowArray *shadowArray =
*static_cast<const RefPtr<nsCSSShadowArray>*>(
StyleDataAtOffset(styleStruct, ssOffset));
if (!shadowArray) {
aComputedValue.SetAndAdoptCSSValueListValue(nullptr, eUnit_Shadow);
return true;
}
nsAutoPtr<nsCSSValueList> result;
nsCSSValueList **resultTail = getter_Transfers(result);
for (uint32_t i = 0, i_end = shadowArray->Length(); i < i_end; ++i) {
AppendCSSShadowValue(shadowArray->ShadowAt(i), resultTail);
}
aComputedValue.SetAndAdoptCSSValueListValue(result.forget(),
eUnit_Shadow);
return true;
}
case eStyleAnimType_None:
NS_NOTREACHED("shouldn't use on non-animatable properties");
}
return false;
}
gfxSize
StyleAnimationValue::GetScaleValue(const nsIFrame* aForFrame) const
{
MOZ_ASSERT(aForFrame);
MOZ_ASSERT(GetUnit() == StyleAnimationValue::eUnit_Transform);
nsCSSValueSharedList* list = GetCSSValueSharedListValue();
MOZ_ASSERT(list->mHead);
RuleNodeCacheConditions dontCare;
bool dontCareBool;
nsStyleTransformMatrix::TransformReferenceBox refBox(aForFrame);
Matrix4x4 transform = nsStyleTransformMatrix::ReadTransforms(
list->mHead,
aForFrame->StyleContext(),
aForFrame->PresContext(), dontCare, refBox,
aForFrame->PresContext()->AppUnitsPerDevPixel(),
&dontCareBool);
Matrix transform2d;
bool canDraw2D = transform.CanDraw2D(&transform2d);
if (!canDraw2D) {
return gfxSize();
}
return ThebesMatrix(transform2d).ScaleFactors(true);
}
StyleAnimationValue::StyleAnimationValue(int32_t aInt, Unit aUnit,
IntegerConstructorType)
{
NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type");
mUnit = aUnit;
mValue.mInt = aInt;
}
StyleAnimationValue::StyleAnimationValue(nscoord aLength, CoordConstructorType)
{
mUnit = eUnit_Coord;
mValue.mCoord = aLength;
}
StyleAnimationValue::StyleAnimationValue(float aPercent,
PercentConstructorType)
{
mUnit = eUnit_Percent;
mValue.mFloat = aPercent;
MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
}
StyleAnimationValue::StyleAnimationValue(float aFloat, FloatConstructorType)
{
mUnit = eUnit_Float;
mValue.mFloat = aFloat;
MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
}
StyleAnimationValue::StyleAnimationValue(nscolor aColor, ColorConstructorType)
{
mUnit = eUnit_Color;
mValue.mColor = aColor;
}
StyleAnimationValue&
StyleAnimationValue::operator=(const StyleAnimationValue& aOther)
{
if (this == &aOther) {
return *this;
}
FreeValue();
mUnit = aOther.mUnit;
switch (mUnit) {
case eUnit_Null:
case eUnit_Normal:
case eUnit_Auto:
case eUnit_None:
case eUnit_CurrentColor:
break;
case eUnit_Enumerated:
case eUnit_Visibility:
case eUnit_Integer:
mValue.mInt = aOther.mValue.mInt;
break;
case eUnit_Coord:
mValue.mCoord = aOther.mValue.mCoord;
break;
case eUnit_Percent:
case eUnit_Float:
mValue.mFloat = aOther.mValue.mFloat;
MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
break;
case eUnit_Color:
mValue.mColor = aOther.mValue.mColor;
break;
case eUnit_Calc:
case eUnit_ObjectPosition:
case eUnit_URL:
MOZ_ASSERT(IsCSSValueUnit(mUnit),
"This clause is for handling nsCSSValue-backed units");
MOZ_ASSERT(aOther.mValue.mCSSValue, "values may not be null");
mValue.mCSSValue = new nsCSSValue(*aOther.mValue.mCSSValue);
break;
case eUnit_CSSValuePair:
MOZ_ASSERT(aOther.mValue.mCSSValuePair,
"value pairs may not be null");
mValue.mCSSValuePair = new nsCSSValuePair(*aOther.mValue.mCSSValuePair);
break;
case eUnit_CSSValueTriplet:
MOZ_ASSERT(aOther.mValue.mCSSValueTriplet,
"value triplets may not be null");
mValue.mCSSValueTriplet = new nsCSSValueTriplet(*aOther.mValue.mCSSValueTriplet);
break;
case eUnit_CSSRect:
MOZ_ASSERT(aOther.mValue.mCSSRect, "rects may not be null");
mValue.mCSSRect = new nsCSSRect(*aOther.mValue.mCSSRect);
break;
case eUnit_Dasharray:
case eUnit_Shadow:
case eUnit_Filter:
case eUnit_BackgroundPosition:
MOZ_ASSERT(mUnit == eUnit_Shadow || mUnit == eUnit_Filter ||
aOther.mValue.mCSSValueList,
"value lists other than shadows and filters may not be null");
if (aOther.mValue.mCSSValueList) {
mValue.mCSSValueList = aOther.mValue.mCSSValueList->Clone();
} else {
mValue.mCSSValueList = nullptr;
}
break;
case eUnit_Shape:
MOZ_ASSERT(aOther.mValue.mCSSValueArray,
"value arrays may not be null");
mValue.mCSSValueArray = aOther.mValue.mCSSValueArray;
mValue.mCSSValueArray->AddRef();
break;
case eUnit_Transform:
mValue.mCSSValueSharedList = aOther.mValue.mCSSValueSharedList;
mValue.mCSSValueSharedList->AddRef();
break;
case eUnit_CSSValuePairList:
MOZ_ASSERT(aOther.mValue.mCSSValuePairList,
"value pair lists may not be null");
mValue.mCSSValuePairList = aOther.mValue.mCSSValuePairList->Clone();
break;
case eUnit_UnparsedString:
MOZ_ASSERT(aOther.mValue.mString, "expecting non-null string");
mValue.mString = aOther.mValue.mString;
mValue.mString->AddRef();
break;
}
return *this;
}
void
StyleAnimationValue::SetNormalValue()
{
FreeValue();
mUnit = eUnit_Normal;
}
void
StyleAnimationValue::SetAutoValue()
{
FreeValue();
mUnit = eUnit_Auto;
}
void
StyleAnimationValue::SetNoneValue()
{
FreeValue();
mUnit = eUnit_None;
}
void
StyleAnimationValue::SetIntValue(int32_t aInt, Unit aUnit)
{
NS_ASSERTION(IsIntUnit(aUnit), "unit must be of integer type");
FreeValue();
mUnit = aUnit;
mValue.mInt = aInt;
}
void
StyleAnimationValue::SetCoordValue(nscoord aLength)
{
FreeValue();
mUnit = eUnit_Coord;
mValue.mCoord = aLength;
}
void
StyleAnimationValue::SetPercentValue(float aPercent)
{
FreeValue();
mUnit = eUnit_Percent;
mValue.mFloat = aPercent;
MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
}
void
StyleAnimationValue::SetFloatValue(float aFloat)
{
FreeValue();
mUnit = eUnit_Float;
mValue.mFloat = aFloat;
MOZ_ASSERT(!mozilla::IsNaN(mValue.mFloat));
}
void
StyleAnimationValue::SetColorValue(nscolor aColor)
{
FreeValue();
mUnit = eUnit_Color;
mValue.mColor = aColor;
}
void
StyleAnimationValue::SetCurrentColorValue()
{
FreeValue();
mUnit = eUnit_CurrentColor;
}
void
StyleAnimationValue::SetUnparsedStringValue(const nsString& aString)
{
FreeValue();
mUnit = eUnit_UnparsedString;
mValue.mString = nsCSSValue::BufferFromString(aString).take();
}
void
StyleAnimationValue::SetAndAdoptCSSValueValue(nsCSSValue *aValue,
Unit aUnit)
{
FreeValue();
MOZ_ASSERT(IsCSSValueUnit(aUnit), "bad unit");
MOZ_ASSERT(aValue != nullptr, "values may not be null");
mUnit = aUnit;
mValue.mCSSValue = aValue; // take ownership
}
void
StyleAnimationValue::SetAndAdoptCSSValuePairValue(nsCSSValuePair *aValuePair,
Unit aUnit)
{
FreeValue();
MOZ_ASSERT(IsCSSValuePairUnit(aUnit), "bad unit");
MOZ_ASSERT(aValuePair != nullptr, "value pairs may not be null");
mUnit = aUnit;
mValue.mCSSValuePair = aValuePair; // take ownership
}
void
StyleAnimationValue::SetAndAdoptCSSValueTripletValue(
nsCSSValueTriplet *aValueTriplet, Unit aUnit)
{
FreeValue();
MOZ_ASSERT(IsCSSValueTripletUnit(aUnit), "bad unit");
MOZ_ASSERT(aValueTriplet != nullptr, "value pairs may not be null");
mUnit = aUnit;
mValue.mCSSValueTriplet = aValueTriplet; // take ownership
}
void
StyleAnimationValue::SetAndAdoptCSSRectValue(nsCSSRect *aRect, Unit aUnit)
{
FreeValue();
MOZ_ASSERT(IsCSSRectUnit(aUnit), "bad unit");
MOZ_ASSERT(aRect != nullptr, "value pairs may not be null");
mUnit = aUnit;
mValue.mCSSRect = aRect; // take ownership
}
void
StyleAnimationValue::SetAndAdoptCSSValueArrayValue(nsCSSValue::Array* aValue,
Unit aUnit)
{
FreeValue();
MOZ_ASSERT(IsCSSValueArrayUnit(aUnit), "bad unit");
MOZ_ASSERT(aValue != nullptr,
"not currently expecting any arrays to be null");
mUnit = aUnit;
mValue.mCSSValueArray = aValue; // take ownership
}
void
StyleAnimationValue::SetAndAdoptCSSValueListValue(nsCSSValueList *aValueList,
Unit aUnit)
{
FreeValue();
MOZ_ASSERT(IsCSSValueListUnit(aUnit), "bad unit");
MOZ_ASSERT(aUnit == eUnit_Shadow || aUnit == eUnit_Filter ||
aValueList != nullptr,
"value lists other than shadows and filters may not be null");
mUnit = aUnit;
mValue.mCSSValueList = aValueList; // take ownership
}
void
StyleAnimationValue::SetTransformValue(nsCSSValueSharedList* aList)
{
FreeValue();
mUnit = eUnit_Transform;
mValue.mCSSValueSharedList = aList;
mValue.mCSSValueSharedList->AddRef();
}
void
StyleAnimationValue::SetAndAdoptCSSValuePairListValue(
nsCSSValuePairList *aValuePairList)
{
FreeValue();
MOZ_ASSERT(aValuePairList, "may not be null");
mUnit = eUnit_CSSValuePairList;
mValue.mCSSValuePairList = aValuePairList; // take ownership
}
void
StyleAnimationValue::FreeValue()
{
if (IsCSSValueUnit(mUnit)) {
delete mValue.mCSSValue;
} else if (IsCSSValueListUnit(mUnit)) {
delete mValue.mCSSValueList;
} else if (IsCSSValueSharedListValue(mUnit)) {
mValue.mCSSValueSharedList->Release();
} else if (IsCSSValuePairUnit(mUnit)) {
delete mValue.mCSSValuePair;
} else if (IsCSSValueTripletUnit(mUnit)) {
delete mValue.mCSSValueTriplet;
} else if (IsCSSRectUnit(mUnit)) {
delete mValue.mCSSRect;
} else if (IsCSSValuePairListUnit(mUnit)) {
delete mValue.mCSSValuePairList;
} else if (IsStringUnit(mUnit)) {
MOZ_ASSERT(mValue.mString, "expecting non-null string");
mValue.mString->Release();
}
}
bool
StyleAnimationValue::operator==(const StyleAnimationValue& aOther) const
{
if (mUnit != aOther.mUnit) {
return false;
}
switch (mUnit) {
case eUnit_Null:
case eUnit_Normal:
case eUnit_Auto:
case eUnit_None:
case eUnit_CurrentColor:
return true;
case eUnit_Enumerated:
case eUnit_Visibility:
case eUnit_Integer:
return mValue.mInt == aOther.mValue.mInt;
case eUnit_Coord:
return mValue.mCoord == aOther.mValue.mCoord;
case eUnit_Percent:
case eUnit_Float:
return mValue.mFloat == aOther.mValue.mFloat;
case eUnit_Color:
return mValue.mColor == aOther.mValue.mColor;
case eUnit_Calc:
case eUnit_ObjectPosition:
case eUnit_URL:
MOZ_ASSERT(IsCSSValueUnit(mUnit),
"This clause is for handling nsCSSValue-backed units");
return *mValue.mCSSValue == *aOther.mValue.mCSSValue;
case eUnit_CSSValuePair:
return *mValue.mCSSValuePair == *aOther.mValue.mCSSValuePair;
case eUnit_CSSValueTriplet:
return *mValue.mCSSValueTriplet == *aOther.mValue.mCSSValueTriplet;
case eUnit_CSSRect:
return *mValue.mCSSRect == *aOther.mValue.mCSSRect;
case eUnit_Dasharray:
case eUnit_Shadow:
case eUnit_Filter:
case eUnit_BackgroundPosition:
return nsCSSValueList::Equal(mValue.mCSSValueList,
aOther.mValue.mCSSValueList);
case eUnit_Shape:
return *mValue.mCSSValueArray == *aOther.mValue.mCSSValueArray;
case eUnit_Transform:
return *mValue.mCSSValueSharedList == *aOther.mValue.mCSSValueSharedList;
case eUnit_CSSValuePairList:
return nsCSSValuePairList::Equal(mValue.mCSSValuePairList,
aOther.mValue.mCSSValuePairList);
case eUnit_UnparsedString:
return (NS_strcmp(GetStringBufferValue(),
aOther.GetStringBufferValue()) == 0);
}
NS_NOTREACHED("incomplete case");
return false;
}