[SCM] WebKit Debian packaging branch, webkit-1.2, updated. upstream/1.1.90-6072-g9a69373

enrica at apple.com enrica at apple.com
Thu Apr 8 00:04:13 UTC 2010


The following commit has been merged in the webkit-1.2 branch:
commit 429c6e2bc1c08d669286405ec264cafd16c9631b
Author: enrica at apple.com <enrica at apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Tue Dec 1 00:44:30 2009 +0000

    WebCore: Can focus but not type into content editable block that contains only non-editable content.
    <rdar://problem/5982901>
    https://bugs.webkit.org/show_bug.cgi?id=31750
    
    Reviewed by Darin Adler.
    
    The goal is to change the way we choose a visible position
    after hit detection, by preferring a visually equivalent editable
    position if available. By doing this, it is possible to add content
    to an editable block that initially contains only non editable elements.
    
    Test: editing/selection/mixed-editability-10.html
    
    * WebCore.base.exp: Changed to match the new signature of downstream
    and upstream in the Position class.
    * dom/Position.cpp:
    (WebCore::Position::atEditingBoundary): Added.
    (WebCore::Position::upstream): Modified to allow to cross the boundary
    between editable and non editable content if required.
    (WebCore::Position::downstream): Modified to allow to cross the boundary
    between editable and non editable content if required.
    (WebCore::Position::isCandidate): Modified to qualify as candidates positions
    that are at the editability boundary.
    (WebCore::Position::getInlineBoxAndOffset): Modified to retrieve the inline box
    to be used in calculating the caret rectangle.
    * dom/Position.h:
    (WebCore::Position::):
    * dom/PositionIterator.cpp:
    (WebCore::PositionIterator::atEditingBoundary): Added.
    (WebCore::PositionIterator::isCandidate): Modified to qualify as candidates positions
    that are at the editability boundary.
    * dom/PositionIterator.h:
    * editing/htmlediting.cpp:
    (WebCore::firstEditablePositionAfterPositionInRoot): Modified to accept not only
    descendants of the editable container, but the container itself.
    (WebCore::lastEditablePositionBeforePositionInRoot): Modified to accept not only
    descendants of the editable container, but the container itself.
    * rendering/RenderObject.cpp:
    (WebCore::RenderObject::createVisiblePosition): Added logic to prefer an editable position,
    if available.
    * rendering/RenderText.cpp:
    (WebCore::RenderText::isAllCollapsibleWhitespace): Added.
    * rendering/RenderText.h:
    
    LayoutTests: Can focus but not type into content editable block that contains only non-editable content.
    <rdar://problem/5982901>
    https://bugs.webkit.org/show_bug.cgi?id=31750
    
    Reviewed by Darin Adler.
    
    * editing/selection/5825350-1-expected.txt:
    * editing/selection/5825350-1.html: Modified to use caret rectangle.
    * editing/selection/5825350-2-expected.txt:
    * editing/selection/5825350-2.html: Modified to use caret rectangle.
    * editing/selection/mixed-editability-10-expected.txt: Added.
    * editing/selection/mixed-editability-10.html: Added.
    * platform/mac/editing/deleting/5390681-2-expected.txt: Re-baselined
    to account for the new possible caret position.
    
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@51522 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 70aad6d..a784868 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,20 @@
+2009-11-30  Enrica Casucci  <enrica at apple.com>
+
+        Reviewed by Darin Adler.
+
+        Can focus but not type into content editable block that contains only non-editable content.
+        <rdar://problem/5982901>
+        https://bugs.webkit.org/show_bug.cgi?id=31750
+
+        * editing/selection/5825350-1-expected.txt:
+        * editing/selection/5825350-1.html: Modified to use caret rectangle.
+        * editing/selection/5825350-2-expected.txt:
+        * editing/selection/5825350-2.html: Modified to use caret rectangle.
+        * editing/selection/mixed-editability-10-expected.txt: Added.
+        * editing/selection/mixed-editability-10.html: Added.
+        * platform/mac/editing/deleting/5390681-2-expected.txt: Re-baselined
+        to account for the new possible caret position.
+
 2009-11-30  Beth Dakin  <bdakin at apple.com>
 
         Reviewed by Oliver Hunt.
