Commit 429c6e2b authored by enrica@apple.com's avatar enrica@apple.com
Browse files

WebCore: Can focus but not type into content editable block that contains only...

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
parent cb0075b0
2009-11-30 Enrica Casucci <enrica@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@apple.com>
 
Reviewed by Oliver Hunt.
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)
<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>
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)
<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>
#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.
<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>
......@@ -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
2009-11-30 Enrica Casucci <enrica@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@theolliviers.com>
 
wx build fix, add header needed for wx build.
......@@ -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
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
}
......
......@@ -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);
......
......@@ -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
......
......@@ -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;
......
......@@ -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);
......
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