Bug 867089 - Validate the playbackRate before using it. r=ehsan
[gecko.git] / layout / svg / nsSVGTextFrame2.h
blobd4f02c7947946926b1cf527ce4c08e9a5dcfbf00
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef NS_SVGTEXTFRAME2_H
7 #define NS_SVGTEXTFRAME2_H
9 #include "gfxFont.h"
10 #include "gfxMatrix.h"
11 #include "gfxRect.h"
12 #include "gfxSVGGlyphs.h"
13 #include "nsStubMutationObserver.h"
14 #include "nsSVGGlyphFrame.h" // for SVGTextObjectPaint
15 #include "nsSVGTextContainerFrame.h"
17 class nsDisplaySVGText;
18 class nsRenderingContext;
19 class nsSVGTextFrame2;
20 class nsTextFrame;
22 typedef nsSVGDisplayContainerFrame nsSVGTextFrame2Base;
24 namespace mozilla {
26 class TextFrameIterator;
27 class TextNodeCorrespondenceRecorder;
28 struct TextRenderedRun;
29 class TextRenderedRunIterator;
31 namespace dom {
32 class SVGIRect;
35 /**
36 * Information about the positioning for a single character in an SVG <text>
37 * element.
39 * During SVG text layout, we use infinity values to represent positions and
40 * rotations that are not explicitly specified with x/y/rotate attributes.
42 struct CharPosition
44 CharPosition()
45 : mAngle(0),
46 mHidden(false),
47 mUnaddressable(false),
48 mClusterOrLigatureGroupMiddle(false),
49 mRunBoundary(false),
50 mStartOfChunk(false)
54 CharPosition(gfxPoint aPosition, double aAngle)
55 : mPosition(aPosition),
56 mAngle(aAngle),
57 mHidden(false),
58 mUnaddressable(false),
59 mClusterOrLigatureGroupMiddle(false),
60 mRunBoundary(false),
61 mStartOfChunk(false)
65 static CharPosition Unspecified(bool aUnaddressable)
67 CharPosition cp(UnspecifiedPoint(), UnspecifiedAngle());
68 cp.mUnaddressable = aUnaddressable;
69 return cp;
72 bool IsAngleSpecified() const
74 return mAngle != UnspecifiedAngle();
77 bool IsXSpecified() const
79 return mPosition.x != UnspecifiedCoord();
82 bool IsYSpecified() const
84 return mPosition.y != UnspecifiedCoord();
87 gfxPoint mPosition;
88 double mAngle;
90 // not displayed due to falling off the end of a <textPath>
91 bool mHidden;
93 // skipped in positioning attributes due to being collapsed-away white space
94 bool mUnaddressable;
96 // a preceding character is what positioning attributes address
97 bool mClusterOrLigatureGroupMiddle;
99 // rendering is split here since an explicit position or rotation was given
100 bool mRunBoundary;
102 // an anchored chunk begins here
103 bool mStartOfChunk;
105 private:
106 static gfxFloat UnspecifiedCoord()
108 return std::numeric_limits<gfxFloat>::infinity();
111 static double UnspecifiedAngle()
113 return std::numeric_limits<double>::infinity();
116 static gfxPoint UnspecifiedPoint()
118 return gfxPoint(UnspecifiedCoord(), UnspecifiedCoord());
123 * A runnable to mark glyph positions as needing to be recomputed
124 * and to invalid the bounds of the nsSVGTextFrame2 frame.
126 class GlyphMetricsUpdater : public nsRunnable {
127 public:
128 NS_DECL_NSIRUNNABLE
129 GlyphMetricsUpdater(nsSVGTextFrame2* aFrame) : mFrame(aFrame) { }
130 static void Run(nsSVGTextFrame2* aFrame);
131 void Revoke() { mFrame = nullptr; }
132 private:
133 nsSVGTextFrame2* mFrame;
139 * Frame class for SVG <text> elements, used when the
140 * layout.svg.css-text.enabled is true.
142 * An nsSVGTextFrame2 manages SVG text layout, painting and interaction for
143 * all descendent text content elements. The frame tree will look like this:
145 * nsSVGTextFrame2 -- for <text>
146 * <anonymous block frame>
147 * ns{Block,Inline,Text}Frames -- for text nodes, <tspan>s, <a>s, etc.
149 * SVG text layout is done by:
151 * 1. Reflowing the anonymous block frame.
152 * 2. Inspecting the (app unit) positions of the glyph for each character in
153 * the nsTextFrames underneath the anonymous block frame.
154 * 3. Determining the (user unit) positions for each character in the <text>
155 * using the x/y/dx/dy/rotate attributes on all the text content elements,
156 * and using the step 2 results to fill in any gaps.
157 * 4. Applying any other SVG specific text layout (anchoring and text paths)
158 * to the positions computed in step 3.
160 * Rendering of the text is done by splitting up each nsTextFrame into ranges
161 * that can be contiguously painted. (For example <text x="10 20">abcd</text>
162 * would have two contiguous ranges: one for the "a" and one for the "bcd".)
163 * Each range is called a "text rendered run", represented by a TextRenderedRun
164 * object. The TextRenderedRunIterator class performs that splitting and
165 * returns a TextRenderedRun for each bit of text to be painted separately.
167 * Each rendered run is painted by calling nsTextFrame::PaintText. If the text
168 * formatting is simple enough (solid fill, no stroking, etc.), PaintText will
169 * itself do the painting. Otherwise, a DrawPathCallback is passed to
170 * PaintText so that we can fill the text geometry with SVG paint servers.
172 class nsSVGTextFrame2 : public nsSVGTextFrame2Base
174 friend nsIFrame*
175 NS_NewSVGTextFrame2(nsIPresShell* aPresShell, nsStyleContext* aContext);
177 friend class mozilla::GlyphMetricsUpdater;
178 friend class mozilla::TextFrameIterator;
179 friend class mozilla::TextNodeCorrespondenceRecorder;
180 friend struct mozilla::TextRenderedRun;
181 friend class mozilla::TextRenderedRunIterator;
182 friend class AutoCanvasTMForMarker;
183 friend class MutationObserver;
184 friend class nsDisplaySVGText;
186 protected:
187 nsSVGTextFrame2(nsStyleContext* aContext)
188 : nsSVGTextFrame2Base(aContext),
189 mFontSizeScaleFactor(1.0f),
190 mGetCanvasTMForFlag(FOR_OUTERSVG_TM),
191 mPositioningDirty(true)
195 public:
196 NS_DECL_QUERYFRAME_TARGET(nsSVGTextFrame2)
197 NS_DECL_QUERYFRAME
198 NS_DECL_FRAMEARENA_HELPERS
200 // nsIFrame:
201 virtual void Init(nsIContent* aContent,
202 nsIFrame* aParent,
203 nsIFrame* aPrevInFlow) MOZ_OVERRIDE;
205 virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE;
207 NS_IMETHOD AttributeChanged(int32_t aNamespaceID,
208 nsIAtom* aAttribute,
209 int32_t aModType);
211 virtual nsIFrame* GetContentInsertionFrame()
213 return GetFirstPrincipalChild()->GetContentInsertionFrame();
216 NS_IMETHOD Reflow(nsPresContext* aPresContext,
217 nsHTMLReflowMetrics& aDesiredSize,
218 const nsHTMLReflowState& aReflowState,
219 nsReflowStatus& aStatus);
221 virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
222 const nsRect& aDirtyRect,
223 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
226 * Get the "type" of the frame
228 * @see nsGkAtoms::svgTextFrame2
230 virtual nsIAtom* GetType() const;
232 #ifdef DEBUG
233 NS_IMETHOD GetFrameName(nsAString& aResult) const
235 return MakeFrameName(NS_LITERAL_STRING("SVGText2"), aResult);
237 #endif
240 * Finds the nsTextFrame for the closest rendered run to the specified point.
242 virtual void FindCloserFrameForSelection(nsPoint aPoint,
243 FrameWithDistance* aCurrentBestFrame);
246 // nsISVGChildFrame interface:
247 virtual void NotifySVGChanged(uint32_t aFlags);
248 NS_IMETHOD PaintSVG(nsRenderingContext* aContext,
249 const nsIntRect* aDirtyRect);
250 NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint& aPoint);
251 virtual void ReflowSVG();
252 NS_IMETHOD_(nsRect) GetCoveredRegion();
253 virtual SVGBBox GetBBoxContribution(const gfxMatrix& aToBBoxUserspace,
254 uint32_t aFlags);
256 // nsSVGContainerFrame methods:
257 virtual gfxMatrix GetCanvasTM(uint32_t aFor);
259 // SVG DOM text methods:
260 uint32_t GetNumberOfChars(nsIContent* aContent);
261 float GetComputedTextLength(nsIContent* aContent);
262 nsresult SelectSubString(nsIContent* aContent, uint32_t charnum, uint32_t nchars);
263 nsresult GetSubStringLength(nsIContent* aContent, uint32_t charnum,
264 uint32_t nchars, float* aResult);
265 int32_t GetCharNumAtPosition(nsIContent* aContent, mozilla::nsISVGPoint* point);
267 nsresult GetStartPositionOfChar(nsIContent* aContent, uint32_t aCharNum,
268 mozilla::nsISVGPoint** aResult);
269 nsresult GetEndPositionOfChar(nsIContent* aContent, uint32_t aCharNum,
270 mozilla::nsISVGPoint** aResult);
271 nsresult GetExtentOfChar(nsIContent* aContent, uint32_t aCharNum,
272 mozilla::dom::SVGIRect** aResult);
273 nsresult GetRotationOfChar(nsIContent* aContent, uint32_t aCharNum,
274 float* aResult);
276 // nsSVGTextFrame2 methods:
279 * Schedules mPositions to be recomputed and the covered region to be
280 * updated. The aFlags argument can take the ePositioningDirtyDueToMutation
281 * value to indicate that glyph metrics need to be recomputed due to
282 * a DOM mutation in the <text> element on one of its descendants.
284 void NotifyGlyphMetricsChange(uint32_t aFlags = 0);
287 * Enum for NotifyGlyphMetricsChange's aFlags argument.
289 enum { ePositioningDirtyDueToMutation = 1 };
292 * Updates the mFontSizeScaleFactor value by looking at the range of
293 * font-sizes used within the <text>.
295 void UpdateFontSizeScaleFactor(bool aForceGlobalTransform);
297 double GetFontSizeScaleFactor() const;
300 * Takes a point from the <text> element's user space and
301 * converts it to the appropriate frame user space of aChildFrame,
302 * according to which rendered run the point hits.
304 gfxPoint TransformFramePointToTextChild(const gfxPoint& aPoint,
305 nsIFrame* aChildFrame);
308 * Takes a rectangle, aRect, in the <text> element's user space, and
309 * returns a rectangle in aChildFrame's frame user space that
310 * covers intersections of aRect with each rendered run for text frames
311 * within aChildFrame.
313 gfxRect TransformFrameRectToTextChild(const gfxRect& aRect,
314 nsIFrame* aChildFrame);
317 * Takes an app unit rectangle in the coordinate space of a given descendant
318 * frame of this frame, and returns a rectangle in the <text> element's user
319 * space that covers all parts of rendered runs that intersect with the
320 * rectangle.
322 gfxRect TransformFrameRectFromTextChild(const nsRect& aRect,
323 nsIFrame* aChildFrame);
325 private:
327 * This class exists purely because it would be too messy to pass the "for"
328 * flag for GetCanvasTM through the call chains to the GetCanvasTM() call in
329 * UpdateFontSizeScaleFactor.
331 class AutoCanvasTMForMarker {
332 public:
333 AutoCanvasTMForMarker(nsSVGTextFrame2* aFrame, uint32_t aFor)
334 : mFrame(aFrame)
336 mOldFor = mFrame->mGetCanvasTMForFlag;
337 mFrame->mGetCanvasTMForFlag = aFor;
339 ~AutoCanvasTMForMarker()
341 // Default
342 mFrame->mGetCanvasTMForFlag = mOldFor;
344 private:
345 nsSVGTextFrame2* mFrame;
346 uint32_t mOldFor;
350 * Mutation observer used to watch for text positioning attribute changes
351 * on descendent text content elements (like <tspan>s).
353 class MutationObserver : public nsStubMutationObserver {
354 public:
355 MutationObserver()
356 : mFrame(nullptr)
360 void StartObserving(nsSVGTextFrame2* aFrame)
362 NS_ASSERTION(!mFrame, "should not be observing yet!");
363 mFrame = aFrame;
364 aFrame->GetContent()->AddMutationObserver(this);
367 virtual ~MutationObserver()
369 if (mFrame) {
370 mFrame->GetContent()->RemoveMutationObserver(this);
374 // nsISupports
375 NS_DECL_ISUPPORTS
377 // nsIMutationObserver
378 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
379 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
380 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
381 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
383 private:
384 nsSVGTextFrame2* mFrame;
388 * Reflows the anonymous block child.
390 void DoReflow(bool aForceGlobalTransform);
393 * Calls FrameNeedsReflow on the anonymous block child.
395 void RequestReflow(nsIPresShell::IntrinsicDirty aType, uint32_t aBit);
398 * Reflows the anonymous block child and recomputes mPositions if needed.
400 * @param aForceGlobalTransform passed down to UpdateFontSizeScaleFactor to
401 * control whether it should use the global transform even when
402 * NS_STATE_NONDISPLAY_CHILD
404 void UpdateGlyphPositioning(bool aForceGlobalTransform);
407 * Populates mPositions with positioning information for each character
408 * within the <text>.
410 void DoGlyphPositioning();
413 * Converts the specified index into mPositions to an addressable
414 * character index (as can be used with the SVG DOM text methods)
415 * relative to the specified text child content element.
417 * @param aIndex The global character index.
418 * @param aContent The descendant text child content element that
419 * the returned addressable index will be relative to; null
420 * means the same as the <text> element.
421 * @return The addressable index, or -1 if the index cannot be
422 * represented as an addressable index relative to aContent.
424 int32_t
425 ConvertTextElementCharIndexToAddressableIndex(int32_t aIndex,
426 nsIContent* aContent);
429 * Recursive helper for ResolvePositions below.
431 * @param aContent The current node.
432 * @param aIndex The current character index.
433 * @param aInTextPath Whether we are currently under a <textPath> element.
434 * @param aForceStartOfChunk Whether the next character we find should start a
435 * new anchored chunk.
436 * @return The character index we got up to.
438 uint32_t ResolvePositions(nsIContent* aContent, uint32_t aIndex,
439 bool aInTextPath, bool& aForceStartOfChunk,
440 nsTArray<gfxPoint>& aDeltas);
443 * Initializes mPositions with character position information based on
444 * x/y/rotate attributes, leaving unspecified values in the array if a position
445 * was not given for that character. Also fills aDeltas with values based on
446 * dx/dy attributes.
448 * @return True if we recorded any positions.
450 bool ResolvePositions(nsTArray<gfxPoint>& aDeltas);
453 * Determines the position, in app units, of each character in the <text> as
454 * laid out by reflow, and appends them to aPositions. Any characters that
455 * are undisplayed or trimmed away just get the last position.
457 void DetermineCharPositions(nsTArray<nsPoint>& aPositions);
460 * Sets mStartOfChunk to true for each character in mPositions that starts a
461 * line of text.
463 void AdjustChunksForLineBreaks();
466 * Adjusts recorded character positions in mPositions to account for glyph
467 * boundaries. Four things are done:
469 * 1. mClusterOrLigatureGroupMiddle is set to true for all such characters.
471 * 2. Any run and anchored chunk boundaries that begin in the middle of a
472 * cluster/ligature group get moved to the start of the next
473 * cluster/ligature group.
475 * 3. The position of any character in the middle of a cluster/ligature
476 * group is updated to take into account partial ligatures and any
477 * rotation the glyph as a whole has. (The values that come out of
478 * DetermineCharPositions which then get written into mPositions in
479 * ResolvePositions store the same position value for each part of the
480 * ligature.)
482 * 4. The rotation of any character in the middle of a cluster/ligature
483 * group is set to the rotation of the first character.
485 void AdjustPositionsForClusters();
488 * Updates the character positions stored in mPositions to account for
489 * text anchoring.
491 void DoAnchoring();
494 * Updates character positions in mPositions for those characters inside a
495 * <textPath>.
497 void DoTextPathLayout();
500 * Returns whether we need to render the text using
501 * nsTextFrame::DrawPathCallbacks rather than directly painting
502 * the text frames.
504 * @param aShouldPaintSVGGlyphs (out) Whether SVG glyphs in the text
505 * should be painted.
507 bool ShouldRenderAsPath(nsRenderingContext* aContext, nsTextFrame* aFrame,
508 bool& aShouldPaintSVGGlyphs);
510 // Methods to get information for a <textPath> frame.
511 nsIFrame* GetTextPathPathFrame(nsIFrame* aTextPathFrame);
512 already_AddRefed<gfxFlattenedPath> GetFlattenedTextPath(nsIFrame* aTextPathFrame);
513 gfxFloat GetOffsetScale(nsIFrame* aTextPathFrame);
514 gfxFloat GetStartOffset(nsIFrame* aTextPathFrame);
516 gfxFont::DrawMode SetupCairoState(gfxContext* aContext,
517 nsIFrame* aFrame,
518 gfxTextObjectPaint* aOuterObjectPaint,
519 gfxTextObjectPaint** aThisObjectPaint);
522 * Sets up the stroke style for |aFrame| in |aContext| and stores stroke
523 * pattern information in |aThisObjectPaint|.
525 bool SetupCairoStroke(gfxContext* aContext,
526 nsIFrame* aFrame,
527 gfxTextObjectPaint* aOuterObjectPaint,
528 SVGTextObjectPaint* aThisObjectPaint);
531 * Sets up the fill style for |aFrame| in |aContext| and stores fill pattern
532 * information in |aThisObjectPaint|.
534 bool SetupCairoFill(gfxContext* aContext,
535 nsIFrame* aFrame,
536 gfxTextObjectPaint* aOuterObjectPaint,
537 SVGTextObjectPaint* aThisObjectPaint);
540 * Sets the current pattern for |aFrame| to the fill or stroke style of the
541 * outer text object. Will also set the paint opacity to transparent if the
542 * paint is set to "none".
544 bool SetupObjectPaint(gfxContext* aContext,
545 nsIFrame* aFrame,
546 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
547 float& aOpacity,
548 gfxTextObjectPaint* aObjectPaint);
551 * Stores in |aTargetPaint| information on how to reconstruct the current
552 * fill or stroke pattern. Will also set the paint opacity to transparent if
553 * the paint is set to "none".
554 * @param aOuterObjectPaint pattern information from the outer text object
555 * @param aTargetPaint where to store the current pattern information
556 * @param aFillOrStroke member pointer to the paint we are setting up
557 * @param aProperty the frame property descriptor of the fill or stroke paint
558 * server frame
560 void SetupInheritablePaint(gfxContext* aContext,
561 nsIFrame* aFrame,
562 float& aOpacity,
563 gfxTextObjectPaint* aOuterObjectPaint,
564 SVGTextObjectPaint::Paint& aTargetPaint,
565 nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
566 const FramePropertyDescriptor* aProperty);
569 * The MutationObserver we have registered for the <text> element subtree.
571 MutationObserver mMutationObserver;
574 * The runnable we have dispatched to perform the work of
575 * NotifyGlyphMetricsChange.
577 nsRefPtr<GlyphMetricsUpdater> mGlyphMetricsUpdater;
580 * Cached canvasTM value.
582 nsAutoPtr<gfxMatrix> mCanvasTM;
585 * The number of characters in the DOM after the final nsTextFrame. For
586 * example, with
588 * <text>abcd<tspan display="none">ef</tspan></text>
590 * mTrailingUndisplayedCharacters would be 2.
592 uint32_t mTrailingUndisplayedCharacters;
595 * Computed position information for each DOM character within the <text>.
597 nsTArray<mozilla::CharPosition> mPositions;
600 * mFontSizeScaleFactor is used to cause the nsTextFrames to create text
601 * runs with a font size different from the actual font-size property value.
602 * This is used so that, for example with:
604 * <svg>
605 * <g transform="scale(2)">
606 * <text font-size="10">abc</text>
607 * </g>
608 * </svg>
610 * a font size of 20 would be used. It's preferable to use a font size that
611 * is identical or close to the size that the text will appear on the screen,
612 * because at very small or large font sizes, text metrics will be computed
613 * differently due to the limited precision that text runs have.
615 * mFontSizeScaleFactor is the amount the actual font-size property value
616 * should be multiplied by to cause the text run font size to (a) be within a
617 * "reasonable" range, and (b) be close to the actual size to be painted on
618 * screen. (The "reasonable" range as determined by some #defines in
619 * nsSVGTextFrame2.cpp is 8..200.)
621 float mFontSizeScaleFactor;
624 * The flag to pass to GetCanvasTM from UpdateFontSizeScaleFactor. This is
625 * normally FOR_OUTERSVG_TM, but while painting or hit testing a pattern or
626 * marker, we set it to FOR_PAINTING or FOR_HIT_TESTING appropriately.
628 uint32_t mGetCanvasTMForFlag;
631 * The NS_FRAME_IS_DIRTY and NS_FRAME_HAS_DIRTY_CHILDREN bits indicate
632 * that our anonymous block child needs to be reflowed, and that mPositions
633 * will likely need to be updated as a consequence. These are set, for
634 * example, when the font-family changes. Sometimes we only need to
635 * update mPositions though. For example if the x/y attributes change.
636 * mPositioningDirty is used to indicate this latter "things are dirty" case
637 * to allow us to avoid reflowing the anonymous block when it is not
638 * necessary.
640 bool mPositioningDirty;
643 #endif