diff --git a/LayoutTests/editing/selection/5825350-1-expected.txt b/LayoutTests/editing/selection/5825350-1-expected.txt
index 0f3673f..62b125e 100644
--- a/LayoutTests/editing/selection/5825350-1-expected.txt
+++ b/LayoutTests/editing/selection/5825350-1-expected.txt
@@ -1,3 +1,3 @@
-This tests for a bug where moving the caret left towards a non-editable pocket of an editable region would make the caret disappear. The caret should be just after the 'b' in "Bob".
+This tests for a bug where moving the caret left towards a non-editable pocket of an editable region would make the caret disappear. The caret should be just before the 'B' in "Bob".
 
-Caret One: (" ; ", 0)
+Caret: (8, 538)
diff --git a/LayoutTests/editing/selection/5825350-1.html b/LayoutTests/editing/selection/5825350-1.html
index 743d3ed..5b06dcd 100644
--- a/LayoutTests/editing/selection/5825350-1.html
+++ b/LayoutTests/editing/selection/5825350-1.html
@@ -1,4 +1,4 @@
-<div id="description">This tests for a bug where moving the caret left towards a non-editable pocket of an editable region would make the caret disappear. The caret should be just after the 'b' in "Bob".</div>
+<div id="description">This tests for a bug where moving the caret left towards a non-editable pocket of an editable region would make the caret disappear. The caret should be just before the 'B' in "Bob".</div>
 <div id="edit" contenteditable="true"><span contenteditable="false">Bob</span> ; <span contenteditable="false">Sally</span></div>
 
 <script>
@@ -10,6 +10,8 @@ text = edit.childNodes[1];
 s = window.getSelection();
 s.setPosition(text, 0);
 s.modify("move", "left", "character");
-if (window.layoutTestController)
-    document.body.innerText = document.getElementById("description").innerText + "\n\nCaret One: (\"" + s.anchorNode.data + "\", " + s.anchorOffset + ")";
+if (window.layoutTestController) {
+    var caretRect = textInputController.firstRectForCharacterRange(textInputController.selectedRange()[0], 0);
+    document.body.innerText = document.getElementById("description").innerText + "\n\nCaret: (" + caretRect[0] + ", " + caretRect[1] + ")";
+}
 </script>
diff --git a/LayoutTests/editing/selection/5825350-2-expected.txt b/LayoutTests/editing/selection/5825350-2-expected.txt
index 9707516..f906101 100644
--- a/LayoutTests/editing/selection/5825350-2-expected.txt
+++ b/LayoutTests/editing/selection/5825350-2-expected.txt
@@ -1,3 +1,3 @@
-This tests for a bug where moving the caret right towards a non-editable pocket of an editable region would make the caret disappear. The caret should be just before the 'S' in "Sally".
+This tests for a bug where moving the caret right towards a non-editable pocket of an editable region would make the caret disappear. The caret should be just after the 'y' in "Sally".
 
-Caret One: (" ; ", 3)
+Caret: (79, 538)
diff --git a/LayoutTests/editing/selection/5825350-2.html b/LayoutTests/editing/selection/5825350-2.html
index 6e7ef57..110b425 100644
--- a/LayoutTests/editing/selection/5825350-2.html
+++ b/LayoutTests/editing/selection/5825350-2.html
@@ -1,4 +1,4 @@
-<div id="description">This tests for a bug where moving the caret right towards a non-editable pocket of an editable region would make the caret disappear. The caret should be just before the 'S' in "Sally".</div>
+<div id="description">This tests for a bug where moving the caret right towards a non-editable pocket of an editable region would make the caret disappear. The caret should be just after the 'y' in "Sally".</div>
 <div id="edit" contenteditable="true"><span contenteditable="false">Bob</span> ; <span contenteditable="false">Sally</span></div>
 
 <script>
@@ -10,6 +10,8 @@ text = edit.childNodes[1];
 s = window.getSelection();
 s.setPosition(text, text.length);
 s.modify("move", "right", "character");
