Reviewed by levi
* editing/selection/editable-non-editable-crash-expected.checksum: Added.
* editing/selection/editable-non-editable-crash-expected.png: Added.
* editing/selection/editable-non-editable-crash-expected.txt: Added.
* editing/selection/editable-non-editable-crash.html: Added.
* editing/selection/skip-non-editable-1-expected.txt:
* editing/selection/skip-non-editable-1.html:
WebCore:
Reviewed by levi
Fix Mail ToDo crashers.
* dom/Range.cpp:
(WebCore::Range::compareBoundaryPoints): Added an ASSERT that both
containers are non-null and an early return.
* editing/Selection.cpp:
(WebCore::Selection::validate): Fix a dangling start/end.
(WebCore::Selection::adjustForEditableContent): Added an early return if
m_start or m_end are null.
(WebCore::Selection::isContentEditable): Use isRichlyEditablePosition.
(WebCore::Selection::isContentRichlyEditable): Ditto.
* editing/Selection.h:
* editing/VisiblePosition.cpp:
(WebCore::VisiblePosition::next): Use the new highestEditableRoot.
(WebCore::VisiblePosition::previous): Ditto.
* editing/htmlediting.cpp:
(WebCore::highestEditableRoot): Takes in a position.
(WebCore::isEditablePosition): Added.
(WebCore::isRichlyEditablePosition): Added.
(WebCore::rootEditableElement): Added.
(WebCore::nextCandidate): Moved and split out from nextVisiblePosition.
(WebCore::nextVisuallyDistinctCandidate): Ditto.
(WebCore::previousCandidate): Moved and split out from previousVisiblePosition.
(WebCore::previousVisuallyDistinctCandidate): Ditto.
(WebCore::firstEditablePositionAfterPositionInRoot): Iterate over positions,
using nextVisuallyDistinctCandidate, skipping atomic nodes that are non-editable.
(WebCore::lastEditablePositionBeforePositionInRoot): Ditto.
* editing/htmlediting.h:
* editing/visible_units.cpp:
(WebCore::startOfWord): Added a FIXME.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@15226
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2006-07-07 Justin Garcia <justin.garcia@apple.com>
+
+ Reviewed by levi
+
+ * editing/selection/editable-non-editable-crash-expected.checksum: Added.
+ * editing/selection/editable-non-editable-crash-expected.png: Added.
+ * editing/selection/editable-non-editable-crash-expected.txt: Added.
+ * editing/selection/editable-non-editable-crash.html: Added.
+ * editing/selection/skip-non-editable-1-expected.txt:
+ * editing/selection/skip-non-editable-1.html:
+
2006-07-07 Levi Weintraub <lweintraub@apple.com>
Reviewed by NOBODY
--- /dev/null
+ebd6d307301e73acdbf6f85451b4e575
\ No newline at end of file
--- /dev/null
+EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of DIV > BODY > HTML > #document to 1 of DIV > BODY > HTML > #document
+EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+ RenderBlock {HTML} at (0,0) size 800x600
+ RenderBody {BODY} at (8,8) size 784x584
+ RenderBlock {DIV} at (0,0) size 784x46
+ RenderTable {TABLE} at (0,0) size 784x46 [border: (1px solid #AAAAAA)]
+ RenderTableSection {TBODY} at (1,1) size 782x44
+ RenderTableRow {TR} at (0,2) size 782x40
+ RenderTableCell {TD} at (2,2) size 778x40 [border: (1px solid #AAAAAA)] [r=0 c=0 rs=1 cs=1]
+ RenderText {#text} at (2,2) size 773x36
+ text run at (2,2) width 773: "This tests for a Mail crasher that happened when a selection was created with one endpoint in non-editable content and the "
+ text run at (2,20) width 155: "other in editable content."
+caret: position 0 of child 0 {TABLE} of child 1 {DIV} of child 1 {BODY} of child 0 {HTML} of document
--- /dev/null
+<head><style>
+table, td {
+ border: 1px solid #aaa;
+}
+</style></head>
+<body>
+<div contenteditable="true"><table style="border: 1px solid #aaa" id="base"><tr><td id="extent" contenteditable="false">This tests for a Mail crasher that happened when a selection was created with one endpoint in non-editable content and the other in editable content.</td></tr></table></div>
+
+<script>
+var s = window.getSelection();
+var b = document.getElementById("base");
+var e = document.getElementById("extent");
+s.setBaseAndExtent(b, 0, e, 0);
+</script>
+</body>
\ No newline at end of file
EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of BODY > HTML > #document to 13 of BODY > HTML > #document
EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldEndEditingInDOMRange:range from 0 of BODY > HTML > #document to 13 of BODY > HTML > #document
EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification
EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of TD > TR > TBODY > TABLE > BODY > HTML > #document to 1 of TD > TR > TBODY > TABLE > BODY > HTML > #document
EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldEndEditingInDOMRange:range from 0 of TD > TR > TBODY > TABLE > BODY > HTML > #document to 1 of TD > TR > TBODY > TABLE > BODY > HTML > #document
EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification
EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of BODY > HTML > #document to 13 of BODY > HTML > #document
EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of TD > TR > TBODY > TABLE > BODY > HTML > #document to 1 of TD > TR > TBODY > TABLE > BODY > HTML > #document
EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldEndEditingInDOMRange:range from 0 of TD > TR > TBODY > TABLE > BODY > HTML > #document to 1 of TD > TR > TBODY > TABLE > BODY > HTML > #document
EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification
EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of BODY > HTML > #document to 13 of BODY > HTML > #document
EDITING DELEGATE: webViewDidBeginEditing:WebViewDidBeginEditingNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldEndEditingInDOMRange:range from 0 of BODY > HTML > #document to 13 of BODY > HTML > #document
EDITING DELEGATE: webViewDidEndEditing:WebViewDidEndEditingNotification
EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of TD > TR > TBODY > TABLE > BODY > HTML > #document to 1 of TD > TR > TBODY > TABLE > BODY > HTML > #document
s.setPosition(e1.firstChild, e1.firstChild.length);
s.modify("move", "forward", "character");
+s.modify("move", "forward", "character");
assert(s.anchorNode == e2.firstChild && s.anchorOffset == 0);
+s.modify("move", "backward", "character");
s.modify("move", "backward", "character");
assert(s.anchorNode == e1.firstChild && s.anchorOffset == e1.firstChild.length);
s.setPosition(e2.firstChild, e2.firstChild.length);
s.modify("move", "forward", "character");
+s.modify("move", "forward", "character");
assert(s.anchorNode == e3.firstChild && s.anchorOffset == 0);
+s.modify("move", "backward", "character");
s.modify("move", "backward", "character");
assert(s.anchorNode == e2.firstChild && s.anchorOffset == e2.firstChild.length)
</script>
\ No newline at end of file
+2006-07-07 Justin Garcia <justin.garcia@apple.com>
+
+ Reviewed by levi
+
+ Fix Mail ToDo crashers.
+
+ * dom/Range.cpp:
+ (WebCore::Range::compareBoundaryPoints): Added an ASSERT that both
+ containers are non-null and an early return.
+ * editing/Selection.cpp:
+ (WebCore::Selection::validate): Fix a dangling start/end.
+ (WebCore::Selection::adjustForEditableContent): Added an early return if
+ m_start or m_end are null.
+ (WebCore::Selection::isContentEditable): Use isRichlyEditablePosition.
+ (WebCore::Selection::isContentRichlyEditable): Ditto.
+ * editing/Selection.h:
+ * editing/VisiblePosition.cpp:
+ (WebCore::VisiblePosition::next): Use the new highestEditableRoot.
+ (WebCore::VisiblePosition::previous): Ditto.
+ * editing/htmlediting.cpp:
+ (WebCore::highestEditableRoot): Takes in a position.
+ (WebCore::isEditablePosition): Added.
+ (WebCore::isRichlyEditablePosition): Added.
+ (WebCore::rootEditableElement): Added.
+ (WebCore::nextCandidate): Moved and split out from nextVisiblePosition.
+ (WebCore::nextVisuallyDistinctCandidate): Ditto.
+ (WebCore::previousCandidate): Moved and split out from previousVisiblePosition.
+ (WebCore::previousVisuallyDistinctCandidate): Ditto.
+ (WebCore::firstEditablePositionAfterPositionInRoot): Iterate over positions,
+ using nextVisuallyDistinctCandidate, skipping atomic nodes that are non-editable.
+ (WebCore::lastEditablePositionBeforePositionInRoot): Ditto.
+ * editing/htmlediting.h:
+ * editing/visible_units.cpp:
+ (WebCore::startOfWord): Added a FIXME.
+
2006-07-07 Levi Weintraub <lweintraub@apple.com>
Reviewed by justin
FAE04190097596C9000540BE /* SVGImageLoader.h in Headers */ = {isa = PBXBuildFile; fileRef = FAE0418E097596C9000540BE /* SVGImageLoader.h */; };
/* End PBXBuildFile section */
+/* Begin PBXBuildStyle section */
+ D041FC250A5E04A700841F7F /* Development */ = {
+ isa = PBXBuildStyle;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ };
+ name = Development;
+ };
+ D041FC260A5E04A700841F7F /* Deployment */ = {
+ isa = PBXBuildStyle;
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ };
+ name = Deployment;
+ };
+/* End PBXBuildStyle section */
+
/* Begin PBXContainerItemProxy section */
DD041FF009D9E3250010AF2A /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 149C284308902B11008A9EFC /* Build configuration list for PBXProject "WebCore" */;
+ buildSettings = {
+ };
+ buildStyles = (
+ D041FC250A5E04A700841F7F /* Development */,
+ D041FC260A5E04A700841F7F /* Deployment */,
+ );
hasScannedForEncodings = 1;
knownRegions = (
English,
short Range::compareBoundaryPoints( Node *containerA, int offsetA, Node *containerB, int offsetB )
{
+ ASSERT(containerA && containerB);
+ if (!containerA)
+ return -1;
+ if (!containerB)
+ return 1;
// see DOM2 traversal & range section 2.5
// case 1: both points have the same container
if (m_extent.isNotNull() && !baseAndExtentEqual)
m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent();
- // Make sure we do not have a dangling start or end
+ // Make sure we do not have a dangling base or extent.
if (m_base.isNull() && m_extent.isNull())
m_baseIsFirst = true;
else if (m_base.isNull()) {
break;
}
+ // Make sure we do not have a dangling start or end.
+ if (m_start.isNull())
+ m_start = m_end;
+ if (m_end.isNull())
+ m_end = m_start;
+
adjustForEditableContent();
// adjust the state
void Selection::adjustForEditableContent()
{
- if (m_base.isNull())
+ if (m_base.isNull() || m_start.isNull() || m_end.isNull())
return;
Node *baseRoot = m_base.node()->rootEditableElement();
m_extent = m_baseIsFirst ? m_end : m_start;
}
+bool Selection::isContentEditable() const
+{
+ return isEditablePosition(start());
+}
+
+bool Selection::isContentRichlyEditable() const
+{
+ return isRichlyEditablePosition(start());
+}
+
+Element* Selection::rootEditableElement() const
+{
+ return editableRootForPosition(start());
+}
+
void Selection::debugPosition() const
{
if (!m_start.node())
PassRefPtr<Range> toRange() const;
- Element* rootEditableElement() const { return start().node() ? start().node()->rootEditableElement() : 0; }
- bool isContentEditable() const { return start().node() ? start().node()->isContentEditable() : false; }
- bool isContentRichlyEditable() const { return start().node() ? start().node()->isContentRichlyEditable() : false; }
+ Element* rootEditableElement() const;
+ bool isContentEditable() const;
+ bool isContentRichlyEditable() const;
void debugPosition() const;
if (!stayInEditableContent || next.isNull())
return next;
- Node* highestRoot = highestEditableRoot(deepEquivalent().node());
+ Node* highestRoot = highestEditableRoot(deepEquivalent());
if (!next.deepEquivalent().node()->isAncestor(highestRoot))
return VisiblePosition();
- if (highestEditableRoot(next.deepEquivalent().node()) == highestRoot)
+ if (highestEditableRoot(next.deepEquivalent()) == highestRoot)
return next;
return firstEditablePositionAfterPositionInRoot(next.deepEquivalent(), highestRoot);
if (!stayInEditableContent || prev.isNull())
return prev;
- Node* highestRoot = highestEditableRoot(deepEquivalent().node());
+ Node* highestRoot = highestEditableRoot(deepEquivalent());
if (!prev.deepEquivalent().node()->isAncestor(highestRoot))
return VisiblePosition();
- if (highestEditableRoot(prev.deepEquivalent().node()) == highestRoot)
+ if (highestEditableRoot(prev.deepEquivalent()) == highestRoot)
return prev;
return lastEditablePositionBeforePositionInRoot(prev.deepEquivalent(), highestRoot);
return Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB);
}
-Node* highestEditableRoot(Node* node)
+Node* highestEditableRoot(const Position& position)
{
+ Node* node = position.node();
if (!node)
return 0;
- Node* highestRoot = node->rootEditableElement();
+ Node* highestRoot = editableRootForPosition(position);
if (!highestRoot)
return 0;
return lowestRoot;
}
+bool isEditablePosition(const Position& p)
+{
+ Node* node = p.node();
+ if (!node)
+ return false;
+
+ if (node->renderer() && node->renderer()->isTable())
+ node = node->parentNode();
+
+ return node->isContentEditable();
+}
+
+bool isRichlyEditablePosition(const Position& p)
+{
+ Node* node = p.node();
+ if (!node)
+ return false;
+
+ if (node->renderer() && node->renderer()->isTable())
+ node = node->parentNode();
+
+ return node->isContentRichlyEditable();
+}
+
+Element* editableRootForPosition(const Position& p)
+{
+ Node* node = p.node();
+ if (!node)
+ return 0;
+
+ if (node->renderer() && node->renderer()->isTable())
+ node = node->parentNode();
+
+ return node->rootEditableElement();
+}
+
+Position nextCandidate(const Position& position)
+{
+ Position p = position;
+ while (!p.atEnd()) {
+ p = p.next(UsingComposedCharacters);
+ if (p.inRenderedContent())
+ return p;
+ }
+ return Position();
+}
+
+Position nextVisuallyDistinctCandidate(const Position& position)
+{
+ Position p = position;
+ Position downstreamStart = p.downstream();
+ while (!p.atEnd()) {
+ p = p.next(UsingComposedCharacters);
+ if (p.inRenderedContent() && p.downstream() != downstreamStart)
+ return p;
+ }
+ return Position();
+}
+
+Position previousCandidate(const Position& position)
+{
+ Position p = position;
+ while (!p.atStart()) {
+ p = p.previous(UsingComposedCharacters);
+ if (p.inRenderedContent())
+ return p;
+ }
+ return Position();
+}
+
+Position previousVisuallyDistinctCandidate(const Position& position)
+{
+ Position p = position;
+ Position downstreamStart = p.downstream();
+ while (!p.atStart()) {
+ p = p.previous(UsingComposedCharacters);
+ if (p.inRenderedContent() && p.downstream() != downstreamStart)
+ return p;
+ }
+ return Position();
+}
+
VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
{
if (comparePositions(position, Position(highestRoot, 0)) == -1)
return VisiblePosition(Position(highestRoot, 0));
- Node* node = position.node();
- Node* child = node->childNode(position.offset());
- node = child ? child : node->traverseNextSibling(highestRoot);
-
- while (node && !node->isContentEditable())
- node = node->traverseNextNode(highestRoot);
-
- if (!node)
- return VisiblePosition();
+ Position p = nextVisuallyDistinctCandidate(position);
+ while (p.isNotNull() && !isEditablePosition(p) && p.node()->isAncestor(highestRoot))
+ p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p);
- return VisiblePosition(Position(node, 0));
+ return VisiblePosition(p);
}
VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
{
if (comparePositions(position, Position(highestRoot, maxDeepOffset(highestRoot))) == 1)
return VisiblePosition(Position(highestRoot, maxDeepOffset(highestRoot)));
-
- Node* node = position.node();
- Node* child = node->firstChild() && position.offset() > 1 ? node->childNode(position.offset() - 1) : 0;
- node = child ? child : node->traversePreviousNode(highestRoot);
-
- while (node && !node->isContentEditable())
- node = node->traversePreviousNodePostOrder(highestRoot);
- if (!node)
- return VisiblePosition();
-
- return VisiblePosition(Position(node, maxDeepOffset(node)));
+ Position p = previousVisuallyDistinctCandidate(position);
+ while (p.isNotNull() && !isEditablePosition(p) && p.node()->isAncestor(highestRoot))
+ p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
+
+ return VisiblePosition(p);
}
// antidote for maxDeepOffset()
bool isAtomicNode(const Node*);
bool editingIgnoresContent(const Node*);
bool canHaveChildrenForEditing(const Node*);
-Node* highestEditableRoot(Node*);
+Node* highestEditableRoot(const Position&);
VisiblePosition firstEditablePositionAfterPositionInRoot(const Position&, Node*);
VisiblePosition lastEditablePositionBeforePositionInRoot(const Position&, Node*);
int comparePositions(const Position&, const Position&);
Node* lowestEditableAncestor(Node*);
+Position nextCandidate(const Position&);
+Position nextVisuallyDistinctCandidate(const Position&);
+Position previousCandidate(const Position&);
+Position previousVisuallyDistinctCandidate(const Position&);
+bool isEditablePosition(const Position&);
+bool isRichlyEditablePosition(const Position&);
+Element* editableRootForPosition(const Position&);
void rebalanceWhitespaceInTextNode(Node*, unsigned start, unsigned length);
const String& nonBreakingSpaceString();
VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
{
+ // FIXME: This returns a null VP for c at the start of the document
+ // and side == LeftWordIfOnBoundary
VisiblePosition p = c;
if (side == RightWordIfOnBoundary) {
// at paragraph end, the startofWord is the current position