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

Reviewed by Darin.

        http://bugs.webkit.org/show_bug.cgi?id=15922
        Implement more of Mozilla Selection API

        Tests: editing/selection/containsNode.html
               editing/selection/deleteFromDocument.html
               editing/selection/extend.html
               editing/selection/selectAllChildren.html

        * editing/SelectionController.cpp:
        (WebCore::SelectionController::deleteFromDocument):
        (WebCore::SelectionController::containsNode):
        (WebCore::SelectionController::selectAllChildren):
        (WebCore::SelectionController::extend):
        * editing/SelectionController.h:
        Added deleteFromDocument(), containsNode(), and selectAllChildren(). Reimplemented extend(),
        which existed, but didn't match Firefox behavior and wasn't exposed via bindings.
        Removed a comment mentioning removeRange(), as this method makes no sense without multiple
        selection range support.

        * page/DOMSelection.cpp:
        (WebCore::DOMSelection::extend):
        (WebCore::DOMSelection::deleteFromDocument):
        (WebCore::DOMSelection::containsNode):
        (WebCore::DOMSelection::selectAllChildren):
        * page/DOMSelection.h:
        * page/DOMSelection.idl:
        Exposed the new methods.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@27666 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent fa296d6a
2007-11-10 Alexey Proskuryakov <ap@nypop.com>
2007-11-10 Alexey Proskuryakov <ap@webkit.org>
Reviewed by Darin.
http://bugs.webkit.org/show_bug.cgi?id=15922
Implement more of Mozilla Selection API
* editing/selection/containsNode-expected.txt: Added.
* editing/selection/containsNode.html: Added.
* editing/selection/deleteFromDocument-expected.txt: Added.
* editing/selection/deleteFromDocument.html: Added.
* editing/selection/extend-expected.txt: Added.
* editing/selection/extend.html: Added.
* editing/selection/selectAllChildren-expected.txt: Added.
* editing/selection/selectAllChildren.html: Added.
* fast/dom/Window/window-properties-expected.txt:
2007-11-10 Alexey Proskuryakov <ap@webkit.org>
Reviewed by Darin.
......
foobarbaz
Success: s.containsNode(null, false) is false.
Success: s.containsNode(null, true) is false.
Success: s.containsNode(testDiv, false) is false.
Success: s.containsNode(testDiv, true) is true.
Success: s.containsNode(span1, false) is false.
Success: s.containsNode(span1, true) is true.
Success: s.containsNode(span2.firstChild, false) is true.
Success: s.containsNode(span2.firstChild, true) is true.
Success: s.containsNode(span3.firstChild, false) is false.
Success: s.containsNode(span3.firstChild, true) is false.
Success: s.containsNode(document, false) is false.
Success: s.containsNode(document.body, true) is true.
Success: s.containsNode(document.getElementById("console"), false) is false.
Success: s.containsNode(document.getElementById("console"), true) is false.
Success: s.containsNode(document.implementation.createDocumentType("name", "", ""), true) is false.
Success: s.containsNode(span2.firstChild, false) is true.
Success: s.containsNode(span2.firstChild, true) is true.
Success: s.containsNode(span2, false) is false.
Success: s.containsNode(span2, true) is true.
Success: s.containsNode(span1, false) is false.
Success: s.containsNode(span1, true) is true.
Success: s.containsNode(span2, false) is true.
Success: s.containsNode(span2.firstChild, false) is true.
Success: s.containsNode(span3, false) is false.
Success: s.containsNode(span3, true) is true.
<html>
<body>
<div id="test"><span id="span1">foo<span id="span2">bar</span></span><span id="span3">baz</span></div>
<div id="console"></div>
<script>
var s = window.getSelection();
var testDiv = document.getElementById("test");
var span1 = document.getElementById("span1");
var span2 = document.getElementById("span2");
var span3 = document.getElementById("span3");
function log(str) {
var li = document.createElement("li");
li.appendChild(document.createTextNode(str));
document.getElementById("console").appendChild(li);
}
function shouldBe(expr, expected) {
var actual = eval(expr);
if (actual != expected)
log("Failure: " + expr + " should be " + expected + ", was " + actual + ".");
else
log("Success: " + expr + " is " + expected + ".");
}
if (window.layoutTestController)
layoutTestController.dumpAsText();
var r = document.createRange();
// select span2
r.setStart(span1, 1);
r.setEnd(span1, 2);
s.addRange(r);
shouldBe('s.containsNode(null, false)', false);
shouldBe('s.containsNode(null, true)', false);
shouldBe('s.containsNode(testDiv, false)', false);
shouldBe('s.containsNode(testDiv, true)', true);
shouldBe('s.containsNode(span1, false)', false);
shouldBe('s.containsNode(span1, true)', true);
shouldBe('s.containsNode(span2.firstChild, false)', true);
shouldBe('s.containsNode(span2.firstChild, true)', true);
shouldBe('s.containsNode(span3.firstChild, false)', false);
shouldBe('s.containsNode(span3.firstChild, true)', false);
shouldBe('s.containsNode(document, false)', false);
shouldBe('s.containsNode(document.body, true)', true);
shouldBe('s.containsNode(document.getElementById("console"), false)', false);
shouldBe('s.containsNode(document.getElementById("console"), true)', false);
shouldBe('s.containsNode(document.implementation.createDocumentType("name", "", ""), true)', false);
// partially select a text node
r.setStart(span2.firstChild, 1);
r.setEnd(span2.firstChild, 2);
s.removeAllRanges();
s.addRange(r);
shouldBe('s.containsNode(span2.firstChild, false)', true);
shouldBe('s.containsNode(span2.firstChild, true)', true);
shouldBe('s.containsNode(span2, false)', false);
shouldBe('s.containsNode(span2, true)', true);
shouldBe('s.containsNode(span1, false)', false);
shouldBe('s.containsNode(span1, true)', true);
// select several nodes
r.setStart(span1.firstChild, 1);
r.setEnd(span3.firstChild, 2);
s.removeAllRanges();
s.addRange(r);
shouldBe('s.containsNode(span2, false)', true);
shouldBe('s.containsNode(span2.firstChild, false)', true);
shouldBe('s.containsNode(span3, false)', false);
shouldBe('s.containsNode(span3, true)', true);
</script>
</body>
</html>
fz
Success: span1.textContent is fooar.
Success: span1.textContent is foar.
Success: span1.textContent is foar.
Success: span1.firstChild.nextSibling.id is span2.
Success: span1.firstChild.nextSibling.firstChild.nodeValue is .
Success: test.textContent is fz.
<html>
<body>
<div id="test"><span id="span1">foo<span id="span2">bar</span></span><span id="span3">baz</span></div>
<div id="console"></div>
<script>
var s = window.getSelection();
var testDiv = document.getElementById("test");
var span1 = document.getElementById("span1");
var span2 = document.getElementById("span2");
var span3 = document.getElementById("span3");
function log(str) {
var li = document.createElement("li");
li.appendChild(document.createTextNode(str));
document.getElementById("console").appendChild(li);
}
function shouldBe(expr, expected) {
var actual = eval(expr);
if (actual != expected)
log("Failure: " + expr + " should be " + expected + ", was " + actual + ".");
else
log("Success: " + expr + " is " + expected + ".");
}
if (window.layoutTestController)
layoutTestController.dumpAsText();
var r = document.createRange();
// delete a collapsed selection
r.setStart(span2.firstChild, 1);
r.setEnd(span2.firstChild, 1);
s.addRange(r);
s.deleteFromDocument()
shouldBe('span1.textContent', 'fooar');
try {
s.deleteFromDocument()
shouldBe('span1.textContent', 'foar');
} catch (ex) {
log(ex);
}
// try to delete a collapsed selection at the start
r.setStart(span1.firstChild, 0);
r.setEnd(span1.firstChild, 0);
s.removeAllRanges();
s.addRange(r);
try {
s.deleteFromDocument()
shouldBe('span1.textContent', 'foar');
} catch (ex) {
log(ex);
}
// delete whole contents
r.setStart(span2.firstChild, 0);
r.setEnd(span2.firstChild, 2);
s.removeAllRanges();
s.addRange(r);
s.deleteFromDocument()
shouldBe('span1.firstChild.nextSibling.id', 'span2');
shouldBe('span1.firstChild.nextSibling.firstChild.nodeValue', '');
// partially delete two nodes
r.setStart(span1.firstChild, 1);
r.setEnd(span3.firstChild, 2);
s.removeAllRanges();
s.addRange(r);
s.deleteFromDocument()
shouldBe('test.textContent', 'fz');
</script>
</body>
</html>
foobarbaz
Success: window.getSelection() is ar.
Success: s.extend(span2.firstChild, 4) raised Error: INDEX_SIZE_ERR: DOM Exception 1.
Success: s.extend(span2.firstChild, -1) raised Error: INDEX_SIZE_ERR: DOM Exception 1.
Success: window.getSelection() is b.
Success: window.getSelection() is arbaz.
Success: s.extend() raised Error: TYPE_MISMATCH_ERR: DOM Exception 17.
Success: s.extend(span3) raised Error: TYPE_MISMATCH_ERR: DOM Exception 17.
Success: s.extend(null, 0) raised Error: TYPE_MISMATCH_ERR: DOM Exception 17.
<html>
<body>
<div id="test"><span id="span1">foo<span id="span2">bar</span></span><span id="span3">baz</span></div>
<div id="console"></div>
<script>
var s = window.getSelection();
var testDiv = document.getElementById("test");
var span1 = document.getElementById("span1");
var span2 = document.getElementById("span2");
var span3 = document.getElementById("span3");
function log(str) {
var li = document.createElement("li");
li.appendChild(document.createTextNode(str));
document.getElementById("console").appendChild(li);
}
function shouldBe(expr, expected) {
var actual = eval(expr);
if (actual != expected)
log("Failure: " + expr + " should be " + expected + ", was " + actual + ".");
else
log("Success: " + expr + " is " + expected + ".");
}
function shouldThrow(expr) {
try {
eval(expr);
log("Failure: " + expr + " didn't raise an exception" + ".");
} catch (ex) {
log("Success: " + expr + " raised " + ex + ".");
}
}
if (window.layoutTestController)
layoutTestController.dumpAsText();
var r = document.createRange();
r.setStart(span2.firstChild, 1);
r.setEnd(span2.firstChild, 1);
s.addRange(r);
s.extend(span2.firstChild, 3);
shouldBe('window.getSelection()', 'ar');
shouldThrow('s.extend(span2.firstChild, 4)');
shouldThrow('s.extend(span2.firstChild, -1)');
s.extend(span2.firstChild, 0);
shouldBe('window.getSelection()', 'b');
s.extend(span3, 1);
shouldBe('window.getSelection()', 'arbaz');
shouldThrow('s.extend()');
shouldThrow('s.extend(span3)');
shouldThrow('s.extend(null, 0)');
</script>
</body>
</html>
foobar
Success: s.anchorNode is [object Text].
Success: s.anchorOffset is 0.
Success: s.focusNode is [object Text].
Success: s.focusOffset is 0.
Success: window.getSelection() is foo.
Success: window.getSelection() is foobar.
<html>
<body>
<div id="test"><span id="span1">foo</span><span>bar</span></div>
<div id="console"></div>
<script>
var s = window.getSelection();
var testDiv = document.getElementById("test");
var span1 = document.getElementById("span1");
function log(str) {
var li = document.createElement("li");
li.appendChild(document.createTextNode(str));
document.getElementById("console").appendChild(li);
}
function shouldBe(expr, expected) {
var actual = eval(expr);
if (actual != expected)
log("Failure: " + expr + " should be " + expected + ", was " + actual + ".");
else
log("Success: " + expr + " is " + expected + ".");
}
if (window.layoutTestController)
layoutTestController.dumpAsText();
var r = document.createRange();
// select span1
r.setStart(test, 0);
r.setEnd(test, 1);
s.addRange(r);
// replace the selection with span1's text node children
s.selectAllChildren(span1.firstChild);
shouldBe('s.anchorNode', span1.firstChild);
shouldBe('s.anchorOffset', 0);
shouldBe('s.focusNode', span1.firstChild);
shouldBe('s.focusOffset', 0); // Strange, but matches Firefox
// replace the selection with span1's children
s.selectAllChildren(span1);
shouldBe('window.getSelection()', 'foo');
// replace the selection with test's children
s.selectAllChildren(test);
shouldBe('window.getSelection()', 'foobar');
</script>
</body>
</html>
......@@ -1327,7 +1327,10 @@ window.getSelection().baseOffset [number]
window.getSelection().collapse [function]
window.getSelection().collapseToEnd [function]
window.getSelection().collapseToStart [function]
window.getSelection().containsNode [function]
window.getSelection().deleteFromDocument [function]
window.getSelection().empty [function]
window.getSelection().extend [function]
window.getSelection().extentNode [null]
window.getSelection().extentOffset [number]
window.getSelection().focusNode [null]
......@@ -1337,6 +1340,7 @@ window.getSelection().isCollapsed [boolean]
window.getSelection().modify [function]
window.getSelection().rangeCount [number]
window.getSelection().removeAllRanges [function]
window.getSelection().selectAllChildren [function]
window.getSelection().setBaseAndExtent [function]
window.getSelection().setPosition [function]
window.getSelection().type [string]
......
2007-11-10 Alexey Proskuryakov <ap@nypop.com>
2007-11-10 Alexey Proskuryakov <ap@webkit.org>
Reviewed by Darin.
http://bugs.webkit.org/show_bug.cgi?id=15922
Implement more of Mozilla Selection API
Tests: editing/selection/containsNode.html
editing/selection/deleteFromDocument.html
editing/selection/extend.html
editing/selection/selectAllChildren.html
* editing/SelectionController.cpp:
(WebCore::SelectionController::deleteFromDocument):
(WebCore::SelectionController::containsNode):
(WebCore::SelectionController::selectAllChildren):
(WebCore::SelectionController::extend):
* editing/SelectionController.h:
Added deleteFromDocument(), containsNode(), and selectAllChildren(). Reimplemented extend(),
which existed, but didn't match Firefox behavior and wasn't exposed via bindings.
Removed a comment mentioning removeRange(), as this method makes no sense without multiple
selection range support.
* page/DOMSelection.cpp:
(WebCore::DOMSelection::extend):
(WebCore::DOMSelection::deleteFromDocument):
(WebCore::DOMSelection::containsNode):
(WebCore::DOMSelection::selectAllChildren):
* page/DOMSelection.h:
* page/DOMSelection.idl:
Exposed the new methods.
2007-11-10 Alexey Proskuryakov <ap@webkit.org>
Reviewed by Darin.
......
......@@ -824,6 +824,61 @@ void SelectionController::addRange(const Range* r)
}
}
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) {
......@@ -869,13 +924,20 @@ void SelectionController::empty()
moveTo(VisiblePosition());
}
void SelectionController::extend(Node *node, int offset, ExceptionCode& ec)
void SelectionController::extend(Node* node, int offset, ExceptionCode& ec)
{
if (offset < 0) {
if (!node) {
ec = TYPE_MISMATCH_ERR;
return;
}
if (offset < 0
|| node->offsetInCharacters() && offset > node->caretMaxOffset()
|| !node->offsetInCharacters() && offset > (int)node->childNodeCount()) {
ec = INDEX_SIZE_ERR;
return;
}
moveTo(VisiblePosition(node, offset, DOWNSTREAM));
m_sel.expandUsingGranularity(CharacterGranularity);
setExtent(VisiblePosition(node, offset, DOWNSTREAM));
}
void SelectionController::layout()
......
......@@ -115,7 +115,7 @@ public:
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,
// 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.
......@@ -134,10 +134,9 @@ public:
int rangeCount() const { return !isNone() ? 1 : 0; }
void removeAllRanges();
void addRange(const Range*);
//void deleteFromDocument();
//bool containsNode(Node *node, bool entirelyContained);
//void selectAllChildren(const Node *);
//void removeRange(const Range *);
void deleteFromDocument();
bool containsNode(const Node*, bool allowPartial) const;
void selectAllChildren(Node*, ExceptionCode&);
// Microsoft Selection Object API
void empty();
......
......@@ -186,6 +186,13 @@ void DOMSelection::modify(const String& alter, const String& direction, const St
m_frame->selectionController()->modify(alter, direction, granularity);
}
void DOMSelection::extend(Node* n, int offset, ExceptionCode& ec)
{
if (!m_frame)
return;
m_frame->selectionController()->extend(n, offset, ec);
}
PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionCode& ec)
{
if (!m_frame)
......@@ -207,6 +214,27 @@ void DOMSelection::addRange(Range* range)
m_frame->selectionController()->addRange(range);
}
void DOMSelection::deleteFromDocument()
{
if (!m_frame)
return;
m_frame->selectionController()->deleteFromDocument();
}
bool DOMSelection::containsNode(const Node* n, bool allowPartial) const
{
if (!m_frame)
return false;
return m_frame->selectionController()->containsNode(n, allowPartial);
}
void DOMSelection::selectAllChildren(Node* n, ExceptionCode& ec)
{
if (!m_frame)
return;
m_frame->selectionController()->selectAllChildren(n, ec);
}
String DOMSelection::toString()
{
if (!m_frame)
......
......@@ -70,9 +70,13 @@ namespace WebCore {
void setPosition(Node*, int offset, ExceptionCode&);
void setPosition(Node*, ExceptionCode&);
void modify(const String& alter, const String& direction, const String& granularity);
void extend(Node*, int offset, ExceptionCode&);
PassRefPtr<Range> getRangeAt(int, ExceptionCode&);
void removeAllRanges();
void addRange(Range*);
void deleteFromDocument();
bool containsNode(const Node*, bool partlyContained) const;
void selectAllChildren(Node*, ExceptionCode&);
String toString();
......
......@@ -45,12 +45,18 @@ module window {
raises(DOMException);
void collapseToEnd();
void collapseToStart();
void deleteFromDocument();
boolean containsNode(in Node node, in boolean allowPartial);
void selectAllChildren(in Node node)
raises(DOMException);
void empty();
void setBaseAndExtent(in Node baseNode, in long baseOffset, in Node extentNode, in long extentOffset)
raises(DOMException);
void setPosition(in Node node, in [Optional] long offset)
raises(DOMException);
void modify(in DOMString alter, in DOMString direction, in DOMString granularity);
void extend(in Node node, in long offset)
raises(DOMException);
Range getRangeAt(in long index)
raises(DOMException);
void removeAllRanges();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment