1/*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include "AXTextStateChangeIntent.h"
29#include "EditingStyle.h"
30#include "Element.h"
31#include "IntRect.h"
32#include "LayoutRect.h"
33#include "Range.h"
34#include "ScrollAlignment.h"
35#include "Timer.h"
36#include "VisibleSelection.h"
37#include <wtf/Noncopyable.h>
38
39#if PLATFORM(IOS_FAMILY)
40#include "Color.h"
41#endif
42
43namespace WebCore {
44
45class CharacterData;
46class Frame;
47class GraphicsContext;
48class HTMLFormElement;
49class MutableStyleProperties;
50class RenderBlock;
51class RenderObject;
52class RenderView;
53class VisiblePosition;
54
55enum EUserTriggered { NotUserTriggered = 0, UserTriggered = 1 };
56
57enum RevealExtentOption {
58 RevealExtent,
59 DoNotRevealExtent
60};
61
62class CaretBase {
63 WTF_MAKE_NONCOPYABLE(CaretBase);
64 WTF_MAKE_FAST_ALLOCATED;
65protected:
66 enum CaretVisibility { Visible, Hidden };
67 explicit CaretBase(CaretVisibility = Hidden);
68
69 void invalidateCaretRect(Node*, bool caretRectChanged = false);
70 void clearCaretRect();
71 bool updateCaretRect(Document*, const VisiblePosition& caretPosition);
72 bool shouldRepaintCaret(const RenderView*, bool isContentEditable) const;
73 void paintCaret(Node*, GraphicsContext&, const LayoutPoint&, const LayoutRect& clipRect) const;
74
75 const LayoutRect& localCaretRectWithoutUpdate() const { return m_caretLocalRect; }
76
77 bool shouldUpdateCaretRect() const { return m_caretRectNeedsUpdate; }
78 void setCaretRectNeedsUpdate() { m_caretRectNeedsUpdate = true; }
79
80 void setCaretVisibility(CaretVisibility visibility) { m_caretVisibility = visibility; }
81 bool caretIsVisible() const { return m_caretVisibility == Visible; }
82 CaretVisibility caretVisibility() const { return m_caretVisibility; }
83
84private:
85 LayoutRect m_caretLocalRect; // caret rect in coords local to the renderer responsible for painting the caret
86 bool m_caretRectNeedsUpdate; // true if m_caretRect (and m_absCaretBounds in FrameSelection) need to be calculated
87 CaretVisibility m_caretVisibility;
88};
89
90class DragCaretController : private CaretBase {
91 WTF_MAKE_NONCOPYABLE(DragCaretController);
92 WTF_MAKE_FAST_ALLOCATED;
93public:
94 DragCaretController();
95
96 RenderBlock* caretRenderer() const;
97 void paintDragCaret(Frame*, GraphicsContext&, const LayoutPoint&, const LayoutRect& clipRect) const;
98
99 bool isContentEditable() const { return m_position.rootEditableElement(); }
100 WEBCORE_EXPORT bool isContentRichlyEditable() const;
101
102 bool hasCaret() const { return m_position.isNotNull(); }
103 const VisiblePosition& caretPosition() { return m_position; }
104 void setCaretPosition(const VisiblePosition&);
105 void clear() { setCaretPosition(VisiblePosition()); }
106 WEBCORE_EXPORT IntRect caretRectInRootViewCoordinates() const;
107 WEBCORE_EXPORT IntRect editableElementRectInRootViewCoordinates() const;
108
109 void nodeWillBeRemoved(Node&);
110
111private:
112 VisiblePosition m_position;
113};
114
115class FrameSelection : private CaretBase {
116 WTF_MAKE_NONCOPYABLE(FrameSelection);
117 WTF_MAKE_FAST_ALLOCATED;
118public:
119 enum EAlteration { AlterationMove, AlterationExtend };
120 enum CursorAlignOnScroll { AlignCursorOnScrollIfNeeded,
121 AlignCursorOnScrollAlways };
122 enum SetSelectionOption {
123 FireSelectEvent = 1 << 0,
124 CloseTyping = 1 << 1,
125 ClearTypingStyle = 1 << 2,
126 SpellCorrectionTriggered = 1 << 3,
127 DoNotSetFocus = 1 << 4,
128 DictationTriggered = 1 << 5,
129 IsUserTriggered = 1 << 6,
130 RevealSelection = 1 << 7,
131 RevealSelectionUpToMainFrame = 1 << 8,
132 };
133 static constexpr OptionSet<SetSelectionOption> defaultSetSelectionOptions(EUserTriggered userTriggered = NotUserTriggered)
134 {
135 OptionSet<SetSelectionOption> options { CloseTyping, ClearTypingStyle };
136 if (userTriggered == UserTriggered)
137 options.add({ RevealSelection, FireSelectEvent, IsUserTriggered });
138 return options;
139 }
140
141 WEBCORE_EXPORT explicit FrameSelection(Frame* = nullptr);
142
143 WEBCORE_EXPORT Element* rootEditableElementOrDocumentElement() const;
144
145 WEBCORE_EXPORT void moveTo(const Range*);
146 WEBCORE_EXPORT void moveTo(const VisiblePosition&, EUserTriggered = NotUserTriggered, CursorAlignOnScroll = AlignCursorOnScrollIfNeeded);
147 WEBCORE_EXPORT void moveTo(const VisiblePosition&, const VisiblePosition&, EUserTriggered = NotUserTriggered);
148 void moveTo(const Position&, EAffinity, EUserTriggered = NotUserTriggered);
149 void moveTo(const Position&, const Position&, EAffinity, EUserTriggered = NotUserTriggered);
150 void moveWithoutValidationTo(const Position&, const Position&, bool selectionHasDirection, bool shouldSetFocus, SelectionRevealMode, const AXTextStateChangeIntent& = AXTextStateChangeIntent());
151
152 const VisibleSelection& selection() const { return m_selection; }
153 WEBCORE_EXPORT void setSelection(const VisibleSelection&, OptionSet<SetSelectionOption> = defaultSetSelectionOptions(), AXTextStateChangeIntent = AXTextStateChangeIntent(), CursorAlignOnScroll = AlignCursorOnScrollIfNeeded, TextGranularity = CharacterGranularity);
154
155 enum class ShouldCloseTyping : bool { No, Yes };
156 WEBCORE_EXPORT bool setSelectedRange(Range*, EAffinity, ShouldCloseTyping, EUserTriggered = NotUserTriggered);
157 WEBCORE_EXPORT void selectAll();
158 WEBCORE_EXPORT void clear();
159 void prepareForDestruction();
160
161 void updateAppearanceAfterLayout();
162 void scheduleAppearanceUpdateAfterStyleChange();
163
164 enum class RevealSelectionAfterUpdate : bool { NotForced, Forced };
165 void setNeedsSelectionUpdate(RevealSelectionAfterUpdate = RevealSelectionAfterUpdate::NotForced);
166
167 bool contains(const LayoutPoint&) const;
168
169 WEBCORE_EXPORT bool modify(EAlteration, SelectionDirection, TextGranularity, EUserTriggered = NotUserTriggered);
170 enum VerticalDirection { DirectionUp, DirectionDown };
171 bool modify(EAlteration, unsigned verticalDistance, VerticalDirection, EUserTriggered = NotUserTriggered, CursorAlignOnScroll = AlignCursorOnScrollIfNeeded);
172
173 TextGranularity granularity() const { return m_granularity; }
174
175 void setStart(const VisiblePosition &, EUserTriggered = NotUserTriggered);
176 void setEnd(const VisiblePosition &, EUserTriggered = NotUserTriggered);
177
178 WEBCORE_EXPORT void setBase(const VisiblePosition&, EUserTriggered = NotUserTriggered);
179 WEBCORE_EXPORT void setBase(const Position&, EAffinity, EUserTriggered = NotUserTriggered);
180 void setExtent(const VisiblePosition&, EUserTriggered = NotUserTriggered);
181 void setExtent(const Position&, EAffinity, EUserTriggered = NotUserTriggered);
182
183 // Return the renderer that is responsible for painting the caret (in the selection start node)
184 RenderBlock* caretRendererWithoutUpdatingLayout() const;
185
186 // Bounds of (possibly transformed) caret in absolute coords
187 WEBCORE_EXPORT IntRect absoluteCaretBounds(bool* insideFixed = nullptr);
188 void setCaretRectNeedsUpdate() { CaretBase::setCaretRectNeedsUpdate(); }
189
190 void willBeModified(EAlteration, SelectionDirection);
191
192 bool isNone() const { return m_selection.isNone(); }
193 bool isCaret() const { return m_selection.isCaret(); }
194 bool isRange() const { return m_selection.isRange(); }
195 bool isCaretOrRange() const { return m_selection.isCaretOrRange(); }
196 bool isAll(EditingBoundaryCrossingRule rule = CannotCrossEditingBoundary) const { return m_selection.isAll(rule); }
197
198 RefPtr<Range> toNormalizedRange() const { return m_selection.toNormalizedRange(); }
199
200 void debugRenderer(RenderObject*, bool selected) const;
201
202 void nodeWillBeRemoved(Node&);
203 void textWasReplaced(CharacterData*, unsigned offset, unsigned oldLength, unsigned newLength);
204
205 void setCaretVisible(bool caretIsVisible) { setCaretVisibility(caretIsVisible ? Visible : Hidden); }
206 void paintCaret(GraphicsContext&, const LayoutPoint&, const LayoutRect& clipRect);
207
208 // Used to suspend caret blinking while the mouse is down.
209 void setCaretBlinkingSuspended(bool suspended) { m_isCaretBlinkingSuspended = suspended; }
210 bool isCaretBlinkingSuspended() const { return m_isCaretBlinkingSuspended; }
211
212 // Focus
213 void setFocused(bool);
214 bool isFocused() const { return m_focused; }
215 WEBCORE_EXPORT bool isFocusedAndActive() const;
216 void pageActivationChanged();
217
218 // Painting.
219 WEBCORE_EXPORT void updateAppearance();
220
221#if ENABLE(TREE_DEBUGGING)
222 void formatForDebugger(char* buffer, unsigned length) const;
223 void showTreeForThis() const;
224#endif
225
226#if PLATFORM(IOS_FAMILY)
227public:
228 WEBCORE_EXPORT void expandSelectionToElementContainingCaretSelection();
229 WEBCORE_EXPORT RefPtr<Range> elementRangeContainingCaretSelection() const;
230 WEBCORE_EXPORT void expandSelectionToWordContainingCaretSelection();
231 WEBCORE_EXPORT RefPtr<Range> wordRangeContainingCaretSelection();
232 WEBCORE_EXPORT void expandSelectionToStartOfWordContainingCaretSelection();
233 WEBCORE_EXPORT UChar characterInRelationToCaretSelection(int amount) const;
234 WEBCORE_EXPORT UChar characterBeforeCaretSelection() const;
235 WEBCORE_EXPORT UChar characterAfterCaretSelection() const;
236 WEBCORE_EXPORT int wordOffsetInRange(const Range*) const;
237 WEBCORE_EXPORT bool spaceFollowsWordInRange(const Range*) const;
238 WEBCORE_EXPORT bool selectionAtDocumentStart() const;
239 WEBCORE_EXPORT bool selectionAtSentenceStart() const;
240 WEBCORE_EXPORT bool selectionAtWordStart() const;
241 WEBCORE_EXPORT RefPtr<Range> rangeByMovingCurrentSelection(int amount) const;
242 WEBCORE_EXPORT RefPtr<Range> rangeByExtendingCurrentSelection(int amount) const;
243 WEBCORE_EXPORT void selectRangeOnElement(unsigned location, unsigned length, Node&);
244 WEBCORE_EXPORT void clearCurrentSelection();
245 void setCaretBlinks(bool caretBlinks = true);
246 WEBCORE_EXPORT void setCaretColor(const Color&);
247 WEBCORE_EXPORT static VisibleSelection wordSelectionContainingCaretSelection(const VisibleSelection&);
248 bool isUpdateAppearanceEnabled() const { return m_updateAppearanceEnabled; }
249 void setUpdateAppearanceEnabled(bool enabled) { m_updateAppearanceEnabled = enabled; }
250 void suppressScrolling() { ++m_scrollingSuppressCount; }
251 void restoreScrolling()
252 {
253 ASSERT(m_scrollingSuppressCount);
254 --m_scrollingSuppressCount;
255 }
256private:
257 bool actualSelectionAtSentenceStart(const VisibleSelection&) const;
258 RefPtr<Range> rangeByAlteringCurrentSelection(EAlteration, int amount) const;
259public:
260#endif
261
262 bool shouldChangeSelection(const VisibleSelection&) const;
263 bool shouldDeleteSelection(const VisibleSelection&) const;
264 enum EndPointsAdjustmentMode { AdjustEndpointsAtBidiBoundary, DoNotAdjsutEndpoints };
265 void setSelectionByMouseIfDifferent(const VisibleSelection&, TextGranularity, EndPointsAdjustmentMode = DoNotAdjsutEndpoints);
266
267 EditingStyle* typingStyle() const;
268 WEBCORE_EXPORT RefPtr<MutableStyleProperties> copyTypingStyle() const;
269 void setTypingStyle(RefPtr<EditingStyle>&& style) { m_typingStyle = WTFMove(style); }
270 void clearTypingStyle();
271
272 WEBCORE_EXPORT FloatRect selectionBounds(bool clipToVisibleContent = true) const;
273
274 enum class TextRectangleHeight { TextHeight, SelectionHeight };
275 WEBCORE_EXPORT void getClippedVisibleTextRectangles(Vector<FloatRect>&, TextRectangleHeight = TextRectangleHeight::SelectionHeight) const;
276 WEBCORE_EXPORT void getTextRectangles(Vector<FloatRect>&, TextRectangleHeight = TextRectangleHeight::SelectionHeight) const;
277
278 WEBCORE_EXPORT HTMLFormElement* currentForm() const;
279
280 WEBCORE_EXPORT void revealSelection(SelectionRevealMode = SelectionRevealMode::Reveal, const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, RevealExtentOption = DoNotRevealExtent);
281 WEBCORE_EXPORT void setSelectionFromNone();
282
283 bool shouldShowBlockCursor() const { return m_shouldShowBlockCursor; }
284 void setShouldShowBlockCursor(bool);
285
286private:
287 enum EPositionType { START, END, BASE, EXTENT };
288
289 void updateAndRevealSelection(const AXTextStateChangeIntent&);
290 void updateDataDetectorsForSelection();
291
292 bool setSelectionWithoutUpdatingAppearance(const VisibleSelection&, OptionSet<SetSelectionOption>, CursorAlignOnScroll, TextGranularity);
293
294 void respondToNodeModification(Node&, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved);
295 TextDirection directionOfEnclosingBlock();
296 TextDirection directionOfSelection();
297
298 VisiblePosition positionForPlatform(bool isGetStart) const;
299 VisiblePosition startForPlatform() const;
300 VisiblePosition endForPlatform() const;
301 VisiblePosition nextWordPositionForPlatform(const VisiblePosition&);
302
303 VisiblePosition modifyExtendingRight(TextGranularity);
304 VisiblePosition modifyExtendingForward(TextGranularity);
305 VisiblePosition modifyMovingRight(TextGranularity, bool* reachedBoundary = nullptr);
306 VisiblePosition modifyMovingForward(TextGranularity, bool* reachedBoundary = nullptr);
307 VisiblePosition modifyExtendingLeft(TextGranularity);
308 VisiblePosition modifyExtendingBackward(TextGranularity);
309 VisiblePosition modifyMovingLeft(TextGranularity, bool* reachedBoundary = nullptr);
310 VisiblePosition modifyMovingBackward(TextGranularity, bool* reachedBoundary = nullptr);
311
312 LayoutUnit lineDirectionPointForBlockDirectionNavigation(EPositionType);
313
314 AXTextStateChangeIntent textSelectionIntent(EAlteration, SelectionDirection, TextGranularity);
315#if HAVE(ACCESSIBILITY)
316 void notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&);
317#else
318 void notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&) { }
319#endif
320
321 void updateSelectionCachesIfSelectionIsInsideTextFormControl(EUserTriggered);
322
323 void selectFrameElementInParentIfFullySelected();
324
325 void setFocusedElementIfNeeded();
326 void focusedOrActiveStateChanged();
327
328 void caretBlinkTimerFired();
329
330 void updateAppearanceAfterLayoutOrStyleChange();
331 void appearanceUpdateTimerFired();
332
333 WEBCORE_EXPORT void setCaretVisibility(CaretVisibility);
334 bool recomputeCaretRect();
335 void invalidateCaretRect();
336
337 bool dispatchSelectStart();
338
339 Frame* m_frame;
340
341 LayoutUnit m_xPosForVerticalArrowNavigation;
342
343 VisibleSelection m_selection;
344 VisiblePosition m_originalBase; // Used to store base before the adjustment at bidi boundary
345 TextGranularity m_granularity;
346
347 RefPtr<Node> m_previousCaretNode; // The last node which painted the caret. Retained for clearing the old caret when it moves.
348
349 RefPtr<EditingStyle> m_typingStyle;
350
351#if ENABLE(TEXT_CARET)
352 Timer m_caretBlinkTimer;
353#endif
354 Timer m_appearanceUpdateTimer;
355 // The painted bounds of the caret in absolute coordinates
356 IntRect m_absCaretBounds;
357
358 SelectionRevealMode m_selectionRevealMode { SelectionRevealMode::DoNotReveal };
359 AXTextStateChangeIntent m_selectionRevealIntent;
360
361 bool m_caretInsidePositionFixed : 1;
362 bool m_absCaretBoundsDirty : 1;
363 bool m_caretPaint : 1;
364 bool m_isCaretBlinkingSuspended : 1;
365 bool m_focused : 1;
366 bool m_shouldShowBlockCursor : 1;
367 bool m_pendingSelectionUpdate : 1;
368 bool m_alwaysAlignCursorOnScrollWhenRevealingSelection : 1;
369
370#if PLATFORM(IOS_FAMILY)
371 bool m_updateAppearanceEnabled : 1;
372 bool m_caretBlinks : 1;
373 Color m_caretColor;
374 int m_scrollingSuppressCount { 0 };
375#endif
376};
377
378inline EditingStyle* FrameSelection::typingStyle() const
379{
380 return m_typingStyle.get();
381}
382
383inline void FrameSelection::clearTypingStyle()
384{
385 m_typingStyle = nullptr;
386}
387
388#if !(PLATFORM(COCOA) || USE(ATK))
389#if HAVE(ACCESSIBILITY)
390inline void FrameSelection::notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&)
391{
392}
393#endif
394#endif
395
396} // namespace WebCore
397
398#if ENABLE(TREE_DEBUGGING)
399// Outside the WebCore namespace for ease of invocation from the debugger.
400void showTree(const WebCore::FrameSelection&);
401void showTree(const WebCore::FrameSelection*);
402#endif
403