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#ifndef FrameSelection_h
27#define FrameSelection_h
28
29#include "EditingStyle.h"
30#include "IntRect.h"
31#include "LayoutRect.h"
32#include "Range.h"
33#include "ScrollBehavior.h"
34#include "Timer.h"
35#include "VisibleSelection.h"
36#include <wtf/Noncopyable.h>
37
38#if PLATFORM(IOS)
39#include "Color.h"
40#endif
41
42namespace WebCore {
43
44class CharacterData;
45class Frame;
46class GraphicsContext;
47class HTMLFormElement;
48class MutableStyleProperties;
49class RenderObject;
50class RenderView;
51class Settings;
52class VisiblePosition;
53
54enum EUserTriggered { NotUserTriggered = 0, UserTriggered = 1 };
55
56enum RevealExtentOption {
57    RevealExtent,
58    DoNotRevealExtent
59};
60
61class CaretBase {
62    WTF_MAKE_NONCOPYABLE(CaretBase);
63    WTF_MAKE_FAST_ALLOCATED;
64protected:
65    enum CaretVisibility { Visible, Hidden };
66    explicit CaretBase(CaretVisibility = Hidden);
67
68    void invalidateCaretRect(Node*, bool caretRectChanged = false);
69    void clearCaretRect();
70    bool updateCaretRect(Document*, const VisiblePosition& caretPosition);
71    bool shouldRepaintCaret(const RenderView*, bool isContentEditable) const;
72    void paintCaret(Node*, GraphicsContext*, const LayoutPoint&, const LayoutRect& clipRect) const;
73
74    const LayoutRect& localCaretRectWithoutUpdate() const { return m_caretLocalRect; }
75
76    bool shouldUpdateCaretRect() const { return m_caretRectNeedsUpdate; }
77    void setCaretRectNeedsUpdate() { m_caretRectNeedsUpdate = true; }
78
79    void setCaretVisibility(CaretVisibility visibility) { m_caretVisibility = visibility; }
80    bool caretIsVisible() const { return m_caretVisibility == Visible; }
81    CaretVisibility caretVisibility() const { return m_caretVisibility; }
82
83private:
84    LayoutRect m_caretLocalRect; // caret rect in coords local to the renderer responsible for painting the caret
85    bool m_caretRectNeedsUpdate; // true if m_caretRect (and m_absCaretBounds in FrameSelection) need to be calculated
86    CaretVisibility m_caretVisibility;
87};
88
89class DragCaretController : private CaretBase {
90    WTF_MAKE_NONCOPYABLE(DragCaretController);
91    WTF_MAKE_FAST_ALLOCATED;
92public:
93    DragCaretController();
94
95    RenderObject* caretRenderer() const;
96    void paintDragCaret(Frame*, GraphicsContext*, const LayoutPoint&, const LayoutRect& clipRect) const;
97
98    bool isContentEditable() const { return m_position.rootEditableElement(); }
99    bool isContentRichlyEditable() const;
100
101    bool hasCaret() const { return m_position.isNotNull(); }
102    const VisiblePosition& caretPosition() { return m_position; }
103    void setCaretPosition(const VisiblePosition&);
104    void clear() { setCaretPosition(VisiblePosition()); }
105
106    void nodeWillBeRemoved(Node*);
107
108private:
109    VisiblePosition m_position;
110};
111
112class FrameSelection : private CaretBase {
113    WTF_MAKE_NONCOPYABLE(FrameSelection);
114    WTF_MAKE_FAST_ALLOCATED;
115public:
116    enum EAlteration { AlterationMove, AlterationExtend };
117    enum CursorAlignOnScroll { AlignCursorOnScrollIfNeeded,
118                               AlignCursorOnScrollAlways };
119    enum SetSelectionOption {
120        FireSelectEvent = 1 << 0,
121        CloseTyping = 1 << 1,
122        ClearTypingStyle = 1 << 2,
123        SpellCorrectionTriggered = 1 << 3,
124        DoNotSetFocus = 1 << 4,
125        DictationTriggered = 1 << 5,
126        RevealSelection = 1 << 6,
127    };
128    typedef unsigned SetSelectionOptions; // Union of values in SetSelectionOption and EUserTriggered
129    static inline SetSelectionOptions defaultSetSelectionOptions(EUserTriggered userTriggered = NotUserTriggered)
130    {
131        return CloseTyping | ClearTypingStyle | (userTriggered ? (RevealSelection | FireSelectEvent) : 0);
132    }
133
134    explicit FrameSelection(Frame* = 0);
135
136    Element* rootEditableElementOrDocumentElement() const;
137
138    void moveTo(const Range*);
139    void moveTo(const VisiblePosition&, EUserTriggered = NotUserTriggered, CursorAlignOnScroll = AlignCursorOnScrollIfNeeded);
140    void moveTo(const VisiblePosition&, const VisiblePosition&, EUserTriggered = NotUserTriggered);
141    void moveTo(const Position&, EAffinity, EUserTriggered = NotUserTriggered);
142    void moveTo(const Position&, const Position&, EAffinity, EUserTriggered = NotUserTriggered);
143    void moveWithoutValidationTo(const Position&, const Position&, bool selectionHasDirection, bool shouldSetFocus);
144
145    const VisibleSelection& selection() const { return m_selection; }
146    void setSelection(const VisibleSelection&, SetSelectionOptions = defaultSetSelectionOptions(), CursorAlignOnScroll = AlignCursorOnScrollIfNeeded, TextGranularity = CharacterGranularity);
147    bool setSelectedRange(Range*, EAffinity, bool closeTyping);
148    void selectAll();
149    void clear();
150    void prepareForDestruction();
151
152    void didLayout();
153    void setNeedsSelectionUpdate();
154
155    bool contains(const LayoutPoint&);
156
157    bool modify(EAlteration, SelectionDirection, TextGranularity, EUserTriggered = NotUserTriggered);
158    enum VerticalDirection { DirectionUp, DirectionDown };
159    bool modify(EAlteration, unsigned verticalDistance, VerticalDirection, EUserTriggered = NotUserTriggered, CursorAlignOnScroll = AlignCursorOnScrollIfNeeded);
160
161    TextGranularity granularity() const { return m_granularity; }
162
163    void setStart(const VisiblePosition &, EUserTriggered = NotUserTriggered);
164    void setEnd(const VisiblePosition &, EUserTriggered = NotUserTriggered);
165
166    void setBase(const VisiblePosition&, EUserTriggered = NotUserTriggered);
167    void setBase(const Position&, EAffinity, EUserTriggered = NotUserTriggered);
168    void setExtent(const VisiblePosition&, EUserTriggered = NotUserTriggered);
169    void setExtent(const Position&, EAffinity, EUserTriggered = NotUserTriggered);
170
171    // Return the renderer that is responsible for painting the caret (in the selection start node)
172    RenderObject* caretRendererWithoutUpdatingLayout() const;
173
174    // Bounds of (possibly transformed) caret in absolute coords
175    IntRect absoluteCaretBounds();
176    void setCaretRectNeedsUpdate() { CaretBase::setCaretRectNeedsUpdate(); }
177
178    void willBeModified(EAlteration, SelectionDirection);
179
180    bool isNone() const { return m_selection.isNone(); }
181    bool isCaret() const { return m_selection.isCaret(); }
182    bool isRange() const { return m_selection.isRange(); }
183    bool isCaretOrRange() const { return m_selection.isCaretOrRange(); }
184    bool isAll(EditingBoundaryCrossingRule rule = CannotCrossEditingBoundary) const { return m_selection.isAll(rule); }
185
186    PassRefPtr<Range> toNormalizedRange() const { return m_selection.toNormalizedRange(); }
187
188    void debugRenderer(RenderObject*, bool selected) const;
189
190    void nodeWillBeRemoved(Node*);
191    void textWasReplaced(CharacterData*, unsigned offset, unsigned oldLength, unsigned newLength);
192
193    void setCaretVisible(bool caretIsVisible) { setCaretVisibility(caretIsVisible ? Visible : Hidden); }
194    void paintCaret(GraphicsContext*, const LayoutPoint&, const LayoutRect& clipRect);
195
196    // Used to suspend caret blinking while the mouse is down.
197    void setCaretBlinkingSuspended(bool suspended) { m_isCaretBlinkingSuspended = suspended; }
198    bool isCaretBlinkingSuspended() const { return m_isCaretBlinkingSuspended; }
199
200    // Focus
201    void setFocused(bool);
202    bool isFocused() const { return m_focused; }
203    bool isFocusedAndActive() const;
204    void pageActivationChanged();
205
206    // Painting.
207    void updateAppearance();
208
209#ifndef NDEBUG
210    void formatForDebugger(char* buffer, unsigned length) const;
211    void showTreeForThis() const;
212#endif
213
214#if PLATFORM(IOS)
215public:
216    void expandSelectionToElementContainingCaretSelection();
217    PassRefPtr<Range> elementRangeContainingCaretSelection() const;
218    void expandSelectionToWordContainingCaretSelection();
219    PassRefPtr<Range> wordRangeContainingCaretSelection();
220    void expandSelectionToStartOfWordContainingCaretSelection();
221    UChar characterInRelationToCaretSelection(int amount) const;
222    UChar characterBeforeCaretSelection() const;
223    UChar characterAfterCaretSelection() const;
224    int wordOffsetInRange(const Range*) const;
225    bool spaceFollowsWordInRange(const Range*) const;
226    bool selectionAtDocumentStart() const;
227    bool selectionAtSentenceStart() const;
228    bool selectionAtWordStart() const;
229    PassRefPtr<Range> rangeByMovingCurrentSelection(int amount) const;
230    PassRefPtr<Range> rangeByExtendingCurrentSelection(int amount) const;
231    void selectRangeOnElement(unsigned location, unsigned length, Node*);
232    void clearCurrentSelection();
233    void setCaretBlinks(bool caretBlinks = true);
234    void setCaretColor(const Color&);
235    static VisibleSelection wordSelectionContainingCaretSelection(const VisibleSelection&);
236    void setUpdateAppearanceEnabled(bool enabled) { m_updateAppearanceEnabled = enabled; }
237    void suppressScrolling() { ++m_scrollingSuppressCount; }
238    void restoreScrolling()
239    {
240        ASSERT(m_scrollingSuppressCount);
241        --m_scrollingSuppressCount;
242    }
243private:
244    bool actualSelectionAtSentenceStart(const VisibleSelection&) const;
245    PassRefPtr<Range> rangeByAlteringCurrentSelection(EAlteration, int amount) const;
246public:
247#endif
248
249    bool shouldChangeSelection(const VisibleSelection&) const;
250    bool shouldDeleteSelection(const VisibleSelection&) const;
251    enum EndPointsAdjustmentMode { AdjustEndpointsAtBidiBoundary, DoNotAdjsutEndpoints };
252    void setSelectionByMouseIfDifferent(const VisibleSelection&, TextGranularity, EndPointsAdjustmentMode = DoNotAdjsutEndpoints);
253
254    EditingStyle* typingStyle() const;
255    PassRefPtr<MutableStyleProperties> copyTypingStyle() const;
256    void setTypingStyle(PassRefPtr<EditingStyle>);
257    void clearTypingStyle();
258
259    FloatRect selectionBounds(bool clipToVisibleContent = true) const;
260
261    void getClippedVisibleTextRectangles(Vector<FloatRect>&) const;
262
263    HTMLFormElement* currentForm() const;
264
265    void revealSelection(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, RevealExtentOption = DoNotRevealExtent);
266    void setSelectionFromNone();
267
268    bool shouldShowBlockCursor() const { return m_shouldShowBlockCursor; }
269    void setShouldShowBlockCursor(bool);
270
271private:
272    enum EPositionType { START, END, BASE, EXTENT };
273
274    void updateAndRevealSelection();
275    void updateDataDetectorsForSelection();
276
277    bool setSelectionWithoutUpdatingAppearance(const VisibleSelection&, SetSelectionOptions, CursorAlignOnScroll, TextGranularity);
278
279    void respondToNodeModification(Node*, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved);
280    TextDirection directionOfEnclosingBlock();
281    TextDirection directionOfSelection();
282
283    VisiblePosition positionForPlatform(bool isGetStart) const;
284    VisiblePosition startForPlatform() const;
285    VisiblePosition endForPlatform() const;
286    VisiblePosition nextWordPositionForPlatform(const VisiblePosition&);
287
288    VisiblePosition modifyExtendingRight(TextGranularity);
289    VisiblePosition modifyExtendingForward(TextGranularity);
290    VisiblePosition modifyMovingRight(TextGranularity);
291    VisiblePosition modifyMovingForward(TextGranularity);
292    VisiblePosition modifyExtendingLeft(TextGranularity);
293    VisiblePosition modifyExtendingBackward(TextGranularity);
294    VisiblePosition modifyMovingLeft(TextGranularity);
295    VisiblePosition modifyMovingBackward(TextGranularity);
296
297    LayoutUnit lineDirectionPointForBlockDirectionNavigation(EPositionType);
298
299#if HAVE(ACCESSIBILITY)
300    void notifyAccessibilityForSelectionChange();
301#else
302    void notifyAccessibilityForSelectionChange() { }
303#endif
304
305    void updateSelectionCachesIfSelectionIsInsideTextFormControl(EUserTriggered);
306
307    void selectFrameElementInParentIfFullySelected();
308
309    void setFocusedElementIfNeeded();
310    void focusedOrActiveStateChanged();
311
312    void caretBlinkTimerFired(Timer<FrameSelection>&);
313
314    void setCaretVisibility(CaretVisibility);
315    bool recomputeCaretRect();
316    void invalidateCaretRect();
317
318    bool dispatchSelectStart();
319
320    Frame* m_frame;
321
322    LayoutUnit m_xPosForVerticalArrowNavigation;
323
324    VisibleSelection m_selection;
325    VisiblePosition m_originalBase; // Used to store base before the adjustment at bidi boundary
326    TextGranularity m_granularity;
327
328    RefPtr<Node> m_previousCaretNode; // The last node which painted the caret. Retained for clearing the old caret when it moves.
329
330    RefPtr<EditingStyle> m_typingStyle;
331
332    Timer<FrameSelection> m_caretBlinkTimer;
333    // The painted bounds of the caret in absolute coordinates
334    IntRect m_absCaretBounds;
335    bool m_absCaretBoundsDirty : 1;
336    bool m_caretPaint : 1;
337    bool m_isCaretBlinkingSuspended : 1;
338    bool m_focused : 1;
339    bool m_shouldShowBlockCursor : 1;
340    bool m_pendingSelectionUpdate : 1;
341    bool m_shouldRevealSelection : 1;
342    bool m_alwaysAlignCursorOnScrollWhenRevealingSelection : 1;
343
344#if PLATFORM(IOS)
345    bool m_updateAppearanceEnabled : 1;
346    bool m_caretBlinks : 1;
347    Color m_caretColor;
348    int m_scrollingSuppressCount;
349#endif
350};
351
352inline EditingStyle* FrameSelection::typingStyle() const
353{
354    return m_typingStyle.get();
355}
356
357inline void FrameSelection::clearTypingStyle()
358{
359    m_typingStyle.clear();
360}
361
362inline void FrameSelection::setTypingStyle(PassRefPtr<EditingStyle> style)
363{
364    m_typingStyle = style;
365}
366
367#if !(PLATFORM(COCOA) || PLATFORM(GTK) || PLATFORM(EFL))
368#if HAVE(ACCESSIBILITY)
369inline void FrameSelection::notifyAccessibilityForSelectionChange()
370{
371}
372#endif
373#endif
374
375} // namespace WebCore
376
377#ifndef NDEBUG
378// Outside the WebCore namespace for ease of invocation from gdb.
379void showTree(const WebCore::FrameSelection&);
380void showTree(const WebCore::FrameSelection*);
381#endif
382
383#endif // FrameSelection_h
384