-if (window.layoutTestController)
-    document.body.innerText = document.getElementById("description").innerText + "\n\nCaret One: (\"" + s.anchorNode.data + "\", " + s.anchorOffset + ")";
+if (window.layoutTestController) {
+    var caretRect = textInputController.firstRectForCharacterRange(textInputController.selectedRange()[0], 0);
+    document.body.innerText = document.getElementById("description").innerText + "\n\nCaret: (" + caretRect[0] + ", " + caretRect[1] + ")";
+}
 </script>
diff --git a/LayoutTests/editing/selection/mixed-editability-10-expected.txt b/LayoutTests/editing/selection/mixed-editability-10-expected.txt
new file mode 100644
index 0000000..c739819
--- /dev/null
+++ b/LayoutTests/editing/selection/mixed-editability-10-expected.txt
@@ -0,0 +1,21 @@
+#1 DIV element with a non-editable element only align center:
+
+Hello
+#2 DIV element with a non-editable element only align left:
+
+Hello
+#3 DIV element with a non-editable element only align right:
+
+Hello
+#4 DIV element with two non-editable elementwith padding:
+
+Hello World
+#5 DIV element empty
+
+Anchor ([object HTMLDivElement], 0 caret[40,540] refpos=40) is correct.
+Anchor ([object HTMLDivElement], 3 caret[75,540] refpos=75) is correct.
+Anchor ([object HTMLDivElement], 1 caret[43,472] refpos=43) is correct.
+Anchor ([object HTMLDivElement], 0 caret[8,472] refpos=8) is correct.
+Anchor ([object HTMLDivElement], 0 caret[73,404] refpos=73) is correct.
+Anchor ([object HTMLDivElement], 3 caret[47,336] refpos=47) is correct.
+Anchor ([object HTMLDivElement], 0 caret[58,268] refpos=58) is correct.
diff --git a/LayoutTests/editing/selection/mixed-editability-10.html b/LayoutTests/editing/selection/mixed-editability-10.html
new file mode 100644
index 0000000..fb7c1e7
--- /dev/null
+++ b/LayoutTests/editing/selection/mixed-editability-10.html
@@ -0,0 +1,106 @@
+<html>
+<head>
+    <title>This tests the ability to place the caret in an editable div that contains only non editable content</title>
+</head>
+<body>
+  <p>#1 DIV element with a non-editable element only <span style="color:red">align center</span>:</p>
+  <div style="width:100px;background-color:#cee;text-align: center;" contenteditable="true" id="edit1">
+    <span contenteditable="false" id="nonedit1">Hello</span>
+  </div>
+  <p>#2 DIV element with a non-editable element only <span style="color:red">align left</span>:</p>
+  <div style="width:100px;background-color:#cee;" contenteditable="true" id="edit2"><span contenteditable="false" id="nonedit2">Hello</span></div>
+  <p>#3 DIV element with a non-editable element only <span style="color:red">align right</span>:</p>
+  <div style="width:100px;background-color:#cee;text-align: right;" contenteditable="true" id="edit3">
+  <span contenteditable="false" id="nonedit3">Hello</span></div>
+  <p>#4 DIV element with two non-editable element<span style="color:red">with padding</span>:</p>
+  <div style="width:200px;background-color:#cee;" contenteditable="true" id="edit4">
+      <span contenteditable="false">Hello </span>
+      <span contenteditable="false" id="nonedit4">World</span>
+  </div>
+  <p>#5 DIV element empty</p>
+  <div style="width:100px;background-color:#cee;text-align: center;" contenteditable="true" id="edit5">
+  </div>
+  <ul id="console"></ul>
+</body>
+<script>
+function log(str) {
+    var li = document.createElement("li");
+    li.appendChild(document.createTextNode(str));
+    var console = document.getElementById("console");
+    console.appendChild(li);
+}
+
+function caretCoordinates()
+{
+    if (!window.textInputController)
+        return { x: 0, y :0 };
+    var caretRect = textInputController.firstRectForCharacterRange(textInputController.selectedRange()[0], 0);
+    return { x: caretRect[0], y: caretRect[1] };
+}
+
+function runTest(x, y, elem, offset, refpos) {
+    eventSender.mouseMoveTo(x, y);
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+    eventSender.mouseDown();
+    eventSender.mouseUp();
+    
+    var selection = window.getSelection();
+
+    var anchorNode = selection.anchorNode;
+    var anchorOffset = selection.anchorOffset;
+
+    var coord = caretCoordinates();
+    var anchorString = "Anchor (" + anchorNode + ", " + anchorOffset + " caret[" + coord.x + "," + coord.y + "] refpos=" + refpos + ")";
+    var anchorCorrect = anchorNode == elem && anchorOffset == offset && coord.x == refpos;
+    if (anchorCorrect)
+        log(anchorString + " is correct.");
+    else
+        log(anchorString + " is incorrect.");
+}
+
+function automaticTest() {
+    if (window.layoutTestController) {   
+        window.layoutTestController.dumpAsText();
+            
+        var elem;
+        
+        // the div has text-alignment center
+        elem = document.getElementById("edit1");
+        x = elem.offsetLeft + 10;
+        y = elem.offsetTop + elem.offsetHeight / 2;
+        runTest(x, y, elem, 0, document.getElementById("nonedit1").offsetLeft);
+        x = elem.offsetLeft + elem.offsetWidth - 10;
+        runTest(x, y, elem, 3, document.getElementById("nonedit1").offsetLeft + document.getElementById("nonedit1").offsetWidth);       
+
+        // the div has text-alignment left
+        elem = document.getElementById("edit2");
+        x = elem.offsetLeft + elem.offsetWidth - 10;
+        y = elem.offsetTop + elem.offsetHeight / 2;
+        runTest(x, y, elem, 1, document.getElementById("nonedit2").offsetLeft + document.getElementById("nonedit2").offsetWidth);
+        x = elem.offsetLeft;
+        runTest(x, y, elem, 0, document.getElementById("nonedit2").offsetLeft);
+
+        // the div has text-alignment right
+        elem = document.getElementById("edit3");
+        x = elem.offsetLeft + 10;
+        y = elem.offsetTop + elem.offsetHeight / 2;
+        runTest(x, y, elem, 0, document.getElementById("nonedit3").offsetLeft);
+
+        // the div contains 2 non editable span
+        elem = document.getElementById("edit4");
+        x = document.getElementById("nonedit4").offsetLeft;
+        y = elem.offsetTop + elem.offsetHeight / 2;
+        runTest(x, y, elem, 3, document.getElementById("nonedit4").offsetLeft);
+
+        // the div is empty
+        elem = document.getElementById("edit5");
+        x = elem.offsetLeft;
+        y = elem.offsetTop + elem.offsetHeight / 2;
+        runTest(x, y, elem, 0, (elem.offsetLeft + elem.offsetWidth)/2 + 4);
+    }
+}
+
+automaticTest();
+</script>
+</html>
diff --git a/LayoutTests/platform/mac/editing/deleting/5390681-2-expected.txt b/LayoutTests/platform/mac/editing/deleting/5390681-2-expected.txt
index 13ba0d1..a8bb54d 100644
--- a/LayoutTests/platform/mac/editing/deleting/5390681-2-expected.txt
+++ b/LayoutTests/platform/mac/editing/deleting/5390681-2-expected.txt
@@ -16,3 +16,4 @@ layer at (0,0) size 800x600
         RenderInline {SPAN} at (0,0) size 20x18
           RenderText {#text} at (0,0) size 20x18
             text run at (0,0) width 20: "bar"
+caret: position 0 of child 2 {DIV} of child 1 {BODY} of child 0 {HTML} of document
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index eb0e253..769717f 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -1,3 +1,49 @@
+2009-11-30  Enrica Casucci  <enrica at apple.com>
+
+        Reviewed by Darin Adler.
+
+        Can focus but not type into content editable block that contains only non-editable content.
+        <rdar://problem/5982901>
+        https://bugs.webkit.org/show_bug.cgi?id=31750
+        
+        The goal is to change the way we choose a visible position
+        after hit detection, by preferring a visually equivalent editable
+        position if available. By doing this, it is possible to add content
+        to an editable block that initially contains only non editable elements.
+
+        Test: editing/selection/mixed-editability-10.html
+
+        * WebCore.base.exp: Changed to match the new signature of downstream
+        and upstream in the Position class.
+        * dom/Position.cpp:
+        (WebCore::Position::atEditingBoundary): Added.
+        (WebCore::Position::upstream): Modified to allow to cross the boundary
+        between editable and non editable content if required.
+        (WebCore::Position::downstream): Modified to allow to cross the boundary
+        between editable and non editable content if required.
+        (WebCore::Position::isCandidate): Modified to qualify as candidates positions
+        that are at the editability boundary.
+        (WebCore::Position::getInlineBoxAndOffset): Modified to retrieve the inline box
+        to be used in calculating the caret rectangle.
+        * dom/Position.h:
+        (WebCore::Position::):
+        * dom/PositionIterator.cpp:
+        (WebCore::PositionIterator::atEditingBoundary): Added.
+        (WebCore::PositionIterator::isCandidate): Modified to qualify as candidates positions
+        that are at the editability boundary.
+        * dom/PositionIterator.h:
+        * editing/htmlediting.cpp:
+        (WebCore::firstEditablePositionAfterPositionInRoot): Modified to accept not only
+        descendants of the editable container, but the container itself.
+        (WebCore::lastEditablePositionBeforePositionInRoot): Modified to accept not only
+        descendants of the editable container, but the container itself.
+        * rendering/RenderObject.cpp:
+        (WebCore::RenderObject::createVisiblePosition): Added logic to prefer an editable position,
+        if available.
+        * rendering/RenderText.cpp:
+        (WebCore::RenderText::isAllCollapsibleWhitespace): Added.
+        * rendering/RenderText.h:
+
 2009-11-30  Kevin Ollivier  <kevino at theolliviers.com>
 
         wx build fix, add header needed for wx build.
diff --git a/WebCore/WebCore.base.exp b/WebCore/WebCore.base.exp
index f82c59a..5f94bbe 100644
--- a/WebCore/WebCore.base.exp
+++ b/WebCore/WebCore.base.exp
@@ -963,10 +963,10 @@ __ZNK7WebCore8Document31displayStringModifiedByEncodingERKNS_6StringE
 __ZNK7WebCore8Document4bodyEv
 __ZNK7WebCore8Document6domainEv
 __ZNK7WebCore8IntPointcv8_NSPointEv
-__ZNK7WebCore8Position10downstreamEv
 __ZNK7WebCore8Position25leadingWhitespacePositionENS_9EAffinityEb
 __ZNK7WebCore8Position26trailingWhitespacePositionENS_9EAffinityEb
-__ZNK7WebCore8Position8upstreamEv
+__ZNK7WebCore8Position10downstreamENS0_27EditingBoundaryCrossingRuleE
+__ZNK7WebCore8Position8upstreamENS0_27EditingBoundaryCrossingRuleE
 __ZNK7WebCore9DOMWindow27pendingUnloadEventListenersEv
 __ZNK7WebCore9FloatRectcv7_NSRectEv
 __ZNK7WebCore9FrameTree12traverseNextEPKNS_5FrameE
diff --git a/WebCore/dom/Position.cpp b/WebCore/dom/Position.cpp
index 060b28c..0ff8262 100644
--- a/WebCore/dom/Position.cpp
+++ b/WebCore/dom/Position.cpp
@@ -307,6 +307,27 @@ bool Position::atLastEditingPositionForNode() const
     return m_offset >= lastOffsetForEditing(node());
 }
 
