Commit fbc55832 authored by ap@webkit.org's avatar ap@webkit.org

Reviewed by Darin.

        http://bugs.webkit.org/show_bug.cgi?id=15954
        Move DOM Selection operations out of SelectionController

        No change in functionality.

WebCore:
        * editing/SelectionController.cpp:
        (WebCore::SelectionController::setSelectedRange):
        * editing/SelectionController.h:
        * page/DOMSelection.cpp:
        (WebCore::DOMSelection::anchorNode):
        (WebCore::DOMSelection::baseNode):
        (WebCore::DOMSelection::anchorOffset):
        (WebCore::DOMSelection::baseOffset):
        (WebCore::DOMSelection::focusNode):
        (WebCore::DOMSelection::extentNode):
        (WebCore::DOMSelection::focusOffset):
        (WebCore::DOMSelection::extentOffset):
        (WebCore::DOMSelection::isCollapsed):
        (WebCore::DOMSelection::type):
        (WebCore::DOMSelection::rangeCount):
        (WebCore::DOMSelection::collapse):
        (WebCore::DOMSelection::collapseToEnd):
        (WebCore::DOMSelection::collapseToStart):
        (WebCore::DOMSelection::empty):
        (WebCore::DOMSelection::setBaseAndExtent):
        (WebCore::DOMSelection::setPosition):
        (WebCore::DOMSelection::modify):
        (WebCore::DOMSelection::extend):
        (WebCore::DOMSelection::getRangeAt):
        (WebCore::DOMSelection::removeAllRanges):
        (WebCore::DOMSelection::addRange):
        (WebCore::DOMSelection::deleteFromDocument):
        (WebCore::DOMSelection::containsNode):
        (WebCore::DOMSelection::selectAllChildren):
        (WebCore::DOMSelection::toString):
        * page/DOMSelection.h:
        Moved all DOM API methods to DOMSelection; changed SelectionController::setSelectedRange()
        to return its result directly instead of via an ExceptionCode that no caller wanted.

        * editing/Editor.cpp:
        (WebCore::Editor::deleteRange):
        (WebCore::Editor::removeFormattingAndStyle):
        (WebCore::Editor::selectComposition):
        (WebCore::Editor::setComposition):
        * html/HTMLInputElement.cpp: (WebCore::HTMLInputElement::defaultEventHandler):
        Adapted for SelectionController::setSelectedRange() now returning a bool.
        SelectionController::toString() is no longer avasilable, use plainText() explicitly.

        * WebCore.base.exp: Changed SelectionController::setSelectedRange() signature.

WebKit:
        * WebView/WebHTMLView.mm:
        (-[WebHTMLView _expandSelectionToGranularity:]):
        (-[WebHTMLView selectToMark:]):
        (-[WebHTMLView swapWithMark:]):
        * WebView/WebView.mm:
        (-[WebView setSelectedDOMRange:affinity:]):
        Adapted for SelectionController::setSelectedRange() now returning a bool.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@27744 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 698099cc
