Line data Source code
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/. */
6 :
7 : #ifndef mozilla_ContentEventHandler_h_
8 : #define mozilla_ContentEventHandler_h_
9 :
10 : #include "mozilla/EventForwards.h"
11 : #include "mozilla/dom/Selection.h"
12 : #include "nsCOMPtr.h"
13 : #include "nsIFrame.h"
14 : #include "nsINode.h"
15 : #include "nsISelectionController.h"
16 : #include "nsRange.h"
17 :
18 : class nsPresContext;
19 :
20 : struct nsRect;
21 :
22 : namespace mozilla {
23 :
24 : enum LineBreakType
25 : {
26 : LINE_BREAK_TYPE_NATIVE,
27 : LINE_BREAK_TYPE_XP
28 : };
29 :
30 : /*
31 : * Query Content Event Handler
32 : * ContentEventHandler is a helper class for EventStateManager.
33 : * The platforms request some content informations, e.g., the selected text,
34 : * the offset of the selected text and the text for specified range.
35 : * This class answers to NS_QUERY_* events from actual contents.
36 : */
37 :
38 0 : class MOZ_STACK_CLASS ContentEventHandler
39 : {
40 : public:
41 : typedef dom::Selection Selection;
42 :
43 : explicit ContentEventHandler(nsPresContext* aPresContext);
44 :
45 : // Handle aEvent in the current process.
46 : nsresult HandleQueryContentEvent(WidgetQueryContentEvent* aEvent);
47 :
48 : // eQuerySelectedText event handler
49 : nsresult OnQuerySelectedText(WidgetQueryContentEvent* aEvent);
50 : // eQueryTextContent event handler
51 : nsresult OnQueryTextContent(WidgetQueryContentEvent* aEvent);
52 : // eQueryCaretRect event handler
53 : nsresult OnQueryCaretRect(WidgetQueryContentEvent* aEvent);
54 : // eQueryTextRect event handler
55 : nsresult OnQueryTextRect(WidgetQueryContentEvent* aEvent);
56 : // eQueryTextRectArray event handler
57 : nsresult OnQueryTextRectArray(WidgetQueryContentEvent* aEvent);
58 : // eQueryEditorRect event handler
59 : nsresult OnQueryEditorRect(WidgetQueryContentEvent* aEvent);
60 : // eQueryContentState event handler
61 : nsresult OnQueryContentState(WidgetQueryContentEvent* aEvent);
62 : // eQuerySelectionAsTransferable event handler
63 : nsresult OnQuerySelectionAsTransferable(WidgetQueryContentEvent* aEvent);
64 : // eQueryCharacterAtPoint event handler
65 : nsresult OnQueryCharacterAtPoint(WidgetQueryContentEvent* aEvent);
66 : // eQueryDOMWidgetHittest event handler
67 : nsresult OnQueryDOMWidgetHittest(WidgetQueryContentEvent* aEvent);
68 :
69 : // NS_SELECTION_* event
70 : nsresult OnSelectionEvent(WidgetSelectionEvent* aEvent);
71 :
72 : protected:
73 : nsPresContext* mPresContext;
74 : nsCOMPtr<nsIPresShell> mPresShell;
75 : // mSelection is typically normal selection but if OnQuerySelectedText()
76 : // is called, i.e., handling eQuerySelectedText, it's the specified selection
77 : // by WidgetQueryContentEvent::mInput::mSelectionType.
78 : RefPtr<Selection> mSelection;
79 : // mFirstSelectedRange is the first selected range of mSelection. If
80 : // mSelection is normal selection, this must not be nullptr if Init()
81 : // succeed. Otherwise, this may be nullptr if there are no selection
82 : // ranges.
83 : RefPtr<nsRange> mFirstSelectedRange;
84 : nsCOMPtr<nsIContent> mRootContent;
85 :
86 : nsresult Init(WidgetQueryContentEvent* aEvent);
87 : nsresult Init(WidgetSelectionEvent* aEvent);
88 :
89 : nsresult InitBasic();
90 : nsresult InitCommon(SelectionType aSelectionType = SelectionType::eNormal);
91 : /**
92 : * InitRootContent() computes the root content of current focused editor.
93 : *
94 : * @param aNormalSelection This must be a Selection instance whose type is
95 : * SelectionType::eNormal.
96 : */
97 : nsresult InitRootContent(Selection* aNormalSelection);
98 :
99 : public:
100 : // FlatText means the text that is generated from DOM tree. The BR elements
101 : // are replaced to native linefeeds. Other elements are ignored.
102 :
103 : // NodePosition stores a pair of node and offset in the node.
104 : // When mNode is an element and mOffset is 0, the start position means after
105 : // the open tag of mNode.
106 : // This is useful to receive one or more sets of them instead of nsRange.
107 0 : struct NodePosition
108 : {
109 : nsCOMPtr<nsINode> mNode;
110 : int32_t mOffset;
111 : // Only when mNode is an element node and mOffset is 0, mAfterOpenTag is
112 : // referred.
113 : bool mAfterOpenTag;
114 :
115 0 : NodePosition()
116 0 : : mOffset(-1)
117 0 : , mAfterOpenTag(true)
118 : {
119 0 : }
120 :
121 0 : NodePosition(nsINode* aNode, int32_t aOffset)
122 0 : : mNode(aNode)
123 : , mOffset(aOffset)
124 0 : , mAfterOpenTag(true)
125 : {
126 0 : }
127 :
128 0 : explicit NodePosition(const nsIFrame::ContentOffsets& aContentOffsets)
129 0 : : mNode(aContentOffsets.content)
130 0 : , mOffset(aContentOffsets.offset)
131 0 : , mAfterOpenTag(true)
132 : {
133 0 : }
134 :
135 : protected:
136 0 : NodePosition(nsINode* aNode, int32_t aOffset, bool aAfterOpenTag)
137 0 : : mNode(aNode)
138 : , mOffset(aOffset)
139 0 : , mAfterOpenTag(aAfterOpenTag)
140 : {
141 0 : }
142 :
143 : public:
144 0 : bool operator==(const NodePosition& aOther) const
145 : {
146 0 : return mNode == aOther.mNode &&
147 0 : mOffset == aOther.mOffset &&
148 0 : mAfterOpenTag == aOther.mAfterOpenTag;
149 : }
150 :
151 0 : bool IsValid() const
152 : {
153 0 : return mNode && mOffset >= 0;
154 : }
155 0 : bool OffsetIsValid() const
156 : {
157 0 : return IsValid() && static_cast<uint32_t>(mOffset) <= mNode->Length();
158 : }
159 0 : bool IsBeforeOpenTag() const
160 : {
161 0 : return IsValid() && mNode->IsElement() && !mOffset && !mAfterOpenTag;
162 : }
163 0 : bool IsImmediatelyAfterOpenTag() const
164 : {
165 0 : return IsValid() && mNode->IsElement() && !mOffset && mAfterOpenTag;
166 : }
167 0 : nsresult SetToRangeStart(nsRange* aRange) const
168 : {
169 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mNode));
170 0 : return aRange->SetStart(domNode, mOffset);
171 : }
172 0 : nsresult SetToRangeEnd(nsRange* aRange) const
173 : {
174 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mNode));
175 0 : return aRange->SetEnd(domNode, mOffset);
176 : }
177 0 : nsresult SetToRangeEndAfter(nsRange* aRange) const
178 : {
179 0 : nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mNode));
180 0 : return aRange->SetEndAfter(domNode);
181 : }
182 : };
183 :
184 : // NodePositionBefore isn't good name if mNode isn't an element node nor
185 : // mOffset is not 0, though, when mNode is an element node and mOffset is 0,
186 : // this is treated as before the open tag of mNode.
187 0 : struct NodePositionBefore final : public NodePosition
188 : {
189 0 : NodePositionBefore(nsINode* aNode, int32_t aOffset)
190 0 : : NodePosition(aNode, aOffset, false)
191 : {
192 0 : }
193 : };
194 :
195 : // Get the flatten text length in the range.
196 : // @param aStartPosition Start node and offset in the node of the range.
197 : // @param aEndPosition End node and offset in the node of the range.
198 : // @param aRootContent The root content of the editor or document.
199 : // aRootContent won't cause any text including
200 : // line breaks.
201 : // @param aLength The result of the flatten text length of the
202 : // range.
203 : // @param aLineBreakType Whether this computes flatten text length with
204 : // native line breakers on the platform or
205 : // with XP line breaker (\n).
206 : // @param aIsRemovingNode Should be true only when this is called from
207 : // nsIMutationObserver::ContentRemoved().
208 : // When this is true, aStartPosition.mNode should
209 : // be the root node of removing nodes and mOffset
210 : // should be 0 and aEndPosition.mNode should be
211 : // same as aStartPosition.mNode and mOffset should
212 : // be number of the children of mNode.
213 : static nsresult GetFlatTextLengthInRange(const NodePosition& aStartPosition,
214 : const NodePosition& aEndPosition,
215 : nsIContent* aRootContent,
216 : uint32_t* aLength,
217 : LineBreakType aLineBreakType,
218 : bool aIsRemovingNode = false);
219 : // Computes the native text length between aStartOffset and aEndOffset of
220 : // aContent. aContent must be a text node.
221 : static uint32_t GetNativeTextLength(nsIContent* aContent,
222 : uint32_t aStartOffset,
223 : uint32_t aEndOffset);
224 : // Get the native text length of aContent. aContent must be a text node.
225 : static uint32_t GetNativeTextLength(nsIContent* aContent,
226 : uint32_t aMaxLength = UINT32_MAX);
227 : // Get the native text length which is inserted before aContent.
228 : // aContent should be an element.
229 : static uint32_t GetNativeTextLengthBefore(nsIContent* aContent,
230 : nsINode* aRootNode);
231 :
232 : protected:
233 : // Get the text length of aContent. aContent must be a text node.
234 : static uint32_t GetTextLength(nsIContent* aContent,
235 : LineBreakType aLineBreakType,
236 : uint32_t aMaxLength = UINT32_MAX);
237 : // Get the text length of a given range of a content node in
238 : // the given line break type.
239 : static uint32_t GetTextLengthInRange(nsIContent* aContent,
240 : uint32_t aXPStartOffset,
241 : uint32_t aXPEndOffset,
242 : LineBreakType aLineBreakType);
243 : // Get the contents in aContent (meaning all children of aContent) as plain
244 : // text. E.g., specifying mRootContent gets whole text in it.
245 : // Note that the result is not same as .textContent. The result is
246 : // optimized for native IMEs. For example, <br> element and some block
247 : // elements causes "\n" (or "\r\n"), see also ShouldBreakLineBefore().
248 : nsresult GenerateFlatTextContent(nsIContent* aContent,
249 : nsString& aString,
250 : LineBreakType aLineBreakType);
251 : // Get the contents of aRange as plain text.
252 : nsresult GenerateFlatTextContent(nsRange* aRange,
253 : nsString& aString,
254 : LineBreakType aLineBreakType);
255 : // Get offset of start of aRange. Note that the result includes the length
256 : // of line breaker caused by the start of aContent because aRange never
257 : // includes the line breaker caused by its start node.
258 : nsresult GetStartOffset(nsRange* aRange,
259 : uint32_t* aOffset,
260 : LineBreakType aLineBreakType);
261 : // Check if we should insert a line break before aContent.
262 : // This should return false only when aContent is an html element which
263 : // is typically used in a paragraph like <em>.
264 : static bool ShouldBreakLineBefore(nsIContent* aContent,
265 : nsINode* aRootNode);
266 : // Get the line breaker length.
267 : static inline uint32_t GetBRLength(LineBreakType aLineBreakType);
268 : static LineBreakType GetLineBreakType(WidgetQueryContentEvent* aEvent);
269 : static LineBreakType GetLineBreakType(WidgetSelectionEvent* aEvent);
270 : static LineBreakType GetLineBreakType(bool aUseNativeLineBreak);
271 : // Returns focused content (including its descendant documents).
272 : nsIContent* GetFocusedContent();
273 : // Returns true if the content is a plugin host.
274 : bool IsPlugin(nsIContent* aContent);
275 : // QueryContentRect() sets the rect of aContent's frame(s) to aEvent.
276 : nsresult QueryContentRect(nsIContent* aContent,
277 : WidgetQueryContentEvent* aEvent);
278 : // Make the DOM range from the offset of FlatText and the text length.
279 : // If aExpandToClusterBoundaries is true, the start offset and the end one are
280 : // expanded to nearest cluster boundaries.
281 : nsresult SetRangeFromFlatTextOffset(nsRange* aRange,
282 : uint32_t aOffset,
283 : uint32_t aLength,
284 : LineBreakType aLineBreakType,
285 : bool aExpandToClusterBoundaries,
286 : uint32_t* aNewOffset = nullptr,
287 : nsIContent** aLastTextNode = nullptr);
288 : // If the aRange isn't in text node but next to a text node, this method
289 : // modifies it in the text node. Otherwise, not modified.
290 : nsresult AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aCollapsedRange);
291 : // Find the first frame for the range and get the start offset in it.
292 : nsresult GetStartFrameAndOffset(const nsRange* aRange,
293 : nsIFrame*& aFrame,
294 : int32_t& aOffsetInFrame);
295 : // Convert the frame relative offset to be relative to the root frame of the
296 : // root presContext (but still measured in appUnits of aFrame's presContext).
297 : nsresult ConvertToRootRelativeOffset(nsIFrame* aFrame,
298 : nsRect& aRect);
299 : // Expand aXPOffset to the nearest offset in cluster boundary. aForward is
300 : // true, it is expanded to forward.
301 : nsresult ExpandToClusterBoundary(nsIContent* aContent, bool aForward,
302 : uint32_t* aXPOffset);
303 :
304 : typedef nsTArray<mozilla::FontRange> FontRangeArray;
305 : static void AppendFontRanges(FontRangeArray& aFontRanges,
306 : nsIContent* aContent,
307 : int32_t aBaseOffset,
308 : int32_t aXPStartOffset,
309 : int32_t aXPEndOffset,
310 : LineBreakType aLineBreakType);
311 : nsresult GenerateFlatFontRanges(nsRange* aRange,
312 : FontRangeArray& aFontRanges,
313 : uint32_t& aLength,
314 : LineBreakType aLineBreakType);
315 : nsresult QueryTextRectByRange(nsRange* aRange,
316 : LayoutDeviceIntRect& aRect,
317 : WritingMode& aWritingMode);
318 :
319 : // Returns a node and position in the node for computing text rect.
320 : NodePosition GetNodePositionHavingFlatText(const NodePosition& aNodePosition);
321 : NodePosition GetNodePositionHavingFlatText(nsINode* aNode,
322 : int32_t aNodeOffset);
323 :
324 : struct MOZ_STACK_CLASS FrameAndNodeOffset final
325 : {
326 : // mFrame is safe since this can live in only stack class and
327 : // ContentEventHandler doesn't modify layout after
328 : // ContentEventHandler::Init() flushes pending layout. In other words,
329 : // this struct shouldn't be used before calling
330 : // ContentEventHandler::Init().
331 : nsIFrame* mFrame;
332 : // offset in the node of mFrame
333 : int32_t mOffsetInNode;
334 :
335 0 : FrameAndNodeOffset()
336 0 : : mFrame(nullptr)
337 0 : , mOffsetInNode(-1)
338 : {
339 0 : }
340 :
341 0 : FrameAndNodeOffset(nsIFrame* aFrame, int32_t aStartOffsetInNode)
342 0 : : mFrame(aFrame)
343 0 : , mOffsetInNode(aStartOffsetInNode)
344 : {
345 0 : }
346 :
347 0 : nsIFrame* operator->() { return mFrame; }
348 : const nsIFrame* operator->() const { return mFrame; }
349 0 : operator nsIFrame*() { return mFrame; }
350 : operator const nsIFrame*() const { return mFrame; }
351 0 : bool IsValid() const { return mFrame && mOffsetInNode >= 0; }
352 : };
353 : // Get first frame after the start of the given range for computing text rect.
354 : // This returns invalid FrameAndNodeOffset if there is no content which
355 : // should affect to computing text rect in the range. mOffsetInNode is start
356 : // offset in the frame.
357 : FrameAndNodeOffset GetFirstFrameInRangeForTextRect(nsRange* aRange);
358 :
359 : // Get last frame before the end of the given range for computing text rect.
360 : // This returns invalid FrameAndNodeOffset if there is no content which
361 : // should affect to computing text rect in the range. mOffsetInNode is end
362 : // offset in the frame.
363 : FrameAndNodeOffset GetLastFrameInRangeForTextRect(nsRange* aRange);
364 :
365 0 : struct MOZ_STACK_CLASS FrameRelativeRect final
366 : {
367 : // mRect is relative to the mBaseFrame's position.
368 : nsRect mRect;
369 : nsIFrame* mBaseFrame;
370 :
371 0 : FrameRelativeRect()
372 0 : : mBaseFrame(nullptr)
373 : {
374 0 : }
375 :
376 0 : explicit FrameRelativeRect(nsIFrame* aBaseFrame)
377 0 : : mBaseFrame(aBaseFrame)
378 : {
379 0 : }
380 :
381 0 : FrameRelativeRect(const nsRect& aRect, nsIFrame* aBaseFrame)
382 0 : : mRect(aRect)
383 0 : , mBaseFrame(aBaseFrame)
384 : {
385 0 : }
386 :
387 0 : bool IsValid() const { return mBaseFrame != nullptr; }
388 :
389 : // Returns an nsRect relative to aBaseFrame instead of mBaseFrame.
390 : nsRect RectRelativeTo(nsIFrame* aBaseFrame) const;
391 : };
392 :
393 : // Returns a rect for line breaker before the node of aFrame (If aFrame is
394 : // a <br> frame or a block level frame, it causes a line break at its
395 : // element's open tag, see also ShouldBreakLineBefore()). Note that this
396 : // doesn't check if aFrame should cause line break in non-debug build.
397 : FrameRelativeRect GetLineBreakerRectBefore(nsIFrame* aFrame);
398 :
399 : // Returns a line breaker rect after aTextContent as there is a line breaker
400 : // immediately after aTextContent. This is useful when following block
401 : // element causes a line break before it and it needs to compute the line
402 : // breaker's rect. For example, if there is |<p>abc</p><p>def</p>|, the
403 : // rect of 2nd <p>'s line breaker should be at right of "c" in the first
404 : // <p>, not the start of 2nd <p>. The result is relative to the last text
405 : // frame which represents the last character of aTextContent.
406 : FrameRelativeRect GuessLineBreakerRectAfter(nsIContent* aTextContent);
407 :
408 : // Returns a guessed first rect. I.e., it may be different from actual
409 : // caret when selection is collapsed at start of aFrame. For example, this
410 : // guess the caret rect only with the content box of aFrame and its font
411 : // height like:
412 : // +-aFrame----------------- (border box)
413 : // |
414 : // | +--------------------- (content box)
415 : // | | I
416 : // ^ guessed caret rect
417 : // However, actual caret is computed with more information like line-height,
418 : // child frames of aFrame etc. But this does not emulate actual caret
419 : // behavior exactly for simpler and faster code because it's difficult and
420 : // we're not sure it's worthwhile to do it with complicated implementation.
421 : FrameRelativeRect GuessFirstCaretRectIn(nsIFrame* aFrame);
422 :
423 : // Make aRect non-empty. If width and/or height is 0, these methods set them
424 : // to 1. Note that it doesn't set nsRect's width nor height to one device
425 : // pixel because using nsRect::ToOutsidePixels() makes actual width or height
426 : // to 2 pixels because x and y may not be aligned to device pixels.
427 : void EnsureNonEmptyRect(nsRect& aRect) const;
428 : void EnsureNonEmptyRect(LayoutDeviceIntRect& aRect) const;
429 : };
430 :
431 : } // namespace mozilla
432 :
433 : #endif // mozilla_ContentEventHandler_h_
|