summaryrefslogtreecommitdiff
path: root/Source/WebCore/editing
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@nokia.com>2012-05-07 11:21:11 +0200
committerSimon Hausmann <simon.hausmann@nokia.com>2012-05-07 11:21:11 +0200
commit2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47 (patch)
tree988e8c5b116dd0466244ae2fe5af8ee9be926d76 /Source/WebCore/editing
parentdd91e772430dc294e3bf478c119ef8d43c0a3358 (diff)
downloadqtwebkit-2cf6c8816a73e0132bd8fa3b509d62d7c51b6e47.tar.gz
Imported WebKit commit 7e538425aa020340619e927792f3d895061fb54b (http://svn.webkit.org/repository/webkit/trunk@116286)
Diffstat (limited to 'Source/WebCore/editing')
-rw-r--r--Source/WebCore/editing/AlternativeTextController.cpp (renamed from Source/WebCore/editing/SpellingCorrectionController.cpp)352
-rw-r--r--Source/WebCore/editing/AlternativeTextController.h (renamed from Source/WebCore/editing/SpellingCorrectionController.h)108
-rw-r--r--Source/WebCore/editing/ApplyBlockElementCommand.cpp6
-rw-r--r--Source/WebCore/editing/ApplyStyleCommand.cpp24
-rw-r--r--Source/WebCore/editing/ApplyStyleCommand.h2
-rw-r--r--Source/WebCore/editing/CompositeEditCommand.cpp33
-rw-r--r--Source/WebCore/editing/CompositeEditCommand.h4
-rw-r--r--Source/WebCore/editing/DeleteButtonController.cpp2
-rw-r--r--Source/WebCore/editing/DeleteSelectionCommand.cpp21
-rw-r--r--Source/WebCore/editing/DictationAlternative.cpp45
-rw-r--r--Source/WebCore/editing/DictationAlternative.h44
-rw-r--r--Source/WebCore/editing/DictationCommand.cpp142
-rw-r--r--Source/WebCore/editing/DictationCommand.h60
-rw-r--r--Source/WebCore/editing/EditingAllInOne.cpp6
-rw-r--r--Source/WebCore/editing/EditingBehavior.h9
-rw-r--r--Source/WebCore/editing/EditingStyle.cpp33
-rw-r--r--Source/WebCore/editing/EditingStyle.h6
-rw-r--r--Source/WebCore/editing/Editor.cpp127
-rw-r--r--Source/WebCore/editing/Editor.h26
-rw-r--r--Source/WebCore/editing/EditorCommand.cpp14
-rw-r--r--Source/WebCore/editing/FrameSelection.cpp70
-rw-r--r--Source/WebCore/editing/FrameSelection.h12
-rw-r--r--Source/WebCore/editing/IndentOutdentCommand.cpp2
-rw-r--r--Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp9
-rw-r--r--Source/WebCore/editing/InsertParagraphSeparatorCommand.h7
-rw-r--r--Source/WebCore/editing/InsertTextCommand.cpp11
-rw-r--r--Source/WebCore/editing/InsertTextCommand.h18
-rw-r--r--Source/WebCore/editing/MarkupAccumulator.cpp207
-rw-r--r--Source/WebCore/editing/MarkupAccumulator.h50
-rw-r--r--Source/WebCore/editing/RenderedPosition.cpp8
-rw-r--r--Source/WebCore/editing/RenderedPosition.h5
-rw-r--r--Source/WebCore/editing/ReplaceSelectionCommand.cpp95
-rw-r--r--Source/WebCore/editing/ReplaceSelectionCommand.h3
-rw-r--r--Source/WebCore/editing/SimplifyMarkupCommand.cpp91
-rw-r--r--Source/WebCore/editing/SimplifyMarkupCommand.h51
-rw-r--r--Source/WebCore/editing/SpellChecker.cpp26
-rw-r--r--Source/WebCore/editing/SpellChecker.h4
-rw-r--r--Source/WebCore/editing/SpellingCorrectionCommand.cpp2
-rw-r--r--Source/WebCore/editing/SurroundingText.cpp88
-rw-r--r--Source/WebCore/editing/SurroundingText.h59
-rw-r--r--Source/WebCore/editing/TextCheckingHelper.h4
-rw-r--r--Source/WebCore/editing/TextInsertionBaseCommand.cpp86
-rw-r--r--Source/WebCore/editing/TextInsertionBaseCommand.h71
-rw-r--r--Source/WebCore/editing/TextIterator.cpp34
-rw-r--r--Source/WebCore/editing/TextIterator.h17
-rw-r--r--Source/WebCore/editing/TypingCommand.cpp84
-rw-r--r--Source/WebCore/editing/TypingCommand.h4
-rw-r--r--Source/WebCore/editing/VisiblePosition.cpp8
-rw-r--r--Source/WebCore/editing/VisibleSelection.cpp15
-rw-r--r--Source/WebCore/editing/gtk/FrameSelectionGtk.cpp3
-rw-r--r--Source/WebCore/editing/mac/EditorMac.mm28
-rw-r--r--Source/WebCore/editing/mac/FrameSelectionMac.mm4
-rw-r--r--Source/WebCore/editing/markup.cpp8
-rw-r--r--Source/WebCore/editing/visible_units.cpp1007
-rw-r--r--Source/WebCore/editing/visible_units.h5
55 files changed, 2031 insertions, 1229 deletions
diff --git a/Source/WebCore/editing/SpellingCorrectionController.cpp b/Source/WebCore/editing/AlternativeTextController.cpp
index 4ffdc7f95..8d09ff1b9 100644
--- a/Source/WebCore/editing/SpellingCorrectionController.cpp
+++ b/Source/WebCore/editing/AlternativeTextController.cpp
@@ -25,11 +25,14 @@
*/
#include "config.h"
-#include "SpellingCorrectionController.h"
+#include "AlternativeTextController.h"
+#include "DictationAlternative.h"
+#include "Document.h"
#include "DocumentMarkerController.h"
#include "EditCommand.h"
#include "EditorClient.h"
+#include "Event.h"
#include "FloatQuad.h"
#include "Frame.h"
#include "FrameView.h"
@@ -37,6 +40,7 @@
#include "SpellingCorrectionCommand.h"
#include "TextCheckerClient.h"
#include "TextCheckingHelper.h"
+#include "TextEvent.h"
#include "TextIterator.h"
#include "VisibleSelection.h"
#include "htmlediting.h"
@@ -48,6 +52,22 @@ namespace WebCore {
using namespace std;
using namespace WTF;
+class AutocorrectionAlternativeDetails : public AlternativeTextDetails {
+public:
+ static PassRefPtr<AutocorrectionAlternativeDetails> create(const String& replacementString)
+ {
+ return adoptRef(new AutocorrectionAlternativeDetails(replacementString));
+ }
+
+ const String& replacementString() const { return m_replacementString; }
+private:
+ AutocorrectionAlternativeDetails(const String& replacementString)
+ : m_replacementString(replacementString)
+ { }
+
+ String m_replacementString;
+};
+
#if USE(AUTOCORRECTION_PANEL)
static const Vector<DocumentMarker::MarkerType>& markerTypesForAutocorrection()
@@ -85,48 +105,48 @@ static bool markersHaveIdenticalDescription(const Vector<DocumentMarker*>& marke
return true;
}
-SpellingCorrectionController::SpellingCorrectionController(Frame* frame)
- : m_frame(frame)
- , m_correctionPanelTimer(this, &SpellingCorrectionController::correctionPanelTimerFired)
+AlternativeTextController::AlternativeTextController(Frame* frame)
+ : m_timer(this, &AlternativeTextController::timerFired)
+ , m_frame(frame)
{
}
-SpellingCorrectionController::~SpellingCorrectionController()
+AlternativeTextController::~AlternativeTextController()
{
- dismiss(ReasonForDismissingCorrectionPanelIgnored);
+ dismiss(ReasonForDismissingAlternativeTextIgnored);
}
-void SpellingCorrectionController::startCorrectionPanelTimer(CorrectionPanelInfo::PanelType type)
+void AlternativeTextController::startAlternativeTextUITimer(AlternativeTextType type)
{
const double correctionPanelTimerInterval = 0.3;
if (!isAutomaticSpellingCorrectionEnabled())
return;
// If type is PanelTypeReversion, then the new range has been set. So we shouldn't clear it.
- if (type == CorrectionPanelInfo::PanelTypeCorrection)
- m_correctionPanelInfo.rangeToBeReplaced.clear();
- m_correctionPanelInfo.panelType = type;
- m_correctionPanelTimer.startOneShot(correctionPanelTimerInterval);
+ if (type == AlternativeTextTypeCorrection)
+ m_alternativeTextInfo.rangeWithAlternative.clear();
+ m_alternativeTextInfo.type = type;
+ m_timer.startOneShot(correctionPanelTimerInterval);
}
-void SpellingCorrectionController::stopCorrectionPanelTimer()
+void AlternativeTextController::stopAlternativeTextUITimer()
{
- m_correctionPanelTimer.stop();
- m_correctionPanelInfo.rangeToBeReplaced.clear();
+ m_timer.stop();
+ m_alternativeTextInfo.rangeWithAlternative.clear();
}
-void SpellingCorrectionController::stopPendingCorrection(const VisibleSelection& oldSelection)
+void AlternativeTextController::stopPendingCorrection(const VisibleSelection& oldSelection)
{
// Make sure there's no pending autocorrection before we call markMisspellingsAndBadGrammar() below.
VisibleSelection currentSelection(m_frame->selection()->selection());
if (currentSelection == oldSelection)
return;
- stopCorrectionPanelTimer();
- dismiss(ReasonForDismissingCorrectionPanelIgnored);
+ stopAlternativeTextUITimer();
+ dismiss(ReasonForDismissingAlternativeTextIgnored);
}
-void SpellingCorrectionController::applyPendingCorrection(const VisibleSelection& selectionAfterTyping)
+void AlternativeTextController::applyPendingCorrection(const VisibleSelection& selectionAfterTyping)
{
// Apply pending autocorrection before next round of spell checking.
bool doApplyCorrection = true;
@@ -138,87 +158,88 @@ void SpellingCorrectionController::applyPendingCorrection(const VisibleSelection
doApplyCorrection = false;
}
if (doApplyCorrection)
- handleCorrectionPanelResult(dismissSoon(ReasonForDismissingCorrectionPanelAccepted));
+ handleAlternativeTextUIResult(dismissSoon(ReasonForDismissingAlternativeTextAccepted));
else
- m_correctionPanelInfo.rangeToBeReplaced.clear();
+ m_alternativeTextInfo.rangeWithAlternative.clear();
}
-bool SpellingCorrectionController::hasPendingCorrection() const
+bool AlternativeTextController::hasPendingCorrection() const
{
- return m_correctionPanelInfo.rangeToBeReplaced;
+ return m_alternativeTextInfo.rangeWithAlternative;
}
-bool SpellingCorrectionController::isSpellingMarkerAllowed(PassRefPtr<Range> misspellingRange) const
+bool AlternativeTextController::isSpellingMarkerAllowed(PassRefPtr<Range> misspellingRange) const
{
return !m_frame->document()->markers()->hasMarkers(misspellingRange.get(), DocumentMarker::SpellCheckingExemption);
}
-void SpellingCorrectionController::show(PassRefPtr<Range> rangeToReplace, const String& replacement)
+void AlternativeTextController::show(PassRefPtr<Range> rangeToReplace, const String& replacement)
{
FloatRect boundingBox = rootViewRectForRange(rangeToReplace.get());
if (boundingBox.isEmpty())
return;
- m_correctionPanelInfo.replacedString = plainText(rangeToReplace.get());
- m_correctionPanelInfo.rangeToBeReplaced = rangeToReplace;
- m_correctionPanelInfo.replacementString = replacement;
- m_correctionPanelInfo.isActive = true;
- client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, replacement, Vector<String>());
+ m_alternativeTextInfo.originalText = plainText(rangeToReplace.get());
+ m_alternativeTextInfo.rangeWithAlternative = rangeToReplace;
+ m_alternativeTextInfo.details = AutocorrectionAlternativeDetails::create(replacement);
+ m_alternativeTextInfo.isActive = true;
+ if (AlternativeTextClient* client = alternativeTextClient())
+ client->showCorrectionAlternative(m_alternativeTextInfo.type, boundingBox, m_alternativeTextInfo.originalText, replacement, Vector<String>());
}
-void SpellingCorrectionController::handleCancelOperation()
+void AlternativeTextController::handleCancelOperation()
{
- if (!m_correctionPanelInfo.isActive)
+ if (!m_alternativeTextInfo.isActive)
return;
- m_correctionPanelInfo.isActive = false;
- dismiss(ReasonForDismissingCorrectionPanelCancelled);
+ m_alternativeTextInfo.isActive = false;
+ dismiss(ReasonForDismissingAlternativeTextCancelled);
}
-void SpellingCorrectionController::dismiss(ReasonForDismissingCorrectionPanel reasonForDismissing)
+void AlternativeTextController::dismiss(ReasonForDismissingAlternativeText reasonForDismissing)
{
- if (!m_correctionPanelInfo.isActive)
+ if (!m_alternativeTextInfo.isActive)
return;
- m_correctionPanelInfo.isActive = false;
- m_correctionPanelIsDismissedByEditor = true;
- if (client())
- client()->dismissCorrectionPanel(reasonForDismissing);
+ m_alternativeTextInfo.isActive = false;
+ m_isDismissedByEditing = true;
+ if (AlternativeTextClient* client = alternativeTextClient())
+ client->dismissAlternative(reasonForDismissing);
}
-String SpellingCorrectionController::dismissSoon(ReasonForDismissingCorrectionPanel reasonForDismissing)
+String AlternativeTextController::dismissSoon(ReasonForDismissingAlternativeText reasonForDismissing)
{
- if (!m_correctionPanelInfo.isActive)
+ if (!m_alternativeTextInfo.isActive)
return String();
- m_correctionPanelInfo.isActive = false;
- m_correctionPanelIsDismissedByEditor = true;
- if (!client())
- return String();
- return client()->dismissCorrectionPanelSoon(reasonForDismissing);
+ m_alternativeTextInfo.isActive = false;
+ m_isDismissedByEditing = true;
+ if (AlternativeTextClient* client = alternativeTextClient())
+ return client->dismissAlternativeSoon(reasonForDismissing);
+ return String();
}
-void SpellingCorrectionController::applyCorrectionPanelInfo(const Vector<DocumentMarker::MarkerType>& markerTypesToAdd)
+void AlternativeTextController::applyAlternativeText(const String& alternative, const Vector<DocumentMarker::MarkerType>& markerTypesToAdd)
{
- if (!m_correctionPanelInfo.rangeToBeReplaced)
+ if (!m_alternativeTextInfo.rangeWithAlternative)
return;
ExceptionCode ec = 0;
- RefPtr<Range> paragraphRangeContainingCorrection = m_correctionPanelInfo.rangeToBeReplaced->cloneRange(ec);
+ RefPtr<Range> paragraphRangeContainingCorrection = m_alternativeTextInfo.rangeWithAlternative->cloneRange(ec);
if (ec)
return;
- setStart(paragraphRangeContainingCorrection.get(), startOfParagraph(m_correctionPanelInfo.rangeToBeReplaced->startPosition()));
- setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(m_correctionPanelInfo.rangeToBeReplaced->endPosition()));
+ setStart(paragraphRangeContainingCorrection.get(), startOfParagraph(m_alternativeTextInfo.rangeWithAlternative->startPosition()));
+ setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(m_alternativeTextInfo.rangeWithAlternative->endPosition()));
- // After we replace the word at range rangeToBeReplaced, we need to add markers to that range.
- // However, once the replacement took place, the value of rangeToBeReplaced is not valid anymore.
- // So before we carry out the replacement, we need to store the start position of rangeToBeReplaced
+ // After we replace the word at range rangeWithAlternative, we need to add markers to that range.
+ // However, once the replacement took place, the value of rangeWithAlternative is not valid anymore.
+ // So before we carry out the replacement, we need to store the start position of rangeWithAlternative
// relative to the start position of the containing paragraph. We use correctionStartOffsetInParagraph
// to store this value. In order to obtain this offset, we need to first create a range
- // which spans from the start of paragraph to the start position of rangeToBeReplaced.
+ // which spans from the start of paragraph to the start position of rangeWithAlternative.
RefPtr<Range> correctionStartOffsetInParagraphAsRange = Range::create(paragraphRangeContainingCorrection->startContainer(ec)->document(), paragraphRangeContainingCorrection->startPosition(), paragraphRangeContainingCorrection->startPosition());
if (ec)
return;
- Position startPositionOfRangeToBeReplaced = m_correctionPanelInfo.rangeToBeReplaced->startPosition();
- correctionStartOffsetInParagraphAsRange->setEnd(startPositionOfRangeToBeReplaced.containerNode(), startPositionOfRangeToBeReplaced.computeOffsetInContainerNode(), ec);
+ Position startPositionOfrangeWithAlternative = m_alternativeTextInfo.rangeWithAlternative->startPosition();
+ correctionStartOffsetInParagraphAsRange->setEnd(startPositionOfrangeWithAlternative.containerNode(), startPositionOfrangeWithAlternative.computeOffsetInContainerNode(), ec);
if (ec)
return;
@@ -226,14 +247,14 @@ void SpellingCorrectionController::applyCorrectionPanelInfo(const Vector<Documen
int correctionStartOffsetInParagraph = TextIterator::rangeLength(correctionStartOffsetInParagraphAsRange.get());
// Clone the range, since the caller of this method may want to keep the original range around.
- RefPtr<Range> rangeToBeReplaced = m_correctionPanelInfo.rangeToBeReplaced->cloneRange(ec);
- applyCommand(SpellingCorrectionCommand::create(rangeToBeReplaced, m_correctionPanelInfo.replacementString));
+ RefPtr<Range> rangeWithAlternative = m_alternativeTextInfo.rangeWithAlternative->cloneRange(ec);
+ applyCommand(SpellingCorrectionCommand::create(rangeWithAlternative, alternative));
setEnd(paragraphRangeContainingCorrection.get(), m_frame->selection()->selection().start());
- RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, m_correctionPanelInfo.replacementString.length());
+ RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, alternative.length());
String newText = plainText(replacementRange.get());
// Check to see if replacement succeeded.
- if (newText != m_correctionPanelInfo.replacementString)
+ if (newText != alternative)
return;
DocumentMarkerController* markers = replacementRange->startContainer()->document()->markers();
@@ -241,36 +262,37 @@ void SpellingCorrectionController::applyCorrectionPanelInfo(const Vector<Documen
for (size_t i = 0; i < size; ++i) {
DocumentMarker::MarkerType markerType = markerTypesToAdd[i];
String description;
- if (m_correctionPanelInfo.panelType != CorrectionPanelInfo::PanelTypeReversion && (markerType == DocumentMarker::Replacement || markerType == DocumentMarker::Autocorrected))
- description = m_correctionPanelInfo.replacedString;
+ if (m_alternativeTextInfo.type != AlternativeTextTypeReversion && (markerType == DocumentMarker::Replacement || markerType == DocumentMarker::Autocorrected))
+ description = m_alternativeTextInfo.originalText;
markers->addMarker(replacementRange.get(), markerType, description);
}
}
-bool SpellingCorrectionController::applyAutocorrectionBeforeTypingIfAppropriate()
+bool AlternativeTextController::applyAutocorrectionBeforeTypingIfAppropriate()
{
- if (!m_correctionPanelInfo.rangeToBeReplaced || !m_correctionPanelInfo.isActive)
+ if (!m_alternativeTextInfo.rangeWithAlternative || !m_alternativeTextInfo.isActive)
return false;
- if (m_correctionPanelInfo.panelType != CorrectionPanelInfo::PanelTypeCorrection)
+ if (m_alternativeTextInfo.type != AlternativeTextTypeCorrection)
return false;
Position caretPosition = m_frame->selection()->selection().start();
- if (m_correctionPanelInfo.rangeToBeReplaced->endPosition() == caretPosition) {
- handleCorrectionPanelResult(dismissSoon(ReasonForDismissingCorrectionPanelAccepted));
+ if (m_alternativeTextInfo.rangeWithAlternative->endPosition() == caretPosition) {
+ handleAlternativeTextUIResult(dismissSoon(ReasonForDismissingAlternativeTextAccepted));
return true;
}
// Pending correction should always be where caret is. But in case this is not always true, we still want to dismiss the panel without accepting the correction.
- ASSERT(m_correctionPanelInfo.rangeToBeReplaced->endPosition() == caretPosition);
- dismiss(ReasonForDismissingCorrectionPanelIgnored);
+ ASSERT(m_alternativeTextInfo.rangeWithAlternative->endPosition() == caretPosition);
+ dismiss(ReasonForDismissingAlternativeTextIgnored);
return false;
}
-void SpellingCorrectionController::respondToUnappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction)
+void AlternativeTextController::respondToUnappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction)
{
- client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, corrected, correction);
+ if (AlternativeTextClient* client = alternativeTextClient())
+ client->recordAutocorrectionResponse(AutocorrectionReverted, corrected, correction);
m_frame->document()->updateLayout();
m_frame->selection()->setSelection(selectionOfCorrected, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | FrameSelection::SpellCorrectionTriggered);
RefPtr<Range> range = Range::create(m_frame->document(), m_frame->selection()->selection().start(), m_frame->selection()->selection().end());
@@ -281,11 +303,11 @@ void SpellingCorrectionController::respondToUnappliedSpellCorrection(const Visib
markers->addMarker(range.get(), DocumentMarker::SpellCheckingExemption);
}
-void SpellingCorrectionController::correctionPanelTimerFired(Timer<SpellingCorrectionController>*)
+void AlternativeTextController::timerFired(Timer<AlternativeTextController>*)
{
- m_correctionPanelIsDismissedByEditor = false;
- switch (m_correctionPanelInfo.panelType) {
- case CorrectionPanelInfo::PanelTypeCorrection: {
+ m_isDismissedByEditing = false;
+ switch (m_alternativeTextInfo.type) {
+ case AlternativeTextTypeCorrection: {
VisibleSelection selection(m_frame->selection()->selection());
VisiblePosition start(selection.start(), selection.affinity());
VisiblePosition p = startOfWord(start, LeftWordIfOnBoundary);
@@ -293,76 +315,78 @@ void SpellingCorrectionController::correctionPanelTimerFired(Timer<SpellingCorre
m_frame->editor()->markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeSpelling | TextCheckingTypeShowCorrectionPanel, adjacentWords.toNormalizedRange().get(), 0);
}
break;
- case CorrectionPanelInfo::PanelTypeReversion: {
- if (!m_correctionPanelInfo.rangeToBeReplaced)
+ case AlternativeTextTypeReversion: {
+ if (!m_alternativeTextInfo.rangeWithAlternative)
break;
- m_correctionPanelInfo.isActive = true;
- m_correctionPanelInfo.replacedString = plainText(m_correctionPanelInfo.rangeToBeReplaced.get());
- FloatRect boundingBox = rootViewRectForRange(m_correctionPanelInfo.rangeToBeReplaced.get());
- if (!boundingBox.isEmpty())
- client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, m_correctionPanelInfo.replacementString, Vector<String>());
+ m_alternativeTextInfo.isActive = true;
+ m_alternativeTextInfo.originalText = plainText(m_alternativeTextInfo.rangeWithAlternative.get());
+ FloatRect boundingBox = rootViewRectForRange(m_alternativeTextInfo.rangeWithAlternative.get());
+ if (!boundingBox.isEmpty()) {
+ const AutocorrectionAlternativeDetails* details = static_cast<const AutocorrectionAlternativeDetails*>(m_alternativeTextInfo.details.get());
+ if (AlternativeTextClient* client = alternativeTextClient())
+ client->showCorrectionAlternative(m_alternativeTextInfo.type, boundingBox, m_alternativeTextInfo.originalText, details->replacementString(), Vector<String>());
+ }
}
break;
- case CorrectionPanelInfo::PanelTypeSpellingSuggestions: {
- if (!m_correctionPanelInfo.rangeToBeReplaced || plainText(m_correctionPanelInfo.rangeToBeReplaced.get()) != m_correctionPanelInfo.replacedString)
+ case AlternativeTextTypeSpellingSuggestions: {
+ if (!m_alternativeTextInfo.rangeWithAlternative || plainText(m_alternativeTextInfo.rangeWithAlternative.get()) != m_alternativeTextInfo.originalText)
break;
- String paragraphText = plainText(TextCheckingParagraph(m_correctionPanelInfo.rangeToBeReplaced).paragraphRange().get());
+ String paragraphText = plainText(TextCheckingParagraph(m_alternativeTextInfo.rangeWithAlternative).paragraphRange().get());
Vector<String> suggestions;
- textChecker()->getGuessesForWord(m_correctionPanelInfo.replacedString, paragraphText, suggestions);
+ textChecker()->getGuessesForWord(m_alternativeTextInfo.originalText, paragraphText, suggestions);
if (suggestions.isEmpty()) {
- m_correctionPanelInfo.rangeToBeReplaced.clear();
+ m_alternativeTextInfo.rangeWithAlternative.clear();
break;
}
String topSuggestion = suggestions.first();
suggestions.remove(0);
- m_correctionPanelInfo.isActive = true;
- FloatRect boundingBox = rootViewRectForRange(m_correctionPanelInfo.rangeToBeReplaced.get());
- if (!boundingBox.isEmpty())
- client()->showCorrectionPanel(m_correctionPanelInfo.panelType, boundingBox, m_correctionPanelInfo.replacedString, topSuggestion, suggestions);
+ m_alternativeTextInfo.isActive = true;
+ FloatRect boundingBox = rootViewRectForRange(m_alternativeTextInfo.rangeWithAlternative.get());
+ if (!boundingBox.isEmpty()) {
+ if (AlternativeTextClient* client = alternativeTextClient())
+ client->showCorrectionAlternative(m_alternativeTextInfo.type, boundingBox, m_alternativeTextInfo.originalText, topSuggestion, suggestions);
+ }
}
break;
}
}
-void SpellingCorrectionController::handleCorrectionPanelResult(const String& correction)
+void AlternativeTextController::handleAlternativeTextUIResult(const String& result)
{
- Range* replacedRange = m_correctionPanelInfo.rangeToBeReplaced.get();
+ Range* replacedRange = m_alternativeTextInfo.rangeWithAlternative.get();
if (!replacedRange || m_frame->document() != replacedRange->ownerDocument())
return;
- String currentWord = plainText(m_correctionPanelInfo.rangeToBeReplaced.get());
+ String currentWord = plainText(m_alternativeTextInfo.rangeWithAlternative.get());
// Check to see if the word we are about to correct has been changed between timer firing and callback being triggered.
- if (currentWord != m_correctionPanelInfo.replacedString)
+ if (currentWord != m_alternativeTextInfo.originalText)
return;
- m_correctionPanelInfo.isActive = false;
+ m_alternativeTextInfo.isActive = false;
- switch (m_correctionPanelInfo.panelType) {
- case CorrectionPanelInfo::PanelTypeCorrection:
- if (correction.length()) {
- m_correctionPanelInfo.replacementString = correction;
- applyCorrectionPanelInfo(markerTypesForAutocorrection());
- } else if (!m_correctionPanelIsDismissedByEditor)
- replacedRange->startContainer()->document()->markers()->addMarker(replacedRange, DocumentMarker::RejectedCorrection, m_correctionPanelInfo.replacedString);
+ switch (m_alternativeTextInfo.type) {
+ case AlternativeTextTypeCorrection:
+ if (result.length())
+ applyAlternativeText(result, markerTypesForAutocorrection());
+ else if (!m_isDismissedByEditing)
+ replacedRange->startContainer()->document()->markers()->addMarker(replacedRange, DocumentMarker::RejectedCorrection, m_alternativeTextInfo.originalText);
break;
- case CorrectionPanelInfo::PanelTypeReversion:
- case CorrectionPanelInfo::PanelTypeSpellingSuggestions:
- if (correction.length()) {
- m_correctionPanelInfo.replacementString = correction;
- applyCorrectionPanelInfo(markerTypesForReplacement());
- }
+ case AlternativeTextTypeReversion:
+ case AlternativeTextTypeSpellingSuggestions:
+ if (result.length())
+ applyAlternativeText(result, markerTypesForReplacement());
break;
}
- m_correctionPanelInfo.rangeToBeReplaced.clear();
+ m_alternativeTextInfo.rangeWithAlternative.clear();
}
-bool SpellingCorrectionController::isAutomaticSpellingCorrectionEnabled()
+bool AlternativeTextController::isAutomaticSpellingCorrectionEnabled()
{
- return client() && client()->isAutomaticSpellingCorrectionEnabled();
+ return editorClient() && editorClient()->isAutomaticSpellingCorrectionEnabled();
}
-FloatRect SpellingCorrectionController::rootViewRectForRange(const Range* range) const
+FloatRect AlternativeTextController::rootViewRectForRange(const Range* range) const
{
FrameView* view = m_frame->view();
if (!view)
@@ -376,7 +400,7 @@ FloatRect SpellingCorrectionController::rootViewRectForRange(const Range* range)
return view->contentsToRootView(IntRect(boundingRect));
}
-void SpellingCorrectionController::respondToChangedSelection(const VisibleSelection& oldSelection)
+void AlternativeTextController::respondToChangedSelection(const VisibleSelection& oldSelection)
{
VisibleSelection currentSelection(m_frame->selection()->selection());
// When user moves caret to the end of autocorrected word and pauses, we show the panel
@@ -414,20 +438,20 @@ void SpellingCorrectionController::respondToChangedSelection(const VisibleSelect
if (!currentWord.length())
continue;
- m_correctionPanelInfo.rangeToBeReplaced = wordRange;
- m_correctionPanelInfo.replacedString = currentWord;
+ m_alternativeTextInfo.rangeWithAlternative = wordRange;
+ m_alternativeTextInfo.originalText = currentWord;
if (marker->type() == DocumentMarker::Spelling)
- startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeSpellingSuggestions);
+ startAlternativeTextUITimer(AlternativeTextTypeSpellingSuggestions);
else {
- m_correctionPanelInfo.replacementString = marker->description();
- startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeReversion);
+ m_alternativeTextInfo.details = AutocorrectionAlternativeDetails::create(marker->description());
+ startAlternativeTextUITimer(AlternativeTextTypeReversion);
}
break;
}
}
-void SpellingCorrectionController::respondToAppliedEditing(CompositeEditCommand* command)
+void AlternativeTextController::respondToAppliedEditing(CompositeEditCommand* command)
{
if (command->isTopLevelCommand() && !command->shouldRetainAutocorrectionIndicator())
m_frame->document()->markers()->removeMarkers(DocumentMarker::CorrectionIndicator);
@@ -436,7 +460,7 @@ void SpellingCorrectionController::respondToAppliedEditing(CompositeEditCommand*
m_originalStringForLastDeletedAutocorrection = String();
}
-void SpellingCorrectionController::respondToUnappliedEditing(EditCommandComposition* command)
+void AlternativeTextController::respondToUnappliedEditing(EditCommandComposition* command)
{
if (!command->wasCreateLinkCommand())
return;
@@ -448,35 +472,47 @@ void SpellingCorrectionController::respondToUnappliedEditing(EditCommandComposit
markers->addMarker(range.get(), DocumentMarker::SpellCheckingExemption);
}
-EditorClient* SpellingCorrectionController::client()
+AlternativeTextClient* AlternativeTextController::alternativeTextClient()
{
+ if (!m_frame)
+ return 0;
+
+ return m_frame->page() ? m_frame->page()->alternativeTextClient() : 0;
+}
+
+EditorClient* AlternativeTextController::editorClient()
+{
+ if (!m_frame)
+ return 0;
+
return m_frame->page() ? m_frame->page()->editorClient() : 0;
}
-TextCheckerClient* SpellingCorrectionController::textChecker()
+TextCheckerClient* AlternativeTextController::textChecker()
{
- if (EditorClient* owner = client())
+ if (EditorClient* owner = editorClient())
return owner->textChecker();
return 0;
}
-void SpellingCorrectionController::recordAutocorrectionResponseReversed(const String& replacedString, const String& replacementString)
+void AlternativeTextController::recordAutocorrectionResponseReversed(const String& replacedString, const String& replacementString)
{
- client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, replacedString, replacementString);
+ if (AlternativeTextClient* client = alternativeTextClient())
+ client->recordAutocorrectionResponse(AutocorrectionReverted, replacedString, replacementString);
}
-void SpellingCorrectionController::recordAutocorrectionResponseReversed(const String& replacedString, PassRefPtr<Range> replacementRange)
+void AlternativeTextController::recordAutocorrectionResponseReversed(const String& replacedString, PassRefPtr<Range> replacementRange)
{
recordAutocorrectionResponseReversed(replacedString, plainText(replacementRange.get()));
}
-void SpellingCorrectionController::markReversed(PassRefPtr<Range> changedRange)
+void AlternativeTextController::markReversed(PassRefPtr<Range> changedRange)
{
changedRange->startContainer()->document()->markers()->removeMarkers(changedRange.get(), DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::SpellCheckingExemption);
}
-void SpellingCorrectionController::markCorrection(PassRefPtr<Range> replacedRange, const String& replacedString)
+void AlternativeTextController::markCorrection(PassRefPtr<Range> replacedRange, const String& replacedString)
{
Vector<DocumentMarker::MarkerType> markerTypesToAdd = markerTypesForAutocorrection();
DocumentMarkerController* markers = replacedRange->startContainer()->document()->markers();
@@ -489,7 +525,7 @@ void SpellingCorrectionController::markCorrection(PassRefPtr<Range> replacedRang
}
}
-void SpellingCorrectionController::recordSpellcheckerResponseForModifiedCorrection(Range* rangeOfCorrection, const String& corrected, const String& correction)
+void AlternativeTextController::recordSpellcheckerResponseForModifiedCorrection(Range* rangeOfCorrection, const String& corrected, const String& correction)
{
if (!rangeOfCorrection)
return;
@@ -497,23 +533,26 @@ void SpellingCorrectionController::recordSpellcheckerResponseForModifiedCorrecti
Vector<DocumentMarker*> correctedOnceMarkers = markers->markersInRange(rangeOfCorrection, DocumentMarker::Autocorrected);
if (correctedOnceMarkers.isEmpty())
return;
-
- // Spelling corrected text has been edited. We need to determine whether user has reverted it to original text or
- // edited it to something else, and notify spellchecker accordingly.
- if (markersHaveIdenticalDescription(correctedOnceMarkers) && correctedOnceMarkers[0]->description() == corrected)
- client()->recordAutocorrectionResponse(EditorClient::AutocorrectionReverted, corrected, correction);
- else
- client()->recordAutocorrectionResponse(EditorClient::AutocorrectionEdited, corrected, correction);
+
+ if (AlternativeTextClient* client = alternativeTextClient()) {
+ // Spelling corrected text has been edited. We need to determine whether user has reverted it to original text or
+ // edited it to something else, and notify spellchecker accordingly.
+ if (markersHaveIdenticalDescription(correctedOnceMarkers) && correctedOnceMarkers[0]->description() == corrected)
+ client->recordAutocorrectionResponse(AutocorrectionReverted, corrected, correction);
+ else
+ client->recordAutocorrectionResponse(AutocorrectionEdited, corrected, correction);
+ }
+
markers->removeMarkers(rangeOfCorrection, DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
}
-void SpellingCorrectionController::deletedAutocorrectionAtPosition(const Position& position, const String& originalString)
+void AlternativeTextController::deletedAutocorrectionAtPosition(const Position& position, const String& originalString)
{
m_originalStringForLastDeletedAutocorrection = originalString;
m_positionForLastDeletedAutocorrection = position;
}
-void SpellingCorrectionController::markPrecedingWhitespaceForDeletedAutocorrectionAfterCommand(EditCommand* command)
+void AlternativeTextController::markPrecedingWhitespaceForDeletedAutocorrectionAfterCommand(EditCommand* command)
{
Position endOfSelection = command->endingSelection().end();
if (endOfSelection != m_positionForLastDeletedAutocorrection)
@@ -534,19 +573,19 @@ void SpellingCorrectionController::markPrecedingWhitespaceForDeletedAutocorrecti
m_frame->document()->markers()->addMarker(precedingCharacterRange.get(), DocumentMarker::DeletedAutocorrection, m_originalStringForLastDeletedAutocorrection);
}
-bool SpellingCorrectionController::processMarkersOnTextToBeReplacedByResult(const TextCheckingResult* result, Range* rangeToBeReplaced, const String& stringToBeReplaced)
+bool AlternativeTextController::processMarkersOnTextToBeReplacedByResult(const TextCheckingResult* result, Range* rangeWithAlternative, const String& stringToBeReplaced)
{
DocumentMarkerController* markerController = m_frame->document()->markers();
- if (markerController->hasMarkers(rangeToBeReplaced, DocumentMarker::Replacement)) {
+ if (markerController->hasMarkers(rangeWithAlternative, DocumentMarker::Replacement)) {
if (result->type == TextCheckingTypeCorrection)
- recordSpellcheckerResponseForModifiedCorrection(rangeToBeReplaced, stringToBeReplaced, result->replacement);
+ recordSpellcheckerResponseForModifiedCorrection(rangeWithAlternative, stringToBeReplaced, result->replacement);
return false;
}
- if (markerController->hasMarkers(rangeToBeReplaced, DocumentMarker::RejectedCorrection))
+ if (markerController->hasMarkers(rangeWithAlternative, DocumentMarker::RejectedCorrection))
return false;
- Position beginningOfRange = rangeToBeReplaced->startPosition();
+ Position beginningOfRange = rangeWithAlternative->startPosition();
Position precedingCharacterPosition = beginningOfRange.previous();
RefPtr<Range> precedingCharacterRange = Range::create(m_frame->document(), precedingCharacterPosition, beginningOfRange);
@@ -562,4 +601,27 @@ bool SpellingCorrectionController::processMarkersOnTextToBeReplacedByResult(cons
#endif
+bool AlternativeTextController::insertDictatedText(const String& text, const Vector<DictationAlternative>& dictationAlternatives, Event* triggeringEvent)
+{
+ if (!m_frame)
+ return false;
+ EventTarget* target;
+ if (triggeringEvent)
+ target = triggeringEvent->target();
+ else
+ target = eventTargetNodeForDocument(m_frame->document());
+ if (!target)
+ return false;
+
+ if (FrameView* view = m_frame->view())
+ view->resetDeferredRepaintDelay();
+
+ RefPtr<TextEvent> event = TextEvent::createForDictation(m_frame->domWindow(), text, dictationAlternatives);
+ event->setUnderlyingEvent(triggeringEvent);
+
+ ExceptionCode ec;
+ target->dispatchEvent(event, ec);
+ return event->defaultHandled();
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/editing/SpellingCorrectionController.h b/Source/WebCore/editing/AlternativeTextController.h
index dfd0608b2..28449efd9 100644
--- a/Source/WebCore/editing/SpellingCorrectionController.h
+++ b/Source/WebCore/editing/AlternativeTextController.h
@@ -23,9 +23,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SpellingCorrectionController_h
-#define SpellingCorrectionController_h
+#ifndef AlternativeTextController_h
+#define AlternativeTextController_h
+#include "AlternativeTextClient.h"
#include "DocumentMarker.h"
#include "Range.h"
#include "TextChecking.h"
@@ -40,51 +41,67 @@ class CompositeEditCommand;
class EditorClient;
class EditCommand;
class EditCommandComposition;
+class Event;
class Frame;
class TextCheckerClient;
+struct DictationAlternative;
-struct CorrectionPanelInfo {
- enum PanelType {
- PanelTypeCorrection = 0,
- PanelTypeReversion,
- PanelTypeSpellingSuggestions
- };
-
- RefPtr<Range> rangeToBeReplaced;
- String replacedString;
- String replacementString;
- PanelType panelType;
- bool isActive;
+class AlternativeTextDetails : public RefCounted<AlternativeTextDetails> {
+public:
+ AlternativeTextDetails() { }
+ virtual ~AlternativeTextDetails() { }
};
-struct TextCheckingResult;
+struct AlternativeTextInfo {
+ RefPtr<Range> rangeWithAlternative;
+ bool isActive;
+ AlternativeTextType type;
+ String originalText;
+ RefPtr<AlternativeTextDetails> details;
+};
-enum ReasonForDismissingCorrectionPanel {
- ReasonForDismissingCorrectionPanelCancelled = 0,
- ReasonForDismissingCorrectionPanelIgnored,
- ReasonForDismissingCorrectionPanelAccepted
+class DictationMarkerDetails : public DocumentMarkerDetails {
+public:
+ static PassRefPtr<DictationMarkerDetails> create(const String& originalText, uint64_t dictationContext)
+ {
+ return adoptRef(new DictationMarkerDetails(originalText, dictationContext));
+ }
+ const String& originalText() const { return m_originalText; }
+ uint64_t dictationContext() const { return m_dictationContext; }
+private:
+ DictationMarkerDetails(const String& originalText, uint64_t dictationContext)
+ : m_dictationContext(dictationContext)
+ , m_originalText(originalText)
+ { }
+
+ uint64_t m_dictationContext;
+ String m_originalText;
};
+struct TextCheckingResult;
+
#if USE(AUTOCORRECTION_PANEL)
-#define UNLESS_ENABLED(functionBody) ;
+// These backslashes are for making style checker happy.
+#define UNLESS_ENABLED(functionBody) \
+;\
+
#else
#define UNLESS_ENABLED(functionBody) functionBody
#endif
-class SpellingCorrectionController {
- WTF_MAKE_NONCOPYABLE(SpellingCorrectionController); WTF_MAKE_FAST_ALLOCATED;
+class AlternativeTextController {
+ WTF_MAKE_NONCOPYABLE(AlternativeTextController);
+ WTF_MAKE_FAST_ALLOCATED;
public:
- SpellingCorrectionController(Frame*) UNLESS_ENABLED({})
- ~SpellingCorrectionController() UNLESS_ENABLED({})
+ AlternativeTextController(Frame*) UNLESS_ENABLED({ })
+ ~AlternativeTextController() UNLESS_ENABLED({ })
- void startCorrectionPanelTimer(CorrectionPanelInfo::PanelType) UNLESS_ENABLED({})
- void stopCorrectionPanelTimer() UNLESS_ENABLED({})
+ void startAlternativeTextUITimer(AlternativeTextType) UNLESS_ENABLED({ })
+ void stopAlternativeTextUITimer() UNLESS_ENABLED({ })
- void dismiss(ReasonForDismissingCorrectionPanel) UNLESS_ENABLED({})
- String dismissSoon(ReasonForDismissingCorrectionPanel) UNLESS_ENABLED({ return String(); })
+ void dismiss(ReasonForDismissingAlternativeText) UNLESS_ENABLED({ })
void show(PassRefPtr<Range> rangeToReplace, const String& replacement) UNLESS_ENABLED({ UNUSED_PARAM(rangeToReplace); UNUSED_PARAM(replacement); })
- void applyCorrectionPanelInfo(const Vector<DocumentMarker::MarkerType>&) UNLESS_ENABLED({})
// Return true if correction was applied, false otherwise.
bool applyAutocorrectionBeforeTypingIfAppropriate() UNLESS_ENABLED({ return false; })
@@ -96,27 +113,31 @@ public:
void stopPendingCorrection(const VisibleSelection& oldSelection) UNLESS_ENABLED({ UNUSED_PARAM(oldSelection); })
void applyPendingCorrection(const VisibleSelection& selectionAfterTyping) UNLESS_ENABLED({ UNUSED_PARAM(selectionAfterTyping); })
- void handleCorrectionPanelResult(const String& correction) UNLESS_ENABLED({ UNUSED_PARAM(correction); })
- void handleCancelOperation() UNLESS_ENABLED({})
+ void handleAlternativeTextUIResult(const String& result) UNLESS_ENABLED({ UNUSED_PARAM(result); })
+ void handleCancelOperation() UNLESS_ENABLED({ })
bool hasPendingCorrection() const UNLESS_ENABLED({ return false; })
bool isSpellingMarkerAllowed(PassRefPtr<Range> misspellingRange) const UNLESS_ENABLED({ UNUSED_PARAM(misspellingRange); return true; })
bool isAutomaticSpellingCorrectionEnabled() UNLESS_ENABLED({ return false; })
bool shouldRemoveMarkersUponEditing();
- void correctionPanelTimerFired(Timer<SpellingCorrectionController>*) UNLESS_ENABLED({})
void recordAutocorrectionResponseReversed(const String& replacedString, PassRefPtr<Range> replacementRange) UNLESS_ENABLED({ UNUSED_PARAM(replacedString); UNUSED_PARAM(replacementRange); })
void markReversed(PassRefPtr<Range> changedRange) UNLESS_ENABLED({ UNUSED_PARAM(changedRange); })
void markCorrection(PassRefPtr<Range> replacedRange, const String& replacedString) UNLESS_ENABLED({ UNUSED_PARAM(replacedRange); UNUSED_PARAM(replacedString); })
- void recordSpellcheckerResponseForModifiedCorrection(Range* rangeOfCorrection, const String& corrected, const String& correction) UNLESS_ENABLED({ UNUSED_PARAM(rangeOfCorrection); UNUSED_PARAM(corrected); UNUSED_PARAM(correction); })
// This function returns false if the replacement should not be carried out.
bool processMarkersOnTextToBeReplacedByResult(const TextCheckingResult*, Range* rangeToBeReplaced, const String& stringToBeReplaced) UNLESS_ENABLED({ UNUSED_PARAM(rangeToBeReplaced); UNUSED_PARAM(stringToBeReplaced); return true; });
void deletedAutocorrectionAtPosition(const Position&, const String& originalString) UNLESS_ENABLED({ UNUSED_PARAM(originalString); })
-#if USE(AUTOCORRECTION_PANEL)
+ bool insertDictatedText(const String&, const Vector<DictationAlternative>&, Event*);
+
private:
+#if USE(AUTOCORRECTION_PANEL)
+ String dismissSoon(ReasonForDismissingAlternativeText);
+ void applyAlternativeText(const String& alternative, const Vector<DocumentMarker::MarkerType>&);
+ void timerFired(Timer<AlternativeTextController>*);
void recordAutocorrectionResponseReversed(const String& replacedString, const String& replacementString);
+ void recordSpellcheckerResponseForModifiedCorrection(Range* rangeOfCorrection, const String& corrected, const String& correction);
bool shouldStartTimerFor(const DocumentMarker* marker, int endOffset) const
{
@@ -124,26 +145,27 @@ private:
|| marker->type() == DocumentMarker::Spelling) && static_cast<int>(marker->endOffset()) == endOffset);
}
- EditorClient* client();
+ AlternativeTextClient* alternativeTextClient();
+ EditorClient* editorClient();
+
TextCheckerClient* textChecker();
FloatRect rootViewRectForRange(const Range*) const;
void markPrecedingWhitespaceForDeletedAutocorrectionAfterCommand(EditCommand*);
- EditorClient* m_client;
- Frame* m_frame;
-
- Timer<SpellingCorrectionController> m_correctionPanelTimer;
- CorrectionPanelInfo m_correctionPanelInfo;
- bool m_correctionPanelIsDismissedByEditor;
+ Timer<AlternativeTextController> m_timer;
+ AlternativeTextInfo m_alternativeTextInfo;
+ bool m_isDismissedByEditing;
String m_originalStringForLastDeletedAutocorrection;
Position m_positionForLastDeletedAutocorrection;
#endif
+
+ Frame* m_frame;
};
#undef UNLESS_ENABLED
-inline bool SpellingCorrectionController::shouldRemoveMarkersUponEditing()
+inline bool AlternativeTextController::shouldRemoveMarkersUponEditing()
{
#if USE(MARKER_REMOVAL_UPON_EDITING)
return true;
@@ -154,4 +176,4 @@ inline bool SpellingCorrectionController::shouldRemoveMarkersUponEditing()
} // namespace WebCore
-#endif // SpellingCorrectionController_h
+#endif // AlternativeTextController_h
diff --git a/Source/WebCore/editing/ApplyBlockElementCommand.cpp b/Source/WebCore/editing/ApplyBlockElementCommand.cpp
index b32641be7..952dc097b 100644
--- a/Source/WebCore/editing/ApplyBlockElementCommand.cpp
+++ b/Source/WebCore/editing/ApplyBlockElementCommand.cpp
@@ -56,14 +56,14 @@ ApplyBlockElementCommand::ApplyBlockElementCommand(Document* document, const Qua
void ApplyBlockElementCommand::doApply()
{
- if (!endingSelection().isNonOrphanedCaretOrRange())
- return;
-
if (!endingSelection().rootEditableElement())
return;
VisiblePosition visibleEnd = endingSelection().visibleEnd();
VisiblePosition visibleStart = endingSelection().visibleStart();
+ if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || visibleEnd.isOrphan())
+ return;
+
// When a selection ends at the start of a paragraph, we rarely paint
// the selection gap before that paragraph, because there often is no gap.
// In a case like this, it's not obvious to the user that the selection
diff --git a/Source/WebCore/editing/ApplyStyleCommand.cpp b/Source/WebCore/editing/ApplyStyleCommand.cpp
index 7c6347c41..403ca570d 100644
--- a/Source/WebCore/editing/ApplyStyleCommand.cpp
+++ b/Source/WebCore/editing/ApplyStyleCommand.cpp
@@ -30,7 +30,6 @@
#include "CSSParser.h"
#include "CSSProperty.h"
#include "CSSPropertyNames.h"
-#include "CSSStyleSelector.h"
#include "CSSValueKeywords.h"
#include "CSSValueList.h"
#include "Document.h"
@@ -45,6 +44,7 @@
#include "RenderObject.h"
#include "RenderText.h"
#include "StylePropertySet.h"
+#include "StyleResolver.h"
#include "Text.h"
#include "TextIterator.h"
#include "htmlediting.h"
@@ -704,12 +704,13 @@ static bool containsNonEditableRegion(Node* node)
return false;
}
-void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, Node* node, Node* pastEndNode)
+void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, PassRefPtr<Node> startNode, PassRefPtr<Node> pastEndNode)
{
if (m_removeOnly)
return;
- for (RefPtr<Node> next; node && node != pastEndNode; node = next.get()) {
+ RefPtr<Node> node = startNode;
+ for (RefPtr<Node> next; node && node != pastEndNode; node = next) {
next = node->traverseNextNode();
if (!node->renderer() || !node->rendererIsEditable())
@@ -719,10 +720,10 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, Node* n
// This is a plaintext-only region. Only proceed if it's fully selected.
// pastEndNode is the node after the last fully selected node, so if it's inside node then
// node isn't fully selected.
- if (pastEndNode && pastEndNode->isDescendantOf(node))
+ if (pastEndNode && pastEndNode->isDescendantOf(node.get()))
break;
// Add to this element's inline style and skip over its contents.
- HTMLElement* element = toHTMLElement(node);
+ HTMLElement* element = toHTMLElement(node.get());
RefPtr<StylePropertySet> inlineStyle = element->ensureInlineStyle()->copy();
inlineStyle->merge(style->style());
setNodeAttribute(element, styleAttr, inlineStyle->asText());
@@ -730,13 +731,13 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, Node* n
continue;
}
- if (isBlock(node))
+ if (isBlock(node.get()))
continue;
if (node->childNodeCount()) {
- if (node->contains(pastEndNode) || containsNonEditableRegion(node) || !node->parentNode()->rendererIsEditable())
+ if (node->contains(pastEndNode.get()) || containsNonEditableRegion(node.get()) || !node->parentNode()->rendererIsEditable())
continue;
- if (editingIgnoresContent(node)) {
+ if (editingIgnoresContent(node.get())) {
next = node->traverseNextSibling();
continue;
}
@@ -745,7 +746,7 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, Node* n
RefPtr<Node> runStart = node;
RefPtr<Node> runEnd = node;
Node* sibling = node->nextSibling();
- while (sibling && sibling != pastEndNode && !sibling->contains(pastEndNode)
+ while (sibling && sibling != pastEndNode && !sibling->contains(pastEndNode.get())
&& (!isBlock(sibling) || sibling->hasTagName(brTag))
&& !containsNonEditableRegion(sibling)) {
runEnd = sibling;
@@ -1062,8 +1063,7 @@ void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &s
// 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(s.anchorType() == Position::PositionIsAfterAnchor
- || s.offsetInContainerNode() >= lastOffsetInNode(s.containerNode()));
+ ASSERT(s.anchorType() == Position::PositionIsAfterAnchor || !offsetIsBeforeLastNodeOffset(s.offsetInContainerNode(), s.containerNode()));
e = lastPositionInOrAfterNode(prev.get());
}
}
@@ -1226,7 +1226,7 @@ bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position& start, const
int endOffset = end.computeOffsetInContainerNode();
if (isAtomicNode(endNode)) {
- if (endOffset < lastOffsetInNode(endNode))
+ if (offsetIsBeforeLastNodeOffset(endOffset, endNode))
return false;
unsigned parentLastOffset = end.deprecatedNode()->parentNode()->childNodes()->length() - 1;
diff --git a/Source/WebCore/editing/ApplyStyleCommand.h b/Source/WebCore/editing/ApplyStyleCommand.h
index 6b6e6fdef..58bc19b9e 100644
--- a/Source/WebCore/editing/ApplyStyleCommand.h
+++ b/Source/WebCore/editing/ApplyStyleCommand.h
@@ -94,7 +94,7 @@ private:
void applyRelativeFontStyleChange(EditingStyle*);
void applyInlineStyle(EditingStyle*);
void fixRangeAndApplyInlineStyle(EditingStyle*, const Position& start, const Position& end);
- void applyInlineStyleToNodeRange(EditingStyle*, Node* startNode, Node* pastEndNode);
+ void applyInlineStyleToNodeRange(EditingStyle*, PassRefPtr<Node> startNode, PassRefPtr<Node> pastEndNode);
void addBlockStyle(const StyleChange&, HTMLElement*);
void addInlineStyleIfNeeded(EditingStyle*, PassRefPtr<Node> start, PassRefPtr<Node> end, EAddStyledElement = AddStyledElement);
void splitTextAtStart(const Position& start, const Position& end);
diff --git a/Source/WebCore/editing/CompositeEditCommand.cpp b/Source/WebCore/editing/CompositeEditCommand.cpp
index 830eacf6f..8f112c067 100644
--- a/Source/WebCore/editing/CompositeEditCommand.cpp
+++ b/Source/WebCore/editing/CompositeEditCommand.cpp
@@ -292,9 +292,9 @@ void CompositeEditCommand::removeStyledElement(PassRefPtr<Element> element)
applyCommandToComposite(ApplyStyleCommand::create(element, true));
}
-void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement)
+void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea)
{
- applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement));
+ applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea));
}
void CompositeEditCommand::insertLineBreak()
@@ -304,11 +304,14 @@ void CompositeEditCommand::insertLineBreak()
bool CompositeEditCommand::isRemovableBlock(const Node* node)
{
+ if (!node->hasTagName(divTag))
+ return false;
+
Node* parentNode = node->parentNode();
- if ((parentNode && parentNode->firstChild() != parentNode->lastChild()) || !node->hasTagName(divTag))
+ if (parentNode && parentNode->firstChild() != parentNode->lastChild())
return false;
- if (!node->isElementNode() || !toElement(node)->hasAttributes())
+ if (!toElement(node)->hasAttributes())
return true;
return false;
@@ -403,6 +406,14 @@ void CompositeEditCommand::removeNodeAndPruneAncestors(PassRefPtr<Node> node)
prune(parent.release());
}
+void CompositeEditCommand::updatePositionForNodeRemovalPreservingChildren(Position& position, Node* node)
+{
+ int offset = (position.anchorType() == Position::PositionIsOffsetInAnchor) ? position.offsetInContainerNode() : 0;
+ updatePositionForNodeRemoval(position, node);
+ if (offset)
+ position.moveToOffset(offset);
+}
+
HTMLElement* CompositeEditCommand::replaceElementWithSpanPreservingChildrenAndAttributes(PassRefPtr<HTMLElement> node)
{
// It would also be possible to implement all of ReplaceNodeWithSpanCommand
@@ -1267,21 +1278,23 @@ bool CompositeEditCommand::breakOutOfEmptyListItem()
if (!newBlock)
newBlock = createDefaultParagraphElement(document());
- if (emptyListItem->renderer()->nextSibling()) {
- // If emptyListItem follows another list item, split the list node.
- if (emptyListItem->renderer()->previousSibling())
+ Node* previousListNode = emptyListItem->isElementNode() ? toElement(emptyListItem)->previousElementSibling(): emptyListItem->previousSibling();
+ Node* nextListNode = emptyListItem->isElementNode() ? toElement(emptyListItem)->nextElementSibling(): emptyListItem->nextSibling();
+ if (isListItem(nextListNode) || isListElement(nextListNode)) {
+ // If emptyListItem follows another list item or nested list, split the list node.
+ if (isListItem(previousListNode) || isListElement(previousListNode))
splitElement(static_cast<Element*>(listNode), emptyListItem);
- // If emptyListItem is followed by other list item, then insert newBlock before the list node.
+ // If emptyListItem is followed by other list item or nested list, then insert newBlock before the list node.
// Because we have splitted the element, emptyListItem is the first element in the list node.
// i.e. insert newBlock before ul or ol whose first element is emptyListItem
insertNodeBefore(newBlock, listNode);
removeNode(emptyListItem);
} else {
- // When emptyListItem does not follow any list item, insert newBlock after the enclosing list node.
+ // When emptyListItem does not follow any list item or nested list, insert newBlock after the enclosing list node.
// Remove the enclosing node if emptyListItem is the only child; otherwise just remove emptyListItem.
insertNodeAfter(newBlock, listNode);
- removeNode(emptyListItem->renderer()->previousSibling() ? emptyListItem : listNode);
+ removeNode(isListItem(previousListNode) || isListElement(previousListNode) ? emptyListItem : listNode);
}
appendBlockPlaceholder(newBlock);
diff --git a/Source/WebCore/editing/CompositeEditCommand.h b/Source/WebCore/editing/CompositeEditCommand.h
index f7d174943..7384cac36 100644
--- a/Source/WebCore/editing/CompositeEditCommand.h
+++ b/Source/WebCore/editing/CompositeEditCommand.h
@@ -82,6 +82,7 @@ public:
virtual bool isCreateLinkCommand() const;
virtual bool isTypingCommand() const;
+ virtual bool isDictationCommand() const { return false; }
virtual bool preservesTypingStyle() const;
virtual bool shouldRetainAutocorrectionIndicator() const;
virtual void setShouldRetainAutocorrectionIndicator(bool);
@@ -108,7 +109,7 @@ protected:
void insertNodeAt(PassRefPtr<Node>, const Position&);
void insertNodeAtTabSpanPosition(PassRefPtr<Node>, const Position&);
void insertNodeBefore(PassRefPtr<Node>, PassRefPtr<Node> refChild);
- void insertParagraphSeparator(bool useDefaultParagraphElement = false);
+ void insertParagraphSeparator(bool useDefaultParagraphElement = false, bool pasteBlockqutoeIntoUnquotedArea = false);
void insertLineBreak();
void insertTextIntoNode(PassRefPtr<Text>, unsigned offset, const String& text);
void mergeIdenticalElements(PassRefPtr<Element>, PassRefPtr<Element>);
@@ -125,6 +126,7 @@ protected:
HTMLElement* replaceElementWithSpanPreservingChildrenAndAttributes(PassRefPtr<HTMLElement>);
void removeNodePreservingChildren(PassRefPtr<Node>);
void removeNodeAndPruneAncestors(PassRefPtr<Node>);
+ void updatePositionForNodeRemovalPreservingChildren(Position&, Node*);
void prune(PassRefPtr<Node>);
void replaceTextInNode(PassRefPtr<Text>, unsigned offset, unsigned count, const String& replacementText);
Position replaceSelectedTextInNode(const String&);
diff --git a/Source/WebCore/editing/DeleteButtonController.cpp b/Source/WebCore/editing/DeleteButtonController.cpp
index e8b05eca5..5bb05e6a0 100644
--- a/Source/WebCore/editing/DeleteButtonController.cpp
+++ b/Source/WebCore/editing/DeleteButtonController.cpp
@@ -92,7 +92,7 @@ static bool isDeletableElement(const Node* node)
return false;
RenderBox* box = toRenderBox(renderer);
- LayoutRect borderBoundingBox = box->borderBoundingBox();
+ IntRect borderBoundingBox = box->borderBoundingBox();
if (borderBoundingBox.width() < minimumWidth || borderBoundingBox.height() < minimumHeight)
return false;
diff --git a/Source/WebCore/editing/DeleteSelectionCommand.cpp b/Source/WebCore/editing/DeleteSelectionCommand.cpp
index 5f5dfcfbd..227235edf 100644
--- a/Source/WebCore/editing/DeleteSelectionCommand.cpp
+++ b/Source/WebCore/editing/DeleteSelectionCommand.cpp
@@ -363,7 +363,7 @@ void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node)
// Make sure empty cell has some height, if a placeholder can be inserted.
document()->updateLayoutIgnorePendingStylesheets();
RenderObject *r = node->renderer();
- if (r && r->isTableCell() && toRenderTableCell(r)->contentHeight(IncludeIntrinsicPadding) <= 0) {
+ if (r && r->isTableCell() && toRenderTableCell(r)->contentHeight() <= 0) {
Position firstEditablePosition = firstEditablePositionInNode(node.get());
if (firstEditablePosition.isNotNull())
insertBlockPlaceholder(firstEditablePosition);
@@ -688,23 +688,6 @@ void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
m_typingStyle->prepareToApplyAt(m_endingPosition);
if (m_typingStyle->isEmpty())
m_typingStyle = 0;
- VisiblePosition visibleEnd(m_endingPosition);
- if (m_typingStyle &&
- isStartOfParagraph(visibleEnd) &&
- isEndOfParagraph(visibleEnd) &&
- lineBreakExistsAtVisiblePosition(visibleEnd)) {
- // Apply style to the placeholder that is now holding open the empty paragraph.
- // This makes sure that the paragraph has the right height, and that the paragraph
- // takes on the right style and retains it even if you move the selection away and
- // then move it back (which will clear typing style).
-
- setEndingSelection(visibleEnd);
- applyStyle(m_typingStyle.get(), EditActionUnspecified);
- // applyStyle can destroy the placeholder that was at m_endingPosition if it needs to
- // move it, but it will set an endingSelection() at [movedPlaceholder, 0] if it does so.
- m_endingPosition = endingSelection().start();
- m_typingStyle = 0;
- }
// This is where we've deleted all traces of a style but not a whole paragraph (that's handled above).
// In this case if we start typing, the new characters should have the same style as the just deleted ones,
// but, if we change the selection, come back and start typing that style should be lost. Also see
@@ -757,7 +740,7 @@ void DeleteSelectionCommand::removeRedundantBlocks()
while (node != rootNode) {
if (isRemovableBlock(node)) {
if (node == m_endingPosition.anchorNode())
- updatePositionForNodeRemoval(m_endingPosition, node);
+ updatePositionForNodeRemovalPreservingChildren(m_endingPosition, node);
CompositeEditCommand::removeNodePreservingChildren(node);
node = m_endingPosition.anchorNode();
diff --git a/Source/WebCore/editing/DictationAlternative.cpp b/Source/WebCore/editing/DictationAlternative.cpp
new file mode 100644
index 000000000..94b4c72a3
--- /dev/null
+++ b/Source/WebCore/editing/DictationAlternative.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DictationAlternative.h"
+
+namespace WebCore {
+
+DictationAlternative::DictationAlternative(unsigned start, unsigned length, uint64_t context)
+ : rangeStart(start)
+ , rangeLength(length)
+ , dictationContext(context)
+{
+}
+
+DictationAlternative::DictationAlternative()
+ : rangeStart(0)
+ , rangeLength(0)
+ , dictationContext(0)
+{
+}
+
+}
diff --git a/Source/WebCore/editing/DictationAlternative.h b/Source/WebCore/editing/DictationAlternative.h
new file mode 100644
index 000000000..08fcea386
--- /dev/null
+++ b/Source/WebCore/editing/DictationAlternative.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DictationAlternative_h
+#define DictationAlternative_h
+
+#include <stdint.h>
+
+namespace WebCore {
+struct DictationAlternative {
+ DictationAlternative(unsigned start, unsigned length, uint64_t context);
+ DictationAlternative();
+ unsigned rangeStart;
+ unsigned rangeLength;
+
+ // This need to be 64 bit becauese it actually holds a pointer in WebKit.
+ uint64_t dictationContext;
+};
+
+}
+
+#endif // DictationAlternative_h
diff --git a/Source/WebCore/editing/DictationCommand.cpp b/Source/WebCore/editing/DictationCommand.cpp
new file mode 100644
index 000000000..a15800e98
--- /dev/null
+++ b/Source/WebCore/editing/DictationCommand.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "DictationCommand.h"
+
+#include "AlternativeTextController.h"
+#include "Document.h"
+#include "DocumentMarker.h"
+#include "DocumentMarkerController.h"
+#include "Frame.h"
+#include "InsertParagraphSeparatorCommand.h"
+#include "InsertTextCommand.h"
+#include "Text.h"
+
+namespace WebCore {
+
+class DictationCommandLineOperation {
+public:
+ DictationCommandLineOperation(DictationCommand* dictationCommand)
+ : m_dictationCommand(dictationCommand)
+ { }
+
+ void operator()(size_t lineOffset, size_t lineLength, bool isLastLine) const
+ {
+ if (lineLength > 0)
+ m_dictationCommand->insertTextRunWithoutNewlines(lineOffset, lineLength);
+ if (!isLastLine)
+ m_dictationCommand->insertParagraphSeparator();
+ }
+private:
+ DictationCommand* m_dictationCommand;
+};
+
+class DictationMarkerSupplier : public TextInsertionMarkerSupplier {
+public:
+ static PassRefPtr<DictationMarkerSupplier> create(const Vector<DictationAlternative>& alternatives)
+ {
+ return adoptRef(new DictationMarkerSupplier(alternatives));
+ }
+
+ virtual void addMarkersToTextNode(Text* textNode, unsigned offsetOfInsertion, const String& textToBeInserted)
+ {
+ Document* document = textNode->document();
+ DocumentMarkerController* markerController =document->markers();
+ for (size_t i = 0; i < m_alternatives.size(); ++i) {
+ const DictationAlternative& alternative = m_alternatives[i];
+ markerController->addMarkerToNode(textNode, alternative.rangeStart + offsetOfInsertion, alternative.rangeLength, DocumentMarker::DictationAlternatives, DictationMarkerDetails::create(textToBeInserted.substring(alternative.rangeStart, alternative.rangeLength), alternative.dictationContext));
+ markerController->addMarkerToNode(textNode, alternative.rangeStart + offsetOfInsertion, alternative.rangeLength, DocumentMarker::SpellCheckingExemption);
+ }
+ }
+
+protected:
+ DictationMarkerSupplier(const Vector<DictationAlternative>& alternatives)
+ : m_alternatives(alternatives)
+ {
+ }
+private:
+ Vector<DictationAlternative> m_alternatives;
+};
+
+DictationCommand::DictationCommand(Document* document, const String& text, const Vector<DictationAlternative>& alternatives)
+ : TextInsertionBaseCommand(document)
+ , m_textToInsert(text)
+ , m_alternatives(alternatives)
+{
+}
+
+void DictationCommand::insertText(Document* document, const String& text, const Vector<DictationAlternative>& alternatives, const VisibleSelection& selectionForInsertion)
+{
+ RefPtr<Frame> frame = document->frame();
+ ASSERT(frame);
+
+ VisibleSelection currentSelection = frame->selection()->selection();
+
+ String newText = dispatchBeforeTextInsertedEvent(text, selectionForInsertion, false);
+
+ RefPtr<DictationCommand> cmd;
+ if (newText == text)
+ cmd = DictationCommand::create(document, newText, alternatives);
+ else
+ // If the text was modified before insertion, the location of dictation alternatives
+ // will not be valid anymore. We will just drop the alternatives.
+ cmd = DictationCommand::create(document, newText, Vector<DictationAlternative>());
+ applyTextInsertionCommand(frame.get(), cmd, selectionForInsertion, currentSelection);
+}
+
+void DictationCommand::doApply()
+{
+ DictationCommandLineOperation operation(this);
+ forEachLineInString(m_textToInsert, operation);
+}
+
+void DictationCommand::insertTextRunWithoutNewlines(size_t lineStart, size_t lineLength)
+{
+ Vector<DictationAlternative> alternativesInLine;
+ collectDictationAlternativesInRange(lineStart, lineLength, alternativesInLine);
+ RefPtr<InsertTextCommand> command = InsertTextCommand::createWithMarkerSupplier(document(), m_textToInsert.substring(lineStart, lineLength), DictationMarkerSupplier::create(alternativesInLine));
+ applyCommandToComposite(command, endingSelection());
+}
+
+void DictationCommand::insertParagraphSeparator()
+{
+ if (!canAppendNewLineFeedToSelection(endingSelection()))
+ return;
+
+ applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
+}
+
+void DictationCommand::collectDictationAlternativesInRange(size_t rangeStart, size_t rangeLength, Vector<DictationAlternative>& alternatives)
+{
+ for (size_t i = 0; i < m_alternatives.size(); ++i) {
+ const DictationAlternative& alternative = m_alternatives[i];
+ if (alternative.rangeStart >= rangeStart && (alternative.rangeStart + alternative.rangeLength) <= rangeStart + rangeLength)
+ alternatives.append(DictationAlternative(alternative.rangeStart - rangeStart, alternative.rangeLength, alternative.dictationContext));
+ }
+
+}
+
+}
diff --git a/Source/WebCore/editing/DictationCommand.h b/Source/WebCore/editing/DictationCommand.h
new file mode 100644
index 000000000..f22ac552d
--- /dev/null
+++ b/Source/WebCore/editing/DictationCommand.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DictationCommand_h
+#define DictationCommand_h
+
+#include "DictationAlternative.h"
+#include "TextInsertionBaseCommand.h"
+
+namespace WebCore {
+
+class DocumentMarker;
+
+class DictationCommand : public TextInsertionBaseCommand {
+ friend class DictationCommandLineOperation;
+public:
+ static void insertText(Document*, const String&, const Vector<DictationAlternative>& alternatives, const VisibleSelection&);
+ virtual bool isDictationCommand() const { return true; }
+private:
+ static PassRefPtr<DictationCommand> create(Document* document, const String& text, const Vector<DictationAlternative>& alternatives)
+ {
+ return adoptRef(new DictationCommand(document, text, alternatives));
+ }
+
+ DictationCommand(Document*, const String& text, const Vector<DictationAlternative>& alternatives);
+
+ virtual void doApply();
+
+ void insertTextRunWithoutNewlines(size_t lineStart, size_t lineLength);
+ void insertParagraphSeparator();
+ void collectDictationAlternativesInRange(size_t rangeStart, size_t rangeLength, Vector<DictationAlternative>&);
+
+ String m_textToInsert;
+ Vector<DictationAlternative> m_alternatives;
+};
+}
+
+#endif // DictationCommand_h
diff --git a/Source/WebCore/editing/EditingAllInOne.cpp b/Source/WebCore/editing/EditingAllInOne.cpp
index c0299b320..9642418de 100644
--- a/Source/WebCore/editing/EditingAllInOne.cpp
+++ b/Source/WebCore/editing/EditingAllInOne.cpp
@@ -25,6 +25,7 @@
// This all-in-one cpp file cuts down on template bloat to allow us to build our Windows release build.
+#include <AlternativeTextController.cpp>
#include <AppendNodeCommand.cpp>
#include <ApplyBlockElementCommand.cpp>
#include <ApplyStyleCommand.cpp>
@@ -35,6 +36,8 @@
#include <DeleteButtonController.cpp>
#include <DeleteFromTextNodeCommand.cpp>
#include <DeleteSelectionCommand.cpp>
+#include <DictationAlternative.cpp>
+#include <DictationCommand.cpp>
#include <EditCommand.cpp>
#include <EditingStyle.cpp>
#include <Editor.cpp>
@@ -62,15 +65,16 @@
#include <ReplaceSelectionCommand.cpp>
#include <SetNodeAttributeCommand.cpp>
#include <SetSelectionCommand.cpp>
+#include <SimplifyMarkupCommand.cpp>
#include <SmartReplace.cpp>
#include <SmartReplaceCF.cpp>
#include <SpellingCorrectionCommand.cpp>
-#include <SpellingCorrectionController.cpp>
#include <SpellChecker.cpp>
#include <SplitElementCommand.cpp>
#include <SplitTextNodeCommand.cpp>
#include <SplitTextNodeContainingElementCommand.cpp>
#include <TextCheckingHelper.cpp>
+#include <TextInsertionBaseCommand.cpp>
#include <TextIterator.cpp>
#include <TypingCommand.cpp>
#include <UnlinkCommand.cpp>
diff --git a/Source/WebCore/editing/EditingBehavior.h b/Source/WebCore/editing/EditingBehavior.h
index 9ed1d77dd..bbf68f503 100644
--- a/Source/WebCore/editing/EditingBehavior.h
+++ b/Source/WebCore/editing/EditingBehavior.h
@@ -60,10 +60,6 @@ public:
// On Mac, when processing a contextual click, the object being clicked upon should be selected.
bool shouldSelectOnContextualMenuClick() const { return m_type == EditingMacBehavior; }
- // On Windows, moving caret left or right by word moves the caret by word in visual order.
- // It moves the caret by word in logical order in other platforms.
- bool shouldMoveLeftRightByWordInVisualOrder() const { return m_type == EditingWindowsBehavior; }
-
// On Mac and Windows, pressing backspace (when it isn't handled otherwise) should navigate back.
bool shouldNavigateBackOnBackspace() const { return m_type != EditingUnixBehavior; }
@@ -72,6 +68,11 @@ public:
// to the other end of the line/word (Unix/Windows behavior).
bool shouldExtendSelectionByWordOrLineAcrossCaret() const { return m_type != EditingMacBehavior; }
+ // Based on native behavior, when using ctrl(alt)+arrow to move caret by word, ctrl(alt)+left arrow moves caret to
+ // immediately before the word in all platforms, for example, the word break positions are: "|abc |def |hij |opq".
+ // But ctrl+right arrow moves caret to "abc |def |hij |opq" on Windows and "abc| def| hij| opq|" on Mac and Linux.
+ bool shouldSkipSpaceWhenMovingRight() const { return m_type == EditingWindowsBehavior; }
+
private:
EditingBehaviorType m_type;
};
diff --git a/Source/WebCore/editing/EditingStyle.cpp b/Source/WebCore/editing/EditingStyle.cpp
index 3a360d8a9..6e441c83d 100644
--- a/Source/WebCore/editing/EditingStyle.cpp
+++ b/Source/WebCore/editing/EditingStyle.cpp
@@ -30,8 +30,8 @@
#include "ApplyStyleCommand.h"
#include "CSSComputedStyleDeclaration.h"
#include "CSSParser.h"
+#include "CSSRuleList.h"
#include "CSSStyleRule.h"
-#include "CSSStyleSelector.h"
#include "CSSValueKeywords.h"
#include "CSSValueList.h"
#include "Frame.h"
@@ -44,6 +44,7 @@
#include "QualifiedName.h"
#include "RenderStyle.h"
#include "StylePropertySet.h"
+#include "StyleResolver.h"
#include "StyleRule.h"
#include "StyledElement.h"
#include "htmlediting.h"
@@ -54,7 +55,7 @@ namespace WebCore {
// Editing style properties must be preserved during editing operation.
// e.g. when a user inserts a new paragraph, all properties listed here must be copied to the new paragraph.
-static const int editingProperties[] = {
+static const CSSPropertyID editingProperties[] = {
CSSPropertyBackgroundColor,
CSSPropertyTextDecoration,
@@ -133,7 +134,7 @@ protected:
HTMLElementEquivalent(CSSPropertyID);
HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName);
HTMLElementEquivalent(CSSPropertyID, int primitiveValue, const QualifiedName& tagName);
- const int m_propertyID;
+ const CSSPropertyID m_propertyID;
const RefPtr<CSSPrimitiveValue> m_primitiveValue;
const QualifiedName* m_tagName; // We can store a pointer because HTML tag names are const global.
};
@@ -331,7 +332,7 @@ EditingStyle::EditingStyle(const CSSStyleDeclaration* style)
extractFontSizeDelta();
}
-EditingStyle::EditingStyle(int propertyID, const String& value)
+EditingStyle::EditingStyle(CSSPropertyID propertyID, const String& value)
: m_mutableStyle(0)
, m_shouldUseFixedDefaultFontSize(false)
, m_fontSizeDelta(NoFontDelta)
@@ -410,7 +411,7 @@ void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyl
m_mutableStyle->removeProperty(CSSPropertyWebkitTextStrokeColor);
}
-void EditingStyle::setProperty(int propertyID, const String& value, bool important)
+void EditingStyle::setProperty(CSSPropertyID propertyID, const String& value, bool important)
{
if (!m_mutableStyle)
m_mutableStyle = StylePropertySet::create();
@@ -602,7 +603,7 @@ void EditingStyle::collapseTextDecorationProperties()
}
// CSS properties that create a visual difference only when applied to text.
-static const int textOnlyProperties[] = {
+static const CSSPropertyID textOnlyProperties[] = {
CSSPropertyTextDecoration,
CSSPropertyWebkitTextDecorationsInEffect,
CSSPropertyFontStyle,
@@ -670,7 +671,7 @@ bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, Edi
unsigned propertyCount = m_mutableStyle->propertyCount();
for (unsigned i = 0; i < propertyCount; ++i) {
- CSSPropertyID propertyID = static_cast<CSSPropertyID>(m_mutableStyle->propertyAt(i).id());
+ CSSPropertyID propertyID = m_mutableStyle->propertyAt(i).id();
// We don't override whitespace property of a tab span because that would collapse the tab into a space.
if (propertyID == CSSPropertyWhiteSpace && isTabSpanNode(element))
@@ -1029,7 +1030,7 @@ void EditingStyle::mergeStyle(const StylePropertySet* style, CSSPropertyOverride
static PassRefPtr<StylePropertySet> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
{
RefPtr<StylePropertySet> style = StylePropertySet::create();
- RefPtr<CSSRuleList> matchedRules = element->document()->styleSelector()->styleRulesForElement(element, rulesToInclude);
+ RefPtr<CSSRuleList> matchedRules = element->document()->styleResolver()->styleRulesForElement(element, rulesToInclude);
if (matchedRules) {
for (unsigned i = 0; i < matchedRules->length(); i++) {
if (matchedRules->item(i)->type() == CSSRule::STYLE_RULE) {
@@ -1045,7 +1046,7 @@ static PassRefPtr<StylePropertySet> styleFromMatchedRulesForElement(Element* ele
void EditingStyle::mergeStyleFromRules(StyledElement* element)
{
RefPtr<StylePropertySet> styleFromMatchedRules = styleFromMatchedRulesForElement(element,
- CSSStyleSelector::AuthorCSSRules | CSSStyleSelector::CrossOriginCSSRules);
+ StyleResolver::AuthorCSSRules | StyleResolver::CrossOriginCSSRules);
// Styles from the inline style declaration, held in the variable "style", take precedence
// over those from matched rules.
if (m_mutableStyle)
@@ -1083,7 +1084,7 @@ void EditingStyle::mergeStyleFromRulesForSerialization(StyledElement* element)
static void removePropertiesInStyle(StylePropertySet* styleToRemovePropertiesFrom, StylePropertySet* style)
{
unsigned propertyCount = style->propertyCount();
- Vector<int> propertiesToRemove(propertyCount);
+ Vector<CSSPropertyID> propertiesToRemove(propertyCount);
for (unsigned i = 0; i < propertyCount; ++i)
propertiesToRemove[i] = style->propertyAt(i).id();
@@ -1097,7 +1098,7 @@ void EditingStyle::removeStyleFromRulesAndContext(StyledElement* element, Node*
return;
// 1. Remove style from matched rules because style remain without repeating it in inline style declaration
- RefPtr<StylePropertySet> styleFromMatchedRules = styleFromMatchedRulesForElement(element, CSSStyleSelector::AllButEmptyCSSRules);
+ RefPtr<StylePropertySet> styleFromMatchedRules = styleFromMatchedRulesForElement(element, StyleResolver::AllButEmptyCSSRules);
if (styleFromMatchedRules && !styleFromMatchedRules->isEmpty())
m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), styleFromMatchedRules->ensureCSSStyleDeclaration());
@@ -1126,7 +1127,7 @@ void EditingStyle::removePropertiesInElementDefaultStyle(Element* element)
if (!m_mutableStyle || m_mutableStyle->isEmpty())
return;
- RefPtr<StylePropertySet> defaultStyle = styleFromMatchedRulesForElement(element, CSSStyleSelector::UAAndUserCSSRules);
+ RefPtr<StylePropertySet> defaultStyle = styleFromMatchedRulesForElement(element, StyleResolver::UAAndUserCSSRules);
removePropertiesInStyle(m_mutableStyle.get(), defaultStyle.get());
}
@@ -1318,7 +1319,7 @@ StyleChange::StyleChange(EditingStyle* style, const Position& position)
m_cssStyle = mutableStyle->asText().stripWhiteSpace();
}
-static void setTextDecorationProperty(StylePropertySet* style, const CSSValueList* newTextDecoration, int propertyID)
+static void setTextDecorationProperty(StylePropertySet* style, const CSSValueList* newTextDecoration, CSSPropertyID propertyID)
{
if (newTextDecoration->length())
style->setProperty(propertyID, newTextDecoration->cssText(), style->propertyIsImportant(propertyID));
@@ -1394,7 +1395,7 @@ void StyleChange::extractTextStyles(Document* document, StylePropertySet* style,
}
}
-static void diffTextDecorations(StylePropertySet* style, int propertID, CSSValue* refTextDecoration)
+static void diffTextDecorations(StylePropertySet* style, CSSPropertyID propertID, CSSValue* refTextDecoration)
{
RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
@@ -1533,10 +1534,10 @@ int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, boo
{
if (isCSSValueLength(value)) {
int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
- int legacyFontSize = CSSStyleSelector::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize);
+ int legacyFontSize = StyleResolver::legacyFontSize(document, pixelFontSize, shouldUseFixedFontDefaultSize);
// Use legacy font size only if pixel value matches exactly to that of legacy font size.
int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
- if (mode == AlwaysUseLegacyFontSize || CSSStyleSelector::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize)
+ if (mode == AlwaysUseLegacyFontSize || StyleResolver::fontSizeForKeyword(document, cssPrimitiveEquivalent, shouldUseFixedFontDefaultSize) == pixelFontSize)
return legacyFontSize;
return 0;
diff --git a/Source/WebCore/editing/EditingStyle.h b/Source/WebCore/editing/EditingStyle.h
index c213036e1..177c2a007 100644
--- a/Source/WebCore/editing/EditingStyle.h
+++ b/Source/WebCore/editing/EditingStyle.h
@@ -91,7 +91,7 @@ public:
return adoptRef(new EditingStyle(style));
}
- static PassRefPtr<EditingStyle> create(int propertyID, const String& value)
+ static PassRefPtr<EditingStyle> create(CSSPropertyID propertyID, const String& value)
{
return adoptRef(new EditingStyle(propertyID, value));
}
@@ -152,10 +152,10 @@ private:
EditingStyle(const Position&, PropertiesToInclude);
EditingStyle(const StylePropertySet*);
EditingStyle(const CSSStyleDeclaration*);
- EditingStyle(int propertyID, const String& value);
+ EditingStyle(CSSPropertyID, const String& value);
void init(Node*, PropertiesToInclude);
void removeTextFillAndStrokeColorsIfNeeded(RenderStyle*);
- void setProperty(int propertyID, const String& value, bool important = false);
+ void setProperty(CSSPropertyID, const String& value, bool important = false);
void replaceFontSizeByKeywordIfPossible(RenderStyle*, CSSComputedStyleDeclaration*);
void extractFontSizeDelta();
TriState triStateOfStyle(CSSStyleDeclaration* styleToCompare, ShouldIgnoreTextOnlyProperties) const;
diff --git a/Source/WebCore/editing/Editor.cpp b/Source/WebCore/editing/Editor.cpp
index 72e21fa24..73acc57af 100644
--- a/Source/WebCore/editing/Editor.cpp
+++ b/Source/WebCore/editing/Editor.cpp
@@ -28,19 +28,20 @@
#include "Editor.h"
#include "AXObjectCache.h"
+#include "AlternativeTextController.h"
#include "ApplyStyleCommand.h"
#include "CSSComputedStyleDeclaration.h"
#include "CSSProperty.h"
#include "CSSPropertyNames.h"
-#include "CSSStyleSelector.h"
#include "CSSValueKeywords.h"
#include "CachedResourceLoader.h"
#include "ClipboardEvent.h"
#include "CompositionEvent.h"
-#include "SpellingCorrectionController.h"
#include "CreateLinkCommand.h"
#include "DeleteButtonController.h"
#include "DeleteSelectionCommand.h"
+#include "DictationAlternative.h"
+#include "DictationCommand.h"
#include "DocumentFragment.h"
#include "DocumentMarkerController.h"
#include "EditingText.h"
@@ -68,16 +69,17 @@
#include "TextCheckingHelper.h"
#include "RemoveFormatCommand.h"
#include "RenderBlock.h"
-#include "RenderLayer.h"
#include "RenderPart.h"
#include "RenderTextControl.h"
#include "RenderedPosition.h"
#include "ReplaceSelectionCommand.h"
#include "Settings.h"
+#include "SimplifyMarkupCommand.h"
#include "Sound.h"
#include "SpellChecker.h"
#include "SpellingCorrectionCommand.h"
#include "StylePropertySet.h"
+#include "StyleResolver.h"
#include "Text.h"
#include "TextCheckerClient.h"
#include "TextCheckingHelper.h"
@@ -413,6 +415,8 @@ void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,
applyCommand(ReplaceSelectionCommand::create(m_frame->document(), fragment, options, EditActionPaste));
revealSelectionAfterEditingOperation();
+ if (m_frame->selection()->isInPasswordField())
+ return;
Node* nodeToCheck = m_frame->selection()->rootEditableElement();
if (!nodeToCheck)
return;
@@ -482,7 +486,7 @@ void Editor::respondToChangedSelection(const VisibleSelection& oldSelection)
client()->respondToChangedSelection(m_frame);
setStartNewKillRingSequence(true);
m_deleteButtonController->respondToChangedSelection(oldSelection);
- m_spellingCorrector->respondToChangedSelection(oldSelection);
+ m_alternativeTextController->respondToChangedSelection(oldSelection);
}
void Editor::respondToChangedContents(const VisibleSelection& endingSelection)
@@ -731,18 +735,18 @@ void Editor::applyParagraphStyleToSelection(StylePropertySet* style, EditAction
applyParagraphStyle(style, editingAction);
}
-bool Editor::selectionStartHasStyle(int propertyID, const String& value) const
+bool Editor::selectionStartHasStyle(CSSPropertyID propertyID, const String& value) const
{
return EditingStyle::create(propertyID, value)->triStateOfStyle(
EditingStyle::styleAtSelectionStart(m_frame->selection()->selection(), propertyID == CSSPropertyBackgroundColor).get());
}
-TriState Editor::selectionHasStyle(int propertyID, const String& value) const
+TriState Editor::selectionHasStyle(CSSPropertyID propertyID, const String& value) const
{
return EditingStyle::create(propertyID, value)->triStateOfStyle(m_frame->selection()->selection());
}
-String Editor::selectionStartCSSPropertyValue(int propertyID)
+String Editor::selectionStartCSSPropertyValue(CSSPropertyID propertyID)
{
RefPtr<EditingStyle> selectionStyle = EditingStyle::styleAtSelectionStart(m_frame->selection()->selection(),
propertyID == CSSPropertyBackgroundColor);
@@ -782,7 +786,7 @@ void Editor::appliedEditing(PassRefPtr<CompositeEditCommand> cmd)
dispatchEditableContentChangedEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement());
VisibleSelection newSelection(cmd->endingSelection());
- m_spellingCorrector->respondToAppliedEditing(cmd.get());
+ m_alternativeTextController->respondToAppliedEditing(cmd.get());
// Don't clear the typing style with this selection change. We do those things elsewhere if necessary.
changeSelectionAfterCommand(newSelection, false, false);
@@ -812,7 +816,7 @@ void Editor::unappliedEditing(PassRefPtr<EditCommandComposition> cmd)
VisibleSelection newSelection(cmd->startingSelection());
changeSelectionAfterCommand(newSelection, true, true);
- m_spellingCorrector->respondToUnappliedEditing(cmd.get());
+ m_alternativeTextController->respondToUnappliedEditing(cmd.get());
m_lastEditCommand = 0;
if (client())
@@ -844,7 +848,7 @@ Editor::Editor(Frame* frame)
, m_shouldStyleWithCSS(false)
, m_killRing(adoptPtr(new KillRing))
, m_spellChecker(adoptPtr(new SpellChecker(frame)))
- , m_spellingCorrector(adoptPtr(new SpellingCorrectionController(frame)))
+ , m_alternativeTextController(adoptPtr(new AlternativeTextController(frame)))
, m_areMarkedTextMatchesHighlighted(false)
, m_defaultParagraphSeparator(EditorParagraphSeparatorIsDiv)
{
@@ -872,6 +876,11 @@ bool Editor::insertTextForConfirmedComposition(const String& text)
return m_frame->eventHandler()->handleTextInputEvent(text, 0, TextEventInputComposition);
}
+bool Editor::insertDictatedText(const String& text, const Vector<DictationAlternative>& dictationAlternatives, Event* triggeringEvent)
+{
+ return m_alternativeTextController->insertDictatedText(text, dictationAlternatives, triggeringEvent);
+}
+
bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, TextEvent* triggeringEvent)
{
if (text.isEmpty())
@@ -895,7 +904,7 @@ bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectIn
if (text.length() == 1 && isPunct(text[0]) && !isAmbiguousBoundaryCharacter(text[0]))
shouldConsiderApplyingAutocorrection = true;
- bool autocorrectionWasApplied = shouldConsiderApplyingAutocorrection && m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
+ bool autocorrectionWasApplied = shouldConsiderApplyingAutocorrection && m_alternativeTextController->applyAutocorrectionBeforeTypingIfAppropriate();
// Get the selection to use for the event that triggered this insertText.
// If the event handler changed the selection, we may want to use a different selection
@@ -906,12 +915,16 @@ bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectIn
RefPtr<Document> document = selectionStart->document();
// Insert the text
- TypingCommand::Options options = 0;
- if (selectInsertedText)
- options |= TypingCommand::SelectInsertedText;
- if (autocorrectionWasApplied)
- options |= TypingCommand::RetainAutocorrectionIndicator;
- TypingCommand::insertText(document.get(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone);
+ if (triggeringEvent && triggeringEvent->isDictation())
+ DictationCommand::insertText(document.get(), text, triggeringEvent->dictationAlternatives(), selection);
+ else {
+ TypingCommand::Options options = 0;
+ if (selectInsertedText)
+ options |= TypingCommand::SelectInsertedText;
+ if (autocorrectionWasApplied)
+ options |= TypingCommand::RetainAutocorrectionIndicator;
+ TypingCommand::insertText(document.get(), text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionConfirm : TypingCommand::TextCompositionNone);
+ }
// Reveal the current selection
if (Frame* editedFrame = document->frame())
@@ -931,7 +944,7 @@ bool Editor::insertLineBreak()
if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
return true;
- bool autocorrectionIsApplied = m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
+ bool autocorrectionIsApplied = m_alternativeTextController->applyAutocorrectionBeforeTypingIfAppropriate();
TypingCommand::insertLineBreak(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0);
revealSelectionAfterEditingOperation();
@@ -949,7 +962,7 @@ bool Editor::insertParagraphSeparator()
if (!shouldInsertText("\n", m_frame->selection()->toNormalizedRange().get(), EditorInsertActionTyped))
return true;
- bool autocorrectionIsApplied = m_spellingCorrector->applyAutocorrectionBeforeTypingIfAppropriate();
+ bool autocorrectionIsApplied = m_alternativeTextController->applyAutocorrectionBeforeTypingIfAppropriate();
TypingCommand::insertParagraphSeparator(m_frame->document(), autocorrectionIsApplied ? TypingCommand::RetainAutocorrectionIndicator : 0);
revealSelectionAfterEditingOperation();
@@ -1039,6 +1052,24 @@ void Editor::performDelete()
setStartNewKillRingSequence(false);
}
+void Editor::simplifyMarkup(Node* startNode, Node* endNode)
+{
+ if (!startNode)
+ return;
+ if (endNode) {
+ if (startNode->document() != endNode->document())
+ return;
+ // check if start node is before endNode
+ Node* node = startNode;
+ while (node && node != endNode)
+ node = node->traverseNextNode();
+ if (!node)
+ return;
+ }
+
+ applyCommand(SimplifyMarkupCommand::create(m_frame->document(), startNode, (endNode) ? endNode->traverseNextNode() : 0));
+}
+
void Editor::copyURL(const KURL& url, const String& title)
{
Pasteboard::generalPasteboard()->writeURL(url, title, m_frame);
@@ -1173,7 +1204,7 @@ void Editor::toggleAutomaticTextReplacement()
bool Editor::isAutomaticSpellingCorrectionEnabled()
{
- return m_spellingCorrector->isAutomaticSpellingCorrectionEnabled();
+ return m_alternativeTextController->isAutomaticSpellingCorrectionEnabled();
}
void Editor::toggleAutomaticSpellingCorrection()
@@ -1328,7 +1359,7 @@ void Editor::setComposition(const String& text, SetCompositionMode mode)
// If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input
// will delete the old composition with an optimized replace operation.
- if (text.isEmpty())
+ if (text.isEmpty() && mode != CancelComposition)
TypingCommand::deleteSelection(m_frame->document(), 0);
m_compositionNode = 0;
@@ -1772,7 +1803,7 @@ void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart,
#endif
if (unifiedTextCheckerEnabled()) {
- m_spellingCorrector->applyPendingCorrection(selectionAfterTyping);
+ m_alternativeTextController->applyPendingCorrection(selectionAfterTyping);
TextCheckingTypeMask textCheckingOptions = 0;
@@ -1908,10 +1939,11 @@ void Editor::markBadGrammar(const VisibleSelection& selection)
void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textCheckingOptions, Range* spellingRange, Range* grammarRange)
{
+ ASSERT(m_frame);
ASSERT(unifiedTextCheckerEnabled());
// There shouldn't be pending autocorrection at this moment.
- ASSERT(!m_spellingCorrector->hasPendingCorrection());
+ ASSERT(!m_alternativeTextController->hasPendingCorrection());
bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
bool shouldShowCorrectionPanel = textCheckingOptions & TextCheckingTypeShowCorrectionPanel;
@@ -2004,7 +2036,7 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
if (shouldMarkSpelling && result->type == TextCheckingTypeSpelling && resultLocation >= paragraph.checkingStart() && resultLocation + resultLength <= spellingRangeEndOffset && !resultEndsAtAmbiguousBoundary) {
ASSERT(resultLength > 0 && resultLocation >= 0);
RefPtr<Range> misspellingRange = paragraph.subrange(resultLocation, resultLength);
- if (!m_spellingCorrector->isSpellingMarkerAllowed(misspellingRange))
+ if (!m_alternativeTextController->isSpellingMarkerAllowed(misspellingRange))
continue;
misspellingRange->startContainer()->document()->markers()->addMarker(misspellingRange.get(), DocumentMarker::Spelling, result->replacement);
} else if (shouldMarkGrammar && result->type == TextCheckingTypeGrammar && paragraph.checkingRangeCovers(resultLocation, resultLength)) {
@@ -2047,7 +2079,7 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
continue;
String replacedString = plainText(rangeToReplace.get());
- bool existingMarkersPermitReplacement = m_spellingCorrector->processMarkersOnTextToBeReplacedByResult(result, rangeToReplace.get(), replacedString);
+ bool existingMarkersPermitReplacement = m_alternativeTextController->processMarkersOnTextToBeReplacedByResult(result, rangeToReplace.get(), replacedString);
if (!existingMarkersPermitReplacement)
continue;
@@ -2058,7 +2090,7 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
// shouldShowCorrectionPanel can be true only when the panel is available.
if (resultLocation + resultLength == spellingRangeEndOffset) {
// We only show the correction panel on the last word.
- m_spellingCorrector->show(rangeToReplace, result->replacement);
+ m_alternativeTextController->show(rangeToReplace, result->replacement);
break;
}
// If this function is called for showing correction panel, we ignore other correction or replacement.
@@ -2094,7 +2126,7 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
// Add a marker so that corrections can easily be undone and won't be re-corrected.
if (result->type == TextCheckingTypeCorrection)
- m_spellingCorrector->markCorrection(paragraph.subrange(resultLocation, replacementLength), replacedString);
+ m_alternativeTextController->markCorrection(paragraph.subrange(resultLocation, replacementLength), replacedString);
}
}
}
@@ -2127,12 +2159,12 @@ void Editor::changeBackToReplacedString(const String& replacedString)
if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
return;
- m_spellingCorrector->recordAutocorrectionResponseReversed(replacedString, selection);
+ m_alternativeTextController->recordAutocorrectionResponseReversed(replacedString, selection);
TextCheckingParagraph paragraph(selection);
replaceSelectionWithText(replacedString, false, false);
RefPtr<Range> changedRange = paragraph.subrange(paragraph.checkingStart(), replacedString.length());
changedRange->startContainer()->document()->markers()->addMarker(changedRange.get(), DocumentMarker::Replacement, String());
- m_spellingCorrector->markReversed(changedRange.get());
+ m_alternativeTextController->markReversed(changedRange.get());
}
@@ -2156,12 +2188,12 @@ void Editor::markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelec
void Editor::unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction)
{
- m_spellingCorrector->respondToUnappliedSpellCorrection(selectionOfCorrected, corrected, correction);
+ m_alternativeTextController->respondToUnappliedSpellCorrection(selectionOfCorrected, corrected, correction);
}
void Editor::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionAtWordBoundary)
{
- if (!m_spellingCorrector->shouldRemoveMarkersUponEditing())
+ if (!m_alternativeTextController->shouldRemoveMarkersUponEditing())
return;
// We want to remove the markers from a word if an editing command will change the word. This can happen in one of
@@ -2225,13 +2257,13 @@ void Editor::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionA
Document* document = m_frame->document();
RefPtr<Range> wordRange = Range::create(document, startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent());
- document->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption, DocumentMarkerController::RemovePartiallyOverlappingMarker);
+ document->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption | DocumentMarker::DictationAlternatives, DocumentMarkerController::RemovePartiallyOverlappingMarker);
document->markers()->clearDescriptionOnMarkersIntersectingRange(wordRange.get(), DocumentMarker::Replacement);
}
void Editor::deletedAutocorrectionAtPosition(const Position& position, const String& originalString)
{
- m_spellingCorrector->deletedAutocorrectionAtPosition(position, originalString);
+ m_alternativeTextController->deletedAutocorrectionAtPosition(position, originalString);
}
PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
@@ -2245,7 +2277,7 @@ PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
FrameView* frameView = frame->view();
if (!frameView)
return 0;
- LayoutPoint framePoint = frameView->windowToContents(windowPoint);
+ IntPoint framePoint = frameView->windowToContents(windowPoint);
VisibleSelection selection(frame->visiblePositionForPoint(framePoint));
return avoidIntersectionWithNode(selection.toNormalizedRange().get(), m_deleteButtonController->containerElement());
}
@@ -2356,20 +2388,20 @@ void Editor::addToKillRing(Range* range, bool prepend)
m_shouldStartNewKillRingSequence = false;
}
-void Editor::startCorrectionPanelTimer()
+void Editor::startAlternativeTextUITimer()
{
- m_spellingCorrector->startCorrectionPanelTimer(CorrectionPanelInfo::PanelTypeCorrection);
+ m_alternativeTextController->startAlternativeTextUITimer(AlternativeTextTypeCorrection);
}
-void Editor::handleCorrectionPanelResult(const String& correction)
+void Editor::handleAlternativeTextUIResult(const String& correction)
{
- m_spellingCorrector->handleCorrectionPanelResult(correction);
+ m_alternativeTextController->handleAlternativeTextUIResult(correction);
}
void Editor::dismissCorrectionPanelAsIgnored()
{
- m_spellingCorrector->dismiss(ReasonForDismissingCorrectionPanelIgnored);
+ m_alternativeTextController->dismiss(ReasonForDismissingAlternativeTextIgnored);
}
bool Editor::insideVisibleArea(const LayoutPoint& point) const
@@ -2553,11 +2585,11 @@ IntRect Editor::firstRectForRange(Range* range) const
ASSERT(range->startContainer());
ASSERT(range->endContainer());
- LayoutRect startCaretRect = RenderedPosition(VisiblePosition(range->startPosition()).deepEquivalent(), DOWNSTREAM).absoluteRect(extraWidthToEndOfLine);
+ IntRect startCaretRect = RenderedPosition(VisiblePosition(range->startPosition()).deepEquivalent(), DOWNSTREAM).absoluteRect(&extraWidthToEndOfLine);
if (startCaretRect == LayoutRect())
return IntRect();
- LayoutRect endCaretRect = RenderedPosition(VisiblePosition(range->endPosition()).deepEquivalent(), UPSTREAM).absoluteRect();
+ IntRect endCaretRect = RenderedPosition(VisiblePosition(range->endPosition()).deepEquivalent(), UPSTREAM).absoluteRect();
if (endCaretRect == LayoutRect())
return IntRect();
@@ -2697,7 +2729,7 @@ PassRefPtr<Range> Editor::findStringAndScrollToVisible(const String& target, Ran
if (!nextMatch)
return 0;
- nextMatch->firstNode()->renderer()->enclosingLayer()->scrollRectToVisible(nextMatch->boundingBox(),
+ nextMatch->firstNode()->renderer()->scrollRectToVisible(nextMatch->boundingBox(),
ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded);
return nextMatch.release();
@@ -2865,7 +2897,7 @@ unsigned Editor::countMatchesForText(const String& target, Range* range, FindOpt
PaintBehavior oldBehavior = m_frame->view()->paintBehavior();
m_frame->view()->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers);
- m_frame->view()->paintContents(&context, visibleRect);
+ m_frame->view()->paintContents(&context, enclosingIntRect(visibleRect));
m_frame->view()->setPaintBehavior(oldBehavior);
}
}
@@ -2885,7 +2917,7 @@ void Editor::setMarkedTextMatchesAreHighlighted(bool flag)
void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options)
{
- m_spellingCorrector->stopPendingCorrection(oldSelection);
+ m_alternativeTextController->stopPendingCorrection(oldSelection);
bool closeTyping = options & FrameSelection::CloseTyping;
bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled();
@@ -2920,11 +2952,18 @@ void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, Fra
}
#if !PLATFORM(MAC) || (PLATFORM(MAC) && (defined(BUILDING_ON_LEOPARD) || defined(BUILDING_ON_SNOW_LEOPARD)))
+#if PLATFORM(CHROMIUM)
+ if (!m_frame->settings() || !m_frame->settings()->asynchronousSpellCheckingEnabled()) {
+ if (RefPtr<Range> wordRange = newAdjacentWords.toNormalizedRange())
+ m_frame->document()->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
+ }
+#else
// This only erases markers that are in the first unit (word or sentence) of the selection.
// Perhaps peculiar, but it matches AppKit on these Mac OS X versions.
if (RefPtr<Range> wordRange = newAdjacentWords.toNormalizedRange())
m_frame->document()->markers()->removeMarkers(wordRange.get(), DocumentMarker::Spelling);
#endif
+#endif
if (RefPtr<Range> sentenceRange = newSelectedSentence.toNormalizedRange())
m_frame->document()->markers()->removeMarkers(sentenceRange.get(), DocumentMarker::Grammar);
}
diff --git a/Source/WebCore/editing/Editor.h b/Source/WebCore/editing/Editor.h
index 3c2544a71..7c875e874 100644
--- a/Source/WebCore/editing/Editor.h
+++ b/Source/WebCore/editing/Editor.h
@@ -28,6 +28,7 @@
#include "ClipboardAccessPolicy.h"
#include "Color.h"
+#include "DictationAlternative.h"
#include "DocumentMarker.h"
#include "EditAction.h"
#include "EditingBehavior.h"
@@ -61,7 +62,7 @@ class Pasteboard;
class SimpleFontData;
class SpellChecker;
class SpellCheckRequest;
-class SpellingCorrectionController;
+class AlternativeTextController;
class StylePropertySet;
class Text;
class TextCheckerClient;
@@ -136,9 +137,9 @@ public:
void respondToChangedSelection(const VisibleSelection& oldSelection);
void respondToChangedContents(const VisibleSelection& endingSelection);
- bool selectionStartHasStyle(int propertyID, const String& value) const;
- TriState selectionHasStyle(int propertyID, const String& value) const;
- String selectionStartCSSPropertyValue(int propertyID);
+ bool selectionStartHasStyle(CSSPropertyID, const String& value) const;
+ TriState selectionHasStyle(CSSPropertyID, const String& value) const;
+ String selectionStartCSSPropertyValue(CSSPropertyID);
TriState selectionUnorderedListState() const;
TriState selectionOrderedListState() const;
@@ -202,9 +203,14 @@ public:
bool insertText(const String&, Event* triggeringEvent);
bool insertTextForConfirmedComposition(const String& text);
+ bool insertDictatedText(const String&, const Vector<DictationAlternative>& dictationAlternatives, Event* triggeringEvent);
bool insertTextWithoutSendingTextEvent(const String&, bool selectInsertedText, TextEvent* triggeringEvent);
bool insertLineBreak();
bool insertParagraphSeparator();
+
+#if PLATFORM(MAC)
+ bool insertParagraphSeparatorInQuotedContent();
+#endif
bool isContinuousSpellCheckingEnabled();
void toggleContinuousSpellChecking();
@@ -300,7 +306,7 @@ public:
void setStartNewKillRingSequence(bool);
- PassRefPtr<Range> rangeForPoint(const LayoutPoint& windowPoint);
+ PassRefPtr<Range> rangeForPoint(const IntPoint& windowPoint);
void clear();
@@ -319,9 +325,9 @@ public:
void addToKillRing(Range*, bool prepend);
- void startCorrectionPanelTimer();
+ void startAlternativeTextUITimer();
// If user confirmed a correction in the correction panel, correction has non-zero length, otherwise it means that user has dismissed the panel.
- void handleCorrectionPanelResult(const String& correction);
+ void handleAlternativeTextUIResult(const String& correction);
void dismissCorrectionPanelAsIgnored();
void pasteAsFragment(PassRefPtr<DocumentFragment>, bool smartReplace, bool matchStyle);
@@ -374,6 +380,8 @@ public:
void takeFindStringFromSelection();
void writeSelectionToPasteboard(const String& pasteboardName, const Vector<String>& pasteboardTypes);
void readSelectionFromPasteboard(const String& pasteboardName);
+ String stringSelectionForPasteboard();
+ PassRefPtr<SharedBuffer> dataSelectionForPasteboard(const String& pasteboardName);
#endif
void replaceSelectionWithFragment(PassRefPtr<DocumentFragment>, bool selectReplacement, bool smartReplace, bool matchStyle);
@@ -381,6 +389,8 @@ public:
bool selectionStartHasMarkerFor(DocumentMarker::MarkerType, int from, int length) const;
void updateMarkersForWordsAffectedByEditing(bool onlyHandleWordsContainingSelection);
void deletedAutocorrectionAtPosition(const Position&, const String& originalString);
+
+ void simplifyMarkup(Node* startNode, Node* endNode);
void deviceScaleFactorChanged();
@@ -401,7 +411,7 @@ private:
bool m_shouldStyleWithCSS;
OwnPtr<KillRing> m_killRing;
OwnPtr<SpellChecker> m_spellChecker;
- OwnPtr<SpellingCorrectionController> m_spellingCorrector;
+ OwnPtr<AlternativeTextController> m_alternativeTextController;
VisibleSelection m_mark;
bool m_areMarkedTextMatchesHighlighted;
EditorParagraphSeparator m_defaultParagraphSeparator;
diff --git a/Source/WebCore/editing/EditorCommand.cpp b/Source/WebCore/editing/EditorCommand.cpp
index b9f34b980..a2f147ea2 100644
--- a/Source/WebCore/editing/EditorCommand.cpp
+++ b/Source/WebCore/editing/EditorCommand.cpp
@@ -114,14 +114,14 @@ static bool applyCommandToFrame(Frame* frame, EditorCommandSource source, EditAc
return false;
}
-static bool executeApplyStyle(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, const String& propertyValue)
+static bool executeApplyStyle(Frame* frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const String& propertyValue)
{
RefPtr<StylePropertySet> style = StylePropertySet::create();
style->setProperty(propertyID, propertyValue);
return applyCommandToFrame(frame, source, action, style.get());
}
-static bool executeApplyStyle(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, int propertyValue)
+static bool executeApplyStyle(Frame* frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, int propertyValue)
{
RefPtr<StylePropertySet> style = StylePropertySet::create();
style->setProperty(propertyID, propertyValue);
@@ -131,7 +131,7 @@ static bool executeApplyStyle(Frame* frame, EditorCommandSource source, EditActi
// FIXME: executeToggleStyleInList does not handle complicated cases such as <b><u>hello</u>world</b> properly.
// This function must use Editor::selectionHasStyle to determine the current style but we cannot fix this
// until https://bugs.webkit.org/show_bug.cgi?id=27818 is resolved.
-static bool executeToggleStyleInList(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, CSSValue* value)
+static bool executeToggleStyleInList(Frame* frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, CSSValue* value)
{
ExceptionCode ec = 0;
RefPtr<EditingStyle> selectionStyle = EditingStyle::styleAtSelectionStart(frame->selection()->selection());
@@ -156,7 +156,7 @@ static bool executeToggleStyleInList(Frame* frame, EditorCommandSource source, E
return applyCommandToFrame(frame, source, action, newMutableStyle.get());
}
-static bool executeToggleStyle(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, const char* offValue, const char* onValue)
+static bool executeToggleStyle(Frame* frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const char* offValue, const char* onValue)
{
// Style is considered present when
// Mac: present at the beginning of selection
@@ -172,7 +172,7 @@ static bool executeToggleStyle(Frame* frame, EditorCommandSource source, EditAct
return applyCommandToFrame(frame, source, action, style->style());
}
-static bool executeApplyParagraphStyle(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, const String& propertyValue)
+static bool executeApplyParagraphStyle(Frame* frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const String& propertyValue)
{
RefPtr<StylePropertySet> style = StylePropertySet::create();
style->setProperty(propertyID, propertyValue);
@@ -224,14 +224,14 @@ static bool expandSelectionToGranularity(Frame* frame, TextGranularity granulari
return true;
}
-static TriState stateStyle(Frame* frame, int propertyID, const char* desiredValue)
+static TriState stateStyle(Frame* frame, CSSPropertyID propertyID, const char* desiredValue)
{
if (frame->editor()->behavior().shouldToggleStyleBasedOnStartOfSelection())
return frame->editor()->selectionStartHasStyle(propertyID, desiredValue) ? TrueTriState : FalseTriState;
return frame->editor()->selectionHasStyle(propertyID, desiredValue);
}
-static String valueStyle(Frame* frame, int propertyID)
+static String valueStyle(Frame* frame, CSSPropertyID propertyID)
{
// FIXME: Rather than retrieving the style at the start of the current selection,
// we should retrieve the style present throughout the selection for non-Mac platforms.
diff --git a/Source/WebCore/editing/FrameSelection.cpp b/Source/WebCore/editing/FrameSelection.cpp
index de2d65d7e..1a7468ee7 100644
--- a/Source/WebCore/editing/FrameSelection.cpp
+++ b/Source/WebCore/editing/FrameSelection.cpp
@@ -50,7 +50,6 @@
#include "InlineTextBox.h"
#include "Page.h"
#include "Range.h"
-#include "RenderLayer.h"
#include "RenderText.h"
#include "RenderTextControl.h"
#include "RenderTheme.h"
@@ -76,7 +75,7 @@ using namespace HTMLNames;
static inline LayoutUnit NoXPosForVerticalArrowNavigation()
{
- return std::numeric_limits<LayoutUnit>::min();
+ return MIN_LAYOUT_UNIT;
}
CaretBase::CaretBase(CaretVisibility visibility)
@@ -641,12 +640,15 @@ VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
} else
pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
break;
- case WordGranularity:
- if (visualWordMovementEnabled()
- || (m_frame && m_frame->editor()->behavior().shouldMoveLeftRightByWordInVisualOrder())) {
- pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
- break;
- }
+ case WordGranularity: {
+#if USE(ICU_UNICODE)
+ // Visual word movement relies on isWordTextBreak which is not implemented in WinCE and QT.
+ // https://bugs.webkit.org/show_bug.cgi?id=81136.
+ bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor()->behavior().shouldSkipSpaceWhenMovingRight();
+ pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
+ break;
+#endif
+ }
case SentenceGranularity:
case LineGranularity:
case ParagraphGranularity:
@@ -808,12 +810,13 @@ VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
else
pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
break;
- case WordGranularity:
- if (visualWordMovementEnabled()
- || (m_frame && m_frame->editor()->behavior().shouldMoveLeftRightByWordInVisualOrder())) {
- pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
- break;
- }
+ case WordGranularity: {
+#if USE(ICU_UNICODE)
+ bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor()->behavior().shouldSkipSpaceWhenMovingRight();
+ pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
+ break;
+#endif
+ }
case SentenceGranularity:
case LineGranularity:
case ParagraphGranularity:
@@ -982,9 +985,9 @@ bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, Tex
}
// FIXME: Maybe baseline would be better?
-static bool absoluteCaretY(const VisiblePosition &c, LayoutUnit &y)
+static bool absoluteCaretY(const VisiblePosition &c, int &y)
{
- LayoutRect rect = c.absoluteCaretBounds();
+ IntRect rect = c.absoluteCaretBounds();
if (rect.isEmpty())
return false;
y = rect.y() + rect.height() / 2;
@@ -1023,12 +1026,12 @@ bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, Vertic
break;
}
- LayoutUnit startY;
+ int startY;
if (!absoluteCaretY(pos, startY))
return false;
if (direction == DirectionUp)
startY = -startY;
- LayoutUnit lastY = startY;
+ int lastY = startY;
VisiblePosition result;
VisiblePosition next;
@@ -1040,12 +1043,12 @@ bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, Vertic
if (next.isNull() || next == p)
break;
- LayoutUnit nextY;
+ int nextY;
if (!absoluteCaretY(next, nextY))
break;
if (direction == DirectionUp)
nextY = -nextY;
- if (nextY - startY > static_cast<LayoutUnit>(verticalDistance))
+ if (nextY - startY > static_cast<int>(verticalDistance))
break;
if (nextY >= lastY) {
lastY = nextY;
@@ -1242,11 +1245,11 @@ LayoutRect FrameSelection::localCaretRect()
return localCaretRectWithoutUpdate();
}
-LayoutRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const
+IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const
{
RenderObject* caretPainter = caretRenderer(node);
if (!caretPainter)
- return LayoutRect();
+ return IntRect();
LayoutRect localRect(rect);
if (caretPainter->isBox())
@@ -1254,7 +1257,7 @@ LayoutRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& r
return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
}
-LayoutRect FrameSelection::absoluteCaretBounds()
+IntRect FrameSelection::absoluteCaretBounds()
{
recomputeCaretRect();
return m_absCaretBounds;
@@ -1270,7 +1273,7 @@ static LayoutRect repaintRectForCaret(LayoutRect caret)
return caret;
}
-LayoutRect CaretBase::caretRepaintRect(Node* node) const
+IntRect CaretBase::caretRepaintRect(Node* node) const
{
return absoluteBoundsForLocalRect(node, repaintRectForCaret(localCaretRectWithoutUpdate()));
}
@@ -1292,19 +1295,22 @@ bool FrameSelection::recomputeCaretRect()
if (oldRect == newRect && !m_absCaretBoundsDirty)
return false;
- LayoutRect oldAbsCaretBounds = m_absCaretBounds;
+ IntRect oldAbsCaretBounds = m_absCaretBounds;
// FIXME: Rename m_caretRect to m_localCaretRect.
m_absCaretBounds = absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate());
m_absCaretBoundsDirty = false;
if (oldAbsCaretBounds == m_absCaretBounds)
return false;
-
- LayoutRect oldAbsoluteCaretRepaintBounds = m_absoluteCaretRepaintBounds;
+
+#if ENABLE(TEXT_CARET)
+ IntRect oldAbsoluteCaretRepaintBounds = m_absoluteCaretRepaintBounds;
+#endif
+
// We believe that we need to inflate the local rect before transforming it to obtain the repaint bounds.
m_absoluteCaretRepaintBounds = caretRepaintRect(m_selection.start().deprecatedNode());
-#if ENABLE(TEXT_CARET)
+#if ENABLE(TEXT_CARET)
if (RenderView* view = toRenderView(m_frame->document()->renderer())) {
// FIXME: make caret repainting container-aware.
view->repaintRectangleInViewAndCompositedLayers(oldAbsoluteCaretRepaintBounds, false);
@@ -1469,7 +1475,7 @@ bool FrameSelection::contains(const LayoutPoint& point)
HitTestRequest request(HitTestRequest::ReadOnly |
HitTestRequest::Active);
HitTestResult result(point);
- document->renderView()->layer()->hitTest(request, result);
+ document->renderView()->hitTest(request, result);
Node* innerNode = result.innerNode();
if (!innerNode || !innerNode->renderer())
return false;
@@ -1626,7 +1632,7 @@ void FrameSelection::focusedOrActiveStateChanged()
// Update for caps lock state
m_frame->eventHandler()->capsLockStateMayHaveChanged();
- // Because CSSStyleSelector::checkOneSelector() and
+ // Because StyleResolver::checkOneSelector() and
// RenderTheme::isFocused() check if the frame is active, we have to
// update style and theme state that depended on those.
if (Node* node = m_frame->document()->focusedNode()) {
@@ -1945,10 +1951,8 @@ void FrameSelection::revealSelection(const ScrollAlignment& alignment, bool reve
// FIXME: This code only handles scrolling the startContainer's layer, but
// the selection rect could intersect more than just that.
// See <rdar://problem/4799899>.
- if (RenderLayer* layer = start.deprecatedNode()->renderer()->enclosingLayer()) {
- layer->scrollRectToVisible(rect, alignment, alignment);
+ if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment))
updateAppearance();
- }
}
}
diff --git a/Source/WebCore/editing/FrameSelection.h b/Source/WebCore/editing/FrameSelection.h
index b1877f6d0..b4d92e33b 100644
--- a/Source/WebCore/editing/FrameSelection.h
+++ b/Source/WebCore/editing/FrameSelection.h
@@ -59,8 +59,8 @@ protected:
void invalidateCaretRect(Node*, bool caretRectChanged = false);
void clearCaretRect();
bool updateCaretRect(Document*, const VisiblePosition& caretPosition);
- LayoutRect absoluteBoundsForLocalRect(Node*, const LayoutRect&) const;
- LayoutRect caretRepaintRect(Node*) const;
+ IntRect absoluteBoundsForLocalRect(Node*, const LayoutRect&) const;
+ IntRect caretRepaintRect(Node*) const;
bool shouldRepaintCaret(const RenderView*, bool isContentEditable) const;
void paintCaret(Node*, GraphicsContext*, const LayoutPoint&, const LayoutRect& clipRect) const;
RenderObject* caretRenderer(Node*) const;
@@ -180,7 +180,7 @@ public:
LayoutRect localCaretRect();
// Bounds of (possibly transformed) caret in absolute coords
- LayoutRect absoluteCaretBounds();
+ IntRect absoluteCaretBounds();
void setCaretRectNeedsUpdate() { CaretBase::setCaretRectNeedsUpdate(); }
void willBeModified(EAlteration, SelectionDirection);
@@ -295,8 +295,10 @@ private:
RefPtr<EditingStyle> m_typingStyle;
Timer<FrameSelection> m_caretBlinkTimer;
- LayoutRect m_absCaretBounds; // absolute bounding rect for the caret
- LayoutRect m_absoluteCaretRepaintBounds;
+ // The painted bounds of the caret in absolute coordinates
+ IntRect m_absCaretBounds;
+ // Similar to above, but inflated to ensure proper repaint (see https://bugs.webkit.org/show_bug.cgi?id=19086)
+ IntRect m_absoluteCaretRepaintBounds;
bool m_absCaretBoundsDirty : 1;
bool m_caretPaint : 1;
bool m_isCaretBlinkingSuspended : 1;
diff --git a/Source/WebCore/editing/IndentOutdentCommand.cpp b/Source/WebCore/editing/IndentOutdentCommand.cpp
index e2cbec4c5..f0c413b9c 100644
--- a/Source/WebCore/editing/IndentOutdentCommand.cpp
+++ b/Source/WebCore/editing/IndentOutdentCommand.cpp
@@ -114,7 +114,7 @@ void IndentOutdentCommand::indentIntoBlockquote(const Position& start, const Pos
insertNodeAt(targetBlockquote, start);
else
insertNodeBefore(targetBlockquote, outerBlock);
- startOfContents = positionAfterNode(targetBlockquote.get());
+ startOfContents = positionInParentAfterNode(targetBlockquote.get());
}
moveParagraphWithClones(startOfContents, end, targetBlockquote.get(), outerBlock.get());
diff --git a/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp b/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp
index 46f3c8a5d..0f12dce37 100644
--- a/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp
+++ b/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp
@@ -58,9 +58,10 @@ static Element* highestVisuallyEquivalentDivBelowRoot(Element* startBlock)
return curBlock;
}
-InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand(Document *document, bool mustUseDefaultParagraphElement)
+InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand(Document *document, bool mustUseDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea)
: CompositeEditCommand(document)
, m_mustUseDefaultParagraphElement(mustUseDefaultParagraphElement)
+ , m_pasteBlockqutoeIntoUnquotedArea(pasteBlockqutoeIntoUnquotedArea)
{
}
@@ -226,8 +227,10 @@ void InsertParagraphSeparatorCommand::doApply()
} else {
// We can get here if we pasted a copied portion of a blockquote with a newline at the end and are trying to paste it
// into an unquoted area. We then don't want the newline within the blockquote or else it will also be quoted.
- if (Node* highestBlockquote = highestEnclosingNodeOfType(canonicalPos, &isMailBlockquote))
- startBlock = static_cast<Element*>(highestBlockquote);
+ if (m_pasteBlockqutoeIntoUnquotedArea) {
+ if (Node* highestBlockquote = highestEnclosingNodeOfType(canonicalPos, &isMailBlockquote))
+ startBlock = static_cast<Element*>(highestBlockquote);
+ }
// Most of the time we want to stay at the nesting level of the startBlock (e.g., when nesting within lists). However,
// for div nodes, this can result in nested div tags that are hard to break out of.
diff --git a/Source/WebCore/editing/InsertParagraphSeparatorCommand.h b/Source/WebCore/editing/InsertParagraphSeparatorCommand.h
index 2eae77d37..9f7210824 100644
--- a/Source/WebCore/editing/InsertParagraphSeparatorCommand.h
+++ b/Source/WebCore/editing/InsertParagraphSeparatorCommand.h
@@ -34,13 +34,13 @@ class EditingStyle;
class InsertParagraphSeparatorCommand : public CompositeEditCommand {
public:
- static PassRefPtr<InsertParagraphSeparatorCommand> create(Document* document, bool useDefaultParagraphElement = false)
+ static PassRefPtr<InsertParagraphSeparatorCommand> create(Document* document, bool useDefaultParagraphElement = false, bool pasteBlockqutoeIntoUnquotedArea = false)
{
- return adoptRef(new InsertParagraphSeparatorCommand(document, useDefaultParagraphElement));
+ return adoptRef(new InsertParagraphSeparatorCommand(document, useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea));
}
private:
- InsertParagraphSeparatorCommand(Document*, bool useDefaultParagraphElement);
+ InsertParagraphSeparatorCommand(Document*, bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea);
virtual void doApply();
@@ -56,6 +56,7 @@ private:
RefPtr<EditingStyle> m_style;
bool m_mustUseDefaultParagraphElement;
+ bool m_pasteBlockqutoeIntoUnquotedArea;
};
}
diff --git a/Source/WebCore/editing/InsertTextCommand.cpp b/Source/WebCore/editing/InsertTextCommand.cpp
index 39fa2a897..cf2497fd2 100644
--- a/Source/WebCore/editing/InsertTextCommand.cpp
+++ b/Source/WebCore/editing/InsertTextCommand.cpp
@@ -46,6 +46,15 @@ InsertTextCommand::InsertTextCommand(Document* document, const String& text, boo
{
}
+InsertTextCommand::InsertTextCommand(Document* document, const String& text, PassRefPtr<TextInsertionMarkerSupplier> markerSupplier)
+ : CompositeEditCommand(document)
+ , m_text(text)
+ , m_selectInsertedText(false)
+ , m_rebalanceType(RebalanceLeadingAndTrailingWhitespaces)
+ , m_markerSupplier(markerSupplier)
+{
+}
+
Position InsertTextCommand::positionInsideTextNode(const Position& p)
{
Position pos = p;
@@ -167,6 +176,8 @@ void InsertTextCommand::doApply()
insertTextIntoNode(textNode, offset, m_text);
endPosition = Position(textNode, offset + m_text.length());
+ if (m_markerSupplier)
+ m_markerSupplier->addMarkersToTextNode(textNode.get(), offset, m_text);
if (m_rebalanceType == RebalanceLeadingAndTrailingWhitespaces) {
// The insertion may require adjusting adjacent whitespace, if it is present.
diff --git a/Source/WebCore/editing/InsertTextCommand.h b/Source/WebCore/editing/InsertTextCommand.h
index 1adba44ae..e5b2e222f 100644
--- a/Source/WebCore/editing/InsertTextCommand.h
+++ b/Source/WebCore/editing/InsertTextCommand.h
@@ -30,6 +30,17 @@
namespace WebCore {
+class DocumentMarkerController;
+class Text;
+
+class TextInsertionMarkerSupplier : public RefCounted<TextInsertionMarkerSupplier> {
+public:
+ virtual ~TextInsertionMarkerSupplier() { }
+ virtual void addMarkersToTextNode(Text*, unsigned offsetOfInsertion, const String& textInserted) = 0;
+protected:
+ TextInsertionMarkerSupplier() { }
+};
+
class InsertTextCommand : public CompositeEditCommand {
public:
enum RebalanceType {
@@ -43,9 +54,15 @@ public:
return adoptRef(new InsertTextCommand(document, text, selectInsertedText, rebalanceType));
}
+ static PassRefPtr<InsertTextCommand> createWithMarkerSupplier(Document* document, const String& text, PassRefPtr<TextInsertionMarkerSupplier> markerSupplier)
+ {
+ return adoptRef(new InsertTextCommand(document, text, markerSupplier));
+ }
+
private:
InsertTextCommand(Document*, const String& text, bool selectInsertedText, RebalanceType);
+ InsertTextCommand(Document*, const String& text, PassRefPtr<TextInsertionMarkerSupplier>);
void deleteCharacter();
@@ -61,6 +78,7 @@ private:
String m_text;
bool m_selectInsertedText;
RebalanceType m_rebalanceType;
+ RefPtr<TextInsertionMarkerSupplier> m_markerSupplier;
};
} // namespace WebCore
diff --git a/Source/WebCore/editing/MarkupAccumulator.cpp b/Source/WebCore/editing/MarkupAccumulator.cpp
index 476904ebc..15752bcba 100644
--- a/Source/WebCore/editing/MarkupAccumulator.cpp
+++ b/Source/WebCore/editing/MarkupAccumulator.cpp
@@ -37,14 +37,13 @@
#include "KURL.h"
#include "ProcessingInstruction.h"
#include "XMLNSNames.h"
-#include <wtf/text/StringBuilder.h>
#include <wtf/unicode/CharacterNames.h>
namespace WebCore {
using namespace HTMLNames;
-void appendCharactersReplacingEntities(StringBuilder& out, const UChar* content, size_t length, EntityMask entityMask)
+void appendCharactersReplacingEntities(StringBuilder& result, const UChar* content, size_t length, EntityMask entityMask)
{
DEFINE_STATIC_LOCAL(const String, ampReference, ("&amp;"));
DEFINE_STATIC_LOCAL(const String, ltReference, ("&lt;"));
@@ -64,14 +63,14 @@ void appendCharactersReplacingEntities(StringBuilder& out, const UChar* content,
for (size_t i = 0; i < length; ++i) {
for (size_t m = 0; m < WTF_ARRAY_LENGTH(entityMaps); ++m) {
if (content[i] == entityMaps[m].entity && entityMaps[m].mask & entityMask) {
- out.append(content + positionAfterLastEntity, i - positionAfterLastEntity);
- out.append(entityMaps[m].reference);
+ result.append(content + positionAfterLastEntity, i - positionAfterLastEntity);
+ result.append(entityMaps[m].reference);
positionAfterLastEntity = i + 1;
break;
}
}
}
- out.append(content + positionAfterLastEntity, length - positionAfterLastEntity);
+ result.append(content + positionAfterLastEntity, length - positionAfterLastEntity);
}
MarkupAccumulator::MarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs resolveUrlsMethod, const Range* range)
@@ -85,18 +84,15 @@ MarkupAccumulator::~MarkupAccumulator()
{
}
-String MarkupAccumulator::serializeNodes(Node* node, Node* nodeToSkip, EChildrenOnly childrenOnly)
+String MarkupAccumulator::serializeNodes(Node* targetNode, Node* nodeToSkip, EChildrenOnly childrenOnly)
{
- StringBuilder out;
- serializeNodesWithNamespaces(node, nodeToSkip, childrenOnly, 0);
- out.reserveCapacity(length());
- concatenateMarkup(out);
- return out.toString();
+ serializeNodesWithNamespaces(targetNode, nodeToSkip, childrenOnly, 0);
+ return m_markup.toString();
}
-void MarkupAccumulator::serializeNodesWithNamespaces(Node* node, Node* nodeToSkip, EChildrenOnly childrenOnly, const Namespaces* namespaces)
+void MarkupAccumulator::serializeNodesWithNamespaces(Node* targetNode, Node* nodeToSkip, EChildrenOnly childrenOnly, const Namespaces* namespaces)
{
- if (node == nodeToSkip)
+ if (targetNode == nodeToSkip)
return;
Namespaces namespaceHash;
@@ -104,15 +100,15 @@ void MarkupAccumulator::serializeNodesWithNamespaces(Node* node, Node* nodeToSki
namespaceHash = *namespaces;
if (!childrenOnly)
- appendStartTag(node, &namespaceHash);
+ appendStartTag(targetNode, &namespaceHash);
- if (!(node->document()->isHTMLDocument() && elementCannotHaveEndTag(node))) {
- for (Node* current = node->firstChild(); current; current = current->nextSibling())
+ if (!(targetNode->document()->isHTMLDocument() && elementCannotHaveEndTag(targetNode))) {
+ for (Node* current = targetNode->firstChild(); current; current = current->nextSibling())
serializeNodesWithNamespaces(current, nodeToSkip, IncludeNode, &namespaceHash);
}
if (!childrenOnly)
- appendEndTag(node);
+ appendEndTag(targetNode);
}
String MarkupAccumulator::resolveURLIfNeeded(const Element* element, const String& urlString) const
@@ -134,23 +130,19 @@ String MarkupAccumulator::resolveURLIfNeeded(const Element* element, const Strin
void MarkupAccumulator::appendString(const String& string)
{
- m_succeedingMarkup.append(string);
+ m_markup.append(string);
}
void MarkupAccumulator::appendStartTag(Node* node, Namespaces* namespaces)
{
- StringBuilder markup;
- appendStartMarkup(markup, node, namespaces);
- appendString(markup.toString());
+ appendStartMarkup(m_markup, node, namespaces);
if (m_nodes)
m_nodes->append(node);
}
void MarkupAccumulator::appendEndTag(Node* node)
{
- StringBuilder markup;
- appendEndMarkup(markup, node);
- appendString(markup.toString());
+ appendEndMarkup(m_markup, node);
}
size_t MarkupAccumulator::totalLength(const Vector<String>& strings)
@@ -161,13 +153,9 @@ size_t MarkupAccumulator::totalLength(const Vector<String>& strings)
return length;
}
-// FIXME: This is a very inefficient way of accumulating the markup.
-// We're converting results of appendStartMarkup and appendEndMarkup from StringBuilder to String
-// and then back to StringBuilder and again to String here.
-void MarkupAccumulator::concatenateMarkup(StringBuilder& out)
+void MarkupAccumulator::concatenateMarkup(StringBuilder& result)
{
- for (size_t i = 0; i < m_succeedingMarkup.size(); ++i)
- out.append(m_succeedingMarkup[i]);
+ result.append(m_markup);
}
void MarkupAccumulator::appendAttributeValue(StringBuilder& result, const String& attribute, bool documentIsHTML)
@@ -184,13 +172,13 @@ void MarkupAccumulator::appendQuotedURLAttributeValue(StringBuilder& result, con
{
ASSERT(element->isURLAttribute(const_cast<Attribute*>(&attribute)));
const String resolvedURLString = resolveURLIfNeeded(element, attribute.value());
- UChar quoteChar = '\"';
+ UChar quoteChar = '"';
String strippedURLString = resolvedURLString.stripWhiteSpace();
if (protocolIsJavaScript(strippedURLString)) {
// minimal escaping for javascript urls
if (strippedURLString.contains('"')) {
if (strippedURLString.contains('\''))
- strippedURLString.replace('\"', "&quot;");
+ strippedURLString.replace('"', "&quot;");
else
quoteChar = '\'';
}
@@ -206,7 +194,7 @@ void MarkupAccumulator::appendQuotedURLAttributeValue(StringBuilder& result, con
result.append(quoteChar);
}
-void MarkupAccumulator::appendNodeValue(StringBuilder& out, const Node* node, const Range* range, EntityMask entityMask)
+void MarkupAccumulator::appendNodeValue(StringBuilder& result, const Node* node, const Range* range, EntityMask entityMask)
{
String str = node->nodeValue();
const UChar* characters = str.characters();
@@ -223,7 +211,7 @@ void MarkupAccumulator::appendNodeValue(StringBuilder& out, const Node* node, co
}
}
- appendCharactersReplacingEntities(out, characters, length, entityMask);
+ appendCharactersReplacingEntities(result, characters, length, entityMask);
}
bool MarkupAccumulator::shouldAddNamespaceElement(const Element* element)
@@ -246,13 +234,13 @@ bool MarkupAccumulator::shouldAddNamespaceAttribute(const Attribute& attribute,
namespaces.set(emptyAtom.impl(), attribute.value().impl());
return false;
}
-
+
QualifiedName xmlnsPrefixAttr(xmlnsAtom, attribute.localName(), XMLNSNames::xmlnsNamespaceURI);
if (attribute.name() == xmlnsPrefixAttr) {
namespaces.set(attribute.localName().impl(), attribute.value().impl());
return false;
}
-
+
return true;
}
@@ -261,7 +249,7 @@ void MarkupAccumulator::appendNamespace(StringBuilder& result, const AtomicStrin
namespaces.checkConsistency();
if (namespaceURI.isEmpty())
return;
-
+
// Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key
AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl();
AtomicStringImpl* foundNS = namespaces.get(pre);
@@ -286,24 +274,56 @@ EntityMask MarkupAccumulator::entityMaskForText(Text* text) const
const QualifiedName* parentName = 0;
if (text->parentElement())
parentName = &static_cast<Element*>(text->parentElement())->tagQName();
-
+
if (parentName && (*parentName == scriptTag || *parentName == styleTag || *parentName == xmpTag))
return EntityMaskInCDATA;
return text->document()->isHTMLDocument() ? EntityMaskInHTMLPCDATA : EntityMaskInPCDATA;
}
-void MarkupAccumulator::appendText(StringBuilder& out, Text* text)
+void MarkupAccumulator::appendText(StringBuilder& result, Text* text)
{
- appendNodeValue(out, text, m_range, entityMaskForText(text));
+ appendNodeValue(result, text, m_range, entityMaskForText(text));
}
-void MarkupAccumulator::appendComment(StringBuilder& out, const String& comment)
+void MarkupAccumulator::appendComment(StringBuilder& result, const String& comment)
{
// FIXME: Comment content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "-->".
- out.append("<!--");
- out.append(comment);
- out.append("-->");
+ static const char commentBegin[] = "<!--";
+ result.append(commentBegin, sizeof(commentBegin) - 1);
+ result.append(comment);
+ static const char commentEnd[] = "-->";
+ result.append(commentEnd, sizeof(commentEnd) - 1);
+}
+
+void MarkupAccumulator::appendXMLDeclaration(StringBuilder& result, const Document* document)
+{
+ if (!document->hasXMLDeclaration())
+ return;
+
+ static const char xmlDeclStart[] = "<?xml version=\"";
+ result.append(xmlDeclStart, sizeof(xmlDeclStart) - 1);
+ result.append(document->xmlVersion());
+ const String& encoding = document->xmlEncoding();
+ if (!encoding.isEmpty()) {
+ static const char xmlEncoding[] = "\" encoding=\"";
+ result.append(xmlEncoding, sizeof(xmlEncoding) - 1);
+ result.append(encoding);
+ }
+ if (document->xmlStandaloneStatus() != Document::StandaloneUnspecified) {
+ static const char xmlStandalone[] = "\" standalone=\"";
+ result.append(xmlStandalone, sizeof(xmlStandalone) - 1);
+ if (document->xmlStandalone()) {
+ static const char standaloneYes[] = "yes";
+ result.append(standaloneYes, sizeof(standaloneYes) - 1);
+ } else {
+ static const char standaloneNo[] = "no";
+ result.append(standaloneNo, sizeof(standaloneNo) - 1);
+ }
+ }
+
+ static const char xmlDeclEnd[] = "\"?>";
+ result.append(xmlDeclEnd, sizeof(xmlDeclEnd) - 1);
}
void MarkupAccumulator::appendDocumentType(StringBuilder& result, const DocumentType* n)
@@ -311,105 +331,114 @@ void MarkupAccumulator::appendDocumentType(StringBuilder& result, const Document
if (n->name().isEmpty())
return;
- result.append("<!DOCTYPE ");
+ static const char doctypeString[] = "<!DOCTYPE ";
+ result.append(doctypeString, sizeof(doctypeString) - 1);
result.append(n->name());
if (!n->publicId().isEmpty()) {
- result.append(" PUBLIC \"");
+ static const char publicString[] = " PUBLIC \"";
+ result.append(publicString, sizeof(publicString) - 1);
result.append(n->publicId());
- result.append("\"");
+ result.append('"');
if (!n->systemId().isEmpty()) {
- result.append(" \"");
+ result.append(' ');
+ result.append('"');
result.append(n->systemId());
- result.append("\"");
+ result.append('"');
}
} else if (!n->systemId().isEmpty()) {
- result.append(" SYSTEM \"");
+ static const char systemString[] = " SYSTEM \"";
+ result.append(systemString, sizeof(systemString) - 1);
result.append(n->systemId());
- result.append("\"");
+ result.append('"');
}
if (!n->internalSubset().isEmpty()) {
- result.append(" [");
+ result.append(' ');
+ result.append('[');
result.append(n->internalSubset());
- result.append("]");
+ result.append(']');
}
- result.append(">");
+ result.append('>');
}
-void MarkupAccumulator::appendProcessingInstruction(StringBuilder& out, const String& target, const String& data)
+void MarkupAccumulator::appendProcessingInstruction(StringBuilder& result, const String& target, const String& data)
{
// FIXME: PI data is not escaped, but XMLSerializer (and possibly other callers) this should raise an exception if it includes "?>".
- out.append("<?");
- out.append(target);
- out.append(" ");
- out.append(data);
- out.append("?>");
+ result.append('<');
+ result.append('?');
+ result.append(target);
+ result.append(' ');
+ result.append(data);
+ result.append('?');
+ result.append('>');
}
-void MarkupAccumulator::appendElement(StringBuilder& out, Element* element, Namespaces* namespaces)
+void MarkupAccumulator::appendElement(StringBuilder& result, Element* element, Namespaces* namespaces)
{
- appendOpenTag(out, element, namespaces);
+ appendOpenTag(result, element, namespaces);
if (element->hasAttributes()) {
unsigned length = element->attributeCount();
for (unsigned int i = 0; i < length; i++)
- appendAttribute(out, element, *element->attributeItem(i), namespaces);
+ appendAttribute(result, element, *element->attributeItem(i), namespaces);
}
// Give an opportunity to subclasses to add their own attributes.
- appendCustomAttributes(out, element, namespaces);
+ appendCustomAttributes(result, element, namespaces);
- appendCloseTag(out, element);
+ appendCloseTag(result, element);
}
-void MarkupAccumulator::appendOpenTag(StringBuilder& out, Element* element, Namespaces* namespaces)
+void MarkupAccumulator::appendOpenTag(StringBuilder& result, Element* element, Namespaces* namespaces)
{
- out.append('<');
- out.append(element->nodeNamePreservingCase());
+ result.append('<');
+ result.append(element->nodeNamePreservingCase());
if (!element->document()->isHTMLDocument() && namespaces && shouldAddNamespaceElement(element))
- appendNamespace(out, element->prefix(), element->namespaceURI(), *namespaces);
+ appendNamespace(result, element->prefix(), element->namespaceURI(), *namespaces);
}
-void MarkupAccumulator::appendCloseTag(StringBuilder& out, Element* element)
+void MarkupAccumulator::appendCloseTag(StringBuilder& result, Element* element)
{
if (shouldSelfClose(element)) {
if (element->isHTMLElement())
- out.append(' '); // XHTML 1.0 <-> HTML compatibility.
- out.append('/');
+ result.append(' '); // XHTML 1.0 <-> HTML compatibility.
+ result.append('/');
}
- out.append('>');
+ result.append('>');
}
-void MarkupAccumulator::appendAttribute(StringBuilder& out, Element* element, const Attribute& attribute, Namespaces* namespaces)
+void MarkupAccumulator::appendAttribute(StringBuilder& result, Element* element, const Attribute& attribute, Namespaces* namespaces)
{
bool documentIsHTML = element->document()->isHTMLDocument();
- out.append(' ');
+ result.append(' ');
if (documentIsHTML)
- out.append(attribute.name().localName());
+ result.append(attribute.name().localName());
else
- out.append(attribute.name().toString());
+ result.append(attribute.name().toString());
- out.append('=');
+ result.append('=');
if (element->isURLAttribute(const_cast<Attribute*>(&attribute)))
- appendQuotedURLAttributeValue(out, element, attribute);
+ appendQuotedURLAttributeValue(result, element, attribute);
else {
- out.append('\"');
- appendAttributeValue(out, attribute.value(), documentIsHTML);
- out.append('\"');
+ result.append('"');
+ appendAttributeValue(result, attribute.value(), documentIsHTML);
+ result.append('"');
}
if (!documentIsHTML && namespaces && shouldAddNamespaceAttribute(attribute, *namespaces))
- appendNamespace(out, attribute.prefix(), attribute.namespaceURI(), *namespaces);
+ appendNamespace(result, attribute.prefix(), attribute.namespaceURI(), *namespaces);
}
-void MarkupAccumulator::appendCDATASection(StringBuilder& out, const String& section)
+void MarkupAccumulator::appendCDATASection(StringBuilder& result, const String& section)
{
// FIXME: CDATA content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "]]>".
- out.append("<![CDATA[");
- out.append(section);
- out.append("]]>");
+ static const char cdataBegin[] = "<![CDATA[";
+ result.append(cdataBegin, sizeof(cdataBegin) - 1);
+ result.append(section);
+ static const char cdataEnd[] = "]]>";
+ result.append(cdataEnd, sizeof(cdataEnd) - 1);
}
void MarkupAccumulator::appendStartMarkup(StringBuilder& result, const Node* node, Namespaces* namespaces)
@@ -425,6 +454,8 @@ void MarkupAccumulator::appendStartMarkup(StringBuilder& result, const Node* nod
appendComment(result, static_cast<const Comment*>(node)->data());
break;
case Node::DOCUMENT_NODE:
+ appendXMLDeclaration(result, static_cast<const Document*>(node));
+ break;
case Node::DOCUMENT_FRAGMENT_NODE:
break;
case Node::DOCUMENT_TYPE_NODE:
@@ -469,7 +500,7 @@ bool MarkupAccumulator::elementCannotHaveEndTag(const Node* node)
{
if (!node->isHTMLElement())
return false;
-
+
// FIXME: ieForbidsInsertHTML may not be the right function to call here
// ieForbidsInsertHTML is used to disallow setting innerHTML/outerHTML
// or createContextualFragment. It does not necessarily align with
diff --git a/Source/WebCore/editing/MarkupAccumulator.h b/Source/WebCore/editing/MarkupAccumulator.h
index a31f91285..367b79fa6 100644
--- a/Source/WebCore/editing/MarkupAccumulator.h
+++ b/Source/WebCore/editing/MarkupAccumulator.h
@@ -30,6 +30,7 @@
#include "markup.h"
#include <wtf/HashMap.h>
#include <wtf/Vector.h>
+#include <wtf/text/StringBuilder.h>
namespace WebCore {
@@ -66,54 +67,55 @@ struct EntityDescription {
// FIXME: Noncopyable?
class MarkupAccumulator {
public:
- MarkupAccumulator(Vector<Node*>*, EAbsoluteURLs resolveUrlsMethod, const Range* = 0);
+ MarkupAccumulator(Vector<Node*>*, EAbsoluteURLs, const Range* = 0);
virtual ~MarkupAccumulator();
- String serializeNodes(Node* node, Node* nodeToSkip, EChildrenOnly childrenOnly);
+ String serializeNodes(Node* targetNode, Node* nodeToSkip, EChildrenOnly);
- static void appendComment(StringBuilder& out, const String& comment);
+ static void appendComment(StringBuilder&, const String&);
protected:
virtual void appendString(const String&);
void appendStartTag(Node*, Namespaces* = 0);
virtual void appendEndTag(Node*);
static size_t totalLength(const Vector<String>&);
- size_t length() const { return totalLength(m_succeedingMarkup); }
- void concatenateMarkup(StringBuilder& out);
- void appendAttributeValue(StringBuilder& result, const String& attribute, bool documentIsHTML);
+ size_t length() const { return m_markup.length(); }
+ void concatenateMarkup(StringBuilder&);
+ void appendAttributeValue(StringBuilder&, const String&, bool);
virtual void appendCustomAttributes(StringBuilder&, Element*, Namespaces*);
- void appendNodeValue(StringBuilder& out, const Node*, const Range*, EntityMask);
+ void appendNodeValue(StringBuilder&, const Node*, const Range*, EntityMask);
bool shouldAddNamespaceElement(const Element*);
bool shouldAddNamespaceAttribute(const Attribute&, Namespaces&);
- void appendNamespace(StringBuilder& result, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces&);
+ void appendNamespace(StringBuilder&, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces&);
EntityMask entityMaskForText(Text*) const;
- virtual void appendText(StringBuilder& out, Text*);
- void appendDocumentType(StringBuilder& result, const DocumentType*);
- void appendProcessingInstruction(StringBuilder& out, const String& target, const String& data);
- virtual void appendElement(StringBuilder& out, Element*, Namespaces*);
- void appendOpenTag(StringBuilder& out, Element*, Namespaces*);
- void appendCloseTag(StringBuilder& out, Element*);
- void appendAttribute(StringBuilder& out, Element*, const Attribute&, Namespaces*);
- void appendCDATASection(StringBuilder& out, const String& section);
- void appendStartMarkup(StringBuilder& result, const Node*, Namespaces*);
+ virtual void appendText(StringBuilder&, Text*);
+ void appendXMLDeclaration(StringBuilder&, const Document*);
+ void appendDocumentType(StringBuilder&, const DocumentType*);
+ void appendProcessingInstruction(StringBuilder&, const String& target, const String& data);
+ virtual void appendElement(StringBuilder&, Element*, Namespaces*);
+ void appendOpenTag(StringBuilder&, Element*, Namespaces*);
+ void appendCloseTag(StringBuilder&, Element*);
+ void appendAttribute(StringBuilder&, Element*, const Attribute&, Namespaces*);
+ void appendCDATASection(StringBuilder&, const String&);
+ void appendStartMarkup(StringBuilder&, const Node*, Namespaces*);
bool shouldSelfClose(const Node*);
- bool elementCannotHaveEndTag(const Node* node);
- void appendEndMarkup(StringBuilder& result, const Node*);
+ bool elementCannotHaveEndTag(const Node*);
+ void appendEndMarkup(StringBuilder&, const Node*);
Vector<Node*>* const m_nodes;
const Range* const m_range;
private:
- String resolveURLIfNeeded(const Element*, const String& urlString) const;
- void appendQuotedURLAttributeValue(StringBuilder& result, const Element*, const Attribute&);
- void serializeNodesWithNamespaces(Node*, Node* nodeToSkip, EChildrenOnly, const Namespaces*);
+ String resolveURLIfNeeded(const Element*, const String&) const;
+ void appendQuotedURLAttributeValue(StringBuilder&, const Element*, const Attribute&);
+ void serializeNodesWithNamespaces(Node* targetNode, Node* nodeToSkip, EChildrenOnly, const Namespaces*);
- Vector<String> m_succeedingMarkup;
+ StringBuilder m_markup;
const EAbsoluteURLs m_resolveURLsMethod;
};
// FIXME: This method should be integrated with MarkupAccumulator.
-void appendCharactersReplacingEntities(StringBuilder& out, const UChar* content, size_t length, EntityMask);
+void appendCharactersReplacingEntities(StringBuilder&, const UChar*, size_t, EntityMask);
}
diff --git a/Source/WebCore/editing/RenderedPosition.cpp b/Source/WebCore/editing/RenderedPosition.cpp
index 8c900232a..7490d71df 100644
--- a/Source/WebCore/editing/RenderedPosition.cpp
+++ b/Source/WebCore/editing/RenderedPosition.cpp
@@ -224,13 +224,13 @@ Position RenderedPosition::positionAtRightBoundaryOfBiDiRun() const
return createLegacyEditingPosition(prevLeafChild()->renderer()->node(), prevLeafChild()->caretRightmostOffset());
}
-LayoutRect RenderedPosition::absoluteRect(LayoutUnit* extraWidthToEndOfLine) const
+IntRect RenderedPosition::absoluteRect(LayoutUnit* extraWidthToEndOfLine) const
{
if (isNull())
- return LayoutRect();
+ return IntRect();
- LayoutRect localRect = m_renderer->localCaretRect(m_inlineBox, m_offset, extraWidthToEndOfLine);
- return localRect == LayoutRect() ? LayoutRect() : m_renderer->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
+ IntRect localRect = pixelSnappedIntRect(m_renderer->localCaretRect(m_inlineBox, m_offset, extraWidthToEndOfLine));
+ return localRect == IntRect() ? IntRect() : m_renderer->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
}
bool renderObjectContainsPosition(RenderObject* target, const Position& position)
diff --git a/Source/WebCore/editing/RenderedPosition.h b/Source/WebCore/editing/RenderedPosition.h
index eef01a1f4..721344a15 100644
--- a/Source/WebCore/editing/RenderedPosition.h
+++ b/Source/WebCore/editing/RenderedPosition.h
@@ -67,8 +67,7 @@ public:
Position positionAtLeftBoundaryOfBiDiRun() const;
Position positionAtRightBoundaryOfBiDiRun() const;
- LayoutRect absoluteRect() const { return absoluteRect(0); }
- LayoutRect absoluteRect(LayoutUnit& extraWidthToEndOfLine) const { return absoluteRect(&extraWidthToEndOfLine); }
+ IntRect absoluteRect(LayoutUnit* extraWidthToEndOfLine = 0) const;
private:
bool operator==(const RenderedPosition&) const { return false; }
@@ -81,8 +80,6 @@ private:
bool atLeftBoundaryOfBidiRun(ShouldMatchBidiLevel, unsigned char bidiLevelOfRun) const;
bool atRightBoundaryOfBidiRun(ShouldMatchBidiLevel, unsigned char bidiLevelOfRun) const;
- LayoutRect absoluteRect(LayoutUnit* extraWidthToEndOfLine) const;
-
RenderObject* m_renderer;
InlineBox* m_inlineBox;
int m_offset;
diff --git a/Source/WebCore/editing/ReplaceSelectionCommand.cpp b/Source/WebCore/editing/ReplaceSelectionCommand.cpp
index af5a8be13..2c699fe67 100644
--- a/Source/WebCore/editing/ReplaceSelectionCommand.cpp
+++ b/Source/WebCore/editing/ReplaceSelectionCommand.cpp
@@ -49,6 +49,7 @@
#include "RenderInline.h"
#include "RenderObject.h"
#include "RenderText.h"
+#include "SimplifyMarkupCommand.h"
#include "SmartReplace.h"
#include "StylePropertySet.h"
#include "TextIterator.h"
@@ -60,8 +61,6 @@
namespace WebCore {
-typedef Vector<RefPtr<Node> > NodeVector;
-
using namespace HTMLNames;
enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment };
@@ -123,9 +122,16 @@ static Position positionAvoidingPrecedingNodes(Position pos)
// same. E.g.,
// <div>foo^</div>^
// The two positions above are the same visual position, but we want to stay in the same block.
- Node* stopNode = pos.deprecatedNode()->enclosingBlockFlowElement();
- while (stopNode != pos.deprecatedNode() && VisiblePosition(pos) == VisiblePosition(pos.next()))
- pos = pos.next();
+ Node* enclosingBlockNode = enclosingBlock(pos.containerNode());
+ for (Position nextPosition = pos; nextPosition.containerNode() != enclosingBlockNode; pos = nextPosition) {
+ if (pos.containerNode()->nonShadowBoundaryParentNode())
+ nextPosition = positionInParentAfterNode(pos.containerNode());
+
+ if (nextPosition == pos
+ || enclosingBlock(nextPosition.containerNode()) != enclosingBlockNode
+ || VisiblePosition(pos) != VisiblePosition(nextPosition))
+ break;
+ }
return pos;
}
@@ -438,11 +444,12 @@ 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);
+ return a->hasTagName(h1Tag)
+ || a->hasTagName(h2Tag)
+ || a->hasTagName(h3Tag)
+ || a->hasTagName(h4Tag)
+ || a->hasTagName(h5Tag)
+ || a->hasTagName(h6Tag);
}
static bool haveSameTagName(Node* a, Node* b)
@@ -545,54 +552,6 @@ void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline(Insert
}
}
-void ReplaceSelectionCommand::removeRedundantMarkup(InsertedNodes& insertedNodes)
-{
- Node* pastEndNode = insertedNodes.pastLastLeaf();
- Node* rootNode = insertedNodes.firstNodeInserted()->parentNode();
- Vector<Node*> nodesToRemove;
-
- // Walk through the inserted nodes, to see if there are elements that could be removed
- // without affecting the style. The goal is to produce leaner markup even when starting
- // from a verbose fragment.
- // We look at inline elements as well as non top level divs that don't have attributes.
- for (Node* node = insertedNodes.firstNodeInserted(); node && node != pastEndNode; node = node->traverseNextNode()) {
- if (node->firstChild() || (node->isTextNode() && node->nextSibling()))
- continue;
-
- Node* startingNode = node->parentNode();
- RenderStyle* startingStyle = startingNode->renderStyle();
- if (!startingStyle)
- continue;
- Node* currentNode = startingNode;
- Node* topNodeWithStartingStyle = 0;
- while (currentNode != rootNode) {
- if (currentNode->parentNode() != rootNode && isRemovableBlock(currentNode))
- nodesToRemove.append(currentNode);
-
- currentNode = currentNode->parentNode();
- if (!currentNode->renderer() || !currentNode->renderer()->isRenderInline() || toRenderInline(currentNode->renderer())->alwaysCreateLineBoxes())
- continue;
-
- if (currentNode && currentNode->firstChild() != currentNode->lastChild()) {
- topNodeWithStartingStyle = 0;
- break;
- }
-
- unsigned context;
- if (currentNode->renderStyle()->diff(startingStyle, context) == StyleDifferenceEqual)
- topNodeWithStartingStyle = currentNode;
-
- }
- if (topNodeWithStartingStyle) {
- for (Node* node = startingNode; node != topNodeWithStartingStyle; node = node->parentNode())
- nodesToRemove.append(node);
- }
- }
- // we perform all the DOM mutations at once.
- for (size_t i = 0; i < nodesToRemove.size(); ++i)
- removeNodePreservingChildren(nodesToRemove[i]);
-}
-
static inline bool nodeHasVisibleRenderText(Text* text)
{
return text->renderer() && toRenderText(text->renderer())->renderedTextLength() > 0;
@@ -930,7 +889,7 @@ void ReplaceSelectionCommand::doApply()
// Adjust insertionPos to prevent nesting.
// If the start was in a Mail blockquote, we will have already handled adjusting insertionPos above.
- if (m_preventNesting && startBlock && !startIsInsideMailBlockquote) {
+ if (m_preventNesting && startBlock && !isTableCell(startBlock) && !startIsInsideMailBlockquote) {
ASSERT(startBlock != currentRoot);
VisiblePosition visibleInsertionPos(insertionPos);
if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInsertionPos) && fragment.hasInterchangeNewlineAtEnd()))
@@ -959,6 +918,10 @@ void ReplaceSelectionCommand::doApply()
bool handledStyleSpans = handleStyleSpansBeforeInsertion(fragment, insertionPos);
+ // We're finished if there is nothing to add.
+ if (fragment.isEmpty() || !fragment.firstChild())
+ return;
+
// If we are not trying to match the destination style we prefer a position
// that is outside inline elements that provide style.
// This way we can produce a less verbose markup.
@@ -981,11 +944,7 @@ void ReplaceSelectionCommand::doApply()
// FIXME: When pasting rich content we're often prevented from heading down the fast path by style spans. Try
// again here if they've been removed.
-
- // We're finished if there is nothing to add.
- if (fragment.isEmpty() || !fragment.firstChild())
- return;
-
+
// 1) Insert the content.
// 2) Remove redundant styles and style tags, this inner <b> for example: <b>foo <b>bar</b> baz</b>.
// 3) Merge the start of the added content with the content before the position being pasted into.
@@ -1062,7 +1021,7 @@ void ReplaceSelectionCommand::doApply()
removeRedundantStylesAndKeepStyleSpanInline(insertedNodes);
if (m_sanitizeFragment)
- removeRedundantMarkup(insertedNodes);
+ applyCommandToComposite(SimplifyMarkupCommand::create(document(), insertedNodes.firstNodeInserted(), insertedNodes.pastLastLeaf()));
// Setup m_startOfInsertedContent and m_endOfInsertedContent. This should be the last two lines of code that access insertedNodes.
m_startOfInsertedContent = firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted());
@@ -1118,10 +1077,12 @@ void ReplaceSelectionCommand::doApply()
RefPtr<Node> newListItem = createListItemElement(document());
insertNodeAfter(newListItem, enclosingNode);
setEndingSelection(VisiblePosition(firstPositionInNode(newListItem.get())));
- } else
+ } else {
// Use a default paragraph element (a plain div) for the empty paragraph, using the last paragraph
// block's style seems to annoy users.
- insertParagraphSeparator(true);
+ insertParagraphSeparator(true, !startIsInsideMailBlockquote && highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(),
+ isMailBlockquote, CannotCrossEditingBoundary, insertedNodes.firstNodeInserted()->parentNode()));
+ }
// Select up to the paragraph separator that was added.
lastPositionToSelect = endingSelection().visibleStart().deepEquivalent();
diff --git a/Source/WebCore/editing/ReplaceSelectionCommand.h b/Source/WebCore/editing/ReplaceSelectionCommand.h
index da053b834..3a0b5a516 100644
--- a/Source/WebCore/editing/ReplaceSelectionCommand.h
+++ b/Source/WebCore/editing/ReplaceSelectionCommand.h
@@ -68,7 +68,7 @@ private:
Node* firstNodeInserted() const { return m_firstNodeInserted.get(); }
Node* lastLeafInserted() const { return m_lastNodeInserted->lastDescendant(); }
- Node* pastLastLeaf() const { return m_firstNodeInserted ? lastLeafInserted()->traverseNextNode() : 0; }
+ Node* pastLastLeaf() const { return m_lastNodeInserted ? lastLeafInserted()->traverseNextNode() : 0; }
private:
RefPtr<Node> m_firstNodeInserted;
@@ -89,7 +89,6 @@ private:
void removeUnrenderedTextNodesAtEnds(InsertedNodes&);
void removeRedundantStylesAndKeepStyleSpanInline(InsertedNodes&);
- void removeRedundantMarkup(InsertedNodes&);
void handleStyleSpans(InsertedNodes&);
void handlePasteAsQuotationNode();
diff --git a/Source/WebCore/editing/SimplifyMarkupCommand.cpp b/Source/WebCore/editing/SimplifyMarkupCommand.cpp
new file mode 100644
index 000000000..ebe386e67
--- /dev/null
+++ b/Source/WebCore/editing/SimplifyMarkupCommand.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SimplifyMarkupCommand.h"
+
+#include "NodeRenderStyle.h"
+#include "RenderInline.h"
+#include "RenderObject.h"
+#include "RenderStyle.h"
+
+namespace WebCore {
+
+SimplifyMarkupCommand::SimplifyMarkupCommand(Document* document, Node* firstNode, Node* nodeAfterLast)
+ : CompositeEditCommand(document), m_firstNode(firstNode), m_nodeAfterLast(nodeAfterLast)
+{
+}
+
+void SimplifyMarkupCommand::doApply()
+{
+ Node* rootNode = m_firstNode->parentNode();
+ Vector<Node*> nodesToRemove;
+
+ // Walk through the inserted nodes, to see if there are elements that could be removed
+ // without affecting the style. The goal is to produce leaner markup even when starting
+ // from a verbose fragment.
+ // We look at inline elements as well as non top level divs that don't have attributes.
+ for (Node* node = m_firstNode.get(); node && node != m_nodeAfterLast; node = node->traverseNextNode()) {
+ if (node->firstChild() || (node->isTextNode() && node->nextSibling()))
+ continue;
+
+ Node* startingNode = node->parentNode();
+ RenderStyle* startingStyle = startingNode->renderStyle();
+ if (!startingStyle)
+ continue;
+ Node* currentNode = startingNode;
+ Node* topNodeWithStartingStyle = 0;
+ while (currentNode != rootNode) {
+ if (currentNode->parentNode() != rootNode && isRemovableBlock(currentNode))
+ nodesToRemove.append(currentNode);
+
+ currentNode = currentNode->parentNode();
+ if (!currentNode)
+ break;
+
+ if (!currentNode->renderer() || !currentNode->renderer()->isRenderInline() || toRenderInline(currentNode->renderer())->alwaysCreateLineBoxes())
+ continue;
+
+ if (currentNode->firstChild() != currentNode->lastChild()) {
+ topNodeWithStartingStyle = 0;
+ break;
+ }
+
+ unsigned context;
+ if (currentNode->renderStyle()->diff(startingStyle, context) == StyleDifferenceEqual)
+ topNodeWithStartingStyle = currentNode;
+
+ }
+ if (topNodeWithStartingStyle) {
+ for (Node* node = startingNode; node != topNodeWithStartingStyle; node = node->parentNode())
+ nodesToRemove.append(node);
+ }
+ }
+ // we perform all the DOM mutations at once.
+ for (size_t i = 0; i < nodesToRemove.size(); ++i)
+ removeNodePreservingChildren(nodesToRemove[i]);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/editing/SimplifyMarkupCommand.h b/Source/WebCore/editing/SimplifyMarkupCommand.h
new file mode 100644
index 000000000..21ff1adc4
--- /dev/null
+++ b/Source/WebCore/editing/SimplifyMarkupCommand.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SimplifyMarkupCommand_h
+#define SimplifyMarkupCommand_h
+
+#include "CompositeEditCommand.h"
+
+namespace WebCore {
+
+class SimplifyMarkupCommand : public CompositeEditCommand {
+public:
+ static PassRefPtr<SimplifyMarkupCommand> create(Document* document, Node* firstNode, Node* nodeAfterLast)
+ {
+ return adoptRef(new SimplifyMarkupCommand(document, firstNode, nodeAfterLast));
+ }
+
+private:
+ SimplifyMarkupCommand(Document*, Node* firstNode, Node* nodeAfterLast);
+
+ virtual void doApply();
+
+ RefPtr<Node> m_firstNode;
+ RefPtr<Node> m_nodeAfterLast;
+};
+
+} // namespace WebCore
+
+#endif // SimplifyMarkupCommand_h
diff --git a/Source/WebCore/editing/SpellChecker.cpp b/Source/WebCore/editing/SpellChecker.cpp
index e1243b14d..8107fdbaa 100644
--- a/Source/WebCore/editing/SpellChecker.cpp
+++ b/Source/WebCore/editing/SpellChecker.cpp
@@ -118,7 +118,12 @@ bool SpellChecker::canCheckAsynchronously(Range* range) const
bool SpellChecker::isCheckable(Range* range) const
{
- return range && range->firstNode() && range->firstNode()->renderer();
+ if (!range || !range->firstNode() || !range->firstNode()->renderer())
+ return false;
+ const Node* node = range->startContainer();
+ if (node && node->isElementNode() && !toElement(node)->isSpellCheckingEnabled())
+ return false;
+ return true;
}
void SpellChecker::requestCheckingFor(PassRefPtr<SpellCheckRequest> request)
@@ -183,5 +188,24 @@ void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& resu
m_timerToProcessQueuedRequest.startOneShot(0);
}
+void SpellChecker::didCheckSucceeded(int sequence, const Vector<TextCheckingResult>& results)
+{
+ if (m_processingRequest->sequence() == sequence) {
+ unsigned markers = 0;
+ if (m_processingRequest->mask() & TextCheckingTypeSpelling)
+ markers |= DocumentMarker::Spelling;
+ if (m_processingRequest->mask() & TextCheckingTypeGrammar)
+ markers |= DocumentMarker::Grammar;
+ if (markers)
+ m_frame->document()->markers()->removeMarkers(m_processingRequest->checkingRange().get(), markers);
+ }
+ didCheck(sequence, results);
+}
+
+void SpellChecker::didCheckCanceled(int sequence)
+{
+ Vector<TextCheckingResult> results;
+ didCheck(sequence, results);
+}
} // namespace WebCore
diff --git a/Source/WebCore/editing/SpellChecker.h b/Source/WebCore/editing/SpellChecker.h
index 07f51e431..835a57c55 100644
--- a/Source/WebCore/editing/SpellChecker.h
+++ b/Source/WebCore/editing/SpellChecker.h
@@ -81,7 +81,8 @@ public:
bool isCheckable(Range*) const;
void requestCheckingFor(PassRefPtr<SpellCheckRequest>);
- void didCheck(int sequence, const Vector<TextCheckingResult>&);
+ void didCheckSucceeded(int sequence, const Vector<TextCheckingResult>&);
+ void didCheckCanceled(int sequence);
int lastRequestSequence() const
{
@@ -101,6 +102,7 @@ private:
void timerFiredToProcessQueuedRequest(Timer<SpellChecker>*);
void invokeRequest(PassRefPtr<SpellCheckRequest>);
void enqueueRequest(PassRefPtr<SpellCheckRequest>);
+ void didCheck(int sequence, const Vector<TextCheckingResult>&);
Frame* m_frame;
int m_lastRequestSequence;
diff --git a/Source/WebCore/editing/SpellingCorrectionCommand.cpp b/Source/WebCore/editing/SpellingCorrectionCommand.cpp
index 3cf2d732c..225cf9351 100644
--- a/Source/WebCore/editing/SpellingCorrectionCommand.cpp
+++ b/Source/WebCore/editing/SpellingCorrectionCommand.cpp
@@ -26,12 +26,12 @@
#include "config.h"
#include "SpellingCorrectionCommand.h"
+#include "AlternativeTextController.h"
#include "Document.h"
#include "DocumentFragment.h"
#include "Frame.h"
#include "ReplaceSelectionCommand.h"
#include "SetSelectionCommand.h"
-#include "SpellingCorrectionController.h"
#include "TextIterator.h"
#include "markup.h"
diff --git a/Source/WebCore/editing/SurroundingText.cpp b/Source/WebCore/editing/SurroundingText.cpp
new file mode 100644
index 000000000..df9aa7e80
--- /dev/null
+++ b/Source/WebCore/editing/SurroundingText.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "SurroundingText.h"
+
+#include "Document.h"
+#include "Range.h"
+#include "TextIterator.h"
+#include "VisiblePosition.h"
+#include "VisibleSelection.h"
+#include "visible_units.h"
+
+namespace WebCore {
+
+SurroundingText::SurroundingText(const VisiblePosition& visiblePosition, unsigned maxLength)
+ : m_positionOffsetInContent(0)
+{
+ const unsigned halfMaxLength = maxLength / 2;
+ CharacterIterator forwardIterator(makeRange(visiblePosition, endOfDocument(visiblePosition)).get(), TextIteratorStopsOnFormControls);
+ forwardIterator.advance(maxLength - halfMaxLength);
+
+ Position position = visiblePosition.deepEquivalent().parentAnchoredEquivalent();
+ Document* document = position.document();
+ if (!Range::create(document, position, forwardIterator.range()->startPosition())->text().length())
+ return;
+
+ BackwardsCharacterIterator backwardsIterator(makeRange(startOfDocument(visiblePosition), visiblePosition).get(), TextIteratorStopsOnFormControls);
+ backwardsIterator.advance(halfMaxLength);
+
+ m_positionOffsetInContent = Range::create(document, backwardsIterator.range()->endPosition(), position)->text().length();
+ m_contentRange = Range::create(document, backwardsIterator.range()->endPosition(), forwardIterator.range()->startPosition());
+}
+
+PassRefPtr<Range> SurroundingText::rangeFromContentOffsets(unsigned startOffsetInContent, unsigned endOffsetInContent)
+{
+ if (startOffsetInContent >= endOffsetInContent || endOffsetInContent > content().length())
+ return 0;
+
+ CharacterIterator iterator(m_contentRange.get());
+ iterator.advance(startOffsetInContent);
+
+ Position start = iterator.range()->startPosition();
+ iterator.advance(endOffsetInContent - startOffsetInContent);
+ Position end = iterator.range()->startPosition();
+ return Range::create(start.document(), start, end);
+}
+
+String SurroundingText::content() const
+{
+ if (m_contentRange)
+ return m_contentRange->text();
+ return String();
+}
+
+unsigned SurroundingText::positionOffsetInContent() const
+{
+ return m_positionOffsetInContent;
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/editing/SurroundingText.h b/Source/WebCore/editing/SurroundingText.h
new file mode 100644
index 000000000..6069875a1
--- /dev/null
+++ b/Source/WebCore/editing/SurroundingText.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SurroundingText_h
+#define SurroundingText_h
+
+#include "PlatformString.h"
+
+namespace WebCore {
+
+class Range;
+class VisiblePosition;
+
+class SurroundingText {
+ WTF_MAKE_NONCOPYABLE(SurroundingText);
+public:
+ SurroundingText(const VisiblePosition&, unsigned maxLength);
+
+ String content() const;
+ unsigned positionOffsetInContent() const;
+
+ PassRefPtr<Range> rangeFromContentOffsets(unsigned startOffsetInContent, unsigned endOffsetInContent);
+
+private:
+ RefPtr<Range> m_contentRange;
+ size_t m_positionOffsetInContent;
+};
+
+} // namespace WebCore
+
+#endif // SurroundingText_h
+
diff --git a/Source/WebCore/editing/TextCheckingHelper.h b/Source/WebCore/editing/TextCheckingHelper.h
index 47a61b360..4887ed5c1 100644
--- a/Source/WebCore/editing/TextCheckingHelper.h
+++ b/Source/WebCore/editing/TextCheckingHelper.h
@@ -22,11 +22,15 @@
#define TextCheckingHelper_h
#include "EditorClient.h"
+#include "ExceptionCode.h"
+#include "TextChecking.h"
+#include <wtf/text/WTFString.h>
namespace WebCore {
class Range;
class Position;
+struct TextCheckingResult;
class TextCheckingParagraph {
public:
diff --git a/Source/WebCore/editing/TextInsertionBaseCommand.cpp b/Source/WebCore/editing/TextInsertionBaseCommand.cpp
new file mode 100644
index 000000000..e35494770
--- /dev/null
+++ b/Source/WebCore/editing/TextInsertionBaseCommand.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "TextInsertionBaseCommand.h"
+
+#include "BeforeTextInsertedEvent.h"
+#include "Document.h"
+#include "Element.h"
+#include "Frame.h"
+#include "Node.h"
+
+namespace WebCore {
+
+TextInsertionBaseCommand::TextInsertionBaseCommand(Document* document)
+ : CompositeEditCommand(document)
+{
+}
+
+void TextInsertionBaseCommand::applyTextInsertionCommand(Frame* frame, PassRefPtr<TextInsertionBaseCommand> command, const VisibleSelection& selectionForInsertion, const VisibleSelection& endingSelection)
+{
+ bool changeSelection = selectionForInsertion != endingSelection;
+ if (changeSelection) {
+ command->setStartingSelection(selectionForInsertion);
+ command->setEndingSelection(selectionForInsertion);
+ }
+ applyCommand(command);
+ if (changeSelection) {
+ command->setEndingSelection(endingSelection);
+ frame->selection()->setSelection(endingSelection);
+ }
+}
+
+String dispatchBeforeTextInsertedEvent(const String& text, const VisibleSelection& selectionForInsertion, bool insertionIsForUpdatingComposition)
+{
+ if (insertionIsForUpdatingComposition)
+ return text;
+
+ String newText = text;
+ if (Node* startNode = selectionForInsertion.start().containerNode()) {
+ if (startNode->rootEditableElement()) {
+ // Send BeforeTextInsertedEvent. The event handler will update text if necessary.
+ ExceptionCode ec = 0;
+ RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
+ startNode->rootEditableElement()->dispatchEvent(evt, ec);
+ newText = evt->text();
+ }
+ }
+ return newText;
+}
+
+bool canAppendNewLineFeedToSelection(const VisibleSelection& selection)
+{
+ Node* node = selection.rootEditableElement();
+ if (!node)
+ return false;
+
+ RefPtr<BeforeTextInsertedEvent> event = BeforeTextInsertedEvent::create(String("\n"));
+ ExceptionCode ec = 0;
+ node->dispatchEvent(event, ec);
+ return event->text().length();
+}
+
+}
diff --git a/Source/WebCore/editing/TextInsertionBaseCommand.h b/Source/WebCore/editing/TextInsertionBaseCommand.h
new file mode 100644
index 000000000..9dff6dc4e
--- /dev/null
+++ b/Source/WebCore/editing/TextInsertionBaseCommand.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TextInsertionBaseCommand_h
+#define TextInsertionBaseCommand_h
+
+#include "CompositeEditCommand.h"
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class Document;
+class VisibleSelection;
+
+class TextInsertionBaseCommand : public CompositeEditCommand {
+public:
+ virtual ~TextInsertionBaseCommand() { };
+
+protected:
+ TextInsertionBaseCommand(Document*);
+ static void applyTextInsertionCommand(Frame*, PassRefPtr<TextInsertionBaseCommand>, const VisibleSelection& selectionForInsertion, const VisibleSelection& endingSelection);
+};
+
+String dispatchBeforeTextInsertedEvent(const String& text, const VisibleSelection& selectionForInsertion, bool insertionIsForUpdatingComposition);
+bool canAppendNewLineFeedToSelection(const VisibleSelection&);
+
+// LineOperation should define member function "opeartor (size_t lineOffset, size_t lineLength, bool isLastLine)".
+// lienLength doesn't include the newline character. So the value of lineLength could be 0.
+template <class LineOperation>
+void forEachLineInString(const String& string, const LineOperation& operation)
+{
+ unsigned offset = 0;
+ size_t newline;
+ while ((newline = string.find('\n', offset)) != notFound) {
+ operation(offset, newline - offset, false);
+ offset = newline + 1;
+ }
+ if (!offset)
+ operation(0, string.length(), true);
+ else {
+ unsigned length = string.length();
+ if (length != offset)
+ operation(offset, length - offset, true);
+ }
+}
+
+}
+
+#endif
diff --git a/Source/WebCore/editing/TextIterator.cpp b/Source/WebCore/editing/TextIterator.cpp
index e0bf8c3a0..85b559514 100644
--- a/Source/WebCore/editing/TextIterator.cpp
+++ b/Source/WebCore/editing/TextIterator.cpp
@@ -43,6 +43,7 @@
#include "TextBreakIterator.h"
#include "VisiblePosition.h"
#include "visible_units.h"
+#include <wtf/text/CString.h>
#include <wtf/unicode/CharacterNames.h>
#if USE(ICU_UNICODE) && !UCONFIG_NO_COLLATION
@@ -261,6 +262,8 @@ TextIterator::TextIterator()
, m_handledFirstLetter(false)
, m_ignoresStyleVisibility(false)
, m_emitsObjectReplacementCharacters(false)
+ , m_stopsOnFormControls(false)
+ , m_shouldStop(false)
{
}
@@ -282,6 +285,8 @@ TextIterator::TextIterator(const Range* r, TextIteratorBehavior behavior)
, m_handledFirstLetter(false)
, m_ignoresStyleVisibility(behavior & TextIteratorIgnoresStyleVisibility)
, m_emitsObjectReplacementCharacters(behavior & TextIteratorEmitsObjectReplacementCharacters)
+ , m_stopsOnFormControls(behavior & TextIteratorStopsOnFormControls)
+ , m_shouldStop(false)
{
if (!r)
return;
@@ -341,6 +346,9 @@ TextIterator::~TextIterator()
void TextIterator::advance()
{
+ if (m_shouldStop)
+ return;
+
// reset the run information
m_positionNode = 0;
m_textLength = 0;
@@ -373,6 +381,9 @@ void TextIterator::advance()
}
while (m_node && m_node != m_pastEndNode) {
+ if (!m_shouldStop && m_stopsOnFormControls && HTMLFormControlElement::enclosingFormControlElement(m_node))
+ m_shouldStop = true;
+
// if the range ends at offset 0 of an element, represent the
// position, but not the content, of that element e.g. if the
// node is a blockflow element, emit a newline that
@@ -395,7 +406,9 @@ void TextIterator::advance()
else if (renderer && (renderer->isImage() || renderer->isWidget() ||
(renderer->node() && renderer->node()->isElementNode() &&
(static_cast<Element*>(renderer->node())->isFormControlElement()
- || static_cast<Element*>(renderer->node())->hasTagName(legendTag)))))
+ || static_cast<Element*>(renderer->node())->hasTagName(legendTag)
+ || static_cast<Element*>(renderer->node())->hasTagName(meterTag)
+ || static_cast<Element*>(renderer->node())->hasTagName(progressTag)))))
m_handledNode = handleReplacedElement();
else
m_handledNode = handleNonTextNode();
@@ -1071,6 +1084,8 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator()
, m_singleCharacterBuffer(0)
, m_havePassedStartNode(false)
, m_shouldHandleFirstLetter(false)
+ , m_stopsOnFormControls(false)
+ , m_shouldStop(false)
{
}
@@ -1094,8 +1109,10 @@ SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range* r,
, m_singleCharacterBuffer(0)
, m_havePassedStartNode(false)
, m_shouldHandleFirstLetter(false)
+ , m_stopsOnFormControls(behavior & TextIteratorStopsOnFormControls)
+ , m_shouldStop(false)
{
- ASSERT(m_behavior == TextIteratorDefaultBehavior);
+ ASSERT(m_behavior == TextIteratorDefaultBehavior || m_behavior == TextIteratorStopsOnFormControls);
if (!r)
return;
@@ -1148,6 +1165,14 @@ void SimplifiedBackwardsTextIterator::advance()
{
ASSERT(m_positionNode);
+ if (m_shouldStop)
+ return;
+
+ if (m_stopsOnFormControls && HTMLFormControlElement::enclosingFormControlElement(m_node)) {
+ m_shouldStop = true;
+ return;
+ }
+
m_positionNode = 0;
m_textLength = 0;
@@ -2511,9 +2536,8 @@ UChar* plainTextToMallocAllocatedBuffer(const Range* r, unsigned& bufferLength,
{
UChar* result = 0;
- // Do this in pieces to avoid massive reallocations if there is a large amount of text.
- // Use system malloc for buffers since they can consume lots of memory and current TCMalloc is unable return it back to OS.
- static const unsigned cMaxSegmentSize = 1 << 16;
+ // The initial buffer size can be critical for performance: https://bugs.webkit.org/show_bug.cgi?id=81192
+ static const unsigned cMaxSegmentSize = 1 << 15;
bufferLength = 0;
typedef pair<UChar*, unsigned> TextSegment;
OwnPtr<Vector<TextSegment> > textSegments;
diff --git a/Source/WebCore/editing/TextIterator.h b/Source/WebCore/editing/TextIterator.h
index 08ff095a6..bfcfae356 100644
--- a/Source/WebCore/editing/TextIterator.h
+++ b/Source/WebCore/editing/TextIterator.h
@@ -43,7 +43,8 @@ enum TextIteratorBehavior {
TextIteratorEmitsTextsWithoutTranscoding = 1 << 2,
TextIteratorIgnoresStyleVisibility = 1 << 3,
TextIteratorEmitsObjectReplacementCharacters = 1 << 4,
- TextIteratorEmitsOriginalText = 1 << 5
+ TextIteratorEmitsOriginalText = 1 << 5,
+ TextIteratorStopsOnFormControls = 1 << 6
};
// FIXME: Can't really answer this question correctly without knowing the white-space mode.
@@ -89,7 +90,7 @@ public:
~TextIterator();
explicit TextIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
- bool atEnd() const { return !m_positionNode; }
+ bool atEnd() const { return !m_positionNode || m_shouldStop; }
void advance();
int length() const { return m_textLength; }
@@ -185,6 +186,10 @@ private:
bool m_ignoresStyleVisibility;
// Used when emitting the special 0xFFFC character is required.
bool m_emitsObjectReplacementCharacters;
+ // Used when the iteration should stop if form controls are reached.
+ bool m_stopsOnFormControls;
+ // Used when m_stopsOnFormControls is set to determine if the iterator should keep advancing.
+ bool m_shouldStop;
};
// Iterates through the DOM range, returning all the text, and 0-length boundaries
@@ -195,7 +200,7 @@ public:
SimplifiedBackwardsTextIterator();
explicit SimplifiedBackwardsTextIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
- bool atEnd() const { return !m_positionNode; }
+ bool atEnd() const { return !m_positionNode || m_shouldStop; }
void advance();
int length() const { return m_textLength; }
@@ -247,6 +252,12 @@ private:
// Should handle first-letter renderer in the next call to handleTextNode.
bool m_shouldHandleFirstLetter;
+
+ // Used when the iteration should stop if form controls are reached.
+ bool m_stopsOnFormControls;
+
+ // Used when m_stopsOnFormControls is set to determine if the iterator should keep advancing.
+ bool m_shouldStop;
};
// Builds on the text iterator, adding a character position so we can walk one
diff --git a/Source/WebCore/editing/TypingCommand.cpp b/Source/WebCore/editing/TypingCommand.cpp
index 9ae395541..f3c42329f 100644
--- a/Source/WebCore/editing/TypingCommand.cpp
+++ b/Source/WebCore/editing/TypingCommand.cpp
@@ -26,7 +26,6 @@
#include "config.h"
#include "TypingCommand.h"
-#include "BeforeTextInsertedEvent.h"
#include "BreakBlockquoteCommand.h"
#include "DeleteSelectionCommand.h"
#include "Document.h"
@@ -48,20 +47,35 @@ namespace WebCore {
using namespace HTMLNames;
-static bool canAppendNewLineFeed(const VisibleSelection& selection)
+class TypingCommandLineOperation
{
- Node* node = selection.rootEditableElement();
- if (!node)
- return false;
-
- RefPtr<BeforeTextInsertedEvent> event = BeforeTextInsertedEvent::create(String("\n"));
- ExceptionCode ec = 0;
- node->dispatchEvent(event, ec);
- return event->text().length();
-}
+public:
+ TypingCommandLineOperation(TypingCommand* typingCommand, bool selectInsertedText, const String& text)
+ : m_typingCommand(typingCommand)
+ , m_selectInsertedText(selectInsertedText)
+ , m_text(text)
+ { }
+
+ void operator()(size_t lineOffset, size_t lineLength, bool isLastLine) const
+ {
+ if (isLastLine) {
+ if (!lineOffset || lineLength > 0)
+ m_typingCommand->insertTextRunWithoutNewlines(m_text.substring(lineOffset, lineLength), m_selectInsertedText);
+ } else {
+ if (lineLength > 0)
+ m_typingCommand->insertTextRunWithoutNewlines(m_text.substring(lineOffset, lineLength), false);
+ m_typingCommand->insertParagraphSeparator();
+ }
+ }
+
+private:
+ TypingCommand* m_typingCommand;
+ bool m_selectInsertedText;
+ const String& m_text;
+};
TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, Options options, TextGranularity granularity, TextCompositionType compositionType)
- : CompositeEditCommand(document)
+ : TextInsertionBaseCommand(document)
, m_commandType(commandType)
, m_textToInsert(textToInsert)
, m_openForMoreTyping(true)
@@ -161,17 +175,8 @@ void TypingCommand::insertText(Document* document, const String& text, const Vis
ASSERT(frame);
VisibleSelection currentSelection = frame->selection()->selection();
- bool changeSelection = currentSelection != selectionForInsertion;
- String newText = text;
- if (Node* startNode = selectionForInsertion.start().containerNode()) {
- if (startNode->rootEditableElement() && compositionType != TextCompositionUpdate) {
- // Send BeforeTextInsertedEvent. The event handler will update text if necessary.
- ExceptionCode ec = 0;
- RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
- startNode->rootEditableElement()->dispatchEvent(evt, ec);
- newText = evt->text();
- }
- }
+
+ String newText = dispatchBeforeTextInsertedEvent(text, selectionForInsertion, compositionType == TextCompositionUpdate);
// Set the starting and ending selection appropriately if we are using a selection
// that is different from the current selection. In the future, we should change EditCommand
@@ -190,15 +195,7 @@ void TypingCommand::insertText(Document* document, const String& text, const Vis
}
RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, options, compositionType);
- if (changeSelection) {
- cmd->setStartingSelection(selectionForInsertion);
- cmd->setEndingSelection(selectionForInsertion);
- }
- applyCommand(cmd);
- if (changeSelection) {
- cmd->setEndingSelection(currentSelection);
- frame->selection()->setSelection(currentSelection);
- }
+ applyTextInsertionCommand(frame.get(), cmd, selectionForInsertion, currentSelection);
}
void TypingCommand::insertLineBreak(Document *document, Options options)
@@ -327,7 +324,7 @@ void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
strippedPreviousWord = plainText(range.get()).stripWhiteSpace();
frame->editor()->markMisspellingsAfterTypingToWord(p1, endingSelection(), !strippedPreviousWord.isEmpty());
} else if (commandType == TypingCommand::InsertText)
- frame->editor()->startCorrectionPanelTimer();
+ frame->editor()->startAlternativeTextUITimer();
}
}
@@ -358,21 +355,8 @@ void TypingCommand::insertText(const String &text, bool selectInsertedText)
// an existing selection; at the moment they can either put the caret after what's inserted or
// select what's inserted, but there's no way to "extend selection" to include both an old selection
// that ends just before where we want to insert text and the newly inserted text.
- unsigned offset = 0;
- size_t newline;
- while ((newline = text.find('\n', offset)) != notFound) {
- if (newline != offset)
- insertTextRunWithoutNewlines(text.substring(offset, newline - offset), false);
- insertParagraphSeparator();
- offset = newline + 1;
- }
- if (!offset)
- insertTextRunWithoutNewlines(text, selectInsertedText);
- else {
- unsigned length = text.length();
- if (length != offset)
- insertTextRunWithoutNewlines(text.substring(offset, length - offset), selectInsertedText);
- }
+ TypingCommandLineOperation operation(this, selectInsertedText, text);
+ forEachLineInString(text, operation);
}
void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
@@ -387,7 +371,7 @@ void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool select
void TypingCommand::insertLineBreak()
{
- if (!canAppendNewLineFeed(endingSelection()))
+ if (!canAppendNewLineFeedToSelection(endingSelection()))
return;
applyCommandToComposite(InsertLineBreakCommand::create(document()));
@@ -396,7 +380,7 @@ void TypingCommand::insertLineBreak()
void TypingCommand::insertParagraphSeparator()
{
- if (!canAppendNewLineFeed(endingSelection()))
+ if (!canAppendNewLineFeedToSelection(endingSelection()))
return;
applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
diff --git a/Source/WebCore/editing/TypingCommand.h b/Source/WebCore/editing/TypingCommand.h
index 1f8c181df..950eec80a 100644
--- a/Source/WebCore/editing/TypingCommand.h
+++ b/Source/WebCore/editing/TypingCommand.h
@@ -26,11 +26,11 @@
#ifndef TypingCommand_h
#define TypingCommand_h
-#include "CompositeEditCommand.h"
+#include "TextInsertionBaseCommand.h"
namespace WebCore {
-class TypingCommand : public CompositeEditCommand {
+class TypingCommand : public TextInsertionBaseCommand {
public:
enum ETypingCommand {
DeleteSelection,
diff --git a/Source/WebCore/editing/VisiblePosition.cpp b/Source/WebCore/editing/VisiblePosition.cpp
index 81168d7b7..05666d34c 100644
--- a/Source/WebCore/editing/VisiblePosition.cpp
+++ b/Source/WebCore/editing/VisiblePosition.cpp
@@ -595,7 +595,7 @@ UChar32 VisiblePosition::characterAfter() const
return ch;
}
-IntRect VisiblePosition::localCaretRect(RenderObject*& renderer) const
+LayoutRect VisiblePosition::localCaretRect(RenderObject*& renderer) const
{
if (m_deepPosition.isNull()) {
renderer = 0;
@@ -605,7 +605,7 @@ IntRect VisiblePosition::localCaretRect(RenderObject*& renderer) const
renderer = node->renderer();
if (!renderer)
- return IntRect();
+ return LayoutRect();
InlineBox* inlineBox;
int caretOffset;
@@ -620,7 +620,7 @@ IntRect VisiblePosition::localCaretRect(RenderObject*& renderer) const
IntRect VisiblePosition::absoluteCaretBounds() const
{
RenderObject* renderer;
- IntRect localRect = localCaretRect(renderer);
+ LayoutRect localRect = localCaretRect(renderer);
if (localRect.isEmpty() || !renderer)
return IntRect();
@@ -630,7 +630,7 @@ IntRect VisiblePosition::absoluteCaretBounds() const
int VisiblePosition::lineDirectionPointForBlockDirectionNavigation() const
{
RenderObject* renderer;
- IntRect localRect = localCaretRect(renderer);
+ LayoutRect localRect = localCaretRect(renderer);
if (localRect.isEmpty() || !renderer)
return 0;
diff --git a/Source/WebCore/editing/VisibleSelection.cpp b/Source/WebCore/editing/VisibleSelection.cpp
index e64739d76..09bef9026 100644
--- a/Source/WebCore/editing/VisibleSelection.cpp
+++ b/Source/WebCore/editing/VisibleSelection.cpp
@@ -30,6 +30,7 @@
#include "Element.h"
#include "htmlediting.h"
#include "TextIterator.h"
+#include "TreeScopeAdjuster.h"
#include "VisiblePosition.h"
#include "visible_units.h"
#include "Range.h"
@@ -461,22 +462,18 @@ void VisibleSelection::adjustSelectionToAvoidCrossingShadowBoundaries()
if (m_base.isNull() || m_start.isNull() || m_end.isNull())
return;
- Node* startRootNode = m_start.anchorNode()->nonBoundaryShadowTreeRootNode();
- Node* endRootNode = m_end.anchorNode()->nonBoundaryShadowTreeRootNode();
-
- if (!startRootNode && !endRootNode)
- return;
-
- if (startRootNode == endRootNode)
+ if (m_start.anchorNode()->treeScope() == m_end.anchorNode()->treeScope())
return;
if (m_baseIsFirst) {
- m_extent = startRootNode ? lastPositionInNode(startRootNode) : positionBeforeNode(endRootNode->shadowAncestorNode());
+ m_extent = TreeScopeAdjuster(m_start.anchorNode()->treeScope()).adjustPositionBefore(m_end);
m_end = m_extent;
} else {
- m_extent = endRootNode ? firstPositionInNode(endRootNode) : positionAfterNode(startRootNode->shadowAncestorNode());
+ m_extent = TreeScopeAdjuster(m_end.anchorNode()->treeScope()).adjustPositionAfter(m_start);
m_start = m_extent;
}
+
+ ASSERT(m_start.anchorNode()->treeScope() == m_end.anchorNode()->treeScope());
}
void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries()
diff --git a/Source/WebCore/editing/gtk/FrameSelectionGtk.cpp b/Source/WebCore/editing/gtk/FrameSelectionGtk.cpp
index e5b2b9bd5..d792cd40c 100644
--- a/Source/WebCore/editing/gtk/FrameSelectionGtk.cpp
+++ b/Source/WebCore/editing/gtk/FrameSelectionGtk.cpp
@@ -22,11 +22,12 @@
#include "AXObjectCache.h"
#include "Frame.h"
-#include "RefPtr.h"
#include "WebKitAccessibleWrapperAtk.h"
#include <gtk/gtk.h>
+#include <wtf/RefPtr.h>
+
namespace WebCore {
static void emitTextSelectionChange(AccessibilityObject* object, VisibleSelection selection, int offset)
diff --git a/Source/WebCore/editing/mac/EditorMac.mm b/Source/WebCore/editing/mac/EditorMac.mm
index 85133dd4f..2c63a24ee 100644
--- a/Source/WebCore/editing/mac/EditorMac.mm
+++ b/Source/WebCore/editing/mac/EditorMac.mm
@@ -30,19 +30,25 @@
#import "ClipboardMac.h"
#import "CachedResourceLoader.h"
#import "DocumentFragment.h"
+#import "DOMRangeInternal.h"
#import "EditingText.h"
#import "Editor.h"
#import "EditorClient.h"
#import "Frame.h"
#import "FrameView.h"
+#import "HTMLConverter.h"
#import "HTMLNames.h"
+#import "LegacyWebArchive.h"
#import "Pasteboard.h"
#import "PasteboardStrategy.h"
#import "PlatformStrategies.h"
+#import "Range.h"
#import "RenderBlock.h"
#import "RuntimeApplicationChecks.h"
#import "Sound.h"
+#import "TypingCommand.h"
#import "htmlediting.h"
+#import "WebNSAttributedStringExtras.h"
namespace WebCore {
@@ -51,7 +57,7 @@ using namespace HTMLNames;
PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy, Frame* frame)
{
return ClipboardMac::create(Clipboard::CopyAndPaste,
- policy == ClipboardWritable ? platformStrategies()->pasteboardStrategy()->uniqueName() : String(NSGeneralPboard), policy, frame);
+ policy == ClipboardWritable ? platformStrategies()->pasteboardStrategy()->uniqueName() : String(NSGeneralPboard), policy, ClipboardMac::CopyAndPasteGeneric, frame);
}
void Editor::showFontPanel()
@@ -97,6 +103,14 @@ void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
m_frame->editor()->client()->setInsertionPasteboard(String());
}
+bool Editor::insertParagraphSeparatorInQuotedContent()
+{
+ // FIXME: Why is this missing calls to canEdit, canEditRichly, etc...
+ TypingCommand::insertParagraphSeparatorInQuotedContent(m_frame->document());
+ revealSelectionAfterEditingOperation();
+ return true;
+}
+
static RenderStyle* styleForSelectionStart(Frame* frame, Node *&nodeToRemove)
{
nodeToRemove = 0;
@@ -291,7 +305,7 @@ void Editor::takeFindStringFromSelection()
void Editor::writeSelectionToPasteboard(const String& pasteboardName, const Vector<String>& pasteboardTypes)
{
Pasteboard pasteboard(pasteboardName);
- pasteboard.writeSelectionForTypes(pasteboardTypes, selectedRange().get(), true, m_frame);
+ pasteboard.writeSelectionForTypes(pasteboardTypes, true, m_frame);
}
void Editor::readSelectionFromPasteboard(const String& pasteboardName)
@@ -303,4 +317,14 @@ void Editor::readSelectionFromPasteboard(const String& pasteboardName)
pasteAsPlainTextWithPasteboard(&pasteboard);
}
+String Editor::stringSelectionForPasteboard()
+{
+ return Pasteboard::getStringSelection(m_frame);
+}
+
+PassRefPtr<SharedBuffer> Editor::dataSelectionForPasteboard(const String& pasteboardType)
+{
+ return Pasteboard::getDataSelection(m_frame, pasteboardType);
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/editing/mac/FrameSelectionMac.mm b/Source/WebCore/editing/mac/FrameSelectionMac.mm
index 791cac578..a1a272b46 100644
--- a/Source/WebCore/editing/mac/FrameSelectionMac.mm
+++ b/Source/WebCore/editing/mac/FrameSelectionMac.mm
@@ -63,8 +63,8 @@ void FrameSelection::notifyAccessibilityForSelectionChange()
if (!frameView)
return;
- LayoutRect selectionRect = absoluteCaretBounds();
- LayoutRect viewRect = renderView->viewRect();
+ IntRect selectionRect = absoluteCaretBounds();
+ IntRect viewRect = pixelSnappedIntRect(renderView->viewRect());
selectionRect = frameView->contentsToScreen(selectionRect);
viewRect = frameView->contentsToScreen(viewRect);
diff --git a/Source/WebCore/editing/markup.cpp b/Source/WebCore/editing/markup.cpp
index 05cde4a63..bb49b372f 100644
--- a/Source/WebCore/editing/markup.cpp
+++ b/Source/WebCore/editing/markup.cpp
@@ -36,7 +36,6 @@
#include "CSSRule.h"
#include "CSSRuleList.h"
#include "CSSStyleRule.h"
-#include "CSSStyleSelector.h"
#include "CSSValue.h"
#include "CSSValueKeywords.h"
#include "ChildListMutationScope.h"
@@ -55,6 +54,7 @@
#include "Range.h"
#include "RenderObject.h"
#include "StylePropertySet.h"
+#include "StyleResolver.h"
#include "TextIterator.h"
#include "VisibleSelection.h"
#include "XMLNSNames.h"
@@ -70,7 +70,7 @@ namespace WebCore {
using namespace HTMLNames;
-static bool propertyMissingOrEqualToNone(StylePropertySet*, int propertyID);
+static bool propertyMissingOrEqualToNone(StylePropertySet*, CSSPropertyID);
class AttributeChange {
public:
@@ -466,7 +466,7 @@ static inline Node* ancestorToRetainStructureAndAppearanceWithNoRenderer(Node* c
return ancestorToRetainStructureAndAppearanceForBlock(commonAncestorBlock);
}
-static bool propertyMissingOrEqualToNone(StylePropertySet* style, int propertyID)
+static bool propertyMissingOrEqualToNone(StylePropertySet* style, CSSPropertyID propertyID)
{
if (!style)
return false;
@@ -859,7 +859,7 @@ PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String
if (renderer && renderer->style()->preserveNewline()) {
fragment->appendChild(document->createTextNode(string), ec);
ASSERT(!ec);
- if (string.endsWith("\n")) {
+ if (string.endsWith('\n')) {
RefPtr<Element> element = createBreakElement(document);
element->setAttribute(classAttr, AppleInterchangeNewline);
fragment->appendChild(element.release(), ec);
diff --git a/Source/WebCore/editing/visible_units.cpp b/Source/WebCore/editing/visible_units.cpp
index db8925946..f225d0b49 100644
--- a/Source/WebCore/editing/visible_units.cpp
+++ b/Source/WebCore/editing/visible_units.cpp
@@ -32,7 +32,6 @@
#include "InlineTextBox.h"
#include "Position.h"
#include "RenderBlock.h"
-#include "RenderLayer.h"
#include "RenderObject.h"
#include "RenderedPosition.h"
#include "Text.h"
@@ -49,6 +48,423 @@ namespace WebCore {
using namespace HTMLNames;
using namespace WTF::Unicode;
+static Node* previousLeafWithSameEditability(Node* node, EditableType editableType)
+{
+ bool editable = node->rendererIsEditable(editableType);
+ node = node->previousLeafNode();
+ while (node) {
+ if (editable == node->rendererIsEditable(editableType))
+ return node;
+ node = node->previousLeafNode();
+ }
+ return 0;
+}
+
+static Node* enclosingNodeWithNonInlineRenderer(Node* node)
+{
+ for (; node; node = node->parentNode()) {
+ if (node->renderer() && !node->renderer()->isInline())
+ return node;
+ }
+ return 0;
+}
+
+static Node* nextLeafWithSameEditability(Node* node, int offset)
+{
+ bool editable = node->rendererIsEditable();
+ ASSERT(offset >= 0);
+ Node* child = node->childNode(offset);
+ node = child ? child->nextLeafNode() : node->lastDescendant()->nextLeafNode();
+ while (node) {
+ if (editable == node->rendererIsEditable())
+ return node;
+ node = node->nextLeafNode();
+ }
+ return 0;
+}
+
+static Node* nextLeafWithSameEditability(Node* node, EditableType editableType = ContentIsEditable)
+{
+ if (!node)
+ return 0;
+
+ bool editable = node->rendererIsEditable(editableType);
+ node = node->nextLeafNode();
+ while (node) {
+ if (editable == node->rendererIsEditable(editableType))
+ return node;
+ node = node->nextLeafNode();
+ }
+ return 0;
+}
+
+// FIXME: consolidate with code in previousLinePosition.
+static const RootInlineBox* previousRootInlineBox(const InlineBox* box, const VisiblePosition& visiblePosition)
+{
+ Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), ContentIsEditable);
+
+ if (!box->renderer() || !box->renderer()->node())
+ return 0;
+
+ Node* node = box->renderer()->node();
+ Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node);
+ Node* previousNode = previousLeafWithSameEditability(node, ContentIsEditable);
+
+ while (previousNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(previousNode))
+ previousNode = previousLeafWithSameEditability(previousNode, ContentIsEditable);
+
+ while (previousNode && !previousNode->isShadowRoot()) {
+ if (highestEditableRoot(firstPositionInOrBeforeNode(previousNode), ContentIsEditable) != highestRoot)
+ break;
+
+ Position pos = previousNode->hasTagName(brTag) ? positionBeforeNode(previousNode) :
+ createLegacyEditingPosition(previousNode, caretMaxOffset(previousNode));
+
+ if (pos.isCandidate()) {
+ RenderedPosition renderedPos(pos, DOWNSTREAM);
+ RootInlineBox* root = renderedPos.rootBox();
+ if (root)
+ return root;
+ }
+
+ previousNode = previousLeafWithSameEditability(previousNode, ContentIsEditable);
+ }
+ return 0;
+}
+
+static const RootInlineBox* nextRootInlineBox(const InlineBox* box, const VisiblePosition& visiblePosition)
+{
+ Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), ContentIsEditable);
+
+ if (!box->renderer() || !box->renderer()->node())
+ return 0;
+
+ Node* node = box->renderer()->node();
+ Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node);
+ Node* nextNode = nextLeafWithSameEditability(node, ContentIsEditable);
+ while (nextNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(nextNode))
+ nextNode = nextLeafWithSameEditability(nextNode, ContentIsEditable);
+
+ while (nextNode && !nextNode->isShadowRoot()) {
+ if (highestEditableRoot(firstPositionInOrBeforeNode(nextNode), ContentIsEditable) != highestRoot)
+ break;
+
+ Position pos;
+ pos = createLegacyEditingPosition(nextNode, caretMinOffset(nextNode));
+
+ if (pos.isCandidate()) {
+ RenderedPosition renderedPos(pos, DOWNSTREAM);
+ RootInlineBox* root = renderedPos.rootBox();
+ if (root)
+ return root;
+ }
+
+ nextNode = nextLeafWithSameEditability(nextNode, ContentIsEditable);
+ }
+ return 0;
+}
+
+class CachedLogicallyOrderedLeafBoxes {
+public:
+ CachedLogicallyOrderedLeafBoxes();
+
+ const InlineTextBox* previousTextBox(const RootInlineBox*, const InlineTextBox*);
+ const InlineTextBox* nextTextBox(const RootInlineBox*, const InlineTextBox*);
+
+ size_t size() const { return m_leafBoxes.size(); }
+ const InlineBox* firstBox() const { return m_leafBoxes[0]; }
+
+private:
+ const Vector<InlineBox*>& collectBoxes(const RootInlineBox*);
+ int boxIndexInLeaves(const InlineTextBox*);
+
+ const RootInlineBox* m_rootInlineBox;
+ Vector<InlineBox*> m_leafBoxes;
+};
+
+CachedLogicallyOrderedLeafBoxes::CachedLogicallyOrderedLeafBoxes() : m_rootInlineBox(0) { };
+
+const InlineTextBox* CachedLogicallyOrderedLeafBoxes::previousTextBox(const RootInlineBox* root, const InlineTextBox* box)
+{
+ if (!root)
+ return 0;
+
+ collectBoxes(root);
+
+ // If box is null, root is box's previous RootInlineBox, and previousBox is the last logical box in root.
+ int boxIndex = m_leafBoxes.size() - 1;
+ if (box)
+ boxIndex = boxIndexInLeaves(box) - 1;
+
+ for (int i = boxIndex; i >= 0; --i) {
+ if (m_leafBoxes[i]->isInlineTextBox())
+ return toInlineTextBox(m_leafBoxes[i]);
+ }
+
+ return 0;
+}
+
+const InlineTextBox* CachedLogicallyOrderedLeafBoxes::nextTextBox(const RootInlineBox* root, const InlineTextBox* box)
+{
+ if (!root)
+ return 0;
+
+ collectBoxes(root);
+
+ // If box is null, root is box's next RootInlineBox, and nextBox is the first logical box in root.
+ // Otherwise, root is box's RootInlineBox, and nextBox is the next logical box in the same line.
+ size_t nextBoxIndex = 0;
+ if (box)
+ nextBoxIndex = boxIndexInLeaves(box) + 1;
+
+ for (size_t i = nextBoxIndex; i < m_leafBoxes.size(); ++i) {
+ if (m_leafBoxes[i]->isInlineTextBox())
+ return toInlineTextBox(m_leafBoxes[i]);
+ }
+
+ return 0;
+}
+
+const Vector<InlineBox*>& CachedLogicallyOrderedLeafBoxes::collectBoxes(const RootInlineBox* root)
+{
+ if (m_rootInlineBox != root) {
+ m_rootInlineBox = root;
+ m_leafBoxes.clear();
+ root->collectLeafBoxesInLogicalOrder(m_leafBoxes);
+ }
+ return m_leafBoxes;
+}
+
+int CachedLogicallyOrderedLeafBoxes::boxIndexInLeaves(const InlineTextBox* box)
+{
+ for (size_t i = 0; i < m_leafBoxes.size(); ++i) {
+ if (box == m_leafBoxes[i])
+ return i;
+ }
+ return 0;
+}
+
+static const InlineTextBox* logicallyPreviousBox(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
+ bool& previousBoxInDifferentBlock, CachedLogicallyOrderedLeafBoxes& leafBoxes)
+{
+ const InlineBox* startBox = textBox;
+
+ const InlineTextBox* previousBox = leafBoxes.previousTextBox(startBox->root(), textBox);
+ if (previousBox)
+ return previousBox;
+
+ previousBox = leafBoxes.previousTextBox(startBox->root()->prevRootBox(), 0);
+ if (previousBox)
+ return previousBox;
+
+ while (1) {
+ const RootInlineBox* previousRoot = previousRootInlineBox(startBox, visiblePosition);
+ if (!previousRoot)
+ break;
+
+ previousBox = leafBoxes.previousTextBox(previousRoot, 0);
+ if (previousBox) {
+ previousBoxInDifferentBlock = true;
+ return previousBox;
+ }
+
+ if (!leafBoxes.size())
+ break;
+ startBox = leafBoxes.firstBox();
+ }
+ return 0;
+}
+
+
+static const InlineTextBox* logicallyNextBox(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
+ bool& nextBoxInDifferentBlock, CachedLogicallyOrderedLeafBoxes& leafBoxes)
+{
+ const InlineBox* startBox = textBox;
+
+ const InlineTextBox* nextBox = leafBoxes.nextTextBox(startBox->root(), textBox);
+ if (nextBox)
+ return nextBox;
+
+ nextBox = leafBoxes.nextTextBox(startBox->root()->nextRootBox(), 0);
+ if (nextBox)
+ return nextBox;
+
+ while (1) {
+ const RootInlineBox* nextRoot = nextRootInlineBox(startBox, visiblePosition);
+ if (!nextRoot)
+ break;
+
+ nextBox = leafBoxes.nextTextBox(nextRoot, 0);
+ if (nextBox) {
+ nextBoxInDifferentBlock = true;
+ return nextBox;
+ }
+
+ if (!leafBoxes.size())
+ break;
+ startBox = leafBoxes.firstBox();
+ }
+ return 0;
+}
+
+static TextBreakIterator* wordBreakIteratorForMinOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
+ int& previousBoxLength, bool& previousBoxInDifferentBlock, Vector<UChar, 1024>& string, CachedLogicallyOrderedLeafBoxes& leafBoxes)
+{
+ previousBoxInDifferentBlock = false;
+
+ // FIXME: Handle the case when we don't have an inline text box.
+ const InlineTextBox* previousBox = logicallyPreviousBox(visiblePosition, textBox, previousBoxInDifferentBlock, leafBoxes);
+
+ int len = 0;
+ string.clear();
+ if (previousBox) {
+ previousBoxLength = previousBox->len();
+ string.append(previousBox->textRenderer()->text()->characters() + previousBox->start(), previousBoxLength);
+ len += previousBoxLength;
+ }
+ string.append(textBox->textRenderer()->text()->characters() + textBox->start(), textBox->len());
+ len += textBox->len();
+
+ return wordBreakIterator(string.data(), len);
+}
+
+static TextBreakIterator* wordBreakIteratorForMaxOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
+ bool& nextBoxInDifferentBlock, Vector<UChar, 1024>& string, CachedLogicallyOrderedLeafBoxes& leafBoxes)
+{
+ nextBoxInDifferentBlock = false;
+
+ // FIXME: Handle the case when we don't have an inline text box.
+ const InlineTextBox* nextBox = logicallyNextBox(visiblePosition, textBox, nextBoxInDifferentBlock, leafBoxes);
+
+ int len = 0;
+ string.clear();
+ string.append(textBox->textRenderer()->text()->characters() + textBox->start(), textBox->len());
+ len += textBox->len();
+ if (nextBox) {
+ string.append(nextBox->textRenderer()->text()->characters() + nextBox->start(), nextBox->len());
+ len += nextBox->len();
+ }
+
+ return wordBreakIterator(string.data(), len);
+}
+
+static bool isLogicalStartOfWord(TextBreakIterator* iter, int position, bool hardLineBreak)
+{
+ bool boundary = hardLineBreak ? true : isTextBreak(iter, position);
+ if (!boundary)
+ return false;
+
+ textBreakFollowing(iter, position);
+ // isWordTextBreak returns true after moving across a word and false after moving across a punctuation/space.
+ return isWordTextBreak(iter);
+}
+
+static bool islogicalEndOfWord(TextBreakIterator* iter, int position, bool hardLineBreak)
+{
+ bool boundary = isTextBreak(iter, position);
+ return (hardLineBreak || boundary) && isWordTextBreak(iter);
+}
+
+enum CursorMovementDirection { MoveLeft, MoveRight };
+
+static VisiblePosition visualWordPosition(const VisiblePosition& visiblePosition, CursorMovementDirection direction,
+ bool skipsSpaceWhenMovingRight)
+{
+ if (visiblePosition.isNull())
+ return VisiblePosition();
+
+ TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
+ InlineBox* previouslyVisitedBox = 0;
+ VisiblePosition current = visiblePosition;
+ TextBreakIterator* iter = 0;
+
+ CachedLogicallyOrderedLeafBoxes leafBoxes;
+ Vector<UChar, 1024> string;
+
+ while (1) {
+ VisiblePosition adjacentCharacterPosition = direction == MoveRight ? current.right(true) : current.left(true);
+ if (adjacentCharacterPosition == current || adjacentCharacterPosition.isNull())
+ return VisiblePosition();
+
+ InlineBox* box;
+ int offsetInBox;
+ adjacentCharacterPosition.deepEquivalent().getInlineBoxAndOffset(UPSTREAM, box, offsetInBox);
+
+ if (!box)
+ break;
+ if (!box->isInlineTextBox()) {
+ current = adjacentCharacterPosition;
+ continue;
+ }
+
+ InlineTextBox* textBox = toInlineTextBox(box);
+ int previousBoxLength = 0;
+ bool previousBoxInDifferentBlock = false;
+ bool nextBoxInDifferentBlock = false;
+ bool movingIntoNewBox = previouslyVisitedBox != box;
+
+ if (offsetInBox == box->caretMinOffset())
+ iter = wordBreakIteratorForMinOffsetBoundary(visiblePosition, textBox, previousBoxLength, previousBoxInDifferentBlock, string, leafBoxes);
+ else if (offsetInBox == box->caretMaxOffset())
+ iter = wordBreakIteratorForMaxOffsetBoundary(visiblePosition, textBox, nextBoxInDifferentBlock, string, leafBoxes);
+ else if (movingIntoNewBox) {
+ iter = wordBreakIterator(textBox->textRenderer()->text()->characters() + textBox->start(), textBox->len());
+ previouslyVisitedBox = box;
+ }
+
+ if (!iter)
+ break;
+
+ textBreakFirst(iter);
+ int offsetInIterator = offsetInBox - textBox->start() + previousBoxLength;
+
+ bool isWordBreak;
+ bool boxHasSameDirectionalityAsBlock = box->direction() == blockDirection;
+ bool movingBackward = (direction == MoveLeft && box->direction() == LTR) || (direction == MoveRight && box->direction() == RTL);
+ if ((skipsSpaceWhenMovingRight && boxHasSameDirectionalityAsBlock)
+ || (!skipsSpaceWhenMovingRight && movingBackward)) {
+ bool logicalStartInRenderer = offsetInBox == static_cast<int>(textBox->start()) && previousBoxInDifferentBlock;
+ isWordBreak = isLogicalStartOfWord(iter, offsetInIterator, logicalStartInRenderer);
+ } else {
+ bool logicalEndInRenderer = offsetInBox == static_cast<int>(textBox->start() + textBox->len()) && nextBoxInDifferentBlock;
+ isWordBreak = islogicalEndOfWord(iter, offsetInIterator, logicalEndInRenderer);
+ }
+
+ if (isWordBreak)
+ return adjacentCharacterPosition;
+
+ current = adjacentCharacterPosition;
+ }
+ return VisiblePosition();
+}
+
+VisiblePosition leftWordPosition(const VisiblePosition& visiblePosition, bool skipsSpaceWhenMovingRight)
+{
+ VisiblePosition leftWordBreak = visualWordPosition(visiblePosition, MoveLeft, skipsSpaceWhenMovingRight);
+ leftWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(leftWordBreak);
+
+ // FIXME: How should we handle a non-editable position?
+ if (leftWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
+ TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
+ leftWordBreak = blockDirection == LTR ? startOfEditableContent(visiblePosition) : endOfEditableContent(visiblePosition);
+ }
+ return leftWordBreak;
+}
+
+VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition, bool skipsSpaceWhenMovingRight)
+{
+ VisiblePosition rightWordBreak = visualWordPosition(visiblePosition, MoveRight, skipsSpaceWhenMovingRight);
+ rightWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(rightWordBreak);
+
+ // FIXME: How should we handle a non-editable position?
+ if (rightWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
+ TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
+ rightWordBreak = blockDirection == LTR ? endOfEditableContent(visiblePosition) : startOfEditableContent(visiblePosition);
+ }
+ return rightWordBreak;
+}
+
+
enum BoundarySearchContextAvailability { DontHaveMoreContext, MayHaveMoreContext };
typedef unsigned (*BoundarySearchFunction)(const UChar*, unsigned length, unsigned offset, BoundarySearchContextAvailability, bool& needMoreContext);
@@ -517,28 +933,6 @@ bool isEndOfLine(const VisiblePosition &p)
return p.isNotNull() && p == endOfLine(p);
}
-// The first leaf before node that has the same editability as node.
-static Node* previousLeafWithSameEditability(Node* node, EditableType editableType)
-{
- bool editable = node->rendererIsEditable(editableType);
- Node* n = node->previousLeafNode();
- while (n) {
- if (editable == n->rendererIsEditable(editableType))
- return n;
- n = n->previousLeafNode();
- }
- return 0;
-}
-
-static Node* enclosingNodeWithNonInlineRenderer(Node* n)
-{
- for (Node* p = n; p; p = p->parentNode()) {
- if (p->renderer() && !p->renderer()->isInline())
- return p;
- }
- return 0;
-}
-
static inline IntPoint absoluteLineDirectionPointToLocalPointInBlock(RootInlineBox* root, int lineDirectionPoint)
{
ASSERT(root);
@@ -625,34 +1019,6 @@ VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int
return VisiblePosition(firstPositionInNode(rootElement), DOWNSTREAM);
}
-static Node* nextLeafWithSameEditability(Node* node, int offset)
-{
- bool editable = node->rendererIsEditable();
- ASSERT(offset >= 0);
- Node* child = node->childNode(offset);
- Node* n = child ? child->nextLeafNode() : node->lastDescendant()->nextLeafNode();
- while (n) {
- if (editable == n->rendererIsEditable())
- return n;
- n = n->nextLeafNode();
- }
- return 0;
-}
-
-static Node* nextLeafWithSameEditability(Node* node, EditableType editableType = ContentIsEditable)
-{
- if (!node)
- return 0;
-
- bool editable = node->rendererIsEditable(editableType);
- Node* n = node->nextLeafNode();
- while (n) {
- if (editable == n->rendererIsEditable(editableType))
- return n;
- n = n->nextLeafNode();
- }
- return 0;
-}
VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint, EditableType editableType)
{
@@ -1099,543 +1465,4 @@ VisiblePosition rightBoundaryOfLine(const VisiblePosition& c, TextDirection dire
return direction == LTR ? logicalEndOfLine(c) : logicalStartOfLine(c);
}
-static const int invalidOffset = -1;
-static const int offsetNotFound = -1;
-
-static bool positionIsInBox(const VisiblePosition& wordBreak, const InlineBox* box, int& offsetOfWordBreak)
-{
- if (wordBreak.isNull())
- return false;
-
- InlineBox* boxOfWordBreak;
- wordBreak.getInlineBoxAndOffset(boxOfWordBreak, offsetOfWordBreak);
- return box == boxOfWordBreak;
-}
-
-static VisiblePosition previousWordBreakInBoxInsideBlockWithSameDirectionality(const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak)
-{
- // In a LTR block, the word break should be on the left boundary of a word.
- // In a RTL block, the word break should be on the right boundary of a word.
- // Because nextWordPosition() returns the word break on the right boundary of the word for LTR text,
- // we need to use previousWordPosition() to traverse words within the inline boxes from right to left
- // to find the previous word break (i.e. the first word break on the left). The same applies to RTL text.
-
- bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull();
-
- VisiblePosition wordBreak;
-
- if (hasSeenWordBreakInThisBox)
- wordBreak = previousWordBreak;
- else {
- wordBreak = createLegacyEditingPosition(box->renderer()->node(), box->caretMaxOffset());
-
- // Return the rightmost word boundary of LTR box or leftmost word boundary of RTL box if
- // it is not in the previously visited boxes. For example, given a logical text
- // "abc def hij opq", there are 2 boxes: the "abc def " (starts at 0 and length is 8)
- // and the "hij opq" (starts at 12 and length is 7). The word breaks are
- // "abc |def | hij |opq". We normally catch the word break between "def" and "hij" when
- // we visit the box that contains "hij opq", but this word break doesn't exist in the box
- // that contains "hij opq" when there are multiple spaces. So we detect it when we're
- // traversing the box that contains "abc def " instead.
-
- if ((box->isLeftToRightDirection() && box->nextLeafChild())
- || (!box->isLeftToRightDirection() && box->prevLeafChild())) {
-
- VisiblePosition positionAfterWord = nextBoundary(wordBreak, nextWordPositionBoundary);
- if (positionAfterWord.isNotNull()) {
- VisiblePosition positionBeforeWord = previousBoundary(positionAfterWord, previousWordPositionBoundary);
-
- if (positionIsInBox(positionBeforeWord, box, offsetOfWordBreak))
- return positionBeforeWord;
- }
- }
- }
-
- wordBreak = previousBoundary(wordBreak, previousWordPositionBoundary);
- if (previousWordBreak == wordBreak)
- return VisiblePosition();
-
- return positionIsInBox(wordBreak, box, offsetOfWordBreak) ? wordBreak : VisiblePosition();
-}
-
-static VisiblePosition leftmostPositionInRTLBoxInLTRBlock(const InlineBox* box)
-{
- // FIXME: Probably need to take care of bidi level too.
- Node* node = box->renderer()->node();
- InlineBox* previousLeaf = box->prevLeafChild();
- InlineBox* nextLeaf = box->nextLeafChild();
-
- if (previousLeaf && !previousLeaf->isLeftToRightDirection())
- return createLegacyEditingPosition(node, box->caretMaxOffset());
-
- if (nextLeaf && !nextLeaf->isLeftToRightDirection()) {
- if (previousLeaf)
- return createLegacyEditingPosition(previousLeaf->renderer()->node(), previousLeaf->caretMaxOffset());
-
- InlineBox* lastRTLLeaf;
- do {
- lastRTLLeaf = nextLeaf;
- nextLeaf = nextLeaf->nextLeafChild();
- } while (nextLeaf && !nextLeaf->isLeftToRightDirection());
- return createLegacyEditingPosition(lastRTLLeaf->renderer()->node(), lastRTLLeaf->caretMinOffset());
- }
-
- return createLegacyEditingPosition(node, box->caretMinOffset());
-}
-
-static VisiblePosition rightmostPositionInLTRBoxInRTLBlock(const InlineBox* box)
-{
- // FIXME: Probably need to take care of bidi level too.
- Node* node = box->renderer()->node();
- InlineBox* previousLeaf = box->prevLeafChild();
- InlineBox* nextLeaf = box->nextLeafChild();
-
- if (nextLeaf && nextLeaf->isLeftToRightDirection())
- return createLegacyEditingPosition(node, box->caretMaxOffset());
-
- if (previousLeaf && previousLeaf->isLeftToRightDirection()) {
- if (nextLeaf)
- return createLegacyEditingPosition(nextLeaf->renderer()->node(), nextLeaf->caretMaxOffset());
-
- InlineBox* firstLTRLeaf;
- do {
- firstLTRLeaf = previousLeaf;
- previousLeaf = previousLeaf->prevLeafChild();
- } while (previousLeaf && previousLeaf->isLeftToRightDirection());
- return createLegacyEditingPosition(firstLTRLeaf->renderer()->node(), firstLTRLeaf->caretMinOffset());
- }
-
- return createLegacyEditingPosition(node, box->caretMinOffset());
-}
-
-static VisiblePosition lastWordBreakInBox(const InlineBox* box, int& offsetOfWordBreak)
-{
- // Add the leftmost word break for RTL box or rightmost word break for LTR box.
- InlineBox* previousLeaf = box->prevLeafChild();
- InlineBox* nextLeaf = box->nextLeafChild();
- VisiblePosition boundaryPosition;
- if (box->direction() == RTL && (!previousLeaf || previousLeaf->isLeftToRightDirection()))
- boundaryPosition = leftmostPositionInRTLBoxInLTRBlock(box);
- else if (box->direction() == LTR && (!nextLeaf || !nextLeaf->isLeftToRightDirection()))
- boundaryPosition = rightmostPositionInLTRBoxInRTLBlock(box);
-
- if (boundaryPosition.isNull())
- return VisiblePosition();
-
- VisiblePosition wordBreak = nextBoundary(boundaryPosition, nextWordPositionBoundary);
- if (wordBreak.isNull())
- wordBreak = boundaryPosition;
- else if (wordBreak != boundaryPosition)
- wordBreak = previousBoundary(wordBreak, previousWordPositionBoundary);
-
- return positionIsInBox(wordBreak, box, offsetOfWordBreak) ? wordBreak : VisiblePosition();
-}
-
-static bool positionIsVisuallyOrderedInBoxInBlockWithDifferentDirectionality(const VisiblePosition& wordBreak, const InlineBox* box, int& offsetOfWordBreak)
-{
- int previousOffset = offsetOfWordBreak;
- return positionIsInBox(wordBreak, box, offsetOfWordBreak)
- && (previousOffset == invalidOffset || previousOffset < offsetOfWordBreak);
-}
-
-static VisiblePosition nextWordBreakInBoxInsideBlockWithDifferentDirectionality(
- const InlineBox* box, const VisiblePosition& previousWordBreak, int& offsetOfWordBreak, bool& isLastWordBreakInBox)
-{
- // FIXME: Probably need to take care of bidi level too.
-
- // In a LTR block, the word break should be on the left boundary of a word.
- // In a RTL block, the word break should be on the right boundary of a word.
- // Because previousWordPosition() returns the word break on the right boundary of the word for RTL text,
- // we need to use nextWordPosition() to traverse words within the inline boxes from right to left to find the next word break.
- // The same applies to LTR text, in which words are traversed within the inline boxes from left to right.
-
- bool hasSeenWordBreakInThisBox = previousWordBreak.isNotNull();
- VisiblePosition wordBreak = hasSeenWordBreakInThisBox ? previousWordBreak :
- createLegacyEditingPosition(box->renderer()->node(), box->caretMinOffset());
-
- wordBreak = nextBoundary(wordBreak, nextWordPositionBoundary);
-
- // Given RTL box "ABC DEF" either follows a LTR box or is the first visual box in an LTR block as an example,
- // the visual display of the RTL box is: "(0)J(10)I(9)H(8) (7)F(6)E(5)D(4) (3)C(2)B(1)A(11)",
- // where the number in parenthesis represents offset in visiblePosition.
- // Start at offset 0, the first word break is at offset 3, the 2nd word break is at offset 7, and the 3rd word break should be at offset 0.
- // But nextWordPosition() of offset 7 is offset 11, which should be ignored,
- // and the position at offset 0 should be manually added as the last word break within the box.
- if (wordBreak != previousWordBreak && positionIsVisuallyOrderedInBoxInBlockWithDifferentDirectionality(wordBreak, box, offsetOfWordBreak)) {
- isLastWordBreakInBox = false;
- return wordBreak;
- }
-
- isLastWordBreakInBox = true;
- return lastWordBreakInBox(box, offsetOfWordBreak);
-}
-
-struct WordBoundaryEntry {
- WordBoundaryEntry()
- : offsetInInlineBox(invalidOffset)
- {
- }
-
- WordBoundaryEntry(const VisiblePosition& position, int offset)
- : visiblePosition(position)
- , offsetInInlineBox(offset)
- {
- }
-
- VisiblePosition visiblePosition;
- int offsetInInlineBox;
-};
-
-typedef Vector<WordBoundaryEntry, 50> WordBoundaryVector;
-
-static void collectWordBreaksInBoxInsideBlockWithSameDirectionality(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries)
-{
- orderedWordBoundaries.clear();
-
- VisiblePosition wordBreak;
- int offsetOfWordBreak = invalidOffset;
- while (1) {
- wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak);
- if (wordBreak.isNull())
- break;
- WordBoundaryEntry wordBoundaryEntry(wordBreak, offsetOfWordBreak);
- orderedWordBoundaries.append(wordBoundaryEntry);
- }
-}
-
-static void collectWordBreaksInBoxInsideBlockWithDifferntDirectionality(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries)
-{
- orderedWordBoundaries.clear();
-
- VisiblePosition wordBreak;
- int offsetOfWordBreak = invalidOffset;
- bool isLastWordBreakInBox = false;
- while (1) {
- wordBreak = nextWordBreakInBoxInsideBlockWithDifferentDirectionality(box, wordBreak, offsetOfWordBreak, isLastWordBreakInBox);
- if (wordBreak.isNotNull()) {
- WordBoundaryEntry wordBoundaryEntry(wordBreak, offsetOfWordBreak);
- orderedWordBoundaries.append(wordBoundaryEntry);
- }
- if (isLastWordBreakInBox)
- break;
- }
-}
-
-static void collectWordBreaksInBox(const InlineBox* box, WordBoundaryVector& orderedWordBoundaries, TextDirection blockDirection)
-{
- if (box->direction() == blockDirection)
- collectWordBreaksInBoxInsideBlockWithSameDirectionality(box, orderedWordBoundaries);
- else
- collectWordBreaksInBoxInsideBlockWithDifferntDirectionality(box, orderedWordBoundaries);
-}
-
-static VisiblePosition previousWordBoundaryInBox(const InlineBox* box, int offset)
-{
- int offsetOfWordBreak = 0;
- VisiblePosition wordBreak;
- while (true) {
- wordBreak = previousWordBreakInBoxInsideBlockWithSameDirectionality(box, wordBreak, offsetOfWordBreak);
- if (wordBreak.isNull())
- break;
- if (offset == invalidOffset || offsetOfWordBreak != offset)
- return wordBreak;
- }
- return VisiblePosition();
-}
-
-static VisiblePosition nextWordBoundaryInBox(const InlineBox* box, int offset)
-{
- int offsetOfWordBreak = 0;
- VisiblePosition wordBreak;
- bool isLastWordBreakInBox = false;
- do {
- wordBreak = nextWordBreakInBoxInsideBlockWithDifferentDirectionality(box, wordBreak, offsetOfWordBreak, isLastWordBreakInBox);
- if (wordBreak.isNotNull() && (offset == invalidOffset || offsetOfWordBreak != offset))
- return wordBreak;
- } while (!isLastWordBreakInBox);
- return VisiblePosition();
-}
-
-static VisiblePosition visuallyLastWordBoundaryInBox(const InlineBox* box, int offset, TextDirection blockDirection)
-{
- WordBoundaryVector orderedWordBoundaries;
- collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
- if (!orderedWordBoundaries.size())
- return VisiblePosition();
- if (offset == invalidOffset || orderedWordBoundaries[orderedWordBoundaries.size() - 1].offsetInInlineBox != offset)
- return orderedWordBoundaries[orderedWordBoundaries.size() - 1].visiblePosition;
- if (orderedWordBoundaries.size() > 1)
- return orderedWordBoundaries[orderedWordBoundaries.size() - 2].visiblePosition;
- return VisiblePosition();
-}
-
-static int greatestOffsetUnder(int offset, bool boxAndBlockAreInSameDirection, const WordBoundaryVector& orderedWordBoundaries)
-{
- if (!orderedWordBoundaries.size())
- return offsetNotFound;
- // FIXME: binary search.
- if (boxAndBlockAreInSameDirection) {
- for (unsigned i = 0; i < orderedWordBoundaries.size(); ++i) {
- if (orderedWordBoundaries[i].offsetInInlineBox < offset)
- return i;
- }
- return offsetNotFound;
- }
- for (int i = orderedWordBoundaries.size() - 1; i >= 0; --i) {
- if (orderedWordBoundaries[i].offsetInInlineBox < offset)
- return i;
- }
- return offsetNotFound;
-}
-
-static int smallestOffsetAbove(int offset, bool boxAndBlockAreInSameDirection, const WordBoundaryVector& orderedWordBoundaries)
-{
- if (!orderedWordBoundaries.size())
- return offsetNotFound;
- // FIXME: binary search.
- if (boxAndBlockAreInSameDirection) {
- for (int i = orderedWordBoundaries.size() - 1; i >= 0; --i) {
- if (orderedWordBoundaries[i].offsetInInlineBox > offset)
- return i;
- }
- return offsetNotFound;
- }
- for (unsigned i = 0; i < orderedWordBoundaries.size(); ++i) {
- if (orderedWordBoundaries[i].offsetInInlineBox > offset)
- return i;
- }
- return offsetNotFound;
-}
-
-static const RootInlineBox* previousRootInlineBox(const InlineBox* box)
-{
- Node* node = box->renderer()->node();
- Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node);
- Node* previousNode = node->previousLeafNode();
- while (previousNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(previousNode))
- previousNode = previousNode->previousLeafNode();
-
- while (previousNode && !previousNode->isShadowRoot()) {
- Position pos = createLegacyEditingPosition(previousNode, caretMaxOffset(previousNode));
-
- if (pos.isCandidate()) {
- RenderedPosition renderedPos(pos, DOWNSTREAM);
- RootInlineBox* root = renderedPos.rootBox();
- if (root)
- return root;
- }
-
- previousNode = previousNode->previousLeafNode();
- }
- return 0;
-}
-
-static const RootInlineBox* nextRootInlineBox(const InlineBox* box)
-{
- Node* node = box->renderer()->node();
- Node* enclosingBlockNode = enclosingNodeWithNonInlineRenderer(node);
- Node* nextNode = node->nextLeafNode();
- while (nextNode && enclosingBlockNode == enclosingNodeWithNonInlineRenderer(nextNode))
- nextNode = nextNode->nextLeafNode();
-
- while (nextNode && !nextNode->isShadowRoot()) {
- Position pos;
- pos = createLegacyEditingPosition(nextNode, caretMinOffset(nextNode));
-
- if (pos.isCandidate()) {
- RenderedPosition renderedPos(pos, DOWNSTREAM);
- RootInlineBox* root = renderedPos.rootBox();
- if (root)
- return root;
- }
-
- nextNode = nextNode->nextLeafNode();
- }
- return 0;
-}
-
-static const InlineBox* leftInlineBox(const InlineBox* box, TextDirection blockDirection)
-{
- if (box->prevLeafChild())
- return box->prevLeafChild();
-
- const RootInlineBox* rootBox = box->root();
- const bool isBlockLTR = blockDirection == LTR;
- const InlineFlowBox* leftLineBox = isBlockLTR ? rootBox->prevLineBox() : rootBox->nextLineBox();
- if (leftLineBox)
- return leftLineBox->lastLeafChild();
-
- const RootInlineBox* leftRootInlineBox = isBlockLTR ? previousRootInlineBox(box) :
- nextRootInlineBox(box);
- return leftRootInlineBox ? leftRootInlineBox->lastLeafChild() : 0;
-}
-
-static const InlineBox* rightInlineBox(const InlineBox* box, TextDirection blockDirection)
-{
- if (box->nextLeafChild())
- return box->nextLeafChild();
-
- const RootInlineBox* rootBox = box->root();
- const bool isBlockLTR = blockDirection == LTR;
- const InlineFlowBox* rightLineBox = isBlockLTR ? rootBox->nextLineBox() : rootBox->prevLineBox();
- if (rightLineBox)
- return rightLineBox->firstLeafChild();
-
- const RootInlineBox* rightRootInlineBox = isBlockLTR ? nextRootInlineBox(box) :
- previousRootInlineBox(box);
- return rightRootInlineBox ? rightRootInlineBox->firstLeafChild() : 0;
-}
-
-static VisiblePosition leftWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection)
-{
- VisiblePosition wordBreak;
- for (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = leftInlineBox(adjacentBox, blockDirection)) {
- if (blockDirection == LTR) {
- if (adjacentBox->isLeftToRightDirection())
- wordBreak = previousWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
- else
- wordBreak = nextWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
- } else
- wordBreak = visuallyLastWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection);
- if (wordBreak.isNotNull())
- return wordBreak;
- }
- return VisiblePosition();
-}
-
-static VisiblePosition rightWordBoundary(const InlineBox* box, int offset, TextDirection blockDirection)
-{
-
- VisiblePosition wordBreak;
- for (const InlineBox* adjacentBox = box; adjacentBox; adjacentBox = rightInlineBox(adjacentBox, blockDirection)) {
- if (blockDirection == RTL) {
- if (adjacentBox->isLeftToRightDirection())
- wordBreak = nextWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
- else
- wordBreak = previousWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset);
- } else
- wordBreak = visuallyLastWordBoundaryInBox(adjacentBox, adjacentBox == box ? offset : invalidOffset, blockDirection);
- if (!wordBreak.isNull())
- return wordBreak;
- }
- return VisiblePosition();
-}
-
-static bool positionIsInBoxButNotOnBoundary(const VisiblePosition& wordBreak, const InlineBox* box)
-{
- int offsetOfWordBreak;
- return positionIsInBox(wordBreak, box, offsetOfWordBreak)
- && offsetOfWordBreak != box->caretMaxOffset() && offsetOfWordBreak != box->caretMinOffset();
-}
-
-static VisiblePosition leftWordPositionIgnoringEditingBoundary(const VisiblePosition& visiblePosition)
-{
- InlineBox* box;
- int offset;
- visiblePosition.getInlineBoxAndOffset(box, offset);
-
- if (!box)
- return VisiblePosition();
-
- TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
-
- // FIXME: If the box's directionality is the same as that of the enclosing block, when the offset is at the box boundary
- // and the direction is towards inside the box, do I still need to make it a special case? For example, a LTR box inside a LTR block,
- // when offset is at box's caretMinOffset and the direction is DirectionRight, should it be taken care as a general case?
- if (offset == box->caretLeftmostOffset())
- return leftWordBoundary(leftInlineBox(box, blockDirection), invalidOffset, blockDirection);
- if (offset == box->caretRightmostOffset())
- return leftWordBoundary(box, offset, blockDirection);
-
-
- VisiblePosition wordBreak;
- if (blockDirection == LTR) {
- if (box->direction() == blockDirection)
- wordBreak = previousBoundary(visiblePosition, previousWordPositionBoundary);
- else
- wordBreak = nextBoundary(visiblePosition, nextWordPositionBoundary);
- }
- if (wordBreak.isNotNull() && positionIsInBoxButNotOnBoundary(wordBreak, box))
- return wordBreak;
-
- WordBoundaryVector orderedWordBoundaries;
- collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
-
- int index = box->isLeftToRightDirection() ? greatestOffsetUnder(offset, blockDirection == LTR, orderedWordBoundaries)
- : smallestOffsetAbove(offset, blockDirection == RTL, orderedWordBoundaries);
- if (index >= 0)
- return orderedWordBoundaries[index].visiblePosition;
-
- return leftWordBoundary(leftInlineBox(box, blockDirection), invalidOffset, blockDirection);
-}
-
-static VisiblePosition rightWordPositionIgnoringEditingBoundary(const VisiblePosition& visiblePosition)
-{
- InlineBox* box;
- int offset;
- visiblePosition.getInlineBoxAndOffset(box, offset);
-
- if (!box)
- return VisiblePosition();
-
- TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
-
- if (offset == box->caretLeftmostOffset())
- return rightWordBoundary(box, offset, blockDirection);
- if (offset == box->caretRightmostOffset())
- return rightWordBoundary(rightInlineBox(box, blockDirection), invalidOffset, blockDirection);
-
- VisiblePosition wordBreak;
- if (blockDirection == RTL) {
- if (box->direction() == blockDirection)
- wordBreak = previousBoundary(visiblePosition, previousWordPositionBoundary);
- else
- wordBreak = nextBoundary(visiblePosition, nextWordPositionBoundary);
- }
- if (wordBreak.isNotNull() && positionIsInBoxButNotOnBoundary(wordBreak, box))
- return wordBreak;
-
- WordBoundaryVector orderedWordBoundaries;
- collectWordBreaksInBox(box, orderedWordBoundaries, blockDirection);
-
- int index = box->isLeftToRightDirection() ? smallestOffsetAbove(offset, blockDirection == LTR, orderedWordBoundaries)
- : greatestOffsetUnder(offset, blockDirection == RTL, orderedWordBoundaries);
- if (index >= 0)
- return orderedWordBoundaries[index].visiblePosition;
-
- return rightWordBoundary(rightInlineBox(box, blockDirection), invalidOffset, blockDirection);
-}
-
-VisiblePosition leftWordPosition(const VisiblePosition& visiblePosition)
-{
- if (visiblePosition.isNull())
- return VisiblePosition();
-
- VisiblePosition leftWordBreak = leftWordPositionIgnoringEditingBoundary(visiblePosition);
- leftWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(leftWordBreak);
-
- // FIXME: How should we handle a non-editable position?
- if (leftWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
- TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
- leftWordBreak = blockDirection == LTR ? startOfEditableContent(visiblePosition) : endOfEditableContent(visiblePosition);
- }
- return leftWordBreak;
-}
-
-VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition)
-{
- if (visiblePosition.isNull())
- return VisiblePosition();
-
- VisiblePosition rightWordBreak = rightWordPositionIgnoringEditingBoundary(visiblePosition);
- rightWordBreak = visiblePosition.honorEditingBoundaryAtOrBefore(rightWordBreak);
-
- // FIXME: How should we handle a non-editable position?
- if (rightWordBreak.isNull() && isEditablePosition(visiblePosition.deepEquivalent())) {
- TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
- rightWordBreak = blockDirection == LTR ? endOfEditableContent(visiblePosition) : startOfEditableContent(visiblePosition);
- }
- return rightWordBreak;
-}
-
}
diff --git a/Source/WebCore/editing/visible_units.h b/Source/WebCore/editing/visible_units.h
index 3ca4cdaac..c5c1eb653 100644
--- a/Source/WebCore/editing/visible_units.h
+++ b/Source/WebCore/editing/visible_units.h
@@ -26,6 +26,7 @@
#ifndef visible_units_h
#define visible_units_h
+#include "EditingBehaviorTypes.h"
#include "EditingBoundary.h"
#include "TextDirection.h"
@@ -41,8 +42,8 @@ VisiblePosition startOfWord(const VisiblePosition &, EWordSide = RightWordIfOnBo
VisiblePosition endOfWord(const VisiblePosition &, EWordSide = RightWordIfOnBoundary);
VisiblePosition previousWordPosition(const VisiblePosition &);
VisiblePosition nextWordPosition(const VisiblePosition &);
-VisiblePosition rightWordPosition(const VisiblePosition&);
-VisiblePosition leftWordPosition(const VisiblePosition&);
+VisiblePosition rightWordPosition(const VisiblePosition&, bool skipsSpaceWhenMovingRight);
+VisiblePosition leftWordPosition(const VisiblePosition&, bool skipsSpaceWhenMovingRight);
bool isStartOfWord(const VisiblePosition&);
// sentences