summaryrefslogtreecommitdiffstats
path: root/WebCore/editing/SelectionController.cpp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 18:28:41 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 18:28:41 -0800
commit648161bb0edfc3d43db63caed5cc5213bc6cb78f (patch)
tree4b825dc642cb6eb9a060e54bf8d69288fbee4904 /WebCore/editing/SelectionController.cpp
parenta65af38181ac7d34544586bdb5cd004de93897ad (diff)
downloadexternal_webkit-648161bb0edfc3d43db63caed5cc5213bc6cb78f.zip
external_webkit-648161bb0edfc3d43db63caed5cc5213bc6cb78f.tar.gz
external_webkit-648161bb0edfc3d43db63caed5cc5213bc6cb78f.tar.bz2
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebCore/editing/SelectionController.cpp')
-rw-r--r--WebCore/editing/SelectionController.cpp1187
1 files changed, 0 insertions, 1187 deletions
diff --git a/WebCore/editing/SelectionController.cpp b/WebCore/editing/SelectionController.cpp
deleted file mode 100644
index bf52be4..0000000
--- a/WebCore/editing/SelectionController.cpp
+++ /dev/null
@@ -1,1187 +0,0 @@
-/*
- * Copyright (C) 2004, 2008 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.
- */
-
-#include "config.h"
-#include "SelectionController.h"
-
-#include "CString.h"
-#include "DeleteSelectionCommand.h"
-#include "Document.h"
-#include "Editor.h"
-#include "Element.h"
-#include "EventHandler.h"
-#include "EventNames.h"
-#include "ExceptionCode.h"
-#include "FocusController.h"
-#include "Frame.h"
-#include "FrameTree.h"
-#include "FrameView.h"
-#include "GraphicsContext.h"
-#include "HTMLInputElement.h"
-#include "HTMLNames.h"
-#include "HitTestRequest.h"
-#include "HitTestResult.h"
-#include "Page.h"
-#include "Range.h"
-#include "RenderTheme.h"
-#include "RenderView.h"
-#include "TextIterator.h"
-#include "TypingCommand.h"
-#include "htmlediting.h"
-#include "visible_units.h"
-#include <stdio.h>
-
-#define EDIT_DEBUG 0
-
-namespace WebCore {
-
-using namespace HTMLNames;
-
-const int NoXPosForVerticalArrowNavigation = INT_MIN;
-
-SelectionController::SelectionController(Frame* frame, bool isDragCaretController)
- : m_needsLayout(true)
- , m_lastChangeWasHorizontalExtension(false)
- , m_frame(frame)
- , m_isDragCaretController(isDragCaretController)
- , m_isCaretBlinkingSuspended(false)
- , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation)
- , m_focused(false)
-{
-}
-
-void SelectionController::moveTo(const VisiblePosition &pos, bool userTriggered)
-{
- setSelection(Selection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered);
-}
-
-void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent, bool userTriggered)
-{
- setSelection(Selection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), true, true, userTriggered);
-}
-
-void SelectionController::moveTo(const Position &pos, EAffinity affinity, bool userTriggered)
-{
- setSelection(Selection(pos, affinity), true, true, userTriggered);
-}
-
-void SelectionController::moveTo(const Range *r, EAffinity affinity, bool userTriggered)
-{
- setSelection(Selection(startPosition(r), endPosition(r), affinity), true, true, userTriggered);
-}
-
-void SelectionController::moveTo(const Position &base, const Position &extent, EAffinity affinity, bool userTriggered)
-{
- setSelection(Selection(base, extent, affinity), true, true, userTriggered);
-}
-
-void SelectionController::setSelection(const Selection& s, bool closeTyping, bool clearTypingStyleAndRemovedAnchor, bool userTriggered)
-{
- if (m_isDragCaretController) {
- invalidateCaretRect();
- m_sel = s;
- m_needsLayout = true;
- invalidateCaretRect();
- return;
- }
- if (!m_frame) {
- m_sel = s;
- return;
- }
-
- if (s.base().node() && s.base().node()->document() != m_frame->document()) {
- s.base().node()->document()->frame()->selection()->setSelection(s, closeTyping, clearTypingStyleAndRemovedAnchor, userTriggered);
- return;
- }
-
- if (closeTyping)
- TypingCommand::closeTyping(m_frame->editor()->lastEditCommand());
-
- if (clearTypingStyleAndRemovedAnchor) {
- m_frame->clearTypingStyle();
- m_frame->editor()->setRemovedAnchor(0);
- }
-
- if (m_sel == s)
- return;
-
- Selection oldSelection = m_sel;
-
- m_sel = s;
-
- m_needsLayout = true;
-
- if (!s.isNone())
- m_frame->setFocusedNodeIfNeeded();
-
- m_frame->selectionLayoutChanged();
- // Always clear the x position used for vertical arrow navigation.
- // It will be restored by the vertical arrow navigation code if necessary.
- m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation;
- selectFrameElementInParentIfFullySelected();
- m_frame->notifyRendererOfSelectionChange(userTriggered);
- m_frame->respondToChangedSelection(oldSelection, closeTyping);
- if (userTriggered)
- m_frame->revealCaret(RenderLayer::gAlignToEdgeIfNeeded);
-
- notifyAccessibilityForSelectionChange();
-}
-
-static bool removingNodeRemovesPosition(Node* node, const Position& position)
-{
- if (!position.node())
- return false;
-
- if (position.node() == node)
- return true;
-
- if (!node->isElementNode())
- return false;
-
- Element* element = static_cast<Element*>(node);
- return element->contains(position.node()) || element->contains(position.node()->shadowAncestorNode());
-}
-
-void SelectionController::nodeWillBeRemoved(Node *node)
-{
- if (isNone())
- return;
-
- // There can't be a selection inside a fragment, so if a fragment's node is being removed,
- // the selection in the document that created the fragment needs no adjustment.
- if (node && highestAncestor(node)->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)
- return;
-
- bool baseRemoved = removingNodeRemovesPosition(node, m_sel.base());
- bool extentRemoved = removingNodeRemovesPosition(node, m_sel.extent());
- bool startRemoved = removingNodeRemovesPosition(node, m_sel.start());
- bool endRemoved = removingNodeRemovesPosition(node, m_sel.end());
-
- bool clearRenderTreeSelection = false;
- bool clearDOMTreeSelection = false;
-
- if (startRemoved || endRemoved) {
- // FIXME: When endpoints are removed, we should just alter the selection, instead of blowing it away.
- clearRenderTreeSelection = true;
- clearDOMTreeSelection = true;
- } else if (baseRemoved || extentRemoved) {
- // The base and/or extent are about to be removed, but the start and end aren't.
- // Change the base and extent to the start and end, but don't re-validate the
- // selection, since doing so could move the start and end into the node
- // that is about to be removed.
- if (m_sel.isBaseFirst())
- m_sel.setWithoutValidation(m_sel.start(), m_sel.end());
- else
- m_sel.setWithoutValidation(m_sel.end(), m_sel.start());
- // FIXME: This could be more efficient if we had an isNodeInRange function on Ranges.
- } else if (Range::compareBoundaryPoints(m_sel.start(), Position(node, 0)) == -1 &&
- Range::compareBoundaryPoints(m_sel.end(), Position(node, 0)) == 1) {
- // If we did nothing here, when this node's renderer was destroyed, the rect that it
- // occupied would be invalidated, but, selection gaps that change as a result of
- // the removal wouldn't be invalidated.
- // FIXME: Don't do so much unnecessary invalidation.
- clearRenderTreeSelection = true;
- }
-
- if (clearRenderTreeSelection) {
- RefPtr<Document> document = m_sel.start().node()->document();
- document->updateRendering();
- if (RenderView* view = static_cast<RenderView*>(document->renderer()))
- view->clearSelection();
- }
-
- if (clearDOMTreeSelection)
- setSelection(Selection(), false, false);
-}
-
-void SelectionController::willBeModified(EAlteration alter, EDirection direction)
-{
- switch (alter) {
- case MOVE:
- m_lastChangeWasHorizontalExtension = false;
- break;
- case EXTEND:
- if (!m_lastChangeWasHorizontalExtension) {
- m_lastChangeWasHorizontalExtension = true;
- Position start = m_sel.start();
- Position end = m_sel.end();
- switch (direction) {
- // FIXME: right for bidi?
- case RIGHT:
- case FORWARD:
- m_sel.setBase(start);
- m_sel.setExtent(end);
- break;
- case LEFT:
- case BACKWARD:
- m_sel.setBase(end);
- m_sel.setExtent(start);
- break;
- }
- }
- break;
- }
-}
-
-VisiblePosition SelectionController::modifyExtendingRightForward(TextGranularity granularity)
-{
- VisiblePosition pos(m_sel.extent(), m_sel.affinity());
- switch (granularity) {
- case CharacterGranularity:
- pos = pos.next(true);
- 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(VisiblePosition(m_sel.end(), m_sel.affinity()));
- break;
- case LineBoundary:
- pos = endOfLine(VisiblePosition(m_sel.end(), m_sel.affinity()));
- break;
- case ParagraphBoundary:
- pos = endOfParagraph(VisiblePosition(m_sel.end(), m_sel.affinity()));
- break;
- case DocumentBoundary:
- pos = VisiblePosition(m_sel.end(), m_sel.affinity());
- if (isEditablePosition(pos.deepEquivalent()))
- pos = endOfEditableContent(pos);
- else
- pos = endOfDocument(pos);
- break;
- }
-
- return pos;
-}
-
-VisiblePosition SelectionController::modifyMovingRight(TextGranularity granularity)
-{
- VisiblePosition pos;
- switch (granularity) {
- case CharacterGranularity:
- if (isRange())
- pos = VisiblePosition(m_sel.end(), m_sel.affinity());
- else
- pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).right(true);
- break;
- case WordGranularity:
- case SentenceGranularity:
- case LineGranularity:
- case ParagraphGranularity:
- case SentenceBoundary:
- case LineBoundary:
- case ParagraphBoundary:
- case DocumentBoundary:
- // FIXME: Implement all of the above.
- pos = modifyMovingForward(granularity);
- break;
- }
- return pos;
-}
-
-VisiblePosition SelectionController::modifyMovingForward(TextGranularity granularity)
-{
- VisiblePosition pos;
- // FIXME: Stay in editable content for the less common granularities.
- switch (granularity) {
- case CharacterGranularity:
- if (isRange())
- pos = VisiblePosition(m_sel.end(), m_sel.affinity());
- else
- pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).next(true);
- break;
- case WordGranularity:
- pos = nextWordPosition(VisiblePosition(m_sel.extent(), m_sel.affinity()));
- break;
- case SentenceGranularity:
- pos = nextSentencePosition(VisiblePosition(m_sel.extent(), m_sel.affinity()));
- break;
- case LineGranularity: {
- // down-arrowing from a range selection that ends at the start of a line needs
- // to leave the selection at that line start (no need to call nextLinePosition!)
- pos = VisiblePosition(m_sel.end(), m_sel.affinity());
- if (!isRange() || !isStartOfLine(pos))
- pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(START));
- break;
- }
- case ParagraphGranularity:
- pos = nextParagraphPosition(VisiblePosition(m_sel.end(), m_sel.affinity()), xPosForVerticalArrowNavigation(START));
- break;
- case SentenceBoundary:
- pos = endOfSentence(VisiblePosition(m_sel.end(), m_sel.affinity()));
- break;
- case LineBoundary:
- pos = endOfLine(VisiblePosition(m_sel.end(), m_sel.affinity()));
- break;
- case ParagraphBoundary:
- pos = endOfParagraph(VisiblePosition(m_sel.end(), m_sel.affinity()));
- break;
- case DocumentBoundary:
- pos = VisiblePosition(m_sel.end(), m_sel.affinity());
- if (isEditablePosition(pos.deepEquivalent()))
- pos = endOfEditableContent(pos);
- else
- pos = endOfDocument(pos);
- break;
-
- }
- return pos;
-}
-
-VisiblePosition SelectionController::modifyExtendingLeftBackward(TextGranularity granularity)
-{
- VisiblePosition pos(m_sel.extent(), m_sel.affinity());
-
- // Extending a selection backward by word or character from just after a table selects
- // the table. This "makes sense" from the user perspective, esp. when deleting.
- // It was done here instead of in VisiblePosition because we want VPs to iterate
- // over everything.
- switch (granularity) {
- case CharacterGranularity:
- pos = pos.previous(true);
- 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(VisiblePosition(m_sel.start(), m_sel.affinity()));
- break;
- case LineBoundary:
- pos = startOfLine(VisiblePosition(m_sel.start(), m_sel.affinity()));
- break;
- case ParagraphBoundary:
- pos = startOfParagraph(VisiblePosition(m_sel.start(), m_sel.affinity()));
- break;
- case DocumentBoundary:
- pos = VisiblePosition(m_sel.start(), m_sel.affinity());
- if (isEditablePosition(pos.deepEquivalent()))
- pos = startOfEditableContent(pos);
- else
- pos = startOfDocument(pos);
- break;
- }
- return pos;
-}
-
-VisiblePosition SelectionController::modifyMovingLeft(TextGranularity granularity)
-{
- VisiblePosition pos;
- switch (granularity) {
- case CharacterGranularity:
- if (isRange())
- pos = VisiblePosition(m_sel.start(), m_sel.affinity());
- else
- pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).left(true);
- break;
- case WordGranularity:
- case SentenceGranularity:
- case LineGranularity:
- case ParagraphGranularity:
- case SentenceBoundary:
- case LineBoundary:
- case ParagraphBoundary:
- case DocumentBoundary:
- // FIXME: Implement all of the above.
- pos = modifyMovingBackward(granularity);
- break;
- }
- return pos;
-}
-
-VisiblePosition SelectionController::modifyMovingBackward(TextGranularity granularity)
-{
- VisiblePosition pos;
- switch (granularity) {
- case CharacterGranularity:
- if (isRange())
- pos = VisiblePosition(m_sel.start(), m_sel.affinity());
- else
- pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).previous(true);
- break;
- case WordGranularity:
- pos = previousWordPosition(VisiblePosition(m_sel.extent(), m_sel.affinity()));
- break;
- case SentenceGranularity:
- pos = previousSentencePosition(VisiblePosition(m_sel.extent(), m_sel.affinity()));
- break;
- case LineGranularity:
- pos = previousLinePosition(VisiblePosition(m_sel.start(), m_sel.affinity()), xPosForVerticalArrowNavigation(START));
- break;
- case ParagraphGranularity:
- pos = previousParagraphPosition(VisiblePosition(m_sel.start(), m_sel.affinity()), xPosForVerticalArrowNavigation(START));
- break;
- case SentenceBoundary:
- pos = startOfSentence(VisiblePosition(m_sel.start(), m_sel.affinity()));
- break;
- case LineBoundary:
- pos = startOfLine(VisiblePosition(m_sel.start(), m_sel.affinity()));
- break;
- case ParagraphBoundary:
- pos = startOfParagraph(VisiblePosition(m_sel.start(), m_sel.affinity()));
- break;
- case DocumentBoundary:
- pos = VisiblePosition(m_sel.start(), m_sel.affinity());
- if (isEditablePosition(pos.deepEquivalent()))
- pos = startOfEditableContent(pos);
- else
- pos = startOfDocument(pos);
- break;
- }
- return pos;
-}
-
-bool SelectionController::modify(EAlteration alter, EDirection dir, TextGranularity granularity, bool userTriggered)
-{
- if (userTriggered) {
- SelectionController trialSelectionController;
- trialSelectionController.setLastChangeWasHorizontalExtension(m_lastChangeWasHorizontalExtension);
- trialSelectionController.setSelection(m_sel);
- trialSelectionController.modify(alter, dir, granularity, false);
-
- bool change = m_frame->shouldChangeSelection(trialSelectionController.selection());
- if (!change)
- return false;
- }
-
- if (m_frame)
- m_frame->setSelectionGranularity(granularity);
-
- willBeModified(alter, dir);
-
- VisiblePosition pos;
- switch (dir) {
- case RIGHT:
- if (alter == MOVE)
- pos = modifyMovingRight(granularity);
- else
- pos = modifyExtendingRightForward(granularity);
- break;
- case FORWARD:
- if (alter == EXTEND)
- pos = modifyExtendingRightForward(granularity);
- else
- pos = modifyMovingForward(granularity);
- break;
- case LEFT:
- if (alter == MOVE)
- pos = modifyMovingLeft(granularity);
- else
- pos = modifyExtendingLeftBackward(granularity);
- break;
- case BACKWARD:
- if (alter == EXTEND)
- pos = modifyExtendingLeftBackward(granularity);
- else
- pos = modifyMovingBackward(granularity);
- break;
- }
-
- if (pos.isNull())
- return false;
-
- // Some of the above operations set an xPosForVerticalArrowNavigation.
- // Setting a selection will clear it, so save it to possibly restore later.
- // Note: the START position type is arbitrary because it is unused, it would be
- // the requested position type if there were no xPosForVerticalArrowNavigation set.
- int x = xPosForVerticalArrowNavigation(START);
-
- switch (alter) {
- case MOVE:
- moveTo(pos, userTriggered);
- break;
- case EXTEND:
- setExtent(pos, userTriggered);
- break;
- }
-
- if (granularity == LineGranularity || granularity == ParagraphGranularity)
- m_xPosForVerticalArrowNavigation = x;
-
- if (userTriggered) {
- // User modified selection change also sets the granularity back to character.
- // NOTE: The one exception is that we need to keep word granularity to
- // preserve smart delete behavior when extending by word (e.g. double-click),
- // then shift-option-right arrow, then delete needs to smart delete, per TextEdit.
- if (!(alter == EXTEND && granularity == WordGranularity && m_frame->selectionGranularity() == WordGranularity))
- m_frame->setSelectionGranularity(CharacterGranularity);
- }
-
- setNeedsLayout();
-
- return true;
-}
-
-// FIXME: Maybe baseline would be better?
-static bool caretY(const VisiblePosition &c, int &y)
-{
- IntRect rect = c.caretRect();
- if (rect.isEmpty())
- return false;
- y = rect.y() + rect.height() / 2;
- return true;
-}
-
-bool SelectionController::modify(EAlteration alter, int verticalDistance, bool userTriggered)
-{
- if (verticalDistance == 0)
- return false;
-
- if (userTriggered) {
- SelectionController trialSelectionController;
- trialSelectionController.setSelection(m_sel);
- trialSelectionController.modify(alter, verticalDistance, false);
-
- bool change = m_frame->shouldChangeSelection(trialSelectionController.selection());
- if (!change)
- return false;
- }
-
- bool up = verticalDistance < 0;
- if (up)
- verticalDistance = -verticalDistance;
-
- willBeModified(alter, up ? BACKWARD : FORWARD);
-
- VisiblePosition pos;
- int xPos = 0;
- switch (alter) {
- case MOVE:
- pos = VisiblePosition(up ? m_sel.start() : m_sel.end(), m_sel.affinity());
- xPos = xPosForVerticalArrowNavigation(up ? START : END);
- m_sel.setAffinity(up ? UPSTREAM : DOWNSTREAM);
- break;
- case EXTEND:
- pos = VisiblePosition(m_sel.extent(), m_sel.affinity());
- xPos = xPosForVerticalArrowNavigation(EXTENT);
- m_sel.setAffinity(DOWNSTREAM);
- break;
- }
-
- int startY;
- if (!caretY(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 (!caretY(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 MOVE:
- moveTo(result, userTriggered);
- break;
- case EXTEND:
- setExtent(result, userTriggered);
- break;
- }
-
- if (userTriggered)
- m_frame->setSelectionGranularity(CharacterGranularity);
-
- return true;
-}
-
-bool SelectionController::expandUsingGranularity(TextGranularity granularity)
-{
- if (isNone())
- return false;
-
- m_sel.expandUsingGranularity(granularity);
- m_needsLayout = true;
- return true;
-}
-
-int SelectionController::xPosForVerticalArrowNavigation(EPositionType type)
-{
- int x = 0;
-
- if (isNone())
- return x;
-
- Position pos;
- switch (type) {
- case START:
- pos = m_sel.start();
- break;
- case END:
- pos = m_sel.end();
- break;
- case BASE:
- pos = m_sel.base();
- break;
- case EXTENT:
- pos = m_sel.extent();
- break;
- }
-
- Frame *frame = pos.node()->document()->frame();
- if (!frame)
- return x;
-
- if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation) {
- VisiblePosition visiblePosition(pos, m_sel.affinity());
- // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
- // after the selection is created and before this function is called.
- x = visiblePosition.isNotNull() ? visiblePosition.caretRect().x() : 0;
- m_xPosForVerticalArrowNavigation = x;
- }
- else
- x = m_xPosForVerticalArrowNavigation;
-
- return x;
-}
-
-void SelectionController::clear()
-{
- setSelection(Selection());
-}
-
-void SelectionController::setBase(const VisiblePosition &pos, bool userTriggered)
-{
- setSelection(Selection(pos.deepEquivalent(), m_sel.extent(), pos.affinity()), true, true, userTriggered);
-}
-
-void SelectionController::setExtent(const VisiblePosition &pos, bool userTriggered)
-{
- setSelection(Selection(m_sel.base(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered);
-}
-
-void SelectionController::setBase(const Position &pos, EAffinity affinity, bool userTriggered)
-{
- setSelection(Selection(pos, m_sel.extent(), affinity), true, true, userTriggered);
-}
-
-void SelectionController::setExtent(const Position &pos, EAffinity affinity, bool userTriggered)
-{
- setSelection(Selection(m_sel.base(), pos, affinity), true, true, userTriggered);
-}
-
-void SelectionController::setNeedsLayout(bool flag)
-{
- m_needsLayout = flag;
-}
-
-void SelectionController::layout()
-{
- if (isNone() || !m_sel.start().node()->inDocument() || !m_sel.end().node()->inDocument()) {
- m_caretRect = IntRect();
- m_caretPositionOnLayout = IntPoint();
- return;
- }
-
- m_sel.start().node()->document()->updateRendering();
-
- m_caretRect = IntRect();
- m_caretPositionOnLayout = IntPoint();
-
- if (isCaret()) {
- VisiblePosition pos(m_sel.start(), m_sel.affinity());
- if (pos.isNotNull()) {
- ASSERT(pos.deepEquivalent().node()->renderer());
- m_caretRect = pos.caretRect();
-
- int x, y;
- pos.deepEquivalent().node()->renderer()->absolutePositionForContent(x, y);
- m_caretPositionOnLayout = IntPoint(x, y);
- }
- }
-
- m_needsLayout = false;
-}
-
-IntRect SelectionController::caretRect() const
-{
- if (m_needsLayout)
- const_cast<SelectionController *>(this)->layout();
-
- IntRect caret = m_caretRect;
-
- if (m_sel.start().node() && m_sel.start().node()->renderer()) {
- int x, y;
- m_sel.start().node()->renderer()->absolutePositionForContent(x, y);
- caret.move(IntPoint(x, y) - m_caretPositionOnLayout);
- }
-
- return caret;
-}
-
-static IntRect repaintRectForCaret(IntRect caret)
-{
- if (caret.isEmpty())
- return IntRect();
- // Ensure that the dirty rect intersects the block that paints the caret even in the case where
- // the caret itself is just outside the block. See <https://bugs.webkit.org/show_bug.cgi?id=19086>.
- caret.inflateX(1);
- return caret;
-}
-
-IntRect SelectionController::caretRepaintRect() const
-{
- return repaintRectForCaret(caretRect());
-}
-
-bool SelectionController::recomputeCaretRect()
-{
- if (!m_frame || !m_frame->document())
- return false;
-
- FrameView* v = m_frame->document()->view();
- if (!v)
- return false;
-
- if (!m_needsLayout)
- return false;
-
- IntRect oldRect = m_caretRect;
- m_needsLayout = true;
- IntRect newRect = caretRect();
- if (oldRect == newRect)
- return false;
-
- if (RenderView* view = static_cast<RenderView*>(m_frame->document()->renderer())) {
- view->repaintViewRectangle(repaintRectForCaret(oldRect), false);
- view->repaintViewRectangle(repaintRectForCaret(newRect), false);
- }
- return true;
-}
-
-void SelectionController::invalidateCaretRect()
-{
- if (!isCaret())
- return;
-
- Document* d = m_sel.start().node()->document();
-
- bool caretRectChanged = recomputeCaretRect();
-
- // EDIT FIXME: This is an unfortunate hack.
- // Basically, we can't trust this layout position since we
- // can't guarantee that the check to see if we are in unrendered
- // content will work at this point. We may have to wait for
- // a layout and re-render of the document to happen. So, resetting this
- // flag will cause another caret layout to happen the first time
- // that we try to paint the caret after this call. That one will work since
- // it happens after the document has accounted for any editing
- // changes which may have been done.
- // And, we need to leave this layout here so the caret moves right
- // away after clicking.
- m_needsLayout = true;
-
- if (!caretRectChanged) {
- if (RenderView* view = static_cast<RenderView*>(d->renderer()))
- view->repaintViewRectangle(caretRepaintRect(), false);
- }
-}
-
-void SelectionController::paintCaret(GraphicsContext *p, const IntRect &rect)
-{
- if (! m_sel.isCaret())
- return;
-
- if (m_needsLayout)
- layout();
-
- IntRect caret = intersection(caretRect(), rect);
- if (!caret.isEmpty()) {
- Color caretColor = Color::black;
- Element* element = rootEditableElement();
- if (element && element->renderer())
- caretColor = element->renderer()->style()->color();
-
- p->fillRect(caret, caretColor);
- }
-}
-
-void SelectionController::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 = static_cast<RenderText*>(r);
- if (textRenderer->textLength() == 0 || !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_sel.start().node())
- offset = m_sel.start().offset();
- else if (r->node() == m_sel.end().node())
- offset = m_sel.end().offset();
-
- int pos;
- InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
- text = text.substring(box->m_start, box->m_len);
-
- String show;
- int mid = max / 2;
- int caret = 0;
-
- // text is shorter than max
- if (textLength < max) {
- show = text;
- caret = pos;
- }
-
- // too few characters to left
- else if (pos - mid < 0) {
- show = text.left(max - 3) + "...";
- caret = pos;
- }
-
- // enough characters on each side
- else if (pos - mid >= 0 && pos + mid <= textLength) {
- show = "..." + text.substring(pos - mid + 3, max - 6) + "...";
- caret = mid;
- }
-
- // too few characters on right
- 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 SelectionController::contains(const IntPoint& point)
-{
- Document* document = m_frame->document();
-
- // Treat a collapsed selection like no selection.
- if (!isRange())
- return false;
- if (!document->renderer())
- return false;
-
- HitTestRequest request(true, true);
- HitTestResult result(point);
- document->renderer()->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_sel.visibleStart().isNull() || m_sel.visibleEnd().isNull())
- return false;
-
- Position start(m_sel.visibleStart().deepEquivalent());
- Position end(m_sel.visibleEnd().deepEquivalent());
- Position p(visiblePos.deepEquivalent());
-
- return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
-}
-
-// Workaround for the fact that it's hard to delete a frame.
-// Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
-// Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
-// for the focus to move to another frame. So instead we call it from places where we are selecting with the
-// mouse or the keyboard after setting the selection.
-void SelectionController::selectFrameElementInParentIfFullySelected()
-{
- // Find the parent frame; if there is none, then we have nothing to do.
- Frame* parent = m_frame->tree()->parent();
- if (!parent)
- return;
- Page* page = m_frame->page();
- if (!page)
- return;
-
- // Check if the selection contains the entire frame contents; if not, then there is nothing to do.
- if (!isRange())
- return;
- if (!isStartOfDocument(selection().visibleStart()))
- return;
- if (!isEndOfDocument(selection().visibleEnd()))
- return;
-
- // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
- Document* doc = m_frame->document();
- if (!doc)
- return;
- Element* ownerElement = doc->ownerElement();
- if (!ownerElement)
- return;
- Node* ownerElementParent = ownerElement->parentNode();
- if (!ownerElementParent)
- return;
-
- // This method's purpose is it to make it easier to select iframes (in order to delete them). Don't do anything if the iframe isn't deletable.
- if (!ownerElementParent->isContentEditable())
- return;
-
- // Create compute positions before and after the element.
- unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
- VisiblePosition beforeOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex, SEL_DEFAULT_AFFINITY));
- VisiblePosition afterOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex + 1, VP_UPSTREAM_IF_POSSIBLE));
-
- // Focus on the parent frame, and then select from before this element to after.
- Selection newSelection(beforeOwnerElement, afterOwnerElement);
- if (parent->shouldChangeSelection(newSelection)) {
- page->focusController()->setFocusedFrame(parent);
- parent->selection()->setSelection(newSelection);
- }
-}
-
-void SelectionController::selectAll()
-{
- Document* document = m_frame->document();
- if (!document)
- return;
-
- if (document->focusedNode() && document->focusedNode()->canSelectAll()) {
- document->focusedNode()->selectAll();
- return;
- }
-
- Node* root = 0;
- if (isContentEditable())
- root = highestEditableRoot(m_sel.start());
- else {
- root = shadowTreeRootNode();
- if (!root)
- root = document->documentElement();
- }
- if (!root)
- return;
- Selection newSelection(Selection::selectionFromContentsOfNode(root));
- if (m_frame->shouldChangeSelection(newSelection))
- setSelection(newSelection);
- selectFrameElementInParentIfFullySelected();
- m_frame->notifyRendererOfSelectionChange(true);
-}
-
-bool SelectionController::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();
-
- // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
- // they start at the beginning of the next line instead
- 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;
-
- // FIXME: Can we provide extentAffinity?
- VisiblePosition visibleStart(startContainer, startOffset, collapsed ? affinity : DOWNSTREAM);
- VisiblePosition visibleEnd(endContainer, endOffset, SEL_DEFAULT_AFFINITY);
- setSelection(Selection(visibleStart, visibleEnd), closeTyping);
- return true;
-}
-
-bool SelectionController::isInPasswordField() const
-{
- Node* startNode = start().node();
- if (!startNode)
- return false;
-
- startNode = startNode->shadowAncestorNode();
- if (!startNode)
- return false;
-
- if (!startNode->hasTagName(inputTag))
- return false;
-
- return static_cast<HTMLInputElement*>(startNode)->inputType() == HTMLInputElement::PASSWORD;
-}
-
-bool SelectionController::isInsideNode() const
-{
- Node* startNode = start().node();
- if (!startNode)
- return false;
- return !isTableElement(startNode) && !editingIgnoresContent(startNode);
-}
-
-void SelectionController::focusedOrActiveStateChanged()
-{
- bool activeAndFocused = isFocusedAndActive();
-
- // Because RenderObject::selectionBackgroundColor() and
- // RenderObject::selectionForegroundColor() check if the frame is active,
- // we have to update places those colors were painted.
- if (RenderView* view = static_cast<RenderView*>(m_frame->document()->renderer()))
- view->repaintViewRectangle(enclosingIntRect(m_frame->selectionRect()));
-
- // Caret appears in the active frame.
- if (activeAndFocused)
- m_frame->setSelectionFromNone();
- m_frame->setCaretVisible(activeAndFocused);
-
- // Update for caps lock state
- m_frame->eventHandler()->capsLockStateMayHaveChanged();
-
- // Because CSSStyleSelector::checkOneSelector() and
- // RenderTheme::isFocused() check if the frame is active, we have to
- // update style and theme state that depended on those.
- if (Node* node = m_frame->document()->focusedNode()) {
- node->setChanged();
- if (RenderObject* renderer = node->renderer())
- if (renderer && renderer->style()->hasAppearance())
- theme()->stateChanged(renderer, FocusState);
- }
-
- // Secure keyboard entry is set by the active frame.
- if (m_frame->document()->useSecureKeyboardEntryWhenActive())
- m_frame->setUseSecureKeyboardEntry(activeAndFocused);
-}
-
-void SelectionController::pageActivationChanged()
-{
- focusedOrActiveStateChanged();
-}
-
-void SelectionController::setFocused(bool flag)
-{
- if (m_focused == flag)
- return;
- m_focused = flag;
-
- focusedOrActiveStateChanged();
-
- if (Document* doc = m_frame->document())
- doc->dispatchWindowEvent(flag ? eventNames().focusEvent : eventNames().blurEvent, false, false);
-}
-
-bool SelectionController::isFocusedAndActive() const
-{
- return m_focused && m_frame->page() && m_frame->page()->focusController()->isActive();
-}
-
-#ifndef NDEBUG
-
-void SelectionController::formatForDebugger(char* buffer, unsigned length) const
-{
- m_sel.formatForDebugger(buffer, length);
-}
-
-void SelectionController::showTreeForThis() const
-{
- m_sel.showTreeForThis();
-}
-
-#endif
-
-}
-
-#ifndef NDEBUG
-
-void showTree(const WebCore::SelectionController& sel)
-{
- sel.showTreeForThis();
-}
-
-void showTree(const WebCore::SelectionController* sel)
-{
- if (sel)
- sel->showTreeForThis();
-}
-
-#endif