summaryrefslogtreecommitdiffstats
path: root/WebCore/editing
diff options
context:
space:
mode:
authorFeng Qian <>2009-04-10 18:11:29 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-04-10 18:11:29 -0700
commit8f72e70a9fd78eec56623b3a62e68f16b7b27e28 (patch)
tree181bf9a400c30a1bf34ea6d72560e8d00111d549 /WebCore/editing
parent7ed56f225e0ade046e1c2178977f72b2d896f196 (diff)
downloadexternal_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.zip
external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.gz
external_webkit-8f72e70a9fd78eec56623b3a62e68f16b7b27e28.tar.bz2
AI 145796: Land the WebKit merge @r42026.
Automated import of CL 145796
Diffstat (limited to 'WebCore/editing')
-rw-r--r--WebCore/editing/ApplyStyleCommand.cpp127
-rw-r--r--WebCore/editing/BreakBlockquoteCommand.cpp18
-rw-r--r--WebCore/editing/CompositeEditCommand.cpp69
-rw-r--r--WebCore/editing/CompositeEditCommand.h2
-rw-r--r--WebCore/editing/CreateLinkCommand.cpp2
-rw-r--r--WebCore/editing/DeleteButtonController.cpp32
-rw-r--r--WebCore/editing/DeleteButtonController.h4
-rw-r--r--WebCore/editing/DeleteSelectionCommand.cpp61
-rw-r--r--WebCore/editing/DeleteSelectionCommand.h6
-rw-r--r--WebCore/editing/EditCommand.cpp12
-rw-r--r--WebCore/editing/EditCommand.h14
-rw-r--r--WebCore/editing/Editor.cpp486
-rw-r--r--WebCore/editing/Editor.h18
-rw-r--r--WebCore/editing/EditorCommand.cpp84
-rw-r--r--WebCore/editing/FormatBlockCommand.cpp4
-rw-r--r--WebCore/editing/IndentOutdentCommand.cpp18
-rw-r--r--WebCore/editing/InsertLineBreakCommand.cpp29
-rw-r--r--WebCore/editing/InsertListCommand.cpp18
-rw-r--r--WebCore/editing/InsertListCommand.h7
-rw-r--r--WebCore/editing/InsertParagraphSeparatorCommand.cpp103
-rw-r--r--WebCore/editing/InsertTextCommand.cpp16
-rw-r--r--WebCore/editing/ModifySelectionListLevel.cpp18
-rw-r--r--WebCore/editing/MoveSelectionCommand.cpp14
-rw-r--r--WebCore/editing/RemoveFormatCommand.cpp4
-rw-r--r--WebCore/editing/ReplaceSelectionCommand.cpp84
-rw-r--r--WebCore/editing/SelectionController.cpp67
-rw-r--r--WebCore/editing/SelectionController.h12
-rw-r--r--WebCore/editing/SmartReplaceCF.cpp1
-rw-r--r--WebCore/editing/SplitElementCommand.cpp2
-rw-r--r--WebCore/editing/TextIterator.cpp95
-rw-r--r--WebCore/editing/TextIterator.h19
-rw-r--r--WebCore/editing/TypingCommand.cpp124
-rw-r--r--WebCore/editing/TypingCommand.h3
-rw-r--r--WebCore/editing/VisiblePosition.cpp60
-rw-r--r--WebCore/editing/VisibleSelection.cpp (renamed from WebCore/editing/Selection.cpp)152
-rw-r--r--WebCore/editing/VisibleSelection.h (renamed from WebCore/editing/Selection.h)75
-rw-r--r--WebCore/editing/htmlediting.cpp107
-rw-r--r--WebCore/editing/htmlediting.h8
-rw-r--r--WebCore/editing/mac/SelectionControllerMac.mm49
-rw-r--r--WebCore/editing/markup.cpp117
-rw-r--r--WebCore/editing/qt/EditorQt.cpp2
-rw-r--r--WebCore/editing/visible_units.cpp207
42 files changed, 1482 insertions, 868 deletions
diff --git a/WebCore/editing/ApplyStyleCommand.cpp b/WebCore/editing/ApplyStyleCommand.cpp
index eca2999..d43cc81 100644
--- a/WebCore/editing/ApplyStyleCommand.cpp
+++ b/WebCore/editing/ApplyStyleCommand.cpp
@@ -32,6 +32,8 @@
#include "CSSPropertyNames.h"
#include "CSSValueKeywords.h"
#include "Document.h"
+#include "Editor.h"
+#include "Frame.h"
#include "HTMLElement.h"
#include "HTMLInterchange.h"
#include "HTMLNames.h"
@@ -50,12 +52,7 @@ using namespace HTMLNames;
class StyleChange {
public:
- enum ELegacyHTMLStyles { DoNotUseLegacyHTMLStyles, UseLegacyHTMLStyles };
-
- explicit StyleChange(CSSStyleDeclaration*, ELegacyHTMLStyles usesLegacyStyles=UseLegacyHTMLStyles);
- StyleChange(CSSStyleDeclaration*, const Position&, ELegacyHTMLStyles usesLegacyStyles=UseLegacyHTMLStyles);
-
- static ELegacyHTMLStyles styleModeForParseMode(bool);
+ explicit StyleChange(CSSStyleDeclaration*, const Position&);
String cssStyle() const { return m_cssStyle; }
bool applyBold() const { return m_applyBold; }
@@ -70,8 +67,6 @@ public:
String fontFace() { return m_applyFontFace; }
String fontSize() { return m_applyFontSize; }
- bool usesLegacyStyles() const { return m_usesLegacyStyles; }
-
private:
void init(PassRefPtr<CSSStyleDeclaration>, const Position&);
bool checkForLegacyHTMLStyleChange(const CSSProperty*);
@@ -85,33 +80,26 @@ private:
String m_applyFontColor;
String m_applyFontFace;
String m_applyFontSize;
- bool m_usesLegacyStyles;
};
-
-StyleChange::StyleChange(CSSStyleDeclaration* style, ELegacyHTMLStyles usesLegacyStyles)
+StyleChange::StyleChange(CSSStyleDeclaration* style, const Position& position)
: m_applyBold(false)
, m_applyItalic(false)
, m_applySubscript(false)
, m_applySuperscript(false)
- , m_usesLegacyStyles(usesLegacyStyles)
-{
- init(style, Position());
-}
-
-StyleChange::StyleChange(CSSStyleDeclaration* style, const Position& position, ELegacyHTMLStyles usesLegacyStyles)
- : m_applyBold(false)
- , m_applyItalic(false)
- , m_applySubscript(false)
- , m_applySuperscript(false)
- , m_usesLegacyStyles(usesLegacyStyles)
{
init(style, position);
}
-void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position &position)
+void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position& position)
{
+ Document* document = position.node() ? position.node()->document() : 0;
+ if (!document || !document->frame())
+ return;
+
+ bool useHTMLFormattingTags = !document->frame()->editor()->shouldStyleWithCSS();
+
RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable();
String styleText("");
@@ -131,7 +119,7 @@ void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position &po
continue;
// If needed, figure out if this change is a legacy HTML style change.
- if (m_usesLegacyStyles && checkForLegacyHTMLStyleChange(property))
+ if (useHTMLFormattingTags && checkForLegacyHTMLStyleChange(property))
continue;
if (property->id() == CSSPropertyDirection) {
@@ -160,11 +148,6 @@ void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position &po
m_cssStyle = styleText.stripWhiteSpace();
}
-StyleChange::ELegacyHTMLStyles StyleChange::styleModeForParseMode(bool isQuirksMode)
-{
- return isQuirksMode ? UseLegacyHTMLStyles : DoNotUseLegacyHTMLStyles;
-}
-
// This function is the mapping from CSS styles to styling tags (like font-weight: bold to <b>)
bool StyleChange::checkForLegacyHTMLStyleChange(const CSSProperty* property)
{
@@ -359,7 +342,7 @@ void ApplyStyleCommand::updateStartEnd(const Position& newStart, const Position&
if (!m_useEndingSelection && (newStart != m_start || newEnd != m_end))
m_useEndingSelection = true;
- setEndingSelection(Selection(newStart, newEnd, VP_DEFAULT_AFFINITY));
+ setEndingSelection(VisibleSelection(newStart, newEnd, VP_DEFAULT_AFFINITY));
m_start = newStart;
m_end = newEnd;
}
@@ -442,7 +425,7 @@ void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclaration *style)
VisiblePosition nextParagraphStart(endOfParagraph(paragraphStart).next());
VisiblePosition beyondEnd(endOfParagraph(visibleEnd).next());
while (paragraphStart.isNotNull() && paragraphStart != beyondEnd) {
- StyleChange styleChange(style, paragraphStart.deepEquivalent(), StyleChange::styleModeForParseMode(document()->inCompatMode()));
+ StyleChange styleChange(style, paragraphStart.deepEquivalent());
if (styleChange.cssStyle().length() > 0 || m_removeOnly) {
RefPtr<Node> block = enclosingBlock(paragraphStart.deepEquivalent().node());
RefPtr<Node> newBlock = moveParagraphContentsToNewBlockIfNecessary(paragraphStart.deepEquivalent());
@@ -537,7 +520,7 @@ void ApplyStyleCommand::applyRelativeFontStyleChange(CSSMutableStyleDeclaration
start = start.upstream(); // Move upstream to ensure we do not add redundant spans.
Node *startNode = start.node();
- if (startNode->isTextNode() && start.offset() >= caretMaxOffset(startNode)) // Move out of text node if range does not include its characters.
+ if (startNode->isTextNode() && start.m_offset >= caretMaxOffset(startNode)) // Move out of text node if range does not include its characters.
startNode = startNode->traverseNextNode();
// Store away font size before making any changes to the document.
@@ -885,17 +868,17 @@ void ApplyStyleCommand::applyInlineStyleToRange(CSSMutableStyleDeclaration* styl
bool rangeIsEmpty = false;
- if (start.offset() >= caretMaxOffset(start.node())) {
+ if (start.m_offset >= caretMaxOffset(start.node())) {
node = node->traverseNextNode();
Position newStart = Position(node, 0);
- if (Range::compareBoundaryPoints(end, newStart) < 0)
+ if (!node || Range::compareBoundaryPoints(end, newStart) < 0)
rangeIsEmpty = true;
}
if (!rangeIsEmpty) {
// pastEndNode is the node after the last fully selected node.
Node* pastEndNode = end.node();
- if (end.offset() >= caretMaxOffset(end.node()))
+ if (end.m_offset >= caretMaxOffset(end.node()))
pastEndNode = end.node()->traverseNextSibling();
// FIXME: Callers should perform this operation on a Range that includes the br
// if they want style applied to the empty line.
@@ -958,7 +941,8 @@ bool ApplyStyleCommand::isHTMLStyleNode(CSSMutableStyleDeclaration* style, HTMLE
for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
switch ((*it).id()) {
case CSSPropertyFontWeight:
- if (elem->hasLocalName(bTag))
+ // IE inserts "strong" tags for execCommand("bold"), so we remove them, even though they're not strictly presentational
+ if (elem->hasLocalName(bTag) || elem->hasLocalName(strongTag))
return true;
break;
case CSSPropertyVerticalAlign:
@@ -966,7 +950,8 @@ bool ApplyStyleCommand::isHTMLStyleNode(CSSMutableStyleDeclaration* style, HTMLE
return true;
break;
case CSSPropertyFontStyle:
- if (elem->hasLocalName(iTag))
+ // IE inserts "em" tags for execCommand("italic"), so we remove them, even though they're not strictly presentational
+ if (elem->hasLocalName(iTag) || elem->hasLocalName(emTag))
return true;
}
}
@@ -1145,7 +1130,7 @@ void ApplyStyleCommand::applyTextDecorationStyle(Node *node, CSSMutableStyleDecl
HTMLElement *element = static_cast<HTMLElement *>(node);
- StyleChange styleChange(style, Position(element, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
+ StyleChange styleChange(style, Position(element, 0));
if (styleChange.cssStyle().length() > 0) {
String cssText = styleChange.cssStyle();
CSSMutableStyleDeclaration *decl = element->inlineStyleDecl();
@@ -1234,13 +1219,13 @@ void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration>
Position s = start;
Position e = end;
- Node *node = start.node();
+ Node* node = start.node();
while (node) {
- Node *next = node->traverseNextNode();
+ Node* next = node->traverseNextNode();
if (node->isHTMLElement() && nodeFullySelected(node, start, end)) {
- HTMLElement *elem = static_cast<HTMLElement *>(node);
- Node *prev = elem->traversePreviousNodePostOrder();
- Node *next = elem->traverseNextNode();
+ HTMLElement* elem = static_cast<HTMLElement*>(node);
+ Node* prev = elem->traversePreviousNodePostOrder();
+ Node* next = elem->traverseNextNode();
if (m_styledInlineElement && elem->hasTagName(m_styledInlineElement->tagQName()))
removeNodePreservingChildren(elem);
if (isHTMLStyleNode(style.get(), elem))
@@ -1254,14 +1239,14 @@ void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration>
if (s.node() == elem) {
// Since elem must have been fully selected, and it is at the start
// of the selection, it is clear we can set the new s offset to 0.
- ASSERT(s.offset() <= caretMinOffset(s.node()));
+ ASSERT(s.m_offset <= caretMinOffset(s.node()));
s = Position(next, 0);
}
if (e.node() == elem) {
// Since elem must have been fully selected, and it is at the end
// of the selection, it is clear we can set the new e offset to
// the max range offset of prev.
- ASSERT(e.offset() >= maxRangeOffset(e.node()));
+ ASSERT(e.m_offset >= maxRangeOffset(e.node()));
e = Position(prev, maxRangeOffset(prev));
}
}
@@ -1282,7 +1267,7 @@ bool ApplyStyleCommand::nodeFullySelected(Node *node, const Position &start, con
ASSERT(node->isElementNode());
Position pos = Position(node, node->childNodeCount()).upstream();
- return Range::compareBoundaryPoints(node, 0, start.node(), start.offset()) >= 0 &&
+ return Range::compareBoundaryPoints(node, 0, start.node(), start.m_offset) >= 0 &&
Range::compareBoundaryPoints(pos, end) <= 0;
}
@@ -1293,7 +1278,7 @@ bool ApplyStyleCommand::nodeFullyUnselected(Node *node, const Position &start, c
Position pos = Position(node, node->childNodeCount()).upstream();
bool isFullyBeforeStart = Range::compareBoundaryPoints(pos, start) < 0;
- bool isFullyAfterEnd = Range::compareBoundaryPoints(node, 0, end.node(), end.offset()) > 0;
+ bool isFullyAfterEnd = Range::compareBoundaryPoints(node, 0, end.node(), end.m_offset) > 0;
return isFullyBeforeStart || isFullyAfterEnd;
}
@@ -1301,11 +1286,11 @@ bool ApplyStyleCommand::nodeFullyUnselected(Node *node, const Position &start, c
bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Position &end)
{
- if (start.node()->isTextNode() && start.offset() > caretMinOffset(start.node()) && start.offset() < caretMaxOffset(start.node())) {
- int endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
+ if (start.node()->isTextNode() && start.m_offset > caretMinOffset(start.node()) && start.m_offset < caretMaxOffset(start.node())) {
+ int endOffsetAdjustment = start.node() == end.node() ? start.m_offset : 0;
Text *text = static_cast<Text *>(start.node());
- splitTextNode(text, start.offset());
- updateStartEnd(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment));
+ splitTextNode(text, start.m_offset);
+ updateStartEnd(Position(start.node(), 0), Position(end.node(), end.m_offset - endOffsetAdjustment));
return true;
}
return false;
@@ -1313,15 +1298,15 @@ bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Po
bool ApplyStyleCommand::splitTextAtEndIfNeeded(const Position &start, const Position &end)
{
- if (end.node()->isTextNode() && end.offset() > caretMinOffset(end.node()) && end.offset() < caretMaxOffset(end.node())) {
+ if (end.node()->isTextNode() && end.m_offset > caretMinOffset(end.node()) && end.m_offset < caretMaxOffset(end.node())) {
Text *text = static_cast<Text *>(end.node());
- splitTextNode(text, end.offset());
+ splitTextNode(text, end.m_offset);
Node *prevNode = text->previousSibling();
ASSERT(prevNode);
Node *startNode = start.node() == end.node() ? prevNode : start.node();
ASSERT(startNode);
- updateStartEnd(Position(startNode, start.offset()), Position(prevNode, caretMaxOffset(prevNode)));
+ updateStartEnd(Position(startNode, start.m_offset), Position(prevNode, caretMaxOffset(prevNode)));
return true;
}
return false;
@@ -1329,12 +1314,12 @@ bool ApplyStyleCommand::splitTextAtEndIfNeeded(const Position &start, const Posi
bool ApplyStyleCommand::splitTextElementAtStartIfNeeded(const Position &start, const Position &end)
{
- if (start.node()->isTextNode() && start.offset() > caretMinOffset(start.node()) && start.offset() < caretMaxOffset(start.node())) {
- int endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
+ if (start.node()->isTextNode() && start.m_offset > caretMinOffset(start.node()) && start.m_offset < caretMaxOffset(start.node())) {
+ int endOffsetAdjustment = start.node() == end.node() ? start.m_offset : 0;
Text *text = static_cast<Text *>(start.node());
- splitTextNodeContainingElement(text, start.offset());
+ splitTextNodeContainingElement(text, start.m_offset);
- updateStartEnd(Position(start.node()->parentNode(), start.node()->nodeIndex()), Position(end.node(), end.offset() - endOffsetAdjustment));
+ updateStartEnd(Position(start.node()->parentNode(), start.node()->nodeIndex()), Position(end.node(), end.m_offset - endOffsetAdjustment));
return true;
}
return false;
@@ -1342,15 +1327,15 @@ bool ApplyStyleCommand::splitTextElementAtStartIfNeeded(const Position &start, c
bool ApplyStyleCommand::splitTextElementAtEndIfNeeded(const Position &start, const Position &end)
{
- if (end.node()->isTextNode() && end.offset() > caretMinOffset(end.node()) && end.offset() < caretMaxOffset(end.node())) {
+ if (end.node()->isTextNode() && end.m_offset > caretMinOffset(end.node()) && end.m_offset < caretMaxOffset(end.node())) {
Text *text = static_cast<Text *>(end.node());
- splitTextNodeContainingElement(text, end.offset());
+ splitTextNodeContainingElement(text, end.m_offset);
Node *prevNode = text->parent()->previousSibling()->lastChild();
ASSERT(prevNode);
Node *startNode = start.node() == end.node() ? prevNode : start.node();
ASSERT(startNode);
- updateStartEnd(Position(startNode, start.offset()), Position(prevNode->parent(), prevNode->nodeIndex() + 1));
+ updateStartEnd(Position(startNode, start.m_offset), Position(prevNode->parent(), prevNode->nodeIndex() + 1));
return true;
}
return false;
@@ -1394,10 +1379,10 @@ static bool areIdenticalElements(Node *first, Node *second)
bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start, const Position &end)
{
Node *startNode = start.node();
- int startOffset = start.offset();
+ int startOffset = start.m_offset;
if (isAtomicNode(start.node())) {
- if (start.offset() != 0)
+ if (start.m_offset != 0)
return false;
// note: prior siblings could be unrendered elements. it's silly to miss the
@@ -1426,7 +1411,7 @@ bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start,
int startOffsetAdjustment = startChild->nodeIndex();
int endOffsetAdjustment = startNode == end.node() ? startOffsetAdjustment : 0;
- updateStartEnd(Position(startNode, startOffsetAdjustment), Position(end.node(), end.offset() + endOffsetAdjustment));
+ updateStartEnd(Position(startNode, startOffsetAdjustment), Position(end.node(), end.m_offset + endOffsetAdjustment));
return true;
}
@@ -1436,7 +1421,7 @@ bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start,
bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const Position &end)
{
Node *endNode = end.node();
- int endOffset = end.offset();
+ int endOffset = end.m_offset;
if (isAtomicNode(endNode)) {
if (endOffset < caretMaxOffset(endNode))
@@ -1466,7 +1451,7 @@ bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const
ASSERT(startNode);
int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length();
- updateStartEnd(Position(startNode, start.offset()), Position(nextElement, endOffset));
+ updateStartEnd(Position(startNode, start.m_offset), Position(nextElement, endOffset));
return true;
}
@@ -1495,7 +1480,7 @@ void ApplyStyleCommand::surroundNodeRangeWithElement(Node* startNode, Node* endN
}
// FIXME: We should probably call updateStartEnd if the start or end was in the node
// range so that the endingSelection() is canonicalized. See the comments at the end of
- // Selection::validate().
+ // VisibleSelection::validate().
}
void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElement* block)
@@ -1544,7 +1529,7 @@ void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style
if (m_removeOnly)
return;
- StyleChange styleChange(style, Position(startNode, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
+ StyleChange styleChange(style, Position(startNode, 0));
//
// Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes.
@@ -1587,7 +1572,7 @@ void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style
surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), supTag));
if (m_styledInlineElement)
- surroundNodeRangeWithElement(startNode, endNode, m_styledInlineElement->cloneElement());
+ surroundNodeRangeWithElement(startNode, endNode, m_styledInlineElement->cloneElementWithoutChildren());
}
float ApplyStyleCommand::computedFontSize(const Node *node)
@@ -1622,9 +1607,9 @@ void ApplyStyleCommand::joinChildTextNodes(Node *node, const Position &start, co
Text *childText = static_cast<Text *>(child);
Text *nextText = static_cast<Text *>(next);
if (next == start.node())
- newStart = Position(childText, childText->length() + start.offset());
+ newStart = Position(childText, childText->length() + start.m_offset);
if (next == end.node())
- newEnd = Position(childText, childText->length() + end.offset());
+ newEnd = Position(childText, childText->length() + end.m_offset);
String textToMove = nextText->data();
insertTextIntoNode(childText, childText->length(), textToMove);
removeNode(next);
diff --git a/WebCore/editing/BreakBlockquoteCommand.cpp b/WebCore/editing/BreakBlockquoteCommand.cpp
index 76a0890..2a513a5 100644
--- a/WebCore/editing/BreakBlockquoteCommand.cpp
+++ b/WebCore/editing/BreakBlockquoteCommand.cpp
@@ -72,7 +72,7 @@ void BreakBlockquoteCommand::doApply()
insertNodeAfter(breakNode.get(), topBlockquote);
if (isLastVisiblePositionInNode(visiblePos, topBlockquote)) {
- setEndingSelection(Selection(Position(breakNode.get(), 0), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(Position(breakNode.get(), 0), DOWNSTREAM));
rebalanceWhitespace();
return;
}
@@ -85,19 +85,19 @@ void BreakBlockquoteCommand::doApply()
// Split at pos if in the middle of a text node.
if (startNode->isTextNode()) {
Text* textNode = static_cast<Text*>(startNode);
- if ((unsigned)pos.offset() >= textNode->length()) {
+ if ((unsigned)pos.m_offset >= textNode->length()) {
startNode = startNode->traverseNextNode();
ASSERT(startNode);
- } else if (pos.offset() > 0)
- splitTextNode(textNode, pos.offset());
- } else if (pos.offset() > 0) {
+ } else if (pos.m_offset > 0)
+ splitTextNode(textNode, pos.m_offset);
+ } else if (pos.m_offset > 0) {
startNode = startNode->traverseNextNode();
ASSERT(startNode);
}
// If there's nothing inside topBlockquote to move, we're finished.
if (!startNode->isDescendantOf(topBlockquote)) {
- setEndingSelection(Selection(VisiblePosition(Position(startNode, 0))));
+ setEndingSelection(VisibleSelection(VisiblePosition(Position(startNode, 0))));
return;
}
@@ -107,7 +107,7 @@ void BreakBlockquoteCommand::doApply()
ancestors.append(node);
// Insert a clone of the top blockquote after the break.
- RefPtr<Element> clonedBlockquote = topBlockquote->cloneElement();
+ RefPtr<Element> clonedBlockquote = topBlockquote->cloneElementWithoutChildren();
insertNodeAfter(clonedBlockquote.get(), breakNode.get());
// Clone startNode's ancestors into the cloned blockquote.
@@ -116,7 +116,7 @@ void BreakBlockquoteCommand::doApply()
// or clonedBlockquote if ancestors is empty).
RefPtr<Element> clonedAncestor = clonedBlockquote;
for (size_t i = ancestors.size(); i != 0; --i) {
- RefPtr<Element> clonedChild = ancestors[i - 1]->cloneElement(); // shallow clone
+ RefPtr<Element> clonedChild = ancestors[i - 1]->cloneElementWithoutChildren();
// Preserve list item numbering in cloned lists.
if (clonedChild->isElementNode() && clonedChild->hasTagName(olTag)) {
Node* listChildNode = i > 1 ? ancestors[i - 2] : startNode;
@@ -168,7 +168,7 @@ void BreakBlockquoteCommand::doApply()
addBlockPlaceholderIfNeeded(clonedBlockquote.get());
// Put the selection right before the break.
- setEndingSelection(Selection(Position(breakNode.get(), 0), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(Position(breakNode.get(), 0), DOWNSTREAM));
rebalanceWhitespace();
}
diff --git a/WebCore/editing/CompositeEditCommand.cpp b/WebCore/editing/CompositeEditCommand.cpp
index 335f77e..9052582 100644
--- a/WebCore/editing/CompositeEditCommand.cpp
+++ b/WebCore/editing/CompositeEditCommand.cpp
@@ -157,7 +157,7 @@ void CompositeEditCommand::insertNodeAt(PassRefPtr<Node> insertChild, const Posi
// likewise for replaced elements, brs, etc.
Position p = rangeCompliantEquivalent(editingPosition);
Node* refChild = p.node();
- int offset = p.offset();
+ int offset = p.m_offset;
if (canHaveChildrenForEditing(refChild)) {
Node* child = refChild->firstChild();
@@ -171,6 +171,10 @@ void CompositeEditCommand::insertNodeAt(PassRefPtr<Node> insertChild, const Posi
insertNodeBefore(insertChild, refChild);
else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) {
splitTextNode(static_cast<Text *>(refChild), offset);
+
+ // Mutation events (bug 22634) from the text node insertion may have removed the refChild
+ if (!refChild->inDocument())
+ return;
insertNodeBefore(insertChild, refChild);
} else
insertNodeAfter(insertChild, refChild);
@@ -296,7 +300,7 @@ void CompositeEditCommand::inputText(const String& text, bool selectInsertedText
if (selectInsertedText) {
RefPtr<Range> selectedRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), startIndex, length);
- setEndingSelection(Selection(selectedRange.get()));
+ setEndingSelection(VisibleSelection(selectedRange.get()));
}
}
@@ -323,13 +327,13 @@ Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
Node* tabSpan = tabSpanNode(pos.node());
- if (pos.offset() <= caretMinOffset(pos.node()))
+ if (pos.m_offset <= caretMinOffset(pos.node()))
return positionBeforeNode(tabSpan);
- if (pos.offset() >= caretMaxOffset(pos.node()))
+ if (pos.m_offset >= caretMaxOffset(pos.node()))
return positionAfterNode(tabSpan);
- splitTextNodeContainingElement(static_cast<Text *>(pos.node()), pos.offset());
+ splitTextNodeContainingElement(static_cast<Text *>(pos.node()), pos.m_offset);
return positionBeforeNode(tabSpan);
}
@@ -345,7 +349,7 @@ void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAft
applyCommandToComposite(DeleteSelectionCommand::create(document(), smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements));
}
-void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
+void CompositeEditCommand::deleteSelection(const VisibleSelection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
{
if (selection.isRange())
applyCommandToComposite(DeleteSelectionCommand::create(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements));
@@ -388,7 +392,7 @@ void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
String text = textNode->data();
ASSERT(!text.isEmpty());
- int offset = position.offset();
+ int offset = position.m_offset;
// If neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing.
if (!isWhitespace(text[offset])) {
offset--;
@@ -445,14 +449,14 @@ void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& positio
Position previous(previousVisiblePos.deepEquivalent());
if (isCollapsibleWhitespace(previousVisiblePos.characterAfter()) && previous.node()->isTextNode() && !previous.node()->hasTagName(brTag))
- replaceTextInNode(static_cast<Text*>(previous.node()), previous.offset(), 1, nonBreakingSpaceString());
+ replaceTextInNode(static_cast<Text*>(previous.node()), previous.m_offset, 1, nonBreakingSpaceString());
if (isCollapsibleWhitespace(visiblePos.characterAfter()) && position.node()->isTextNode() && !position.node()->hasTagName(brTag))
- replaceTextInNode(static_cast<Text*>(position.node()), position.offset(), 1, nonBreakingSpaceString());
+ replaceTextInNode(static_cast<Text*>(position.node()), position.m_offset, 1, nonBreakingSpaceString());
}
void CompositeEditCommand::rebalanceWhitespace()
{
- Selection selection = endingSelection();
+ VisibleSelection selection = endingSelection();
if (selection.isNone())
return;
@@ -538,8 +542,8 @@ void CompositeEditCommand::deleteInsignificantText(const Position& start, const
next = node->traverseNextNode();
if (node->isTextNode()) {
Text* textNode = static_cast<Text*>(node);
- int startOffset = node == start.node() ? start.offset() : 0;
- int endOffset = node == end.node() ? end.offset() : textNode->length();
+ int startOffset = node == start.node() ? start.m_offset : 0;
+ int endOffset = node == end.node() ? end.m_offset : textNode->length();
deleteInsignificantText(textNode, startOffset, endOffset);
}
if (node == end.node())
@@ -592,7 +596,7 @@ PassRefPtr<Node> CompositeEditCommand::addBlockPlaceholderIfNeeded(Element* cont
// append the placeholder to make sure it follows
// any unrendered blocks
- RenderBlock* block = static_cast<RenderBlock*>(renderer);
+ RenderBlock* block = toRenderBlock(renderer);
if (block->height() == 0 || (block->isListItem() && block->isEmpty()))
return appendBlockPlaceholder(container);
@@ -612,10 +616,10 @@ void CompositeEditCommand::removePlaceholderAt(const VisiblePosition& visiblePos
// the start of a paragraph will render it superfluous.
// FIXME: This doesn't remove placeholders at the end of anonymous blocks.
if (isEndOfBlock(visiblePosition) && isStartOfParagraph(visiblePosition)) {
- if (p.node()->hasTagName(brTag) && p.offset() == 0)
+ if (p.node()->hasTagName(brTag) && p.m_offset == 0)
removeNode(p.node());
else if (lineBreakExistsAtPosition(visiblePosition))
- deleteTextFromNode(static_cast<Text*>(p.node()), p.offset(), 1);
+ deleteTextFromNode(static_cast<Text*>(p.node()), p.m_offset, 1);
}
}
@@ -655,11 +659,10 @@ PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessar
// Perform some checks to see if we need to perform work in this function.
if (isBlock(upstreamStart.node())) {
- // If the block is the body element, always move content to a new block, so that
- // we avoid adding styles to the body element, since Mail's Make Plain Text feature
- // can't handle those.
- if (upstreamStart.node()->hasTagName(bodyTag)) {
- // If the block is the body element and there is nothing insde of it, create a new
+ // If the block is the root editable element, always move content to a new block,
+ // since it is illegal to modify attributes on the root editable element for editing.
+ if (upstreamStart.node() == editableRootForPosition(upstreamStart)) {
+ // If the block is the root editable element and there is nothing insde of it, create a new
// block but don't try and move content into it, since there's nothing to move.
if (upstreamStart == upstreamEnd)
return insertNewDefaultParagraphElementAt(upstreamStart);
@@ -696,7 +699,7 @@ void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
ASSERT(anchorNode->isLink());
- setEndingSelection(Selection::selectionFromContentsOfNode(anchorNode));
+ setEndingSelection(VisibleSelection::selectionFromContentsOfNode(anchorNode));
applyStyledElement(static_cast<Element*>(anchorNode));
// Clones of anchorNode have been pushed down, now remove it.
if (anchorNode->inDocument())
@@ -709,7 +712,7 @@ void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
// Anchors cannot be nested.
void CompositeEditCommand::pushPartiallySelectedAnchorElementsDown()
{
- Selection originalSelection = endingSelection();
+ VisibleSelection originalSelection = endingSelection();
VisiblePosition visibleStart(originalSelection.start());
VisiblePosition visibleEnd(originalSelection.end());
@@ -779,7 +782,7 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
// start and end can't be used directly to create a Range; they are "editing positions"
Position startRangeCompliant = rangeCompliantEquivalent(start);
Position endRangeCompliant = rangeCompliantEquivalent(end);
- RefPtr<Range> range = Range::create(document(), startRangeCompliant.node(), startRangeCompliant.offset(), endRangeCompliant.node(), endRangeCompliant.offset());
+ RefPtr<Range> range = Range::create(document(), startRangeCompliant.node(), startRangeCompliant.m_offset, endRangeCompliant.node(), endRangeCompliant.m_offset);
// FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move. It
// shouldn't matter though, since moved paragraphs will usually be quite small.
@@ -797,7 +800,7 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
// FIXME (5098931): We should add a new insert action "WebViewInsertActionMoved" and call shouldInsertFragment here.
- setEndingSelection(Selection(start, end, DOWNSTREAM));
+ setEndingSelection(VisibleSelection(start, end, DOWNSTREAM));
deleteSelection(false, false, false, false);
ASSERT(destination.deepEquivalent().node()->inDocument());
@@ -828,7 +831,7 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
if (textNode->length() == 1)
removeNodeAndPruneAncestors(node);
else
- deleteTextFromNode(textNode, position.offset(), 1);
+ deleteTextFromNode(textNode, position.m_offset, 1);
}
}
@@ -866,7 +869,7 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
RefPtr<Range> start = TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + startIndex, 0, true);
RefPtr<Range> end = TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + endIndex, 0, true);
if (start && end)
- setEndingSelection(Selection(start->startPosition(), end->startPosition(), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(start->startPosition(), end->startPosition(), DOWNSTREAM));
}
}
@@ -897,7 +900,7 @@ bool CompositeEditCommand::breakOutOfEmptyListItem()
}
appendBlockPlaceholder(newBlock);
- setEndingSelection(Selection(Position(newBlock.get(), 0), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(Position(newBlock.get(), 0), DOWNSTREAM));
computedStyle(endingSelection().start().node())->diff(style.get());
if (style->length() > 0)
@@ -935,7 +938,7 @@ bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph()
// a second one.
if (!isStartOfParagraph(atBR))
insertNodeBefore(createBreakElement(document()), br);
- setEndingSelection(Selection(atBR));
+ setEndingSelection(VisibleSelection(atBR));
// If this is an empty paragraph there must be a line break here.
if (!lineBreakExistsAtPosition(caret))
@@ -950,7 +953,7 @@ bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph()
removeNode(caretPos.node());
prune(beforeBR.node());
} else {
- ASSERT(caretPos.offset() == 0);
+ ASSERT(caretPos.m_offset == 0);
Text* textNode = static_cast<Text*>(caretPos.node());
Node* parentNode = textNode->parentNode();
// The preserved newline must be the first thing in the node, since otherwise the previous
@@ -976,8 +979,8 @@ Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Posi
Position result = original;
// Don't avoid block level anchors, because that would insert content into the wrong paragraph.
if (enclosingAnchor && !isBlock(enclosingAnchor)) {
- VisiblePosition firstInAnchor(Position(enclosingAnchor, 0));
- VisiblePosition lastInAnchor(Position(enclosingAnchor, maxDeepOffset(enclosingAnchor)));
+ VisiblePosition firstInAnchor(firstDeepEditingPositionForNode(enclosingAnchor));
+ VisiblePosition lastInAnchor(lastDeepEditingPositionForNode(enclosingAnchor));
// If visually just after the anchor, insert *inside* the anchor unless it's the last
// VisiblePosition in the document, to match NSTextView.
if (visiblePos == lastInAnchor) {
@@ -1034,9 +1037,7 @@ PassRefPtr<Node> CompositeEditCommand::splitTreeToNode(Node* start, Node* end, b
PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
{
- ExceptionCode ec = 0;
- RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec);
- ASSERT(ec == 0);
+ RefPtr<Element> breakNode = document->createElement(brTag, false);
return breakNode.release();
}
diff --git a/WebCore/editing/CompositeEditCommand.h b/WebCore/editing/CompositeEditCommand.h
index 6bb142b..4a3defd 100644
--- a/WebCore/editing/CompositeEditCommand.h
+++ b/WebCore/editing/CompositeEditCommand.h
@@ -52,7 +52,7 @@ protected:
void applyStyledElement(PassRefPtr<Element>);
void removeStyledElement(PassRefPtr<Element>);
void deleteSelection(bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = true);
- void deleteSelection(const Selection&, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = true);
+ void deleteSelection(const VisibleSelection&, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = true);
virtual void deleteTextFromNode(PassRefPtr<Text>, unsigned offset, unsigned count);
void inputText(const String&, bool selectInsertedText = false);
void insertNodeAfter(PassRefPtr<Node>, PassRefPtr<Node> refChild);
diff --git a/WebCore/editing/CreateLinkCommand.cpp b/WebCore/editing/CreateLinkCommand.cpp
index c5d68dd..263feab 100644
--- a/WebCore/editing/CreateLinkCommand.cpp
+++ b/WebCore/editing/CreateLinkCommand.cpp
@@ -53,7 +53,7 @@ void CreateLinkCommand::doApply()
insertNodeAt(anchorElement.get(), endingSelection().start());
RefPtr<Text> textNode = new Text(document(), m_url);
appendNode(textNode.get(), anchorElement.get());
- setEndingSelection(Selection(positionBeforeNode(anchorElement.get()), positionAfterNode(anchorElement.get()), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(positionBeforeNode(anchorElement.get()), positionAfterNode(anchorElement.get()), DOWNSTREAM));
}
}
diff --git a/WebCore/editing/DeleteButtonController.cpp b/WebCore/editing/DeleteButtonController.cpp
index 0516e0c..c0775e3 100644
--- a/WebCore/editing/DeleteButtonController.cpp
+++ b/WebCore/editing/DeleteButtonController.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -101,12 +101,12 @@ static bool isDeletableElement(const Node* node)
return false;
}
-static HTMLElement* enclosingDeletableElement(const Selection& selection)
+static HTMLElement* enclosingDeletableElement(const VisibleSelection& selection)
{
if (!selection.isContentEditable())
return 0;
- RefPtr<Range> range = selection.toRange();
+ RefPtr<Range> range = selection.toNormalizedRange();
if (!range)
return 0;
@@ -128,7 +128,7 @@ static HTMLElement* enclosingDeletableElement(const Selection& selection)
return static_cast<HTMLElement*>(element);
}
-void DeleteButtonController::respondToChangedSelection(const Selection& oldSelection)
+void DeleteButtonController::respondToChangedSelection(const VisibleSelection& oldSelection)
{
if (!enabled())
return;
@@ -154,6 +154,13 @@ void DeleteButtonController::createDeletionUI()
style->setProperty(CSSPropertyWebkitUserDrag, CSSValueNone);
style->setProperty(CSSPropertyWebkitUserSelect, CSSValueNone);
style->setProperty(CSSPropertyWebkitUserModify, CSSValueNone);
+ style->setProperty(CSSPropertyVisibility, CSSValueHidden);
+ style->setProperty(CSSPropertyPosition, CSSValueAbsolute);
+ style->setProperty(CSSPropertyCursor, CSSValueDefault);
+ style->setProperty(CSSPropertyTop, "0");
+ style->setProperty(CSSPropertyRight, "0");
+ style->setProperty(CSSPropertyBottom, "0");
+ style->setProperty(CSSPropertyLeft, "0");
RefPtr<HTMLDivElement> outline = new HTMLDivElement(divTag, m_target->document());
outline->setId(outlineElementIdentifier);
@@ -163,10 +170,6 @@ void DeleteButtonController::createDeletionUI()
style = outline->getInlineStyleDecl();
style->setProperty(CSSPropertyPosition, CSSValueAbsolute);
- style->setProperty(CSSPropertyCursor, CSSValueDefault);
- style->setProperty(CSSPropertyWebkitUserDrag, CSSValueNone);
- style->setProperty(CSSPropertyWebkitUserSelect, CSSValueNone);
- style->setProperty(CSSPropertyWebkitUserModify, CSSValueNone);
style->setProperty(CSSPropertyZIndex, String::number(-1000000));
style->setProperty(CSSPropertyTop, String::number(-borderWidth - m_target->renderBox()->borderTop()) + "px");
style->setProperty(CSSPropertyRight, String::number(-borderWidth - m_target->renderBox()->borderRight()) + "px");
@@ -174,6 +177,7 @@ void DeleteButtonController::createDeletionUI()
style->setProperty(CSSPropertyLeft, String::number(-borderWidth - m_target->renderBox()->borderLeft()) + "px");
style->setProperty(CSSPropertyBorder, String::number(borderWidth) + "px solid rgba(0, 0, 0, 0.6)");
style->setProperty(CSSPropertyWebkitBorderRadius, String::number(borderRadius) + "px");
+ style->setProperty(CSSPropertyVisibility, CSSValueVisible);
ExceptionCode ec = 0;
container->appendChild(outline.get(), ec);
@@ -190,15 +194,12 @@ void DeleteButtonController::createDeletionUI()
style = button->getInlineStyleDecl();
style->setProperty(CSSPropertyPosition, CSSValueAbsolute);
- style->setProperty(CSSPropertyCursor, CSSValueDefault);
- style->setProperty(CSSPropertyWebkitUserDrag, CSSValueNone);
- style->setProperty(CSSPropertyWebkitUserSelect, CSSValueNone);
- style->setProperty(CSSPropertyWebkitUserModify, CSSValueNone);
style->setProperty(CSSPropertyZIndex, String::number(1000000));
style->setProperty(CSSPropertyTop, String::number((-buttonHeight / 2) - m_target->renderBox()->borderTop() - (borderWidth / 2) + buttonBottomShadowOffset) + "px");
style->setProperty(CSSPropertyLeft, String::number((-buttonWidth / 2) - m_target->renderBox()->borderLeft() - (borderWidth / 2)) + "px");
style->setProperty(CSSPropertyWidth, String::number(buttonWidth) + "px");
style->setProperty(CSSPropertyHeight, String::number(buttonHeight) + "px");
+ style->setProperty(CSSPropertyVisibility, CSSValueVisible);
RefPtr<Image> buttonImage = Image::loadPlatformResource("deleteButton");
if (buttonImage->isNull())
@@ -283,8 +284,13 @@ void DeleteButtonController::enable()
ASSERT(m_disableStack > 0);
if (m_disableStack > 0)
m_disableStack--;
- if (enabled())
+ if (enabled()) {
+ // Determining if the element is deletable currently depends on style
+ // because whether something is editable depends on style, so we need
+ // to recalculate style before calling enclosingDeletableElement.
+ m_frame->document()->updateRendering();
show(enclosingDeletableElement(m_frame->selection()->selection()));
+ }
}
void DeleteButtonController::disable()
diff --git a/WebCore/editing/DeleteButtonController.h b/WebCore/editing/DeleteButtonController.h
index ab2d0b0..713ae8b 100644
--- a/WebCore/editing/DeleteButtonController.h
+++ b/WebCore/editing/DeleteButtonController.h
@@ -34,7 +34,7 @@ class DeleteButton;
class Frame;
class HTMLElement;
class RenderObject;
-class Selection;
+class VisibleSelection;
class DeleteButtonController {
public:
@@ -45,7 +45,7 @@ public:
HTMLElement* target() const { return m_target.get(); }
HTMLElement* containerElement() const { return m_containerElement.get(); }
- void respondToChangedSelection(const Selection& oldSelection);
+ void respondToChangedSelection(const VisibleSelection& oldSelection);
void show(HTMLElement*);
void hide();
diff --git a/WebCore/editing/DeleteSelectionCommand.cpp b/WebCore/editing/DeleteSelectionCommand.cpp
index 21b597e..09288ee 100644
--- a/WebCore/editing/DeleteSelectionCommand.cpp
+++ b/WebCore/editing/DeleteSelectionCommand.cpp
@@ -56,9 +56,7 @@ static bool isTableRow(const Node* node)
static bool isTableCellEmpty(Node* cell)
{
ASSERT(isTableCell(cell));
- VisiblePosition firstInCell(Position(cell, 0));
- VisiblePosition lastInCell(Position(cell, maxDeepOffset(cell)));
- return firstInCell == lastInCell;
+ return VisiblePosition(firstDeepEditingPositionForNode(cell)) == VisiblePosition(lastDeepEditingPositionForNode(cell));
}
static bool isTableRowEmpty(Node* row)
@@ -88,7 +86,7 @@ DeleteSelectionCommand::DeleteSelectionCommand(Document *document, bool smartDel
{
}
-DeleteSelectionCommand::DeleteSelectionCommand(const Selection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
+DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
: CompositeEditCommand(selection.start().node()->document()),
m_hasSelectionToDelete(true),
m_smartDelete(smartDelete),
@@ -312,8 +310,8 @@ static void updatePositionForNodeRemoval(Node* node, Position& position)
{
if (position.isNull())
return;
- if (node->parent() == position.node() && node->nodeIndex() < (unsigned)position.offset())
- position = Position(position.node(), position.offset() - 1);
+ if (node->parent() == position.node() && node->nodeIndex() < (unsigned)position.m_offset)
+ position = Position(position.node(), position.m_offset - 1);
if (position.node() == node || position.node()->isDescendantOf(node))
position = positionBeforeNode(node);
}
@@ -363,9 +361,9 @@ void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node)
return;
}
- if (node == m_startBlock && !isEndOfBlock(VisiblePosition(m_startBlock.get(), 0, DOWNSTREAM).previous()))
+ if (node == m_startBlock && !isEndOfBlock(VisiblePosition(firstDeepEditingPositionForNode(m_startBlock.get())).previous()))
m_needPlaceholder = true;
- else if (node == m_endBlock && !isStartOfBlock(VisiblePosition(m_endBlock.get(), maxDeepOffset(m_endBlock.get()), DOWNSTREAM).next()))
+ else if (node == m_endBlock && !isStartOfBlock(VisiblePosition(lastDeepEditingPositionForNode(m_startBlock.get())).next()))
m_needPlaceholder = true;
// FIXME: Update the endpoints of the range being deleted.
@@ -379,9 +377,9 @@ void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node)
static void updatePositionForTextRemoval(Node* node, int offset, int count, Position& position)
{
if (position.node() == node) {
- if (position.offset() > offset + count)
- position = Position(position.node(), position.offset() - count);
- else if (position.offset() > offset)
+ if (position.m_offset > offset + count)
+ position = Position(position.node(), position.m_offset - count);
+ else if (position.m_offset > offset)
position = Position(position.node(), offset);
}
}
@@ -398,7 +396,7 @@ void DeleteSelectionCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned
void DeleteSelectionCommand::handleGeneralDelete()
{
- int startOffset = m_upstreamStart.offset();
+ int startOffset = m_upstreamStart.m_offset;
Node* startNode = m_upstreamStart.node();
// Never remove the start block unless it's a table, in which case we won't merge content in.
@@ -413,7 +411,7 @@ void DeleteSelectionCommand::handleGeneralDelete()
deleteTextFromNode(text, caretMaxOffset(startNode), text->length() - caretMaxOffset(startNode));
}
- if (startOffset >= maxDeepOffset(startNode)) {
+ if (startOffset >= lastOffsetForEditing(startNode)) {
startNode = startNode->traverseNextSibling();
startOffset = 0;
}
@@ -424,17 +422,16 @@ void DeleteSelectionCommand::handleGeneralDelete()
if (startNode == m_downstreamEnd.node()) {
// The selection to delete is all in one node.
- if (!startNode->renderer() ||
- (startOffset == 0 && m_downstreamEnd.offset() >= maxDeepOffset(startNode))) {
+ if (!startNode->renderer() || (startOffset == 0 && m_downstreamEnd.atLastEditingPositionForNode())) {
// just delete
removeNode(startNode);
- } else if (m_downstreamEnd.offset() - startOffset > 0) {
+ } else if (m_downstreamEnd.m_offset - startOffset > 0) {
if (startNode->isTextNode()) {
// in a text node that needs to be trimmed
- Text *text = static_cast<Text *>(startNode);
- deleteTextFromNode(text, startOffset, m_downstreamEnd.offset() - startOffset);
+ Text* text = static_cast<Text*>(startNode);
+ deleteTextFromNode(text, startOffset, m_downstreamEnd.m_offset - startOffset);
} else {
- removeChildrenInRange(startNode, startOffset, m_downstreamEnd.offset());
+ removeChildrenInRange(startNode, startOffset, m_downstreamEnd.m_offset);
m_endingPosition = m_upstreamStart;
}
}
@@ -465,14 +462,14 @@ void DeleteSelectionCommand::handleGeneralDelete()
// if we just removed a node from the end container, update end position so the
// check above will work
if (node->parentNode() == m_downstreamEnd.node()) {
- ASSERT(node->nodeIndex() < (unsigned)m_downstreamEnd.offset());
- m_downstreamEnd = Position(m_downstreamEnd.node(), m_downstreamEnd.offset() - 1);
+ ASSERT(node->nodeIndex() < (unsigned)m_downstreamEnd.m_offset);
+ m_downstreamEnd = Position(m_downstreamEnd.node(), m_downstreamEnd.m_offset - 1);
}
removeNode(node.get());
node = nextNode.get();
} else {
Node* n = node->lastDescendant();
- if (m_downstreamEnd.node() == n && m_downstreamEnd.offset() >= caretMaxOffset(n)) {
+ if (m_downstreamEnd.node() == n && m_downstreamEnd.m_offset >= caretMaxOffset(n)) {
removeNode(node.get());
node = 0;
} else
@@ -480,16 +477,16 @@ void DeleteSelectionCommand::handleGeneralDelete()
}
}
- if (m_downstreamEnd.node() != startNode && !m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node()) && m_downstreamEnd.node()->inDocument() && m_downstreamEnd.offset() >= caretMinOffset(m_downstreamEnd.node())) {
- if (m_downstreamEnd.offset() >= maxDeepOffset(m_downstreamEnd.node()) && !canHaveChildrenForEditing(m_downstreamEnd.node())) {
+ if (m_downstreamEnd.node() != startNode && !m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node()) && m_downstreamEnd.node()->inDocument() && m_downstreamEnd.m_offset >= caretMinOffset(m_downstreamEnd.node())) {
+ if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildrenForEditing(m_downstreamEnd.node())) {
// The node itself is fully selected, not just its contents. Delete it.
removeNode(m_downstreamEnd.node());
} else {
if (m_downstreamEnd.node()->isTextNode()) {
// in a text node that needs to be trimmed
Text *text = static_cast<Text *>(m_downstreamEnd.node());
- if (m_downstreamEnd.offset() > 0) {
- deleteTextFromNode(text, 0, m_downstreamEnd.offset());
+ if (m_downstreamEnd.m_offset > 0) {
+ deleteTextFromNode(text, 0, m_downstreamEnd.m_offset);
m_downstreamEnd = Position(text, 0);
}
// Remove children of m_downstreamEnd.node() that come after m_upstreamStart.
@@ -507,7 +504,7 @@ void DeleteSelectionCommand::handleGeneralDelete()
if (n)
offset = n->nodeIndex() + 1;
}
- removeChildrenInRange(m_downstreamEnd.node(), offset, m_downstreamEnd.offset());
+ removeChildrenInRange(m_downstreamEnd.node(), offset, m_downstreamEnd.m_offset);
m_downstreamEnd = Position(m_downstreamEnd.node(), offset);
}
}
@@ -522,12 +519,12 @@ void DeleteSelectionCommand::fixupWhitespace()
if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter()) {
Text* textNode = static_cast<Text*>(m_leadingWhitespace.node());
ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
- replaceTextInNode(textNode, m_leadingWhitespace.offset(), 1, nonBreakingSpaceString());
+ replaceTextInNode(textNode, m_leadingWhitespace.m_offset, 1, nonBreakingSpaceString());
}
if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter()) {
Text* textNode = static_cast<Text*>(m_trailingWhitespace.node());
ASSERT(!textNode->renderer() ||textNode->renderer()->style()->collapseWhiteSpace());
- replaceTextInNode(textNode, m_trailingWhitespace.offset(), 1, nonBreakingSpaceString());
+ replaceTextInNode(textNode, m_trailingWhitespace.m_offset, 1, nonBreakingSpaceString());
}
}
@@ -696,7 +693,7 @@ void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
void DeleteSelectionCommand::clearTransientState()
{
- m_selectionToDelete = Selection();
+ m_selectionToDelete = VisibleSelection();
m_upstreamStart.clear();
m_downstreamStart.clear();
m_upstreamEnd.clear();
@@ -755,7 +752,7 @@ void DeleteSelectionCommand::doApply()
// want to replace it with a placeholder BR!
if (handleSpecialCaseBRDelete()) {
calculateTypingStyleAfterDelete();
- setEndingSelection(Selection(m_endingPosition, affinity));
+ setEndingSelection(VisibleSelection(m_endingPosition, affinity));
clearTransientState();
rebalanceWhitespace();
return;
@@ -778,7 +775,7 @@ void DeleteSelectionCommand::doApply()
calculateTypingStyleAfterDelete();
- setEndingSelection(Selection(m_endingPosition, affinity));
+ setEndingSelection(VisibleSelection(m_endingPosition, affinity));
clearTransientState();
}
diff --git a/WebCore/editing/DeleteSelectionCommand.h b/WebCore/editing/DeleteSelectionCommand.h
index 0f9f2f7..640c549 100644
--- a/WebCore/editing/DeleteSelectionCommand.h
+++ b/WebCore/editing/DeleteSelectionCommand.h
@@ -36,14 +36,14 @@ public:
{
return adoptRef(new DeleteSelectionCommand(document, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements));
}
- static PassRefPtr<DeleteSelectionCommand> create(const Selection& selection, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false)
+ static PassRefPtr<DeleteSelectionCommand> create(const VisibleSelection& selection, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false)
{
return adoptRef(new DeleteSelectionCommand(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements));
}
private:
DeleteSelectionCommand(Document*, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements);
- DeleteSelectionCommand(const Selection&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements);
+ DeleteSelectionCommand(const VisibleSelection&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements);
virtual void doApply();
virtual EditAction editingAction() const;
@@ -74,7 +74,7 @@ private:
bool m_pruneStartBlockIfNecessary;
// This data is transient and should be cleared at the end of the doApply function.
- Selection m_selectionToDelete;
+ VisibleSelection m_selectionToDelete;
Position m_upstreamStart;
Position m_downstreamStart;
Position m_upstreamEnd;
diff --git a/WebCore/editing/EditCommand.cpp b/WebCore/editing/EditCommand.cpp
index 7ec0c19..fefe658 100644
--- a/WebCore/editing/EditCommand.cpp
+++ b/WebCore/editing/EditCommand.cpp
@@ -47,8 +47,7 @@ EditCommand::EditCommand(Document* document)
{
ASSERT(m_document);
ASSERT(m_document->frame());
- DeleteButtonController* deleteButton = m_document->frame()->editor()->deleteButtonController();
- setStartingSelection(avoidIntersectionWithNode(m_document->frame()->selection()->selection(), deleteButton ? deleteButton->containerElement() : 0));
+ setStartingSelection(avoidIntersectionWithNode(m_document->frame()->selection()->selection(), m_document->frame()->editor()->deleteButtonController()->containerElement()));
setEndingSelection(m_startingSelection);
}
@@ -94,7 +93,10 @@ void EditCommand::apply()
if (!m_parent) {
updateLayout();
- frame->editor()->appliedEditing(this);
+ // Only need to call appliedEditing for top-level commands, and TypingCommands do it on their
+ // own (see TypingCommand::typingAddedToOpenCommand).
+ if (!isTypingCommand())
+ frame->editor()->appliedEditing(this);
}
}
@@ -158,7 +160,7 @@ EditAction EditCommand::editingAction() const
return EditActionUnspecified;
}
-void EditCommand::setStartingSelection(const Selection& s)
+void EditCommand::setStartingSelection(const VisibleSelection& s)
{
Element* root = s.rootEditableElement();
for (EditCommand* cmd = this; ; cmd = cmd->m_parent) {
@@ -169,7 +171,7 @@ void EditCommand::setStartingSelection(const Selection& s)
}
}
-void EditCommand::setEndingSelection(const Selection &s)
+void EditCommand::setEndingSelection(const VisibleSelection &s)
{
Element* root = s.rootEditableElement();
for (EditCommand* cmd = this; cmd; cmd = cmd->m_parent) {
diff --git a/WebCore/editing/EditCommand.h b/WebCore/editing/EditCommand.h
index 78490f8..c4969f6 100644
--- a/WebCore/editing/EditCommand.h
+++ b/WebCore/editing/EditCommand.h
@@ -28,7 +28,7 @@
#include "EditAction.h"
#include "Element.h"
-#include "Selection.h"
+#include "VisibleSelection.h"
namespace WebCore {
@@ -47,8 +47,8 @@ public:
virtual EditAction editingAction() const;
- const Selection& startingSelection() const { return m_startingSelection; }
- const Selection& endingSelection() const { return m_endingSelection; }
+ const VisibleSelection& startingSelection() const { return m_startingSelection; }
+ const VisibleSelection& endingSelection() const { return m_endingSelection; }
Element* startingRootEditableElement() const { return m_startingRootEditableElement.get(); }
Element* endingRootEditableElement() const { return m_endingRootEditableElement.get(); }
@@ -63,8 +63,8 @@ protected:
Document* document() const { return m_document.get(); }
- void setStartingSelection(const Selection&);
- void setEndingSelection(const Selection&);
+ void setStartingSelection(const VisibleSelection&);
+ void setEndingSelection(const VisibleSelection&);
PassRefPtr<CSSMutableStyleDeclaration> styleAtPosition(const Position&);
void updateLayout() const;
@@ -75,8 +75,8 @@ private:
virtual void doReapply(); // calls doApply()
RefPtr<Document> m_document;
- Selection m_startingSelection;
- Selection m_endingSelection;
+ VisibleSelection m_startingSelection;
+ VisibleSelection m_endingSelection;
RefPtr<Element> m_startingRootEditableElement;
RefPtr<Element> m_endingRootEditableElement;
CompositeEditCommand* m_parent;
diff --git a/WebCore/editing/Editor.cpp b/WebCore/editing/Editor.cpp
index 510b910..f44449b 100644
--- a/WebCore/editing/Editor.cpp
+++ b/WebCore/editing/Editor.cpp
@@ -74,9 +74,9 @@ using namespace HTMLNames;
// When an event handler has moved the selection outside of a text control
// we should use the target control's selection for this editing operation.
-Selection Editor::selectionForCommand(Event* event)
+VisibleSelection Editor::selectionForCommand(Event* event)
{
- Selection selection = m_frame->selection()->selection();
+ VisibleSelection selection = m_frame->selection()->selection();
if (!event)
return selection;
// If the target is a text control, and the current selection is outside of its shadow tree,
@@ -222,7 +222,7 @@ bool Editor::isSelectTrailingWhitespaceEnabled()
bool Editor::deleteWithDirection(SelectionController::EDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction)
{
- if (!canEdit() || !m_frame->document())
+ if (!canEdit())
return false;
if (m_frame->selection()->isRange()) {
@@ -317,7 +317,7 @@ PassRefPtr<Range> Editor::selectedRange()
{
if (!m_frame)
return 0;
- return m_frame->selection()->toRange();
+ return m_frame->selection()->toNormalizedRange();
}
bool Editor::shouldDeleteRange(Range* range) const
@@ -376,14 +376,14 @@ bool Editor::shouldShowDeleteInterface(HTMLElement* element) const
return client() && client()->shouldShowDeleteInterface(element);
}
-void Editor::respondToChangedSelection(const Selection& oldSelection)
+void Editor::respondToChangedSelection(const VisibleSelection& oldSelection)
{
if (client())
client()->respondToChangedSelection();
m_deleteButtonController->respondToChangedSelection(oldSelection);
}
-void Editor::respondToChangedContents(const Selection& endingSelection)
+void Editor::respondToChangedContents(const VisibleSelection& endingSelection)
{
if (AXObjectCache::accessibilityEnabled()) {
Node* node = endingSelection.start().node();
@@ -419,7 +419,7 @@ const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const
const SimpleFontData* font = 0;
- RefPtr<Range> range = m_frame->selection()->toRange();
+ RefPtr<Range> range = m_frame->selection()->toNormalizedRange();
Node* startNode = range->editingStartPosition().node();
if (startNode) {
Node* pastEnd = range->pastLastNode();
@@ -572,7 +572,7 @@ bool Editor::hasBidiSelection() const
if (style->direction() == RTL)
return true;
- return static_cast<RenderBlock*>(renderer)->containsNonZeroBidiLevel();
+ return toRenderBlock(renderer)->containsNonZeroBidiLevel();
}
TriState Editor::selectionUnorderedListState() const
@@ -689,7 +689,7 @@ void Editor::clearLastEditCommand()
bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy)
{
Node* target = m_frame->selection()->start().element();
- if (!target && m_frame->document())
+ if (!target)
target = m_frame->document()->body();
if (!target)
return true;
@@ -699,7 +699,7 @@ bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPoli
ExceptionCode ec = 0;
RefPtr<Event> evt = ClipboardEvent::create(eventType, true, true, clipboard);
- EventTargetNodeCast(target)->dispatchEvent(evt, ec);
+ target->dispatchEvent(evt, ec);
bool noDefaultProcessing = evt->defaultPrevented();
// invalidate clipboard here for security
@@ -710,15 +710,15 @@ bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPoli
void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction)
{
- switch (m_frame->selection()->state()) {
- case Selection::NONE:
+ switch (m_frame->selection()->selectionType()) {
+ case VisibleSelection::NoSelection:
// do nothing
break;
- case Selection::CARET:
+ case VisibleSelection::CaretSelection:
m_frame->computeAndSetTypingStyle(style, editingAction);
break;
- case Selection::RANGE:
- if (m_frame->document() && style)
+ case VisibleSelection::RangeSelection:
+ if (style)
applyCommand(ApplyStyleCommand::create(m_frame->document(), style, editingAction));
break;
}
@@ -731,13 +731,13 @@ bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range)
void Editor::applyParagraphStyle(CSSStyleDeclaration* style, EditAction editingAction)
{
- switch (m_frame->selection()->state()) {
- case Selection::NONE:
+ switch (m_frame->selection()->selectionType()) {
+ case VisibleSelection::NoSelection:
// do nothing
break;
- case Selection::CARET:
- case Selection::RANGE:
- if (m_frame->document() && style)
+ case VisibleSelection::CaretSelection:
+ case VisibleSelection::RangeSelection:
+ if (style)
applyCommand(ApplyStyleCommand::create(m_frame->document(), style, editingAction, ApplyStyleCommand::ForceBlockProperties));
break;
}
@@ -748,7 +748,7 @@ void Editor::applyStyleToSelection(CSSStyleDeclaration* style, EditAction editin
if (!style || style->length() == 0 || !canEditRichly())
return;
- if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toRange().get()))
+ if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get()))
applyStyle(style, editingAction);
}
@@ -757,7 +757,7 @@ void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditActi
if (!style || style->length() == 0 || !canEditRichly())
return;
- if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toRange().get()))
+ if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toNormalizedRange().get()))
applyParagraphStyle(style, editingAction);
}
@@ -870,14 +870,9 @@ void Editor::appliedEditing(PassRefPtr<EditCommand> cmd)
{
dispatchEditableContentChangedEvents(*cmd);
- Selection newSelection(cmd->endingSelection());
- // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
- // because there is work that it must do in this situation.
- // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
- // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
+ VisibleSelection newSelection(cmd->endingSelection());
// Don't clear the typing style with this selection change. We do those things elsewhere if necessary.
- if (newSelection == m_frame->selection()->selection() || m_frame->shouldChangeSelection(newSelection))
- m_frame->selection()->setSelection(newSelection, false, false);
+ changeSelectionAfterCommand(newSelection, false, false, cmd.get());
if (!cmd->preservesTypingStyle())
m_frame->setTypingStyle(0);
@@ -899,13 +894,8 @@ void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd)
{
dispatchEditableContentChangedEvents(*cmd);
- Selection newSelection(cmd->startingSelection());
- // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
- // because there is work that it must do in this situation.
- // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
- // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
- if (newSelection == m_frame->selection()->selection() || m_frame->shouldChangeSelection(newSelection))
- m_frame->selection()->setSelection(newSelection, true);
+ VisibleSelection newSelection(cmd->startingSelection());
+ changeSelectionAfterCommand(newSelection, true, true, cmd.get());
m_lastEditCommand = 0;
if (client())
@@ -917,13 +907,8 @@ void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd)
{
dispatchEditableContentChangedEvents(*cmd);
- Selection newSelection(cmd->endingSelection());
- // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
- // because there is work that it must do in this situation.
- // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
- // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
- if (newSelection == m_frame->selection()->selection() || m_frame->shouldChangeSelection(newSelection))
- m_frame->selection()->setSelection(newSelection, true);
+ VisibleSelection newSelection(cmd->endingSelection());
+ changeSelectionAfterCommand(newSelection, true, true, cmd.get());
m_lastEditCommand = 0;
if (client())
@@ -936,6 +921,8 @@ Editor::Editor(Frame* frame)
, m_deleteButtonController(new DeleteButtonController(frame))
, m_ignoreCompositionSelectionChange(false)
, m_shouldStartNewKillRingSequence(false)
+ // This is off by default, since most editors want this behavior (this matches IE but not FF).
+ , m_shouldStyleWithCSS(false)
{
}
@@ -947,6 +934,7 @@ void Editor::clear()
{
m_compositionNode = 0;
m_customCompositionUnderlines.clear();
+ m_shouldStyleWithCSS = false;
}
bool Editor::insertText(const String& text, Event* triggeringEvent)
@@ -959,10 +947,10 @@ bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectIn
if (text.isEmpty())
return false;
- Selection selection = selectionForCommand(triggeringEvent);
+ VisibleSelection selection = selectionForCommand(triggeringEvent);
if (!selection.isContentEditable())
return false;
- RefPtr<Range> range = selection.toRange();
+ RefPtr<Range> range = selection.toNormalizedRange();
if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
return true;
@@ -981,7 +969,7 @@ bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectIn
// Reveal the current selection
if (Frame* editedFrame = document->frame())
if (Page* page = editedFrame->page())
- page->focusController()->focusedOrMainFrame()->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
+ page->focusController()->focusedOrMainFrame()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
}
}
@@ -993,7 +981,7 @@ bool Editor::insertLineBreak()
if (!canEdit())
return false;
- if (!shouldInsertText("\n", m_frame->selection()->toRange().get(), EditorInsertActionTyped))
+ if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
return true;
TypingCommand::insertLineBreak(m_frame->document());
@@ -1009,7 +997,7 @@ bool Editor::insertParagraphSeparator()
if (!canEditRichly())
return insertLineBreak();
- if (!shouldInsertText("\n", m_frame->selection()->toRange().get(), EditorInsertActionTyped))
+ if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
return true;
TypingCommand::insertParagraphSeparator(m_frame->document());
@@ -1204,8 +1192,8 @@ void Editor::setBaseWritingDirection(WritingDirection direction)
{
Node* focusedNode = frame()->document()->focusedNode();
if (focusedNode && (focusedNode->hasTagName(textareaTag)
- || focusedNode->hasTagName(inputTag) && (static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::TEXT
- || static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::SEARCH))) {
+ || (focusedNode->hasTagName(inputTag) && (static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::TEXT
+ || static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::SEARCH)))) {
if (direction == NaturalWritingDirection)
return;
static_cast<HTMLElement*>(focusedNode)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl");
@@ -1226,7 +1214,7 @@ void Editor::selectComposition()
// The composition can start inside a composed character sequence, so we have to override checks.
// See <http://bugs.webkit.org/show_bug.cgi?id=15781>
- Selection selection;
+ VisibleSelection selection;
selection.setWithoutValidation(range->startPosition(), range->endPosition());
m_frame->selection()->setSelection(selection, false, false);
}
@@ -1254,7 +1242,7 @@ void Editor::confirmComposition(const String& text, bool preserveSelection)
{
setIgnoreCompositionSelectionChange(true);
- Selection oldSelection = m_frame->selection()->selection();
+ VisibleSelection oldSelection = m_frame->selection()->selection();
selectComposition();
@@ -1302,9 +1290,9 @@ void Editor::setComposition(const String& text, const Vector<CompositionUnderlin
TypingCommand::insertText(m_frame->document(), text, true, true);
Node* baseNode = m_frame->selection()->base().node();
- unsigned baseOffset = m_frame->selection()->base().offset();
+ unsigned baseOffset = m_frame->selection()->base().m_offset;
Node* extentNode = m_frame->selection()->extent().node();
- unsigned extentOffset = m_frame->selection()->extent().offset();
+ unsigned extentOffset = m_frame->selection()->extent().m_offset;
if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
m_compositionNode = static_cast<Text*>(baseNode);
@@ -1334,7 +1322,7 @@ void Editor::ignoreSpelling()
if (!client())
return;
- RefPtr<Range> selectedRange = frame()->selection()->toRange();
+ RefPtr<Range> selectedRange = frame()->selection()->toNormalizedRange();
if (selectedRange)
frame()->document()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling);
@@ -1548,6 +1536,130 @@ static String findFirstBadGrammarInRange(EditorClient* client, Range* searchRang
#endif /* not BUILDING_ON_TIGER */
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+
+static String findFirstMisspellingOrBadGrammarInRange(EditorClient* client, Range* searchRange, bool checkGrammar, bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail)
+{
+ ASSERT_ARG(client, client);
+ ASSERT_ARG(searchRange, searchRange);
+
+ String firstFoundItem;
+ String misspelledWord;
+ String badGrammarPhrase;
+ ExceptionCode ec = 0;
+
+ // Initialize out parameters; these will be updated if we find something to return.
+ outIsSpelling = true;
+ outFirstFoundOffset = 0;
+ outGrammarDetail.location = -1;
+ outGrammarDetail.length = 0;
+ outGrammarDetail.guesses.clear();
+ outGrammarDetail.userDescription = "";
+
+ // Expand the search range to encompass entire paragraphs, since text checking needs that much context.
+ // Determine the character offset from the start of the paragraph to the start of the original search range,
+ // since we will want to ignore results in this area.
+ RefPtr<Range> paragraphRange = searchRange->cloneRange(ec);
+ setStart(paragraphRange.get(), startOfParagraph(searchRange->startPosition()));
+ int totalRangeLength = TextIterator::rangeLength(paragraphRange.get());
+ setEnd(paragraphRange.get(), endOfParagraph(searchRange->startPosition()));
+
+ RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), searchRange->startPosition());
+ int searchRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
+ int totalLengthProcessed = 0;
+
+ bool firstIteration = true;
+ bool lastIteration = false;
+ while (totalLengthProcessed < totalRangeLength) {
+ // Iterate through the search range by paragraphs, checking each one for spelling and grammar.
+ int currentLength = TextIterator::rangeLength(paragraphRange.get());
+ int currentStartOffset = firstIteration ? searchRangeStartOffset : 0;
+ int currentEndOffset = currentLength;
+ if (inSameParagraph(paragraphRange->startPosition(), searchRange->endPosition())) {
+ // Determine the character offset from the end of the original search range to the end of the paragraph,
+ // since we will want to ignore results in this area.
+ RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), searchRange->endPosition());
+ currentEndOffset = TextIterator::rangeLength(endOffsetAsRange.get());
+ lastIteration = true;
+ }
+ if (currentStartOffset < currentEndOffset) {
+ String paragraphString = plainText(paragraphRange.get());
+ if (paragraphString.length() > 0) {
+ bool foundGrammar = false;
+ int spellingLocation = 0;
+ int grammarPhraseLocation = 0;
+ int grammarDetailLocation = 0;
+ unsigned grammarDetailIndex = 0;
+
+ Vector<TextCheckingResult> results;
+ client->checkSpellingAndGrammarOfParagraph(paragraphString.characters(), paragraphString.length(), checkGrammar, results);
+
+ for (unsigned i = 0; i < results.size(); i++) {
+ const TextCheckingResult* result = &results[i];
+ if (result->resultType == 1 && result->location >= currentStartOffset && result->location + result->length <= currentEndOffset) {
+ ASSERT(result->length > 0 && result->location >= 0);
+ spellingLocation = result->location;
+ misspelledWord = paragraphString.substring(result->location, result->length);
+ ASSERT(misspelledWord.length() != 0);
+ break;
+ } else if (checkGrammar && result->resultType == 2 && result->location < currentEndOffset && result->location + result->length > currentStartOffset) {
+ ASSERT(result->length > 0 && result->location >= 0);
+ // We can't stop after the first grammar result, since there might still be a spelling result after
+ // it begins but before the first detail in it, but we can stop if we find a second grammar result.
+ if (foundGrammar) break;
+ for (unsigned j = 0; j < result->details.size(); j++) {
+ const GrammarDetail* detail = &result->details[j];
+ ASSERT(detail->length > 0 && detail->location >= 0);
+ if (result->location + detail->location >= currentStartOffset && result->location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result->location + detail->location < grammarDetailLocation)) {
+ grammarDetailIndex = j;
+ grammarDetailLocation = result->location + detail->location;
+ foundGrammar = true;
+ }
+ }
+ if (foundGrammar) {
+ grammarPhraseLocation = result->location;
+ outGrammarDetail = result->details[grammarDetailIndex];
+ badGrammarPhrase = paragraphString.substring(result->location, result->length);
+ ASSERT(badGrammarPhrase.length() != 0);
+ }
+ }
+ }
+
+ if (!misspelledWord.isEmpty() && (!checkGrammar || badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) {
+ int spellingOffset = spellingLocation - currentStartOffset;
+ if (!firstIteration) {
+ RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), searchRange->startPosition(), paragraphRange->startPosition());
+ spellingOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get());
+ }
+ outIsSpelling = true;
+ outFirstFoundOffset = spellingOffset;
+ firstFoundItem = misspelledWord;
+ break;
+ } else if (checkGrammar && !badGrammarPhrase.isEmpty()) {
+ int grammarPhraseOffset = grammarPhraseLocation - currentStartOffset;
+ if (!firstIteration) {
+ RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), searchRange->startPosition(), paragraphRange->startPosition());
+ grammarPhraseOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get());
+ }
+ outIsSpelling = false;
+ outFirstFoundOffset = grammarPhraseOffset;
+ firstFoundItem = badGrammarPhrase;
+ break;
+ }
+ }
+ }
+ if (lastIteration || totalLengthProcessed + currentLength >= totalRangeLength)
+ break;
+ setStart(paragraphRange.get(), startOfNextParagraph(paragraphRange->endPosition()));
+ setEnd(paragraphRange.get(), endOfParagraph(paragraphRange->startPosition()));
+ firstIteration = false;
+ totalLengthProcessed += currentLength;
+ }
+ return firstFoundItem;
+}
+
+#endif
+
void Editor::advanceToNextMisspelling(bool startBeforeSelection)
{
ExceptionCode ec = 0;
@@ -1557,7 +1669,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// Start at the end of the selection, search to edge of document. Starting at the selection end makes
// repeated "check spelling" commands work.
- Selection selection(frame()->selection()->selection());
+ VisibleSelection selection(frame()->selection()->selection());
RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
bool startedWithSelection = false;
if (selection.start().node()) {
@@ -1584,13 +1696,14 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
return;
Position rangeCompliantPosition = rangeCompliantEquivalent(position);
- spellingSearchRange->setStart(rangeCompliantPosition.node(), rangeCompliantPosition.offset(), ec);
+ spellingSearchRange->setStart(rangeCompliantPosition.node(), rangeCompliantPosition.m_offset, ec);
startedWithSelection = false; // won't need to wrap
}
// topNode defines the whole range we want to operate on
Node* topNode = highestEditableRoot(position);
- spellingSearchRange->setEnd(topNode, maxDeepOffset(topNode), ec);
+ // FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>)
+ spellingSearchRange->setEnd(topNode, lastOffsetForEditing(topNode), ec);
// If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
// at a word boundary. Going back by one char and then forward by a word does the trick.
@@ -1614,7 +1727,24 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec);
int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec);
- int misspellingOffset;
+ int misspellingOffset = 0;
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+ RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec);
+ String misspelledWord;
+ String badGrammarPhrase;
+ int grammarPhraseOffset = 0;
+ bool isSpelling = true;
+ int foundOffset = 0;
+ GrammarDetail grammarDetail;
+ String foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
+ if (isSpelling) {
+ misspelledWord = foundItem;
+ misspellingOffset = foundOffset;
+ } else {
+ badGrammarPhrase = foundItem;
+ grammarPhraseOffset = foundOffset;
+ }
+#else
String misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false);
String badGrammarPhrase;
@@ -1635,6 +1765,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
if (isGrammarCheckingEnabled())
badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
#endif
+#endif
// If we found neither bad grammar nor a misspelled word, wrap and try again (but don't bother if we started at the beginning of the
// block rather than at a selection).
@@ -1643,6 +1774,17 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// going until the end of the very first chunk we tested is far enough
spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec);
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+ grammarSearchRange = spellingSearchRange->cloneRange(ec);
+ foundItem = findFirstMisspellingOrBadGrammarInRange(client(), spellingSearchRange.get(), isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
+ if (isSpelling) {
+ misspelledWord = foundItem;
+ misspellingOffset = foundOffset;
+ } else {
+ badGrammarPhrase = foundItem;
+ grammarPhraseOffset = foundOffset;
+ }
+#else
misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false);
#ifndef BUILDING_ON_TIGER
@@ -1656,6 +1798,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
if (isGrammarCheckingEnabled())
badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
#endif
+#endif
}
if (!badGrammarPhrase.isEmpty()) {
@@ -1671,7 +1814,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
- frame()->selection()->setSelection(Selection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
+ frame()->selection()->setSelection(VisibleSelection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
frame()->revealSelection();
client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
@@ -1682,7 +1825,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// a marker so we draw the red squiggle later.
RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
- frame()->selection()->setSelection(Selection(misspellingRange.get(), DOWNSTREAM));
+ frame()->selection()->setSelection(VisibleSelection(misspellingRange.get(), DOWNSTREAM));
frame()->revealSelection();
client()->updateSpellingUIWithMisspelledWord(misspelledWord);
@@ -1770,7 +1913,7 @@ bool Editor::isSelectionUngrammatical()
return false;
#else
Vector<String> ignoredGuesses;
- return isRangeUngrammatical(client(), frame()->selection()->toRange().get(), ignoredGuesses);
+ return isRangeUngrammatical(client(), frame()->selection()->toNormalizedRange().get(), ignoredGuesses);
#endif
}
@@ -1781,7 +1924,7 @@ Vector<String> Editor::guessesForUngrammaticalSelection()
#else
Vector<String> guesses;
// Ignore the result of isRangeUngrammatical; we just want the guesses, whether or not there are any
- isRangeUngrammatical(client(), frame()->selection()->toRange().get(), guesses);
+ isRangeUngrammatical(client(), frame()->selection()->toNormalizedRange().get(), guesses);
return guesses;
#endif
}
@@ -1797,6 +1940,86 @@ Vector<String> Editor::guessesForMisspelledSelection()
return guesses;
}
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+
+static Vector<String> guessesForMisspelledOrUngrammaticalRange(EditorClient* client, Range *range, bool checkGrammar, bool& misspelled, bool& ungrammatical)
+{
+ Vector<String> guesses;
+ ExceptionCode ec;
+ misspelled = false;
+ ungrammatical = false;
+
+ if (!client || !range || range->collapsed(ec))
+ return guesses;
+
+ // Expand the range to encompass entire paragraphs, since text checking needs that much context.
+ int rangeStartOffset;
+ String paragraphString;
+ RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(range, rangeStartOffset, paragraphString);
+ int rangeLength = TextIterator::rangeLength(range);
+ if (rangeLength == 0 || paragraphString.length() == 0)
+ return guesses;
+
+ Vector<TextCheckingResult> results;
+ client->checkSpellingAndGrammarOfParagraph(paragraphString.characters(), paragraphString.length(), checkGrammar, results);
+
+ for (unsigned i = 0; i < results.size(); i++) {
+ const TextCheckingResult* result = &results[i];
+ if (result->resultType == 1 && result->location == rangeStartOffset && result->length == rangeLength) {
+ String misspelledWord = paragraphString.substring(rangeStartOffset, rangeLength);
+ ASSERT(misspelledWord.length() != 0);
+ client->getGuessesForWord(misspelledWord, guesses);
+ client->updateSpellingUIWithMisspelledWord(misspelledWord);
+ misspelled = true;
+ return guesses;
+ }
+ }
+
+ if (!checkGrammar)
+ return guesses;
+
+ for (unsigned i = 0; i < results.size(); i++) {
+ const TextCheckingResult* result = &results[i];
+ if (result->resultType == 2 && result->location <= rangeStartOffset && result->location + result->length >= rangeStartOffset + rangeLength) {
+ for (unsigned j = 0; j < result->details.size(); j++) {
+ const GrammarDetail* detail = &result->details[j];
+ ASSERT(detail->length > 0 && detail->location >= 0);
+ if (result->location + detail->location == rangeStartOffset && detail->length == rangeLength) {
+ String badGrammarPhrase = paragraphString.substring(result->location, result->length);
+ ASSERT(badGrammarPhrase.length() != 0);
+ for (unsigned k = 0; k < detail->guesses.size(); k++)
+ guesses.append(detail->guesses[k]);
+ client->updateSpellingUIWithGrammarString(badGrammarPhrase, *detail);
+ ungrammatical = true;
+ return guesses;
+ }
+ }
+ }
+ }
+ return guesses;
+}
+
+#endif
+
+Vector<String> Editor::guessesForMisspelledOrUngrammaticalSelection(bool& misspelled, bool& ungrammatical)
+{
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+ return guessesForMisspelledOrUngrammaticalRange(client(), frame()->selection()->toNormalizedRange().get(), isGrammarCheckingEnabled(), misspelled, ungrammatical);
+#else
+ misspelled = isSelectionMisspelled();
+ if (misspelled) {
+ ungrammatical = false;
+ return guessesForMisspelledSelection();
+ }
+ if (isGrammarCheckingEnabled() && isSelectionUngrammatical()) {
+ ungrammatical = true;
+ return guessesForUngrammaticalSelection();
+ }
+ ungrammatical = false;
+ return Vector<String>();
+#endif
+}
+
void Editor::showSpellingGuessPanel()
{
if (!client()) {
@@ -1829,14 +2052,24 @@ void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p)
if (!isContinuousSpellCheckingEnabled())
return;
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+ VisibleSelection adjacentWords = VisibleSelection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary));
+ if (isGrammarCheckingEnabled()) {
+ VisibleSelection selectedSentence = VisibleSelection(startOfSentence(p), endOfSentence(p));
+ markMisspellingsAndBadGrammar(adjacentWords, true, selectedSentence);
+ } else {
+ markMisspellingsAndBadGrammar(adjacentWords, false, adjacentWords);
+ }
+#else
// Check spelling of one word
- markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
+ markMisspellings(VisibleSelection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
if (!isGrammarCheckingEnabled())
return;
// Check grammar of entire sentence
- markBadGrammar(Selection(startOfSentence(p), endOfSentence(p)));
+ markBadGrammar(VisibleSelection(startOfSentence(p), endOfSentence(p)));
+#endif
}
static void markAllMisspellingsInRange(EditorClient* client, Range* searchRange)
@@ -1858,7 +2091,7 @@ static void markAllBadGrammarInRange(EditorClient* client, Range* searchRange)
}
#endif
-static void markMisspellingsOrBadGrammar(Editor* editor, const Selection& selection, bool checkSpelling)
+static void markMisspellingsOrBadGrammar(Editor* editor, const VisibleSelection& selection, bool checkSpelling)
{
// This function is called with a selection already expanded to word boundaries.
// Might be nice to assert that here.
@@ -1868,7 +2101,7 @@ static void markMisspellingsOrBadGrammar(Editor* editor, const Selection& select
if (!editor->isContinuousSpellCheckingEnabled())
return;
- RefPtr<Range> searchRange(selection.toRange());
+ RefPtr<Range> searchRange(selection.toNormalizedRange());
if (!searchRange)
return;
@@ -1893,12 +2126,12 @@ static void markMisspellingsOrBadGrammar(Editor* editor, const Selection& select
}
}
-void Editor::markMisspellings(const Selection& selection)
+void Editor::markMisspellings(const VisibleSelection& selection)
{
markMisspellingsOrBadGrammar(this, selection, true);
}
-void Editor::markBadGrammar(const Selection& selection)
+void Editor::markBadGrammar(const VisibleSelection& selection)
{
#ifndef BUILDING_ON_TIGER
markMisspellingsOrBadGrammar(this, selection, false);
@@ -1907,6 +2140,78 @@ void Editor::markBadGrammar(const Selection& selection)
#endif
}
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+
+static void markAllMisspellingsAndBadGrammarInRanges(EditorClient* client, Range *spellingRange, bool markGrammar, Range *grammarRange)
+{
+ // This function is called with selections already expanded to word boundaries.
+ ExceptionCode ec;
+ if (!client || !spellingRange || (markGrammar && !grammarRange))
+ return;
+
+ // If we're not in an editable node, bail.
+ Node* editableNode = spellingRange->startContainer();
+ if (!editableNode || !editableNode->isContentEditable())
+ return;
+
+ // Expand the range to encompass entire paragraphs, since text checking needs that much context.
+ int spellingRangeStartOffset = 0;
+ int spellingRangeEndOffset = 0;
+ int grammarRangeStartOffset = 0;
+ int grammarRangeEndOffset = 0;
+ String paragraphString;
+
+ if (markGrammar) {
+ // The spelling range should be contained in the paragraph-aligned extension of the grammar range.
+ RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(grammarRange, grammarRangeStartOffset, paragraphString);
+ RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), spellingRange->startPosition());
+ spellingRangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
+ grammarRangeEndOffset = grammarRangeStartOffset + TextIterator::rangeLength(grammarRange);
+ } else {
+ RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(spellingRange, spellingRangeStartOffset, paragraphString);
+ }
+ spellingRangeEndOffset = spellingRangeStartOffset + TextIterator::rangeLength(spellingRange);
+ if (paragraphString.length() == 0 || (spellingRangeStartOffset >= spellingRangeEndOffset && (!markGrammar || grammarRangeStartOffset >= grammarRangeEndOffset)))
+ return;
+
+ Vector<TextCheckingResult> results;
+ client->checkSpellingAndGrammarOfParagraph(paragraphString.characters(), paragraphString.length(), markGrammar, results);
+
+ for (unsigned i = 0; i < results.size(); i++) {
+ const TextCheckingResult* result = &results[i];
+ if (result->resultType == 1 && result->location >= spellingRangeStartOffset && result->location + result->length <= spellingRangeEndOffset) {
+ ASSERT(result->length > 0 && result->location >= 0);
+ RefPtr<Range> misspellingRange = TextIterator::subrange(spellingRange, result->location - spellingRangeStartOffset, result->length);
+ misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
+ } else if (markGrammar && result->resultType == 2 && result->location < grammarRangeEndOffset && result->location + result->length > grammarRangeStartOffset) {
+ ASSERT(result->length > 0 && result->location >= 0);
+ for (unsigned j = 0; j < result->details.size(); j++) {
+ const GrammarDetail* detail = &result->details[j];
+ ASSERT(detail->length > 0 && detail->location >= 0);
+ if (result->location + detail->location >= grammarRangeStartOffset && result->location + detail->location + detail->length <= grammarRangeEndOffset) {
+ RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarRange, result->location + detail->location - grammarRangeStartOffset, detail->length);
+ grammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection)
+{
+#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)
+ if (!isContinuousSpellCheckingEnabled())
+ return;
+ markAllMisspellingsAndBadGrammarInRanges(client(), spellingSelection.toNormalizedRange().get(), markGrammar && isGrammarCheckingEnabled(), grammarSelection.toNormalizedRange().get());
+#else
+ markMisspellings(spellingSelection);
+ if (markGrammar)
+ markBadGrammar(grammarSelection);
+#endif
+}
+
PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
{
Document* document = m_frame->documentAtPoint(windowPoint);
@@ -1919,8 +2224,8 @@ PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
if (!frameView)
return 0;
IntPoint framePoint = frameView->windowToContents(windowPoint);
- Selection selection(frame->visiblePositionForPoint(framePoint));
- return avoidIntersectionWithNode(selection.toRange().get(), deleteButtonController() ? deleteButtonController()->containerElement() : 0);
+ VisibleSelection selection(frame->visiblePositionForPoint(framePoint));
+ return avoidIntersectionWithNode(selection.toNormalizedRange().get(), m_deleteButtonController->containerElement());
}
void Editor::revealSelectionAfterEditingOperation()
@@ -1928,7 +2233,7 @@ void Editor::revealSelectionAfterEditingOperation()
if (m_ignoreCompositionSelectionChange)
return;
- m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded);
+ m_frame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
}
void Editor::setIgnoreCompositionSelectionChange(bool ignore)
@@ -1964,13 +2269,13 @@ bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selecti
if (end.node() != m_compositionNode)
return false;
- if (static_cast<unsigned>(start.offset()) < m_compositionStart)
+ if (static_cast<unsigned>(start.m_offset) < m_compositionStart)
return false;
- if (static_cast<unsigned>(end.offset()) > m_compositionEnd)
+ if (static_cast<unsigned>(end.m_offset) > m_compositionEnd)
return false;
- selectionStart = start.offset() - m_compositionStart;
- selectionEnd = start.offset() - m_compositionEnd;
+ selectionStart = start.m_offset - m_compositionStart;
+ selectionEnd = start.m_offset - m_compositionEnd;
return true;
}
@@ -1979,7 +2284,7 @@ void Editor::transpose()
if (!canEdit())
return;
- Selection selection = m_frame->selection()->selection();
+ VisibleSelection selection = m_frame->selection()->selection();
if (!selection.isCaret())
return;
@@ -1995,7 +2300,7 @@ void Editor::transpose()
RefPtr<Range> range = makeRange(previous, next);
if (!range)
return;
- Selection newSelection(range.get(), DOWNSTREAM);
+ VisibleSelection newSelection(range.get(), DOWNSTREAM);
// Transpose the two characters.
String text = plainText(range.get());
@@ -2070,7 +2375,7 @@ bool Editor::insideVisibleArea(const IntPoint& point) const
if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
return true;
- IntRect rectInPageCoords = container->getOverflowClipRect(0, 0);
+ IntRect rectInPageCoords = container->overflowClipRect(0, 0);
IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
rectInPageCoords.width(), rectInPageCoords.height());
@@ -2096,7 +2401,7 @@ bool Editor::insideVisibleArea(Range* range) const
if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN))
return true;
- IntRect rectInPageCoords = container->getOverflowClipRect(0, 0);
+ IntRect rectInPageCoords = container->overflowClipRect(0, 0);
IntRect rectInFrameCoords = IntRect(renderer->x() * -1, renderer->y() * -1,
rectInPageCoords.width(), rectInPageCoords.height());
IntRect resultRect = range->boundingBox();
@@ -2186,4 +2491,25 @@ PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& ta
return lastVisibleRange(target, caseFlag);
}
+void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle, EditCommand* cmd)
+{
+ // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection,
+ // because there is work that it must do in this situation.
+ // The old selection can be invalid here and calling shouldChangeSelection can produce some strange calls.
+ // See <rdar://problem/5729315> Some shouldChangeSelectedDOMRange contain Ranges for selections that are no longer valid
+ bool selectionDidNotChangeDOMPosition = newSelection == m_frame->selection()->selection();
+ if (selectionDidNotChangeDOMPosition || m_frame->shouldChangeSelection(newSelection))
+ m_frame->selection()->setSelection(newSelection, closeTyping, clearTypingStyle);
+
+ // Some kinds of deletes and line break insertions change the selection's position within the document without
+ // changing its position within the DOM. For example when you press return in the following (the caret is marked by ^):
+ // <div contentEditable="true"><div>^Hello</div></div>
+ // WebCore inserts <div><br></div> *before* the current block, which correctly moves the paragraph down but which doesn't
+ // change the caret's DOM position (["hello", 0]). In these situations the above SelectionController::setSelection call
+ // does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and
+ // starts a new kill ring sequence, but we want to do these things (matches AppKit).
+ if (selectionDidNotChangeDOMPosition && cmd->isTypingCommand())
+ client()->respondToChangedSelection();
+}
+
} // namespace WebCore
diff --git a/WebCore/editing/Editor.h b/WebCore/editing/Editor.h
index d97f670..ce8fa0f 100644
--- a/WebCore/editing/Editor.h
+++ b/WebCore/editing/Editor.h
@@ -110,8 +110,8 @@ public:
bool shouldDeleteRange(Range*) const;
bool shouldApplyStyle(CSSStyleDeclaration*, Range*);
- void respondToChangedSelection(const Selection& oldSelection);
- void respondToChangedContents(const Selection& endingSelection);
+ void respondToChangedSelection(const VisibleSelection& oldSelection);
+ void respondToChangedContents(const VisibleSelection& endingSelection);
TriState selectionHasStyle(CSSStyleDeclaration*) const;
const SimpleFontData* fontForSelection(bool&) const;
@@ -151,6 +151,9 @@ public:
bool selectionStartHasStyle(CSSStyleDeclaration*) const;
bool clientIsEditable() const;
+
+ void setShouldStyleWithCSS(bool flag) { m_shouldStyleWithCSS = flag; }
+ bool shouldStyleWithCSS() const { return m_shouldStyleWithCSS; }
class Command {
public:
@@ -192,9 +195,11 @@ public:
bool isSelectionMisspelled();
Vector<String> guessesForMisspelledSelection();
Vector<String> guessesForUngrammaticalSelection();
+ Vector<String> guessesForMisspelledOrUngrammaticalSelection(bool& misspelled, bool& ungrammatical);
void markMisspellingsAfterTypingToPosition(const VisiblePosition&);
- void markMisspellings(const Selection&);
- void markBadGrammar(const Selection&);
+ void markMisspellings(const VisibleSelection&);
+ void markBadGrammar(const VisibleSelection&);
+ void markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection);
void advanceToNextMisspelling(bool startBeforeSelection = false);
void showSpellingGuessPanel();
bool spellingPanelIsShowing();
@@ -250,7 +255,7 @@ public:
void clear();
- Selection selectionForCommand(Event*);
+ VisibleSelection selectionForCommand(Event*);
void appendToKillRing(const String&);
void prependToKillRing(const String&);
@@ -278,6 +283,7 @@ private:
Vector<CompositionUnderline> m_customCompositionUnderlines;
bool m_ignoreCompositionSelectionChange;
bool m_shouldStartNewKillRingSequence;
+ bool m_shouldStyleWithCSS;
bool canDeleteRange(Range*) const;
bool canSmartReplaceWithPasteboard(Pasteboard*);
@@ -295,6 +301,8 @@ private:
PassRefPtr<Range> firstVisibleRange(const String&, bool caseFlag);
PassRefPtr<Range> lastVisibleRange(const String&, bool caseFlag);
+
+ void changeSelectionAfterCommand(const VisibleSelection& newSelection, bool closeTyping, bool clearTypingStyle, EditCommand*);
};
inline void Editor::setStartNewKillRingSequence(bool flag)
diff --git a/WebCore/editing/EditorCommand.cpp b/WebCore/editing/EditorCommand.cpp
index 54c73b9..0090df7 100644
--- a/WebCore/editing/EditorCommand.cpp
+++ b/WebCore/editing/EditorCommand.cpp
@@ -35,13 +35,14 @@
#include "EditorClient.h"
#include "Event.h"
#include "EventHandler.h"
-#include "Frame.h"
#include "FormatBlockCommand.h"
+#include "Frame.h"
#include "HTMLFontElement.h"
#include "HTMLImageElement.h"
#include "IndentOutdentCommand.h"
#include "InsertListCommand.h"
#include "Page.h"
+#include "RenderBox.h"
#include "ReplaceSelectionCommand.h"
#include "Scrollbar.h"
#include "Settings.h"
@@ -184,15 +185,15 @@ static bool executeInsertNode(Frame* frame, PassRefPtr<Node> content)
static bool expandSelectionToGranularity(Frame* frame, TextGranularity granularity)
{
- Selection selection = frame->selection()->selection();
+ VisibleSelection selection = frame->selection()->selection();
selection.expandUsingGranularity(granularity);
- RefPtr<Range> newRange = selection.toRange();
+ RefPtr<Range> newRange = selection.toNormalizedRange();
if (!newRange)
return false;
ExceptionCode ec = 0;
if (newRange->collapsed(ec))
return false;
- RefPtr<Range> oldRange = frame->selection()->selection().toRange();
+ RefPtr<Range> oldRange = frame->selection()->selection().toNormalizedRange();
EAffinity affinity = frame->selection()->affinity();
if (!frame->editor()->client()->shouldChangeSelectedRange(oldRange.get(), newRange.get(), affinity, false))
return false;
@@ -342,7 +343,7 @@ static bool executeDeleteToEndOfParagraph(Frame* frame, Event*, EditorCommandSou
static bool executeDeleteToMark(Frame* frame, Event*, EditorCommandSource, const String&)
{
- RefPtr<Range> mark = frame->mark().toRange();
+ RefPtr<Range> mark = frame->mark().toNormalizedRange();
if (mark) {
SelectionController* selection = frame->selection();
bool selected = selection->setSelectedRange(unionDOMRanges(mark.get(), frame->editor()->selectedRange().get()).get(), DOWNSTREAM, true);
@@ -491,9 +492,9 @@ static bool executeInsertNewlineInQuotedContent(Frame* frame, Event*, EditorComm
return true;
}
-static bool executeInsertOrderedList(Frame* frame, Event*, EditorCommandSource, const String& value)
+static bool executeInsertOrderedList(Frame* frame, Event*, EditorCommandSource, const String&)
{
- applyCommand(InsertListCommand::create(frame->document(), InsertListCommand::OrderedList, value));
+ applyCommand(InsertListCommand::create(frame->document(), InsertListCommand::OrderedList));
return true;
}
@@ -514,9 +515,9 @@ static bool executeInsertText(Frame* frame, Event*, EditorCommandSource, const S
return true;
}
-static bool executeInsertUnorderedList(Frame* frame, Event*, EditorCommandSource, const String& value)
+static bool executeInsertUnorderedList(Frame* frame, Event*, EditorCommandSource, const String&)
{
- applyCommand(InsertListCommand::create(frame->document(), InsertListCommand::UnorderedList, value));
+ applyCommand(InsertListCommand::create(frame->document(), InsertListCommand::UnorderedList));
return true;
}
@@ -826,6 +827,30 @@ static bool executeMoveWordRightAndModifySelection(Frame* frame, Event*, EditorC
return true;
}
+static bool executeMoveToLeftEndOfLine(Frame* frame, Event*, EditorCommandSource, const String&)
+{
+ frame->selection()->modify(SelectionController::MOVE, SelectionController::LEFT, LineBoundary, true);
+ return true;
+}
+
+static bool executeMoveToLeftEndOfLineAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&)
+{
+ frame->selection()->modify(SelectionController::EXTEND, SelectionController::LEFT, LineBoundary, true);
+ return true;
+}
+
+static bool executeMoveToRightEndOfLine(Frame* frame, Event*, EditorCommandSource, const String&)
+{
+ frame->selection()->modify(SelectionController::MOVE, SelectionController::RIGHT, LineBoundary, true);
+ return true;
+}
+
+static bool executeMoveToRightEndOfLineAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&)
+{
+ frame->selection()->modify(SelectionController::EXTEND, SelectionController::RIGHT, LineBoundary, true);
+ return true;
+}
+
static bool executeOutdent(Frame* frame, Event*, EditorCommandSource, const String&)
{
applyCommand(IndentOutdentCommand::create(frame->document(), IndentOutdentCommand::Outdent));
@@ -888,7 +913,7 @@ static bool executeSelectSentence(Frame* frame, Event*, EditorCommandSource, con
static bool executeSelectToMark(Frame* frame, Event*, EditorCommandSource, const String&)
{
- RefPtr<Range> mark = frame->mark().toRange();
+ RefPtr<Range> mark = frame->mark().toNormalizedRange();
RefPtr<Range> selection = frame->editor()->selectedRange();
if (!mark || !selection) {
systemBeep();
@@ -914,6 +939,15 @@ static bool executeStrikethrough(Frame* frame, Event*, EditorCommandSource sourc
return executeToggleStyle(frame, source, EditActionChangeAttributes, CSSPropertyWebkitTextDecorationsInEffect, "none", "line-through");
}
+static bool executeStyleWithCSS(Frame* frame, Event*, EditorCommandSource, const String& value)
+{
+ if (value != "false" && value != "true")
+ return false;
+
+ frame->editor()->setShouldStyleWithCSS(value == "true" ? true : false);
+ return true;
+}
+
static bool executeSubscript(Frame* frame, Event*, EditorCommandSource source, const String&)
{
return executeToggleStyle(frame, source, EditActionSubscript, CSSPropertyVerticalAlign, "baseline", "sub");
@@ -926,8 +960,8 @@ static bool executeSuperscript(Frame* frame, Event*, EditorCommandSource source,
static bool executeSwapWithMark(Frame* frame, Event*, EditorCommandSource, const String&)
{
- const Selection& mark = frame->mark();
- const Selection& selection = frame->selection()->selection();
+ const VisibleSelection& mark = frame->mark();
+ const VisibleSelection& selection = frame->selection()->selection();
if (mark.isNone() || selection.isNone()) {
systemBeep();
return false;
@@ -1033,20 +1067,20 @@ static bool enabled(Frame*, Event*, EditorCommandSource)
static bool enabledVisibleSelection(Frame* frame, Event* event, EditorCommandSource)
{
// The term "visible" here includes a caret in editable text or a range in any text.
- const Selection& selection = frame->editor()->selectionForCommand(event);
+ const VisibleSelection& selection = frame->editor()->selectionForCommand(event);
return (selection.isCaret() && selection.isContentEditable()) || selection.isRange();
}
static bool enabledVisibleSelectionAndMark(Frame* frame, Event* event, EditorCommandSource)
{
- const Selection& selection = frame->editor()->selectionForCommand(event);
+ const VisibleSelection& selection = frame->editor()->selectionForCommand(event);
return ((selection.isCaret() && selection.isContentEditable()) || selection.isRange())
&& frame->mark().isCaretOrRange();
}
static bool enableCaretInEditableText(Frame* frame, Event* event, EditorCommandSource)
{
- const Selection& selection = frame->editor()->selectionForCommand(event);
+ const VisibleSelection& selection = frame->editor()->selectionForCommand(event);
return selection.isCaret() && selection.isContentEditable();
}
@@ -1138,6 +1172,11 @@ static TriState stateStrikethrough(Frame* frame, Event*)
return stateStyle(frame, CSSPropertyTextDecoration, "line-through");
}
+static TriState stateStyleWithCSS(Frame* frame, Event*)
+{
+ return frame->editor()->shouldStyleWithCSS() ? TrueTriState : FalseTriState;
+}
+
static TriState stateSubscript(Frame* frame, Event*)
{
return stateStyle(frame, CSSPropertyVerticalAlign, "sub");
@@ -1244,8 +1283,8 @@ static const CommandMap& createCommandMap()
{ "IgnoreSpelling", { executeIgnoreSpelling, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "Indent", { executeIndent, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "InsertBacktab", { executeInsertBacktab, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },
- { "InsertHorizontalRule", { executeInsertHorizontalRule, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "InsertHTML", { executeInsertHTML, supported, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
+ { "InsertHorizontalRule", { executeInsertHorizontalRule, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "InsertImage", { executeInsertImage, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "InsertLineBreak", { executeInsertLineBreak, supported, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "InsertNewline", { executeInsertNewline, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },
@@ -1296,6 +1335,10 @@ static const CommandMap& createCommandMap()
{ "MoveToEndOfParagraphAndModifySelection", { executeMoveToEndOfParagraphAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "MoveToEndOfSentence", { executeMoveToEndOfSentence, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "MoveToEndOfSentenceAndModifySelection", { executeMoveToEndOfSentenceAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
+ { "MoveToLeftEndOfLine", { executeMoveToLeftEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
+ { "MoveToLeftEndOfLineAndModifySelection", { executeMoveToLeftEndOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
+ { "MoveToRightEndOfLine", { executeMoveToRightEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
+ { "MoveToRightEndOfLineAndModifySelection", { executeMoveToRightEndOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "MoveUp", { executeMoveUp, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "MoveUpAndModifySelection", { executeMoveUpAndModifySelection, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "MoveWordBackward", { executeMoveWordBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
@@ -1320,6 +1363,7 @@ static const CommandMap& createCommandMap()
{ "SelectWord", { executeSelectWord, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "SetMark", { executeSetMark, supportedFromMenuOrKeyBinding, enabledVisibleSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "Strikethrough", { executeStrikethrough, supported, enabledInRichlyEditableText, stateStrikethrough, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
+ { "StyleWithCSS", { executeStyleWithCSS, supported, enabledInRichlyEditableText, stateStyleWithCSS, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "Subscript", { executeSubscript, supported, enabledInRichlyEditableText, stateSubscript, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "Superscript", { executeSuperscript, supported, enabledInRichlyEditableText, stateSuperscript, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "SwapWithMark", { executeSwapWithMark, supportedFromMenuOrKeyBinding, enabledVisibleSelectionAndMark, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
@@ -1428,7 +1472,7 @@ bool Editor::Command::execute(const String& parameter, Event* triggeringEvent) c
{
if (!isEnabled(triggeringEvent)) {
// Let certain commands be executed when performed explicitly even if they are disabled.
- if (!isSupported() || !m_frame || !m_frame->document() || !m_command->allowExecutionWhenDisabled)
+ if (!isSupported() || !m_frame || !m_command->allowExecutionWhenDisabled)
return false;
}
m_frame->document()->updateLayoutIgnorePendingStylesheets();
@@ -1447,21 +1491,21 @@ bool Editor::Command::isSupported() const
bool Editor::Command::isEnabled(Event* triggeringEvent) const
{
- if (!isSupported() || !m_frame || !m_frame->document())
+ if (!isSupported() || !m_frame)
return false;
return m_command->isEnabled(m_frame.get(), triggeringEvent, m_source);
}
TriState Editor::Command::state(Event* triggeringEvent) const
{
- if (!isSupported() || !m_frame || !m_frame->document())
+ if (!isSupported() || !m_frame)
return FalseTriState;
return m_command->state(m_frame.get(), triggeringEvent);
}
String Editor::Command::value(Event* triggeringEvent) const
{
- if (!isSupported() || !m_frame || !m_frame->document())
+ if (!isSupported() || !m_frame)
return String();
return m_command->value(m_frame.get(), triggeringEvent);
}
diff --git a/WebCore/editing/FormatBlockCommand.cpp b/WebCore/editing/FormatBlockCommand.cpp
index 6a4ee7e..88169be 100644
--- a/WebCore/editing/FormatBlockCommand.cpp
+++ b/WebCore/editing/FormatBlockCommand.cpp
@@ -63,7 +63,7 @@ bool FormatBlockCommand::modifyRange()
setEndingSelection(visibleEnd);
doApply();
visibleEnd = endingSelection().visibleEnd();
- setEndingSelection(Selection(visibleStart.deepEquivalent(), visibleEnd.deepEquivalent(), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(visibleStart.deepEquivalent(), visibleEnd.deepEquivalent(), DOWNSTREAM));
return true;
}
@@ -87,7 +87,7 @@ void FormatBlockCommand::doApply()
// margin/padding, but not others. We should make the gap painting more consistent and
// then use a left margin/padding rule here.
if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd))
- setEndingSelection(Selection(visibleStart, visibleEnd.previous(true)));
+ setEndingSelection(VisibleSelection(visibleStart, visibleEnd.previous(true)));
if (endingSelection().isRange() && modifyRange())
return;
diff --git a/WebCore/editing/IndentOutdentCommand.cpp b/WebCore/editing/IndentOutdentCommand.cpp
index 242aeb0..9444b11 100644
--- a/WebCore/editing/IndentOutdentCommand.cpp
+++ b/WebCore/editing/IndentOutdentCommand.cpp
@@ -109,7 +109,7 @@ PassRefPtr<Element> IndentOutdentCommand::prepareBlockquoteLevelForInsertion(Vis
void IndentOutdentCommand::indentRegion()
{
- Selection selection = selectionForParagraphIteration(endingSelection());
+ VisibleSelection selection = selectionForParagraphIteration(endingSelection());
VisiblePosition startOfSelection = selection.visibleStart();
VisiblePosition endOfSelection = selection.visibleEnd();
int startIndex = indexForVisiblePosition(startOfSelection);
@@ -126,7 +126,7 @@ void IndentOutdentCommand::indentRegion()
insertNodeAt(blockquote, start);
RefPtr<Element> placeholder = createBreakElement(document());
appendNode(placeholder, blockquote);
- setEndingSelection(Selection(Position(placeholder.get(), 0), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(Position(placeholder.get(), 0), DOWNSTREAM));
return;
}
@@ -151,7 +151,7 @@ void IndentOutdentCommand::indentRegion()
appendNode(placeholder, listItem);
} else {
// Clone the list element, insert it before the current paragraph, and move the paragraph into it.
- RefPtr<Element> clonedList = listNode->cloneElement();
+ RefPtr<Element> clonedList = listNode->cloneElementWithoutChildren();
insertNodeBefore(clonedList, enclosingListChild(endOfCurrentParagraph.deepEquivalent().node()));
appendNode(listItem, clonedList);
appendNode(placeholder, listItem);
@@ -191,7 +191,7 @@ void IndentOutdentCommand::indentRegion()
RefPtr<Range> startRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), startIndex, 0, true);
RefPtr<Range> endRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), endIndex, 0, true);
if (startRange && endRange)
- setEndingSelection(Selection(startRange->startPosition(), endRange->startPosition(), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(startRange->startPosition(), endRange->startPosition(), DOWNSTREAM));
}
void IndentOutdentCommand::outdentParagraph()
@@ -205,11 +205,11 @@ void IndentOutdentCommand::outdentParagraph()
// Use InsertListCommand to remove the selection from the list
if (enclosingNode->hasTagName(olTag)) {
- applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::OrderedList, ""));
+ applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::OrderedList));
return;
}
if (enclosingNode->hasTagName(ulTag)) {
- applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::UnorderedList, ""));
+ applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::UnorderedList));
return;
}
@@ -262,13 +262,13 @@ void IndentOutdentCommand::outdentRegion()
while (endOfCurrentParagraph != endAfterSelection) {
VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
if (endOfCurrentParagraph == endOfLastParagraph)
- setEndingSelection(Selection(originalSelectionEnd, DOWNSTREAM));
+ setEndingSelection(VisibleSelection(originalSelectionEnd, DOWNSTREAM));
else
setEndingSelection(endOfCurrentParagraph);
outdentParagraph();
endOfCurrentParagraph = endOfNextParagraph;
}
- setEndingSelection(Selection(originalSelectionStart, endingSelection().end(), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(originalSelectionStart, endingSelection().end(), DOWNSTREAM));
}
void IndentOutdentCommand::doApply()
@@ -290,7 +290,7 @@ void IndentOutdentCommand::doApply()
// margin/padding, but not others. We should make the gap painting more consistent and
// then use a left margin/padding rule here.
if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd))
- setEndingSelection(Selection(visibleStart, visibleEnd.previous(true)));
+ setEndingSelection(VisibleSelection(visibleStart, visibleEnd.previous(true)));
if (m_typeOfAction == Indent)
indentRegion();
diff --git a/WebCore/editing/InsertLineBreakCommand.cpp b/WebCore/editing/InsertLineBreakCommand.cpp
index 2ec71af..a5c588d 100644
--- a/WebCore/editing/InsertLineBreakCommand.cpp
+++ b/WebCore/editing/InsertLineBreakCommand.cpp
@@ -30,11 +30,12 @@
#include "Document.h"
#include "Element.h"
#include "Frame.h"
+#include "HTMLNames.h"
+#include "Range.h"
+#include "RenderObject.h"
#include "Text.h"
#include "VisiblePosition.h"
-#include "Range.h"
#include "htmlediting.h"
-#include "HTMLNames.h"
#include "visible_units.h"
namespace WebCore {
@@ -88,7 +89,7 @@ bool InsertLineBreakCommand::shouldUseBreakElement(const Position& insertionPos)
void InsertLineBreakCommand::doApply()
{
deleteSelection();
- Selection selection = endingSelection();
+ VisibleSelection selection = endingSelection();
if (selection.isNone())
return;
@@ -116,30 +117,27 @@ void InsertLineBreakCommand::doApply()
insertNodeBefore(nodeToInsert->cloneNode(false), nodeToInsert);
VisiblePosition endingPosition(Position(nodeToInsert.get(), 0));
- setEndingSelection(Selection(endingPosition));
- } else if (pos.offset() <= caretMinOffset(pos.node())) {
+ setEndingSelection(VisibleSelection(endingPosition));
+ } else if (pos.m_offset <= caretMinOffset(pos.node())) {
insertNodeAt(nodeToInsert.get(), pos);
// Insert an extra br or '\n' if the just inserted one collapsed.
if (!isStartOfParagraph(VisiblePosition(Position(nodeToInsert.get(), 0))))
insertNodeBefore(nodeToInsert->cloneNode(false).get(), nodeToInsert.get());
- setEndingSelection(Selection(positionAfterNode(nodeToInsert.get()), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(positionAfterNode(nodeToInsert.get()), DOWNSTREAM));
// If we're inserting after all of the rendered text in a text node, or into a non-text node,
// a simple insertion is sufficient.
- } else if (pos.offset() >= caretMaxOffset(pos.node()) || !pos.node()->isTextNode()) {
+ } else if (pos.m_offset >= caretMaxOffset(pos.node()) || !pos.node()->isTextNode()) {
insertNodeAt(nodeToInsert.get(), pos);
- setEndingSelection(Selection(positionAfterNode(nodeToInsert.get()), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(positionAfterNode(nodeToInsert.get()), DOWNSTREAM));
} else {
// Split a text node
ASSERT(pos.node()->isTextNode());
// Do the split
- ExceptionCode ec = 0;
Text* textNode = static_cast<Text*>(pos.node());
- RefPtr<Text> textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), ec));
- deleteTextFromNode(textNode, 0, pos.offset());
- insertNodeBefore(textBeforeNode, textNode);
+ splitTextNode(textNode, pos.m_offset);
insertNodeBefore(nodeToInsert, textNode);
Position endingPosition = Position(textNode, 0);
@@ -160,7 +158,7 @@ void InsertLineBreakCommand::doApply()
}
}
- setEndingSelection(Selection(endingPosition, DOWNSTREAM));
+ setEndingSelection(VisibleSelection(endingPosition, DOWNSTREAM));
}
// Handle the case where there is a typing style.
@@ -172,10 +170,9 @@ void InsertLineBreakCommand::doApply()
// leaves and then comes back, new input will have the right style.
// FIXME: We shouldn't always apply the typing style to the line break here,
// see <rdar://problem/5794462>.
- applyStyle(typingStyle, Position(nodeToInsert.get(), 0),
- Position(nodeToInsert.get(), maxDeepOffset(nodeToInsert.get())));
+ applyStyle(typingStyle, firstDeepEditingPositionForNode(nodeToInsert.get()), lastDeepEditingPositionForNode(nodeToInsert.get()));
// Even though this applyStyle operates on a Range, it still sets an endingSelection().
- // It tries to set a Selection around the content it operated on. So, that Selection
+ // It tries to set a VisibleSelection around the content it operated on. So, that VisibleSelection
// will either (a) select the line break we inserted, or it will (b) be a caret just
// before the line break (if the line break is at the end of a block it isn't selectable).
// So, this next call sets the endingSelection() to a caret just after the line break
diff --git a/WebCore/editing/InsertListCommand.cpp b/WebCore/editing/InsertListCommand.cpp
index 20c63b8..aa48761 100644
--- a/WebCore/editing/InsertListCommand.cpp
+++ b/WebCore/editing/InsertListCommand.cpp
@@ -39,7 +39,7 @@ using namespace HTMLNames;
PassRefPtr<HTMLElement> InsertListCommand::insertList(Document* document, Type type)
{
- RefPtr<InsertListCommand> insertCommand = new InsertListCommand(document, type, "");
+ RefPtr<InsertListCommand> insertCommand = new InsertListCommand(document, type);
insertCommand->apply();
return insertCommand->m_listElement;
}
@@ -54,14 +54,14 @@ HTMLElement* InsertListCommand::fixOrphanedListChild(Node* node)
return listElement.get();
}
-InsertListCommand::InsertListCommand(Document* document, Type type, const String& id)
- : CompositeEditCommand(document), m_type(type), m_id(id), m_forceCreateList(false)
+InsertListCommand::InsertListCommand(Document* document, Type type)
+ : CompositeEditCommand(document), m_type(type), m_forceCreateList(false)
{
}
bool InsertListCommand::modifyRange()
{
- Selection selection = selectionForParagraphIteration(endingSelection());
+ VisibleSelection selection = selectionForParagraphIteration(endingSelection());
ASSERT(selection.isRange());
VisiblePosition startOfSelection = selection.visibleStart();
VisiblePosition endOfSelection = selection.visibleEnd();
@@ -99,7 +99,7 @@ bool InsertListCommand::modifyRange()
doApply();
// Fetch the end of the selection, for the reason mentioned above.
endOfSelection = endingSelection().visibleEnd();
- setEndingSelection(Selection(startOfSelection, endOfSelection));
+ setEndingSelection(VisibleSelection(startOfSelection, endOfSelection));
m_forceCreateList = false;
return true;
}
@@ -123,7 +123,7 @@ void InsertListCommand::doApply()
// margin/padding, but not others. We should make the gap painting more consistent and
// then use a left margin/padding rule here.
if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd))
- setEndingSelection(Selection(visibleStart, visibleEnd.previous(true)));
+ setEndingSelection(VisibleSelection(visibleStart, visibleEnd.previous(true)));
if (endingSelection().isRange() && modifyRange())
return;
@@ -148,8 +148,8 @@ void InsertListCommand::doApply()
VisiblePosition start;
VisiblePosition end;
if (listChildNode->hasTagName(liTag)) {
- start = VisiblePosition(Position(listChildNode, 0));
- end = VisiblePosition(Position(listChildNode, maxDeepOffset(listChildNode)));
+ start = firstDeepEditingPositionForNode(listChildNode);
+ end = lastDeepEditingPositionForNode(listChildNode);
nextListChild = listChildNode->nextSibling();
previousListChild = listChildNode->previousSibling();
} else {
@@ -230,8 +230,6 @@ void InsertListCommand::doApply()
// Create the list.
RefPtr<HTMLElement> listElement = m_type == OrderedList ? createOrderedListElement(document()) : createUnorderedListElement(document());
m_listElement = listElement;
- if (!m_id.isEmpty())
- listElement->setId(m_id);
appendNode(listItemElement, listElement);
if (start == end && isBlock(start.deepEquivalent().node())) {
diff --git a/WebCore/editing/InsertListCommand.h b/WebCore/editing/InsertListCommand.h
index b39f2b5..ecdd2cf 100644
--- a/WebCore/editing/InsertListCommand.h
+++ b/WebCore/editing/InsertListCommand.h
@@ -36,9 +36,9 @@ class InsertListCommand : public CompositeEditCommand {
public:
enum Type { OrderedList, UnorderedList };
- static PassRefPtr<InsertListCommand> create(Document* document, Type listType, const String& listID)
+ static PassRefPtr<InsertListCommand> create(Document* document, Type listType)
{
- return adoptRef(new InsertListCommand(document, listType, listID));
+ return adoptRef(new InsertListCommand(document, listType));
}
static PassRefPtr<HTMLElement> insertList(Document*, Type);
@@ -46,7 +46,7 @@ public:
virtual bool preservesTypingStyle() const { return true; }
private:
- InsertListCommand(Document*, Type, const String&);
+ InsertListCommand(Document*, Type);
virtual void doApply();
virtual EditAction editingAction() const { return EditActionInsertList; }
@@ -55,7 +55,6 @@ private:
bool modifyRange();
RefPtr<HTMLElement> m_listElement;
Type m_type;
- String m_id;
bool m_forceCreateList;
};
diff --git a/WebCore/editing/InsertParagraphSeparatorCommand.cpp b/WebCore/editing/InsertParagraphSeparatorCommand.cpp
index 30e006e..0e9c291 100644
--- a/WebCore/editing/InsertParagraphSeparatorCommand.cpp
+++ b/WebCore/editing/InsertParagraphSeparatorCommand.cpp
@@ -106,42 +106,42 @@ void InsertParagraphSeparatorCommand::doApply()
if (endingSelection().isNone())
return;
- Position pos = endingSelection().start();
+ Position insertionPosition = endingSelection().start();
EAffinity affinity = endingSelection().affinity();
// Delete the current selection.
if (endingSelection().isRange()) {
- calculateStyleBeforeInsertion(pos);
+ calculateStyleBeforeInsertion(insertionPosition);
deleteSelection(false, true);
- pos = endingSelection().start();
+ insertionPosition = endingSelection().start();
affinity = endingSelection().affinity();
}
// FIXME: The rangeCompliantEquivalent conversion needs to be moved into enclosingBlock.
- Node* startBlockNode = enclosingBlock(rangeCompliantEquivalent(pos).node());
- Position canonicalPos = VisiblePosition(pos).deepEquivalent();
+ Node* startBlockNode = enclosingBlock(rangeCompliantEquivalent(insertionPosition).node());
+ Position canonicalPos = VisiblePosition(insertionPosition).deepEquivalent();
Element* startBlock = static_cast<Element*>(startBlockNode);
if (!startBlockNode
|| !startBlockNode->isElementNode()
|| !startBlock->parentNode()
|| isTableCell(startBlock)
|| startBlock->hasTagName(formTag)
- || canonicalPos.node()->renderer() && canonicalPos.node()->renderer()->isTable()
+ || (canonicalPos.node()->renderer() && canonicalPos.node()->renderer()->isTable())
|| canonicalPos.node()->hasTagName(hrTag)) {
applyCommandToComposite(InsertLineBreakCommand::create(document()));
return;
}
// Use the leftmost candidate.
- pos = pos.upstream();
- if (!pos.isCandidate())
- pos = pos.downstream();
+ insertionPosition = insertionPosition.upstream();
+ if (!insertionPosition.isCandidate())
+ insertionPosition = insertionPosition.downstream();
// Adjust the insertion position after the delete
- pos = positionAvoidingSpecialElementBoundary(pos);
- VisiblePosition visiblePos(pos, affinity);
- calculateStyleBeforeInsertion(pos);
+ insertionPosition = positionAvoidingSpecialElementBoundary(insertionPosition);
+ VisiblePosition visiblePos(insertionPosition, affinity);
+ calculateStyleBeforeInsertion(insertionPosition);
//---------------------------------------------------------------------
// Handle special case of typing return on an empty list item
@@ -150,10 +150,6 @@ void InsertParagraphSeparatorCommand::doApply()
//---------------------------------------------------------------------
// Prepare for more general cases.
- // FIXME: We shouldn't peel off the node here because then we lose track of
- // the fact that it's the node that belongs to an editing position and
- // not a rangeCompliantEquivalent.
- Node *startNode = pos.node();
bool isFirstInBlock = isStartOfBlock(visiblePos);
bool isLastInBlock = isEndOfBlock(visiblePos);
@@ -167,7 +163,7 @@ void InsertParagraphSeparatorCommand::doApply()
} else if (shouldUseDefaultParagraphElement(startBlock))
blockToInsert = createDefaultParagraphElement(document());
else
- blockToInsert = startBlock->cloneElement();
+ blockToInsert = startBlock->cloneElementWithoutChildren();
//---------------------------------------------------------------------
// Handle case when position is in the last visible position in its block,
@@ -186,7 +182,7 @@ void InsertParagraphSeparatorCommand::doApply()
insertNodeAfter(blockToInsert, startBlock);
appendBlockPlaceholder(blockToInsert);
- setEndingSelection(Selection(Position(blockToInsert.get(), 0), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(Position(blockToInsert.get(), 0), DOWNSTREAM));
applyStyleAfterInsertion(startBlock);
return;
}
@@ -198,20 +194,20 @@ void InsertParagraphSeparatorCommand::doApply()
Node *refNode;
if (isFirstInBlock && !nestNewBlock)
refNode = startBlock;
- else if (pos.node() == startBlock && nestNewBlock) {
- refNode = startBlock->childNode(pos.offset());
+ else if (insertionPosition.node() == startBlock && nestNewBlock) {
+ refNode = startBlock->childNode(insertionPosition.m_offset);
ASSERT(refNode); // must be true or we'd be in the end of block case
} else
- refNode = pos.node();
+ refNode = insertionPosition.node();
// find ending selection position easily before inserting the paragraph
- pos = pos.downstream();
+ insertionPosition = insertionPosition.downstream();
insertNodeBefore(blockToInsert, refNode);
appendBlockPlaceholder(blockToInsert.get());
- setEndingSelection(Selection(Position(blockToInsert.get(), 0), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(Position(blockToInsert.get(), 0), DOWNSTREAM));
applyStyleAfterInsertion(startBlock);
- setEndingSelection(Selection(pos, DOWNSTREAM));
+ setEndingSelection(VisibleSelection(insertionPosition, DOWNSTREAM));
return;
}
@@ -224,41 +220,45 @@ void InsertParagraphSeparatorCommand::doApply()
// content will move down a line.
if (isStartOfParagraph(visiblePos)) {
RefPtr<Element> br = createBreakElement(document());
- insertNodeAt(br.get(), pos);
- pos = positionAfterNode(br.get());
+ insertNodeAt(br.get(), insertionPosition);
+ insertionPosition = positionAfterNode(br.get());
}
// Move downstream. Typing style code will take care of carrying along the
// style of the upstream position.
- pos = pos.downstream();
- startNode = pos.node();
+ insertionPosition = insertionPosition.downstream();
+
+ // At this point, the insertionPosition's node could be a container, and we want to make sure we include
+ // all of the correct nodes when building the ancestor list. So this needs to be the deepest representation of the position
+ // before we walk the DOM tree.
+ insertionPosition = VisiblePosition(insertionPosition).deepEquivalent();
// Build up list of ancestors in between the start node and the start block.
Vector<Element*> ancestors;
- if (startNode != startBlock) {
- for (Element* n = startNode->parentElement(); n && n != startBlock; n = n->parentElement())
+ if (insertionPosition.node() != startBlock) {
+ for (Element* n = insertionPosition.node()->parentElement(); n && n != startBlock; n = n->parentElement())
ancestors.append(n);
}
// Make sure we do not cause a rendered space to become unrendered.
// FIXME: We need the affinity for pos, but pos.downstream() does not give it
- Position leadingWhitespace = pos.leadingWhitespacePosition(VP_DEFAULT_AFFINITY);
+ Position leadingWhitespace = insertionPosition.leadingWhitespacePosition(VP_DEFAULT_AFFINITY);
// FIXME: leadingWhitespacePosition is returning the position before preserved newlines for positions
// after the preserved newline, causing the newline to be turned into a nbsp.
if (leadingWhitespace.isNotNull()) {
Text* textNode = static_cast<Text*>(leadingWhitespace.node());
ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
- replaceTextInNode(textNode, leadingWhitespace.offset(), 1, nonBreakingSpaceString());
+ replaceTextInNode(textNode, leadingWhitespace.m_offset, 1, nonBreakingSpaceString());
}
// Split at pos if in the middle of a text node.
- if (startNode->isTextNode()) {
- Text *textNode = static_cast<Text *>(startNode);
- bool atEnd = (unsigned)pos.offset() >= textNode->length();
- if (pos.offset() > 0 && !atEnd) {
- splitTextNode(textNode, pos.offset());
- pos = Position(startNode, 0);
- visiblePos = VisiblePosition(pos);
+ if (insertionPosition.node()->isTextNode()) {
+ Text* textNode = static_cast<Text*>(insertionPosition.node());
+ bool atEnd = (unsigned)insertionPosition.m_offset >= textNode->length();
+ if (insertionPosition.m_offset > 0 && !atEnd) {
+ splitTextNode(textNode, insertionPosition.m_offset);
+ insertionPosition.m_offset = 0;
+ visiblePos = VisiblePosition(insertionPosition);
splitText = true;
}
}
@@ -274,7 +274,7 @@ void InsertParagraphSeparatorCommand::doApply()
// Make clones of ancestors in between the start node and the start block.
RefPtr<Element> parent = blockToInsert;
for (size_t i = ancestors.size(); i != 0; --i) {
- RefPtr<Element> child = ancestors[i - 1]->cloneElement(); // shallow clone
+ RefPtr<Element> child = ancestors[i - 1]->cloneElementWithoutChildren();
appendNode(child, parent);
parent = child.release();
}
@@ -286,10 +286,10 @@ void InsertParagraphSeparatorCommand::doApply()
appendNode(createBreakElement(document()).get(), blockToInsert.get());
// Move the start node and the siblings of the start node.
- if (startNode != startBlock) {
- Node *n = startNode;
- if (pos.offset() >= caretMaxOffset(startNode))
- n = startNode->nextSibling();
+ if (insertionPosition.node() != startBlock) {
+ Node* n = insertionPosition.node();
+ if (insertionPosition.m_offset >= caretMaxOffset(n))
+ n = n->nextSibling();
while (n && n != blockToInsert) {
Node *next = n->nextSibling();
@@ -320,18 +320,17 @@ void InsertParagraphSeparatorCommand::doApply()
// Handle whitespace that occurs after the split
if (splitText) {
updateLayout();
- pos = Position(startNode, 0);
- if (!pos.isRenderedCharacter()) {
+ insertionPosition = Position(insertionPosition.node(), 0);
+ if (!insertionPosition.isRenderedCharacter()) {
// Clear out all whitespace and insert one non-breaking space
- ASSERT(startNode);
- ASSERT(startNode->isTextNode());
- ASSERT(!startNode->renderer() || startNode->renderer()->style()->collapseWhiteSpace());
- deleteInsignificantTextDownstream(pos);
- insertTextIntoNode(static_cast<Text*>(startNode), 0, nonBreakingSpaceString());
+ ASSERT(insertionPosition.node()->isTextNode());
+ ASSERT(!insertionPosition.node()->renderer() || insertionPosition.node()->renderer()->style()->collapseWhiteSpace());
+ deleteInsignificantTextDownstream(insertionPosition);
+ insertTextIntoNode(static_cast<Text*>(insertionPosition.node()), 0, nonBreakingSpaceString());
}
}
- setEndingSelection(Selection(Position(blockToInsert.get(), 0), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(Position(blockToInsert.get(), 0), DOWNSTREAM));
applyStyleAfterInsertion(startBlock);
}
diff --git a/WebCore/editing/InsertTextCommand.cpp b/WebCore/editing/InsertTextCommand.cpp
index 46e9a94..52da5d0 100644
--- a/WebCore/editing/InsertTextCommand.cpp
+++ b/WebCore/editing/InsertTextCommand.cpp
@@ -89,19 +89,19 @@ bool InsertTextCommand::performTrivialReplace(const String& text, bool selectIns
if (start.node() != end.node() || !start.node()->isTextNode() || isTabSpanTextNode(start.node()))
return false;
- replaceTextInNode(static_cast<Text*>(start.node()), start.offset(), end.offset() - start.offset(), text);
+ replaceTextInNode(static_cast<Text*>(start.node()), start.m_offset, end.m_offset - start.m_offset, text);
- Position endPosition(start.node(), start.offset() + text.length());
+ Position endPosition(start.node(), start.m_offset + text.length());
// We could have inserted a part of composed character sequence,
// so we are basically treating ending selection as a range to avoid validation.
// <http://bugs.webkit.org/show_bug.cgi?id=15781>
- Selection forcedEndingSelection;
+ VisibleSelection forcedEndingSelection;
forcedEndingSelection.setWithoutValidation(start, endPosition);
setEndingSelection(forcedEndingSelection);
if (!selectInsertedText)
- setEndingSelection(Selection(endingSelection().visibleEnd()));
+ setEndingSelection(VisibleSelection(endingSelection().visibleEnd()));
return true;
}
@@ -155,7 +155,7 @@ void InsertTextCommand::input(const String& originalText, bool selectInsertedTex
startPosition = prepareForTextInsertion(startPosition);
removePlaceholderAt(VisiblePosition(startPosition));
Text *textNode = static_cast<Text *>(startPosition.node());
- int offset = startPosition.offset();
+ int offset = startPosition.m_offset;
insertTextIntoNode(textNode, offset, text);
endPosition = Position(textNode, offset + text.length());
@@ -172,7 +172,7 @@ void InsertTextCommand::input(const String& originalText, bool selectInsertedTex
// We could have inserted a part of composed character sequence,
// so we are basically treating ending selection as a range to avoid validation.
// <http://bugs.webkit.org/show_bug.cgi?id=15781>
- Selection forcedEndingSelection;
+ VisibleSelection forcedEndingSelection;
forcedEndingSelection.setWithoutValidation(startPosition, endPosition);
setEndingSelection(forcedEndingSelection);
@@ -199,7 +199,7 @@ void InsertTextCommand::input(const String& originalText, bool selectInsertedTex
applyStyle(typingStyle);
if (!selectInsertedText)
- setEndingSelection(Selection(endingSelection().end(), endingSelection().affinity()));
+ setEndingSelection(VisibleSelection(endingSelection().end(), endingSelection().affinity()));
}
Position InsertTextCommand::insertTab(const Position& pos)
@@ -207,7 +207,7 @@ Position InsertTextCommand::insertTab(const Position& pos)
Position insertPos = VisiblePosition(pos, DOWNSTREAM).deepEquivalent();
Node *node = insertPos.node();
- unsigned int offset = insertPos.offset();
+ unsigned int offset = insertPos.m_offset;
// keep tabs coalesced in tab span
if (isTabSpanTextNode(node)) {
diff --git a/WebCore/editing/ModifySelectionListLevel.cpp b/WebCore/editing/ModifySelectionListLevel.cpp
index 9bc73c6..5ea658c 100644
--- a/WebCore/editing/ModifySelectionListLevel.cpp
+++ b/WebCore/editing/ModifySelectionListLevel.cpp
@@ -46,7 +46,7 @@ bool ModifySelectionListLevelCommand::preservesTypingStyle() const
}
// This needs to be static so it can be called by canIncreaseSelectionListLevel and canDecreaseSelectionListLevel
-static bool getStartEndListChildren(const Selection& selection, Node*& start, Node*& end)
+static bool getStartEndListChildren(const VisibleSelection& selection, Node*& start, Node*& end)
{
if (selection.isNone())
return false;
@@ -79,8 +79,8 @@ static bool getStartEndListChildren(const Selection& selection, Node*& start, No
// if the selection ends on a list item with a sublist, include the entire sublist
if (endListChild->renderer()->isListItem()) {
RenderObject* r = endListChild->renderer()->nextSibling();
- if (r && isListElement(r->element()))
- endListChild = r->element();
+ if (r && isListElement(r->node()))
+ endListChild = r->node();
}
start = startListChild;
@@ -141,7 +141,7 @@ IncreaseSelectionListLevelCommand::IncreaseSelectionListLevelCommand(Document* d
}
// This needs to be static so it can be called by canIncreaseSelectionListLevel
-static bool canIncreaseListLevel(const Selection& selection, Node*& start, Node*& end)
+static bool canIncreaseListLevel(const VisibleSelection& selection, Node*& start, Node*& end)
{
if (!getStartEndListChildren(selection, start, end))
return false;
@@ -175,7 +175,7 @@ void IncreaseSelectionListLevelCommand::doApply()
if (!canIncreaseListLevel(endingSelection(), startListChild, endListChild))
return;
- Node* previousItem = startListChild->renderer()->previousSibling()->element();
+ Node* previousItem = startListChild->renderer()->previousSibling()->node();
if (isListElement(previousItem)) {
// move nodes up into preceding list
appendSiblingNodeRange(startListChild, endListChild, static_cast<Element*>(previousItem));
@@ -187,7 +187,7 @@ void IncreaseSelectionListLevelCommand::doApply()
case InheritedListType:
newParent = startListChild->parentElement();
if (newParent)
- newParent = newParent->cloneElement();
+ newParent = newParent->cloneElementWithoutChildren();
break;
case OrderedList:
newParent = createOrderedListElement(document());
@@ -239,7 +239,7 @@ DecreaseSelectionListLevelCommand::DecreaseSelectionListLevelCommand(Document* d
}
// This needs to be static so it can be called by canDecreaseSelectionListLevel
-static bool canDecreaseListLevel(const Selection& selection, Node*& start, Node*& end)
+static bool canDecreaseListLevel(const VisibleSelection& selection, Node*& start, Node*& end)
{
if (!getStartEndListChildren(selection, start, end))
return false;
@@ -258,8 +258,8 @@ void DecreaseSelectionListLevelCommand::doApply()
if (!canDecreaseListLevel(endingSelection(), startListChild, endListChild))
return;
- Node* previousItem = startListChild->renderer()->previousSibling() ? startListChild->renderer()->previousSibling()->element() : 0;
- Node* nextItem = endListChild->renderer()->nextSibling() ? endListChild->renderer()->nextSibling()->element() : 0;
+ Node* previousItem = startListChild->renderer()->previousSibling() ? startListChild->renderer()->previousSibling()->node() : 0;
+ Node* nextItem = endListChild->renderer()->nextSibling() ? endListChild->renderer()->nextSibling()->node() : 0;
Element* listNode = startListChild->parentElement();
if (!previousItem) {
diff --git a/WebCore/editing/MoveSelectionCommand.cpp b/WebCore/editing/MoveSelectionCommand.cpp
index 08587cb..2c656e7 100644
--- a/WebCore/editing/MoveSelectionCommand.cpp
+++ b/WebCore/editing/MoveSelectionCommand.cpp
@@ -39,7 +39,7 @@ MoveSelectionCommand::MoveSelectionCommand(PassRefPtr<DocumentFragment> fragment
void MoveSelectionCommand::doApply()
{
- Selection selection = endingSelection();
+ VisibleSelection selection = endingSelection();
ASSERT(selection.isRange());
Position pos = m_position;
@@ -48,14 +48,14 @@ void MoveSelectionCommand::doApply()
// Update the position otherwise it may become invalid after the selection is deleted.
Node *positionNode = m_position.node();
- int positionOffset = m_position.offset();
+ int positionOffset = m_position.m_offset;
Position selectionEnd = selection.end();
- int selectionEndOffset = selectionEnd.offset();
+ int selectionEndOffset = selectionEnd.m_offset;
if (selectionEnd.node() == positionNode && selectionEndOffset < positionOffset) {
positionOffset -= selectionEndOffset;
Position selectionStart = selection.start();
if (selectionStart.node() == positionNode) {
- positionOffset += selectionStart.offset();
+ positionOffset += selectionStart.m_offset;
}
pos = Position(positionNode, positionOffset);
}
@@ -69,7 +69,11 @@ void MoveSelectionCommand::doApply()
if (!pos.node()->inDocument())
pos = endingSelection().start();
- setEndingSelection(Selection(pos, endingSelection().affinity()));
+ setEndingSelection(VisibleSelection(pos, endingSelection().affinity()));
+ if (!positionNode->inDocument()) {
+ // Document was modified out from under us.
+ return;
+ }
applyCommandToComposite(ReplaceSelectionCommand::create(positionNode->document(), m_fragment, true, m_smartMove));
}
diff --git a/WebCore/editing/RemoveFormatCommand.cpp b/WebCore/editing/RemoveFormatCommand.cpp
index 80e0ee7..609ab0e 100644
--- a/WebCore/editing/RemoveFormatCommand.cpp
+++ b/WebCore/editing/RemoveFormatCommand.cpp
@@ -30,7 +30,7 @@
#include "Editor.h"
#include "Frame.h"
#include "HTMLNames.h"
-#include "Selection.h"
+#include "VisibleSelection.h"
#include "SelectionController.h"
#include "TextIterator.h"
#include "TypingCommand.h"
@@ -49,7 +49,7 @@ void RemoveFormatCommand::doApply()
Frame* frame = document()->frame();
// Make a plain text string from the selection to remove formatting like tables and lists.
- String string = plainText(frame->selection()->selection().toRange().get());
+ String string = plainText(frame->selection()->selection().toNormalizedRange().get());
// Get the default style for this editable root, it's the style that we'll give the
// content that we're operating on.
diff --git a/WebCore/editing/ReplaceSelectionCommand.cpp b/WebCore/editing/ReplaceSelectionCommand.cpp
index 15fc736..09ad985 100644
--- a/WebCore/editing/ReplaceSelectionCommand.cpp
+++ b/WebCore/editing/ReplaceSelectionCommand.cpp
@@ -61,7 +61,7 @@ enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment };
class ReplacementFragment : Noncopyable {
public:
- ReplacementFragment(Document*, DocumentFragment*, bool matchStyle, const Selection&);
+ ReplacementFragment(Document*, DocumentFragment*, bool matchStyle, const VisibleSelection&);
Node* firstChild() const;
Node* lastChild() const;
@@ -103,7 +103,7 @@ static bool isInterchangeConvertedSpaceSpan(const Node *node)
static_cast<const HTMLElement *>(node)->getAttribute(classAttr) == convertedSpaceSpanClassString;
}
-ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* fragment, bool matchStyle, const Selection& selection)
+ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* fragment, bool matchStyle, const VisibleSelection& selection)
: m_document(document),
m_fragment(fragment),
m_matchStyle(matchStyle),
@@ -126,8 +126,7 @@ ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* f
if (!editableRoot->inlineEventListenerForType(eventNames().webkitBeforeTextInsertedEvent) &&
// FIXME: Remove these checks once textareas and textfields actually register an event handler.
- !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextField()) &&
- !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextArea()) &&
+ !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextControl()) &&
editableRoot->isContentRichlyEditable()) {
removeInterchangeNodes(m_fragment.get());
return;
@@ -136,7 +135,7 @@ ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* f
Node* styleNode = selection.base().node();
RefPtr<Node> holder = insertFragmentForTestRendering(styleNode);
- RefPtr<Range> range = Selection::selectionFromContentsOfNode(holder.get()).toRange();
+ RefPtr<Range> range = VisibleSelection::selectionFromContentsOfNode(holder.get()).toNormalizedRange();
String text = plainText(range.get());
// Give the root a chance to change the text.
RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
@@ -147,7 +146,7 @@ ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* f
restoreTestRenderingNodesToFragment(holder.get());
removeNode(holder);
- m_fragment = createFragmentFromText(selection.toRange().get(), evt->text());
+ m_fragment = createFragmentFromText(selection.toNormalizedRange().get(), evt->text());
if (!m_fragment->firstChild())
return;
holder = insertFragmentForTestRendering(styleNode);
@@ -406,6 +405,23 @@ void ReplaceSelectionCommand::removeNodeAndPruneAncestors(Node* node)
m_firstNodeInserted = m_lastLeafInserted && m_lastLeafInserted->inDocument() ? afterFirst : 0;
}
+static bool isHeaderElement(Node* a)
+{
+ if (!a)
+ return false;
+
+ return a->hasTagName(h1Tag) ||
+ a->hasTagName(h2Tag) ||
+ a->hasTagName(h3Tag) ||
+ a->hasTagName(h4Tag) ||
+ a->hasTagName(h5Tag);
+}
+
+static bool haveSameTagName(Node* a, Node* b)
+{
+ return a && b && a->isElementNode() && b->isElementNode() && static_cast<Element*>(a)->tagName() == static_cast<Element*>(b)->tagName();
+}
+
bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const VisiblePosition& destination)
{
if (source.isNull() || destination.isNull())
@@ -414,10 +430,12 @@ bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const V
Node* sourceNode = source.deepEquivalent().node();
Node* destinationNode = destination.deepEquivalent().node();
Node* sourceBlock = enclosingBlock(sourceNode);
+ Node* destinationBlock = enclosingBlock(destinationNode);
return !enclosingNodeOfType(source.deepEquivalent(), &isMailPasteAsQuotationNode) &&
sourceBlock && (!sourceBlock->hasTagName(blockquoteTag) || isMailBlockquote(sourceBlock)) &&
enclosingListChild(sourceBlock) == enclosingListChild(destinationNode) &&
enclosingTableCell(source.deepEquivalent()) == enclosingTableCell(destination.deepEquivalent()) &&
+ (!isHeaderElement(sourceBlock) || haveSameTagName(sourceBlock, destinationBlock)) &&
// Don't merge to or from a position before or after a block because it would
// be a no-op and cause infinite recursion.
!isBlock(sourceNode) && !isBlock(destinationNode);
@@ -491,10 +509,11 @@ void ReplaceSelectionCommand::handlePasteAsQuotationNode()
VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent()
{
Node* lastNode = m_lastLeafInserted.get();
- Node* enclosingSelect = enclosingNodeWithTag(Position(lastNode, 0), selectTag);
+ // FIXME: Why is this hack here? What's special about <select> tags?
+ Node* enclosingSelect = enclosingNodeWithTag(firstDeepEditingPositionForNode(lastNode), selectTag);
if (enclosingSelect)
lastNode = enclosingSelect;
- return VisiblePosition(Position(lastNode, maxDeepOffset(lastNode)));
+ return lastDeepEditingPositionForNode(lastNode);
}
VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent()
@@ -509,8 +528,9 @@ static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const
{
Node* topNode = fragment.firstChild();
- // Handling this case is more complicated (see handleStyleSpans) and doesn't receive the optimization.
- if (isMailPasteAsQuotationNode(topNode))
+ // Handling the case where we are doing Paste as Quotation or pasting into quoted content is more complicated (see handleStyleSpans)
+ // and doesn't receive the optimization.
+ if (isMailPasteAsQuotationNode(topNode) || nearestMailBlockquote(topNode))
return false;
// Either there are no style spans in the fragment or a WebKit client has added content to the fragment
@@ -573,11 +593,12 @@ void ReplaceSelectionCommand::handleStyleSpans()
RefPtr<CSSMutableStyleDeclaration> sourceDocumentStyle = static_cast<HTMLElement*>(sourceDocumentStyleSpan)->getInlineStyleDecl()->copy();
Node* context = sourceDocumentStyleSpan->parentNode();
- // If Mail wraps the fragment with a Paste as Quotation blockquote, styles from that element are
- // allowed to override those from the source document, see <rdar://problem/4930986>.
- if (isMailPasteAsQuotationNode(context)) {
- RefPtr<CSSMutableStyleDeclaration> blockquoteStyle = computedStyle(context)->copyInheritableProperties();
- RefPtr<CSSMutableStyleDeclaration> parentStyle = computedStyle(context->parentNode())->copyInheritableProperties();
+ // If Mail wraps the fragment with a Paste as Quotation blockquote, or if you're pasting into a quoted region,
+ // styles from blockquoteNode are allowed to override those from the source document, see <rdar://problem/4930986> and <rdar://problem/5089327>.
+ Node* blockquoteNode = isMailPasteAsQuotationNode(context) ? context : nearestMailBlockquote(context);
+ if (blockquoteNode) {
+ RefPtr<CSSMutableStyleDeclaration> blockquoteStyle = computedStyle(blockquoteNode)->copyInheritableProperties();
+ RefPtr<CSSMutableStyleDeclaration> parentStyle = computedStyle(blockquoteNode->parentNode())->copyInheritableProperties();
parentStyle->diff(blockquoteStyle.get());
CSSMutableStyleDeclaration::const_iterator end = blockquoteStyle->end();
@@ -586,7 +607,7 @@ void ReplaceSelectionCommand::handleStyleSpans()
sourceDocumentStyle->removeProperty(property.id());
}
- context = context->parentNode();
+ context = blockquoteNode->parentNode();
}
RefPtr<CSSMutableStyleDeclaration> contextStyle = computedStyle(context)->copyInheritableProperties();
@@ -679,7 +700,7 @@ void ReplaceSelectionCommand::mergeEndIfNeeded()
void ReplaceSelectionCommand::doApply()
{
- Selection selection = endingSelection();
+ VisibleSelection selection = endingSelection();
ASSERT(selection.isCaretOrRange());
ASSERT(selection.start().node());
if (selection.isNone() || !selection.start().node())
@@ -704,9 +725,9 @@ void ReplaceSelectionCommand::doApply()
Position insertionPos = selection.start();
bool startIsInsideMailBlockquote = nearestMailBlockquote(insertionPos.node());
- if (selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !startIsInsideMailBlockquote ||
+ if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !startIsInsideMailBlockquote) ||
startBlock == currentRoot ||
- startBlock && startBlock->renderer() && startBlock->renderer()->isListItem() ||
+ (startBlock && startBlock->renderer() && startBlock->renderer()->isListItem()) ||
selectionIsPlainText)
m_preventNesting = false;
@@ -821,11 +842,20 @@ void ReplaceSelectionCommand::doApply()
fragment.removeNode(refNode);
insertNodeAtAndUpdateNodesInserted(refNode, insertionPos);
-
+
+ // Mutation events (bug 22634) may have already removed the inserted content
+ if (!refNode->inDocument())
+ return;
+
while (node) {
Node* next = node->nextSibling();
fragment.removeNode(node);
insertNodeAfterAndUpdateNodesInserted(node, refNode.get());
+
+ // Mutation events (bug 22634) may have already removed the inserted content
+ if (!node->inDocument())
+ return;
+
refNode = node;
node = next;
}
@@ -846,7 +876,7 @@ void ReplaceSelectionCommand::doApply()
// We inserted before the startBlock to prevent nesting, and the content before the startBlock wasn't in its own block and
// didn't have a br after it, so the inserted content ended up in the same paragraph.
- if (startBlock && insertionPos.node() == startBlock->parentNode() && (unsigned)insertionPos.offset() < startBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent))
+ if (startBlock && insertionPos.node() == startBlock->parentNode() && (unsigned)insertionPos.m_offset < startBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent))
insertNodeAt(createBreakElement(document()).get(), startOfInsertedContent.deepEquivalent());
Position lastPositionToSelect;
@@ -878,9 +908,13 @@ void ReplaceSelectionCommand::doApply()
// Insert a line break just after the inserted content to separate it from what
// comes after and prevent that from happening.
VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent();
- if (startOfParagraph(endOfInsertedContent) == startOfParagraphToMove)
+ if (startOfParagraph(endOfInsertedContent) == startOfParagraphToMove) {
insertNodeAt(createBreakElement(document()).get(), endOfInsertedContent.deepEquivalent());
-
+ // Mutation events (bug 22634) triggered by inserting the <br> might have removed the content we're about to move
+ if (!startOfParagraphToMove.deepEquivalent().node()->inDocument())
+ return;
+ }
+
// FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes. The nodes are
// only ever used to create positions where inserted content starts/ends.
moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination);
@@ -1013,9 +1047,9 @@ void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositi
return;
if (m_selectReplacement)
- setEndingSelection(Selection(start, end, SEL_DEFAULT_AFFINITY));
+ setEndingSelection(VisibleSelection(start, end, SEL_DEFAULT_AFFINITY));
else
- setEndingSelection(Selection(end, SEL_DEFAULT_AFFINITY));
+ setEndingSelection(VisibleSelection(end, SEL_DEFAULT_AFFINITY));
}
EditAction ReplaceSelectionCommand::editingAction() const
diff --git a/WebCore/editing/SelectionController.cpp b/WebCore/editing/SelectionController.cpp
index 67f675e..d606891 100644
--- a/WebCore/editing/SelectionController.cpp
+++ b/WebCore/editing/SelectionController.cpp
@@ -76,30 +76,30 @@ SelectionController::SelectionController(Frame* frame, bool isDragCaretControlle
void SelectionController::moveTo(const VisiblePosition &pos, bool userTriggered)
{
- setSelection(Selection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered);
+ setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered);
}
void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent, bool userTriggered)
{
- setSelection(Selection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), true, true, userTriggered);
+ setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), true, true, userTriggered);
}
void SelectionController::moveTo(const Position &pos, EAffinity affinity, bool userTriggered)
{
- setSelection(Selection(pos, affinity), true, true, userTriggered);
+ setSelection(VisibleSelection(pos, affinity), true, true, userTriggered);
}
void SelectionController::moveTo(const Range *r, EAffinity affinity, bool userTriggered)
{
- setSelection(Selection(startPosition(r), endPosition(r), affinity), true, true, userTriggered);
+ setSelection(VisibleSelection(startPosition(r), endPosition(r), affinity), true, true, userTriggered);
}
void SelectionController::moveTo(const Position &base, const Position &extent, EAffinity affinity, bool userTriggered)
{
- setSelection(Selection(base, extent, affinity), true, true, userTriggered);
+ setSelection(VisibleSelection(base, extent, affinity), true, true, userTriggered);
}
-void SelectionController::setSelection(const Selection& s, bool closeTyping, bool clearTypingStyle, bool userTriggered)
+void SelectionController::setSelection(const VisibleSelection& s, bool closeTyping, bool clearTypingStyle, bool userTriggered)
{
if (m_isDragCaretController) {
invalidateCaretRect();
@@ -134,7 +134,7 @@ void SelectionController::setSelection(const Selection& s, bool closeTyping, boo
if (m_sel == s)
return;
- Selection oldSelection = m_sel;
+ VisibleSelection oldSelection = m_sel;
m_sel = s;
@@ -151,7 +151,7 @@ void SelectionController::setSelection(const Selection& s, bool closeTyping, boo
m_frame->notifyRendererOfSelectionChange(userTriggered);
m_frame->respondToChangedSelection(oldSelection, closeTyping);
if (userTriggered)
- m_frame->revealCaret(RenderLayer::gAlignToEdgeIfNeeded);
+ m_frame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded, true);
notifyAccessibilityForSelectionChange();
}
@@ -215,12 +215,12 @@ void SelectionController::nodeWillBeRemoved(Node *node)
if (clearRenderTreeSelection) {
RefPtr<Document> document = m_sel.start().node()->document();
document->updateRendering();
- if (RenderView* view = static_cast<RenderView*>(document->renderer()))
+ if (RenderView* view = toRenderView(document->renderer()))
view->clearSelection();
}
if (clearDOMTreeSelection)
- setSelection(Selection(), false, false);
+ setSelection(VisibleSelection(), false, false);
}
void SelectionController::willBeModified(EAlteration alter, EDirection direction)
@@ -700,27 +700,27 @@ int SelectionController::xPosForVerticalArrowNavigation(EPositionType type)
void SelectionController::clear()
{
- setSelection(Selection());
+ setSelection(VisibleSelection());
}
void SelectionController::setBase(const VisiblePosition &pos, bool userTriggered)
{
- setSelection(Selection(pos.deepEquivalent(), m_sel.extent(), pos.affinity()), true, true, userTriggered);
+ setSelection(VisibleSelection(pos.deepEquivalent(), m_sel.extent(), pos.affinity()), true, true, userTriggered);
}
void SelectionController::setExtent(const VisiblePosition &pos, bool userTriggered)
{
- setSelection(Selection(m_sel.base(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered);
+ setSelection(VisibleSelection(m_sel.base(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered);
}
void SelectionController::setBase(const Position &pos, EAffinity affinity, bool userTriggered)
{
- setSelection(Selection(pos, m_sel.extent(), affinity), true, true, userTriggered);
+ setSelection(VisibleSelection(pos, m_sel.extent(), affinity), true, true, userTriggered);
}
void SelectionController::setExtent(const Position &pos, EAffinity affinity, bool userTriggered)
{
- setSelection(Selection(m_sel.base(), pos, affinity), true, true, userTriggered);
+ setSelection(VisibleSelection(m_sel.base(), pos, affinity), true, true, userTriggered);
}
void SelectionController::setNeedsLayout(bool flag)
@@ -830,7 +830,7 @@ IntRect SelectionController::caretRepaintRect() const
bool SelectionController::recomputeCaretRect()
{
- if (!m_frame || !m_frame->document())
+ if (!m_frame)
return false;
FrameView* v = m_frame->document()->view();
@@ -853,9 +853,10 @@ bool SelectionController::recomputeCaretRect()
if (oldAbsRepaintRect == m_absCaretBounds)
return false;
- if (RenderView* view = static_cast<RenderView*>(m_frame->document()->renderer())) {
- view->repaintViewRectangle(oldAbsRepaintRect, false);
- view->repaintViewRectangle(m_absCaretBounds, false);
+ if (RenderView* view = toRenderView(m_frame->document()->renderer())) {
+ // FIXME: make caret repainting container-aware.
+ view->repaintRectangleInViewAndCompositedLayers(oldAbsRepaintRect, false);
+ view->repaintRectangleInViewAndCompositedLayers(m_absCaretBounds, false);
}
return true;
@@ -886,8 +887,8 @@ void SelectionController::invalidateCaretRect()
m_needsLayout = true;
if (!caretRectChanged) {
- if (RenderView* view = static_cast<RenderView*>(d->renderer()))
- view->repaintViewRectangle(caretRepaintRect(), false);
+ if (RenderView* view = toRenderView(d->renderer()))
+ view->repaintRectangleInViewAndCompositedLayers(caretRepaintRect(), false);
}
}
@@ -931,9 +932,9 @@ void SelectionController::debugRenderer(RenderObject *r, bool selected) const
if (selected) {
int offset = 0;
if (r->node() == m_sel.start().node())
- offset = m_sel.start().offset();
+ offset = m_sel.start().m_offset;
else if (r->node() == m_sel.end().node())
- offset = m_sel.end().offset();
+ offset = m_sel.end().m_offset;
int pos;
InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos);
@@ -995,7 +996,8 @@ bool SelectionController::contains(const IntPoint& point)
if (!document->renderer())
return false;
- HitTestRequest request(true, true);
+ HitTestRequest request(HitTestRequest::ReadOnly |
+ HitTestRequest::Active);
HitTestResult result(point);
document->renderView()->layer()->hitTest(request, result);
Node* innerNode = result.innerNode();
@@ -1041,8 +1043,6 @@ void SelectionController::selectFrameElementInParentIfFullySelected()
// Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
Document* doc = m_frame->document();
- if (!doc)
- return;
Element* ownerElement = doc->ownerElement();
if (!ownerElement)
return;
@@ -1060,7 +1060,7 @@ void SelectionController::selectFrameElementInParentIfFullySelected()
VisiblePosition afterOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex + 1, VP_UPSTREAM_IF_POSSIBLE));
// Focus on the parent frame, and then select from before this element to after.
- Selection newSelection(beforeOwnerElement, afterOwnerElement);
+ VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
if (parent->shouldChangeSelection(newSelection)) {
page->focusController()->setFocusedFrame(parent);
parent->selection()->setSelection(newSelection);
@@ -1070,8 +1070,6 @@ void SelectionController::selectFrameElementInParentIfFullySelected()
void SelectionController::selectAll()
{
Document* document = m_frame->document();
- if (!document)
- return;
if (document->focusedNode() && document->focusedNode()->canSelectAll()) {
document->focusedNode()->selectAll();
@@ -1088,7 +1086,7 @@ void SelectionController::selectAll()
}
if (!root)
return;
- Selection newSelection(Selection::selectionFromContentsOfNode(root));
+ VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root));
if (m_frame->shouldChangeSelection(newSelection))
setSelection(newSelection);
selectFrameElementInParentIfFullySelected();
@@ -1132,7 +1130,7 @@ bool SelectionController::setSelectedRange(Range* range, EAffinity affinity, boo
// FIXME: Can we provide extentAffinity?
VisiblePosition visibleStart(startContainer, startOffset, collapsed ? affinity : DOWNSTREAM);
VisiblePosition visibleEnd(endContainer, endOffset, SEL_DEFAULT_AFFINITY);
- setSelection(Selection(visibleStart, visibleEnd), closeTyping);
+ setSelection(VisibleSelection(visibleStart, visibleEnd), closeTyping);
return true;
}
@@ -1166,8 +1164,8 @@ void SelectionController::focusedOrActiveStateChanged()
// Because RenderObject::selectionBackgroundColor() and
// RenderObject::selectionForegroundColor() check if the frame is active,
// we have to update places those colors were painted.
- if (RenderView* view = static_cast<RenderView*>(m_frame->document()->renderer()))
- view->repaintViewRectangle(enclosingIntRect(m_frame->selectionBounds()));
+ if (RenderView* view = toRenderView(m_frame->document()->renderer()))
+ view->repaintRectangleInViewAndCompositedLayers(enclosingIntRect(m_frame->selectionBounds()));
// Caret appears in the active frame.
if (activeAndFocused)
@@ -1205,8 +1203,7 @@ void SelectionController::setFocused(bool flag)
focusedOrActiveStateChanged();
- if (Document* doc = m_frame->document())
- doc->dispatchWindowEvent(flag ? eventNames().focusEvent : eventNames().blurEvent, false, false);
+ m_frame->document()->dispatchWindowEvent(flag ? eventNames().focusEvent : eventNames().blurEvent, false, false);
}
bool SelectionController::isFocusedAndActive() const
diff --git a/WebCore/editing/SelectionController.h b/WebCore/editing/SelectionController.h
index fe5e90d..21e849d 100644
--- a/WebCore/editing/SelectionController.h
+++ b/WebCore/editing/SelectionController.h
@@ -27,8 +27,8 @@
#define SelectionController_h
#include "IntRect.h"
-#include "Selection.h"
#include "Range.h"
+#include "VisibleSelection.h"
#include <wtf/Noncopyable.h>
namespace WebCore {
@@ -56,8 +56,8 @@ public:
void moveTo(const Position&, EAffinity, bool userTriggered = false);
void moveTo(const Position&, const Position&, EAffinity, bool userTriggered = false);
- const Selection& selection() const { return m_sel; }
- void setSelection(const Selection&, bool closeTyping = true, bool clearTypingStyle = true, bool userTriggered = false);
+ const VisibleSelection& selection() const { return m_sel; }
+ void setSelection(const VisibleSelection&, bool closeTyping = true, bool clearTypingStyle = true, bool userTriggered = false);
bool setSelectedRange(Range*, EAffinity, bool closeTyping);
void selectAll();
void clear();
@@ -67,7 +67,7 @@ public:
bool contains(const IntPoint&);
- Selection::EState state() const { return m_sel.state(); }
+ VisibleSelection::SelectionType selectionType() const { return m_sel.selectionType(); }
EAffinity affinity() const { return m_sel.affinity(); }
@@ -103,7 +103,7 @@ public:
bool isCaretOrRange() const { return m_sel.isCaretOrRange(); }
bool isInPasswordField() const;
- PassRefPtr<Range> toRange() const { return m_sel.toRange(); }
+ PassRefPtr<Range> toNormalizedRange() const { return m_sel.toNormalizedRange(); }
void debugRenderer(RenderObject*, bool selected) const;
@@ -154,7 +154,7 @@ private:
Frame* m_frame;
int m_xPosForVerticalArrowNavigation;
- Selection m_sel;
+ VisibleSelection m_sel;
IntRect m_caretRect; // caret rect in coords local to the renderer responsible for painting the caret
IntRect m_absCaretBounds; // absolute bounding rect for the caret
diff --git a/WebCore/editing/SmartReplaceCF.cpp b/WebCore/editing/SmartReplaceCF.cpp
index f2fd985..c5fa9a8 100644
--- a/WebCore/editing/SmartReplaceCF.cpp
+++ b/WebCore/editing/SmartReplaceCF.cpp
@@ -30,6 +30,7 @@
#include "SmartReplace.h"
#include <CoreFoundation/CFCharacterSet.h>
+#include <CoreFoundation/CFString.h>
namespace WebCore {
diff --git a/WebCore/editing/SplitElementCommand.cpp b/WebCore/editing/SplitElementCommand.cpp
index 69447d4..35dfc6f 100644
--- a/WebCore/editing/SplitElementCommand.cpp
+++ b/WebCore/editing/SplitElementCommand.cpp
@@ -43,7 +43,7 @@ SplitElementCommand::SplitElementCommand(PassRefPtr<Element> element, PassRefPtr
void SplitElementCommand::doApply()
{
- RefPtr<Element> prefixElement = m_element2->cloneElement();
+ RefPtr<Element> prefixElement = m_element2->cloneElementWithoutChildren();
if (m_atChild->parentNode() != m_element2)
return;
diff --git a/WebCore/editing/TextIterator.cpp b/WebCore/editing/TextIterator.cpp
index f617889..2d7e641 100644
--- a/WebCore/editing/TextIterator.cpp
+++ b/WebCore/editing/TextIterator.cpp
@@ -224,7 +224,9 @@ void TextIterator::advance()
if (!m_handledNode) {
if (renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) // FIXME: What about CDATA_SECTION_NODE?
m_handledNode = handleTextNode();
- else if (renderer && (renderer->isImage() || renderer->isWidget() || (renderer->element() && renderer->element()->isControl())))
+ else if (renderer && (renderer->isImage() || renderer->isWidget() ||
+ (renderer->node() && renderer->node()->isElementNode() &&
+ static_cast<Element*>(renderer->node())->isFormControlElement())))
m_handledNode = handleReplacedElement();
else
m_handledNode = handleNonTextNode();
@@ -247,7 +249,7 @@ void TextIterator::advance()
parentNode = m_node->shadowParentNode();
}
while (!next && parentNode) {
- if (pastEnd && parentNode == m_endContainer || m_endContainer->isDescendantOf(parentNode))
+ if ((pastEnd && parentNode == m_endContainer) || m_endContainer->isDescendantOf(parentNode))
return;
bool haveRenderer = m_node->renderer();
m_node = parentNode;
@@ -414,8 +416,8 @@ bool TextIterator::handleReplacedElement()
return false;
}
- if (m_enterTextControls && (renderer->isTextArea() || renderer->isTextField())) {
- m_node = static_cast<RenderTextControl*>(renderer)->innerTextElement();
+ if (m_enterTextControls && renderer->isTextControl()) {
+ m_node = toRenderTextControl(renderer)->innerTextElement();
m_offset = 0;
m_inShadowContent = true;
return false;
@@ -858,7 +860,7 @@ void SimplifiedBackwardsTextIterator::advance()
if (!m_handledNode &&
canHaveChildrenForEditing(m_node) &&
m_node->parentNode() &&
- (!m_node->lastChild() || m_node == m_endNode && m_endOffset == 0)) {
+ (!m_node->lastChild() || (m_node == m_endNode && m_endOffset == 0))) {
exitNode();
if (m_positionNode) {
m_handledNode = true;
@@ -1057,7 +1059,7 @@ void CharacterIterator::advance(int count)
String CharacterIterator::string(int numChars)
{
Vector<UChar> result;
- result.reserveCapacity(numChars);
+ result.reserveInitialCapacity(numChars);
while (numChars > 0 && !atEnd()) {
int runSize = min(numChars, length());
result.append(characters(), runSize);
@@ -1081,6 +1083,81 @@ static PassRefPtr<Range> characterSubrange(CharacterIterator& it, int offset, in
end->endContainer(), end->endOffset());
}
+BackwardsCharacterIterator::BackwardsCharacterIterator()
+ : m_offset(0)
+ , m_runOffset(0)
+ , m_atBreak(true)
+{
+}
+
+BackwardsCharacterIterator::BackwardsCharacterIterator(const Range* range)
+ : m_offset(0)
+ , m_runOffset(0)
+ , m_atBreak(true)
+ , m_textIterator(range)
+{
+ while (!atEnd() && !m_textIterator.length())
+ m_textIterator.advance();
+}
+
+PassRefPtr<Range> BackwardsCharacterIterator::range() const
+{
+ RefPtr<Range> r = m_textIterator.range();
+ if (!m_textIterator.atEnd()) {
+ if (m_textIterator.length() <= 1)
+ ASSERT(m_runOffset == 0);
+ else {
+ Node* n = r->startContainer();
+ ASSERT(n == r->endContainer());
+ int offset = r->endOffset() - m_runOffset;
+ ExceptionCode ec = 0;
+ r->setStart(n, offset - 1, ec);
+ r->setEnd(n, offset, ec);
+ ASSERT(!ec);
+ }
+ }
+ return r.release();
+}
+
+void BackwardsCharacterIterator::advance(int count)
+{
+ if (count <= 0) {
+ ASSERT(!count);
+ return;
+ }
+
+ m_atBreak = false;
+
+ int remaining = m_textIterator.length() - m_runOffset;
+ if (count < remaining) {
+ m_runOffset += count;
+ m_offset += count;
+ return;
+ }
+
+ count -= remaining;
+ m_offset += remaining;
+
+ for (m_textIterator.advance(); !atEnd(); m_textIterator.advance()) {
+ int runLength = m_textIterator.length();
+ if (runLength == 0)
+ m_atBreak = true;
+ else {
+ if (count < runLength) {
+ m_runOffset = count;
+ m_offset += count;
+ return;
+ }
+
+ count -= runLength;
+ m_offset += runLength;
+ }
+ }
+
+ m_atBreak = true;
+ m_runOffset = 0;
+}
+
// --------
WordAwareIterator::WordAwareIterator()
@@ -1225,7 +1302,7 @@ inline SearchBuffer::SearchBuffer(const String& target, bool isCaseSensitive)
ASSERT(!m_target.isEmpty());
size_t targetLength = target.length();
- m_buffer.reserveCapacity(max(targetLength * 8, minimumSearchBufferSize));
+ m_buffer.reserveInitialCapacity(max(targetLength * 8, minimumSearchBufferSize));
m_overlap = m_buffer.capacity() / 4;
// Grab the single global searcher.
@@ -1481,7 +1558,7 @@ PassRefPtr<Range> TextIterator::rangeFromLocationAndLength(Element *scope, int r
Position runEnd = VisiblePosition(runStart).next().deepEquivalent();
if (runEnd.isNotNull()) {
ExceptionCode ec = 0;
- textRunRange->setEnd(runEnd.node(), runEnd.offset(), ec);
+ textRunRange->setEnd(runEnd.node(), runEnd.m_offset, ec);
ASSERT(!ec);
}
}
@@ -1542,7 +1619,7 @@ UChar* plainTextToMallocAllocatedBuffer(const Range* r, unsigned& bufferLength,
typedef pair<UChar*, unsigned> TextSegment;
Vector<TextSegment>* textSegments = 0;
Vector<UChar> textBuffer;
- textBuffer.reserveCapacity(cMaxSegmentSize);
+ textBuffer.reserveInitialCapacity(cMaxSegmentSize);
for (TextIterator it(r); !it.atEnd(); it.advance()) {
if (textBuffer.size() && textBuffer.size() + it.length() > cMaxSegmentSize) {
UChar* newSegmentBuffer = static_cast<UChar*>(malloc(textBuffer.size() * sizeof(UChar)));
diff --git a/WebCore/editing/TextIterator.h b/WebCore/editing/TextIterator.h
index 308ffc9..f00bea4 100644
--- a/WebCore/editing/TextIterator.h
+++ b/WebCore/editing/TextIterator.h
@@ -216,6 +216,25 @@ private:
TextIterator m_textIterator;
};
+class BackwardsCharacterIterator {
+public:
+ BackwardsCharacterIterator();
+ explicit BackwardsCharacterIterator(const Range*);
+
+ void advance(int);
+
+ bool atEnd() const { return m_textIterator.atEnd(); }
+
+ PassRefPtr<Range> range() const;
+
+private:
+ int m_offset;
+ int m_runOffset;
+ bool m_atBreak;
+
+ SimplifiedBackwardsTextIterator m_textIterator;
+};
+
// Very similar to the TextIterator, except that the chunks of text returned are "well behaved",
// meaning they never end split up a word. This is useful for spellcheck or (perhaps one day) searching.
class WordAwareIterator {
diff --git a/WebCore/editing/TypingCommand.cpp b/WebCore/editing/TypingCommand.cpp
index 414680f..6235f7a 100644
--- a/WebCore/editing/TypingCommand.cpp
+++ b/WebCore/editing/TypingCommand.cpp
@@ -36,6 +36,7 @@
#include "InsertLineBreakCommand.h"
#include "InsertParagraphSeparatorCommand.h"
#include "InsertTextCommand.h"
+#include "RenderObject.h"
#include "SelectionController.h"
#include "VisiblePosition.h"
#include "htmlediting.h"
@@ -48,7 +49,6 @@ TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, con
m_commandType(commandType),
m_textToInsert(textToInsert),
m_openForMoreTyping(true),
- m_applyEditing(false),
m_selectInsertedText(selectInsertedText),
m_smartDelete(false),
m_granularity(granularity),
@@ -125,14 +125,14 @@ void TypingCommand::insertText(Document* document, const String& text, bool sele
insertText(document, text, frame->selection()->selection(), selectInsertedText, insertedTextIsComposition);
}
-void TypingCommand::insertText(Document* document, const String& text, const Selection& selectionForInsertion, bool selectInsertedText, bool insertedTextIsComposition)
+void TypingCommand::insertText(Document* document, const String& text, const VisibleSelection& selectionForInsertion, bool selectInsertedText, bool insertedTextIsComposition)
{
ASSERT(document);
RefPtr<Frame> frame = document->frame();
ASSERT(frame);
- Selection currentSelection = frame->selection()->selection();
+ VisibleSelection currentSelection = frame->selection()->selection();
bool changeSelection = currentSelection != selectionForInsertion;
String newText = text;
@@ -300,13 +300,7 @@ void TypingCommand::markMisspellingsAfterTyping()
void TypingCommand::typingAddedToOpenCommand()
{
markMisspellingsAfterTyping();
- // Do not apply editing to the frame on the first time through.
- // The frame will get told in the same way as all other commands.
- // But since this command stays open and is used for additional typing,
- // we need to tell the frame here as other commands are added.
- if (m_applyEditing)
- document()->frame()->editor()->appliedEditing(this);
- m_applyEditing = true;
+ document()->frame()->editor()->appliedEditing(this);
}
void TypingCommand::insertText(const String &text, bool selectInsertedText)
@@ -370,19 +364,19 @@ void TypingCommand::insertParagraphSeparatorInQuotedContent()
void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
{
- Selection selectionToDelete;
- Selection selectionAfterUndo;
+ VisibleSelection selectionToDelete;
+ VisibleSelection selectionAfterUndo;
- switch (endingSelection().state()) {
- case Selection::RANGE:
+ switch (endingSelection().selectionType()) {
+ case VisibleSelection::RangeSelection:
selectionToDelete = endingSelection();
selectionAfterUndo = selectionToDelete;
break;
- case Selection::CARET: {
- if (breakOutOfEmptyMailBlockquotedParagraph()) {
+ case VisibleSelection::CaretSelection: {
+ // After breaking out of an empty mail blockquote, we still want continue with the deletion
+ // so actual content will get deleted, and not just the quote style.
+ if (breakOutOfEmptyMailBlockquotedParagraph())
typingAddedToOpenCommand();
- return;
- }
m_smartDelete = false;
@@ -410,63 +404,62 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity);
// If the caret is just after a table, select the table and don't delete anything.
} else if (Node* table = isFirstPositionAfterTable(visibleStart)) {
- setEndingSelection(Selection(Position(table, 0), endingSelection().start(), DOWNSTREAM));
+ setEndingSelection(VisibleSelection(Position(table, 0), endingSelection().start(), DOWNSTREAM));
typingAddedToOpenCommand();
return;
}
selectionToDelete = selection.selection();
- if (granularity == CharacterGranularity && selectionToDelete.end().offset() - selectionToDelete.start().offset() > 1) {
- // When we delete a ligature consisting of multiple Unicode characters with a backspace key,
- // we should not delete the ligature but delete only its last characeter. To check we are deleting
- // a ligature, we retrieve the previous position of the caret and count the number of
- // characters to be deleted.
- // To prevent from calculating the previous position every time when pressing a backspace key,
- // we retrieve the previous position only when the given selection consists of two or more characters.
- if (selectionToDelete.end().offset() - selectionToDelete.end().previous(UsingComposedCharacters).offset() > 1)
- selectionToDelete.setWithoutValidation(selectionToDelete.end(), selectionToDelete.end().previous(NotUsingComposedCharacters));
+ if (granularity == CharacterGranularity && selectionToDelete.end().node() == selectionToDelete.start().node() && selectionToDelete.end().m_offset - selectionToDelete.start().m_offset > 1) {
+ // If there are multiple Unicode code points to be deleted, adjust the range to match platform conventions.
+ selectionToDelete.setWithoutValidation(selectionToDelete.end(), selectionToDelete.end().previous(BackwardDeletion));
}
if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start())
selectionAfterUndo = selectionToDelete;
else
// It's a little tricky to compute what the starting selection would have been in the original document.
- // We can't let the Selection class's validation kick in or it'll adjust for us based on
+ // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
// the current state of the document and we'll get the wrong result.
selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent());
break;
}
- case Selection::NONE:
+ case VisibleSelection::NoSelection:
ASSERT_NOT_REACHED();
break;
}
- if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) {
- if (killRing)
- document()->frame()->editor()->addToKillRing(selectionToDelete.toRange().get(), false);
- // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
- // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
- // more text than you insert. In that case all of the text that was around originally should be selected.
- if (m_openedByBackwardDelete)
- setStartingSelection(selectionAfterUndo);
- CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
- setSmartDelete(false);
- typingAddedToOpenCommand();
- }
+ ASSERT(!selectionToDelete.isNone());
+ if (selectionToDelete.isNone())
+ return;
+
+ if (selectionToDelete.isCaret() || !document()->frame()->shouldDeleteSelection(selectionToDelete))
+ return;
+
+ if (killRing)
+ document()->frame()->editor()->addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
+ // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
+ // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
+ // more text than you insert. In that case all of the text that was around originally should be selected.
+ if (m_openedByBackwardDelete)
+ setStartingSelection(selectionAfterUndo);
+ CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
+ setSmartDelete(false);
+ typingAddedToOpenCommand();
}
void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool killRing)
{
- Selection selectionToDelete;
- Selection selectionAfterUndo;
+ VisibleSelection selectionToDelete;
+ VisibleSelection selectionAfterUndo;
- switch (endingSelection().state()) {
- case Selection::RANGE:
+ switch (endingSelection().selectionType()) {
+ case VisibleSelection::RangeSelection:
selectionToDelete = endingSelection();
selectionAfterUndo = selectionToDelete;
break;
- case Selection::CARET: {
+ case VisibleSelection::CaretSelection: {
m_smartDelete = false;
// Handle delete at beginning-of-block case.
@@ -483,8 +476,8 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
if (visibleEnd == endOfParagraph(visibleEnd))
downstreamEnd = visibleEnd.next(true).deepEquivalent().downstream();
// When deleting tables: Select the table first, then perform the deletion
- if (downstreamEnd.node() && downstreamEnd.node()->renderer() && downstreamEnd.node()->renderer()->isTable() && downstreamEnd.offset() == 0) {
- setEndingSelection(Selection(endingSelection().end(), Position(downstreamEnd.node(), maxDeepOffset(downstreamEnd.node())), DOWNSTREAM));
+ if (downstreamEnd.node() && downstreamEnd.node()->renderer() && downstreamEnd.node()->renderer()->isTable() && downstreamEnd.m_offset == 0) {
+ setEndingSelection(VisibleSelection(endingSelection().end(), lastDeepEditingPositionForNode(downstreamEnd.node()), DOWNSTREAM));
typingAddedToOpenCommand();
return;
}
@@ -498,7 +491,7 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
selectionAfterUndo = selectionToDelete;
else {
// It's a little tricky to compute what the starting selection would have been in the original document.
- // We can't let the Selection class's validation kick in or it'll adjust for us based on
+ // We can't let the VisibleSelection class's validation kick in or it'll adjust for us based on
// the current state of the document and we'll get the wrong result.
Position extent = startingSelection().end();
if (extent.node() != selectionToDelete.end().node())
@@ -506,29 +499,34 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
else {
int extraCharacters;
if (selectionToDelete.start().node() == selectionToDelete.end().node())
- extraCharacters = selectionToDelete.end().offset() - selectionToDelete.start().offset();
+ extraCharacters = selectionToDelete.end().m_offset - selectionToDelete.start().m_offset;
else
- extraCharacters = selectionToDelete.end().offset();
- extent = Position(extent.node(), extent.offset() + extraCharacters);
+ extraCharacters = selectionToDelete.end().m_offset;
+ extent = Position(extent.node(), extent.m_offset + extraCharacters);
}
selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent);
}
break;
}
- case Selection::NONE:
+ case VisibleSelection::NoSelection:
ASSERT_NOT_REACHED();
break;
}
- if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) {
- if (killRing)
- document()->frame()->editor()->addToKillRing(selectionToDelete.toRange().get(), false);
- // make undo select what was deleted
- setStartingSelection(selectionAfterUndo);
- CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
- setSmartDelete(false);
- typingAddedToOpenCommand();
- }
+ ASSERT(!selectionToDelete.isNone());
+ if (selectionToDelete.isNone())
+ return;
+
+ if (selectionToDelete.isCaret() || !document()->frame()->shouldDeleteSelection(selectionToDelete))
+ return;
+
+ if (killRing)
+ document()->frame()->editor()->addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
+ // make undo select what was deleted
+ setStartingSelection(selectionAfterUndo);
+ CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
+ setSmartDelete(false);
+ typingAddedToOpenCommand();
}
void TypingCommand::deleteSelection(bool smartDelete)
diff --git a/WebCore/editing/TypingCommand.h b/WebCore/editing/TypingCommand.h
index b4e1091..bf588be 100644
--- a/WebCore/editing/TypingCommand.h
+++ b/WebCore/editing/TypingCommand.h
@@ -46,7 +46,7 @@ public:
static void deleteKeyPressed(Document*, bool smartDelete = false, TextGranularity = CharacterGranularity, bool killRing = false);
static void forwardDeleteKeyPressed(Document*, bool smartDelete = false, TextGranularity = CharacterGranularity, bool killRing = false);
static void insertText(Document*, const String&, bool selectInsertedText = false, bool insertedTextIsComposition = false);
- static void insertText(Document*, const String&, const Selection&, bool selectInsertedText = false, bool insertedTextIsComposition = false);
+ static void insertText(Document*, const String&, const VisibleSelection&, bool selectInsertedText = false, bool insertedTextIsComposition = false);
static void insertLineBreak(Document*);
static void insertParagraphSeparator(Document*);
static void insertParagraphSeparatorInQuotedContent(Document*);
@@ -87,7 +87,6 @@ private:
ETypingCommand m_commandType;
String m_textToInsert;
bool m_openForMoreTyping;
- bool m_applyEditing;
bool m_selectInsertedText;
bool m_smartDelete;
TextGranularity m_granularity;
diff --git a/WebCore/editing/VisiblePosition.cpp b/WebCore/editing/VisiblePosition.cpp
index 3d771c6..27ee146 100644
--- a/WebCore/editing/VisiblePosition.cpp
+++ b/WebCore/editing/VisiblePosition.cpp
@@ -81,7 +81,7 @@ VisiblePosition VisiblePosition::previous(bool stayInEditableContent) const
Position pos = previousVisuallyDistinctCandidate(m_deepPosition);
// return null visible position if there is no previous visible position
- if (pos.atStart())
+ if (pos.atStartOfTree())
return VisiblePosition();
VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM);
@@ -125,7 +125,7 @@ Position VisiblePosition::leftVisuallyDistinctCandidate() const
if (!box)
return primaryDirection == LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition);
- RenderObject* renderer = box->object();
+ RenderObject* renderer = box->renderer();
while (true) {
if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretRightmostOffset())
@@ -147,7 +147,7 @@ Position VisiblePosition::leftVisuallyDistinctCandidate() const
// Reposition at the other logical position corresponding to our edge's visual position and go for another round.
box = prevBox;
- renderer = box->object();
+ renderer = box->renderer();
offset = prevBox->caretRightmostOffset();
continue;
}
@@ -176,7 +176,7 @@ Position VisiblePosition::leftVisuallyDistinctCandidate() const
break;
box = prevBox;
}
- renderer = box->object();
+ renderer = box->renderer();
offset = box->caretRightmostOffset();
if (box->direction() == primaryDirection)
break;
@@ -185,7 +185,7 @@ Position VisiblePosition::leftVisuallyDistinctCandidate() const
if (prevBox) {
box = prevBox;
- renderer = box->object();
+ renderer = box->renderer();
offset = box->caretRightmostOffset();
if (box->bidiLevel() > level) {
do {
@@ -215,15 +215,15 @@ Position VisiblePosition::leftVisuallyDistinctCandidate() const
break;
level = box->bidiLevel();
}
- renderer = box->object();
+ renderer = box->renderer();
offset = primaryDirection == LTR ? box->caretMinOffset() : box->caretMaxOffset();
}
break;
}
- p = Position(renderer->element(), offset);
+ p = Position(renderer->node(), offset);
- if (p.isCandidate() && p.downstream() != downstreamStart || p.atStart() || p.atEnd())
+ if ((p.isCandidate() && p.downstream() != downstreamStart) || p.atStartOfTree() || p.atEndOfTree())
return p;
}
}
@@ -231,7 +231,8 @@ Position VisiblePosition::leftVisuallyDistinctCandidate() const
VisiblePosition VisiblePosition::left(bool stayInEditableContent) const
{
Position pos = leftVisuallyDistinctCandidate();
- if (pos.atStart() || pos.atEnd())
+ // FIXME: Why can't we move left from the last position in a tree?
+ if (pos.atStartOfTree() || pos.atEndOfTree())
return VisiblePosition();
VisiblePosition left = VisiblePosition(pos, DOWNSTREAM);
@@ -266,7 +267,7 @@ Position VisiblePosition::rightVisuallyDistinctCandidate() const
if (!box)
return primaryDirection == LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition);
- RenderObject* renderer = box->object();
+ RenderObject* renderer = box->renderer();
while (true) {
if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretLeftmostOffset())
@@ -288,7 +289,7 @@ Position VisiblePosition::rightVisuallyDistinctCandidate() const
// Reposition at the other logical position corresponding to our edge's visual position and go for another round.
box = nextBox;
- renderer = box->object();
+ renderer = box->renderer();
offset = nextBox->caretLeftmostOffset();
continue;
}
@@ -318,7 +319,7 @@ Position VisiblePosition::rightVisuallyDistinctCandidate() const
break;
box = nextBox;
}
- renderer = box->object();
+ renderer = box->renderer();
offset = box->caretLeftmostOffset();
if (box->direction() == primaryDirection)
break;
@@ -327,7 +328,7 @@ Position VisiblePosition::rightVisuallyDistinctCandidate() const
if (nextBox) {
box = nextBox;
- renderer = box->object();
+ renderer = box->renderer();
offset = box->caretLeftmostOffset();
if (box->bidiLevel() > level) {
do {
@@ -357,15 +358,15 @@ Position VisiblePosition::rightVisuallyDistinctCandidate() const
break;
level = box->bidiLevel();
}
- renderer = box->object();
+ renderer = box->renderer();
offset = primaryDirection == LTR ? box->caretMaxOffset() : box->caretMinOffset();
}
break;
}
- p = Position(renderer->element(), offset);
+ p = Position(renderer->node(), offset);
- if (p.isCandidate() && p.downstream() != downstreamStart || p.atStart() || p.atEnd())
+ if ((p.isCandidate() && p.downstream() != downstreamStart) || p.atStartOfTree() || p.atEndOfTree())
return p;
}
}
@@ -373,7 +374,8 @@ Position VisiblePosition::rightVisuallyDistinctCandidate() const
VisiblePosition VisiblePosition::right(bool stayInEditableContent) const
{
Position pos = rightVisuallyDistinctCandidate();
- if (pos.atStart() || pos.atEnd())
+ // FIXME: Why can't we move left from the last position in a tree?
+ if (pos.atStartOfTree() || pos.atEndOfTree())
return VisiblePosition();
VisiblePosition right = VisiblePosition(pos, DOWNSTREAM);
@@ -399,7 +401,7 @@ VisiblePosition VisiblePosition::honorEditableBoundaryAtOrBefore(const VisiblePo
// Return pos itself if the two are from the very same editable region, or both are non-editable
// FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement
- // to it is allowed. Selection::adjustForEditableContent has this problem too.
+ // to it is allowed. VisibleSelection::adjustForEditableContent has this problem too.
if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
return pos;
@@ -425,7 +427,7 @@ VisiblePosition VisiblePosition::honorEditableBoundaryAtOrAfter(const VisiblePos
// Return pos itself if the two are from the very same editable region, or both are non-editable
// FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement
- // to it is allowed. Selection::adjustForEditableContent has this problem too.
+ // to it is allowed. VisibleSelection::adjustForEditableContent has this problem too.
if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
return pos;
@@ -478,24 +480,24 @@ Position VisiblePosition::canonicalPosition(const Position& position)
// The new position must be in the same editable element. Enforce that first.
// Unless the descent is from a non-editable html element to an editable body.
- if (node->hasTagName(htmlTag) && !node->isContentEditable())
+ if (node->hasTagName(htmlTag) && !node->isContentEditable() && node->document()->body() && node->document()->body()->isContentEditable())
return next.isNotNull() ? next : prev;
Node* editingRoot = editableRootForPosition(position);
// If the html element is editable, descending into its body will look like a descent
// from non-editable to editable content since rootEditableElement() always stops at the body.
- if (editingRoot && editingRoot->hasTagName(htmlTag) || position.node()->isDocumentNode())
+ if ((editingRoot && editingRoot->hasTagName(htmlTag)) || position.node()->isDocumentNode())
return next.isNotNull() ? next : prev;
bool prevIsInSameEditableElement = prevNode && editableRootForPosition(prev) == editingRoot;
bool nextIsInSameEditableElement = nextNode && editableRootForPosition(next) == editingRoot;
if (prevIsInSameEditableElement && !nextIsInSameEditableElement)
return prev;
-
+
if (nextIsInSameEditableElement && !prevIsInSameEditableElement)
return next;
-
+
if (!nextIsInSameEditableElement && !prevIsInSameEditableElement)
return Position();
@@ -518,7 +520,7 @@ UChar VisiblePosition::characterAfter() const
if (!node || !node->isTextNode())
return 0;
Text* textNode = static_cast<Text*>(pos.node());
- int offset = pos.offset();
+ int offset = pos.m_offset;
if ((unsigned)offset >= textNode->length())
return 0;
return textNode->data()[offset];
@@ -541,7 +543,7 @@ IntRect VisiblePosition::localCaretRect(RenderObject*& renderer) const
getInlineBoxAndOffset(inlineBox, caretOffset);
if (inlineBox)
- renderer = inlineBox->object();
+ renderer = inlineBox->renderer();
return renderer->localCaretRect(inlineBox, caretOffset);
}
@@ -574,7 +576,7 @@ void VisiblePosition::debugPosition(const char* msg) const
if (isNull())
fprintf(stderr, "Position [%s]: null\n", msg);
else
- fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, m_deepPosition.node()->nodeName().utf8().data(), m_deepPosition.node(), m_deepPosition.offset());
+ fprintf(stderr, "Position [%s]: %s [%p] at %d\n", msg, m_deepPosition.node()->nodeName().utf8().data(), m_deepPosition.node(), m_deepPosition.m_offset);
}
#ifndef NDEBUG
@@ -598,7 +600,7 @@ PassRefPtr<Range> makeRange(const VisiblePosition &start, const VisiblePosition
Position s = rangeCompliantEquivalent(start);
Position e = rangeCompliantEquivalent(end);
- return Range::create(s.node()->document(), s.node(), s.offset(), e.node(), e.offset());
+ return Range::create(s.node()->document(), s.node(), s.m_offset, e.node(), e.m_offset);
}
VisiblePosition startVisiblePosition(const Range *r, EAffinity affinity)
@@ -619,7 +621,7 @@ bool setStart(Range *r, const VisiblePosition &visiblePosition)
return false;
Position p = rangeCompliantEquivalent(visiblePosition);
int code = 0;
- r->setStart(p.node(), p.offset(), code);
+ r->setStart(p.node(), p.m_offset, code);
return code == 0;
}
@@ -629,7 +631,7 @@ bool setEnd(Range *r, const VisiblePosition &visiblePosition)
return false;
Position p = rangeCompliantEquivalent(visiblePosition);
int code = 0;
- r->setEnd(p.node(), p.offset(), code);
+ r->setEnd(p.node(), p.m_offset, code);
return code == 0;
}
diff --git a/WebCore/editing/Selection.cpp b/WebCore/editing/VisibleSelection.cpp
index 4fb3a54..279adf2 100644
--- a/WebCore/editing/Selection.cpp
+++ b/WebCore/editing/VisibleSelection.cpp
@@ -24,7 +24,7 @@
*/
#include "config.h"
-#include "Selection.h"
+#include "VisibleSelection.h"
#include "CharacterNames.h"
#include "CString.h"
@@ -35,20 +35,21 @@
#include "VisiblePosition.h"
#include "visible_units.h"
#include "Range.h"
+
#include <wtf/Assertions.h>
#include <stdio.h>
namespace WebCore {
-Selection::Selection()
+VisibleSelection::VisibleSelection()
: m_affinity(DOWNSTREAM)
, m_granularity(CharacterGranularity)
- , m_state(NONE)
+ , m_selectionType(NoSelection)
, m_baseIsFirst(true)
{
}
-Selection::Selection(const Position& pos, EAffinity affinity)
+VisibleSelection::VisibleSelection(const Position& pos, EAffinity affinity)
: m_base(pos)
, m_extent(pos)
, m_affinity(affinity)
@@ -57,7 +58,7 @@ Selection::Selection(const Position& pos, EAffinity affinity)
validate();
}
-Selection::Selection(const Position& base, const Position& extent, EAffinity affinity)
+VisibleSelection::VisibleSelection(const Position& base, const Position& extent, EAffinity affinity)
: m_base(base)
, m_extent(extent)
, m_affinity(affinity)
@@ -66,7 +67,7 @@ Selection::Selection(const Position& base, const Position& extent, EAffinity aff
validate();
}
-Selection::Selection(const VisiblePosition& pos)
+VisibleSelection::VisibleSelection(const VisiblePosition& pos)
: m_base(pos.deepEquivalent())
, m_extent(pos.deepEquivalent())
, m_affinity(pos.affinity())
@@ -75,7 +76,7 @@ Selection::Selection(const VisiblePosition& pos)
validate();
}
-Selection::Selection(const VisiblePosition& base, const VisiblePosition& extent)
+VisibleSelection::VisibleSelection(const VisiblePosition& base, const VisiblePosition& extent)
: m_base(base.deepEquivalent())
, m_extent(extent.deepEquivalent())
, m_affinity(base.affinity())
@@ -84,7 +85,7 @@ Selection::Selection(const VisiblePosition& base, const VisiblePosition& extent)
validate();
}
-Selection::Selection(const Range* range, EAffinity affinity)
+VisibleSelection::VisibleSelection(const Range* range, EAffinity affinity)
: m_base(range->startPosition())
, m_extent(range->endPosition())
, m_affinity(affinity)
@@ -93,36 +94,45 @@ Selection::Selection(const Range* range, EAffinity affinity)
validate();
}
-Selection Selection::selectionFromContentsOfNode(Node* node)
+VisibleSelection VisibleSelection::selectionFromContentsOfNode(Node* node)
{
- return Selection(Position(node, 0), Position(node, maxDeepOffset(node)), DOWNSTREAM);
+ return VisibleSelection(firstDeepEditingPositionForNode(node), lastDeepEditingPositionForNode(node), DOWNSTREAM);
}
-void Selection::setBase(const Position& position)
+void VisibleSelection::setBase(const Position& position)
{
m_base = position;
validate();
}
-void Selection::setBase(const VisiblePosition& visiblePosition)
+void VisibleSelection::setBase(const VisiblePosition& visiblePosition)
{
m_base = visiblePosition.deepEquivalent();
validate();
}
-void Selection::setExtent(const Position& position)
+void VisibleSelection::setExtent(const Position& position)
{
m_extent = position;
validate();
}
-void Selection::setExtent(const VisiblePosition& visiblePosition)
+void VisibleSelection::setExtent(const VisiblePosition& visiblePosition)
{
m_extent = visiblePosition.deepEquivalent();
validate();
}
-PassRefPtr<Range> Selection::toRange() const
+PassRefPtr<Range> VisibleSelection::firstRange() const
+{
+ if (isNone())
+ return 0;
+ Position start = rangeCompliantEquivalent(m_start);
+ Position end = rangeCompliantEquivalent(m_end);
+ return Range::create(start.node()->document(), start, end);
+}
+
+PassRefPtr<Range> VisibleSelection::toNormalizedRange() const
{
if (isNone())
return 0;
@@ -159,7 +169,7 @@ PassRefPtr<Range> Selection::toRange() const
ASSERT(isRange());
s = m_start.downstream();
e = m_end.upstream();
- if (Range::compareBoundaryPoints(s.node(), s.offset(), e.node(), e.offset()) > 0) {
+ if (Range::compareBoundaryPoints(s.node(), s.m_offset, e.node(), e.m_offset) > 0) {
// Make sure the start is before the end.
// The end can wind up before the start if collapsed whitespace is the only thing selected.
Position tmp = s;
@@ -170,22 +180,12 @@ PassRefPtr<Range> Selection::toRange() const
e = rangeCompliantEquivalent(e);
}
- ExceptionCode ec = 0;
- RefPtr<Range> result(Range::create(s.node()->document()));
- result->setStart(s.node(), s.offset(), ec);
- if (ec) {
- LOG_ERROR("Exception setting Range start from Selection: %d", ec);
- return 0;
- }
- result->setEnd(e.node(), e.offset(), ec);
- if (ec) {
- LOG_ERROR("Exception setting Range end from Selection: %d", ec);
- return 0;
- }
- return result.release();
+ // VisibleSelections are supposed to always be valid. This constructor will ASSERT
+ // if a valid range could not be created, which is fine for this callsite.
+ return Range::create(s.node()->document(), s, e);
}
-bool Selection::expandUsingGranularity(TextGranularity granularity)
+bool VisibleSelection::expandUsingGranularity(TextGranularity granularity)
{
if (isNone())
return false;
@@ -213,7 +213,7 @@ static PassRefPtr<Range> makeSearchRange(const Position& pos)
Position start(rangeCompliantEquivalent(pos));
searchRange->selectNodeContents(boundary, ec);
- searchRange->setStart(start.node(), start.offset(), ec);
+ searchRange->setStart(start.node(), start.m_offset, ec);
ASSERT(!ec);
if (ec)
@@ -222,7 +222,7 @@ static PassRefPtr<Range> makeSearchRange(const Position& pos)
return searchRange.release();
}
-void Selection::appendTrailingWhitespace()
+void VisibleSelection::appendTrailingWhitespace()
{
RefPtr<Range> searchRange = makeSearchRange(m_end);
if (!searchRange)
@@ -238,7 +238,7 @@ void Selection::appendTrailingWhitespace()
}
}
-void Selection::validate()
+void VisibleSelection::setBaseAndExtentToDeepEquivalents()
{
// Move the selection to rendered positions, if possible.
bool baseAndExtentEqual = m_base == m_extent;
@@ -259,10 +259,12 @@ void Selection::validate()
} else if (m_extent.isNull()) {
m_extent = m_base;
m_baseIsFirst = true;
- } else {
+ } else
m_baseIsFirst = comparePositions(m_base, m_extent) <= 0;
- }
+}
+void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity()
+{
if (m_baseIsFirst) {
m_start = m_base;
m_end = m_extent;
@@ -271,7 +273,6 @@ void Selection::validate()
m_end = m_base;
}
- // Expand the selection if requested.
switch (m_granularity) {
case CharacterGranularity:
// Don't do any expansion.
@@ -385,26 +386,31 @@ void Selection::validate()
m_start = m_end;
if (m_end.isNull())
m_end = m_start;
-
- adjustForEditableContent();
+}
- // adjust the state
+void VisibleSelection::updateSelectionType()
+{
if (m_start.isNull()) {
ASSERT(m_end.isNull());
- m_state = NONE;
-
- // enforce downstream affinity if not caret, as affinity only
- // makes sense for caret
- m_affinity = DOWNSTREAM;
+ m_selectionType = NoSelection;
} else if (m_start == m_end || m_start.upstream() == m_end.upstream()) {
- m_state = CARET;
- } else {
- m_state = RANGE;
+ m_selectionType = CaretSelection;
+ } else
+ m_selectionType = RangeSelection;
- // enforce downstream affinity if not caret, as affinity only
- // makes sense for caret
+ // Affinity only makes sense for a caret
+ if (m_selectionType != CaretSelection)
m_affinity = DOWNSTREAM;
+}
+
+void VisibleSelection::validate()
+{
+ setBaseAndExtentToDeepEquivalents();
+ setStartAndEndFromBaseAndExtentRespectingGranularity();
+ adjustSelectionToAvoidCrossingEditingBoundaries();
+ updateSelectionType();
+ if (selectionType() == RangeSelection) {
// "Constrain" the selection to be the smallest equivalent range of nodes.
// This is a somewhat arbitrary choice, but experience shows that it is
// useful to make to make the selection "canonical" (if only for
@@ -419,12 +425,12 @@ void Selection::validate()
}
// FIXME: This function breaks the invariant of this class.
-// But because we use Selection to store values in editing commands for use when
+// But because we use VisibleSelection to store values in editing commands for use when
// undoing the command, we need to be able to create a selection that while currently
// invalid, will be valid once the changes are undone. This is a design problem.
-// To fix it we either need to change the invariants of Selection or create a new
+// To fix it we either need to change the invariants of VisibleSelection or create a new
// class for editing to use that can manipulate selections that are not currently valid.
-void Selection::setWithoutValidation(const Position& base, const Position& extent)
+void VisibleSelection::setWithoutValidation(const Position& base, const Position& extent)
{
ASSERT(!base.isNull());
ASSERT(!extent.isNull());
@@ -441,10 +447,10 @@ void Selection::setWithoutValidation(const Position& base, const Position& exten
m_start = extent;
m_end = base;
}
- m_state = RANGE;
+ m_selectionType = RangeSelection;
}
-void Selection::adjustForEditableContent()
+void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries()
{
if (m_base.isNull() || m_start.isNull() || m_end.isNull())
return;
@@ -496,17 +502,19 @@ void Selection::adjustForEditableContent()
Position p = previousVisuallyDistinctCandidate(m_end);
Node* shadowAncestor = endRoot ? endRoot->shadowAncestorNode() : 0;
if (p.isNull() && endRoot && (shadowAncestor != endRoot))
- p = Position(shadowAncestor, maxDeepOffset(shadowAncestor));
+ p = lastDeepEditingPositionForNode(shadowAncestor);
while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) {
Node* root = editableRootForPosition(p);
shadowAncestor = root ? root->shadowAncestorNode() : 0;
p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
if (p.isNull() && (shadowAncestor != root))
- p = Position(shadowAncestor, maxDeepOffset(shadowAncestor));
+ p = lastDeepEditingPositionForNode(shadowAncestor);
}
VisiblePosition previous(p);
-
+
if (previous.isNull()) {
+ // The selection crosses an Editing boundary. This is a
+ // programmer error in the editing code. Happy debugging!
ASSERT_NOT_REACHED();
m_base = Position();
m_extent = Position();
@@ -534,6 +542,8 @@ void Selection::adjustForEditableContent()
VisiblePosition next(p);
if (next.isNull()) {
+ // The selection crosses an Editing boundary. This is a
+ // programmer error in the editing code. Happy debugging!
ASSERT_NOT_REACHED();
m_base = Position();
m_extent = Position();
@@ -549,42 +559,42 @@ void Selection::adjustForEditableContent()
m_extent = m_baseIsFirst ? m_end : m_start;
}
-bool Selection::isContentEditable() const
+bool VisibleSelection::isContentEditable() const
{
return isEditablePosition(start());
}
-bool Selection::isContentRichlyEditable() const
+bool VisibleSelection::isContentRichlyEditable() const
{
return isRichlyEditablePosition(start());
}
-Element* Selection::rootEditableElement() const
+Element* VisibleSelection::rootEditableElement() const
{
return editableRootForPosition(start());
}
-Node* Selection::shadowTreeRootNode() const
+Node* VisibleSelection::shadowTreeRootNode() const
{
return start().node() ? start().node()->shadowTreeRootNode() : 0;
}
-void Selection::debugPosition() const
+void VisibleSelection::debugPosition() const
{
if (!m_start.node())
return;
- fprintf(stderr, "Selection =================\n");
+ fprintf(stderr, "VisibleSelection =================\n");
if (m_start == m_end) {
Position pos = m_start;
- fprintf(stderr, "pos: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset());
+ fprintf(stderr, "pos: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.m_offset);
} else {
Position pos = m_start;
- fprintf(stderr, "start: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset());
+ fprintf(stderr, "start: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.m_offset);
fprintf(stderr, "-----------------------------------\n");
pos = m_end;
- fprintf(stderr, "end: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset());
+ fprintf(stderr, "end: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.m_offset);
fprintf(stderr, "-----------------------------------\n");
}
@@ -593,7 +603,7 @@ void Selection::debugPosition() const
#ifndef NDEBUG
-void Selection::formatForDebugger(char* buffer, unsigned length) const
+void VisibleSelection::formatForDebugger(char* buffer, unsigned length) const
{
String result;
String s;
@@ -614,11 +624,11 @@ void Selection::formatForDebugger(char* buffer, unsigned length) const
strncpy(buffer, result.utf8().data(), length - 1);
}
-void Selection::showTreeForThis() const
+void VisibleSelection::showTreeForThis() const
{
if (start().node()) {
start().node()->showTreeAndMark(start().node(), "S", end().node(), "E");
- fprintf(stderr, "start offset: %d, end offset: %d\n", start().offset(), end().offset());
+ fprintf(stderr, "start offset: %d, end offset: %d\n", start().m_offset, end().m_offset);
}
}
@@ -628,12 +638,12 @@ void Selection::showTreeForThis() const
#ifndef NDEBUG
-void showTree(const WebCore::Selection& sel)
+void showTree(const WebCore::VisibleSelection& sel)
{
sel.showTreeForThis();
}
-void showTree(const WebCore::Selection* sel)
+void showTree(const WebCore::VisibleSelection* sel)
{
if (sel)
sel->showTreeForThis();
diff --git a/WebCore/editing/Selection.h b/WebCore/editing/VisibleSelection.h
index 5e21701..ae2142d 100644
--- a/WebCore/editing/Selection.h
+++ b/WebCore/editing/VisibleSelection.h
@@ -23,8 +23,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Selection_h
-#define Selection_h
+#ifndef VisibleSelection_h
+#define VisibleSelection_h
#include "TextGranularity.h"
#include "VisiblePosition.h"
@@ -35,24 +35,23 @@ class Position;
const EAffinity SEL_DEFAULT_AFFINITY = DOWNSTREAM;
-class Selection {
+class VisibleSelection {
public:
- enum EState { NONE, CARET, RANGE };
- enum EDirection { FORWARD, BACKWARD, RIGHT, LEFT };
+ enum SelectionType { NoSelection, CaretSelection, RangeSelection };
- Selection();
+ VisibleSelection();
- Selection(const Position&, EAffinity);
- Selection(const Position&, const Position&, EAffinity);
+ VisibleSelection(const Position&, EAffinity);
+ VisibleSelection(const Position&, const Position&, EAffinity);
- Selection(const Range*, EAffinity = SEL_DEFAULT_AFFINITY);
+ VisibleSelection(const Range*, EAffinity = SEL_DEFAULT_AFFINITY);
- Selection(const VisiblePosition&);
- Selection(const VisiblePosition&, const VisiblePosition&);
+ VisibleSelection(const VisiblePosition&);
+ VisibleSelection(const VisiblePosition&, const VisiblePosition&);
- static Selection selectionFromContentsOfNode(Node*);
+ static VisibleSelection selectionFromContentsOfNode(Node*);
- EState state() const { return m_state; }
+ SelectionType selectionType() const { return m_selectionType; }
void setAffinity(EAffinity affinity) { m_affinity = affinity; }
EAffinity affinity() const { return m_affinity; }
@@ -63,17 +62,17 @@ public:
void setExtent(const VisiblePosition&);
Position base() const { return m_base; }
- Position extent() const { return m_extent; }
+ Position extent() const { return m_extent; }
Position start() const { return m_start; }
Position end() const { return m_end; }
VisiblePosition visibleStart() const { return VisiblePosition(m_start, isRange() ? DOWNSTREAM : affinity()); }
VisiblePosition visibleEnd() const { return VisiblePosition(m_end, isRange() ? UPSTREAM : affinity()); }
- bool isNone() const { return state() == NONE; }
- bool isCaret() const { return state() == CARET; }
- bool isRange() const { return state() == RANGE; }
- bool isCaretOrRange() const { return state() != NONE; }
+ bool isNone() const { return selectionType() == NoSelection; }
+ bool isCaret() const { return selectionType() == CaretSelection; }
+ bool isRange() const { return selectionType() == RangeSelection; }
+ bool isCaretOrRange() const { return selectionType() != NoSelection; }
bool isBaseFirst() const { return m_baseIsFirst; }
@@ -82,7 +81,13 @@ public:
bool expandUsingGranularity(TextGranularity granularity);
TextGranularity granularity() const { return m_granularity; }
- PassRefPtr<Range> toRange() const;
+ // We don't yet support multi-range selections, so we only ever have one range to return.
+ PassRefPtr<Range> firstRange() const;
+
+ // FIXME: Most callers probably don't want this function, but are using it
+ // for historical reasons. toNormalizedRange contracts the range around
+ // text, and moves the caret upstream before returning the range.
+ PassRefPtr<Range> toNormalizedRange() const;
Element* rootEditableElement() const;
bool isContentEditable() const;
@@ -100,27 +105,33 @@ public:
private:
void validate();
- void adjustForEditableContent();
- Position m_base; // base position for the selection
- Position m_extent; // extent position for the selection
- Position m_start; // start position for the selection
- Position m_end; // end position for the selection
+ // Support methods for validate()
+ void setBaseAndExtentToDeepEquivalents();
+ void setStartAndEndFromBaseAndExtentRespectingGranularity();
+ void adjustSelectionToAvoidCrossingEditingBoundaries();
+ void updateSelectionType();
+
+ // FIXME: These should all be VisiblePositions
+ Position m_base; // Where the first click happened
+ Position m_extent; // Where the end click happened
+ Position m_start; // Leftmost position when expanded to respect granularity
+ Position m_end; // Rightmost position when expanded to respect granularity
- EAffinity m_affinity; // the upstream/downstream affinity of the caret
- TextGranularity m_granularity; // granularity of start/end selection
+ EAffinity m_affinity; // the upstream/downstream affinity of the caret
+ TextGranularity m_granularity; // granularity of start/end selection
// these are cached, can be recalculated by validate()
- EState m_state; // the state of the selection
+ SelectionType m_selectionType; // None, Caret, Range
bool m_baseIsFirst; // true if base is before the extent
};
-inline bool operator==(const Selection& a, const Selection& b)
+inline bool operator==(const VisibleSelection& a, const VisibleSelection& b)
{
return a.start() == b.start() && a.end() == b.end() && a.affinity() == b.affinity() && a.granularity() == b.granularity() && a.isBaseFirst() == b.isBaseFirst();
}
-inline bool operator!=(const Selection& a, const Selection& b)
+inline bool operator!=(const VisibleSelection& a, const VisibleSelection& b)
{
return !(a == b);
}
@@ -129,8 +140,8 @@ inline bool operator!=(const Selection& a, const Selection& b)
#ifndef NDEBUG
// Outside the WebCore namespace for ease of invocation from gdb.
-void showTree(const WebCore::Selection&);
-void showTree(const WebCore::Selection*);
+void showTree(const WebCore::VisibleSelection&);
+void showTree(const WebCore::VisibleSelection*);
#endif
-#endif // Selection_h
+#endif // VisibleSelection_h
diff --git a/WebCore/editing/htmlediting.cpp b/WebCore/editing/htmlediting.cpp
index ee41e69..055c3a7 100644
--- a/WebCore/editing/htmlediting.cpp
+++ b/WebCore/editing/htmlediting.cpp
@@ -40,7 +40,7 @@
#include "PositionIterator.h"
#include "RenderObject.h"
#include "Range.h"
-#include "Selection.h"
+#include "VisibleSelection.h"
#include "Text.h"
#include "TextIterator.h"
#include "VisiblePosition.h"
@@ -98,8 +98,8 @@ int comparePositions(const Position& a, const Position& b)
ASSERT(nodeA);
Node* nodeB = b.node();
ASSERT(nodeB);
- int offsetA = a.offset();
- int offsetB = b.offset();
+ int offsetA = a.m_offset;
+ int offsetB = b.m_offset;
Node* shadowAncestorA = nodeA->shadowAncestorNode();
if (shadowAncestorA == nodeA)
@@ -221,8 +221,8 @@ Position nextVisuallyDistinctCandidate(const Position& position)
{
Position p = position;
Position downstreamStart = p.downstream();
- while (!p.atEnd()) {
- p = p.next(UsingComposedCharacters);
+ while (!p.atEndOfTree()) {
+ p = p.next(Character);
if (p.isCandidate() && p.downstream() != downstreamStart)
return p;
}
@@ -244,8 +244,8 @@ Position previousVisuallyDistinctCandidate(const Position& position)
{
Position p = position;
Position downstreamStart = p.downstream();
- while (!p.atStart()) {
- p = p.previous(UsingComposedCharacters);
+ while (!p.atStartOfTree()) {
+ p = p.previous(Character);
if (p.isCandidate() && p.downstream() != downstreamStart)
return p;
}
@@ -255,14 +255,14 @@ Position previousVisuallyDistinctCandidate(const Position& position)
VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
{
// position falls before highestRoot.
- if (comparePositions(position, Position(highestRoot, 0)) == -1 && highestRoot->isContentEditable())
- return VisiblePosition(Position(highestRoot, 0));
-
+ if (comparePositions(position, firstDeepEditingPositionForNode(highestRoot)) == -1 && highestRoot->isContentEditable())
+ return firstDeepEditingPositionForNode(highestRoot);
+
Position p = position;
if (Node* shadowAncestor = p.node()->shadowAncestorNode())
if (shadowAncestor != p.node())
- p = Position(shadowAncestor, maxDeepOffset(shadowAncestor));
+ p = lastDeepEditingPositionForNode(shadowAncestor);
while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot))
p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p);
@@ -276,14 +276,14 @@ VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& positio
VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
{
// When position falls after highestRoot, the result is easy to compute.
- if (comparePositions(position, Position(highestRoot, maxDeepOffset(highestRoot))) == 1)
- return VisiblePosition(Position(highestRoot, maxDeepOffset(highestRoot)));
-
+ if (comparePositions(position, lastDeepEditingPositionForNode(highestRoot)) == 1)
+ return lastDeepEditingPositionForNode(highestRoot);
+
Position p = position;
if (Node* shadowAncestor = p.node()->shadowAncestorNode())
if (shadowAncestor != p.node())
- p = Position(shadowAncestor, 0);
+ p = firstDeepEditingPositionForNode(shadowAncestor);
while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot))
p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
@@ -294,6 +294,7 @@ VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& positio
return VisiblePosition(p);
}
+// FIXME: The method name, comment, and code say three different things here!
// Whether or not content before and after this node will collapse onto the same line as it.
bool isBlock(const Node* node)
{
@@ -309,39 +310,44 @@ Node* enclosingBlock(Node* node)
return static_cast<Element*>(enclosingNodeOfType(Position(node, 0), isBlock));
}
+// Internally editing uses "invalid" positions for historical reasons. For
+// example, in <div><img /></div>, Editing might use (img, 1) for the position
+// after <img>, but we have to convert that to (div, 1) before handing the
+// position to a Range object. Ideally all internal positions should
+// be "range compliant" for simplicity.
Position rangeCompliantEquivalent(const Position& pos)
{
if (pos.isNull())
return Position();
- Node *node = pos.node();
-
- if (pos.offset() <= 0) {
+ Node* node = pos.node();
+
+ if (pos.m_offset <= 0) {
if (node->parentNode() && (editingIgnoresContent(node) || isTableElement(node)))
return positionBeforeNode(node);
return Position(node, 0);
}
-
+
if (node->offsetInCharacters())
- return Position(node, min(node->maxCharacterOffset(), pos.offset()));
-
+ return Position(node, min(node->maxCharacterOffset(), pos.m_offset));
+
int maxCompliantOffset = node->childNodeCount();
- if (pos.offset() > maxCompliantOffset) {
+ if (pos.m_offset > maxCompliantOffset) {
if (node->parentNode())
return positionAfterNode(node);
-
+
// there is no other option at this point than to
// use the highest allowed position in the node
return Position(node, maxCompliantOffset);
}
// Editing should never generate positions like this.
- if ((pos.offset() < maxCompliantOffset) && editingIgnoresContent(node)) {
+ if ((pos.m_offset < maxCompliantOffset) && editingIgnoresContent(node)) {
ASSERT_NOT_REACHED();
return node->parentNode() ? positionBeforeNode(node) : Position(node, 0);
}
- if (pos.offset() == maxCompliantOffset && (editingIgnoresContent(node) || isTableElement(node)))
+ if (pos.m_offset == maxCompliantOffset && (editingIgnoresContent(node) || isTableElement(node)))
return positionAfterNode(node);
return Position(pos);
@@ -356,7 +362,7 @@ Position rangeCompliantEquivalent(const VisiblePosition& vpos)
// in a node. It returns 1 for some elements even though they do not have children, which
// creates technically invalid DOM Positions. Be sure to call rangeCompliantEquivalent
// on a Position before using it to create a DOM Range, or an exception will be thrown.
-int maxDeepOffset(const Node *node)
+int lastOffsetForEditing(const Node* node)
{
ASSERT(node);
if (!node)
@@ -536,7 +542,7 @@ Position positionOutsideContainingSpecialElement(const Position &pos, Node **con
Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
{
Position upstream(visiblePosition.deepEquivalent().upstream());
- if (upstream.node() && upstream.node()->renderer() && upstream.node()->renderer()->isTable() && upstream.offset() == maxDeepOffset(upstream.node()))
+ if (upstream.node() && upstream.node()->renderer() && upstream.node()->renderer()->isTable() && upstream.atLastEditingPositionForNode())
return upstream.node();
return 0;
@@ -545,19 +551,21 @@ Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
{
Position downstream(visiblePosition.deepEquivalent().downstream());
- if (downstream.node() && downstream.node()->renderer() && downstream.node()->renderer()->isTable() && downstream.offset() == 0)
+ if (downstream.node() && downstream.node()->renderer() && downstream.node()->renderer()->isTable() && downstream.atFirstEditingPositionForNode())
return downstream.node();
return 0;
}
-Position positionBeforeNode(const Node *node)
+Position positionBeforeNode(const Node* node)
{
+ // FIXME: This should ASSERT(node->parentNode())
return Position(node->parentNode(), node->nodeIndex());
}
-Position positionAfterNode(const Node *node)
+Position positionAfterNode(const Node* node)
{
+ // FIXME: This should ASSERT(node->parentNode())
return Position(node->parentNode(), node->nodeIndex() + 1);
}
@@ -694,16 +702,17 @@ static Node* appendedSublist(Node* listItem)
return 0;
}
+// FIXME: This method should not need to call isStartOfParagraph/isEndOfParagraph
Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
{
// Check that position is on a line by itself inside a list item
Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().node());
if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
return 0;
-
- VisiblePosition firstInListChild(Position(listChildNode, 0));
- VisiblePosition lastInListChild(Position(listChildNode, maxDeepOffset(listChildNode)));
-
+
+ VisiblePosition firstInListChild(firstDeepEditingPositionForNode(listChildNode));
+ VisiblePosition lastInListChild(lastDeepEditingPositionForNode(listChildNode));
+
if (firstInListChild != visiblePos || lastInListChild != visiblePos)
return 0;
@@ -814,16 +823,16 @@ Position positionBeforeTabSpan(const Position& pos)
PassRefPtr<Element> createTabSpanElement(Document* document, PassRefPtr<Node> tabTextNode)
{
- // make the span to hold the tab
- ExceptionCode ec = 0;
- RefPtr<Element> spanElement = document->createElementNS(xhtmlNamespaceURI, "span", ec);
- ASSERT(ec == 0);
+ // Make the span to hold the tab.
+ RefPtr<Element> spanElement = document->createElement(spanTag, false);
spanElement->setAttribute(classAttr, AppleTabSpanClass);
spanElement->setAttribute(styleAttr, "white-space:pre");
- // add tab text to that span
+ // Add tab text to that span.
if (!tabTextNode)
tabTextNode = document->createEditingTextNode("\t");
+
+ ExceptionCode ec = 0;
spanElement->appendChild(tabTextNode, ec);
ASSERT(ec == 0);
@@ -873,7 +882,7 @@ unsigned numEnclosingMailBlockquotes(const Position& p)
bool isMailBlockquote(const Node *node)
{
- if (!node || !node->isElementNode() && !node->hasTagName(blockquoteTag))
+ if (!node || (!node->isElementNode() && !node->hasTagName(blockquoteTag)))
return false;
return static_cast<const Element *>(node)->getAttribute("type") == "cite";
@@ -894,7 +903,7 @@ int caretMaxOffset(const Node* n)
if (n->isTextNode() && n->renderer())
return n->renderer()->caretMaxOffset();
// For containers return the number of children. For others do the same as above.
- return maxDeepOffset(n);
+ return lastOffsetForEditing(n);
}
bool lineBreakExistsAtPosition(const VisiblePosition& visiblePosition)
@@ -904,15 +913,15 @@ bool lineBreakExistsAtPosition(const VisiblePosition& visiblePosition)
Position downstream(visiblePosition.deepEquivalent().downstream());
return downstream.node()->hasTagName(brTag) ||
- downstream.node()->isTextNode() && downstream.node()->renderer()->style()->preserveNewline() && visiblePosition.characterAfter() == '\n';
+ (downstream.node()->isTextNode() && downstream.node()->renderer()->style()->preserveNewline() && visiblePosition.characterAfter() == '\n');
}
// Modifies selections that have an end point at the edge of a table
// that contains the other endpoint so that they don't confuse
// code that iterates over selected paragraphs.
-Selection selectionForParagraphIteration(const Selection& original)
+VisibleSelection selectionForParagraphIteration(const VisibleSelection& original)
{
- Selection newSelection(original);
+ VisibleSelection newSelection(original);
VisiblePosition startOfSelection(newSelection.visibleStart());
VisiblePosition endOfSelection(newSelection.visibleEnd());
@@ -922,7 +931,7 @@ Selection selectionForParagraphIteration(const Selection& original)
// (a table is itself a paragraph).
if (Node* table = isFirstPositionAfterTable(endOfSelection))
if (startOfSelection.deepEquivalent().node()->isDescendantOf(table))
- newSelection = Selection(startOfSelection, endOfSelection.previous(true));
+ newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(true));
// If the start of the selection to modify is just before a table,
// and if the end of the selection is inside that table, then the first paragraph
@@ -930,7 +939,7 @@ Selection selectionForParagraphIteration(const Selection& original)
// containing the table itself.
if (Node* table = isLastPositionBeforeTable(startOfSelection))
if (endOfSelection.deepEquivalent().node()->isDescendantOf(table))
- newSelection = Selection(startOfSelection.next(true), endOfSelection);
+ newSelection = VisibleSelection(startOfSelection.next(true), endOfSelection);
return newSelection;
}
@@ -976,12 +985,12 @@ PassRefPtr<Range> avoidIntersectionWithNode(const Range* range, Node* node)
return Range::create(document, startContainer, startOffset, endContainer, endOffset);
}
-Selection avoidIntersectionWithNode(const Selection& selection, Node* node)
+VisibleSelection avoidIntersectionWithNode(const VisibleSelection& selection, Node* node)
{
if (selection.isNone())
- return Selection(selection);
+ return VisibleSelection(selection);
- Selection updatedSelection(selection);
+ VisibleSelection updatedSelection(selection);
Node* base = selection.base().node();
Node* extent = selection.extent().node();
ASSERT(base);
diff --git a/WebCore/editing/htmlediting.h b/WebCore/editing/htmlediting.h
index a8bc0b4..ece5e29 100644
--- a/WebCore/editing/htmlediting.h
+++ b/WebCore/editing/htmlediting.h
@@ -37,13 +37,13 @@ class HTMLElement;
class Node;
class Position;
class Range;
-class Selection;
+class VisibleSelection;
class String;
class VisiblePosition;
Position rangeCompliantEquivalent(const Position&);
Position rangeCompliantEquivalent(const VisiblePosition&);
-int maxDeepOffset(const Node*);
+int lastOffsetForEditing(const Node*);
bool isAtomicNode(const Node*);
bool editingIgnoresContent(const Node*);
bool canHaveChildrenForEditing(const Node*);
@@ -72,7 +72,7 @@ Position positionBeforeNode(const Node*);
Position positionAfterNode(const Node*);
PassRefPtr<Range> avoidIntersectionWithNode(const Range*, Node*);
-Selection avoidIntersectionWithNode(const Selection&, Node*);
+VisibleSelection avoidIntersectionWithNode(const VisibleSelection&, Node*);
bool isSpecialElement(const Node*);
bool validBlockTag(const String&);
@@ -129,7 +129,7 @@ bool isTableCell(const Node*);
bool lineBreakExistsAtPosition(const VisiblePosition&);
-Selection selectionForParagraphIteration(const Selection&);
+VisibleSelection selectionForParagraphIteration(const VisibleSelection&);
int indexForVisiblePosition(VisiblePosition&);
diff --git a/WebCore/editing/mac/SelectionControllerMac.mm b/WebCore/editing/mac/SelectionControllerMac.mm
index 4125c3a..03e1051 100644
--- a/WebCore/editing/mac/SelectionControllerMac.mm
+++ b/WebCore/editing/mac/SelectionControllerMac.mm
@@ -27,42 +27,41 @@
#import "SelectionController.h"
#import "AXObjectCache.h"
-#import "Document.h"
#import "Frame.h"
-#import "FrameView.h"
#import "RenderView.h"
-#import "Selection.h"
#import "WebCoreViewFactory.h"
-#import <ApplicationServices/ApplicationServices.h>
-
namespace WebCore {
void SelectionController::notifyAccessibilityForSelectionChange()
{
+ Document* document = m_frame->document();
+
if (AXObjectCache::accessibilityEnabled() && m_sel.start().isNotNull() && m_sel.end().isNotNull())
- m_frame->document()->axObjectCache()->postNotification(m_sel.start().node()->renderer(), "AXSelectedTextChanged");
-
+ document->axObjectCache()->postNotification(m_sel.start().node()->renderer(), "AXSelectedTextChanged");
+
// if zoom feature is enabled, insertion point changes should update the zoom
- if (UAZoomEnabled() && m_sel.isCaret() && m_sel.start().node()) {
- RenderView *renderView = static_cast<RenderView*>(m_sel.start().node()->renderer());
- if (renderView) {
- IntRect selectionRect = absoluteCaretBounds();
- IntRect viewRect = renderView->viewRect();
- FrameView* frameView = renderView->view()->frameView();
- if (frameView) {
- selectionRect = frameView->contentsToScreen(selectionRect);
- viewRect = frameView->contentsToScreen(viewRect);
- CGRect cgCaretRect = CGRectMake(selectionRect.x(), selectionRect.y(), selectionRect.width(), selectionRect.height());
- CGRect cgViewRect = CGRectMake(viewRect.x(), viewRect.y(), viewRect.width(), viewRect.height());
- cgCaretRect = [[WebCoreViewFactory sharedFactory] accessibilityConvertScreenRect:cgCaretRect];
- cgViewRect = [[WebCoreViewFactory sharedFactory] accessibilityConvertScreenRect:cgViewRect];
+ if (!UAZoomEnabled() || !m_sel.isCaret())
+ return;
- (void)UAZoomChangeFocus(&cgViewRect, &cgCaretRect, kUAZoomFocusTypeInsertionPoint);
- }
- }
- }
-}
+ RenderView* renderView = document->renderView();
+ if (!renderView)
+ return;
+ FrameView* frameView = m_frame->view();
+ if (!frameView)
+ return;
+ IntRect selectionRect = absoluteCaretBounds();
+ IntRect viewRect = renderView->viewRect();
+
+ selectionRect = frameView->contentsToScreen(selectionRect);
+ viewRect = frameView->contentsToScreen(viewRect);
+ CGRect cgCaretRect = CGRectMake(selectionRect.x(), selectionRect.y(), selectionRect.width(), selectionRect.height());
+ CGRect cgViewRect = CGRectMake(viewRect.x(), viewRect.y(), viewRect.width(), viewRect.height());
+ cgCaretRect = [[WebCoreViewFactory sharedFactory] accessibilityConvertScreenRect:cgCaretRect];
+ cgViewRect = [[WebCoreViewFactory sharedFactory] accessibilityConvertScreenRect:cgViewRect];
+
+ UAZoomChangeFocus(&cgViewRect, &cgCaretRect, kUAZoomFocusTypeInsertionPoint);
+}
} // namespace WebCore
diff --git a/WebCore/editing/markup.cpp b/WebCore/editing/markup.cpp
index 1352457..7de5287 100644
--- a/WebCore/editing/markup.cpp
+++ b/WebCore/editing/markup.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -52,7 +52,7 @@
#include "ProcessingInstruction.h"
#include "QualifiedName.h"
#include "Range.h"
-#include "Selection.h"
+#include "VisibleSelection.h"
#include "TextIterator.h"
#include "htmlediting.h"
#include "visible_units.h"
@@ -408,8 +408,7 @@ static void appendStartMarkup(Vector<UChar>& result, const Node *node, const Ran
bool useRenderedText = !enclosingNodeWithTag(Position(const_cast<Node*>(node), 0), selectTag);
String markup = escapeContentText(useRenderedText ? renderedText(node, range) : stringValueForRange(node, range), false);
- if (annotate)
- markup = convertHTMLTextToInterchangeFormat(markup, static_cast<const Text*>(node));
+ markup = convertHTMLTextToInterchangeFormat(markup, static_cast<const Text*>(node));
append(result, markup);
break;
}
@@ -438,7 +437,7 @@ static void appendStartMarkup(Vector<UChar>& result, const Node *node, const Ran
case Node::ELEMENT_NODE: {
result.append('<');
const Element* el = static_cast<const Element*>(node);
- bool convert = convertBlocksToInlines & isBlock(const_cast<Node*>(node));
+ bool convert = convertBlocksToInlines && isBlock(const_cast<Node*>(node));
append(result, el->nodeNamePreservingCase());
NamedAttrMap *attrs = el->attributes();
unsigned length = attrs->length();
@@ -587,26 +586,50 @@ static String getEndMarkup(const Node *node)
return String::adopt(result);
}
-static void appendMarkup(Vector<UChar>& result, Node* startNode, bool onlyIncludeChildren, Vector<Node*>* nodes, const HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0)
+class MarkupAccumulator {
+public:
+ MarkupAccumulator(Node* nodeToSkip, Vector<Node*>* nodes)
+ : m_nodeToSkip(nodeToSkip)
+ , m_nodes(nodes)
+ {
+ }
+
+ void appendMarkup(Node* startNode, EChildrenOnly, const HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0);
+
+ String takeResult() { return String::adopt(m_result); }
+
+private:
+ Vector<UChar> m_result;
+ Node* m_nodeToSkip;
+ Vector<Node*>* m_nodes;
+};
+
+// FIXME: Would be nice to do this in a non-recursive way.
+void MarkupAccumulator::appendMarkup(Node* startNode, EChildrenOnly childrenOnly, const HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces)
{
+ if (startNode == m_nodeToSkip)
+ return;
+
HashMap<AtomicStringImpl*, AtomicStringImpl*> namespaceHash;
if (namespaces)
namespaceHash = *namespaces;
-
- if (!onlyIncludeChildren) {
- if (nodes)
- nodes->append(startNode);
-
- appendStartMarkup(result,startNode, 0, DoNotAnnotateForInterchange, false, &namespaceHash);
+
+ // start tag
+ if (!childrenOnly) {
+ if (m_nodes)
+ m_nodes->append(startNode);
+ appendStartMarkup(m_result, startNode, 0, DoNotAnnotateForInterchange, false, &namespaceHash);
}
- // print children
- if (!(startNode->document()->isHTMLDocument() && doesHTMLForbidEndTag(startNode)))
+
+ // children
+ if (!(startNode->document()->isHTMLDocument() && doesHTMLForbidEndTag(startNode))) {
for (Node* current = startNode->firstChild(); current; current = current->nextSibling())
- appendMarkup(result, current, false, nodes, &namespaceHash);
-
- // Print my ending tag
- if (!onlyIncludeChildren)
- appendEndMarkup(result, startNode);
+ appendMarkup(current, IncludeNode, &namespaceHash);
+ }
+
+ // end tag
+ if (!childrenOnly)
+ appendEndMarkup(m_result, startNode);
}
static void completeURLs(Node* node, const String& baseURL)
@@ -690,7 +713,7 @@ static String joinMarkups(const Vector<String>& preMarkups, const Vector<String>
length += postMarkups[i].length();
Vector<UChar> result;
- result.reserveCapacity(length);
+ result.reserveInitialCapacity(length);
for (size_t i = preCount; i > 0; --i)
append(result, preMarkups[i - 1]);
@@ -701,6 +724,24 @@ static String joinMarkups(const Vector<String>& preMarkups, const Vector<String>
return String::adopt(result);
}
+static bool isSpecialAncestorBlock(Node* node)
+{
+ if (!node || !isBlock(node))
+ return false;
+
+ return node->hasTagName(listingTag) ||
+ node->hasTagName(olTag) ||
+ node->hasTagName(preTag) ||
+ node->hasTagName(tableTag) ||
+ node->hasTagName(ulTag) ||
+ node->hasTagName(xmpTag) ||
+ node->hasTagName(h1Tag) ||
+ node->hasTagName(h2Tag) ||
+ node->hasTagName(h3Tag) ||
+ node->hasTagName(h4Tag) ||
+ node->hasTagName(h5Tag);
+}
+
// FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange?
// FIXME: At least, annotation and style info should probably not be included in range.markupString()
String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterchange annotate, bool convertBlocksToInlines)
@@ -849,12 +890,7 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc
table = table->parentNode();
if (table)
specialCommonAncestor = table;
- } else if (commonAncestorBlock->hasTagName(listingTag)
- || commonAncestorBlock->hasTagName(olTag)
- || commonAncestorBlock->hasTagName(preTag)
- || commonAncestorBlock->hasTagName(tableTag)
- || commonAncestorBlock->hasTagName(ulTag)
- || commonAncestorBlock->hasTagName(xmpTag))
+ } else if (isSpecialAncestorBlock(commonAncestorBlock))
specialCommonAncestor = commonAncestorBlock;
}
@@ -888,7 +924,7 @@ String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterc
// FIXME: Only include markup for a fully selected root (and ancestors of lastClosed up to that root) if
// there are styles/attributes on those nodes that need to be included to preserve the appearance of the copied markup.
// FIXME: Do this for all fully selected blocks, not just the body.
- Node* fullySelectedRoot = body && *Selection::selectionFromContentsOfNode(body).toRange() == *updatedRange ? body : 0;
+ Node* fullySelectedRoot = body && *VisibleSelection::selectionFromContentsOfNode(body).toNormalizedRange() == *updatedRange ? body : 0;
if (annotate && fullySelectedRoot)
specialCommonAncestor = fullySelectedRoot;
@@ -1004,30 +1040,21 @@ PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document* document, const
return fragment.release();
}
-String createMarkup(const Node* node, EChildrenOnly includeChildren, Vector<Node*>* nodes)
+String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>* nodes)
{
- Vector<UChar> result;
-
if (!node)
return "";
- Document* document = node->document();
- Frame* frame = document->frame();
- DeleteButtonController* deleteButton = frame ? frame->editor()->deleteButtonController() : 0;
-
- // disable the delete button so it's elements are not serialized into the markup
- if (deleteButton) {
- if (node->isDescendantOf(deleteButton->containerElement()))
+ HTMLElement* deleteButtonContainerElement = 0;
+ if (Frame* frame = node->document()->frame()) {
+ deleteButtonContainerElement = frame->editor()->deleteButtonController()->containerElement();
+ if (node->isDescendantOf(deleteButtonContainerElement))
return "";
- deleteButton->disable();
}
- appendMarkup(result, const_cast<Node*>(node), includeChildren, nodes);
-
- if (deleteButton)
- deleteButton->enable();
-
- return String::adopt(result);
+ MarkupAccumulator accumulator(deleteButtonContainerElement, nodes);
+ accumulator.appendMarkup(const_cast<Node*>(node), childrenOnly);
+ return accumulator.takeResult();
}
static void fillContainerFromString(ContainerNode* paragraph, const String& string)
@@ -1140,7 +1167,7 @@ PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String
element->setAttribute(classAttr, AppleInterchangeNewline);
} else {
if (useClonesOfEnclosingBlock)
- element = block->cloneElement();
+ element = block->cloneElementWithoutChildren();
else
element = createDefaultParagraphElement(document);
fillContainerFromString(element.get(), s);
diff --git a/WebCore/editing/qt/EditorQt.cpp b/WebCore/editing/qt/EditorQt.cpp
index 89ee78e..e74e2f3 100644
--- a/WebCore/editing/qt/EditorQt.cpp
+++ b/WebCore/editing/qt/EditorQt.cpp
@@ -31,7 +31,7 @@
#include "ClipboardQt.h"
#include "Document.h"
#include "Element.h"
-#include "Selection.h"
+#include "VisibleSelection.h"
#include "SelectionController.h"
#include "TextIterator.h"
#include "htmlediting.h"
diff --git a/WebCore/editing/visible_units.cpp b/WebCore/editing/visible_units.cpp
index a50503d..1e8b05b 100644
--- a/WebCore/editing/visible_units.cpp
+++ b/WebCore/editing/visible_units.cpp
@@ -36,12 +36,32 @@
#include "TextIterator.h"
#include "VisiblePosition.h"
#include "htmlediting.h"
+#include <wtf/unicode/Unicode.h>
namespace WebCore {
using namespace HTMLNames;
+using namespace WTF::Unicode;
-static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned))
+static int firstNonComplexContextLineBreak(const UChar* characters, int length)
+{
+ for (int i = 0; i < length; ++i) {
+ if (!hasLineBreakingPropertyComplexContext(characters[i]))
+ return i;
+ }
+ return length;
+}
+
+static int lastNonComplexContextLineBreak(const UChar* characters, int length)
+{
+ for (int i = length - 1; i >= 0; --i) {
+ if (!hasLineBreakingPropertyComplexContext(characters[i]))
+ return i;
+ }
+ return -1;
+}
+
+static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned, unsigned))
{
Position pos = c.deepEquivalent();
Node *n = pos.node();
@@ -62,16 +82,35 @@ static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*sea
Position end = rangeCompliantEquivalent(pos);
RefPtr<Range> searchRange = Range::create(d);
- int exception = 0;
- searchRange->setStart(start.node(), start.offset(), exception);
- searchRange->setEnd(end.node(), end.offset(), exception);
+ Vector<UChar, 1024> string;
+ unsigned suffixLength = 0;
+
+ ExceptionCode ec = 0;
+ if (hasLineBreakingPropertyComplexContext(c.characterBefore())) {
+ RefPtr<Range> forwardsScanRange(d->createRange());
+ forwardsScanRange->setEndAfter(boundary, ec);
+ forwardsScanRange->setStart(end.node(), end.m_offset, ec);
+ TextIterator forwardsIterator(forwardsScanRange.get());
+ while (!forwardsIterator.atEnd()) {
+ const UChar* characters = forwardsIterator.characters();
+ int length = forwardsIterator.length();
+ int i = firstNonComplexContextLineBreak(characters, length);
+ string.append(characters, i);
+ suffixLength += i;
+ if (i < length)
+ break;
+ forwardsIterator.advance();
+ }
+ }
+
+ searchRange->setStart(start.node(), start.m_offset, ec);
+ searchRange->setEnd(end.node(), end.m_offset, ec);
- ASSERT(!exception);
- if (exception)
+ ASSERT(!ec);
+ if (ec)
return VisiblePosition();
-
+
SimplifiedBackwardsTextIterator it(searchRange.get());
- Vector<UChar, 1024> string;
unsigned next = 0;
bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;
while (!it.atEnd()) {
@@ -85,7 +124,7 @@ static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*sea
string.prepend(iteratorString.characters(), iteratorString.length());
}
- next = searchFunction(string.data(), string.size());
+ next = searchFunction(string.data(), string.size(), string.size() - suffixLength);
if (next != 0)
break;
it.advance();
@@ -94,26 +133,22 @@ static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*sea
if (it.atEnd() && next == 0) {
pos = it.range()->startPosition();
} else if (next != 0) {
- Node *node = it.range()->startContainer(exception);
- if (node->isTextNode() || (node->renderer() && node->renderer()->isBR()))
+ Node *node = it.range()->startContainer(ec);
+ if ((node->isTextNode() && static_cast<int>(next) <= node->maxCharacterOffset()) || (node->renderer() && node->renderer()->isBR() && !next))
// The next variable contains a usable index into a text node
pos = Position(node, next);
else {
- // Use the end of the found range, the start is not guaranteed to
- // be correct.
- Position end = it.range()->endPosition();
- VisiblePosition boundary(end);
- unsigned i = it.length() - next;
- while (i--)
- boundary = boundary.previous();
- return boundary;
+ // Use the character iterator to translate the next value into a DOM position.
+ BackwardsCharacterIterator charIt(searchRange.get());
+ charIt.advance(string.size() - suffixLength - next);
+ pos = charIt.range()->endPosition();
}
}
return VisiblePosition(pos, DOWNSTREAM);
}
-static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned))
+static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned, unsigned))
{
Position pos = c.deepEquivalent();
Node *n = pos.node();
@@ -132,11 +167,30 @@ static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchF
RefPtr<Range> searchRange(d->createRange());
Position start(rangeCompliantEquivalent(pos));
+
+ Vector<UChar, 1024> string;
+ unsigned prefixLength = 0;
+
ExceptionCode ec = 0;
+ if (hasLineBreakingPropertyComplexContext(c.characterAfter())) {
+ RefPtr<Range> backwardsScanRange(d->createRange());
+ backwardsScanRange->setEnd(start.node(), start.m_offset, ec);
+ SimplifiedBackwardsTextIterator backwardsIterator(backwardsScanRange.get());
+ while (!backwardsIterator.atEnd()) {
+ const UChar* characters = backwardsIterator.characters();
+ int length = backwardsIterator.length();
+ int i = lastNonComplexContextLineBreak(characters, length);
+ string.prepend(characters + i + 1, length - i - 1);
+ prefixLength += length - i - 1;
+ if (i > -1)
+ break;
+ backwardsIterator.advance();
+ }
+ }
+
searchRange->selectNodeContents(boundary, ec);
- searchRange->setStart(start.node(), start.offset(), ec);
+ searchRange->setStart(start.node(), start.m_offset, ec);
TextIterator it(searchRange.get(), true);
- Vector<UChar, 1024> string;
unsigned next = 0;
bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;
while (!it.atEnd()) {
@@ -151,7 +205,7 @@ static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchF
string.append(iteratorString.characters(), iteratorString.length());
}
- next = searchFunction(string.data(), string.size());
+ next = searchFunction(string.data(), string.size(), prefixLength);
if (next != string.size())
break;
it.advance();
@@ -159,16 +213,18 @@ static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchF
if (it.atEnd() && next == string.size()) {
pos = it.range()->startPosition();
- } else if (next != 0) {
+ } else if (next != prefixLength) {
// Use the character iterator to translate the next value into a DOM position.
CharacterIterator charIt(searchRange.get(), true);
- charIt.advance(next - 1);
+ charIt.advance(next - prefixLength - 1);
pos = charIt.range()->endPosition();
- // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593)
- VisiblePosition visPos = VisiblePosition(pos);
- if (visPos == VisiblePosition(charIt.range()->startPosition()))
- pos = visPos.next(true).deepEquivalent();
+ if (*charIt.characters() == '\n') {
+ // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593)
+ VisiblePosition visPos = VisiblePosition(pos);
+ if (visPos == VisiblePosition(charIt.range()->startPosition()))
+ pos = visPos.next(true).deepEquivalent();
+ }
}
// generate VisiblePosition, use UPSTREAM affinity if possible
@@ -177,10 +233,13 @@ static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchF
// ---------
-static unsigned startWordBoundary(const UChar* characters, unsigned length)
+static unsigned startWordBoundary(const UChar* characters, unsigned length, unsigned offset)
{
+ ASSERT(offset);
+ if (lastNonComplexContextLineBreak(characters, offset) == -1)
+ return 0;
int start, end;
- findWordBoundary(characters, length, length, &start, &end);
+ findWordBoundary(characters, length, offset - 1, &start, &end);
return start;
}
@@ -201,10 +260,13 @@ VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
return previousBoundary(p, startWordBoundary);
}
-static unsigned endWordBoundary(const UChar* characters, unsigned length)
+static unsigned endWordBoundary(const UChar* characters, unsigned length, unsigned offset)
{
+ ASSERT(offset <= length);
+ if (firstNonComplexContextLineBreak(characters + offset, length - offset) == static_cast<int>(length - offset))
+ return length;
int start, end;
- findWordBoundary(characters, length, 0, &start, &end);
+ findWordBoundary(characters, length, offset, &start, &end);
return end;
}
@@ -224,9 +286,11 @@ VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side)
return nextBoundary(p, endWordBoundary);
}
-static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length)
+static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset)
{
- return findNextWordFromIndex(characters, length, length, false);
+ if (lastNonComplexContextLineBreak(characters, offset) == -1)
+ return 0;
+ return findNextWordFromIndex(characters, length, offset, false);
}
VisiblePosition previousWordPosition(const VisiblePosition &c)
@@ -235,9 +299,11 @@ VisiblePosition previousWordPosition(const VisiblePosition &c)
return c.honorEditableBoundaryAtOrAfter(prev);
}
-static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length)
+static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset)
{
- return findNextWordFromIndex(characters, length, 0, true);
+ if (firstNonComplexContextLineBreak(characters + offset, length - offset) == static_cast<int>(length - offset))
+ return length;
+ return findNextWordFromIndex(characters, length, offset, true);
}
VisiblePosition nextWordPosition(const VisiblePosition &c)
@@ -286,7 +352,7 @@ static VisiblePosition startPositionForLine(const VisiblePosition& c)
// There are VisiblePositions at offset 0 in blocks without
// RootInlineBoxes, like empty editable blocks and bordered blocks.
Position p = c.deepEquivalent();
- if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.offset() == 0)
+ if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.m_offset == 0)
return positionAvoidingFirstPositionInTable(c);
return VisiblePosition();
@@ -301,11 +367,11 @@ static VisiblePosition startPositionForLine(const VisiblePosition& c)
if (!startBox)
return VisiblePosition();
- RenderObject *startRenderer = startBox->object();
+ RenderObject *startRenderer = startBox->renderer();
if (!startRenderer)
return VisiblePosition();
- startNode = startRenderer->element();
+ startNode = startRenderer->node();
if (startNode)
break;
@@ -333,7 +399,7 @@ VisiblePosition startOfLine(const VisiblePosition& c)
// greater than the input position. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space
// style versus lines without that style, which would break before a space by default.
Position p = visPos.deepEquivalent();
- if (p.offset() > c.deepEquivalent().offset() && p.node()->isSameNode(c.deepEquivalent().node())) {
+ if (p.m_offset > c.deepEquivalent().m_offset && p.node()->isSameNode(c.deepEquivalent().node())) {
visPos = c.previous();
if (visPos.isNull())
return VisiblePosition();
@@ -354,7 +420,7 @@ static VisiblePosition endPositionForLine(const VisiblePosition& c)
// There are VisiblePositions at offset 0 in blocks without
// RootInlineBoxes, like empty editable blocks and bordered blocks.
Position p = c.deepEquivalent();
- if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.offset() == 0)
+ if (p.node()->renderer() && p.node()->renderer()->isRenderBlock() && p.m_offset == 0)
return c;
return VisiblePosition();
}
@@ -368,11 +434,11 @@ static VisiblePosition endPositionForLine(const VisiblePosition& c)
if (!endBox)
return VisiblePosition();
- RenderObject *endRenderer = endBox->object();
+ RenderObject *endRenderer = endBox->renderer();
if (!endRenderer)
return VisiblePosition();
- endNode = endRenderer->element();
+ endNode = endRenderer->node();
if (endNode)
break;
@@ -498,7 +564,7 @@ VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int
FloatPoint absPos = containingBlock->localToAbsolute(FloatPoint());
if (containingBlock->hasOverflowClip())
absPos -= containingBlock->layer()->scrolledContentOffset();
- RenderObject *renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->object();
+ RenderObject* renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->renderer();
Node* node = renderer->node();
if (editingIgnoresContent(node))
return Position(node->parent(), node->nodeIndex());
@@ -571,7 +637,7 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x)
// Need to move forward to next containing editable block in this root editable
// block and find the first root line box in that block.
Node* startBlock = enclosingBlock(node);
- Node* n = nextLeafWithSameEditability(node, p.offset());
+ Node* n = nextLeafWithSameEditability(node, p.m_offset);
while (n && startBlock == enclosingBlock(n))
n = nextLeafWithSameEditability(n);
while (n) {
@@ -599,7 +665,7 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x)
FloatPoint absPos = containingBlock->localToAbsolute(FloatPoint());
if (containingBlock->hasOverflowClip())
absPos -= containingBlock->layer()->scrolledContentOffset();
- RenderObject *renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->object();
+ RenderObject* renderer = root->closestLeafChildForXPos(x - absPos.x(), isEditablePosition(p))->renderer();
Node* node = renderer->node();
if (editingIgnoresContent(node))
return Position(node->parent(), node->nodeIndex());
@@ -615,7 +681,7 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x)
// ---------
-static unsigned startSentenceBoundary(const UChar* characters, unsigned length)
+static unsigned startSentenceBoundary(const UChar* characters, unsigned length, unsigned)
{
TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
// FIXME: The following function can return -1; we don't handle that.
@@ -627,7 +693,7 @@ VisiblePosition startOfSentence(const VisiblePosition &c)
return previousBoundary(c, startSentenceBoundary);
}
-static unsigned endSentenceBoundary(const UChar* characters, unsigned length)
+static unsigned endSentenceBoundary(const UChar* characters, unsigned length, unsigned)
{
TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
return textBreakNext(iterator);
@@ -639,7 +705,7 @@ VisiblePosition endOfSentence(const VisiblePosition &c)
return nextBoundary(c, endSentenceBoundary);
}
-static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length)
+static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length, unsigned)
{
// FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right.
TextBreakIterator* iterator = sentenceBreakIterator(characters, length);
@@ -653,7 +719,7 @@ VisiblePosition previousSentencePosition(const VisiblePosition &c)
return c.honorEditableBoundaryAtOrAfter(prev);
}
-static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length)
+static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length, unsigned)
{
// FIXME: This is identical to endSentenceBoundary. This isn't right, it needs to
// move to the equivlant position in the following sentence.
@@ -667,8 +733,13 @@ VisiblePosition nextSentencePosition(const VisiblePosition &c)
return c.honorEditableBoundaryAtOrBefore(next);
}
+static bool renderedAsNonInlineTableOrHR(RenderObject* renderer)
+{
+ return renderer && ((renderer->isTable() && !renderer->isInline()) || renderer->isHR());
+}
+
// FIXME: Broken for positions before/after images that aren't inline (5027702)
-VisiblePosition startOfParagraph(const VisiblePosition &c)
+VisiblePosition startOfParagraph(const VisiblePosition& c)
{
Position p = c.deepEquivalent();
Node *startNode = p.node();
@@ -676,16 +747,13 @@ VisiblePosition startOfParagraph(const VisiblePosition &c)
if (!startNode)
return VisiblePosition();
- if (startNode->renderer()
- && ((startNode->renderer()->isTable() && !startNode->renderer()->isInline())
- || startNode->renderer()->isHR())
- && p.offset() == maxDeepOffset(startNode))
- return VisiblePosition(Position(startNode, 0));
+ if (renderedAsNonInlineTableOrHR(startNode->renderer()) && p.atLastEditingPositionForNode())
+ return firstDeepEditingPositionForNode(startNode);
Node* startBlock = enclosingBlock(startNode);
Node *node = startNode;
- int offset = p.offset();
+ int offset = p.m_offset;
Node *n = startNode;
while (n) {
@@ -739,17 +807,14 @@ VisiblePosition endOfParagraph(const VisiblePosition &c)
Position p = c.deepEquivalent();
Node* startNode = p.node();
- if (startNode->renderer()
- && ((startNode->renderer()->isTable() && !startNode->renderer()->isInline())
- || startNode->renderer()->isHR())
- && p.offset() == 0)
- return VisiblePosition(Position(startNode, maxDeepOffset(startNode)));
+ if (renderedAsNonInlineTableOrHR(startNode->renderer()) && p.atFirstEditingPositionForNode())
+ return lastDeepEditingPositionForNode(startNode);
Node* startBlock = enclosingBlock(startNode);
Node *stayInsideBlock = startBlock;
Node *node = startNode;
- int offset = p.offset();
+ int offset = p.m_offset;
Node *n = startNode;
while (n) {
@@ -785,7 +850,7 @@ VisiblePosition endOfParagraph(const VisiblePosition &c)
n = n->traverseNextNode(stayInsideBlock);
} else if (editingIgnoresContent(n) || isTableElement(n)) {
node = n;
- offset = maxDeepOffset(n);
+ offset = lastOffsetForEditing(n);
n = n->traverseNextSibling(stayInsideBlock);
} else
n = n->traverseNextNode(stayInsideBlock);
@@ -820,25 +885,25 @@ bool isEndOfParagraph(const VisiblePosition &pos)
return pos.isNotNull() && pos == endOfParagraph(pos);
}
-VisiblePosition previousParagraphPosition(const VisiblePosition &p, int x)
+VisiblePosition previousParagraphPosition(const VisiblePosition& p, int x)
{
VisiblePosition pos = p;
do {
VisiblePosition n = previousLinePosition(pos, x);
if (n.isNull() || n == pos)
- return p;
+ break;
pos = n;
} while (inSameParagraph(p, pos));
return pos;
}
-VisiblePosition nextParagraphPosition(const VisiblePosition &p, int x)
+VisiblePosition nextParagraphPosition(const VisiblePosition& p, int x)
{
VisiblePosition pos = p;
do {
VisiblePosition n = nextLinePosition(pos, x);
if (n.isNull() || n == pos)
- return p;
+ break;
pos = n;
} while (inSameParagraph(p, pos));
return pos;
@@ -944,7 +1009,7 @@ VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition)
if (!highestRoot)
return VisiblePosition();
- return VisiblePosition(highestRoot, 0, DOWNSTREAM);
+ return firstDeepEditingPositionForNode(highestRoot);
}
VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition)
@@ -953,7 +1018,7 @@ VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition)
if (!highestRoot)
return VisiblePosition();
- return VisiblePosition(highestRoot, maxDeepOffset(highestRoot), DOWNSTREAM);
+ return lastDeepEditingPositionForNode(highestRoot);
}
}