2007-11-12 Alexey Proskuryakov <ap@webkit.org>
Reviewed by Darin.
http://bugs.webkit.org/show_bug.cgi?id=15954
Move DOM Selection operations out of SelectionController
No change in functionality.
* editing/SelectionController.cpp:
(WebCore::SelectionController::setSelectedRange):
* editing/SelectionController.h:
* page/DOMSelection.cpp:
(WebCore::DOMSelection::anchorNode):
(WebCore::DOMSelection::baseNode):
(WebCore::DOMSelection::anchorOffset):
(WebCore::DOMSelection::baseOffset):
(WebCore::DOMSelection::focusNode):
(WebCore::DOMSelection::extentNode):
(WebCore::DOMSelection::focusOffset):
(WebCore::DOMSelection::extentOffset):
(WebCore::DOMSelection::isCollapsed):
(WebCore::DOMSelection::type):
(WebCore::DOMSelection::rangeCount):
(WebCore::DOMSelection::collapse):
(WebCore::DOMSelection::collapseToEnd):
(WebCore::DOMSelection::collapseToStart):
(WebCore::DOMSelection::empty):
(WebCore::DOMSelection::setBaseAndExtent):
(WebCore::DOMSelection::setPosition):
(WebCore::DOMSelection::modify):
(WebCore::DOMSelection::extend):
(WebCore::DOMSelection::getRangeAt):
(WebCore::DOMSelection::removeAllRanges):
(WebCore::DOMSelection::addRange):
(WebCore::DOMSelection::deleteFromDocument):
(WebCore::DOMSelection::containsNode):
(WebCore::DOMSelection::selectAllChildren):
(WebCore::DOMSelection::toString):
* page/DOMSelection.h:
Moved all DOM API methods to DOMSelection; changed SelectionController::setSelectedRange()
to return its result directly instead of via an ExceptionCode that no caller wanted.
* editing/Editor.cpp:
(WebCore::Editor::deleteRange):
(WebCore::Editor::removeFormattingAndStyle):
(WebCore::Editor::selectComposition):
(WebCore::Editor::setComposition):
* html/HTMLInputElement.cpp: (WebCore::HTMLInputElement::defaultEventHandler):
Adapted for SelectionController::setSelectedRange() now returning a bool.
SelectionController::toString() is no longer avasilable, use plainText() explicitly.
* WebCore.base.exp: Changed SelectionController::setSelectedRange() signature.
2007-11-12 Dan Bernstein <mitz@apple.com>
Reviewed by Darin Adler.
......
......@@ -331,7 +331,7 @@ __ZN7WebCore19InspectorController16setWindowVisibleEb
__ZN7WebCore19InspectorController4showEv
__ZN7WebCore19InspectorController5closeEv
__ZN7WebCore19InspectorController7inspectEPNS_4NodeE
__ZN7WebCore19SelectionController16setSelectedRangeEPNS_5RangeENS_9EAffinityEbRi
__ZN7WebCore19SelectionController16setSelectedRangeEPNS_5RangeENS_9EAffinityEb
__ZN7WebCore19SelectionController5clearEv
__ZN7WebCore19SelectionController9selectAllEv
__ZN7WebCore19TextResourceDecoder5flushEv
......
......@@ -237,20 +237,16 @@ void Editor::deleteRange(Range* range, bool killRing, bool prepend, bool smartDe
if (killRing)
addToKillRing(range, prepend);
ExceptionCode ec = 0;
SelectionController* selectionController = m_frame->selectionController();
bool smartDelete = smartDeleteOK && canSmartCopyOrDelete();
switch (deletionAction) {
case deleteSelectionAction:
selectionController->setSelectedRange(range, DOWNSTREAM, true, ec);
if (ec)
if (!selectionController->setSelectedRange(range, DOWNSTREAM, true))
return;
deleteSelectionWithSmartDelete(smartDelete);
break;
case deleteKeyAction:
selectionController->setSelectedRange(range, DOWNSTREAM, (granularity != CharacterGranularity), ec);
if (ec)
if (!selectionController->setSelectedRange(range, DOWNSTREAM, (granularity != CharacterGranularity)))
return;
if (m_frame->document()) {
TypingCommand::deleteKeyPressed(m_frame->document(), smartDelete, granularity);
......@@ -258,8 +254,7 @@ void Editor::deleteRange(Range* range, bool killRing, bool prepend, bool smartDe
}
break;
case forwardDeleteKeyAction:
selectionController->setSelectedRange(range, DOWNSTREAM, (granularity != CharacterGranularity), ec);
if (ec)
if (!selectionController->setSelectedRange(range, DOWNSTREAM, (granularity != CharacterGranularity)))
return;
if (m_frame->document()) {
TypingCommand::forwardDeleteKeyPressed(m_frame->document(), smartDelete, granularity);
......@@ -617,8 +612,8 @@ void Editor::removeFormattingAndStyle()
Document* document = m_frame->document();
// Make a plain text string from the selection to remove formatting like tables and lists.
String string = m_frame->selectionController()->toString();
String string = plainText(m_frame->selectionController()->selection().toRange().get());
// Get the default style for this editable root, it's the style that we'll give the
// content that we're operating on.
Node* root = m_frame->selectionController()->rootEditableElement();
......@@ -1670,8 +1665,7 @@ void Editor::selectComposition()
RefPtr<Range> range = compositionRange();
if (!range)
return;
ExceptionCode ec = 0;
m_frame->selectionController()->setSelectedRange(range.get(), DOWNSTREAM, false, ec);
m_frame->selectionController()->setSelectedRange(range.get(), DOWNSTREAM, false);
}
void Editor::confirmComposition()
......@@ -1742,9 +1736,9 @@ void Editor::setComposition(const String& text, const Vector<CompositionUnderlin
if (!text.isEmpty()) {
TypingCommand::insertText(m_frame->document(), text, true, true);
Node* baseNode = m_frame->selectionController()->baseNode();
Node* baseNode = m_frame->selectionController()->base().node();
unsigned baseOffset = m_frame->selectionController()->base().offset();
Node* extentNode = m_frame->selectionController()->extentNode();
Node* extentNode = m_frame->selectionController()->extent().node();
unsigned extentOffset = m_frame->selectionController()->extent().offset();
if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
......@@ -1763,8 +1757,7 @@ void Editor::setComposition(const String& text, const Vector<CompositionUnderlin
unsigned start = min(baseOffset + selectionStart, extentOffset);
unsigned end = min(max(start, baseOffset + selectionEnd), extentOffset);
RefPtr<Range> selectedRange = new Range(baseNode->document(), baseNode, start, baseNode, end);
ExceptionCode ec = 0;
m_frame->selectionController()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false, ec);
m_frame->selectionController()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false);
}
}
......
......@@ -407,56 +407,6 @@ VisiblePosition SelectionController::modifyMovingLeftBackward(TextGranularity gr
return pos;
}
bool SelectionController::modify(const String &alterString, const String &directionString, const String &granularityString, bool userTriggered)
{
String alterStringLower = alterString.lower();
EAlteration alter;
if (alterStringLower == "extend")
alter = EXTEND;
else if (alterStringLower == "move")
alter = MOVE;
else
return false;
String directionStringLower = directionString.lower();
EDirection direction;
if (directionStringLower == "forward")
direction = FORWARD;
else if (directionStringLower == "backward")
direction = BACKWARD;
else if (directionStringLower == "left")
direction = LEFT;
else if (directionStringLower == "right")
direction = RIGHT;
else
return false;
String granularityStringLower = granularityString.lower();
TextGranularity granularity;
if (granularityStringLower == "character")
granularity = CharacterGranularity;
else if (granularityStringLower == "word")
granularity = WordGranularity;
else if (granularityStringLower == "sentence")
granularity = SentenceGranularity;
else if (granularityStringLower == "line")
granularity = LineGranularity;
else if (granularityStringLower == "paragraph")
granularity = ParagraphGranularity;
else if (granularityStringLower == "lineboundary")
granularity = LineBoundary;
else if (granularityStringLower == "sentenceboundary")
granularity = SentenceBoundary;
else if (granularityStringLower == "paragraphboundary")
granularity = ParagraphBoundary;
else if (granularityStringLower == "documentboundary")
granularity = DocumentBoundary;
else
return false;
return modify(alter, direction, granularity, userTriggered);
}
bool SelectionController::modify(EAlteration alter, EDirection dir, TextGranularity granularity, bool userTriggered)
{
if (userTriggered) {
......@@ -706,240 +656,6 @@ void SelectionController::setNeedsLayout(bool flag)
m_needsLayout = flag;
}
String SelectionController::type() const
{
if (isNone())
return "None";
else if (isCaret())
return "Caret";
else
return "Range";
}
// These methods are accessible via JS (and are not used internally), so they must return valid DOM positions.
Node* SelectionController::baseNode() const
{
Position base = rangeCompliantEquivalent(m_sel.base());
return base.node();
}
int SelectionController::baseOffset() const
{
Position base = rangeCompliantEquivalent(m_sel.base());
return base.offset();
}
Node* SelectionController::extentNode() const
{
Position extent = rangeCompliantEquivalent(m_sel.extent());
return extent.node();
}
int SelectionController::extentOffset() const
{
Position extent = rangeCompliantEquivalent(m_sel.extent());
return extent.offset();
}
Node* SelectionController::anchorNode() const
{
Position anchor = m_sel.isBaseFirst() ? m_sel.start() : m_sel.end();
anchor = rangeCompliantEquivalent(anchor);
return anchor.node();
}
int SelectionController::anchorOffset() const
{
Position anchor = m_sel.isBaseFirst() ? m_sel.start() : m_sel.end();
anchor = rangeCompliantEquivalent(anchor);
return anchor.offset();
}
Node* SelectionController::focusNode() const
{
Position focus = m_sel.isBaseFirst() ? m_sel.end() : m_sel.start();
focus = rangeCompliantEquivalent(focus);
return focus.node();
}
int SelectionController::focusOffset() const
{
Position focus = m_sel.isBaseFirst() ? m_sel.end() : m_sel.start();
focus = rangeCompliantEquivalent(focus);
return focus.offset();
}
String SelectionController::toString() const
{
return plainText(m_sel.toRange().get());
}
PassRefPtr<Range> SelectionController::getRangeAt(int index, ExceptionCode& ec) const
{
if (index < 0 || index >= rangeCount()) {
ec = INDEX_SIZE_ERR;
return 0;
}
return m_sel.toRange();
}
void SelectionController::removeAllRanges()
{
clear();
}
// Adds r to the currently selected range.
void SelectionController::addRange(const Range* r)
{
if (!r)
return;
if (isNone()) {
setSelection(Selection(r));
return;
}
RefPtr<Range> range = m_sel.toRange();
ExceptionCode ec = 0;
if (r->compareBoundaryPoints(Range::START_TO_START, range.get(), ec) == -1) {
// We don't support discontiguous selection. We don't do anything if r and range don't intersect.
if (r->compareBoundaryPoints(Range::END_TO_START, range.get(), ec) > -1) {
if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1)
// The original range and r intersect.
setSelection(Selection(r->startPosition(), range->endPosition(), DOWNSTREAM));
else
// r contains the original range.
setSelection(Selection(r));
}
} else {
// We don't support discontiguous selection. We don't do anything if r and range don't intersect.
if (r->compareBoundaryPoints(Range::START_TO_END, range.get(), ec) < 1) {
if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1)
// The original range contains r.
setSelection(Selection(range.get()));
else
// The original range and r intersect.
setSelection(Selection(range->startPosition(), r->endPosition(), DOWNSTREAM));
}
}
}
void SelectionController::deleteFromDocument()
{
if (isNone())
return;
if (isCollapsed())
modify(EXTEND, BACKWARD, CharacterGranularity);
RefPtr<Range> selectedRange = m_sel.toRange();
ExceptionCode ec = 0;
selectedRange->deleteContents(ec);
ASSERT(!ec);
setBaseAndExtent(selectedRange->startContainer(ec), selectedRange->startOffset(ec), selectedRange->startContainer(ec), selectedRange->startOffset(ec), ec);
ASSERT(!ec);
}
bool SelectionController::containsNode(const Node* n, bool allowPartial) const
{
if (!n || isNone())
return false;
Node* parentNode = n->parentNode();
unsigned nodeIndex = n->nodeIndex();
RefPtr<Range> selectedRange = m_sel.toRange();
if (!parentNode)
return false;
ExceptionCode ec = 0;
bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(ec), selectedRange->startOffset(ec)) >= 0
&& Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(ec), selectedRange->endOffset(ec)) <= 0;
ASSERT(!ec);
if (nodeFullySelected)
return true;
bool nodeFullyUnselected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(ec), selectedRange->endOffset(ec)) > 0
|| Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(ec), selectedRange->startOffset(ec)) < 0;
ASSERT(!ec);
if (nodeFullyUnselected)
return false;
return allowPartial || n->isTextNode();
}
void SelectionController::selectAllChildren(Node* n, ExceptionCode& ec)
{
if (!n)
return;
// This doesn't (and shouldn't) select text node characters.
setBaseAndExtent(n, 0, n, n->childNodeCount(), ec);
}
void SelectionController::setBaseAndExtent(Node *baseNode, int baseOffset, Node *extentNode, int extentOffset, ExceptionCode& ec)
{
if (baseOffset < 0 || extentOffset < 0) {
ec = INDEX_SIZE_ERR;
return;
}
VisiblePosition visibleBase = VisiblePosition(baseNode, baseOffset, DOWNSTREAM);
VisiblePosition visibleExtent = VisiblePosition(extentNode, extentOffset, DOWNSTREAM);
moveTo(visibleBase, visibleExtent);
}
void SelectionController::setPosition(Node *node, int offset, ExceptionCode& ec)
{
if (offset < 0) {
ec = INDEX_SIZE_ERR;
return;
}
moveTo(VisiblePosition(node, offset, DOWNSTREAM));
}
void SelectionController::collapse(Node *node, int offset, ExceptionCode& ec)
{
if (offset < 0) {
ec = INDEX_SIZE_ERR;
return;
}
moveTo(VisiblePosition(node, offset, DOWNSTREAM));
}
void SelectionController::collapseToEnd()
{
moveTo(VisiblePosition(m_sel.end(), DOWNSTREAM));
}
void SelectionController::collapseToStart()
{
moveTo(VisiblePosition(m_sel.start(), DOWNSTREAM));
}
void SelectionController::empty()
{
moveTo(VisiblePosition());
}
void SelectionController::extend(Node* node, int offset, ExceptionCode& ec)
{
if (!node) {
ec = TYPE_MISMATCH_ERR;
return;
}
if (offset < 0
|| node->offsetInCharacters() && offset > caretMaxOffset(node)
|| !node->offsetInCharacters() && offset > (int)node->childNodeCount()) {
ec = INDEX_SIZE_ERR;
return;
}
m_sel.expandUsingGranularity(CharacterGranularity);
setExtent(VisiblePosition(node, offset, DOWNSTREAM));
}
void SelectionController::layout()
{
if (isNone() || !m_sel.start().node()->inDocument() || !m_sel.end().node()->inDocument()) {
......@@ -1244,22 +960,19 @@ void SelectionController::selectAll()
m_frame->notifyRendererOfSelectionChange(true);
}
void SelectionController::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping, ExceptionCode& ec)
bool SelectionController::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping)
{
ec = 0;
if (!range) {
ec = INVALID_STATE_ERR;
return;
}
if (!range)
return false;
ExceptionCode ec = 0;
Node* startContainer = range->startContainer(ec);
if (ec)
return;
return false;
Node* endContainer = range->endContainer(ec);
if (ec)
return;
return false;
ASSERT(startContainer);
ASSERT(endContainer);
......@@ -1271,20 +984,21 @@ void SelectionController::setSelectedRange(Range* range, EAffinity affinity, boo
// they start at the beginning of the next line instead
bool collapsed = range->collapsed(ec);
if (ec)
return;
return false;
int startOffset = range->startOffset(ec);
if (ec)
return;
return false;
int endOffset = range->endOffset(ec);
if (ec)
return;
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
......
......@@ -57,7 +57,7 @@ public:
const Selection& selection() const { return m_sel; }
void setSelection(const Selection&, bool closeTyping = true, bool clearTypingStyle = true, bool userTriggered = false);
void setSelectedRange(Range*, EAffinity, bool closeTyping, ExceptionCode&);
bool setSelectedRange(Range*, EAffinity, bool closeTyping);
void selectAll();
void clear();
......@@ -103,46 +103,6 @@ public:
void nodeWillBeRemoved(Node*);
// Safari Selection Object API
// These methods return the valid equivalents of internal editing positions.
Node* baseNode() const;
Node* extentNode() const;
int baseOffset() const;
int extentOffset() const;
String type() const;
void setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode&);
void setPosition(Node*, int offset, ExceptionCode&);
bool modify(const String& alterString, const String& directionString, const String& granularityString, bool userTriggered = false);
// Mozilla Selection Object API
// In Firefox, anchor/focus are the equal to the start/end of the selection,
// but reflect the direction in which the selection was made by the user. That does
// not mean that they are base/extent, since the base/extent don't reflect
// expansion.
// These methods return the valid equivalents of internal editing positions.
Node* anchorNode() const;
int anchorOffset() const;
Node* focusNode() const;
int focusOffset() const;
bool isCollapsed() const { return !isRange(); }
String toString() const;
void collapse(Node*, int offset, ExceptionCode&);
void collapseToEnd();
void collapseToStart();
void extend(Node*, int offset, ExceptionCode&);
PassRefPtr<Range> getRangeAt(int index, ExceptionCode&) const;
int rangeCount() const { return !isNone() ? 1 : 0; }
void removeAllRanges();
void addRange(const Range*);
void deleteFromDocument();
bool containsNode(const Node*, bool allowPartial) const;
void selectAllChildren(Node*, ExceptionCode&);
// Microsoft Selection Object API
void empty();
//void clear();
//TextRange *createRange();
bool recomputeCaretRect(); // returns true if caret rect moved
void invalidateCaretRect();
void paintCaret(GraphicsContext*, const IntRect&);
......
......@@ -53,6 +53,7 @@
#include "SelectionController.h"
#include "TextBreakIterator.h"
#include "TextEvent.h"
#include "TextIterator.h"
using namespace std;
......@@ -1292,7 +1293,7 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
// Make sure that the text to be inserted will not violate the maxLength.
int oldLen = numGraphemeClusters(value().impl());
ASSERT(oldLen <= maxLength());
int selectionLen = numGraphemeClusters(document()->frame()->selectionController()->toString().impl());
int selectionLen = numGraphemeClusters(plainText(document()->frame()->selectionController()->selection().toRange().get()).impl());
ASSERT(oldLen >= selectionLen);
int maxNewLen = maxLength() - (oldLen - selectionLen);
......
......@@ -30,11 +30,14 @@
#include "config.h"
#include "DOMSelection.h"
#include "ExceptionCode.h"
#include "Frame.h"
#include "htmlediting.h"
#include "Node.h"
#include "PlatformString.h"
#include "Range.h"
#include "SelectionController.h"
#include "TextIterator.h"
namespace WebCore {
......@@ -57,189 +60,375 @@ Node* DOMSelection::anchorNode() const
{
if (!m_frame)
return 0;
return m_frame->selectionController()->anchorNode();
const Selection& selection = m_frame->selectionController()->selection();
Position anchor = selection.isBaseFirst() ? selection.start() : selection.end();
anchor = rangeCompliantEquivalent(anchor);
return anchor.node();
}
Node* DOMSelection::baseNode() const
{
if (!m_frame)
return 0;
return m_frame->selectionController()->baseNode();
return rangeCompliantEquivalent(m_frame->selectionController()->selection().base()).node();
}
int DOMSelection::anchorOffset() const
{
if (!m_frame)
return 0;
return m_frame->selectionController()->anchorOffset();
const Selection& selection = m_frame->selectionController()->selection();
Position anchor = selection.isBaseFirst() ? selection.start() : selection.end();
anchor = rangeCompliantEquivalent(anchor);
return anchor.offset();
}
int DOMSelection::baseOffset() const
{
if (!m_frame)
return 0;
return m_frame->selectionController()->baseOffset();
return rangeCompliantEquivalent(m_frame->selectionController()->selection().base()).offset();
}