+// A position is considered at editing boundary if one of the following is true:
+// 1. It is the first position in the node and the next visually equivalent position
+//    is non editable.
+// 2. It is the last position in the node and the previous visually equivalent position
+//    is non editable.
+// 3. It is an editable position and both the next and previous visually equivalent
+//    positions are both non editable.
+bool Position::atEditingBoundary() const
+{
+    Position nextPosition = downstream(CanCrossEditingBoundary);
+    if (atFirstEditingPositionForNode() && nextPosition.isNotNull() && !nextPosition.node()->isContentEditable())
+        return true;
+        
+    Position prevPosition = upstream(CanCrossEditingBoundary);
+    if (atLastEditingPositionForNode() && prevPosition.isNotNull() && !prevPosition.node()->isContentEditable())
+        return true;
+        
+    return nextPosition.isNotNull() && !nextPosition.node()->isContentEditable()
+        && prevPosition.isNotNull() && !prevPosition.node()->isContentEditable();
+}
+
 bool Position::atStartOfTree() const
 {
     if (isNull())
@@ -448,7 +469,7 @@ static bool isStreamer(const PositionIterator& pos)
 // and downstream() will return the right one.
 // Also, upstream() will return [boundary, 0] for any of the positions from [boundary, 0] to the first candidate
 // in boundary, where endsOfNodeAreVisuallyDistinctPositions(boundary) is true.
-Position Position::upstream() const
+Position Position::upstream(EditingBoundaryCrossingRule rule) const
 {
     Node* startNode = node();
     if (!startNode)
@@ -460,6 +481,7 @@ Position Position::upstream() const
     PositionIterator currentPos = lastVisible;
     bool startEditable = startNode->isContentEditable();
     Node* lastNode = startNode;
+    bool boundaryCrossed = false;
     for (; !currentPos.atStart(); currentPos.decrement()) {
         Node* currentNode = currentPos.node();
         
@@ -468,8 +490,11 @@ Position Position::upstream() const
         if (currentNode != lastNode) {
             // Don't change editability.
             bool currentEditable = currentNode->isContentEditable();
-            if (startEditable != currentEditable)
-                break;
+            if (startEditable != currentEditable) {
+                if (rule == CannotCrossEditingBoundary)
+                    break;
+                boundaryCrossed = true;
+            }
             lastNode = currentNode;
         }
 
@@ -483,6 +508,11 @@ Position Position::upstream() const
         if (!renderer || renderer->style()->visibility() != VISIBLE)
             continue;
                  
+        if (rule == CanCrossEditingBoundary && boundaryCrossed) {
+            lastVisible = currentPos;
+            break;
+        }
+        
         // track last visible streamer position
         if (isStreamer(currentPos))
             lastVisible = currentPos;
@@ -560,7 +590,7 @@ Position Position::upstream() const
 // and upstream() will return the left one.
 // Also, downstream() will return the last position in the last atomic node in boundary for all of the positions
 // in boundary after the last candidate, where endsOfNodeAreVisuallyDistinctPositions(boundary).
-Position Position::downstream() const
+Position Position::downstream(EditingBoundaryCrossingRule rule) const
 {
     Node* startNode = node();
     if (!startNode)
@@ -572,6 +602,7 @@ Position Position::downstream() const
     PositionIterator currentPos = lastVisible;
     bool startEditable = startNode->isContentEditable();
     Node* lastNode = startNode;
+    bool boundaryCrossed = false;
     for (; !currentPos.atEnd(); currentPos.increment()) {   
         Node* currentNode = currentPos.node();
         
@@ -580,8 +611,12 @@ Position Position::downstream() const
         if (currentNode != lastNode) {
             // Don't change editability.
             bool currentEditable = currentNode->isContentEditable();
-            if (startEditable != currentEditable)
-                break;
+            if (startEditable != currentEditable) {
+                if (rule == CannotCrossEditingBoundary)
+                    break;
+                boundaryCrossed = true;
+            }
+                
             lastNode = currentNode;
         }
 
@@ -604,6 +639,11 @@ Position Position::downstream() const
         if (!renderer || renderer->style()->visibility() != VISIBLE)
             continue;
             
+        if (rule == CanCrossEditingBoundary && boundaryCrossed) {
+            lastVisible = currentPos;
+            break;
+        }
+        
         // track last visible streamer position
         if (isStreamer(currentPos))
             lastVisible = currentPos;
@@ -704,10 +744,14 @@ bool Position::isCandidate() const
     if (isTableElement(node()) || editingIgnoresContent(node()))
         return (atFirstEditingPositionForNode() || atLastEditingPositionForNode()) && !nodeIsUserSelectNone(node()->parent());
 
-    if (!node()->hasTagName(htmlTag) && renderer->isBlockFlow() && !hasRenderedNonAnonymousDescendantsWithHeight(renderer) &&
-       (toRenderBox(renderer)->height() || node()->hasTagName(bodyTag)))
-        return atFirstEditingPositionForNode() && !nodeIsUserSelectNone(node());
-    
+    if (!m_anchorNode->hasTagName(htmlTag) && renderer->isBlockFlow()) {
+        if (toRenderBlock(renderer)->height() || m_anchorNode->hasTagName(bodyTag)) {
+            if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer))
+                return atFirstEditingPositionForNode() && !Position::nodeIsUserSelectNone(node());
+            return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(node()) && atEditingBoundary();
+        }
+    }
+
     return false;
 }
 
@@ -949,7 +993,22 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi
 {
     caretOffset = m_offset;
     RenderObject* renderer = node()->renderer();
+          
     if (!renderer->isText()) {
+        if (renderer->isBlockFlow() && hasRenderedNonAnonymousDescendantsWithHeight(renderer)) {
+            bool lastPosition = caretOffset == lastOffsetInNode(node());
+            Node* startNode = lastPosition ? node()->childNode(caretOffset - 1) : node()->childNode(caretOffset);
+            while (startNode && (!startNode->renderer() || (startNode->isTextNode() && toRenderText(startNode->renderer())->isAllCollapsibleWhitespace())))
+                startNode = (lastPosition)? startNode->previousSibling(): startNode->nextSibling();
+            if (startNode) {
+                Position pos(startNode, 0);
+                pos = pos.downstream(CanCrossEditingBoundary);
+                pos.getInlineBoxAndOffset(UPSTREAM, primaryDirection, inlineBox, caretOffset);
+                if (lastPosition && inlineBox)
+                    caretOffset = inlineBox->caretMaxOffset();
+                return;
+            }
+        }
         inlineBox = renderer->isBox() ? toRenderBox(renderer)->inlineBoxWrapper() : 0;
         if (!inlineBox || (caretOffset > inlineBox->caretMinOffset() && caretOffset < inlineBox->caretMaxOffset()))
             return;
diff --git a/WebCore/dom/Position.h b/WebCore/dom/Position.h
index c08872d..1e0304e 100644
--- a/WebCore/dom/Position.h
+++ b/WebCore/dom/Position.h
@@ -56,6 +56,11 @@ public:
         PositionIsBeforeAnchor
     };
 
+    enum EditingBoundaryCrossingRule {
+        CanCrossEditingBoundary,
+        CannotCrossEditingBoundary
+    };
+    
     Position()
         : m_offset(0)
         , m_anchorType(PositionIsOffsetInAnchor)
@@ -130,6 +135,9 @@ public:
     bool atFirstEditingPositionForNode() const;
     bool atLastEditingPositionForNode() const;
 
+    // Retuns true if the visually equivalent positions around have different editability
+    bool atEditingBoundary() const;
+    
     bool atStartOfTree() const;
     bool atEndOfTree() const;
 
@@ -139,8 +147,8 @@ public:
     Position trailingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace = false) const;
     
     // These return useful visually equivalent positions.
-    Position upstream() const;
-    Position downstream() const;
+    Position upstream(EditingBoundaryCrossingRule = CannotCrossEditingBoundary) const;
+    Position downstream(EditingBoundaryCrossingRule = CannotCrossEditingBoundary) const;
     
     bool isCandidate() const;
     bool inRenderedText() const;
diff --git a/WebCore/dom/PositionIterator.cpp b/WebCore/dom/PositionIterator.cpp
index 8d881ba..f5b65f5 100644
--- a/WebCore/dom/PositionIterator.cpp
+++ b/WebCore/dom/PositionIterator.cpp
@@ -156,10 +156,14 @@ bool PositionIterator::isCandidate() const
     if (isTableElement(m_anchorNode) || editingIgnoresContent(m_anchorNode))
         return (atStartOfNode() || atEndOfNode()) && !Position::nodeIsUserSelectNone(m_anchorNode->parent());
 
-    if (!m_anchorNode->hasTagName(htmlTag) && renderer->isBlockFlow() && !Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer) &&
-       (toRenderBlock(renderer)->height() || m_anchorNode->hasTagName(bodyTag)))
-        return atStartOfNode() && !Position::nodeIsUserSelectNone(m_anchorNode);
-    
+    if (!m_anchorNode->hasTagName(htmlTag) && renderer->isBlockFlow()) {
+        if (toRenderBlock(renderer)->height() || m_anchorNode->hasTagName(bodyTag)) {
+            if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer))
+                return atStartOfNode() && !Position::nodeIsUserSelectNone(m_anchorNode);
+            return m_anchorNode->isContentEditable() && !Position::nodeIsUserSelectNone(m_anchorNode) && Position(*this).atEditingBoundary();
+        }
+    }
+
     return false;
 }
 
