1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_ContentEventHandler_h_
8 #define mozilla_ContentEventHandler_h_
11 #include "mozilla/Assertions.h"
12 #include "mozilla/EventForwards.h"
13 #include "mozilla/RangeBoundary.h"
14 #include "mozilla/dom/Selection.h"
15 #include "mozilla/dom/Text.h"
31 enum LineBreakType
{ LINE_BREAK_TYPE_NATIVE
, LINE_BREAK_TYPE_XP
};
34 * Query Content Event Handler
35 * ContentEventHandler is a helper class for EventStateManager.
36 * The platforms request some content informations, e.g., the selected text,
37 * the offset of the selected text and the text for specified range.
38 * This class answers to NS_QUERY_* events from actual contents.
41 class MOZ_STACK_CLASS ContentEventHandler
{
44 * SimpleRangeBase is a helper template class of ContentEventHandler class
45 * that stores 2 DOM points as a range without observing the mutation. I.e.,
46 * similar to dom::StaticRange, but can only be on the stack and does not have
47 * unnecessary features for ContentEventHandler so it is fast.
48 * Therefore, initializers are responsible for making sure the start/end nodes
49 * are in document order. This is enforced by assertions in DEBUG builds.
51 template <typename NodeType
, typename RangeBoundaryType
>
52 class MOZ_STACK_CLASS SimpleRangeBase final
{
55 SimpleRangeBase(SimpleRangeBase
<NodeType
, RangeBoundaryType
>&&) noexcept
;
56 template <typename OtherNodeType
, typename OtherRangeBoundaryType
>
57 explicit SimpleRangeBase(
58 const SimpleRangeBase
<OtherNodeType
, OtherRangeBoundaryType
>& aOther
);
59 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
69 bool IsPositioned() const { return mStart
.IsSet() && mEnd
.IsSet(); }
70 bool Collapsed() const { return mStart
== mEnd
&& IsPositioned(); }
71 nsINode
* GetStartContainer() const { return mStart
.Container(); }
72 nsINode
* GetEndContainer() const { return mEnd
.Container(); }
73 uint32_t StartOffset() const {
74 return *mStart
.Offset(
75 RangeBoundaryType::OffsetFilter::kValidOrInvalidOffsets
);
77 uint32_t EndOffset() const {
79 RangeBoundaryType::OffsetFilter::kValidOrInvalidOffsets
);
81 nsIContent
* StartRef() const { return mStart
.Ref(); }
82 nsIContent
* EndRef() const { return mEnd
.Ref(); }
84 const RangeBoundaryType
& Start() const { return mStart
; }
85 const RangeBoundaryType
& End() const { return mEnd
; }
87 nsINode
* GetRoot() const { return mRoot
; }
89 // XXX: Make these use RangeBoundaries...
90 nsresult
CollapseTo(const RawRangeBoundary
& aBoundary
) {
91 return SetStartAndEnd(aBoundary
, aBoundary
);
93 nsresult
SetStart(const RawRangeBoundary
& aStart
);
94 nsresult
SetEnd(const RawRangeBoundary
& aEnd
);
96 // NOTE: These helpers can hide performance problems, as they perform a
97 // search to find aStartOffset in aStartContainer.
98 nsresult
SetStart(nsINode
* aStartContainer
, uint32_t aStartOffset
) {
99 return SetStart(RawRangeBoundary(aStartContainer
, aStartOffset
));
101 nsresult
SetEnd(nsINode
* aEndContainer
, uint32_t aEndOffset
) {
102 return SetEnd(RawRangeBoundary(aEndContainer
, aEndOffset
));
105 nsresult
SetEndAfter(nsINode
* aEndContainer
);
106 void SetStartAndEnd(const nsRange
* aRange
);
107 nsresult
SetStartAndEnd(const RawRangeBoundary
& aStart
,
108 const RawRangeBoundary
& aEnd
);
110 nsresult
SelectNodeContents(const nsINode
* aNodeToSelectContents
);
113 inline void AssertStartIsBeforeOrEqualToEnd();
117 RangeBoundaryType mStart
;
118 RangeBoundaryType mEnd
;
120 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
121 nsMutationGuard mMutationGuard
;
122 Maybe
<JS::AutoAssertNoGC
> mAssertNoGC
;
126 using SimpleRange
= SimpleRangeBase
<RefPtr
<nsINode
>, RangeBoundary
>;
127 using UnsafeSimpleRange
= SimpleRangeBase
<nsINode
*, RawRangeBoundary
>;
130 using Element
= dom::Element
;
131 using Selection
= dom::Selection
;
133 explicit ContentEventHandler(nsPresContext
* aPresContext
);
135 // Handle aEvent in the current process.
136 MOZ_CAN_RUN_SCRIPT nsresult
137 HandleQueryContentEvent(WidgetQueryContentEvent
* aEvent
);
139 // eQuerySelectedText event handler
140 MOZ_CAN_RUN_SCRIPT nsresult
141 OnQuerySelectedText(WidgetQueryContentEvent
* aEvent
);
142 // eQueryTextContent event handler
143 MOZ_CAN_RUN_SCRIPT nsresult
144 OnQueryTextContent(WidgetQueryContentEvent
* aEvent
);
145 // eQueryCaretRect event handler
146 MOZ_CAN_RUN_SCRIPT nsresult
OnQueryCaretRect(WidgetQueryContentEvent
* aEvent
);
147 // eQueryTextRect event handler
148 MOZ_CAN_RUN_SCRIPT nsresult
OnQueryTextRect(WidgetQueryContentEvent
* aEvent
);
149 // eQueryTextRectArray event handler
150 MOZ_CAN_RUN_SCRIPT nsresult
151 OnQueryTextRectArray(WidgetQueryContentEvent
* aEvent
);
152 // eQueryEditorRect event handler
153 MOZ_CAN_RUN_SCRIPT nsresult
154 OnQueryEditorRect(WidgetQueryContentEvent
* aEvent
);
155 // eQueryContentState event handler
156 MOZ_CAN_RUN_SCRIPT nsresult
157 OnQueryContentState(WidgetQueryContentEvent
* aEvent
);
158 // eQuerySelectionAsTransferable event handler
159 MOZ_CAN_RUN_SCRIPT nsresult
160 OnQuerySelectionAsTransferable(WidgetQueryContentEvent
* aEvent
);
161 // eQueryCharacterAtPoint event handler
162 MOZ_CAN_RUN_SCRIPT nsresult
163 OnQueryCharacterAtPoint(WidgetQueryContentEvent
* aEvent
);
164 // eQueryDOMWidgetHittest event handler
165 MOZ_CAN_RUN_SCRIPT nsresult
166 OnQueryDOMWidgetHittest(WidgetQueryContentEvent
* aEvent
);
168 // NS_SELECTION_* event
169 MOZ_CAN_RUN_SCRIPT nsresult
OnSelectionEvent(WidgetSelectionEvent
* aEvent
);
172 RefPtr
<dom::Document
> mDocument
;
173 // mSelection is typically normal selection but if OnQuerySelectedText()
174 // is called, i.e., handling eQuerySelectedText, it's the specified selection
175 // by WidgetQueryContentEvent::mInput::mSelectionType.
176 RefPtr
<Selection
> mSelection
;
177 // mFirstSelectedSimpleRange is initialized from the first range of
178 // mSelection, if it exists. Otherwise, it is reset by Clear().
179 SimpleRange mFirstSelectedSimpleRange
;
180 RefPtr
<Element
> mRootElement
;
182 MOZ_CAN_RUN_SCRIPT nsresult
Init(WidgetQueryContentEvent
* aEvent
);
183 MOZ_CAN_RUN_SCRIPT nsresult
Init(WidgetSelectionEvent
* aEvent
);
185 nsresult
InitBasic(bool aRequireFlush
= true);
186 MOZ_CAN_RUN_SCRIPT nsresult
187 InitCommon(EventMessage aEventMessage
,
188 SelectionType aSelectionType
= SelectionType::eNormal
,
189 bool aRequireFlush
= true);
191 * InitRootContent() computes the root content of current focused editor.
193 * @param aNormalSelection This must be a Selection instance whose type is
194 * SelectionType::eNormal.
196 MOZ_CAN_RUN_SCRIPT nsresult
197 InitRootContent(const Selection
& aNormalSelection
);
200 // FlatText means the text that is generated from DOM tree. The BR elements
201 // are replaced to native linefeeds. Other elements are ignored.
203 // RawNodePosition stores a pair of node and offset in the node.
204 // When mNode is an element and mOffset is 0, the start position means after
205 // the open tag of mNode.
206 // This is useful to receive one or more sets of them instead of nsRange.
207 // This type is intended to be used for short-lived operations, and is thus
208 // marked MOZ_STACK_CLASS.
209 struct MOZ_STACK_CLASS RawNodePosition
: public RawRangeBoundary
{
210 // Only when mNode is an element node and mOffset is 0, mAfterOpenTag is
212 bool mAfterOpenTag
= true;
214 RawNodePosition() = default;
215 MOZ_IMPLICIT
RawNodePosition(const RawNodePosition
& aOther
)
216 : RawRangeBoundary(aOther
),
217 mAfterOpenTag(aOther
.mAfterOpenTag
)
218 // Don't use the copy constructor of mAssertNoGC.
222 * Factory method returning a RawNodePosition object which points start of
223 * first content of aContainer (first child or first character in the data).
224 * I.e., if aContainer is an element node, the result points before the
225 * first child but after the open tag, e.g., <div>{}abc</div> if aContainer
226 * is the <div>. This is important to understand the difference with the
227 * result of Before().
229 static RawNodePosition
BeforeFirstContentOf(const nsINode
& aContainer
) {
230 return RawNodePosition(const_cast<nsINode
*>(&aContainer
), 0u);
234 * Factory method returning a RawNodePosition object which points after
235 * aContent. I.e., if aContent is an element node, the result points after
236 * its close tag, e.g., `<div>abc</div>{}` if aContent is the <div>.
238 static RawNodePosition
After(const nsIContent
& aContent
) {
239 RawNodePosition
it(aContent
.GetParentNode(),
240 const_cast<nsIContent
*>(&aContent
));
241 it
.mAfterOpenTag
= false;
246 * Factory method returning a RawNodePosition object which points end of
247 * aContainer. If aContainer is an element node, the result points before
248 * its close tag, e.g., `<div>abc{}</div>` if aContainer is the <div>.
250 static RawNodePosition
AtEndOf(const nsINode
& aContainer
) {
251 return RawNodePosition(const_cast<nsINode
*>(&aContainer
),
253 ? aContainer
.AsText()->TextDataLength()
254 : aContainer
.GetChildCount());
258 * Factory method returning a RawNodePosition object which points before
259 * aContent. I.e., if aContent is an element node, the result points
260 * before its open tag, e.g., `{}<div>abc</div>` if aContent is the <div>.
261 * Note that this sets different containers whether aContent is being
262 * removed or not. If aContent is being removed, i.e., this is used in
263 * nsIMutationObserver::ContentRemoved(), aContent is already not a child
264 * of its ex-parent. Therefore, the container becomes aContent, but
265 * indicates that it points before the container with mAfterOpenTag.
266 * On the other hand, if it's not being removed, the container is set to
267 * the parent node of aContent. So, in this case, it points after the
268 * previous sibling of aContent actually.
270 static RawNodePosition
Before(const nsIContent
& aContent
) {
271 if (!aContent
.IsBeingRemoved()) {
272 return RawNodePosition(aContent
.GetParentNode(),
273 aContent
.GetPreviousSibling());
275 RawNodePosition
ret(const_cast<nsIContent
*>(&aContent
), 0u);
276 ret
.mAfterOpenTag
= false;
280 RawNodePosition(nsINode
* aContainer
, uint32_t aOffset
)
281 : RawRangeBoundary(aContainer
, aOffset
) {}
283 RawNodePosition(nsINode
* aContainer
, nsIContent
* aRef
)
284 : RawRangeBoundary(aContainer
, aRef
) {}
286 explicit RawNodePosition(const nsIFrame::ContentOffsets
& aContentOffsets
)
287 : RawRangeBoundary(aContentOffsets
.content
, aContentOffsets
.offset
) {}
289 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
290 ~RawNodePosition() { MOZ_DIAGNOSTIC_ASSERT(!mMutationGuard
.Mutated(0)); }
291 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
294 const RawNodePosition
& operator=(const RawNodePosition
& aOther
) {
295 if (this != &aOther
) {
296 RawRangeBoundary::operator=(aOther
);
297 mAfterOpenTag
= aOther
.mAfterOpenTag
;
302 bool operator==(const RawNodePosition
& aOther
) const {
303 return RawRangeBoundary::operator==(aOther
) &&
304 mAfterOpenTag
== aOther
.mAfterOpenTag
;
307 bool IsBeforeOpenTag() const {
308 return IsSet() && Container()->IsElement() && !Ref() && !mAfterOpenTag
;
310 bool IsImmediatelyAfterOpenTag() const {
311 return IsSet() && Container()->IsElement() && !Ref() && mAfterOpenTag
;
314 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
316 nsMutationGuard mMutationGuard
;
317 JS::AutoAssertNoGC mAssertNoGC
;
318 #endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
322 * Get the flatten text length in the range.
323 * @param aStartPosition Start node and offset in the node of the range.
324 * If the container is an element node, it's
325 * important to start from before or after its open
326 * tag because open tag of some elements causes a
327 * line break in the result. If you need the line
328 * break, you need to use
329 * RawNodePosition::Before().
330 * @param aEndPosition End node and offset in the node of the range.
331 * If you don't want to include line break which is
332 * caused by the open tag of the container when
333 * you specify start of an element node, you need
334 * to use RawNodePosition::Before().
335 * @param aRootElement The root element of the editor or document.
336 * aRootElement won't cause any text including
338 * @param aLength The result of the flatten text length of the
340 * @param aLineBreakType Whether this computes flatten text length with
341 * native line breakers on the platform or
342 * with XP line breaker (\n).
343 * @param aIsRemovingNode Should be true only when this is called from
344 * nsIMutationObserver::ContentRemoved().
345 * When this is true, the container of
346 * aStartPosition should be the removing node and
347 * points start of it and the container of
348 * aEndPosition must be same as the container of
349 * aStartPosition and points end of the container.
351 static nsresult
GetFlatTextLengthInRange(
352 const RawNodePosition
& aStartPosition
,
353 const RawNodePosition
& aEndPosition
, const Element
* aRootElement
,
354 uint32_t* aLength
, LineBreakType aLineBreakType
,
355 bool aIsRemovingNode
= false);
357 // Computes the native text length between aStartOffset and aEndOffset of
359 static uint32_t GetNativeTextLength(const dom::Text
& aTextNode
,
360 uint32_t aStartOffset
,
361 uint32_t aEndOffset
);
362 // Get the native text length of aTextNode.
363 static uint32_t GetNativeTextLength(const dom::Text
& aTextNode
,
364 uint32_t aMaxLength
= UINT32_MAX
);
366 static uint32_t GetNativeTextLength(const nsAString
& aText
);
368 // Get the range between start offset and end offset
370 already_AddRefed
<nsRange
> GetRangeFromFlatTextOffset(
371 WidgetContentCommandEvent
* aEvent
, uint32_t aOffset
, uint32_t aLength
);
373 // Get the contents of aRange as plain text.
374 nsresult
GenerateFlatTextContent(const nsRange
* aRange
, nsString
& aString
);
377 // Get the text length of aTextNode.
378 static uint32_t GetTextLength(const dom::Text
& aTextNode
,
379 LineBreakType aLineBreakType
,
380 uint32_t aMaxLength
= UINT32_MAX
);
381 // Get the text length of a given range of a content node in
382 // the given line break type.
383 static uint32_t GetTextLengthInRange(const dom::Text
& aTextNode
,
384 uint32_t aXPStartOffset
,
385 uint32_t aXPEndOffset
,
386 LineBreakType aLineBreakType
);
387 // Get the contents in aElement (meaning all children of aElement) as plain
388 // text. E.g., specifying mRootElement gets whole text in it.
389 // Note that the result is not same as .textContent. The result is
390 // optimized for native IMEs. For example, <br> element and some block
391 // elements causes "\n" (or "\r\n"), see also ShouldBreakLineBefore().
392 nsresult
GenerateFlatTextContent(const Element
* aElement
, nsString
& aString
,
393 LineBreakType aLineBreakType
);
394 // Get the contents of aRange as plain text.
395 template <typename NodeType
, typename RangeBoundaryType
>
396 nsresult
GenerateFlatTextContent(
397 const SimpleRangeBase
<NodeType
, RangeBoundaryType
>& aSimpleRange
,
398 nsString
& aString
, LineBreakType aLineBreakType
);
399 // Get offset of start of aRange. Note that the result includes the length
400 // of line breaker caused by the start of aContent because aRange never
401 // includes the line breaker caused by its start node.
402 template <typename SimpleRangeType
>
403 nsresult
GetStartOffset(const SimpleRangeType
& aSimpleRange
,
404 uint32_t* aOffset
, LineBreakType aLineBreakType
);
405 // Check if we should insert a line break before aContent.
406 // This should return false only when aContent is an html element which
407 // is typically used in a paragraph like <em>.
408 static bool ShouldBreakLineBefore(const nsIContent
& aContent
,
409 const Element
* aRootElement
);
410 // Get the line breaker length.
411 static inline uint32_t GetBRLength(LineBreakType aLineBreakType
);
412 static LineBreakType
GetLineBreakType(WidgetQueryContentEvent
* aEvent
);
413 static LineBreakType
GetLineBreakType(WidgetSelectionEvent
* aEvent
);
414 static LineBreakType
GetLineBreakType(bool aUseNativeLineBreak
);
415 // Returns focused content (including its descendant documents).
416 nsIContent
* GetFocusedContent();
417 // QueryContentRect() sets the rect of aContent's frame(s) to aEvent.
418 nsresult
QueryContentRect(nsIContent
* aContent
,
419 WidgetQueryContentEvent
* aEvent
);
421 template <typename RangeType
, typename TextNodeType
>
422 struct MOZ_STACK_CLASS DOMRangeAndAdjustedOffsetInFlattenedTextBase
{
423 bool RangeStartsFromLastTextNode() const {
424 return mLastTextNode
&& mRange
.GetStartContainer() == mLastTextNode
;
426 bool RangeStartsFromEndOfContainer() const {
427 return mRange
.GetStartContainer() &&
428 mRange
.GetStartContainer()->Length() == mRange
.StartOffset();
430 bool RangeStartsFromContent() const {
431 return mRange
.GetStartContainer() &&
432 mRange
.GetStartContainer()->IsContent();
435 // The range in the DOM tree.
437 // Actual start offset of the range in the flattened text. If aOffset
438 // of ConvertFlatTextOffsetToDOMRange() is middle of a surrogate pair,
439 // a CRLF or a complex character of some languages, this may be set to
441 uint32_t mAdjustedOffset
= 0;
442 // The last text node which is found while walking the tree.
443 // If the range ends in a text node, this is the text node. Otherwise,
444 // the last found text node before the end container of mRange.
445 TextNodeType mLastTextNode
= nullptr;
447 using DOMRangeAndAdjustedOffsetInFlattenedText
=
448 DOMRangeAndAdjustedOffsetInFlattenedTextBase
<SimpleRange
,
450 using UnsafeDOMRangeAndAdjustedOffsetInFlattenedText
=
451 DOMRangeAndAdjustedOffsetInFlattenedTextBase
<UnsafeSimpleRange
,
455 * Scans the DOM tree and set mRange as same as from aOffset to aOffset +
456 * aLength in the flattened text.
457 * NOTE: Use ConvertFlatTextOffsetToDOMRange() or
458 * ConvertFlatTextOffsetToUnsafeDOMRange() instead of
459 * ConvertFlatTextOffsetToDOMRangeBase<RangeType, TextNodeType>().
461 template <typename RangeType
, typename TextNodeType
>
462 Result
<DOMRangeAndAdjustedOffsetInFlattenedTextBase
<RangeType
, TextNodeType
>,
464 ConvertFlatTextOffsetToDOMRangeBase(uint32_t aOffset
, uint32_t aLength
,
465 LineBreakType aLineBreakType
,
466 bool aExpandToClusterBoundaries
);
467 MOZ_ALWAYS_INLINE Result
<DOMRangeAndAdjustedOffsetInFlattenedText
, nsresult
>
468 ConvertFlatTextOffsetToDOMRange(uint32_t aOffset
, uint32_t aLength
,
469 LineBreakType aLineBreakType
,
470 bool aExpandToClusterBoundaries
) {
471 return ConvertFlatTextOffsetToDOMRangeBase
<SimpleRange
, RefPtr
<dom::Text
>>(
472 aOffset
, aLength
, aLineBreakType
, aExpandToClusterBoundaries
);
475 Result
<UnsafeDOMRangeAndAdjustedOffsetInFlattenedText
, nsresult
>
476 ConvertFlatTextOffsetToUnsafeDOMRange(uint32_t aOffset
, uint32_t aLength
,
477 LineBreakType aLineBreakType
,
478 bool aExpandToClusterBoundaries
) {
479 return ConvertFlatTextOffsetToDOMRangeBase
<UnsafeSimpleRange
, dom::Text
*>(
480 aOffset
, aLength
, aLineBreakType
, aExpandToClusterBoundaries
);
483 // If the aSimpleRange isn't in text node but next to a text node,
484 // this method modifies it in the text node. Otherwise, not modified.
485 // Note that aSimpleRange must be collapsed.
486 nsresult
AdjustCollapsedRangeMaybeIntoTextNode(SimpleRange
& aSimpleRange
);
487 // Convert the frame relative offset to be relative to the root frame of the
488 // root presContext (but still measured in appUnits of aFrame's presContext).
489 nsresult
ConvertToRootRelativeOffset(nsIFrame
* aFrame
, nsRect
& aRect
);
490 // Expand aXPOffset to the nearest offset in cluster boundary. aForward is
491 // true, it is expanded to forward.
492 // FYI: Due to `nsFrameSelection::GetFrameForNodeOffset()`, this cannot
493 // take `const dom::Text&`.
494 nsresult
ExpandToClusterBoundary(dom::Text
& aTextNode
, bool aForward
,
495 uint32_t* aXPOffset
) const;
497 using FontRangeArray
= nsTArray
<mozilla::FontRange
>;
498 static void AppendFontRanges(FontRangeArray
& aFontRanges
,
499 const dom::Text
& aTextNode
, uint32_t aBaseOffset
,
500 uint32_t aXPStartOffset
, uint32_t aXPEndOffset
,
501 LineBreakType aLineBreakType
);
502 nsresult
GenerateFlatFontRanges(const UnsafeSimpleRange
& aSimpleRange
,
503 FontRangeArray
& aFontRanges
,
505 LineBreakType aLineBreakType
);
506 nsresult
QueryTextRectByRange(const SimpleRange
& aSimpleRange
,
507 LayoutDeviceIntRect
& aRect
,
508 WritingMode
& aWritingMode
);
510 struct MOZ_STACK_CLASS FrameAndNodeOffset final
{
511 // mFrame is safe since this can live in only stack class and
512 // ContentEventHandler doesn't modify layout after
513 // ContentEventHandler::Init() flushes pending layout. In other words,
514 // this struct shouldn't be used before calling
515 // ContentEventHandler::Init().
517 // offset in the node of mFrame
518 int32_t mOffsetInNode
;
520 FrameAndNodeOffset() : mFrame(nullptr), mOffsetInNode(-1) {}
522 FrameAndNodeOffset(nsIFrame
* aFrame
, int32_t aStartOffsetInNode
)
523 : mFrame(aFrame
), mOffsetInNode(aStartOffsetInNode
) {}
525 nsIFrame
* operator->() { return mFrame
; }
526 const nsIFrame
* operator->() const { return mFrame
; }
527 operator nsIFrame
*() { return mFrame
; }
528 operator const nsIFrame
*() const { return mFrame
; }
529 bool IsValid() const { return mFrame
&& mOffsetInNode
>= 0; }
531 // Get first frame after the start of the given range for computing text rect.
532 // This returns invalid FrameAndNodeOffset if there is no content which
533 // should affect to computing text rect in the range. mOffsetInNode is start
534 // offset in the frame.
535 template <typename NodeType
, typename RangeBoundaryType
>
536 FrameAndNodeOffset
GetFirstFrameInRangeForTextRect(
537 const SimpleRangeBase
<NodeType
, RangeBoundaryType
>& aSimpleRange
);
539 // Get last frame before the end of the given range for computing text rect.
540 // This returns invalid FrameAndNodeOffset if there is no content which
541 // should affect to computing text rect in the range. mOffsetInNode is end
542 // offset in the frame.
543 template <typename NodeType
, typename RangeBoundaryType
>
544 FrameAndNodeOffset
GetLastFrameInRangeForTextRect(
545 const SimpleRangeBase
<NodeType
, RangeBoundaryType
>& aSimpleRange
);
547 struct MOZ_STACK_CLASS FrameRelativeRect final
{
548 // mRect is relative to the mBaseFrame's position.
550 nsIFrame
* mBaseFrame
;
552 FrameRelativeRect() : mBaseFrame(nullptr) {}
554 explicit FrameRelativeRect(nsIFrame
* aBaseFrame
) : mBaseFrame(aBaseFrame
) {}
556 FrameRelativeRect(const nsRect
& aRect
, nsIFrame
* aBaseFrame
)
557 : mRect(aRect
), mBaseFrame(aBaseFrame
) {}
559 bool IsValid() const { return mBaseFrame
!= nullptr; }
561 // Returns an nsRect relative to aBaseFrame instead of mBaseFrame.
562 nsRect
RectRelativeTo(nsIFrame
* aBaseFrame
) const;
565 // Returns a rect for line breaker before the node of aFrame (If aFrame is
566 // a <br> frame or a block level frame, it causes a line break at its
567 // element's open tag, see also ShouldBreakLineBefore()). Note that this
568 // doesn't check if aFrame should cause line break in non-debug build.
569 FrameRelativeRect
GetLineBreakerRectBefore(nsIFrame
* aFrame
);
571 // Returns a line breaker rect after aTextNode as there is a line breaker
572 // immediately after aTextNode. This is useful when following block
573 // element causes a line break before it and it needs to compute the line
574 // breaker's rect. For example, if there is |<p>abc</p><p>def</p>|, the
575 // rect of 2nd <p>'s line breaker should be at right of "c" in the first
576 // <p>, not the start of 2nd <p>. The result is relative to the last text
577 // frame which represents the last character of aTextNode.
578 FrameRelativeRect
GuessLineBreakerRectAfter(const dom::Text
& aTextNode
);
580 // Returns a guessed first rect. I.e., it may be different from actual
581 // caret when selection is collapsed at start of aFrame. For example, this
582 // guess the caret rect only with the content box of aFrame and its font
584 // +-aFrame----------------- (border box)
586 // | +--------------------- (content box)
588 // ^ guessed caret rect
589 // However, actual caret is computed with more information like line-height,
590 // child frames of aFrame etc. But this does not emulate actual caret
591 // behavior exactly for simpler and faster code because it's difficult and
592 // we're not sure it's worthwhile to do it with complicated implementation.
593 FrameRelativeRect
GuessFirstCaretRectIn(nsIFrame
* aFrame
);
595 // Make aRect non-empty. If width and/or height is 0, these methods set them
596 // to 1. Note that it doesn't set nsRect's width nor height to one device
597 // pixel because using nsRect::ToOutsidePixels() makes actual width or height
598 // to 2 pixels because x and y may not be aligned to device pixels.
599 void EnsureNonEmptyRect(nsRect
& aRect
) const;
600 void EnsureNonEmptyRect(LayoutDeviceIntRect
& aRect
) const;
603 * Compute caret rect before or after a character rect.
605 static LayoutDeviceIntRect
GetCaretRectBefore(
606 const LayoutDeviceIntRect
& aCharRect
, const WritingMode
& aWritingMode
);
607 static LayoutDeviceIntRect
GetCaretRectAfter(
608 const LayoutDeviceIntRect
& aCharRect
, const WritingMode
& aWritingMode
);
609 static nsRect
GetCaretRectBefore(const nsRect
& aCharRect
,
610 const WritingMode
& aWritingMode
);
611 static nsRect
GetCaretRectAfter(nsPresContext
& aPresContext
,
612 const nsRect
& aCharRect
,
613 const WritingMode
& aWritingMode
);
616 } // namespace mozilla
618 #endif // mozilla_ContentEventHandler_h_