Files
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

2552 lines
92 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsMathMLChar.h"
#include "gfxTextRun.h"
#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/unused.h"
#include "nsCOMPtr.h"
#include "nsDeviceContext.h"
#include "nsFontMetrics.h"
#include "nsIFrame.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h"
#include "nsStyleContext.h"
#include "nsUnicharUtils.h"
#include "nsRenderingContext.h"
#include "mozilla/Preferences.h"
#include "nsIPersistentProperties2.h"
#include "nsIObserverService.h"
#include "nsIObserver.h"
#include "nsNetUtil.h"
#include "nsContentUtils.h"
#include "mozilla/LookAndFeel.h"
#include "nsCSSRendering.h"
#include "mozilla/Snprintf.h"
#include "nsDisplayList.h"
#include "nsMathMLOperators.h"
#include <algorithm>
#include "gfxMathTable.h"
#include "nsUnicodeScriptCodes.h"
using namespace mozilla;
using namespace mozilla::gfx;
using namespace mozilla::image;
//#define NOISY_SEARCH 1
// BUG 848725 Drawing failure with stretchy horizontal parenthesis when no fonts
// are installed. "kMaxScaleFactor" is required to limit the scale for the
// vertical and horizontal stretchy operators.
static const float kMaxScaleFactor = 20.0;
static const float kLargeOpFactor = float(M_SQRT2);
static const float kIntegralFactor = 2.0;
static void
NormalizeDefaultFont(nsFont& aFont, float aFontSizeInflation)
{
if (aFont.fontlist.GetDefaultFontType() != eFamily_none) {
aFont.fontlist.Append(FontFamilyName(aFont.fontlist.GetDefaultFontType()));
aFont.fontlist.SetDefaultFontType(eFamily_none);
}
aFont.size = NSToCoordRound(aFont.size * aFontSizeInflation);
}
// -----------------------------------------------------------------------------
static const nsGlyphCode kNullGlyph = {{{0, 0}}, 0};
// -----------------------------------------------------------------------------
// nsGlyphTable is a class that provides an interface for accessing glyphs
// of stretchy chars. It acts like a table that stores the variants of bigger
// sizes (if any) and the partial glyphs needed to build extensible symbols.
//
// Bigger sizes (if any) of the char can then be retrieved with BigOf(...).
// Partial glyphs can be retrieved with ElementAt(...).
//
// A table consists of "nsGlyphCode"s which are viewed either as Unicode
// points (for nsPropertiesTable) or as direct glyph indices (for
// nsOpenTypeTable)
// -----------------------------------------------------------------------------
class nsGlyphTable {
public:
virtual ~nsGlyphTable() {}
virtual const FontFamilyName&
FontNameFor(const nsGlyphCode& aGlyphCode) const = 0;
// Getters for the parts
virtual nsGlyphCode ElementAt(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical,
uint32_t aPosition) = 0;
virtual nsGlyphCode BigOf(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical,
uint32_t aSize) = 0;
// True if this table contains parts to render this char
virtual bool HasPartsOf(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical) = 0;
virtual UniquePtr<gfxTextRun>
MakeTextRun(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
const nsGlyphCode& aGlyph) = 0;
protected:
nsGlyphTable() : mCharCache(0) {}
// For speedy re-use, we always cache the last data used in the table.
// mCharCache is the Unicode point of the last char that was queried in this
// table.
char16_t mCharCache;
};
// An instance of nsPropertiesTable is associated with one primary font. Extra
// glyphs can be taken in other additional fonts when stretching certain
// characters.
// These supplementary fonts are referred to as "external" fonts to the table.
// General format of MathFont Property Files from which glyph data are
// retrieved:
// -----------------------------------------------------------------------------
// Each font should have its set of glyph data. For example, the glyph data for
// the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
// and "mathfontMTExtra.properties", respectively. The mathfont property file
// is a set of all the stretchy MathML characters that can be rendered with that
// font using larger and/or partial glyphs. The entry of each stretchy character
// in the mathfont property file gives, in that order, the 4 partial glyphs:
// Top (or Left), Middle, Bottom (or Right), Glue; and the variants of bigger
// sizes (if any).
// A position that is not relevant to a particular character is indicated there
// with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
// -----------------------------------------------------------------------------
#define NS_TABLE_STATE_ERROR -1
#define NS_TABLE_STATE_EMPTY 0
#define NS_TABLE_STATE_READY 1
// helper to trim off comments from data in a MathFont Property File
static void
Clean(nsString& aValue)
{
// chop the trailing # comment portion if any ...
int32_t comment = aValue.RFindChar('#');
if (comment > 0) aValue.Truncate(comment);
aValue.CompressWhitespace();
}
// helper to load a MathFont Property File
static nsresult
LoadProperties(const nsString& aName,
nsCOMPtr<nsIPersistentProperties>& aProperties)
{
nsAutoString uriStr;
uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
uriStr.Append(aName);
uriStr.StripWhitespace(); // that may come from aName
uriStr.AppendLiteral(".properties");
return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties),
NS_ConvertUTF16toUTF8(uriStr));
}
class nsPropertiesTable final : public nsGlyphTable {
public:
explicit nsPropertiesTable(const nsString& aPrimaryFontName)
: mState(NS_TABLE_STATE_EMPTY)
{
MOZ_COUNT_CTOR(nsPropertiesTable);
mGlyphCodeFonts.AppendElement(FontFamilyName(aPrimaryFontName, eUnquotedName));
}
~nsPropertiesTable()
{
MOZ_COUNT_DTOR(nsPropertiesTable);
}
const FontFamilyName& PrimaryFontName() const
{
return mGlyphCodeFonts[0];
}
const FontFamilyName&
FontNameFor(const nsGlyphCode& aGlyphCode) const override
{
NS_ASSERTION(!aGlyphCode.IsGlyphID(),
"nsPropertiesTable can only access glyphs by code point");
return mGlyphCodeFonts[aGlyphCode.font];
}
virtual nsGlyphCode ElementAt(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical,
uint32_t aPosition) override;
virtual nsGlyphCode BigOf(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical,
uint32_t aSize) override
{
return ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
aChar, aVertical, 4 + aSize);
}
virtual bool HasPartsOf(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical) override
{
return (ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
aChar, aVertical, 0).Exists() ||
ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
aChar, aVertical, 1).Exists() ||
ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
aChar, aVertical, 2).Exists() ||
ElementAt(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup,
aChar, aVertical, 3).Exists());
}
virtual UniquePtr<gfxTextRun>
MakeTextRun(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
const nsGlyphCode& aGlyph) override;
private:
// mGlyphCodeFonts[0] is the primary font associated to this table. The
// others are possible "external" fonts for glyphs not in the primary font
// but which are needed to stretch certain characters in the table
nsTArray<FontFamilyName> mGlyphCodeFonts;
// Tri-state variable for error/empty/ready
int32_t mState;
// The set of glyph data in this table, as provided by the MathFont Property
// File
nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
// mGlyphCache is a buffer containing the glyph data associated with
// mCharCache.
// For a property line 'key = value' in the MathFont Property File,
// mCharCache will retain the 'key' -- which is a Unicode point, while
// mGlyphCache will retain the 'value', which is a consecutive list of
// nsGlyphCodes, i.e., the pairs of 'code@font' needed by the char -- in
// which 'code@0' can be specified
// without the optional '@0'. However, to ease subsequent processing,
// mGlyphCache excludes the '@' symbol and explicitly inserts all optional '0'
// that indicates the primary font identifier. Specifically therefore, the
// k-th glyph is characterized by :
// 1) mGlyphCache[3*k],mGlyphCache[3*k+1] : its Unicode point
// 2) mGlyphCache[3*k+2] : the numeric identifier of the font where it comes
// from.
// A font identifier of '0' means the default primary font associated to this
// table. Other digits map to the "external" fonts that may have been
// specified in the MathFont Property File.
nsString mGlyphCache;
};
/* virtual */
nsGlyphCode
nsPropertiesTable::ElementAt(DrawTarget* /* aDrawTarget */,
int32_t /* aAppUnitsPerDevPixel */,
gfxFontGroup* /* aFontGroup */,
char16_t aChar,
bool /* aVertical */,
uint32_t aPosition)
{
if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
// Load glyph properties if this is the first time we have been here
if (mState == NS_TABLE_STATE_EMPTY) {
nsAutoString primaryFontName;
mGlyphCodeFonts[0].AppendToString(primaryFontName);
nsresult rv = LoadProperties(primaryFontName, mGlyphProperties);
#ifdef DEBUG
nsAutoCString uriStr;
uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
LossyAppendUTF16toASCII(primaryFontName, uriStr);
uriStr.StripWhitespace(); // that may come from mGlyphCodeFonts
uriStr.AppendLiteral(".properties");
printf("Loading %s ... %s\n",
uriStr.get(),
(NS_FAILED(rv)) ? "Failed" : "Done");
#endif
if (NS_FAILED(rv)) {
mState = NS_TABLE_STATE_ERROR; // never waste time with this table again
return kNullGlyph;
}
mState = NS_TABLE_STATE_READY;
// see if there are external fonts needed for certain chars in this table
nsAutoCString key;
nsAutoString value;
for (int32_t i = 1; ; i++) {
key.AssignLiteral("external.");
key.AppendInt(i, 10);
rv = mGlyphProperties->GetStringProperty(key, value);
if (NS_FAILED(rv)) break;
Clean(value);
mGlyphCodeFonts.AppendElement(FontFamilyName(value, eUnquotedName)); // i.e., mGlyphCodeFonts[i] holds this font name
}
}
// Update our cache if it is not associated to this character
if (mCharCache != aChar) {
// The key in the property file is interpreted as ASCII and kept
// as such ...
char key[10]; snprintf_literal(key, "\\u%04X", aChar);
nsAutoString value;
nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key),
value);
if (NS_FAILED(rv)) return kNullGlyph;
Clean(value);
// See if this char uses external fonts; e.g., if the 2nd glyph is taken
// from the external font '1', the property line looks like
// \uNNNN = \uNNNN\uNNNN@1\uNNNN.
// This is where mGlyphCache is pre-processed to explicitly store all glyph
// codes as combined pairs of 'code@font', excluding the '@' separator. This
// means that mGlyphCache[3*k],mGlyphCache[3*k+1] will later be rendered
// with mGlyphCodeFonts[mGlyphCache[3*k+2]]
// Note: font identifier is internally an ASCII digit to avoid the null
// char issue
nsAutoString buffer;
int32_t length = value.Length();
int32_t i = 0; // index in value
while (i < length) {
char16_t code = value[i];
++i;
buffer.Append(code);
// Read the next word if we have a non-BMP character.
if (i < length && NS_IS_HIGH_SURROGATE(code)) {
code = value[i];
++i;
} else {
code = char16_t('\0');
}
buffer.Append(code);
// See if an external font is needed for the code point.
// Limit of 9 external fonts
char16_t font = 0;
if (i+1 < length && value[i] == char16_t('@') &&
value[i+1] >= char16_t('0') && value[i+1] <= char16_t('9')) {
++i;
font = value[i] - '0';
++i;
if (font >= mGlyphCodeFonts.Length()) {
NS_ERROR("Nonexistent font referenced in glyph table");
return kNullGlyph;
}
// The char cannot be handled if this font is not installed
if (!mGlyphCodeFonts[font].mName.Length()) {
return kNullGlyph;
}
}
buffer.Append(font);
}
// update our cache with the new settings
mGlyphCache.Assign(buffer);
mCharCache = aChar;
}
// 3* is to account for the code@font pairs
uint32_t index = 3*aPosition;
if (index+2 >= mGlyphCache.Length()) return kNullGlyph;
nsGlyphCode ch;
ch.code[0] = mGlyphCache.CharAt(index);
ch.code[1] = mGlyphCache.CharAt(index + 1);
ch.font = mGlyphCache.CharAt(index + 2);
return ch.code[0] == char16_t(0xFFFD) ? kNullGlyph : ch;
}
/* virtual */
UniquePtr<gfxTextRun>
nsPropertiesTable::MakeTextRun(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
const nsGlyphCode& aGlyph)
{
NS_ASSERTION(!aGlyph.IsGlyphID(),
"nsPropertiesTable can only access glyphs by code point");
return aFontGroup->
MakeTextRun(aGlyph.code, aGlyph.Length(), aDrawTarget,
aAppUnitsPerDevPixel, 0, nullptr);
}
// An instance of nsOpenTypeTable is associated with one gfxFontEntry that
// corresponds to an Open Type font with a MATH table. All the glyphs come from
// the same font and the calls to access size variants and parts are directly
// forwarded to the gfx code.
class nsOpenTypeTable final : public nsGlyphTable {
public:
~nsOpenTypeTable()
{
MOZ_COUNT_DTOR(nsOpenTypeTable);
}
virtual nsGlyphCode ElementAt(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical,
uint32_t aPosition) override;
virtual nsGlyphCode BigOf(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical,
uint32_t aSize) override;
virtual bool HasPartsOf(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical) override;
const FontFamilyName&
FontNameFor(const nsGlyphCode& aGlyphCode) const override {
NS_ASSERTION(aGlyphCode.IsGlyphID(),
"nsOpenTypeTable can only access glyphs by id");
return mFontFamilyName;
}
virtual UniquePtr<gfxTextRun>
MakeTextRun(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
const nsGlyphCode& aGlyph) override;
// This returns a new OpenTypeTable instance to give access to OpenType MATH
// table or nullptr if the font does not have such table. Ownership is passed
// to the caller.
static nsOpenTypeTable* Create(gfxFont* aFont)
{
if (!aFont->GetFontEntry()->TryGetMathTable()) {
return nullptr;
}
return new nsOpenTypeTable(aFont->GetFontEntry());
}
private:
RefPtr<gfxFontEntry> mFontEntry;
FontFamilyName mFontFamilyName;
uint32_t mGlyphID;
explicit nsOpenTypeTable(gfxFontEntry* aFontEntry)
: mFontEntry(aFontEntry),
mFontFamilyName(aFontEntry->FamilyName(), eUnquotedName) {
MOZ_COUNT_CTOR(nsOpenTypeTable);
}
void UpdateCache(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar);
};
void
nsOpenTypeTable::UpdateCache(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar)
{
if (mCharCache != aChar) {
UniquePtr<gfxTextRun> textRun = aFontGroup->
MakeTextRun(&aChar, 1, aDrawTarget, aAppUnitsPerDevPixel, 0, nullptr);
const gfxTextRun::CompressedGlyph& data = textRun->GetCharacterGlyphs()[0];
if (data.IsSimpleGlyph()) {
mGlyphID = data.GetSimpleGlyph();
} else if (data.GetGlyphCount() == 1) {
mGlyphID = textRun->GetDetailedGlyphs(0)->mGlyphID;
} else {
mGlyphID = 0;
}
mCharCache = aChar;
}
}
/* virtual */
nsGlyphCode
nsOpenTypeTable::ElementAt(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical,
uint32_t aPosition)
{
UpdateCache(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup, aChar);
uint32_t parts[4];
if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
return kNullGlyph;
}
uint32_t glyphID = parts[aPosition];
if (!glyphID) {
return kNullGlyph;
}
nsGlyphCode glyph;
glyph.glyphID = glyphID;
glyph.font = -1;
return glyph;
}
/* virtual */
nsGlyphCode
nsOpenTypeTable::BigOf(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical,
uint32_t aSize)
{
UpdateCache(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup, aChar);
uint32_t glyphID =
mFontEntry->GetMathVariantsSize(mGlyphID, aVertical, aSize);
if (!glyphID) {
return kNullGlyph;
}
nsGlyphCode glyph;
glyph.glyphID = glyphID;
glyph.font = -1;
return glyph;
}
/* virtual */
bool
nsOpenTypeTable::HasPartsOf(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
char16_t aChar,
bool aVertical)
{
UpdateCache(aDrawTarget, aAppUnitsPerDevPixel, aFontGroup, aChar);
uint32_t parts[4];
if (!mFontEntry->GetMathVariantsParts(mGlyphID, aVertical, parts)) {
return false;
}
return parts[0] || parts[1] || parts[2] || parts[3];
}
/* virtual */
UniquePtr<gfxTextRun>
nsOpenTypeTable::MakeTextRun(DrawTarget* aDrawTarget,
int32_t aAppUnitsPerDevPixel,
gfxFontGroup* aFontGroup,
const nsGlyphCode& aGlyph)
{
NS_ASSERTION(aGlyph.IsGlyphID(),
"nsOpenTypeTable can only access glyphs by id");
gfxTextRunFactory::Parameters params = {
aDrawTarget, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
};
UniquePtr<gfxTextRun> textRun =
gfxTextRun::Create(&params, 1, aFontGroup, 0);
textRun->AddGlyphRun(aFontGroup->GetFirstValidFont(),
gfxTextRange::kFontGroup, 0,
false, gfxTextRunFactory::TEXT_ORIENT_HORIZONTAL);
// We don't care about CSS writing mode here;
// math runs are assumed to be horizontal.
gfxTextRun::DetailedGlyph detailedGlyph;
detailedGlyph.mGlyphID = aGlyph.glyphID;
detailedGlyph.mAdvance =
NSToCoordRound(aAppUnitsPerDevPixel *
aFontGroup->GetFirstValidFont()->
GetGlyphHAdvance(aDrawTarget, aGlyph.glyphID));
detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
gfxShapedText::CompressedGlyph g;
g.SetComplex(true, true, 1);
textRun->SetGlyphs(0, g, &detailedGlyph);
return textRun;
}
// -----------------------------------------------------------------------------
// This is the list of all the applicable glyph tables.
// We will maintain a single global instance that will only reveal those
// glyph tables that are associated to fonts currently installed on the
// user' system. The class is an XPCOM shutdown observer to allow us to
// free its allocated data at shutdown
class nsGlyphTableList final : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
nsPropertiesTable mUnicodeTable;
nsGlyphTableList()
: mUnicodeTable(NS_LITERAL_STRING("Unicode"))
{
MOZ_COUNT_CTOR(nsGlyphTableList);
}
nsresult Initialize();
nsresult Finalize();
// Add a glyph table in the list, return the new table that was added
nsGlyphTable*
AddGlyphTable(const nsString& aPrimaryFontName);
// Find the glyph table in the list corresponding to the given font family.
nsGlyphTable*
GetGlyphTableFor(const nsAString& aFamily);
private:
~nsGlyphTableList()
{
MOZ_COUNT_DTOR(nsGlyphTableList);
}
nsPropertiesTable* PropertiesTableAt(int32_t aIndex) {
return &mPropertiesTableList.ElementAt(aIndex);
}
int32_t PropertiesTableCount() {
return mPropertiesTableList.Length();
}
// List of glyph tables;
nsTArray<nsPropertiesTable> mPropertiesTableList;
};
NS_IMPL_ISUPPORTS(nsGlyphTableList, nsIObserver)
// -----------------------------------------------------------------------------
// Here is the global list of applicable glyph tables that we will be using
static nsGlyphTableList* gGlyphTableList = nullptr;
static bool gGlyphTableInitialized = false;
// XPCOM shutdown observer
NS_IMETHODIMP
nsGlyphTableList::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* someData)
{
Finalize();
return NS_OK;
}
// Add an observer to XPCOM shutdown so that we can free our data at shutdown
nsresult
nsGlyphTableList::Initialize()
{
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs)
return NS_ERROR_FAILURE;
nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
// Remove our observer and free the memory that were allocated for us
nsresult
nsGlyphTableList::Finalize()
{
// Remove our observer from the observer service
nsresult rv = NS_OK;
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs)
rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
else
rv = NS_ERROR_FAILURE;
gGlyphTableInitialized = false;
// our oneself will be destroyed when our |Release| is called by the observer
NS_IF_RELEASE(gGlyphTableList);
return rv;
}
nsGlyphTable*
nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
{
// See if there is already a special table for this family.
nsGlyphTable* glyphTable = GetGlyphTableFor(aPrimaryFontName);
if (glyphTable != &mUnicodeTable)
return glyphTable;
// allocate a table
glyphTable = mPropertiesTableList.AppendElement(aPrimaryFontName);
return glyphTable;
}
nsGlyphTable*
nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily)
{
for (int32_t i = 0; i < PropertiesTableCount(); i++) {
nsPropertiesTable* glyphTable = PropertiesTableAt(i);
const FontFamilyName& primaryFontName = glyphTable->PrimaryFontName();
nsAutoString primaryFontNameStr;
primaryFontName.AppendToString(primaryFontNameStr);
// TODO: would be nice to consider StripWhitespace and other aliasing
if (primaryFontNameStr.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
return glyphTable;
}
}
// Fall back to default Unicode table
return &mUnicodeTable;
}
// -----------------------------------------------------------------------------
static nsresult
InitCharGlobals()
{
NS_ASSERTION(!gGlyphTableInitialized, "Error -- already initialized");
gGlyphTableInitialized = true;
// Allocate the placeholders for the preferred parts and variants
nsresult rv = NS_ERROR_OUT_OF_MEMORY;
RefPtr<nsGlyphTableList> glyphTableList = new nsGlyphTableList();
if (glyphTableList) {
rv = glyphTableList->Initialize();
}
if (NS_FAILED(rv)) {
return rv;
}
// The gGlyphTableList has been successfully registered as a shutdown
// observer and will be deleted at shutdown. We now add some private
// per font-family tables for stretchy operators, in order of preference.
// Do not include the Unicode table in this list.
if (!glyphTableList->AddGlyphTable(NS_LITERAL_STRING("STIXGeneral"))
#ifdef XP_WIN
|| !glyphTableList->AddGlyphTable(NS_LITERAL_STRING("Symbol"))
#endif
) {
rv = NS_ERROR_OUT_OF_MEMORY;
}
glyphTableList.forget(&gGlyphTableList);
return rv;
}
// -----------------------------------------------------------------------------
// And now the implementation of nsMathMLChar
nsMathMLChar::~nsMathMLChar()
{
MOZ_COUNT_DTOR(nsMathMLChar);
mStyleContext->Release();
}
nsStyleContext*
nsMathMLChar::GetStyleContext() const
{
NS_ASSERTION(mStyleContext, "chars should always have style context");
return mStyleContext;
}
void
nsMathMLChar::SetStyleContext(nsStyleContext* aStyleContext)
{
NS_PRECONDITION(aStyleContext, "null ptr");
if (aStyleContext != mStyleContext) {
if (mStyleContext)
mStyleContext->Release();
if (aStyleContext) {
mStyleContext = aStyleContext;
aStyleContext->AddRef();
}
}
}
void
nsMathMLChar::SetData(nsString& aData)
{
if (!gGlyphTableInitialized) {
InitCharGlobals();
}
mData = aData;
// some assumptions until proven otherwise
// note that mGlyph is not initialized
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
mBoundingMetrics = nsBoundingMetrics();
// check if stretching is applicable ...
if (gGlyphTableList && (1 == mData.Length())) {
mDirection = nsMathMLOperators::GetStretchyDirection(mData);
// default tentative table (not the one that is necessarily going
// to be used)
}
}
// -----------------------------------------------------------------------------
/*
The Stretch:
@param aContainerSize - suggested size for the stretched char
@param aDesiredStretchSize - OUT parameter. The desired size
after stretching. If no stretching is done, the output will
simply give the base size.
How it works?
Summary:-
The Stretch() method first looks for a glyph of appropriate
size; If a glyph is found, it is cached by this object and
its size is returned in aDesiredStretchSize. The cached
glyph will then be used at the painting stage.
If no glyph of appropriate size is found, a search is made
to see if the char can be built by parts.
Details:-
A character gets stretched through the following pipeline :
1) If the base size of the char is sufficient to cover the
container' size, we use that. If not, it will still be
used as a fallback if the other stages in the pipeline fail.
Issues :
a) The base size, the parts and the variants of a char can
be in different fonts. For eg., the base size for '(' should
come from a normal ascii font if CMEX10 is used, since CMEX10
only contains the stretched versions. Hence, there are two
style contexts in use throughout the process. The leaf style
context of the char holds fonts with which to try to stretch
the char. The parent style context of the char contains fonts
for normal rendering. So the parent context is the one used
to get the initial base size at the start of the pipeline.
b) For operators that can be largeop's in display mode,
we will skip the base size even if it fits, so that
the next stage in the pipeline is given a chance to find
a largeop variant. If the next stage fails, we fallback
to the base size.
2) We search for the first larger variant of the char that fits the
container' size. We first search for larger variants using the glyph
table corresponding to the first existing font specified in the list of
stretchy fonts held by the leaf style context (from -moz-math-stretchy in
mathml.css). Generic fonts are resolved by the preference
"font.mathfont-family".
Issues :
a) the largeop and display settings determine the starting
size when we do the above search, regardless of whether
smaller variants already fit the container' size.
b) if it is a largeopOnly request (i.e., a displaystyle operator
with largeop=true and stretchy=false), we break after finding
the first starting variant, regardless of whether that
variant fits the container's size.
3) If a variant of appropriate size wasn't found, we see if the char
can be built by parts using the same glyph table.
Issue:
There are chars that have no middle and glue glyphs. For
such chars, the parts need to be joined using the rule.
By convention (TeXbook p.225), the descent of the parts is
zero while their ascent gives the thickness of the rule that
should be used to join them.
4) If a match was not found in that glyph table, repeat from 2 to search the
ordered list of stretchy fonts for the first font with a glyph table that
provides a fit to the container size. If no fit is found, the closest fit
is used.
Of note:
When the pipeline completes successfully, the desired size of the
stretched char can actually be slightly larger or smaller than
aContainerSize. But it is the responsibility of the caller to
account for the spacing when setting aContainerSize, and to leave
any extra margin when placing the stretched char.
*/
// -----------------------------------------------------------------------------
// plain TeX settings (TeXbook p.152)
#define NS_MATHML_DELIMITER_FACTOR 0.901f
#define NS_MATHML_DELIMITER_SHORTFALL_POINTS 5.0f
static bool
IsSizeOK(nscoord a, nscoord b, uint32_t aHint)
{
// Normal: True if 'a' is around +/-10% of the target 'b' (10% is
// 1-DelimiterFactor). This often gives a chance to the base size to
// win, especially in the context of <mfenced> without tall elements
// or in sloppy markups without protective <mrow></mrow>
bool isNormal =
(aHint & NS_STRETCH_NORMAL) &&
Abs<float>(a - b) < (1.0f - NS_MATHML_DELIMITER_FACTOR) * float(b);
// Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
// as documented in The TeXbook, Ch.17, p.152.
// i.e. within 10% and within 5pt
bool isNearer = false;
if (aHint & (NS_STRETCH_NEARER | NS_STRETCH_LARGEOP)) {
float c = std::max(float(b) * NS_MATHML_DELIMITER_FACTOR,
float(b) - nsPresContext::
CSSPointsToAppUnits(NS_MATHML_DELIMITER_SHORTFALL_POINTS));
isNearer = Abs<float>(b - a) <= float(b) - c;
}
// Smaller: Mainly for transitory use, to compare two candidate
// choices
bool isSmaller =
(aHint & NS_STRETCH_SMALLER) &&
float(a) >= NS_MATHML_DELIMITER_FACTOR * float(b) &&
a <= b;
// Larger: Critical to the sqrt code to ensure that the radical
// size is tall enough
bool isLarger =
(aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP)) &&
a >= b;
return (isNormal || isSmaller || isNearer || isLarger);
}
static bool
IsSizeBetter(nscoord a, nscoord olda, nscoord b, uint32_t aHint)
{
if (0 == olda)
return true;
if (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
return (a >= olda) ? (olda < b) : (a >= b);
if (aHint & NS_STRETCH_SMALLER)
return (a <= olda) ? (olda > b) : (a <= b);
// XXXkt prob want log scale here i.e. 1.5 is closer to 1 than 0.5
return Abs(a - b) < Abs(olda - b);
}
// We want to place the glyphs even when they don't fit at their
// full extent, i.e., we may clip to tolerate a small amount of
// overlap between the parts. This is important to cater for fonts
// with long glues.
static nscoord
ComputeSizeFromParts(nsPresContext* aPresContext,
nsGlyphCode* aGlyphs,
nscoord* aSizes,
nscoord aTargetSize)
{
enum {first, middle, last, glue};
// Add the parts that cannot be left out.
nscoord sum = 0;
for (int32_t i = first; i <= last; i++) {
sum += aSizes[i];
}
// Determine how much is used in joins
nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
int32_t joins = aGlyphs[middle] == aGlyphs[glue] ? 1 : 2;
// Pick a maximum size using a maximum number of glue glyphs that we are
// prepared to draw for one character.
const int32_t maxGlyphs = 1000;
// This also takes into account the fact that, if the glue has no size,
// then the character can't be lengthened.
nscoord maxSize = sum - 2 * joins * oneDevPixel + maxGlyphs * aSizes[glue];
if (maxSize < aTargetSize)
return maxSize; // settle with the maximum size
// Get the minimum allowable size using some flex.
nscoord minSize = NSToCoordRound(NS_MATHML_DELIMITER_FACTOR * sum);
if (minSize > aTargetSize)
return minSize; // settle with the minimum size
// Fill-up the target area
return aTargetSize;
}
// Update the font if there is a family change and returns the font group.
bool
nsMathMLChar::SetFontFamily(nsPresContext* aPresContext,
const nsGlyphTable* aGlyphTable,
const nsGlyphCode& aGlyphCode,
const FontFamilyList& aDefaultFamilyList,
nsFont& aFont,
RefPtr<gfxFontGroup>* aFontGroup)
{
FontFamilyList glyphCodeFont;
if (aGlyphCode.font) {
glyphCodeFont.Append(aGlyphTable->FontNameFor(aGlyphCode));
}
const FontFamilyList& familyList =
aGlyphCode.font ? glyphCodeFont : aDefaultFamilyList;
if (!*aFontGroup || !(aFont.fontlist == familyList)) {
nsFont font = aFont;
font.fontlist = familyList;
const nsStyleFont* styleFont = mStyleContext->StyleFont();
nsFontMetrics::Params params;
params.language = styleFont->mLanguage;
params.explicitLanguage = styleFont->mExplicitLanguage;
params.userFontSet = aPresContext->GetUserFontSet();
params.textPerf = aPresContext->GetTextPerfMetrics();
RefPtr<nsFontMetrics> fm =
aPresContext->DeviceContext()->GetMetricsFor(font, params);
// Set the font if it is an unicode table
// or if the same family name has been found
gfxFont *firstFont = fm->GetThebesFontGroup()->GetFirstValidFont();
FontFamilyList firstFontList;
firstFontList.Append(
FontFamilyName(firstFont->GetFontEntry()->FamilyName(), eUnquotedName));
if (aGlyphTable == &gGlyphTableList->mUnicodeTable ||
firstFontList == familyList) {
aFont.fontlist = familyList;
*aFontGroup = fm->GetThebesFontGroup();
} else {
return false; // We did not set the font
}
}
return true;
}
static nsBoundingMetrics
MeasureTextRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun)
{
gfxTextRun::Metrics metrics =
aTextRun->MeasureText(gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS, aDrawTarget);
nsBoundingMetrics bm;
bm.leftBearing = NSToCoordFloor(metrics.mBoundingBox.X());
bm.rightBearing = NSToCoordCeil(metrics.mBoundingBox.XMost());
bm.ascent = NSToCoordCeil(-metrics.mBoundingBox.Y());
bm.descent = NSToCoordCeil(metrics.mBoundingBox.YMost());
bm.width = NSToCoordRound(metrics.mAdvanceWidth);
return bm;
}
class nsMathMLChar::StretchEnumContext {
public:
StretchEnumContext(nsMathMLChar* aChar,
nsPresContext* aPresContext,
DrawTarget* aDrawTarget,
float aFontSizeInflation,
nsStretchDirection aStretchDirection,
nscoord aTargetSize,
uint32_t aStretchHint,
nsBoundingMetrics& aStretchedMetrics,
const FontFamilyList& aFamilyList,
bool& aGlyphFound)
: mChar(aChar),
mPresContext(aPresContext),
mDrawTarget(aDrawTarget),
mFontSizeInflation(aFontSizeInflation),
mDirection(aStretchDirection),
mTargetSize(aTargetSize),
mStretchHint(aStretchHint),
mBoundingMetrics(aStretchedMetrics),
mFamilyList(aFamilyList),
mTryVariants(true),
mTryParts(true),
mGlyphFound(aGlyphFound) {}
static bool
EnumCallback(const FontFamilyName& aFamily, bool aGeneric, void *aData);
private:
bool TryVariants(nsGlyphTable* aGlyphTable,
RefPtr<gfxFontGroup>* aFontGroup,
const FontFamilyList& aFamilyList);
bool TryParts(nsGlyphTable* aGlyphTable,
RefPtr<gfxFontGroup>* aFontGroup,
const FontFamilyList& aFamilyList);
nsMathMLChar* mChar;
nsPresContext* mPresContext;
DrawTarget* mDrawTarget;
float mFontSizeInflation;
const nsStretchDirection mDirection;
const nscoord mTargetSize;
const uint32_t mStretchHint;
nsBoundingMetrics& mBoundingMetrics;
// Font families to search
const FontFamilyList& mFamilyList;
public:
bool mTryVariants;
bool mTryParts;
private:
AutoTArray<nsGlyphTable*,16> mTablesTried;
bool& mGlyphFound;
};
// 2. See if there are any glyphs of the appropriate size.
// Returns true if the size is OK, false to keep searching.
// Always updates the char if a better match is found.
bool
nsMathMLChar::
StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable,
RefPtr<gfxFontGroup>* aFontGroup,
const FontFamilyList& aFamilyList)
{
// Use our stretchy style context now that stretching is in progress
nsStyleContext *sc = mChar->mStyleContext;
nsFont font = sc->StyleFont()->mFont;
NormalizeDefaultFont(font, mFontSizeInflation);
bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
char16_t uchar = mChar->mData[0];
bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0;
bool largeopOnly =
largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0;
bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
nscoord bestSize =
isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
: mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
bool haveBetter = false;
// start at size = 1 (size = 0 is the char at its normal size)
int32_t size = 1;
nsGlyphCode ch;
nscoord displayOperatorMinHeight = 0;
if (largeopOnly) {
NS_ASSERTION(isVertical, "Stretching should be in the vertical direction");
ch = aGlyphTable->BigOf(mDrawTarget, oneDevPixel, *aFontGroup, uchar,
isVertical, 0);
if (ch.IsGlyphID()) {
gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont();
// For OpenType MATH fonts, we will rely on the DisplayOperatorMinHeight
// to select the right size variant. Note that the value is sometimes too
// small so we use kLargeOpFactor/kIntegralFactor as a minimum value.
if (mathFont) {
displayOperatorMinHeight =
mathFont->GetMathConstant(gfxFontEntry::DisplayOperatorMinHeight,
oneDevPixel);
UniquePtr<gfxTextRun> textRun =
aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch);
nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun.get());
float largeopFactor = kLargeOpFactor;
if (NS_STRETCH_INTEGRAL & mStretchHint) {
// integrals are drawn taller
largeopFactor = kIntegralFactor;
}
nscoord minHeight = largeopFactor * (bm.ascent + bm.descent);
if (displayOperatorMinHeight < minHeight) {
displayOperatorMinHeight = minHeight;
}
}
}
}
#ifdef NOISY_SEARCH
printf(" searching in %s ...\n",
NS_LossyConvertUTF16toASCII(aFamily).get());
#endif
while ((ch = aGlyphTable->BigOf(mDrawTarget, oneDevPixel, *aFontGroup,
uchar, isVertical, size)).Exists()) {
if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamilyList, font,
aFontGroup)) {
// if largeopOnly is set, break now
if (largeopOnly) break;
++size;
continue;
}
UniquePtr<gfxTextRun> textRun =
aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel, *aFontGroup, ch);
nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun.get());
if (ch.IsGlyphID()) {
gfxFont* mathFont = aFontGroup->get()->GetFirstMathFont();
if (mathFont) {
// MeasureTextRun should have set the advance width to the right
// bearing for OpenType MATH fonts. We now subtract the italic
// correction, so that nsMathMLmmultiscripts will place the scripts
// correctly.
// Note that STIX-Word does not provide italic corrections but its
// advance widths do not match right bearings.
// (http://sourceforge.net/p/stixfonts/tracking/50/)
gfxFloat italicCorrection;
if (mathFont->GetFontEntry()->
GetMathItalicsCorrection(ch.glyphID, &italicCorrection)) {
bm.width -=
NSToCoordRound(italicCorrection *
mathFont->GetAdjustedSize() * oneDevPixel);
if (bm.width < 0) {
bm.width = 0;
}
}
}
}
nscoord charSize =
isVertical ? bm.ascent + bm.descent
: bm.rightBearing - bm.leftBearing;
if (largeopOnly ||
IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
mGlyphFound = true;
if (maxWidth) {
// IsSizeBetter() checked that charSize < maxsize;
// Leave ascent, descent, and bestsize as these contain maxsize.
if (mBoundingMetrics.width < bm.width)
mBoundingMetrics.width = bm.width;
if (mBoundingMetrics.leftBearing > bm.leftBearing)
mBoundingMetrics.leftBearing = bm.leftBearing;
if (mBoundingMetrics.rightBearing < bm.rightBearing)
mBoundingMetrics.rightBearing = bm.rightBearing;
// Continue to check other sizes unless largeopOnly
haveBetter = largeopOnly;
}
else {
mBoundingMetrics = bm;
haveBetter = true;
bestSize = charSize;
mChar->mGlyphs[0] = Move(textRun);
mChar->mDraw = DRAW_VARIANT;
}
#ifdef NOISY_SEARCH
printf(" size:%d Current best\n", size);
#endif
}
else {
#ifdef NOISY_SEARCH
printf(" size:%d Rejected!\n", size);
#endif
if (haveBetter)
break; // Not making an futher progress, stop searching
}
// If this a largeop only operator, we stop if the glyph is large enough.
if (largeopOnly && (bm.ascent + bm.descent) >= displayOperatorMinHeight) {
break;
}
++size;
}
return haveBetter &&
(largeopOnly || IsSizeOK(bestSize, mTargetSize, mStretchHint));
}
// 3. Build by parts.
// Returns true if the size is OK, false to keep searching.
// Always updates the char if a better match is found.
bool
nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable,
RefPtr<gfxFontGroup>* aFontGroup,
const FontFamilyList& aFamilyList)
{
// Use our stretchy style context now that stretching is in progress
nsFont font = mChar->mStyleContext->StyleFont()->mFont;
NormalizeDefaultFont(font, mFontSizeInflation);
// Compute the bounding metrics of all partial glyphs
UniquePtr<gfxTextRun> textRun[4];
nsGlyphCode chdata[4];
nsBoundingMetrics bmdata[4];
nscoord sizedata[4];
bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
nscoord oneDevPixel = mPresContext->AppUnitsPerDevPixel();
char16_t uchar = mChar->mData[0];
bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
if (!aGlyphTable->HasPartsOf(mDrawTarget, oneDevPixel, *aFontGroup,
uchar, isVertical))
return false; // to next table
for (int32_t i = 0; i < 4; i++) {
nsGlyphCode ch = aGlyphTable->ElementAt(mDrawTarget, oneDevPixel,
*aFontGroup, uchar, isVertical, i);
chdata[i] = ch;
if (ch.Exists()) {
if (!mChar->SetFontFamily(mPresContext, aGlyphTable, ch, aFamilyList, font,
aFontGroup))
return false;
textRun[i] = aGlyphTable->MakeTextRun(mDrawTarget, oneDevPixel,
*aFontGroup, ch);
nsBoundingMetrics bm = MeasureTextRun(mDrawTarget, textRun[i].get());
bmdata[i] = bm;
sizedata[i] = isVertical ? bm.ascent + bm.descent
: bm.rightBearing - bm.leftBearing;
} else {
// Null glue indicates that a rule will be drawn, which can stretch to
// fill any space.
textRun[i] = nullptr;
bmdata[i] = nsBoundingMetrics();
sizedata[i] = i == 3 ? mTargetSize : 0;
}
}
// For the Unicode table, we check that all the glyphs are actually found and
// come from the same font.
if (aGlyphTable == &gGlyphTableList->mUnicodeTable) {
gfxFont* unicodeFont = nullptr;
for (int32_t i = 0; i < 4; i++) {
if (!textRun[i]) {
continue;
}
if (textRun[i]->GetLength() != 1 ||
textRun[i]->GetCharacterGlyphs()[0].IsMissing()) {
return false;
}
uint32_t numGlyphRuns;
const gfxTextRun::GlyphRun* glyphRuns =
textRun[i]->GetGlyphRuns(&numGlyphRuns);
if (numGlyphRuns != 1) {
return false;
}
if (!unicodeFont) {
unicodeFont = glyphRuns[0].mFont;
} else if (unicodeFont != glyphRuns[0].mFont) {
return false;
}
}
}
// Build by parts if we have successfully computed the
// bounding metrics of all parts.
nscoord computedSize = ComputeSizeFromParts(mPresContext, chdata, sizedata,
mTargetSize);
nscoord currentSize =
isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
: mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
if (!IsSizeBetter(computedSize, currentSize, mTargetSize, mStretchHint)) {
#ifdef NOISY_SEARCH
printf(" Font %s Rejected!\n",
NS_LossyConvertUTF16toASCII(fontName).get());
#endif
return false; // to next table
}
#ifdef NOISY_SEARCH
printf(" Font %s Current best!\n",
NS_LossyConvertUTF16toASCII(fontName).get());
#endif
// The computed size is the best we have found so far...
// now is the time to compute and cache our bounding metrics
if (isVertical) {
int32_t i;
// Try and find the first existing part and then determine the extremal
// horizontal metrics of the parts.
for (i = 0; i <= 3 && !textRun[i]; i++);
if (i == 4) {
NS_ERROR("Cannot stretch - All parts missing");
return false;
}
nscoord lbearing = bmdata[i].leftBearing;
nscoord rbearing = bmdata[i].rightBearing;
nscoord width = bmdata[i].width;
i++;
for (; i <= 3; i++) {
if (!textRun[i]) continue;
lbearing = std::min(lbearing, bmdata[i].leftBearing);
rbearing = std::max(rbearing, bmdata[i].rightBearing);
width = std::max(width, bmdata[i].width);
}
if (maxWidth) {
lbearing = std::min(lbearing, mBoundingMetrics.leftBearing);
rbearing = std::max(rbearing, mBoundingMetrics.rightBearing);
width = std::max(width, mBoundingMetrics.width);
}
mBoundingMetrics.width = width;
// When maxWidth, updating ascent and descent indicates that no characters
// larger than this character's minimum size need to be checked as they
// will not be used.
mBoundingMetrics.ascent = bmdata[0].ascent; // not used except with descent
// for height
mBoundingMetrics.descent = computedSize - mBoundingMetrics.ascent;
mBoundingMetrics.leftBearing = lbearing;
mBoundingMetrics.rightBearing = rbearing;
}
else {
int32_t i;
// Try and find the first existing part and then determine the extremal
// vertical metrics of the parts.
for (i = 0; i <= 3 && !textRun[i]; i++);
if (i == 4) {
NS_ERROR("Cannot stretch - All parts missing");
return false;
}
nscoord ascent = bmdata[i].ascent;
nscoord descent = bmdata[i].descent;
i++;
for (; i <= 3; i++) {
if (!textRun[i]) continue;
ascent = std::max(ascent, bmdata[i].ascent);
descent = std::max(descent, bmdata[i].descent);
}
mBoundingMetrics.width = computedSize;
mBoundingMetrics.ascent = ascent;
mBoundingMetrics.descent = descent;
mBoundingMetrics.leftBearing = 0;
mBoundingMetrics.rightBearing = computedSize;
}
mGlyphFound = true;
if (maxWidth)
return false; // Continue to check other sizes
// reset
mChar->mDraw = DRAW_PARTS;
for (int32_t i = 0; i < 4; i++) {
mChar->mGlyphs[i] = Move(textRun[i]);
mChar->mBmData[i] = bmdata[i];
}
return IsSizeOK(computedSize, mTargetSize, mStretchHint);
}
// This is called for each family, whether it exists or not
bool
nsMathMLChar::StretchEnumContext::EnumCallback(const FontFamilyName& aFamily,
bool aGeneric, void *aData)
{
StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
// for comparisons, force use of unquoted names
FontFamilyName unquotedFamilyName(aFamily);
if (unquotedFamilyName.mType == eFamily_named_quoted) {
unquotedFamilyName.mType = eFamily_named;
}
// Check font family if it is not a generic one
// We test with the kNullGlyph
nsStyleContext *sc = context->mChar->mStyleContext;
nsFont font = sc->StyleFont()->mFont;
NormalizeDefaultFont(font, context->mFontSizeInflation);
RefPtr<gfxFontGroup> fontGroup;
FontFamilyList family;
family.Append(unquotedFamilyName);
if (!aGeneric && !context->mChar->SetFontFamily(context->mPresContext,
nullptr, kNullGlyph, family,
font, &fontGroup))
return true; // Could not set the family
// Determine the glyph table to use for this font.
nsAutoPtr<nsOpenTypeTable> openTypeTable;
nsGlyphTable* glyphTable;
if (aGeneric) {
// This is a generic font, use the Unicode table.
glyphTable = &gGlyphTableList->mUnicodeTable;
} else {
// If the font contains an Open Type MATH table, use it.
openTypeTable = nsOpenTypeTable::Create(fontGroup->GetFirstValidFont());
if (openTypeTable) {
glyphTable = openTypeTable;
} else {
// Otherwise try to find a .properties file corresponding to that font
// family or fallback to the Unicode table.
nsAutoString familyName;
unquotedFamilyName.AppendToString(familyName);
glyphTable = gGlyphTableList->GetGlyphTableFor(familyName);
}
}
if (!openTypeTable) {
if (context->mTablesTried.Contains(glyphTable))
return true; // already tried this one
// Only try this table once.
context->mTablesTried.AppendElement(glyphTable);
}
// If the unicode table is being used, then search all font families. If a
// special table is being used then the font in this family should have the
// specified glyphs.
const FontFamilyList& familyList = glyphTable == &gGlyphTableList->mUnicodeTable ?
context->mFamilyList : family;
if((context->mTryVariants &&
context->TryVariants(glyphTable, &fontGroup, familyList)) ||
(context->mTryParts && context->TryParts(glyphTable,
&fontGroup,
familyList)))
return false; // no need to continue
return true; // true means continue
}
// insert math fallback families just before the first generic or at the end
// when no generic present
static void
InsertMathFallbacks(FontFamilyList& aFamilyList,
nsTArray<nsString>& aFallbacks)
{
FontFamilyList aMergedList;
bool inserted = false;
const nsTArray<FontFamilyName>& fontlist = aFamilyList.GetFontlist();
uint32_t i, num = fontlist.Length();
for (i = 0; i < num; i++) {
const FontFamilyName& name = fontlist[i];
if (!inserted && name.IsGeneric()) {
inserted = true;
aMergedList.Append(aFallbacks);
}
aMergedList.Append(name);
}
if (!inserted) {
aMergedList.Append(aFallbacks);
}
aFamilyList = aMergedList;
}
nsresult
nsMathMLChar::StretchInternal(nsPresContext* aPresContext,
DrawTarget* aDrawTarget,
float aFontSizeInflation,
nsStretchDirection& aStretchDirection,
const nsBoundingMetrics& aContainerSize,
nsBoundingMetrics& aDesiredStretchSize,
uint32_t aStretchHint,
// These are currently only used when
// aStretchHint & NS_STRETCH_MAXWIDTH:
float aMaxSize,
bool aMaxSizeIsAbsolute)
{
// if we have been called before, and we didn't actually stretch, our
// direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
// So first set our direction back to its instrinsic value
nsStretchDirection direction = nsMathMLOperators::GetStretchyDirection(mData);
// Set default font and get the default bounding metrics
// mStyleContext is a leaf context used only when stretching happens.
// For the base size, the default font should come from the parent context
nsFont font = mStyleContext->GetParent()->StyleFont()->mFont;
NormalizeDefaultFont(font, aFontSizeInflation);
const nsStyleFont* styleFont = mStyleContext->StyleFont();
nsFontMetrics::Params params;
params.language = styleFont->mLanguage;
params.explicitLanguage = styleFont->mExplicitLanguage;
params.userFontSet = aPresContext->GetUserFontSet();
params.textPerf = aPresContext->GetTextPerfMetrics();
RefPtr<nsFontMetrics> fm =
aPresContext->DeviceContext()->GetMetricsFor(font, params);
uint32_t len = uint32_t(mData.Length());
mGlyphs[0] = fm->GetThebesFontGroup()->
MakeTextRun(static_cast<const char16_t*>(mData.get()), len, aDrawTarget,
aPresContext->AppUnitsPerDevPixel(), 0,
aPresContext->MissingFontRecorder());
aDesiredStretchSize = MeasureTextRun(aDrawTarget, mGlyphs[0].get());
bool maxWidth = (NS_STRETCH_MAXWIDTH & aStretchHint) != 0;
if (!maxWidth) {
mUnscaledAscent = aDesiredStretchSize.ascent;
}
//////////////////////////////////////////////////////////////////////////////
// 1. Check the common situations where stretching is not actually needed
//////////////////////////////////////////////////////////////////////////////
// quick return if there is nothing special about this char
if ((aStretchDirection != direction &&
aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT) ||
(aStretchHint & ~NS_STRETCH_MAXWIDTH) == NS_STRETCH_NONE) {
mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
return NS_OK;
}
// if no specified direction, attempt to stretch in our preferred direction
if (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT) {
aStretchDirection = direction;
}
// see if this is a particular largeop or largeopOnly request
bool largeop = (NS_STRETCH_LARGEOP & aStretchHint) != 0;
bool stretchy = (NS_STRETCH_VARIABLE_MASK & aStretchHint) != 0;
bool largeopOnly = largeop && !stretchy;
bool isVertical = (direction == NS_STRETCH_DIRECTION_VERTICAL);
nscoord targetSize =
isVertical ? aContainerSize.ascent + aContainerSize.descent
: aContainerSize.rightBearing - aContainerSize.leftBearing;
if (maxWidth) {
// See if it is only necessary to consider glyphs up to some maximum size.
// Set the current height to the maximum size, and set aStretchHint to
// NS_STRETCH_SMALLER if the size is variable, so that only smaller sizes
// are considered. targetSize from GetMaxWidth() is 0.
if (stretchy) {
// variable size stretch - consider all sizes < maxsize
aStretchHint =
(aStretchHint & ~NS_STRETCH_VARIABLE_MASK) | NS_STRETCH_SMALLER;
}
// Use NS_MATHML_DELIMITER_FACTOR to allow some slightly larger glyphs as
// maxsize is not enforced exactly.
if (aMaxSize == NS_MATHML_OPERATOR_SIZE_INFINITY) {
aDesiredStretchSize.ascent = nscoord_MAX;
aDesiredStretchSize.descent = 0;
}
else {
nscoord height = aDesiredStretchSize.ascent + aDesiredStretchSize.descent;
if (height == 0) {
if (aMaxSizeIsAbsolute) {
aDesiredStretchSize.ascent =
NSToCoordRound(aMaxSize / NS_MATHML_DELIMITER_FACTOR);
aDesiredStretchSize.descent = 0;
}
// else: leave height as 0
}
else {
float scale = aMaxSizeIsAbsolute ? aMaxSize / height : aMaxSize;
scale /= NS_MATHML_DELIMITER_FACTOR;
aDesiredStretchSize.ascent =
NSToCoordRound(scale * aDesiredStretchSize.ascent);
aDesiredStretchSize.descent =
NSToCoordRound(scale * aDesiredStretchSize.descent);
}
}
}
nsBoundingMetrics initialSize = aDesiredStretchSize;
nscoord charSize =
isVertical ? initialSize.ascent + initialSize.descent
: initialSize.rightBearing - initialSize.leftBearing;
bool done = false;
if (!maxWidth && !largeop) {
// Doing Stretch() not GetMaxWidth(),
// and not a largeop in display mode; we're done if size fits
if ((targetSize <= 0) ||
((isVertical && charSize >= targetSize) ||
IsSizeOK(charSize, targetSize, aStretchHint)))
done = true;
}
//////////////////////////////////////////////////////////////////////////////
// 2/3. Search for a glyph or set of part glyphs of appropriate size
//////////////////////////////////////////////////////////////////////////////
bool glyphFound = false;
if (!done) { // normal case
// Use the css font-family but add preferred fallback fonts.
font = mStyleContext->StyleFont()->mFont;
NormalizeDefaultFont(font, aFontSizeInflation);
// really shouldn't be doing things this way but for now
// insert fallbacks into the list
AutoTArray<nsString, 16> mathFallbacks;
gfxFontUtils::GetPrefsFontList("font.name.serif.x-math", mathFallbacks);
gfxFontUtils::AppendPrefsFontList("font.name-list.serif.x-math",
mathFallbacks);
InsertMathFallbacks(font.fontlist, mathFallbacks);
#ifdef NOISY_SEARCH
nsAutoString fontlistStr;
font.fontlist.ToString(fontlistStr, false, true);
printf("Searching in "%s" for a glyph of appropriate size for: 0x%04X:%c\n",
NS_ConvertUTF16toUTF8(fontlistStr).get(), mData[0], mData[0]&0x00FF);
#endif
StretchEnumContext enumData(this, aPresContext, aDrawTarget,
aFontSizeInflation,
aStretchDirection, targetSize, aStretchHint,
aDesiredStretchSize, font.fontlist, glyphFound);
enumData.mTryParts = !largeopOnly;
const nsTArray<FontFamilyName>& fontlist = font.fontlist.GetFontlist();
uint32_t i, num = fontlist.Length();
bool next = true;
for (i = 0; i < num && next; i++) {
const FontFamilyName& name = fontlist[i];
next = StretchEnumContext::EnumCallback(name, name.IsGeneric(), &enumData);
}
}
if (!maxWidth) {
// Now, we know how we are going to draw the char. Update the member
// variables accordingly.
mUnscaledAscent = aDesiredStretchSize.ascent;
}
if (glyphFound) {
return NS_OK;
}
// We did not find a size variant or a glyph assembly to stretch this
// operator. Verify whether a font with an OpenType MATH table is available
// and record missing math script otherwise.
gfxMissingFontRecorder* MFR = aPresContext->MissingFontRecorder();
if (MFR && !fm->GetThebesFontGroup()->GetFirstMathFont()) {
MFR->RecordScript(unicode::Script::MATHEMATICAL_NOTATION);
}
// If the scale_stretchy_operators option is disabled, we are done.
if (!Preferences::GetBool("mathml.scale_stretchy_operators.enabled", true)) {
return NS_OK;
}
// stretchy character
if (stretchy) {
if (isVertical) {
float scale =
std::min(kMaxScaleFactor, float(aContainerSize.ascent + aContainerSize.descent) /
(aDesiredStretchSize.ascent + aDesiredStretchSize.descent));
if (!largeop || scale > 1.0) {
// make the character match the desired height.
if (!maxWidth) {
mScaleY *= scale;
}
aDesiredStretchSize.ascent *= scale;
aDesiredStretchSize.descent *= scale;
}
} else {
float scale =
std::min(kMaxScaleFactor, float(aContainerSize.rightBearing - aContainerSize.leftBearing) /
(aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing));
if (!largeop || scale > 1.0) {
// make the character match the desired width.
if (!maxWidth) {
mScaleX *= scale;
}
aDesiredStretchSize.leftBearing *= scale;
aDesiredStretchSize.rightBearing *= scale;
aDesiredStretchSize.width *= scale;
}
}
}
// We do not have a char variant for this largeop in display mode, so we
// apply a scale transform to the base char.
if (largeop) {
float scale;
float largeopFactor = kLargeOpFactor;
// increase the width if it is not largeopFactor times larger
// than the initial one.
if ((aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing) <
largeopFactor * (initialSize.rightBearing - initialSize.leftBearing)) {
scale = (largeopFactor *
(initialSize.rightBearing - initialSize.leftBearing)) /
(aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
if (!maxWidth) {
mScaleX *= scale;
}
aDesiredStretchSize.leftBearing *= scale;
aDesiredStretchSize.rightBearing *= scale;
aDesiredStretchSize.width *= scale;
}
// increase the height if it is not largeopFactor times larger
// than the initial one.
if (NS_STRETCH_INTEGRAL & aStretchHint) {
// integrals are drawn taller
largeopFactor = kIntegralFactor;
}
if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) <
largeopFactor * (initialSize.ascent + initialSize.descent)) {
scale = (largeopFactor *
(initialSize.ascent + initialSize.descent)) /
(aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
if (!maxWidth) {
mScaleY *= scale;
}
aDesiredStretchSize.ascent *= scale;
aDesiredStretchSize.descent *= scale;
}
}
return NS_OK;
}
nsresult
nsMathMLChar::Stretch(nsPresContext* aPresContext,
DrawTarget* aDrawTarget,
float aFontSizeInflation,
nsStretchDirection aStretchDirection,
const nsBoundingMetrics& aContainerSize,
nsBoundingMetrics& aDesiredStretchSize,
uint32_t aStretchHint,
bool aRTL)
{
NS_ASSERTION(!(aStretchHint &
~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP |
NS_STRETCH_INTEGRAL)),
"Unexpected stretch flags");
mDraw = DRAW_NORMAL;
mMirrored = aRTL && nsMathMLOperators::IsMirrorableOperator(mData);
mScaleY = mScaleX = 1.0;
mDirection = aStretchDirection;
nsresult rv =
StretchInternal(aPresContext, aDrawTarget, aFontSizeInflation, mDirection,
aContainerSize, aDesiredStretchSize, aStretchHint);
// Record the metrics
mBoundingMetrics = aDesiredStretchSize;
return rv;
}
// What happens here is that the StretchInternal algorithm is used but
// modified by passing the NS_STRETCH_MAXWIDTH stretch hint. That causes
// StretchInternal to return horizontal bounding metrics that are the maximum
// that might be returned from a Stretch.
//
// In order to avoid considering widths of some characters in fonts that will
// not be used for any stretch size, StretchInternal sets the initial height
// to infinity and looks for any characters smaller than this height. When a
// character built from parts is considered, (it will be used by Stretch for
// any characters greater than its minimum size, so) the height is set to its
// minimum size, so that only widths of smaller subsequent characters are
// considered.
nscoord
nsMathMLChar::GetMaxWidth(nsPresContext* aPresContext, DrawTarget* aDrawTarget,
float aFontSizeInflation, uint32_t aStretchHint)
{
nsBoundingMetrics bm;
nsStretchDirection direction = NS_STRETCH_DIRECTION_VERTICAL;
const nsBoundingMetrics container; // zero target size
StretchInternal(aPresContext, aDrawTarget, aFontSizeInflation, direction,
container, bm, aStretchHint | NS_STRETCH_MAXWIDTH);
return std::max(bm.width, bm.rightBearing) - std::min(0, bm.leftBearing);
}
class nsDisplayMathMLSelectionRect : public nsDisplayItem {
public:
nsDisplayMathMLSelectionRect(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, const nsRect& aRect)
: nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
MOZ_COUNT_CTOR(nsDisplayMathMLSelectionRect);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayMathMLSelectionRect() {
MOZ_COUNT_DTOR(nsDisplayMathMLSelectionRect);
}
#endif
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) override;
NS_DISPLAY_DECL_NAME("MathMLSelectionRect", TYPE_MATHML_SELECTION_RECT)
private:
nsRect mRect;
};
void nsDisplayMathMLSelectionRect::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx)
{
DrawTarget* drawTarget = aCtx->GetDrawTarget();
Rect rect = NSRectToSnappedRect(mRect + ToReferenceFrame(),
mFrame->PresContext()->AppUnitsPerDevPixel(),
*drawTarget);
// get color to use for selection from the look&feel object
nscolor bgColor =
LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground,
NS_RGB(0, 0, 0));
drawTarget->FillRect(rect, ColorPattern(ToDeviceColor(bgColor)));
}
class nsDisplayMathMLCharBackground : public nsDisplayItem {
public:
nsDisplayMathMLCharBackground(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, const nsRect& aRect,
nsStyleContext* aStyleContext)
: nsDisplayItem(aBuilder, aFrame), mStyleContext(aStyleContext),
mRect(aRect) {
MOZ_COUNT_CTOR(nsDisplayMathMLCharBackground);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayMathMLCharBackground() {
MOZ_COUNT_DTOR(nsDisplayMathMLCharBackground);
}
#endif
virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override;
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion) override;
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) override;
NS_DISPLAY_DECL_NAME("MathMLCharBackground", TYPE_MATHML_CHAR_BACKGROUND)
private:
nsStyleContext* mStyleContext;
nsRect mRect;
};
nsDisplayItemGeometry*
nsDisplayMathMLCharBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
{
return new nsDisplayItemGenericImageGeometry(this, aBuilder);
}
void
nsDisplayMathMLCharBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion)
{
auto geometry =
static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
if (aBuilder->ShouldSyncDecodeImages() &&
geometry->ShouldInvalidateToSyncDecodeImages()) {
bool snap;
aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
}
nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
}
void nsDisplayMathMLCharBackground::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx)
{
const nsStyleBorder* border = mStyleContext->StyleBorder();
nsRect rect(mRect + ToReferenceFrame());
DrawResult result =
nsCSSRendering::PaintBackgroundWithSC(mFrame->PresContext(), *aCtx, mFrame,
mVisibleRect, rect,
mStyleContext, *border,
aBuilder->GetBackgroundPaintFlags());
nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
}
class nsDisplayMathMLCharForeground : public nsDisplayItem {
public:
nsDisplayMathMLCharForeground(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsMathMLChar* aChar,
uint32_t aIndex, bool aIsSelected)
: nsDisplayItem(aBuilder, aFrame), mChar(aChar),
mIndex(aIndex), mIsSelected(aIsSelected) {
MOZ_COUNT_CTOR(nsDisplayMathMLCharForeground);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayMathMLCharForeground() {
MOZ_COUNT_DTOR(nsDisplayMathMLCharForeground);
}
#endif
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override {
*aSnap = false;
nsRect rect;
mChar->GetRect(rect);
nsPoint offset = ToReferenceFrame() + rect.TopLeft();
nsBoundingMetrics bm;
mChar->GetBoundingMetrics(bm);
nsRect temp(offset.x + bm.leftBearing, offset.y,
bm.rightBearing - bm.leftBearing, bm.ascent + bm.descent);
// Bug 748220
temp.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
return temp;
}
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) override
{
mChar->PaintForeground(mFrame->PresContext(), *aCtx,
ToReferenceFrame(), mIsSelected);
}
NS_DISPLAY_DECL_NAME("MathMLCharForeground", TYPE_MATHML_CHAR_FOREGROUND)
virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) override
{
bool snap;
return GetBounds(aBuilder, &snap);
}
virtual uint32_t GetPerFrameKey() override {
return (mIndex << nsDisplayItem::TYPE_BITS)
| nsDisplayItem::GetPerFrameKey();
}
private:
nsMathMLChar* mChar;
uint32_t mIndex;
bool mIsSelected;
};
#ifdef DEBUG
class nsDisplayMathMLCharDebug : public nsDisplayItem {
public:
nsDisplayMathMLCharDebug(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, const nsRect& aRect)
: nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
MOZ_COUNT_CTOR(nsDisplayMathMLCharDebug);
}
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayMathMLCharDebug() {
MOZ_COUNT_DTOR(nsDisplayMathMLCharDebug);
}
#endif
virtual void Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) override;
NS_DISPLAY_DECL_NAME("MathMLCharDebug", TYPE_MATHML_CHAR_DEBUG)
private:
nsRect mRect;
};
void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx)
{
// for visual debug
Sides skipSides;
nsPresContext* presContext = mFrame->PresContext();
nsStyleContext* styleContext = mFrame->StyleContext();
nsRect rect = mRect + ToReferenceFrame();
PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
? PaintBorderFlags::SYNC_DECODE_IMAGES
: PaintBorderFlags();
// Since this is used only for debugging, we don't need to worry about
// tracking the DrawResult.
Unused <<
nsCSSRendering::PaintBorder(presContext, *aCtx, mFrame, mVisibleRect,
rect, styleContext, flags, skipSides);
nsCSSRendering::PaintOutline(presContext, *aCtx, mFrame,
mVisibleRect, rect, styleContext);
}
#endif
void
nsMathMLChar::Display(nsDisplayListBuilder* aBuilder,
nsIFrame* aForFrame,
const nsDisplayListSet& aLists,
uint32_t aIndex,
const nsRect* aSelectedRect)
{
nsStyleContext* parentContext = mStyleContext->GetParent();
nsStyleContext* styleContext = mStyleContext;
if (mDraw == DRAW_NORMAL) {
// normal drawing if there is nothing special about this char
// Set default context to the parent context
styleContext = parentContext;
}
if (!styleContext->StyleVisibility()->IsVisible())
return;
// if the leaf style context that we use for stretchy chars has a background
// color we use it -- this feature is mostly used for testing and debugging
// purposes. Normally, users will set the background on the container frame.
// paint the selection background -- beware MathML frames overlap a lot
if (aSelectedRect && !aSelectedRect->IsEmpty()) {
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
nsDisplayMathMLSelectionRect(aBuilder, aForFrame, *aSelectedRect));
}
else if (mRect.width && mRect.height) {
const nsStyleBackground* backg = styleContext->StyleBackground();
if (styleContext != parentContext &&
NS_GET_A(backg->mBackgroundColor) > 0) {
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
nsDisplayMathMLCharBackground(aBuilder, aForFrame, mRect,
styleContext));
}
//else
// our container frame will take care of painting its background
#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
// for visual debug
aLists.BorderBackground()->AppendToTop(new (aBuilder)
nsDisplayMathMLCharDebug(aBuilder, aForFrame, mRect));
#endif
}
aLists.Content()->AppendNewToTop(new (aBuilder)
nsDisplayMathMLCharForeground(aBuilder, aForFrame, this,
aIndex,
aSelectedRect &&
!aSelectedRect->IsEmpty()));
}
void
nsMathMLChar::ApplyTransforms(gfxContext* aThebesContext,
int32_t aAppUnitsPerGfxUnit,
nsRect &r)
{
// apply the transforms
if (mMirrored) {
nsPoint pt = r.TopRight();
gfxPoint devPixelOffset(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit));
aThebesContext->SetMatrix(
aThebesContext->CurrentMatrix().Translate(devPixelOffset).
Scale(-mScaleX, mScaleY));
} else {
nsPoint pt = r.TopLeft();
gfxPoint devPixelOffset(NSAppUnitsToFloatPixels(pt.x, aAppUnitsPerGfxUnit),
NSAppUnitsToFloatPixels(pt.y, aAppUnitsPerGfxUnit));
aThebesContext->SetMatrix(
aThebesContext->CurrentMatrix().Translate(devPixelOffset).
Scale(mScaleX, mScaleY));
}
// update the bounding rectangle.
r.x = r.y = 0;
r.width /= mScaleX;
r.height /= mScaleY;
}
void
nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
nsPoint aPt,
bool aIsSelected)
{
nsStyleContext* parentContext = mStyleContext->GetParent();
nsStyleContext* styleContext = mStyleContext;
if (mDraw == DRAW_NORMAL) {
// normal drawing if there is nothing special about this char
// Set default context to the parent context
styleContext = parentContext;
}
RefPtr<gfxContext> thebesContext = aRenderingContext.ThebesContext();
// Set color ...
nsCSSProperty colorProp = styleContext->GetTextFillColorProp();
nscolor fgColor = styleContext->GetVisitedDependentColor(colorProp);
if (aIsSelected) {
// get color to use for selection from the look&feel object
fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
fgColor);
}
thebesContext->SetColor(Color::FromABGR(fgColor));
thebesContext->Save();
nsRect r = mRect + aPt;
ApplyTransforms(thebesContext, aPresContext->AppUnitsPerDevPixel(), r);
switch(mDraw)
{
case DRAW_NORMAL:
case DRAW_VARIANT:
// draw a single glyph (base size or size variant)
// XXXfredw verify if mGlyphs[0] is non-null to workaround bug 973322.
if (mGlyphs[0]) {
mGlyphs[0]->Draw(Range(mGlyphs[0].get()), gfxPoint(0.0, mUnscaledAscent),
gfxTextRun::DrawParams(thebesContext));
}
break;
case DRAW_PARTS: {
// paint by parts
if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
PaintVertically(aPresContext, thebesContext, r, fgColor);
else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
PaintHorizontally(aPresContext, thebesContext, r, fgColor);
break;
}
default:
NS_NOTREACHED("Unknown drawing method");
break;
}
thebesContext->Restore();
}
/* =============================================================================
Helper routines that actually do the job of painting the char by parts
*/
class AutoPushClipRect {
gfxContext* mThebesContext;
public:
AutoPushClipRect(gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
const nsRect& aRect)
: mThebesContext(aThebesContext) {
mThebesContext->Save();
mThebesContext->NewPath();
gfxRect clip = nsLayoutUtils::RectToGfxRect(aRect, aAppUnitsPerGfxUnit);
mThebesContext->SnappedRectangle(clip);
mThebesContext->Clip();
}
~AutoPushClipRect() {
mThebesContext->Restore();
}
};
static nsPoint
SnapToDevPixels(const gfxContext* aThebesContext, int32_t aAppUnitsPerGfxUnit,
const nsPoint& aPt)
{
gfxPoint pt(NSAppUnitsToFloatPixels(aPt.x, aAppUnitsPerGfxUnit),
NSAppUnitsToFloatPixels(aPt.y, aAppUnitsPerGfxUnit));
pt = aThebesContext->UserToDevice(pt);
pt.Round();
pt = aThebesContext->DeviceToUser(pt);
return nsPoint(NSFloatPixelsToAppUnits(pt.x, aAppUnitsPerGfxUnit),
NSFloatPixelsToAppUnits(pt.y, aAppUnitsPerGfxUnit));
}
static void
PaintRule(DrawTarget& aDrawTarget,
int32_t aAppUnitsPerGfxUnit,
nsRect& aRect,
nscolor aColor)
{
Rect rect = NSRectToSnappedRect(aRect, aAppUnitsPerGfxUnit, aDrawTarget);
ColorPattern color(ToDeviceColor(aColor));
aDrawTarget.FillRect(rect, color);
}
// paint a stretchy char by assembling glyphs vertically
nsresult
nsMathMLChar::PaintVertically(nsPresContext* aPresContext,
gfxContext* aThebesContext,
nsRect& aRect,
nscolor aColor)
{
DrawTarget& aDrawTarget = *aThebesContext->GetDrawTarget();
// Get the device pixel size in the vertical direction.
// (This makes no effort to optimize for non-translation transformations.)
nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
// get metrics data to be re-used later
int32_t i = 0;
nscoord dx = aRect.x;
nscoord offset[3], start[3], end[3];
for (i = 0; i <= 2; ++i) {
const nsBoundingMetrics& bm = mBmData[i];
nscoord dy;
if (0 == i) { // top
dy = aRect.y + bm.ascent;
}
else if (2 == i) { // bottom
dy = aRect.y + aRect.height - bm.descent;
}
else { // middle
dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2;
}
// _cairo_scaled_font_show_glyphs snaps origins to device pixels.
// Do this now so that we can get the other dimensions right.
// (This may not achieve much with non-rectangular transformations.)
dy = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).y;
// abcissa passed to Draw
offset[i] = dy;
// _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
// pixel, so the bm values can include 1 row of faint pixels on each edge.
// Don't rely on this pixel as it can look like a gap.
if (bm.ascent + bm.descent >= 2 * oneDevPixel) {
start[i] = dy - bm.ascent + oneDevPixel; // top join
end[i] = dy + bm.descent - oneDevPixel; // bottom join
} else {
// To avoid overlaps, we don't add one pixel on each side when the part
// is too small.
start[i] = dy - bm.ascent; // top join
end[i] = dy + bm.descent; // bottom join
}
}
// If there are overlaps, then join at the mid point
for (i = 0; i < 2; ++i) {
if (end[i] > start[i+1]) {
end[i] = (end[i] + start[i+1]) / 2;
start[i+1] = end[i];
}
}
nsRect unionRect = aRect;
unionRect.x += mBoundingMetrics.leftBearing;
unionRect.width =
mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
unionRect.Inflate(oneDevPixel, oneDevPixel);
gfxTextRun::DrawParams params(aThebesContext);
/////////////////////////////////////
// draw top, middle, bottom
for (i = 0; i <= 2; ++i) {
// glue can be null
if (mGlyphs[i]) {
nscoord dy = offset[i];
// Draw a glyph in a clipped area so that we don't have hairy chars
// pending outside
nsRect clipRect = unionRect;
// Clip at the join to get a solid edge (without overlap or gap), when
// this won't change the glyph too much. If the glyph is too small to
// clip then we'll overlap rather than have a gap.
nscoord height = mBmData[i].ascent + mBmData[i].descent;
if (height * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
if (0 == i) { // top
clipRect.height = end[i] - clipRect.y;
}
else if (2 == i) { // bottom
clipRect.height -= start[i] - clipRect.y;
clipRect.y = start[i];
}
else { // middle
clipRect.y = start[i];
clipRect.height = end[i] - start[i];
}
}
if (!clipRect.IsEmpty()) {
AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
mGlyphs[i]->Draw(Range(mGlyphs[i].get()), gfxPoint(dx, dy), params);
}
}
}
///////////////
// fill the gap between top and middle, and between middle and bottom.
if (!mGlyphs[3]) { // null glue : draw a rule
// figure out the dimensions of the rule to be drawn :
// set lbearing to rightmost lbearing among the two current successive
// parts.
// set rbearing to leftmost rbearing among the two current successive parts.
// this not only satisfies the convention used for over/underbraces
// in TeX, but also takes care of broken fonts like the stretchy integral
// in Symbol for small font sizes in unix.
nscoord lbearing, rbearing;
int32_t first = 0, last = 1;
while (last <= 2) {
if (mGlyphs[last]) {
lbearing = mBmData[last].leftBearing;
rbearing = mBmData[last].rightBearing;
if (mGlyphs[first]) {
if (lbearing < mBmData[first].leftBearing)
lbearing = mBmData[first].leftBearing;
if (rbearing > mBmData[first].rightBearing)
rbearing = mBmData[first].rightBearing;
}
}
else if (mGlyphs[first]) {
lbearing = mBmData[first].leftBearing;
rbearing = mBmData[first].rightBearing;
}
else {
NS_ERROR("Cannot stretch - All parts missing");
return NS_ERROR_UNEXPECTED;
}
// paint the rule between the parts
nsRect rule(aRect.x + lbearing, end[first],
rbearing - lbearing, start[last] - end[first]);
PaintRule(aDrawTarget, oneDevPixel, rule, aColor);
first = last;
last++;
}
}
else if (mBmData[3].ascent + mBmData[3].descent > 0) {
// glue is present
nsBoundingMetrics& bm = mBmData[3];
// Ensure the stride for the glue is not reduced to less than one pixel
if (bm.ascent + bm.descent >= 3 * oneDevPixel) {
// To protect against gaps, pretend the glue is smaller than it is,
// in order to trim off ends and thus get a solid edge for the join.
bm.ascent -= oneDevPixel;
bm.descent -= oneDevPixel;
}
nsRect clipRect = unionRect;
for (i = 0; i < 2; ++i) {
// Make sure not to draw outside the character
nscoord dy = std::max(end[i], aRect.y);
nscoord fillEnd = std::min(start[i+1], aRect.YMost());
while (dy < fillEnd) {
clipRect.y = dy;
clipRect.height = std::min(bm.ascent + bm.descent, fillEnd - dy);
AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
dy += bm.ascent;
mGlyphs[3]->Draw(Range(mGlyphs[3].get()), gfxPoint(dx, dy), params);
dy += bm.descent;
}
}
}
#ifdef DEBUG
else {
for (i = 0; i < 2; ++i) {
NS_ASSERTION(end[i] >= start[i+1],
"gap between parts with missing glue glyph");
}
}
#endif
return NS_OK;
}
// paint a stretchy char by assembling glyphs horizontally
nsresult
nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext,
gfxContext* aThebesContext,
nsRect& aRect,
nscolor aColor)
{
DrawTarget& aDrawTarget = *aThebesContext->GetDrawTarget();
// Get the device pixel size in the horizontal direction.
// (This makes no effort to optimize for non-translation transformations.)
nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
// get metrics data to be re-used later
int32_t i = 0;
nscoord dy = aRect.y + mBoundingMetrics.ascent;
nscoord offset[3], start[3], end[3];
for (i = 0; i <= 2; ++i) {
const nsBoundingMetrics& bm = mBmData[i];
nscoord dx;
if (0 == i) { // left
dx = aRect.x - bm.leftBearing;
}
else if (2 == i) { // right
dx = aRect.x + aRect.width - bm.rightBearing;
}
else { // middle
dx = aRect.x + (aRect.width - bm.width)/2;
}
// _cairo_scaled_font_show_glyphs snaps origins to device pixels.
// Do this now so that we can get the other dimensions right.
// (This may not achieve much with non-rectangular transformations.)
dx = SnapToDevPixels(aThebesContext, oneDevPixel, nsPoint(dx, dy)).x;
// abcissa passed to Draw
offset[i] = dx;
// _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
// pixel, so the bm values can include 1 row of faint pixels on each edge.
// Don't rely on this pixel as it can look like a gap.
if (bm.rightBearing - bm.leftBearing >= 2 * oneDevPixel) {
start[i] = dx + bm.leftBearing + oneDevPixel; // left join
end[i] = dx + bm.rightBearing - oneDevPixel; // right join
} else {
// To avoid overlaps, we don't add one pixel on each side when the part
// is too small.
start[i] = dx + bm.leftBearing; // left join
end[i] = dx + bm.rightBearing; // right join
}
}
// If there are overlaps, then join at the mid point
for (i = 0; i < 2; ++i) {
if (end[i] > start[i+1]) {
end[i] = (end[i] + start[i+1]) / 2;
start[i+1] = end[i];
}
}
nsRect unionRect = aRect;
unionRect.Inflate(oneDevPixel, oneDevPixel);
gfxTextRun::DrawParams params(aThebesContext);
///////////////////////////
// draw left, middle, right
for (i = 0; i <= 2; ++i) {
// glue can be null
if (mGlyphs[i]) {
nscoord dx = offset[i];
nsRect clipRect = unionRect;
// Clip at the join to get a solid edge (without overlap or gap), when
// this won't change the glyph too much. If the glyph is too small to
// clip then we'll overlap rather than have a gap.
nscoord width = mBmData[i].rightBearing - mBmData[i].leftBearing;
if (width * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
if (0 == i) { // left
clipRect.width = end[i] - clipRect.x;
}
else if (2 == i) { // right
clipRect.width -= start[i] - clipRect.x;
clipRect.x = start[i];
}
else { // middle
clipRect.x = start[i];
clipRect.width = end[i] - start[i];
}
}
if (!clipRect.IsEmpty()) {
AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
mGlyphs[i]->Draw(Range(mGlyphs[i].get()), gfxPoint(dx, dy), params);
}
}
}
////////////////
// fill the gap between left and middle, and between middle and right.
if (!mGlyphs[3]) { // null glue : draw a rule
// figure out the dimensions of the rule to be drawn :
// set ascent to lowest ascent among the two current successive parts.
// set descent to highest descent among the two current successive parts.
// this satisfies the convention used for over/underbraces, and helps
// fix broken fonts.
nscoord ascent, descent;
int32_t first = 0, last = 1;
while (last <= 2) {
if (mGlyphs[last]) {
ascent = mBmData[last].ascent;
descent = mBmData[last].descent;
if (mGlyphs[first]) {
if (ascent > mBmData[first].ascent)
ascent = mBmData[first].ascent;
if (descent > mBmData[first].descent)
descent = mBmData[first].descent;
}
}
else if (mGlyphs[first]) {
ascent = mBmData[first].ascent;
descent = mBmData[first].descent;
}
else {
NS_ERROR("Cannot stretch - All parts missing");
return NS_ERROR_UNEXPECTED;
}
// paint the rule between the parts
nsRect rule(end[first], dy - ascent,
start[last] - end[first], ascent + descent);
PaintRule(aDrawTarget, oneDevPixel, rule, aColor);
first = last;
last++;
}
}
else if (mBmData[3].rightBearing - mBmData[3].leftBearing > 0) {
// glue is present
nsBoundingMetrics& bm = mBmData[3];
// Ensure the stride for the glue is not reduced to less than one pixel
if (bm.rightBearing - bm.leftBearing >= 3 * oneDevPixel) {
// To protect against gaps, pretend the glue is smaller than it is,
// in order to trim off ends and thus get a solid edge for the join.
bm.leftBearing += oneDevPixel;
bm.rightBearing -= oneDevPixel;
}
nsRect clipRect = unionRect;
for (i = 0; i < 2; ++i) {
// Make sure not to draw outside the character
nscoord dx = std::max(end[i], aRect.x);
nscoord fillEnd = std::min(start[i+1], aRect.XMost());
while (dx < fillEnd) {
clipRect.x = dx;
clipRect.width = std::min(bm.rightBearing - bm.leftBearing, fillEnd - dx);
AutoPushClipRect clip(aThebesContext, oneDevPixel, clipRect);
dx -= bm.leftBearing;
mGlyphs[3]->Draw(Range(mGlyphs[3].get()), gfxPoint(dx, dy), params);
dx += bm.rightBearing;
}
}
}
#ifdef DEBUG
else { // no glue
for (i = 0; i < 2; ++i) {
NS_ASSERTION(end[i] >= start[i+1],
"gap between parts with missing glue glyph");
}
}
#endif
return NS_OK;
}