diff --git a/WebCore/editing/htmlediting.cpp b/WebCore/editing/htmlediting.cpp
index 48a8e7c..8b1c98d 100644
--- a/WebCore/editing/htmlediting.cpp
+++ b/WebCore/editing/htmlediting.cpp
@@ -289,7 +289,7 @@ VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& positio
     while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot))
         p = isAtomicNode(p.node()) ? positionInParentAfterNode(p.node()) : nextVisuallyDistinctCandidate(p);
     
-    if (p.node() && !p.node()->isDescendantOf(highestRoot))
+    if (p.node() && p.node() != highestRoot && !p.node()->isDescendantOf(highestRoot))
         return VisiblePosition();
     
     return VisiblePosition(p);
@@ -310,7 +310,7 @@ VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& positio
     while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot))
         p = isAtomicNode(p.node()) ? positionInParentBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
     
-    if (p.node() && !p.node()->isDescendantOf(highestRoot))
+    if (p.node() && p.node() != highestRoot && !p.node()->isDescendantOf(highestRoot))
         return VisiblePosition();
     
     return VisiblePosition(p);
diff --git a/WebCore/rendering/RenderObject.cpp b/WebCore/rendering/RenderObject.cpp
index 1e8e19f..37ac0cf 100644
--- a/WebCore/rendering/RenderObject.cpp
+++ b/WebCore/rendering/RenderObject.cpp
@@ -2366,9 +2366,20 @@ RenderBoxModelObject* RenderObject::offsetParent() const
 
 VisiblePosition RenderObject::createVisiblePosition(int offset, EAffinity affinity)
 {
-    // If this is a non-anonymous renderer, then it's simple.
-    if (Node* node = this->node())
+    // If this is a non-anonymous renderer in an editable area, then it's simple.
+    if (Node* node = this->node()) {
+        if (!node->isContentEditable()) {
+            // If it can be found, we prefer a visually equivalent position that is editable. 
+            Position position(node, offset);
+            Position candidate = position.downstream(Position::CanCrossEditingBoundary);
+            if (candidate.node()->isContentEditable())
+                return VisiblePosition(candidate, affinity);
+            candidate = position.upstream(Position::CanCrossEditingBoundary);
+            if (candidate.node()->isContentEditable())
+                return VisiblePosition(candidate, affinity);
+        }
         return VisiblePosition(node, offset, affinity);
+    }
 
     // We don't want to cross the boundary between editable and non-editable
     // regions of the document, but that is either impossible or at least
diff --git a/WebCore/rendering/RenderText.cpp b/WebCore/rendering/RenderText.cpp
index a4f53a2..95aa277 100644
--- a/WebCore/rendering/RenderText.cpp
+++ b/WebCore/rendering/RenderText.cpp
@@ -757,6 +757,17 @@ void RenderText::calcPrefWidths(int leadWidth, HashSet<const SimpleFontData*>& f
     setPrefWidthsDirty(false);
 }
 
+bool RenderText::isAllCollapsibleWhitespace()
+{
+    int length = textLength();
+    const UChar* text = characters();
+    for (int i = 0; i < length; i++) {
+        if (!style()->isCollapsibleWhiteSpace(text[i]))
+            return false;
+    }
+    return true;
+}
+    
 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const
 {
     unsigned currPos;
diff --git a/WebCore/rendering/RenderText.h b/WebCore/rendering/RenderText.h
index 915ff40..d46bce9 100644
--- a/WebCore/rendering/RenderText.h
+++ b/WebCore/rendering/RenderText.h
@@ -121,7 +121,8 @@ public:
     void checkConsistency() const;
 
     virtual void calcPrefWidths(int leadWidth);
-
+    bool isAllCollapsibleWhitespace();
+    
 protected:
     virtual void styleWillChange(StyleDifference, const RenderStyle*) { }
     virtual void styleDidChange(StyleDifference, const RenderStyle* oldStyle);

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list