blob: 8daf585217d5009e723c601ce7db485aae8acb2c [file] [log] [blame]
/*
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FrameSelection_h
#define FrameSelection_h
#include "core/CoreExport.h"
#include "core/dom/Range.h"
#include "core/editing/CaretBase.h"
#include "core/editing/EditingStyle.h"
#include "core/editing/EphemeralRange.h"
#include "core/editing/VisiblePosition.h"
#include "core/editing/VisibleSelection.h"
#include "core/editing/iterators/TextIteratorFlags.h"
#include "core/layout/ScrollAlignment.h"
#include "platform/Timer.h"
#include "platform/geometry/IntRect.h"
#include "platform/geometry/LayoutRect.h"
#include "platform/heap/Handle.h"
#include "wtf/Noncopyable.h"
namespace blink {
class CharacterData;
class CullRect;
class LocalFrame;
class GranularityStrategy;
class GraphicsContext;
class HTMLFormElement;
class SelectionEditor;
class PendingSelection;
class Text;
enum class CursorAlignOnScroll { IfNeeded, Always };
enum EUserTriggered { NotUserTriggered = 0, UserTriggered = 1 };
enum RevealExtentOption {
RevealExtent,
DoNotRevealExtent
};
enum class SelectionDirectionalMode { NonDirectional, Directional };
class CORE_EXPORT FrameSelection final : public NoBaseWillBeGarbageCollectedFinalized<FrameSelection>, private CaretBase {
WTF_MAKE_NONCOPYABLE(FrameSelection);
USING_FAST_MALLOC_WILL_BE_REMOVED(FrameSelection);
public:
static PassOwnPtrWillBeRawPtr<FrameSelection> create(LocalFrame* frame = nullptr)
{
return adoptPtrWillBeNoop(new FrameSelection(frame));
}
~FrameSelection();
enum EAlteration { AlterationMove, AlterationExtend };
enum SetSelectionOption {
// 1 << 0 is reserved for EUserTriggered
CloseTyping = 1 << 1,
ClearTypingStyle = 1 << 2,
SpellCorrectionTriggered = 1 << 3,
DoNotSetFocus = 1 << 4,
DoNotUpdateAppearance = 1 << 5,
DoNotClearStrategy = 1 << 6,
DoNotAdjustInFlatTree = 1 << 7,
};
typedef unsigned SetSelectionOptions; // Union of values in SetSelectionOption and EUserTriggered
static inline EUserTriggered selectionOptionsToUserTriggered(SetSelectionOptions options)
{
return static_cast<EUserTriggered>(options & UserTriggered);
}
LocalFrame* frame() const { return m_frame; }
Element* rootEditableElement() const { return selection().rootEditableElement(); }
Element* rootEditableElementOrDocumentElement() const;
ContainerNode* rootEditableElementOrTreeScopeRootNode() const;
bool hasEditableStyle() const { return selection().hasEditableStyle(); }
bool isContentEditable() const { return selection().isContentEditable(); }
bool isContentRichlyEditable() const { return selection().isContentRichlyEditable(); }
void moveTo(const VisiblePosition&, EUserTriggered = NotUserTriggered, CursorAlignOnScroll = CursorAlignOnScroll::IfNeeded);
void moveTo(const VisiblePosition&, const VisiblePosition&, EUserTriggered = NotUserTriggered);
void moveTo(const Position&, TextAffinity, EUserTriggered = NotUserTriggered);
template <typename Strategy>
const VisibleSelectionTemplate<Strategy>& visibleSelection() const;
const VisibleSelection& selection() const;
void setSelection(const VisibleSelection&, SetSelectionOptions = CloseTyping | ClearTypingStyle, CursorAlignOnScroll = CursorAlignOnScroll::IfNeeded, TextGranularity = CharacterGranularity);
void setSelection(const VisibleSelectionInFlatTree&, SetSelectionOptions = CloseTyping | ClearTypingStyle, CursorAlignOnScroll = CursorAlignOnScroll::IfNeeded, TextGranularity = CharacterGranularity);
// TODO(yosin) We should get rid of two parameters version of
// |setSelection()| to avoid conflict of four parameters version.
void setSelection(const VisibleSelection& selection, TextGranularity granularity) { setSelection(selection, CloseTyping | ClearTypingStyle, CursorAlignOnScroll::IfNeeded, granularity); }
void setSelection(const VisibleSelectionInFlatTree& selection, TextGranularity granularity) { setSelection(selection, CloseTyping | ClearTypingStyle, CursorAlignOnScroll::IfNeeded, granularity); }
// TODO(yosin) We should get rid of |Range| version of |setSelectedRagne()|
// for Oilpan.
bool setSelectedRange(Range*, TextAffinity, SelectionDirectionalMode = SelectionDirectionalMode::NonDirectional, SetSelectionOptions = CloseTyping | ClearTypingStyle);
bool setSelectedRange(const EphemeralRange&, TextAffinity, SelectionDirectionalMode = SelectionDirectionalMode::NonDirectional, FrameSelection::SetSelectionOptions = CloseTyping | ClearTypingStyle);
void selectAll();
void clear();
void prepareForDestruction();
// Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
void selectFrameElementInParentIfFullySelected();
bool contains(const LayoutPoint&);
SelectionType selectionType() const { return selection().selectionType(); }
TextAffinity affinity() const { return selection().affinity(); }
bool modify(EAlteration, SelectionDirection, TextGranularity, EUserTriggered = NotUserTriggered);
enum VerticalDirection { DirectionUp, DirectionDown };
bool modify(EAlteration, unsigned verticalDistance, VerticalDirection, EUserTriggered = NotUserTriggered, CursorAlignOnScroll = CursorAlignOnScroll::IfNeeded);
// Moves the selection extent based on the selection granularity strategy.
// This function does not allow the selection to collapse. If the new
// extent is resolved to the same position as the current base, this
// function will do nothing.
void moveRangeSelectionExtent(const IntPoint&);
void moveRangeSelection(const VisiblePosition& base, const VisiblePosition& extent, TextGranularity);
TextGranularity granularity() const { return m_granularity; }
void setStart(const VisiblePosition &, EUserTriggered = NotUserTriggered);
void setEnd(const VisiblePosition &, EUserTriggered = NotUserTriggered);
void setBase(const VisiblePosition&, EUserTriggered = NotUserTriggered);
void setExtent(const VisiblePosition&, EUserTriggered = NotUserTriggered);
Position base() const { return selection().base(); }
Position extent() const { return selection().extent(); }
Position start() const { return selection().start(); }
Position end() const { return selection().end(); }
// Return the layoutObject that is responsible for painting the caret (in the selection start node)
LayoutBlock* caretLayoutObject() const;
// Bounds of (possibly transformed) caret in absolute coords
IntRect absoluteCaretBounds();
void didChangeFocus();
bool isNone() const { return selection().isNone(); }
bool isCaret() const { return selection().isCaret(); }
bool isRange() const { return selection().isRange(); }
bool isCaretOrRange() const { return selection().isCaretOrRange(); }
bool isInPasswordField() const;
bool isDirectional() const { return selection().isDirectional(); }
// If this FrameSelection has a logical range which is still valid, this function return its clone. Otherwise,
// the return value from underlying VisibleSelection's firstRange() is returned.
PassRefPtrWillBeRawPtr<Range> firstRange() const;
void nodeWillBeRemoved(Node&);
void didUpdateCharacterData(CharacterData*, unsigned offset, unsigned oldLength, unsigned newLength);
void didMergeTextNodes(const Text& oldNode, unsigned offset);
void didSplitTextNode(const Text& oldNode);
bool isAppearanceDirty() const;
void commitAppearanceIfNeeded(LayoutView&);
void updateAppearance();
void setCaretVisible(bool caretIsVisible) { setCaretVisibility(caretIsVisible ? Visible : Hidden); }
bool isCaretBoundsDirty() const { return m_caretRectDirty; }
void setCaretRectNeedsUpdate();
void scheduleVisualUpdate() const;
void invalidateCaretRect();
void paintCaret(GraphicsContext&, const LayoutPoint&);
bool ShouldPaintCaretForTesting() const { return m_shouldPaintCaret; }
// Used to suspend caret blinking while the mouse is down.
void setCaretBlinkingSuspended(bool suspended) { m_isCaretBlinkingSuspended = suspended; }
bool isCaretBlinkingSuspended() const { return m_isCaretBlinkingSuspended; }
// Focus
void setFocused(bool);
bool isFocused() const { return m_focused; }
bool isFocusedAndActive() const;
void pageActivationChanged();
void updateSecureKeyboardEntryIfActive();
// Returns true if a word is selected.
bool selectWordAroundPosition(const VisiblePosition&);
#ifndef NDEBUG
void formatForDebugger(char* buffer, unsigned length) const;
void showTreeForThis() const;
#endif
enum EndPointsAdjustmentMode { AdjustEndpointsAtBidiBoundary, DoNotAdjsutEndpoints };
void setNonDirectionalSelectionIfNeeded(const VisibleSelection&, TextGranularity, EndPointsAdjustmentMode = DoNotAdjsutEndpoints);
void setNonDirectionalSelectionIfNeeded(const VisibleSelectionInFlatTree&, TextGranularity, EndPointsAdjustmentMode = DoNotAdjsutEndpoints);
void setFocusedNodeIfNeeded();
void notifyLayoutObjectOfSelectionChange(EUserTriggered);
EditingStyle* typingStyle() const;
void setTypingStyle(PassRefPtrWillBeRawPtr<EditingStyle>);
void clearTypingStyle();
String selectedHTMLForClipboard() const;
String selectedText(TextIteratorBehavior = TextIteratorDefaultBehavior) const;
String selectedTextForClipboard() const;
// The bounds are clipped to the viewport as this is what callers expect.
LayoutRect bounds() const;
LayoutRect unclippedBounds() const;
HTMLFormElement* currentForm() const;
void revealSelection(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, RevealExtentOption = DoNotRevealExtent);
void setSelectionFromNone();
bool shouldShowBlockCursor() const { return m_shouldShowBlockCursor; }
void setShouldShowBlockCursor(bool);
// TODO(yosin): We should check DOM tree version and style version in
// |FrameSelection::selection()| to make sure we use updated selection,
// rather than having |updateIfNeeded()|. Once, we update all layout tests
// to use updated selection, we should make |updateIfNeeded()| private.
void updateIfNeeded();
DECLARE_VIRTUAL_TRACE();
private:
friend class FrameSelectionTest;
explicit FrameSelection(LocalFrame*);
// Note: We have |selectionInFlatTree()| for unit tests, we should
// use |visibleSelection<EditingInFlatTreeStrategy>()|.
const VisibleSelectionInFlatTree& selectionInFlatTree() const;
template <typename Strategy>
VisiblePositionTemplate<Strategy> originalBase() const;
void setOriginalBase(const VisiblePosition& newBase) { m_originalBase = newBase; }
void setOriginalBase(const VisiblePositionInFlatTree& newBase) { m_originalBaseInFlatTree = newBase; }
template <typename Strategy>
bool containsAlgorithm(const LayoutPoint&);
template <typename Strategy>
void setNonDirectionalSelectionIfNeededAlgorithm(const VisibleSelectionTemplate<Strategy>&, TextGranularity, EndPointsAdjustmentMode);
template <typename Strategy>
void setSelectionAlgorithm(const VisibleSelectionTemplate<Strategy>&, SetSelectionOptions, CursorAlignOnScroll, TextGranularity);
void respondToNodeModification(Node&, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved);
void notifyAccessibilityForSelectionChange();
void notifyCompositorForSelectionChange();
void notifyEventHandlerForSelectionChange();
void focusedOrActiveStateChanged();
void caretBlinkTimerFired(Timer<FrameSelection>*);
void stopCaretBlinkTimer();
void setUseSecureKeyboardEntry(bool);
void setCaretVisibility(CaretVisibility);
bool shouldBlinkCaret() const;
void updateSelectionIfNeeded(const Position& base, const Position& extent, const Position& start, const Position& end);
template <typename Strategy>
VisibleSelectionTemplate<Strategy> validateSelection(const VisibleSelectionTemplate<Strategy>&);
GranularityStrategy* granularityStrategy();
RawPtrWillBeMember<LocalFrame> m_frame;
const OwnPtrWillBeMember<PendingSelection> m_pendingSelection;
const OwnPtrWillBeMember<SelectionEditor> m_selectionEditor;
// Used to store base before the adjustment at bidi boundary
VisiblePosition m_originalBase;
VisiblePositionInFlatTree m_originalBaseInFlatTree;
TextGranularity m_granularity;
RefPtrWillBeMember<Node> m_previousCaretNode; // The last node which painted the caret. Retained for clearing the old caret when it moves.
LayoutRect m_previousCaretRect;
CaretVisibility m_previousCaretVisibility;
RefPtrWillBeMember<EditingStyle> m_typingStyle;
Timer<FrameSelection> m_caretBlinkTimer;
bool m_caretRectDirty : 1;
bool m_shouldPaintCaret : 1;
bool m_isCaretBlinkingSuspended : 1;
bool m_focused : 1;
bool m_shouldShowBlockCursor : 1;
// Controls text granularity used to adjust the selection's extent in moveRangeSelectionExtent.
OwnPtr<GranularityStrategy> m_granularityStrategy;
};
inline EditingStyle* FrameSelection::typingStyle() const
{
return m_typingStyle.get();
}
inline void FrameSelection::clearTypingStyle()
{
m_typingStyle.clear();
}
inline void FrameSelection::setTypingStyle(PassRefPtrWillBeRawPtr<EditingStyle> style)
{
m_typingStyle = style;
}
} // namespace blink
#ifndef NDEBUG
// Outside the WebCore namespace for ease of invocation from gdb.
void showTree(const blink::FrameSelection&);
void showTree(const blink::FrameSelection*);
#endif
#endif // FrameSelection_h