FrameSelection.cpp [plain text]
#include "config.h"
#include "FrameSelection.h"
#include "CharacterData.h"
#include "DeleteSelectionCommand.h"
#include "Document.h"
#include "Editor.h"
#include "EditorClient.h"
#include "Element.h"
#include "EventHandler.h"
#include "ExceptionCode.h"
#include "FloatQuad.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameTree.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "HTMLFormElement.h"
#include "HTMLFrameElementBase.h"
#include "HTMLInputElement.h"
#include "HTMLSelectElement.h"
#include "HTMLNames.h"
#include "HitTestRequest.h"
#include "HitTestResult.h"
#include "InlineTextBox.h"
#include "Page.h"
#include "Range.h"
#include "RenderLayer.h"
#include "RenderText.h"
#include "RenderTextControl.h"
#include "RenderTheme.h"
#include "RenderView.h"
#include "RenderWidget.h"
#include "SecureTextInput.h"
#include "Settings.h"
#include "SpatialNavigation.h"
#include "TextIterator.h"
#include "TypingCommand.h"
#include "htmlediting.h"
#include "visible_units.h"
#include <stdio.h>
#include <wtf/text/CString.h>
#define EDIT_DEBUG 0
namespace WebCore {
using namespace HTMLNames;
const int NoXPosForVerticalArrowNavigation = INT_MIN;
CaretBase::CaretBase(CaretVisibility visibility)
: m_caretRectNeedsUpdate(true)
, m_caretVisibility(visibility)
{
}
DragCaretController::DragCaretController()
: CaretBase(Visible)
{
}
bool DragCaretController::isContentRichlyEditable() const
{
return isRichlyEditablePosition(m_position.deepEquivalent());
}
FrameSelection::FrameSelection(Frame* frame)
: m_frame(frame)
, m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation)
, m_granularity(CharacterGranularity)
, m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired)
, m_absCaretBoundsDirty(true)
, m_caretPaint(true)
, m_isCaretBlinkingSuspended(false)
, m_focused(frame && frame->page() && frame->page()->focusController()->focusedFrame() == frame)
{
setIsDirectional(false);
}
void FrameSelection::moveTo(const VisiblePosition &pos, bool userTriggered, CursorAlignOnScroll align)
{
SetSelectionOptions options = CloseTyping | ClearTypingStyle;
if (userTriggered)
options |= UserTriggered;
setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), options, align);
}
void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, bool userTriggered)
{
SetSelectionOptions options = CloseTyping | ClearTypingStyle;
if (userTriggered)
options |= UserTriggered;
setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), options);
}
void FrameSelection::moveTo(const Position &pos, EAffinity affinity, bool userTriggered)
{
SetSelectionOptions options = CloseTyping | ClearTypingStyle;
if (userTriggered)
options |= UserTriggered;
setSelection(VisibleSelection(pos, affinity), options);
}
void FrameSelection::moveTo(const Range *r, EAffinity affinity, bool userTriggered)
{
SetSelectionOptions options = CloseTyping | ClearTypingStyle;
if (userTriggered)
options |= UserTriggered;
VisibleSelection selection = r ? VisibleSelection(r->startPosition(), r->endPosition(), affinity) : VisibleSelection(Position(), Position(), affinity);
setSelection(selection, options);
}
void FrameSelection::moveTo(const Position &base, const Position &extent, EAffinity affinity, bool userTriggered)
{
SetSelectionOptions options = CloseTyping | ClearTypingStyle;
if (userTriggered)
options |= UserTriggered;
setSelection(VisibleSelection(base, extent, affinity), options);
}
void DragCaretController::setCaretPosition(const VisiblePosition& position)
{
if (Node* node = m_position.deepEquivalent().deprecatedNode())
invalidateCaretRect(node);
m_position = position;
setCaretRectNeedsUpdate();
Document* document = 0;
if (Node* node = m_position.deepEquivalent().deprecatedNode()) {
invalidateCaretRect(node);
document = node->document();
}
if (m_position.isNull() || m_position.isOrphan())
clearCaretRect();
else
updateCaretRect(document, m_position);
}
void FrameSelection::setSelection(const VisibleSelection& s, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity, DirectionalityPolicy directionalityPolicy)
{
m_granularity = granularity;
bool closeTyping = options & CloseTyping;
bool shouldClearTypingStyle = options & ClearTypingStyle;
bool userTriggered = options & UserTriggered;
setIsDirectional(directionalityPolicy == MakeDirectionalSelection);
if (!m_frame) {
m_selection = s;
return;
}
if (s.base().anchorNode()) {
Document* document = s.base().anchorNode()->document();
if (document && document->frame() && document->frame() != m_frame && document != m_frame->document()) {
document->frame()->selection()->setSelection(s, options);
return;
}
}
if (closeTyping)
TypingCommand::closeTyping(m_frame->editor()->lastEditCommand());
if (shouldClearTypingStyle)
clearTypingStyle();
if (m_selection == s) {
notifyRendererOfSelectionChange(userTriggered);
return;
}
VisibleSelection oldSelection = m_selection;
m_selection = s;
setCaretRectNeedsUpdate();
if (!s.isNone())
setFocusedNodeIfNeeded();
updateAppearance();
m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation;
selectFrameElementInParentIfFullySelected();
notifyRendererOfSelectionChange(userTriggered);
m_frame->editor()->respondToChangedSelection(oldSelection, options);
if (userTriggered) {
ScrollAlignment alignment;
if (m_frame->editor()->behavior().shouldCenterAlignWhenSelectionIsRevealed())
alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
else
alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
revealSelection(alignment, true);
}
notifyAccessibilityForSelectionChange();
m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
}
static bool removingNodeRemovesPosition(Node* node, const Position& position)
{
if (!position.anchorNode())
return false;
if (position.anchorNode() == node)
return true;
if (!node->isElementNode())
return false;
Element* element = static_cast<Element*>(node);
return element->contains(position.anchorNode()) || element->contains(position.anchorNode()->shadowAncestorNode());
}
static void clearRenderViewSelection(const Position& position)
{
RefPtr<Document> document = position.anchorNode()->document();
document->updateStyleIfNeeded();
if (RenderView* view = toRenderView(document->renderer()))
view->clearSelection();
}
void DragCaretController::nodeWillBeRemoved(Node* node)
{
if (!hasCaret() || (node && !node->inDocument()))
return;
if (!removingNodeRemovesPosition(node, m_position.deepEquivalent()))
return;
clearRenderViewSelection(m_position.deepEquivalent());
clear();
}
void FrameSelection::nodeWillBeRemoved(Node* node)
{
if (isNone() || (node && !node->inDocument()))
return;
respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
}
void FrameSelection::respondToNodeModification(Node* node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
{
bool clearRenderTreeSelection = false;
bool clearDOMTreeSelection = false;
if (startRemoved || endRemoved) {
clearRenderTreeSelection = true;
clearDOMTreeSelection = true;
} else if (baseRemoved || extentRemoved) {
if (m_selection.isBaseFirst())
m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
else
m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
} else if (RefPtr<Range> range = m_selection.firstRange()) {
ExceptionCode ec = 0;
Range::CompareResults compareResult = range->compareNode(node, ec);
if (!ec && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
clearRenderTreeSelection = true;
}
}
if (clearRenderTreeSelection)
clearRenderViewSelection(m_selection.start());
if (clearDOMTreeSelection)
setSelection(VisibleSelection(), 0);
}
enum EndPointType { EndPointIsStart, EndPointIsEnd };
static bool shouldRemovePositionAfterAdoptingTextReplacement(Position& position, EndPointType type, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
{
if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
return false;
ASSERT(position.offsetInContainerNode() >= 0);
unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
if (positionOffset > offset && positionOffset < offset + oldLength)
return true;
if ((positionOffset > offset + oldLength) || (positionOffset == offset + oldLength && (type == EndPointIsStart || oldLength)))
position.moveToOffset(positionOffset - oldLength + newLength);
return false;
}
void FrameSelection::textWillBeReplaced(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
{
if (isNone() || !node || highestAncestor(node)->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)
return;
Position base = m_selection.base();
Position extent = m_selection.extent();
Position start = m_selection.start();
Position end = m_selection.end();
bool shouldRemoveBase = shouldRemovePositionAfterAdoptingTextReplacement(base, m_selection.isBaseFirst() ? EndPointIsStart : EndPointIsEnd, node, offset, oldLength, newLength);
bool shouldRemoveExtent = shouldRemovePositionAfterAdoptingTextReplacement(extent, m_selection.isBaseFirst() ? EndPointIsEnd : EndPointIsStart, node, offset, oldLength, newLength);
bool shouldRemoveStart = shouldRemovePositionAfterAdoptingTextReplacement(start, EndPointIsStart, node, offset, oldLength, newLength);
bool shouldRemoveEnd = shouldRemovePositionAfterAdoptingTextReplacement(end, EndPointIsEnd, node, offset, oldLength, newLength);
if ((base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end())
&& !shouldRemoveStart && !shouldRemoveEnd) {
VisibleSelection newSelection;
if (!shouldRemoveBase && !shouldRemoveExtent)
newSelection.setWithoutValidation(base, extent);
else {
if (newSelection.isBaseFirst())
newSelection.setWithoutValidation(start, end);
else
newSelection.setWithoutValidation(end, start);
}
m_frame->document()->updateLayout();
setSelection(newSelection, 0);
return;
}
respondToNodeModification(node, shouldRemoveBase, shouldRemoveExtent, shouldRemoveStart, shouldRemoveEnd);
}
void FrameSelection::setIsDirectional(bool isDirectional)
{
m_isDirectional = !m_frame || m_frame->editor()->behavior().shouldConsiderSelectionAsDirectional() || isDirectional;
}
TextDirection FrameSelection::directionOfEnclosingBlock()
{
return WebCore::directionOfEnclosingBlock(m_selection.extent());
}
void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
{
if (alter != AlterationExtend)
return;
Position start = m_selection.start();
Position end = m_selection.end();
bool baseIsStart = true;
if (m_isDirectional) {
if (m_selection.isBaseFirst())
baseIsStart = true;
else
baseIsStart = false;
} else {
switch (direction) {
case DirectionRight:
if (directionOfEnclosingBlock() == LTR)
baseIsStart = true;
else
baseIsStart = false;
break;
case DirectionForward:
baseIsStart = true;
break;
case DirectionLeft:
if (directionOfEnclosingBlock() == LTR)
baseIsStart = false;
else
baseIsStart = true;
break;
case DirectionBackward:
baseIsStart = false;
break;
}
}
if (baseIsStart) {
m_selection.setBase(start);
m_selection.setExtent(end);
} else {
m_selection.setBase(end);
m_selection.setExtent(start);
}
}
VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
{
Settings* settings = m_frame ? m_frame->settings() : 0;
if (settings && settings->editingBehaviorType() == EditingMacBehavior)
return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
}
VisiblePosition FrameSelection::startForPlatform() const
{
return positionForPlatform(true);
}
VisiblePosition FrameSelection::endForPlatform() const
{
return positionForPlatform(false);
}
VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
{
VisiblePosition pos(m_selection.extent(), m_selection.affinity());
switch (granularity) {
case CharacterGranularity:
if (directionOfEnclosingBlock() == LTR)
pos = pos.next(CannotCrossEditingBoundary);
else
pos = pos.previous(CannotCrossEditingBoundary);
break;
case WordGranularity:
if (directionOfEnclosingBlock() == LTR)
pos = nextWordPosition(pos);
else
pos = previousWordPosition(pos);
break;
case LineBoundary:
if (directionOfEnclosingBlock() == LTR)
pos = modifyExtendingForward(granularity);
else
pos = modifyExtendingBackward(granularity);
break;
case SentenceGranularity:
case LineGranularity:
case ParagraphGranularity:
case SentenceBoundary:
case ParagraphBoundary:
case DocumentBoundary:
pos = modifyExtendingForward(granularity);
break;
case WebKitVisualWordGranularity:
break;
}
return pos;
}
VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
{
VisiblePosition pos(m_selection.extent(), m_selection.affinity());
switch (granularity) {
case CharacterGranularity:
pos = pos.next(CannotCrossEditingBoundary);
break;
case WordGranularity:
pos = nextWordPosition(pos);
break;
case SentenceGranularity:
pos = nextSentencePosition(pos);
break;
case LineGranularity:
pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
break;
case ParagraphGranularity:
pos = nextParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
break;
case SentenceBoundary:
pos = endOfSentence(endForPlatform());
break;
case LineBoundary:
pos = logicalEndOfLine(endForPlatform());
break;
case ParagraphBoundary:
pos = endOfParagraph(endForPlatform());
break;
case DocumentBoundary:
pos = endForPlatform();
if (isEditablePosition(pos.deepEquivalent()))
pos = endOfEditableContent(pos);
else
pos = endOfDocument(pos);
break;
case WebKitVisualWordGranularity:
break;
}
return pos;
}
VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
{
VisiblePosition pos;
switch (granularity) {
case CharacterGranularity:
if (isRange()) {
if (directionOfEnclosingBlock() == LTR)
pos = VisiblePosition(m_selection.end(), m_selection.affinity());
else
pos = VisiblePosition(m_selection.start(), m_selection.affinity());
} else
pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
break;
case WordGranularity:
case SentenceGranularity:
case LineGranularity:
case ParagraphGranularity:
case SentenceBoundary:
case ParagraphBoundary:
case DocumentBoundary:
pos = modifyMovingForward(granularity);
break;
case LineBoundary:
pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
break;
case WebKitVisualWordGranularity:
pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
break;
}
return pos;
}
VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
{
VisiblePosition pos;
switch (granularity) {
case CharacterGranularity:
if (isRange())
pos = VisiblePosition(m_selection.end(), m_selection.affinity());
else
pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary);
break;
case WordGranularity:
pos = nextWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
break;
case SentenceGranularity:
pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
break;
case LineGranularity: {
pos = endForPlatform();
if (!isRange() || !isStartOfLine(pos))
pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(START));
break;
}
case ParagraphGranularity:
pos = nextParagraphPosition(endForPlatform(), xPosForVerticalArrowNavigation(START));
break;
case SentenceBoundary:
pos = endOfSentence(endForPlatform());
break;
case LineBoundary:
pos = logicalEndOfLine(endForPlatform());
break;
case ParagraphBoundary:
pos = endOfParagraph(endForPlatform());
break;
case DocumentBoundary:
pos = endForPlatform();
if (isEditablePosition(pos.deepEquivalent()))
pos = endOfEditableContent(pos);
else
pos = endOfDocument(pos);
break;
case WebKitVisualWordGranularity:
break;
}
return pos;
}
VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
{
VisiblePosition pos(m_selection.extent(), m_selection.affinity());
switch (granularity) {
case CharacterGranularity:
if (directionOfEnclosingBlock() == LTR)
pos = pos.previous(CannotCrossEditingBoundary);
else
pos = pos.next(CannotCrossEditingBoundary);
break;
case WordGranularity:
if (directionOfEnclosingBlock() == LTR)
pos = previousWordPosition(pos);
else
pos = nextWordPosition(pos);
break;
case LineBoundary:
if (directionOfEnclosingBlock() == LTR)
pos = modifyExtendingBackward(granularity);
else
pos = modifyExtendingForward(granularity);
break;
case SentenceGranularity:
case LineGranularity:
case ParagraphGranularity:
case SentenceBoundary:
case ParagraphBoundary:
case DocumentBoundary:
pos = modifyExtendingBackward(granularity);
break;
case WebKitVisualWordGranularity:
break;
}
return pos;
}
VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
{
VisiblePosition pos(m_selection.extent(), m_selection.affinity());
switch (granularity) {
case CharacterGranularity:
pos = pos.previous(CannotCrossEditingBoundary);
break;
case WordGranularity:
pos = previousWordPosition(pos);
break;
case SentenceGranularity:
pos = previousSentencePosition(pos);
break;
case LineGranularity:
pos = previousLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
break;
case ParagraphGranularity:
pos = previousParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
break;
case SentenceBoundary:
pos = startOfSentence(startForPlatform());
break;
case LineBoundary:
pos = logicalStartOfLine(startForPlatform());
break;
case ParagraphBoundary:
pos = startOfParagraph(startForPlatform());
break;
case DocumentBoundary:
pos = startForPlatform();
if (isEditablePosition(pos.deepEquivalent()))
pos = startOfEditableContent(pos);
else
pos = startOfDocument(pos);
break;
case WebKitVisualWordGranularity:
break;
}
return pos;
}
VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
{
VisiblePosition pos;
switch (granularity) {
case CharacterGranularity:
if (isRange())
if (directionOfEnclosingBlock() == LTR)
pos = VisiblePosition(m_selection.start(), m_selection.affinity());
else
pos = VisiblePosition(m_selection.end(), m_selection.affinity());
else
pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
break;
case WordGranularity:
case SentenceGranularity:
case LineGranularity:
case ParagraphGranularity:
case SentenceBoundary:
case ParagraphBoundary:
case DocumentBoundary:
pos = modifyMovingBackward(granularity);
break;
case LineBoundary:
pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
break;
case WebKitVisualWordGranularity:
pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
break;
}
return pos;
}
VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity)
{
VisiblePosition pos;
switch (granularity) {
case CharacterGranularity:
if (isRange())
pos = VisiblePosition(m_selection.start(), m_selection.affinity());
else
pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary);
break;
case WordGranularity:
pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
break;
case SentenceGranularity:
pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
break;
case LineGranularity:
pos = previousLinePosition(startForPlatform(), xPosForVerticalArrowNavigation(START));
break;
case ParagraphGranularity:
pos = previousParagraphPosition(startForPlatform(), xPosForVerticalArrowNavigation(START));
break;
case SentenceBoundary:
pos = startOfSentence(startForPlatform());
break;
case LineBoundary:
pos = logicalStartOfLine(startForPlatform());
break;
case ParagraphBoundary:
pos = startOfParagraph(startForPlatform());
break;
case DocumentBoundary:
pos = startForPlatform();
if (isEditablePosition(pos.deepEquivalent()))
pos = startOfEditableContent(pos);
else
pos = startOfDocument(pos);
break;
case WebKitVisualWordGranularity:
break;
}
return pos;
}
static bool isBoundary(TextGranularity granularity)
{
return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
}
bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, bool userTriggered)
{
if (userTriggered) {
FrameSelection trialFrameSelection;
trialFrameSelection.setSelection(m_selection);
trialFrameSelection.setIsDirectional(m_isDirectional);
trialFrameSelection.modify(alter, direction, granularity, false);
bool change = shouldChangeSelection(trialFrameSelection.selection());
if (!change)
return false;
}
willBeModified(alter, direction);
bool wasRange = m_selection.isRange();
Position originalStartPosition = m_selection.start();
VisiblePosition position;
switch (direction) {
case DirectionRight:
if (alter == AlterationMove)
position = modifyMovingRight(granularity);
else
position = modifyExtendingRight(granularity);
break;
case DirectionForward:
if (alter == AlterationExtend)
position = modifyExtendingForward(granularity);
else
position = modifyMovingForward(granularity);
break;
case DirectionLeft:
if (alter == AlterationMove)
position = modifyMovingLeft(granularity);
else
position = modifyExtendingLeft(granularity);
break;
case DirectionBackward:
if (alter == AlterationExtend)
position = modifyExtendingBackward(granularity);
else
position = modifyMovingBackward(granularity);
break;
}
if (position.isNull())
return false;
if (isSpatialNavigationEnabled(m_frame))
if (!wasRange && alter == AlterationMove && position == originalStartPosition)
return false;
int x = xPosForVerticalArrowNavigation(START);
switch (alter) {
case AlterationMove:
moveTo(position, userTriggered);
break;
case AlterationExtend:
if (!m_frame || !m_frame->editor()->behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
setExtent(position, userTriggered);
else {
TextDirection textDirection = directionOfEnclosingBlock();
if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
setEnd(position, userTriggered);
else
setStart(position, userTriggered);
}
break;
}
if (granularity == LineGranularity || granularity == ParagraphGranularity)
m_xPosForVerticalArrowNavigation = x;
if (userTriggered)
m_granularity = CharacterGranularity;
setCaretRectNeedsUpdate();
setIsDirectional(alter == AlterationExtend);
return true;
}
static bool absoluteCaretY(const VisiblePosition &c, int &y)
{
IntRect rect = c.absoluteCaretBounds();
if (rect.isEmpty())
return false;
y = rect.y() + rect.height() / 2;
return true;
}
bool FrameSelection::modify(EAlteration alter, int verticalDistance, bool userTriggered, CursorAlignOnScroll align)
{
if (!verticalDistance)
return false;
if (userTriggered) {
FrameSelection trialFrameSelection;
trialFrameSelection.setSelection(m_selection);
trialFrameSelection.setIsDirectional(m_isDirectional);
trialFrameSelection.modify(alter, verticalDistance, false);
bool change = shouldChangeSelection(trialFrameSelection.selection());
if (!change)
return false;
}
bool up = verticalDistance < 0;
if (up)
verticalDistance = -verticalDistance;
willBeModified(alter, up ? DirectionBackward : DirectionForward);
VisiblePosition pos;
int xPos = 0;
switch (alter) {
case AlterationMove:
pos = VisiblePosition(up ? m_selection.start() : m_selection.end(), m_selection.affinity());
xPos = xPosForVerticalArrowNavigation(up ? START : END);
m_selection.setAffinity(up ? UPSTREAM : DOWNSTREAM);
break;
case AlterationExtend:
pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
xPos = xPosForVerticalArrowNavigation(EXTENT);
m_selection.setAffinity(DOWNSTREAM);
break;
}
int startY;
if (!absoluteCaretY(pos, startY))
return false;
if (up)
startY = -startY;
int lastY = startY;
VisiblePosition result;
VisiblePosition next;
for (VisiblePosition p = pos; ; p = next) {
next = (up ? previousLinePosition : nextLinePosition)(p, xPos);
if (next.isNull() || next == p)
break;
int nextY;
if (!absoluteCaretY(next, nextY))
break;
if (up)
nextY = -nextY;
if (nextY - startY > verticalDistance)
break;
if (nextY >= lastY) {
lastY = nextY;
result = next;
}
}
if (result.isNull())
return false;
switch (alter) {
case AlterationMove:
moveTo(result, userTriggered, align);
break;
case AlterationExtend:
setExtent(result, userTriggered);
break;
}
if (userTriggered)
m_granularity = CharacterGranularity;
setIsDirectional(alter == AlterationExtend);
return true;
}
int FrameSelection::xPosForVerticalArrowNavigation(EPositionType type)
{
int x = 0;
if (isNone())
return x;
Position pos;
switch (type) {
case START:
pos = m_selection.start();
break;
case END:
pos = m_selection.end();
break;
case BASE:
pos = m_selection.base();
break;
case EXTENT:
pos = m_selection.extent();
break;
}
Frame* frame = pos.anchorNode()->document()->frame();
if (!frame)
return x;
if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation) {
VisiblePosition visiblePosition(pos, m_selection.affinity());
x = visiblePosition.isNotNull() ? visiblePosition.xOffsetForVerticalNavigation() : 0;
m_xPosForVerticalArrowNavigation = x;
} else
x = m_xPosForVerticalArrowNavigation;
return x;
}
void FrameSelection::clear()
{
m_granularity = CharacterGranularity;
setSelection(VisibleSelection());
}
void FrameSelection::setStart(const VisiblePosition &pos, bool userTriggered)
{
if (m_selection.isBaseFirst())
setBase(pos, userTriggered);
else
setExtent(pos, userTriggered);
}
void FrameSelection::setEnd(const VisiblePosition &pos, bool userTriggered)
{
if (m_selection.isBaseFirst())
setExtent(pos, userTriggered);
else
setBase(pos, userTriggered);
}
void FrameSelection::setBase(const VisiblePosition &pos, bool userTriggered)
{
SetSelectionOptions options = CloseTyping | ClearTypingStyle;
if (userTriggered)
options |= UserTriggered;
setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity()), options);
}
void FrameSelection::setExtent(const VisiblePosition &pos, bool userTriggered)
{
SetSelectionOptions options = CloseTyping | ClearTypingStyle;
if (userTriggered)
options |= UserTriggered;
setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity()), options);
}
void FrameSelection::setBase(const Position &pos, EAffinity affinity, bool userTriggered)
{
SetSelectionOptions options = CloseTyping | ClearTypingStyle;
if (userTriggered)
options |= UserTriggered;
setSelection(VisibleSelection(pos, m_selection.extent(), affinity), options);
}
void FrameSelection::setExtent(const Position &pos, EAffinity affinity, bool userTriggered)
{
SetSelectionOptions options = CloseTyping | ClearTypingStyle;
if (userTriggered)
options |= UserTriggered;
setSelection(VisibleSelection(m_selection.base(), pos, affinity), options);
}
void CaretBase::clearCaretRect()
{
m_caretLocalRect = IntRect();
}
bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition)
{
document->updateStyleIfNeeded();
m_caretLocalRect = IntRect();
m_caretRectNeedsUpdate = false;
if (caretPosition.isNull())
return false;
ASSERT(caretPosition.deepEquivalent().deprecatedNode()->renderer());
RenderObject* renderer;
IntRect localRect = caretPosition.localCaretRect(renderer);
RenderObject* caretPainter = caretRenderer(caretPosition.deepEquivalent().deprecatedNode());
bool unrooted = false;
while (renderer != caretPainter) {
RenderObject* containerObject = renderer->container();
if (!containerObject) {
unrooted = true;
break;
}
localRect.move(renderer->offsetFromContainer(containerObject, localRect.location()));
renderer = containerObject;
}
if (!unrooted)
m_caretLocalRect = localRect;
return true;
}
static inline bool caretRendersInsideNode(Node* node)
{
return node && !isTableElement(node) && !editingIgnoresContent(node);
}
RenderObject* CaretBase::caretRenderer(Node* node) const
{
if (!node)
return 0;
RenderObject* renderer = node->renderer();
if (!renderer)
return 0;
bool paintedByBlock = renderer->isBlockFlow() && caretRendersInsideNode(node);
return paintedByBlock ? renderer : renderer->containingBlock();
}
RenderObject* FrameSelection::caretRenderer() const
{
return CaretBase::caretRenderer(m_selection.start().deprecatedNode());
}
RenderObject* DragCaretController::caretRenderer() const
{
return CaretBase::caretRenderer(m_position.deepEquivalent().deprecatedNode());
}
IntRect FrameSelection::localCaretRect()
{
if (shouldUpdateCaretRect()) {
if (!isCaret() || m_selection.start().isOrphan() || m_selection.end().isOrphan())
clearCaretRect();
else if (updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity())))
m_absCaretBoundsDirty = true;
}
return localCaretRectWithoutUpdate();
}
IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const IntRect& rect) const
{
RenderObject* caretPainter = caretRenderer(node);
if (!caretPainter)
return IntRect();
IntRect localRect(rect);
if (caretPainter->isBox())
toRenderBox(caretPainter)->flipForWritingMode(localRect);
return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
}
IntRect FrameSelection::absoluteCaretBounds()
{
recomputeCaretRect();
return m_absCaretBounds;
}
static IntRect repaintRectForCaret(IntRect caret)
{
if (caret.isEmpty())
return IntRect();
caret.inflateX(1);
return caret;
}
IntRect CaretBase::caretRepaintRect(Node* node) const
{
return absoluteBoundsForLocalRect(node, repaintRectForCaret(localCaretRectWithoutUpdate()));
}
bool FrameSelection::recomputeCaretRect()
{
if (!shouldUpdateCaretRect())
return false;
if (!m_frame)
return false;
FrameView* v = m_frame->document()->view();
if (!v)
return false;
IntRect oldRect = localCaretRectWithoutUpdate();
IntRect newRect = localCaretRect();
if (oldRect == newRect && !m_absCaretBoundsDirty)
return false;
IntRect oldAbsCaretBounds = m_absCaretBounds;
m_absCaretBounds = absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate());
m_absCaretBoundsDirty = false;
if (oldAbsCaretBounds == m_absCaretBounds)
return false;
IntRect oldAbsoluteCaretRepaintBounds = m_absoluteCaretRepaintBounds;
m_absoluteCaretRepaintBounds = caretRepaintRect(m_selection.start().deprecatedNode());
#if ENABLE(TEXT_CARET)
if (RenderView* view = toRenderView(m_frame->document()->renderer())) {
view->repaintRectangleInViewAndCompositedLayers(oldAbsoluteCaretRepaintBounds, false);
if (shouldRepaintCaret(view, isContentEditable()))
view->repaintRectangleInViewAndCompositedLayers(m_absoluteCaretRepaintBounds, false);
}
#endif
return true;
}
bool CaretBase::shouldRepaintCaret(const RenderView* view, bool isContentEditable) const
{
ASSERT(view);
Frame* frame = view->frameView() ? view->frameView()->frame() : 0; bool caretBrowsing = frame && frame->settings() && frame->settings()->caretBrowsingEnabled();
return (caretBrowsing || isContentEditable);
}
void FrameSelection::invalidateCaretRect()
{
if (!isCaret())
return;
CaretBase::invalidateCaretRect(m_selection.start().deprecatedNode(), recomputeCaretRect());
}
void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged)
{
m_caretRectNeedsUpdate = true;
if (!caretRectChanged) {
RenderView* view = toRenderView(node->document()->renderer());
if (view && shouldRepaintCaret(view, node->isContentEditable()))
view->repaintRectangleInViewAndCompositedLayers(caretRepaintRect(node), false);
}
}
void FrameSelection::paintCaret(GraphicsContext* context, int tx, int ty, const IntRect& clipRect)
{
if (m_selection.isCaret() && m_caretPaint)
CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, tx, ty, clipRect);
}
void CaretBase::paintCaret(Node* node, GraphicsContext* context, int tx, int ty, const IntRect& clipRect) const
{
#if ENABLE(TEXT_CARET)
if (m_caretVisibility == Hidden)
return;
IntRect drawingRect = localCaretRectWithoutUpdate();
RenderObject* renderer = caretRenderer(node);
if (renderer && renderer->isBox())
toRenderBox(renderer)->flipForWritingMode(drawingRect);
drawingRect.move(tx, ty);
IntRect caret = intersection(drawingRect, clipRect);
if (caret.isEmpty())
return;
Color caretColor = Color::black;
ColorSpace colorSpace = ColorSpaceDeviceRGB;
Element* element = node->rootEditableElement();
if (element && element->renderer()) {
caretColor = element->renderer()->style()->visitedDependentColor(CSSPropertyColor);
colorSpace = element->renderer()->style()->colorSpace();
}
context->fillRect(caret, caretColor, colorSpace);
#else
UNUSED_PARAM(node);
UNUSED_PARAM(context);
UNUSED_PARAM(tx);
UNUSED_PARAM(ty);
UNUSED_PARAM(clipRect);
#endif
}
void FrameSelection::debugRenderer(RenderObject *r, bool selected) const
{
if (r->node()->isElementNode()) {
Element* element = static_cast<Element *>(r->node());
fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data());
} else if (r->isText()) {
RenderText* textRenderer = toRenderText(r);
if (!textRenderer->textLength() || !textRenderer->firstTextBox()) {
fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
return;
}
static const int max = 36;
String text = textRenderer->text();
int textLength = text.length();
if (selected) {
int offset = 0;
if (r->node() == m_selection.start().containerNode())
offset = m_selection.start().computeOffsetInContainerNode();
else if (r->node() == m_selection.end().containerNode())
offset = m_selection.end().computeOffsetInContainerNode();
int pos;
InlineTextBox* box = textRenderer->findNextInlineTextBox(offset, pos);
text = text.substring(box->start(), box->len());
String show;
int mid = max / 2;
int caret = 0;
if (textLength < max) {
show = text;
caret = pos;
} else if (pos - mid < 0) {
show = text.left(max - 3) + "...";
caret = pos;
} else if (pos - mid >= 0 && pos + mid <= textLength) {
show = "..." + text.substring(pos - mid + 3, max - 6) + "...";
caret = mid;
} else {
show = "..." + text.right(max - 3);
caret = pos - (textLength - show.length());
}
show.replace('\n', ' ');
show.replace('\r', ' ');
fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos);
fprintf(stderr, " ");
for (int i = 0; i < caret; i++)
fprintf(stderr, " ");
fprintf(stderr, "^\n");
} else {
if ((int)text.length() > max)
text = text.left(max - 3) + "...";
else
text = text.left(max);
fprintf(stderr, " #text : \"%s\"\n", text.utf8().data());
}
}
}
bool FrameSelection::contains(const IntPoint& point)
{
Document* document = m_frame->document();
if (!isRange())
return false;
if (!document->renderer())
return false;
HitTestRequest request(HitTestRequest::ReadOnly |
HitTestRequest::Active);
HitTestResult result(point);
document->renderView()->layer()->hitTest(request, result);
Node* innerNode = result.innerNode();
if (!innerNode || !innerNode->renderer())
return false;
VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
if (visiblePos.isNull())
return false;
if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
return false;
Position start(m_selection.visibleStart().deepEquivalent());
Position end(m_selection.visibleEnd().deepEquivalent());
Position p(visiblePos.deepEquivalent());
return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
}
void FrameSelection::selectFrameElementInParentIfFullySelected()
{
Frame* parent = m_frame->tree()->parent();
if (!parent)
return;
Page* page = m_frame->page();
if (!page)
return;
if (!isRange())
return;
if (!isStartOfDocument(selection().visibleStart()))
return;
if (!isEndOfDocument(selection().visibleEnd()))
return;
Element* ownerElement = m_frame->ownerElement();
if (!ownerElement)
return;
ContainerNode* ownerElementParent = ownerElement->parentNode();
if (!ownerElementParent)
return;
if (!ownerElementParent->rendererIsEditable())
return;
unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
if (parent->selection()->shouldChangeSelection(newSelection)) {
page->focusController()->setFocusedFrame(parent);
parent->selection()->setSelection(newSelection);
}
}
void FrameSelection::selectAll()
{
Document* document = m_frame->document();
if (document->focusedNode() && document->focusedNode()->hasTagName(selectTag)) {
HTMLSelectElement* selectElement = static_cast<HTMLSelectElement*>(document->focusedNode());
if (selectElement->canSelectAll()) {
selectElement->selectAll();
return;
}
}
Node* root = 0;
Node* selectStartTarget = 0;
if (isContentEditable()) {
root = highestEditableRoot(m_selection.start());
if (Node* shadowRoot = shadowTreeRootNode())
selectStartTarget = shadowRoot->shadowHost();
else
selectStartTarget = root;
} else {
root = shadowTreeRootNode();
if (root)
selectStartTarget = root->shadowHost();
else {
root = document->documentElement();
selectStartTarget = document->body();
}
}
if (!root)
return;
if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true)))
return;
VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root));
if (shouldChangeSelection(newSelection))
setSelection(newSelection);
selectFrameElementInParentIfFullySelected();
notifyRendererOfSelectionChange(true);
}
bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping)
{
if (!range)
return false;
ExceptionCode ec = 0;
Node* startContainer = range->startContainer(ec);
if (ec)
return false;
Node* endContainer = range->endContainer(ec);
if (ec)
return false;
ASSERT(startContainer);
ASSERT(endContainer);
ASSERT(startContainer->document() == endContainer->document());
m_frame->document()->updateLayoutIgnorePendingStylesheets();
bool collapsed = range->collapsed(ec);
if (ec)
return false;
int startOffset = range->startOffset(ec);
if (ec)
return false;
int endOffset = range->endOffset(ec);
if (ec)
return false;
VisiblePosition visibleStart(Position(startContainer, startOffset, Position::PositionIsOffsetInAnchor), collapsed ? affinity : DOWNSTREAM);
VisiblePosition visibleEnd(Position(endContainer, endOffset, Position::PositionIsOffsetInAnchor), SEL_DEFAULT_AFFINITY);
SetSelectionOptions options = ClearTypingStyle;
if (closeTyping)
options |= CloseTyping;
setSelection(VisibleSelection(visibleStart, visibleEnd), options);
return true;
}
bool FrameSelection::isInPasswordField() const
{
ASSERT(start().isNull() || start().anchorType() == Position::PositionIsOffsetInAnchor
|| start().containerNode() || !start().anchorNode()->shadowAncestorNode());
Node* startNode = start().containerNode();
if (!startNode)
return false;
startNode = startNode->shadowAncestorNode();
if (!startNode)
return false;
if (!startNode->hasTagName(inputTag))
return false;
return static_cast<HTMLInputElement*>(startNode)->isPasswordField();
}
void FrameSelection::focusedOrActiveStateChanged()
{
bool activeAndFocused = isFocusedAndActive();
if (RenderView* view = toRenderView(m_frame->document()->renderer()))
view->repaintRectangleInViewAndCompositedLayers(enclosingIntRect(bounds()));
if (activeAndFocused)
setSelectionFromNone();
setCaretVisibility(activeAndFocused ? Visible : Hidden);
m_frame->eventHandler()->capsLockStateMayHaveChanged();
if (Node* node = m_frame->document()->focusedNode()) {
node->setNeedsStyleRecalc();
if (RenderObject* renderer = node->renderer())
if (renderer && renderer->style()->hasAppearance())
renderer->theme()->stateChanged(renderer, FocusState);
}
if (m_frame->document()->useSecureKeyboardEntryWhenActive())
setUseSecureKeyboardEntry(activeAndFocused);
}
void FrameSelection::pageActivationChanged()
{
focusedOrActiveStateChanged();
}
void FrameSelection::updateSecureKeyboardEntryIfActive()
{
if (m_frame->document() && isFocusedAndActive())
setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive());
}
void FrameSelection::setUseSecureKeyboardEntry(bool enable)
{
if (enable)
enableSecureTextInput();
else
disableSecureTextInput();
}
void FrameSelection::setFocused(bool flag)
{
if (m_focused == flag)
return;
m_focused = flag;
focusedOrActiveStateChanged();
}
bool FrameSelection::isFocusedAndActive() const
{
return m_focused && m_frame->page() && m_frame->page()->focusController()->isActive();
}
void FrameSelection::updateAppearance()
{
#if ENABLE(TEXT_CARET)
bool caretRectChanged = recomputeCaretRect();
bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
bool shouldBlink = caretIsVisible() && isCaret() && (isContentEditable() || caretBrowsing);
if (caretRectChanged || !shouldBlink)
m_caretBlinkTimer.stop();
if (shouldBlink && !m_caretBlinkTimer.isActive()) {
if (double blinkInterval = m_frame->page()->theme()->caretBlinkInterval())
m_caretBlinkTimer.startRepeating(blinkInterval);
if (!m_caretPaint) {
m_caretPaint = true;
invalidateCaretRect();
}
}
#endif
m_frame->document()->updateStyleIfNeeded();
RenderView* view = m_frame->contentRenderer();
if (!view)
return;
VisibleSelection selection = this->selection();
if (!selection.isRange()) {
view->clearSelection();
return;
}
Position startPos = selection.start();
Position candidate = startPos.downstream();
if (candidate.isCandidate())
startPos = candidate;
Position endPos = selection.end();
candidate = endPos.upstream();
if (candidate.isCandidate())
endPos = candidate;
if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
}
}
void FrameSelection::setCaretVisibility(CaretVisibility visibility)
{
if (caretVisibility() == visibility)
return;
clearCaretRectIfNeeded();
CaretBase::setCaretVisibility(visibility);
updateAppearance();
}
void FrameSelection::clearCaretRectIfNeeded()
{
#if ENABLE(TEXT_CARET)
if (!m_caretPaint)
return;
m_caretPaint = false;
invalidateCaretRect();
#endif
}
void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>*)
{
#if ENABLE(TEXT_CARET)
ASSERT(caretIsVisible());
ASSERT(isCaret());
bool caretPaint = m_caretPaint;
if (isCaretBlinkingSuspended() && caretPaint)
return;
m_caretPaint = !caretPaint;
invalidateCaretRect();
#endif
}
void FrameSelection::notifyRendererOfSelectionChange(bool userTriggered)
{
m_frame->document()->updateStyleIfNeeded();
if (!rootEditableElement())
return;
RenderObject* renderer = rootEditableElement()->shadowAncestorNode()->renderer();
if (!renderer || !renderer->isTextControl())
return;
toRenderTextControl(renderer)->selectionChanged(userTriggered);
}
static bool isFrameElement(const Node* n)
{
if (!n)
return false;
RenderObject* renderer = n->renderer();
if (!renderer || !renderer->isWidget())
return false;
Widget* widget = toRenderWidget(renderer)->widget();
return widget && widget->isFrameView();
}
void FrameSelection::setFocusedNodeIfNeeded()
{
if (isNone() || !isFocused())
return;
bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
if (caretBrowsing) {
if (Node* anchor = enclosingAnchorElement(base())) {
m_frame->page()->focusController()->setFocusedNode(anchor, m_frame);
return;
}
}
if (Node* target = rootEditableElement()) {
while (target) {
if (target && target->isMouseFocusable() && !isFrameElement(target)) {
m_frame->page()->focusController()->setFocusedNode(target, m_frame);
return;
}
target = target->parentOrHostNode();
}
m_frame->document()->setFocusedNode(0);
}
if (caretBrowsing)
m_frame->page()->focusController()->setFocusedNode(0, m_frame);
}
void DragCaretController::paintDragCaret(Frame* frame, GraphicsContext* p, int tx, int ty, const IntRect& clipRect) const
{
#if ENABLE(TEXT_CARET)
if (m_position.deepEquivalent().deprecatedNode()->document()->frame() == frame)
paintCaret(m_position.deepEquivalent().deprecatedNode(), p, tx, ty, clipRect);
#else
UNUSED_PARAM(frame);
UNUSED_PARAM(p);
UNUSED_PARAM(tx);
UNUSED_PARAM(ty);
UNUSED_PARAM(clipRect);
#endif
}
PassRefPtr<CSSMutableStyleDeclaration> FrameSelection::copyTypingStyle() const
{
if (!m_typingStyle || !m_typingStyle->style())
return 0;
return m_typingStyle->style()->copy();
}
bool FrameSelection::shouldDeleteSelection(const VisibleSelection& selection) const
{
return m_frame->editor()->client()->shouldDeleteRange(selection.toNormalizedRange().get());
}
FloatRect FrameSelection::bounds(bool clipToVisibleContent) const
{
RenderView* root = m_frame->contentRenderer();
FrameView* view = m_frame->view();
if (!root || !view)
return IntRect();
IntRect selectionRect = root->selectionBounds(clipToVisibleContent);
return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect;
}
void FrameSelection::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles) const
{
RenderView* root = m_frame->contentRenderer();
if (!root)
return;
FloatRect visibleContentRect = m_frame->view()->visibleContentRect();
Vector<FloatQuad> quads;
toNormalizedRange()->textQuads(quads, true);
size_t size = quads.size();
for (size_t i = 0; i < size; ++i)
rectangles.append(intersection(quads[i].enclosingBoundingBox(), visibleContentRect));
}
static HTMLFormElement* scanForForm(Node* start)
{
for (Node* node = start; node; node = node->traverseNextNode()) {
if (node->hasTagName(formTag))
return static_cast<HTMLFormElement*>(node);
if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
return static_cast<HTMLFormControlElement*>(node)->form();
if (node->hasTagName(frameTag) || node->hasTagName(iframeTag)) {
Node* childDocument = static_cast<HTMLFrameElementBase*>(node)->contentDocument();
if (HTMLFormElement* frameResult = scanForForm(childDocument))
return frameResult;
}
}
return 0;
}
HTMLFormElement* FrameSelection::currentForm() const
{
Node* start = m_frame->document()->focusedNode();
if (!start)
start = this->start().deprecatedNode();
Node* node;
for (node = start; node; node = node->parentNode()) {
if (node->hasTagName(formTag))
return static_cast<HTMLFormElement*>(node);
if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
return static_cast<HTMLFormControlElement*>(node)->form();
}
return scanForForm(start);
}
void FrameSelection::revealSelection(const ScrollAlignment& alignment, bool revealExtent)
{
IntRect rect;
switch (selectionType()) {
case VisibleSelection::NoSelection:
return;
case VisibleSelection::CaretSelection:
rect = absoluteCaretBounds();
break;
case VisibleSelection::RangeSelection:
rect = revealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds(false));
break;
}
Position start = this->start();
ASSERT(start.deprecatedNode());
if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
if (RenderLayer* layer = start.deprecatedNode()->renderer()->enclosingLayer()) {
layer->scrollRectToVisible(rect, false, alignment, alignment);
updateAppearance();
}
}
}
void FrameSelection::setSelectionFromNone()
{
Document* document = m_frame->document();
bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
if (!isNone() || !(document->rendererIsEditable() || caretBrowsing))
return;
Node* node = document->documentElement();
while (node && !node->hasTagName(bodyTag))
node = node->traverseNextNode();
if (node)
setSelection(VisibleSelection(firstPositionInOrBeforeNode(node), DOWNSTREAM));
}
bool FrameSelection::shouldChangeSelection(const VisibleSelection& newSelection) const
{
return m_frame->editor()->shouldChangeSelection(selection(), newSelection, newSelection.affinity(), false);
}
#ifndef NDEBUG
void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
{
m_selection.formatForDebugger(buffer, length);
}
void FrameSelection::showTreeForThis() const
{
m_selection.showTreeForThis();
}
#endif
}
#ifndef NDEBUG
void showTree(const WebCore::FrameSelection& sel)
{
sel.showTreeForThis();
}
void showTree(const WebCore::FrameSelection* sel)
{
if (sel)
sel->showTreeForThis();
}
#endif