summaryrefslogtreecommitdiff
path: root/Source/WebCore/editing
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/editing')
-rw-r--r--Source/WebCore/editing/AlternativeTextController.cpp227
-rw-r--r--Source/WebCore/editing/AlternativeTextController.h43
-rw-r--r--Source/WebCore/editing/AppendNodeCommand.cpp37
-rw-r--r--Source/WebCore/editing/AppendNodeCommand.h23
-rw-r--r--Source/WebCore/editing/ApplyBlockElementCommand.cpp94
-rw-r--r--Source/WebCore/editing/ApplyBlockElementCommand.h15
-rw-r--r--Source/WebCore/editing/ApplyStyleCommand.cpp635
-rw-r--r--Source/WebCore/editing/ApplyStyleCommand.h42
-rw-r--r--Source/WebCore/editing/BreakBlockquoteCommand.cpp48
-rw-r--r--Source/WebCore/editing/BreakBlockquoteCommand.h15
-rw-r--r--Source/WebCore/editing/ClipboardAccessPolicy.h36
-rw-r--r--Source/WebCore/editing/CompositeEditCommand.cpp714
-rw-r--r--Source/WebCore/editing/CompositeEditCommand.h84
-rw-r--r--Source/WebCore/editing/CreateLinkCommand.cpp18
-rw-r--r--Source/WebCore/editing/CreateLinkCommand.h19
-rw-r--r--Source/WebCore/editing/DeleteButton.cpp63
-rw-r--r--Source/WebCore/editing/DeleteButtonController.cpp396
-rw-r--r--Source/WebCore/editing/DeleteButtonController.h103
-rw-r--r--Source/WebCore/editing/DeleteFromTextNodeCommand.cpp35
-rw-r--r--Source/WebCore/editing/DeleteFromTextNodeCommand.h26
-rw-r--r--Source/WebCore/editing/DeleteSelectionCommand.cpp288
-rw-r--r--Source/WebCore/editing/DeleteSelectionCommand.h33
-rw-r--r--Source/WebCore/editing/DictationAlternative.h11
-rw-r--r--Source/WebCore/editing/DictationCommand.cpp32
-rw-r--r--Source/WebCore/editing/DictationCommand.h18
-rw-r--r--Source/WebCore/editing/DictionaryPopupInfo.h46
-rw-r--r--Source/WebCore/editing/EditAction.h38
-rw-r--r--Source/WebCore/editing/EditCommand.cpp153
-rw-r--r--Source/WebCore/editing/EditCommand.h37
-rw-r--r--Source/WebCore/editing/EditingAllInOne.cpp87
-rw-r--r--Source/WebCore/editing/EditingBehavior.h26
-rw-r--r--Source/WebCore/editing/EditingBehaviorTypes.h8
-rw-r--r--Source/WebCore/editing/EditingBoundary.h11
-rw-r--r--Source/WebCore/editing/EditingStyle.cpp918
-rw-r--r--Source/WebCore/editing/EditingStyle.h123
-rw-r--r--Source/WebCore/editing/Editor.cpp1775
-rw-r--r--Source/WebCore/editing/Editor.h430
-rw-r--r--Source/WebCore/editing/EditorCommand.cpp369
-rw-r--r--Source/WebCore/editing/EditorDeleteAction.h14
-rw-r--r--Source/WebCore/editing/EditorInsertAction.h22
-rw-r--r--Source/WebCore/editing/FindOptions.h12
-rw-r--r--Source/WebCore/editing/FormatBlockCommand.cpp84
-rw-r--r--Source/WebCore/editing/FormatBlockCommand.h21
-rw-r--r--Source/WebCore/editing/FrameSelection.cpp1081
-rw-r--r--Source/WebCore/editing/FrameSelection.h205
-rw-r--r--Source/WebCore/editing/HTMLInterchange.cpp9
-rw-r--r--Source/WebCore/editing/HTMLInterchange.h11
-rw-r--r--Source/WebCore/editing/IndentOutdentCommand.cpp69
-rw-r--r--Source/WebCore/editing/IndentOutdentCommand.h24
-rw-r--r--Source/WebCore/editing/InsertIntoTextNodeCommand.cpp43
-rw-r--r--Source/WebCore/editing/InsertIntoTextNodeCommand.h33
-rw-r--r--Source/WebCore/editing/InsertLineBreakCommand.cpp80
-rw-r--r--Source/WebCore/editing/InsertLineBreakCommand.h17
-rw-r--r--Source/WebCore/editing/InsertListCommand.cpp109
-rw-r--r--Source/WebCore/editing/InsertListCommand.h30
-rw-r--r--Source/WebCore/editing/InsertNodeBeforeCommand.cpp30
-rw-r--r--Source/WebCore/editing/InsertNodeBeforeCommand.h28
-rw-r--r--Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp64
-rw-r--r--Source/WebCore/editing/InsertParagraphSeparatorCommand.h23
-rw-r--r--Source/WebCore/editing/InsertTextCommand.cpp50
-rw-r--r--Source/WebCore/editing/InsertTextCommand.h32
-rw-r--r--Source/WebCore/editing/MarkupAccumulator.cpp277
-rw-r--r--Source/WebCore/editing/MarkupAccumulator.h45
-rw-r--r--Source/WebCore/editing/MergeIdenticalElementsCommand.cpp33
-rw-r--r--Source/WebCore/editing/MergeIdenticalElementsCommand.h19
-rw-r--r--Source/WebCore/editing/ModifySelectionListLevel.cpp33
-rw-r--r--Source/WebCore/editing/ModifySelectionListLevel.h25
-rw-r--r--Source/WebCore/editing/MoveSelectionCommand.cpp29
-rw-r--r--Source/WebCore/editing/MoveSelectionCommand.h18
-rw-r--r--Source/WebCore/editing/RemoveCSSPropertyCommand.cpp9
-rw-r--r--Source/WebCore/editing/RemoveCSSPropertyCommand.h19
-rw-r--r--Source/WebCore/editing/RemoveFormatCommand.cpp61
-rw-r--r--Source/WebCore/editing/RemoveFormatCommand.h17
-rw-r--r--Source/WebCore/editing/RemoveNodeCommand.cpp28
-rw-r--r--Source/WebCore/editing/RemoveNodeCommand.h21
-rw-r--r--Source/WebCore/editing/RemoveNodePreservingChildrenCommand.cpp12
-rw-r--r--Source/WebCore/editing/RemoveNodePreservingChildrenCommand.h17
-rw-r--r--Source/WebCore/editing/RenderedPosition.cpp12
-rw-r--r--Source/WebCore/editing/RenderedPosition.h17
-rw-r--r--Source/WebCore/editing/ReplaceNodeWithSpanCommand.cpp35
-rw-r--r--Source/WebCore/editing/ReplaceNodeWithSpanCommand.h15
-rw-r--r--Source/WebCore/editing/ReplaceRangeWithTextCommand.cpp96
-rw-r--r--Source/WebCore/editing/ReplaceRangeWithTextCommand.h (renamed from Source/WebCore/editing/DeleteButton.h)44
-rw-r--r--Source/WebCore/editing/ReplaceSelectionCommand.cpp533
-rw-r--r--Source/WebCore/editing/ReplaceSelectionCommand.h46
-rw-r--r--Source/WebCore/editing/SelectionRectGatherer.cpp89
-rw-r--r--Source/WebCore/editing/SelectionRectGatherer.h75
-rw-r--r--Source/WebCore/editing/SetNodeAttributeCommand.cpp6
-rw-r--r--Source/WebCore/editing/SetNodeAttributeCommand.h19
-rw-r--r--Source/WebCore/editing/SetSelectionCommand.cpp8
-rw-r--r--Source/WebCore/editing/SetSelectionCommand.h19
-rw-r--r--Source/WebCore/editing/SimplifyMarkupCommand.cpp19
-rw-r--r--Source/WebCore/editing/SimplifyMarkupCommand.h15
-rw-r--r--Source/WebCore/editing/SmartReplace.cpp11
-rw-r--r--Source/WebCore/editing/SmartReplace.h11
-rw-r--r--Source/WebCore/editing/SmartReplaceCF.cpp73
-rw-r--r--Source/WebCore/editing/SpellChecker.cpp57
-rw-r--r--Source/WebCore/editing/SpellChecker.h23
-rw-r--r--Source/WebCore/editing/SpellingCorrectionCommand.cpp59
-rw-r--r--Source/WebCore/editing/SpellingCorrectionCommand.h23
-rw-r--r--Source/WebCore/editing/SplitElementCommand.cpp40
-rw-r--r--Source/WebCore/editing/SplitElementCommand.h21
-rw-r--r--Source/WebCore/editing/SplitTextNodeCommand.cpp23
-rw-r--r--Source/WebCore/editing/SplitTextNodeCommand.h21
-rw-r--r--Source/WebCore/editing/SplitTextNodeContainingElementCommand.cpp10
-rw-r--r--Source/WebCore/editing/SplitTextNodeContainingElementCommand.h15
-rw-r--r--Source/WebCore/editing/SurroundingText.h55
-rw-r--r--Source/WebCore/editing/TextAffinity.h37
-rw-r--r--Source/WebCore/editing/TextCheckingHelper.cpp359
-rw-r--r--Source/WebCore/editing/TextCheckingHelper.h27
-rw-r--r--Source/WebCore/editing/TextGranularity.h15
-rw-r--r--Source/WebCore/editing/TextInsertionBaseCommand.cpp24
-rw-r--r--Source/WebCore/editing/TextInsertionBaseCommand.h11
-rw-r--r--Source/WebCore/editing/TextIterator.cpp1702
-rw-r--r--Source/WebCore/editing/TextIterator.h381
-rw-r--r--Source/WebCore/editing/TextIteratorBehavior.h66
-rw-r--r--Source/WebCore/editing/TypingCommand.cpp352
-rw-r--r--Source/WebCore/editing/TypingCommand.h73
-rw-r--r--Source/WebCore/editing/UndoStep.h7
-rw-r--r--Source/WebCore/editing/UnlinkCommand.cpp6
-rw-r--r--Source/WebCore/editing/UnlinkCommand.h17
-rw-r--r--Source/WebCore/editing/VisiblePosition.cpp193
-rw-r--r--Source/WebCore/editing/VisiblePosition.h55
-rw-r--r--Source/WebCore/editing/VisibleSelection.cpp121
-rw-r--r--Source/WebCore/editing/VisibleSelection.h45
-rw-r--r--Source/WebCore/editing/VisibleUnits.cpp902
-rw-r--r--Source/WebCore/editing/VisibleUnits.h118
-rw-r--r--Source/WebCore/editing/WrapContentsInDummySpanCommand.cpp40
-rw-r--r--Source/WebCore/editing/WrapContentsInDummySpanCommand.h25
-rw-r--r--Source/WebCore/editing/WritingDirection.h4
-rw-r--r--Source/WebCore/editing/atk/FrameSelectionAtk.cpp40
-rw-r--r--Source/WebCore/editing/gtk/EditorGtk.cpp151
-rw-r--r--Source/WebCore/editing/htmlediting.cpp1116
-rw-r--r--Source/WebCore/editing/htmlediting.h248
-rw-r--r--Source/WebCore/editing/markup.cpp483
-rw-r--r--Source/WebCore/editing/markup.h38
136 files changed, 9957 insertions, 8019 deletions
diff --git a/Source/WebCore/editing/AlternativeTextController.cpp b/Source/WebCore/editing/AlternativeTextController.cpp
index 31ca46ff2..15312a541 100644
--- a/Source/WebCore/editing/AlternativeTextController.cpp
+++ b/Source/WebCore/editing/AlternativeTextController.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -32,26 +32,28 @@
#include "Editor.h"
#include "Element.h"
#include "Event.h"
-#include "ExceptionCodePlaceholder.h"
#include "FloatQuad.h"
#include "Frame.h"
#include "FrameView.h"
#include "Page.h"
+#include "RenderedDocumentMarker.h"
#include "SpellingCorrectionCommand.h"
#include "TextCheckerClient.h"
#include "TextCheckingHelper.h"
#include "TextEvent.h"
+#include "TextIterator.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
#include "markup.h"
+#include <wtf/NeverDestroyed.h>
namespace WebCore {
class AutocorrectionAlternativeDetails : public AlternativeTextDetails {
public:
- static PassRefPtr<AutocorrectionAlternativeDetails> create(const String& replacementString)
+ static Ref<AutocorrectionAlternativeDetails> create(const String& replacementString)
{
- return adoptRef(new AutocorrectionAlternativeDetails(replacementString));
+ return adoptRef(*new AutocorrectionAlternativeDetails(replacementString));
}
const String& replacementString() const { return m_replacementString; }
@@ -65,9 +67,9 @@ private:
class DictationAlternativeDetails : public AlternativeTextDetails {
public:
- static PassRefPtr<DictationAlternativeDetails> create(uint64_t dictationContext)
+ static Ref<DictationAlternativeDetails> create(uint64_t dictationContext)
{
- return adoptRef(new DictationAlternativeDetails(dictationContext));
+ return adoptRef(*new DictationAlternativeDetails(dictationContext));
}
uint64_t dictationContext() const { return m_dictationContext; }
@@ -84,35 +86,35 @@ private:
static const Vector<DocumentMarker::MarkerType>& markerTypesForAutocorrection()
{
- DEFINE_STATIC_LOCAL(Vector<DocumentMarker::MarkerType>, markerTypesForAutoCorrection, ());
- if (markerTypesForAutoCorrection.isEmpty()) {
- markerTypesForAutoCorrection.append(DocumentMarker::Replacement);
- markerTypesForAutoCorrection.append(DocumentMarker::CorrectionIndicator);
- markerTypesForAutoCorrection.append(DocumentMarker::SpellCheckingExemption);
- markerTypesForAutoCorrection.append(DocumentMarker::Autocorrected);
+ static NeverDestroyed<Vector<DocumentMarker::MarkerType>> markerTypesForAutoCorrection;
+ if (markerTypesForAutoCorrection.get().isEmpty()) {
+ markerTypesForAutoCorrection.get().append(DocumentMarker::Replacement);
+ markerTypesForAutoCorrection.get().append(DocumentMarker::CorrectionIndicator);
+ markerTypesForAutoCorrection.get().append(DocumentMarker::SpellCheckingExemption);
+ markerTypesForAutoCorrection.get().append(DocumentMarker::Autocorrected);
}
return markerTypesForAutoCorrection;
}
static const Vector<DocumentMarker::MarkerType>& markerTypesForReplacement()
{
- DEFINE_STATIC_LOCAL(Vector<DocumentMarker::MarkerType>, markerTypesForReplacement, ());
- if (markerTypesForReplacement.isEmpty()) {
- markerTypesForReplacement.append(DocumentMarker::Replacement);
- markerTypesForReplacement.append(DocumentMarker::SpellCheckingExemption);
+ static NeverDestroyed<Vector<DocumentMarker::MarkerType>> markerTypesForReplacement;
+ if (markerTypesForReplacement.get().isEmpty()) {
+ markerTypesForReplacement.get().append(DocumentMarker::Replacement);
+ markerTypesForReplacement.get().append(DocumentMarker::SpellCheckingExemption);
}
return markerTypesForReplacement;
}
static const Vector<DocumentMarker::MarkerType>& markerTypesForAppliedDictationAlternative()
{
- DEFINE_STATIC_LOCAL(Vector<DocumentMarker::MarkerType>, markerTypesForAppliedDictationAlternative, ());
- if (markerTypesForAppliedDictationAlternative.isEmpty())
- markerTypesForAppliedDictationAlternative.append(DocumentMarker::SpellCheckingExemption);
+ static NeverDestroyed<Vector<DocumentMarker::MarkerType>> markerTypesForAppliedDictationAlternative;
+ if (markerTypesForAppliedDictationAlternative.get().isEmpty())
+ markerTypesForAppliedDictationAlternative.get().append(DocumentMarker::SpellCheckingExemption);
return markerTypesForAppliedDictationAlternative;
}
-static bool markersHaveIdenticalDescription(const Vector<DocumentMarker*>& markers)
+static bool markersHaveIdenticalDescription(const Vector<RenderedDocumentMarker*>& markers)
{
if (markers.isEmpty())
return true;
@@ -126,7 +128,7 @@ static bool markersHaveIdenticalDescription(const Vector<DocumentMarker*>& marke
}
AlternativeTextController::AlternativeTextController(Frame& frame)
- : m_timer(this, &AlternativeTextController::timerFired)
+ : m_timer(*this, &AlternativeTextController::timerFired)
, m_frame(frame)
{
}
@@ -144,7 +146,7 @@ void AlternativeTextController::startAlternativeTextUITimer(AlternativeTextType
// If type is PanelTypeReversion, then the new range has been set. So we shouldn't clear it.
if (type == AlternativeTextTypeCorrection)
- m_alternativeTextInfo.rangeWithAlternative.clear();
+ m_alternativeTextInfo.rangeWithAlternative = nullptr;
m_alternativeTextInfo.type = type;
m_timer.startOneShot(correctionPanelTimerInterval);
}
@@ -152,7 +154,7 @@ void AlternativeTextController::startAlternativeTextUITimer(AlternativeTextType
void AlternativeTextController::stopAlternativeTextUITimer()
{
m_timer.stop();
- m_alternativeTextInfo.rangeWithAlternative.clear();
+ m_alternativeTextInfo.rangeWithAlternative = nullptr;
}
void AlternativeTextController::stopPendingCorrection(const VisibleSelection& oldSelection)
@@ -180,7 +182,7 @@ void AlternativeTextController::applyPendingCorrection(const VisibleSelection& s
if (doApplyCorrection)
handleAlternativeTextUIResult(dismissSoon(ReasonForDismissingAlternativeTextAccepted));
else
- m_alternativeTextInfo.rangeWithAlternative.clear();
+ m_alternativeTextInfo.rangeWithAlternative = nullptr;
}
bool AlternativeTextController::hasPendingCorrection() const
@@ -240,10 +242,7 @@ void AlternativeTextController::applyAlternativeTextToRange(const Range* range,
if (!range)
return;
- ExceptionCode ec = 0;
- RefPtr<Range> paragraphRangeContainingCorrection = range->cloneRange(ec);
- if (ec)
- return;
+ RefPtr<Range> paragraphRangeContainingCorrection = range->cloneRange();
setStart(paragraphRangeContainingCorrection.get(), startOfParagraph(range->startPosition()));
setEnd(paragraphRangeContainingCorrection.get(), endOfParagraph(range->endPosition()));
@@ -254,25 +253,28 @@ void AlternativeTextController::applyAlternativeTextToRange(const Range* range,
// 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 rangeWithAlternative.
- RefPtr<Range> correctionStartOffsetInParagraphAsRange = Range::create(paragraphRangeContainingCorrection->startContainer(ec)->document(), paragraphRangeContainingCorrection->startPosition(), paragraphRangeContainingCorrection->startPosition());
- if (ec)
- return;
+ RefPtr<Range> correctionStartOffsetInParagraphAsRange = Range::create(paragraphRangeContainingCorrection->startContainer().document(), paragraphRangeContainingCorrection->startPosition(), paragraphRangeContainingCorrection->startPosition());
- Position startPositionOfrangeWithAlternative = range->startPosition();
- correctionStartOffsetInParagraphAsRange->setEnd(startPositionOfrangeWithAlternative.containerNode(), startPositionOfrangeWithAlternative.computeOffsetInContainerNode(), ec);
- if (ec)
+ Position startPositionOfRangeWithAlternative = range->startPosition();
+ if (!startPositionOfRangeWithAlternative.containerNode())
+ return;
+ auto setEndResult = correctionStartOffsetInParagraphAsRange->setEnd(*startPositionOfRangeWithAlternative.containerNode(), startPositionOfRangeWithAlternative.computeOffsetInContainerNode());
+ if (setEndResult.hasException())
return;
// Take note of the location of autocorrection so that we can add marker after the replacement took place.
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> rangeWithAlternative = range->cloneRange(ec);
-
- int paragraphStartIndex = TextIterator::rangeLength(Range::create(*m_frame.document(), m_frame.document(), 0, paragraphRangeContainingCorrection.get()->startContainer(), paragraphRangeContainingCorrection.get()->startOffset()).get());
- applyCommand(SpellingCorrectionCommand::create(rangeWithAlternative, alternative));
+ Ref<Range> rangeWithAlternative = range->cloneRange();
+
+ ContainerNode& rootNode = paragraphRangeContainingCorrection.get()->startContainer().treeScope().rootNode();
+ int paragraphStartIndex = TextIterator::rangeLength(Range::create(rootNode.document(), &rootNode, 0, &paragraphRangeContainingCorrection->startContainer(), paragraphRangeContainingCorrection->startOffset()).ptr());
+ applyCommand(SpellingCorrectionCommand::create(rangeWithAlternative.ptr(), alternative));
// Recalculate pragraphRangeContainingCorrection, since SpellingCorrectionCommand modified the DOM, such that the original paragraphRangeContainingCorrection is no longer valid. Radar: 10305315 Bugzilla: 89526
- paragraphRangeContainingCorrection = TextIterator::rangeFromLocationAndLength(m_frame.document(), paragraphStartIndex, correctionStartOffsetInParagraph + alternative.length());
+ paragraphRangeContainingCorrection = TextIterator::rangeFromLocationAndLength(&rootNode, paragraphStartIndex, correctionStartOffsetInParagraph + alternative.length());
+ if (!paragraphRangeContainingCorrection)
+ return;
setEnd(paragraphRangeContainingCorrection.get(), m_frame.selection().selection().start());
RefPtr<Range> replacementRange = TextIterator::subrange(paragraphRangeContainingCorrection.get(), correctionStartOffsetInParagraph, alternative.length());
@@ -282,10 +284,10 @@ void AlternativeTextController::applyAlternativeTextToRange(const Range* range,
if (newText != alternative)
return;
- DocumentMarkerController& markers = replacementRange->startContainer()->document().markers();
- size_t size = markerTypesToAdd.size();
- for (size_t i = 0; i < size; ++i)
- markers.addMarker(replacementRange.get(), markerTypesToAdd[i], markerDescriptionForAppliedAlternativeText(alternativeType, markerTypesToAdd[i]));
+ DocumentMarkerController& markers = replacementRange->startContainer().document().markers();
+
+ for (auto& markerType : markerTypesToAdd)
+ markers.addMarker(replacementRange.get(), markerType, markerDescriptionForAppliedAlternativeText(alternativeType, markerType));
}
bool AlternativeTextController::applyAutocorrectionBeforeTypingIfAppropriate()
@@ -312,9 +314,11 @@ bool AlternativeTextController::applyAutocorrectionBeforeTypingIfAppropriate()
void AlternativeTextController::respondToUnappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction)
{
if (AlternativeTextClient* client = alternativeTextClient())
- client->recordAutocorrectionResponse(AutocorrectionReverted, corrected, correction);
+ client->recordAutocorrectionResponse(AutocorrectionResponse::Reverted, corrected, correction);
+
+ Ref<Frame> protector(m_frame);
m_frame.document()->updateLayout();
- m_frame.selection().setSelection(selectionOfCorrected, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | FrameSelection::SpellCorrectionTriggered);
+ m_frame.selection().setSelection(selectionOfCorrected, FrameSelection::defaultSetSelectionOptions() | FrameSelection::SpellCorrectionTriggered);
RefPtr<Range> range = Range::create(*m_frame.document(), m_frame.selection().selection().start(), m_frame.selection().selection().end());
DocumentMarkerController& markers = m_frame.document()->markers();
@@ -323,7 +327,7 @@ void AlternativeTextController::respondToUnappliedSpellCorrection(const VisibleS
markers.addMarker(range.get(), DocumentMarker::SpellCheckingExemption);
}
-void AlternativeTextController::timerFired(Timer<AlternativeTextController>&)
+void AlternativeTextController::timerFired()
{
m_isDismissedByEditing = false;
switch (m_alternativeTextInfo.type) {
@@ -336,13 +340,13 @@ void AlternativeTextController::timerFired(Timer<AlternativeTextController>&)
}
break;
case AlternativeTextTypeReversion: {
- if (!m_alternativeTextInfo.rangeWithAlternative)
+ auto* details = static_cast<const AutocorrectionAlternativeDetails*>(m_alternativeTextInfo.details.get());
+ if (!m_alternativeTextInfo.rangeWithAlternative || !details || details->replacementString().isEmpty())
break;
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>());
}
@@ -353,9 +357,9 @@ void AlternativeTextController::timerFired(Timer<AlternativeTextController>&)
break;
String paragraphText = plainText(TextCheckingParagraph(m_alternativeTextInfo.rangeWithAlternative).paragraphRange().get());
Vector<String> suggestions;
- textChecker()->getGuessesForWord(m_alternativeTextInfo.originalText, paragraphText, suggestions);
+ textChecker()->getGuessesForWord(m_alternativeTextInfo.originalText, paragraphText, m_frame.selection().selection(), suggestions);
if (suggestions.isEmpty()) {
- m_alternativeTextInfo.rangeWithAlternative.clear();
+ m_alternativeTextInfo.rangeWithAlternative = nullptr;
break;
}
String topSuggestion = suggestions.first();
@@ -405,7 +409,7 @@ void AlternativeTextController::handleAlternativeTextUIResult(const String& resu
if (result.length())
applyAlternativeTextToRange(rangeWithAlternative, result, m_alternativeTextInfo.type, markerTypesForAutocorrection());
else if (!m_isDismissedByEditing)
- rangeWithAlternative->startContainer()->document().markers().addMarker(rangeWithAlternative, DocumentMarker::RejectedCorrection, m_alternativeTextInfo.originalText);
+ rangeWithAlternative->startContainer().document().markers().addMarker(rangeWithAlternative, DocumentMarker::RejectedCorrection, m_alternativeTextInfo.originalText);
break;
case AlternativeTextTypeReversion:
case AlternativeTextTypeSpellingSuggestions:
@@ -418,7 +422,7 @@ void AlternativeTextController::handleAlternativeTextUIResult(const String& resu
break;
}
- m_alternativeTextInfo.rangeWithAlternative.clear();
+ m_alternativeTextInfo.rangeWithAlternative = nullptr;
}
bool AlternativeTextController::isAutomaticSpellingCorrectionEnabled()
@@ -432,15 +436,14 @@ FloatRect AlternativeTextController::rootViewRectForRange(const Range* range) co
if (!view)
return FloatRect();
Vector<FloatQuad> textQuads;
- range->textQuads(textQuads);
+ range->absoluteTextQuads(textQuads);
FloatRect boundingRect;
- size_t size = textQuads.size();
- for (size_t i = 0; i < size; ++i)
- boundingRect.unite(textQuads[i].boundingBox());
+ for (auto& textQuad : textQuads)
+ boundingRect.unite(textQuad.boundingBox());
return view->contentsToRootView(IntRect(boundingRect));
}
-void AlternativeTextController::respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options)
+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
@@ -466,14 +469,9 @@ void AlternativeTextController::respondToChangedSelection(const VisibleSelection
return;
Node* node = position.containerNode();
- Vector<DocumentMarker*> markers = node->document().markers().markersFor(node);
- size_t markerCount = markers.size();
- for (size_t i = 0; i < markerCount; ++i) {
- const DocumentMarker* marker = markers[i];
- if (!marker)
- continue;
-
- if (respondToMarkerAtEndOfWord(*marker, position, options))
+ for (auto* marker : node->document().markers().markersFor(node)) {
+ ASSERT(marker);
+ if (respondToMarkerAtEndOfWord(*marker, position))
break;
}
}
@@ -485,6 +483,8 @@ void AlternativeTextController::respondToAppliedEditing(CompositeEditCommand* co
markPrecedingWhitespaceForDeletedAutocorrectionAfterCommand(command);
m_originalStringForLastDeletedAutocorrection = String();
+
+ dismiss(ReasonForDismissingAlternativeTextIgnored);
}
void AlternativeTextController::respondToUnappliedEditing(EditCommandComposition* command)
@@ -506,39 +506,32 @@ AlternativeTextClient* AlternativeTextController::alternativeTextClient()
EditorClient* AlternativeTextController::editorClient()
{
- return m_frame.page() ? m_frame.page()->editorClient() : 0;
+ return m_frame.page() ? &m_frame.page()->editorClient() : nullptr;
}
TextCheckerClient* AlternativeTextController::textChecker()
{
if (EditorClient* owner = editorClient())
return owner->textChecker();
- return 0;
+ return nullptr;
}
-void AlternativeTextController::recordAutocorrectionResponseReversed(const String& replacedString, const String& replacementString)
+void AlternativeTextController::recordAutocorrectionResponse(AutocorrectionResponse response, const String& replacedString, PassRefPtr<Range> replacementRange)
{
- if (AlternativeTextClient* client = alternativeTextClient())
- client->recordAutocorrectionResponse(AutocorrectionReverted, replacedString, replacementString);
-}
-
-void AlternativeTextController::recordAutocorrectionResponseReversed(const String& replacedString, PassRefPtr<Range> replacementRange)
-{
- recordAutocorrectionResponseReversed(replacedString, plainText(replacementRange.get()));
+ if (auto client = alternativeTextClient())
+ client->recordAutocorrectionResponse(response, replacedString, plainText(replacementRange.get()));
}
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);
+ changedRange->startContainer().document().markers().removeMarkers(changedRange.get(), DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
+ changedRange->startContainer().document().markers().addMarker(changedRange.get(), DocumentMarker::SpellCheckingExemption);
}
void AlternativeTextController::markCorrection(PassRefPtr<Range> replacedRange, const String& replacedString)
{
- Vector<DocumentMarker::MarkerType> markerTypesToAdd = markerTypesForAutocorrection();
- DocumentMarkerController& markers = replacedRange->startContainer()->document().markers();
- for (size_t i = 0; i < markerTypesToAdd.size(); ++i) {
- DocumentMarker::MarkerType markerType = markerTypesToAdd[i];
+ DocumentMarkerController& markers = replacedRange->startContainer().document().markers();
+ for (auto& markerType : markerTypesForAutocorrection()) {
if (markerType == DocumentMarker::Replacement || markerType == DocumentMarker::Autocorrected)
markers.addMarker(replacedRange.get(), markerType, replacedString);
else
@@ -550,8 +543,8 @@ void AlternativeTextController::recordSpellcheckerResponseForModifiedCorrection(
{
if (!rangeOfCorrection)
return;
- DocumentMarkerController& markers = rangeOfCorrection->startContainer()->document().markers();
- Vector<DocumentMarker*> correctedOnceMarkers = markers.markersInRange(rangeOfCorrection, DocumentMarker::Autocorrected);
+ DocumentMarkerController& markers = rangeOfCorrection->startContainer().document().markers();
+ Vector<RenderedDocumentMarker*> correctedOnceMarkers = markers.markersInRange(rangeOfCorrection, DocumentMarker::Autocorrected);
if (correctedOnceMarkers.isEmpty())
return;
@@ -559,9 +552,9 @@ void AlternativeTextController::recordSpellcheckerResponseForModifiedCorrection(
// 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);
+ client->recordAutocorrectionResponse(AutocorrectionResponse::Reverted, corrected, correction);
else
- client->recordAutocorrectionResponse(AutocorrectionEdited, corrected, correction);
+ client->recordAutocorrectionResponse(AutocorrectionResponse::Edited, corrected, correction);
}
markers.removeMarkers(rangeOfCorrection, DocumentMarker::Autocorrected, DocumentMarkerController::RemovePartiallyOverlappingMarker);
@@ -585,7 +578,7 @@ void AlternativeTextController::markPrecedingWhitespaceForDeletedAutocorrectionA
RefPtr<Range> precedingCharacterRange = Range::create(*m_frame.document(), precedingCharacterPosition, endOfSelection);
String string = plainText(precedingCharacterRange.get());
- if (string.isEmpty() || !isWhitespace(string[string.length() - 1]))
+ if (string.isEmpty() || !deprecatedIsEditingWhitespace(string[string.length() - 1]))
return;
// Mark this whitespace to indicate we have deleted an autocorrection following this
@@ -606,14 +599,16 @@ bool AlternativeTextController::processMarkersOnTextToBeReplacedByResult(const T
if (markerController.hasMarkers(rangeWithAlternative, DocumentMarker::RejectedCorrection))
return false;
+ if (markerController.hasMarkers(rangeWithAlternative, DocumentMarker::AcceptedCandidate))
+ return false;
+
Position beginningOfRange = rangeWithAlternative->startPosition();
Position precedingCharacterPosition = beginningOfRange.previous();
RefPtr<Range> precedingCharacterRange = Range::create(*m_frame.document(), precedingCharacterPosition, beginningOfRange);
- Vector<DocumentMarker*> markers = markerController.markersInRange(precedingCharacterRange.get(), DocumentMarker::DeletedAutocorrection);
-
- for (size_t i = 0; i < markers.size(); ++i) {
- if (markers[i]->description() == stringToBeReplaced)
+ Vector<RenderedDocumentMarker*> markers = markerController.markersInRange(precedingCharacterRange.get(), DocumentMarker::DeletedAutocorrection);
+ for (const auto* marker : markers) {
+ if (marker->description() == stringToBeReplaced)
return false;
}
@@ -625,10 +620,8 @@ bool AlternativeTextController::shouldStartTimerFor(const WebCore::DocumentMarke
return (((marker.type() == DocumentMarker::Replacement && !marker.description().isNull()) || marker.type() == DocumentMarker::Spelling || marker.type() == DocumentMarker::DictationAlternatives) && static_cast<int>(marker.endOffset()) == endOffset);
}
-bool AlternativeTextController::respondToMarkerAtEndOfWord(const DocumentMarker& marker, const Position& endOfWordPosition, FrameSelection::SetSelectionOptions options)
+bool AlternativeTextController::respondToMarkerAtEndOfWord(const DocumentMarker& marker, const Position& endOfWordPosition)
{
- if (options & FrameSelection::DictationTriggered)
- return false;
if (!shouldStartTimerFor(marker, endOfWordPosition.offsetInContainerNode()))
return false;
Node* node = endOfWordPosition.containerNode();
@@ -642,7 +635,7 @@ bool AlternativeTextController::respondToMarkerAtEndOfWord(const DocumentMarker&
switch (marker.type()) {
case DocumentMarker::Spelling:
m_alternativeTextInfo.rangeWithAlternative = wordRange;
- m_alternativeTextInfo.details = AutocorrectionAlternativeDetails::create("");
+ m_alternativeTextInfo.details = AutocorrectionAlternativeDetails::create(emptyString());
startAlternativeTextUITimer(AlternativeTextTypeSpellingSuggestions);
break;
case DocumentMarker::Replacement:
@@ -651,13 +644,13 @@ bool AlternativeTextController::respondToMarkerAtEndOfWord(const DocumentMarker&
startAlternativeTextUITimer(AlternativeTextTypeReversion);
break;
case DocumentMarker::DictationAlternatives: {
- const DictationMarkerDetails* markerDetails = static_cast<const DictationMarkerDetails*>(marker.details());
- if (!markerDetails)
+ if (!WTF::holds_alternative<DocumentMarker::DictationData>(marker.data()))
return false;
- if (currentWord != markerDetails->originalText())
+ auto& markerData = WTF::get<DocumentMarker::DictationData>(marker.data());
+ if (currentWord != markerData.originalText)
return false;
m_alternativeTextInfo.rangeWithAlternative = wordRange;
- m_alternativeTextInfo.details = DictationAlternativeDetails::create(markerDetails->dictationContext());
+ m_alternativeTextInfo.details = DictationAlternativeDetails::create(markerData.context);
startAlternativeTextUITimer(AlternativeTextTypeDictationAlternatives);
}
break;
@@ -673,7 +666,7 @@ String AlternativeTextController::markerDescriptionForAppliedAlternativeText(Alt
if (alternativeTextType != AlternativeTextTypeReversion && alternativeTextType != AlternativeTextTypeDictationAlternatives && (markerType == DocumentMarker::Replacement || markerType == DocumentMarker::Autocorrected))
return m_alternativeTextInfo.originalText;
- return "";
+ return emptyString();
}
#endif
@@ -691,32 +684,30 @@ bool AlternativeTextController::insertDictatedText(const String& text, const Vec
if (FrameView* view = m_frame.view())
view->disableLayerFlushThrottlingTemporarilyForInteraction();
- RefPtr<TextEvent> event = TextEvent::createForDictation(m_frame.document()->domWindow(), text, dictationAlternatives);
+ Ref<TextEvent> event = TextEvent::createForDictation(m_frame.document()->domWindow(), text, dictationAlternatives);
event->setUnderlyingEvent(triggeringEvent);
- target->dispatchEvent(event, IGNORE_EXCEPTION);
+ target->dispatchEvent(event);
return event->defaultHandled();
}
-void AlternativeTextController::removeDictationAlternativesForMarker(const DocumentMarker* marker)
+void AlternativeTextController::removeDictationAlternativesForMarker(const DocumentMarker& marker)
{
#if USE(DICTATION_ALTERNATIVES)
- DictationMarkerDetails* details = static_cast<DictationMarkerDetails*>(marker->details());
- if (AlternativeTextClient* client = alternativeTextClient())
- client->removeDictationAlternatives(details->dictationContext());
+ ASSERT(WTF::holds_alternative<DocumentMarker::DictationData>(marker.data()));
+ if (auto* client = alternativeTextClient())
+ client->removeDictationAlternatives(WTF::get<DocumentMarker::DictationData>(marker.data()).context);
#else
UNUSED_PARAM(marker);
#endif
}
-Vector<String> AlternativeTextController::dictationAlternativesForMarker(const DocumentMarker* marker)
+Vector<String> AlternativeTextController::dictationAlternativesForMarker(const DocumentMarker& marker)
{
#if USE(DICTATION_ALTERNATIVES)
- ASSERT(marker->type() == DocumentMarker::DictationAlternatives);
- if (AlternativeTextClient* client = alternativeTextClient()) {
- DictationMarkerDetails* details = static_cast<DictationMarkerDetails*>(marker->details());
- return client->dictationAlternatives(details->dictationContext());
- }
+ ASSERT(marker.type() == DocumentMarker::DictationAlternatives);
+ if (auto* client = alternativeTextClient())
+ return client->dictationAlternatives(WTF::get<DocumentMarker::DictationData>(marker.data()).context);
return Vector<String>();
#else
UNUSED_PARAM(marker);
@@ -729,12 +720,12 @@ void AlternativeTextController::applyDictationAlternative(const String& alternat
#if USE(DICTATION_ALTERNATIVES)
Editor& editor = m_frame.editor();
RefPtr<Range> selection = editor.selectedRange();
- if (!selection || !editor.shouldInsertText(alternativeString, selection.get(), EditorInsertActionPasted))
+ if (!selection || !editor.shouldInsertText(alternativeString, selection.get(), EditorInsertAction::Pasted))
return;
- DocumentMarkerController& markers = selection->startContainer()->document().markers();
- Vector<DocumentMarker*> dictationAlternativesMarkers = markers.markersInRange(selection.get(), DocumentMarker::DictationAlternatives);
- for (size_t i = 0; i < dictationAlternativesMarkers.size(); ++i)
- removeDictationAlternativesForMarker(dictationAlternativesMarkers[i]);
+ DocumentMarkerController& markers = selection->startContainer().document().markers();
+ Vector<RenderedDocumentMarker*> dictationAlternativesMarkers = markers.markersInRange(selection.get(), DocumentMarker::DictationAlternatives);
+ for (auto* marker : dictationAlternativesMarkers)
+ removeDictationAlternativesForMarker(*marker);
applyAlternativeTextToRange(selection.get(), alternativeString, AlternativeTextTypeDictationAlternatives, markerTypesForAppliedDictationAlternative());
#else
diff --git a/Source/WebCore/editing/AlternativeTextController.h b/Source/WebCore/editing/AlternativeTextController.h
index c843f603d..c583503ea 100644
--- a/Source/WebCore/editing/AlternativeTextController.h
+++ b/Source/WebCore/editing/AlternativeTextController.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AlternativeTextController_h
-#define AlternativeTextController_h
+#pragma once
#include "AlternativeTextClient.h"
#include "DocumentMarker.h"
@@ -48,7 +47,6 @@ struct DictationAlternative;
class AlternativeTextDetails : public RefCounted<AlternativeTextDetails> {
public:
- AlternativeTextDetails() { }
virtual ~AlternativeTextDetails() { }
};
@@ -60,24 +58,6 @@ struct AlternativeTextInfo {
RefPtr<AlternativeTextDetails> details;
};
-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)
@@ -108,7 +88,7 @@ public:
void respondToUnappliedSpellCorrection(const VisibleSelection&, const String& corrected, const String& correction) UNLESS_ENABLED({ UNUSED_PARAM(corrected); UNUSED_PARAM(correction); })
void respondToAppliedEditing(CompositeEditCommand*) UNLESS_ENABLED({ })
void respondToUnappliedEditing(EditCommandComposition*) UNLESS_ENABLED({ })
- void respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions) UNLESS_ENABLED({ UNUSED_PARAM(oldSelection); })
+ void respondToChangedSelection(const VisibleSelection& oldSelection) UNLESS_ENABLED({ UNUSED_PARAM(oldSelection); })
void stopPendingCorrection(const VisibleSelection& oldSelection) UNLESS_ENABLED({ UNUSED_PARAM(oldSelection); })
void applyPendingCorrection(const VisibleSelection& selectionAfterTyping) UNLESS_ENABLED({ UNUSED_PARAM(selectionAfterTyping); })
@@ -121,7 +101,7 @@ public:
bool isAutomaticSpellingCorrectionEnabled() UNLESS_ENABLED({ return false; })
bool shouldRemoveMarkersUponEditing();
- void recordAutocorrectionResponseReversed(const String& replacedString, PassRefPtr<Range> replacementRange) UNLESS_ENABLED({ UNUSED_PARAM(replacedString); UNUSED_PARAM(replacementRange); })
+ void recordAutocorrectionResponse(AutocorrectionResponse, 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); })
@@ -130,21 +110,20 @@ public:
void deletedAutocorrectionAtPosition(const Position&, const String& originalString) UNLESS_ENABLED({ UNUSED_PARAM(originalString); })
bool insertDictatedText(const String&, const Vector<DictationAlternative>&, Event*);
- void removeDictationAlternativesForMarker(const DocumentMarker*);
- Vector<String> dictationAlternativesForMarker(const DocumentMarker*);
+ void removeDictationAlternativesForMarker(const DocumentMarker&);
+ Vector<String> dictationAlternativesForMarker(const DocumentMarker&);
void applyDictationAlternative(const String& alternativeString);
private:
#if USE(AUTOCORRECTION_PANEL)
String dismissSoon(ReasonForDismissingAlternativeText);
void applyAlternativeTextToRange(const Range*, const String& alternative, AlternativeTextType, const Vector<DocumentMarker::MarkerType>&);
- void timerFired(Timer<AlternativeTextController>&);
- void recordAutocorrectionResponseReversed(const String& replacedString, const String& replacementString);
+ void timerFired();
void recordSpellcheckerResponseForModifiedCorrection(Range* rangeOfCorrection, const String& corrected, const String& correction);
String markerDescriptionForAppliedAlternativeText(AlternativeTextType, DocumentMarker::MarkerType);
bool shouldStartTimerFor(const DocumentMarker&, int endOffset) const;
- bool respondToMarkerAtEndOfWord(const DocumentMarker&, const Position& endOfWordPosition, FrameSelection::SetSelectionOptions);
+ bool respondToMarkerAtEndOfWord(const DocumentMarker&, const Position& endOfWordPosition);
AlternativeTextClient* alternativeTextClient();
EditorClient* editorClient();
@@ -153,7 +132,7 @@ private:
FloatRect rootViewRectForRange(const Range*) const;
void markPrecedingWhitespaceForDeletedAutocorrectionAfterCommand(EditCommand*);
- Timer<AlternativeTextController> m_timer;
+ Timer m_timer;
AlternativeTextInfo m_alternativeTextInfo;
bool m_isDismissedByEditing;
@@ -176,5 +155,3 @@ inline bool AlternativeTextController::shouldRemoveMarkersUponEditing()
}
} // namespace WebCore
-
-#endif // AlternativeTextController_h
diff --git a/Source/WebCore/editing/AppendNodeCommand.cpp b/Source/WebCore/editing/AppendNodeCommand.cpp
index 25c596a45..a8fb92c3c 100644
--- a/Source/WebCore/editing/AppendNodeCommand.cpp
+++ b/Source/WebCore/editing/AppendNodeCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -28,63 +28,44 @@
#include "AXObjectCache.h"
#include "Document.h"
-#include "ExceptionCodePlaceholder.h"
#include "RenderElement.h"
+#include "Text.h"
#include "htmlediting.h"
namespace WebCore {
-AppendNodeCommand::AppendNodeCommand(PassRefPtr<ContainerNode> parent, PassRefPtr<Node> node)
- : SimpleEditCommand(parent->document())
+AppendNodeCommand::AppendNodeCommand(PassRefPtr<ContainerNode> parent, Ref<Node>&& node, EditAction editingAction)
+ : SimpleEditCommand(parent->document(), editingAction)
, m_parent(parent)
- , m_node(node)
+ , m_node(WTFMove(node))
{
ASSERT(m_parent);
- ASSERT(m_node);
ASSERT(!m_node->parentNode());
ASSERT(m_parent->hasEditableStyle() || !m_parent->renderer());
}
-static void sendAXTextChangedIgnoringLineBreaks(Node* node, AXObjectCache::AXTextChange textChange)
-{
- String nodeValue = node->nodeValue();
- // Don't consider linebreaks in this command
- if (nodeValue == "\n")
- return;
-
- if (AXObjectCache* cache = node->document().existingAXObjectCache())
- cache->nodeTextChangeNotification(node, textChange, 0, nodeValue);
-}
-
void AppendNodeCommand::doApply()
{
if (!m_parent->hasEditableStyle() && m_parent->renderer())
return;
- m_parent->appendChild(m_node.get(), IGNORE_EXCEPTION);
-
- if (AXObjectCache::accessibilityEnabled())
- sendAXTextChangedIgnoringLineBreaks(m_node.get(), AXObjectCache::AXTextInserted);
+ m_parent->appendChild(m_node);
}
void AppendNodeCommand::doUnapply()
{
if (!m_parent->hasEditableStyle())
return;
-
- // Need to notify this before actually deleting the text
- if (AXObjectCache::accessibilityEnabled())
- sendAXTextChangedIgnoringLineBreaks(m_node.get(), AXObjectCache::AXTextDeleted);
- m_node->remove(IGNORE_EXCEPTION);
+ m_node->remove();
}
#ifndef NDEBUG
void AppendNodeCommand::getNodesInCommand(HashSet<Node*>& nodes)
{
addNodeAndDescendants(m_parent.get(), nodes);
- addNodeAndDescendants(m_node.get(), nodes);
+ addNodeAndDescendants(m_node.ptr(), nodes);
}
#endif
diff --git a/Source/WebCore/editing/AppendNodeCommand.h b/Source/WebCore/editing/AppendNodeCommand.h
index 397ae2b5a..5d58be7a8 100644
--- a/Source/WebCore/editing/AppendNodeCommand.h
+++ b/Source/WebCore/editing/AppendNodeCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef AppendNodeCommand_h
-#define AppendNodeCommand_h
+#pragma once
#include "EditCommand.h"
@@ -32,25 +31,23 @@ namespace WebCore {
class AppendNodeCommand : public SimpleEditCommand {
public:
- static PassRefPtr<AppendNodeCommand> create(PassRefPtr<ContainerNode> parent, PassRefPtr<Node> node)
+ static Ref<AppendNodeCommand> create(PassRefPtr<ContainerNode> parent, Ref<Node>&& node, EditAction editingAction)
{
- return adoptRef(new AppendNodeCommand(parent, node));
+ return adoptRef(*new AppendNodeCommand(parent, WTFMove(node), editingAction));
}
private:
- AppendNodeCommand(PassRefPtr<ContainerNode> parent, PassRefPtr<Node>);
+ AppendNodeCommand(PassRefPtr<ContainerNode> parent, Ref<Node>&&, EditAction);
- virtual void doApply() override;
- virtual void doUnapply() override;
+ void doApply() override;
+ void doUnapply() override;
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
RefPtr<ContainerNode> m_parent;
- RefPtr<Node> m_node;
+ Ref<Node> m_node;
};
} // namespace WebCore
-
-#endif // AppendNodeCommand_h
diff --git a/Source/WebCore/editing/ApplyBlockElementCommand.cpp b/Source/WebCore/editing/ApplyBlockElementCommand.cpp
index 2deb1b46d..6731880ea 100644
--- a/Source/WebCore/editing/ApplyBlockElementCommand.cpp
+++ b/Source/WebCore/editing/ApplyBlockElementCommand.cpp
@@ -27,12 +27,11 @@
#include "config.h"
#include "ApplyBlockElementCommand.h"
-#include "HTMLElement.h"
+#include "HTMLBRElement.h"
#include "HTMLNames.h"
#include "RenderElement.h"
#include "RenderStyle.h"
#include "Text.h"
-#include "TextIterator.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
@@ -71,8 +70,12 @@ void ApplyBlockElementCommand::doApply()
// FIXME: We paint the gap before some paragraphs that are indented with left
// margin/padding, but not others. We should make the gap painting more consistent and
// then use a left margin/padding rule here.
- if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd))
- setEndingSelection(VisibleSelection(visibleStart, visibleEnd.previous(CannotCrossEditingBoundary), endingSelection().isDirectional()));
+ if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) {
+ VisibleSelection newSelection(visibleStart, visibleEnd.previous(CannotCrossEditingBoundary), endingSelection().isDirectional());
+ if (newSelection.isNone())
+ return;
+ setEndingSelection(newSelection);
+ }
VisibleSelection selection = selectionForParagraphIteration(endingSelection());
VisiblePosition startOfSelection = selection.visibleStart();
@@ -94,6 +97,11 @@ void ApplyBlockElementCommand::doApply()
if (startScope == endScope && startIndex >= 0 && startIndex <= endIndex) {
VisiblePosition start(visiblePositionForIndex(startIndex, startScope.get()));
VisiblePosition end(visiblePositionForIndex(endIndex, endScope.get()));
+ // Work around the fact indexForVisiblePosition can return a larger index due to TextIterator
+ // using an extra newline to represent a large margin.
+ // FIXME: Add a new TextIteratorBehavior to suppress it.
+ if (start.isNotNull() && end.isNull())
+ end = lastPositionInNode(endScope.get());
if (start.isNotNull() && end.isNotNull())
setEndingSelection(VisibleSelection(start, end, endingSelection().isDirectional()));
}
@@ -104,12 +112,12 @@ void ApplyBlockElementCommand::formatSelection(const VisiblePosition& startOfSel
// Special case empty unsplittable elements because there's nothing to split
// and there's nothing to move.
Position start = startOfSelection.deepEquivalent().downstream();
- if (isAtUnsplittableElement(start)) {
- RefPtr<Element> blockquote = createBlockElement();
- insertNodeAt(blockquote, start);
- RefPtr<Element> placeholder = createBreakElement(document());
- appendNode(placeholder, blockquote);
- setEndingSelection(VisibleSelection(positionBeforeNode(placeholder.get()), DOWNSTREAM, endingSelection().isDirectional()));
+ if (isAtUnsplittableElement(start) && startOfParagraph(start) == endOfParagraph(endOfSelection)) {
+ auto blockquote = createBlockElement();
+ insertNodeAt(blockquote.copyRef(), start);
+ auto placeholder = HTMLBRElement::create(document());
+ appendNode(placeholder.copyRef(), WTFMove(blockquote));
+ setEndingSelection(VisibleSelection(positionBeforeNode(placeholder.ptr()), DOWNSTREAM, endingSelection().isDirectional()));
return;
}
@@ -127,25 +135,33 @@ void ApplyBlockElementCommand::formatSelection(const VisiblePosition& startOfSel
rangeForParagraphSplittingTextNodesIfNeeded(endOfCurrentParagraph, start, end);
endOfCurrentParagraph = end;
+ // FIXME: endOfParagraph can errornously return a position at the beginning of a block element
+ // when the position passed into endOfParagraph is at the beginning of a block.
+ // Work around this bug here because too much of the existing code depends on the current behavior of endOfParagraph.
+ if (start == end && startOfBlock(start) != endOfBlock(start) && !isEndOfBlock(end) && start == startOfParagraph(endOfBlock(start))) {
+ endOfCurrentParagraph = endOfBlock(end);
+ end = endOfCurrentParagraph.deepEquivalent();
+ }
+
Position afterEnd = end.next();
Node* enclosingCell = enclosingNodeOfType(start, &isTableCell);
- VisiblePosition endOfNextParagraph = endOfNextParagrahSplittingTextNodesIfNeeded(endOfCurrentParagraph, start, end);
+ VisiblePosition endOfNextParagraph = endOfNextParagraphSplittingTextNodesIfNeeded(endOfCurrentParagraph, start, end);
formatRange(start, end, m_endOfLastParagraph, blockquoteForNextIndent);
// Don't put the next paragraph in the blockquote we just created for this paragraph unless
// the next paragraph is in the same cell.
if (enclosingCell && enclosingCell != enclosingNodeOfType(endOfNextParagraph.deepEquivalent(), &isTableCell))
- blockquoteForNextIndent = 0;
+ blockquoteForNextIndent = nullptr;
// indentIntoBlockquote could move more than one paragraph if the paragraph
// is in a list item or a table. As a result, endAfterSelection could refer to a position
// no longer in the document.
- if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().anchorNode()->inDocument())
+ if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().anchorNode()->isConnected())
break;
// Sanity check: Make sure our moveParagraph calls didn't remove endOfNextParagraph.deepEquivalent().deprecatedNode()
// If somehow we did, return to prevent crashes.
- if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().anchorNode()->inDocument()) {
+ if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().anchorNode()->isConnected()) {
ASSERT_NOT_REACHED();
return;
}
@@ -157,29 +173,23 @@ static bool isNewLineAtPosition(const Position& position)
{
Node* textNode = position.containerNode();
int offset = position.offsetInContainerNode();
- if (!textNode || !textNode->isTextNode() || offset < 0 || offset >= textNode->maxCharacterOffset())
+ if (!is<Text>(textNode) || offset < 0 || offset >= textNode->maxCharacterOffset())
return false;
-
- ExceptionCode ec = 0;
- String textAtPosition = toText(textNode)->substringData(offset, 1, ec);
- if (ec)
- return false;
-
- return textAtPosition[0] == '\n';
+ return downcast<Text>(*textNode).data()[offset] == '\n';
}
-RenderStyle* ApplyBlockElementCommand::renderStyleOfEnclosingTextNode(const Position& position)
+const RenderStyle* ApplyBlockElementCommand::renderStyleOfEnclosingTextNode(const Position& position)
{
if (position.anchorType() != Position::PositionIsOffsetInAnchor
|| !position.containerNode()
|| !position.containerNode()->isTextNode())
- return 0;
+ return nullptr;
document().updateStyleIfNeeded();
RenderObject* renderer = position.containerNode()->renderer();
if (!renderer)
- return 0;
+ return nullptr;
return &renderer->style();
}
@@ -190,7 +200,7 @@ void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded(const
end = endOfCurrentParagraph.deepEquivalent();
bool isStartAndEndOnSameNode = false;
- if (RenderStyle* startStyle = renderStyleOfEnclosingTextNode(start)) {
+ if (auto* startStyle = renderStyleOfEnclosingTextNode(start)) {
isStartAndEndOnSameNode = renderStyleOfEnclosingTextNode(end) && start.containerNode() == end.containerNode();
bool isStartAndEndOfLastParagraphOnSameNode = renderStyleOfEnclosingTextNode(m_endOfLastParagraph) && start.containerNode() == m_endOfLastParagraph.containerNode();
@@ -215,7 +225,7 @@ void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded(const
}
}
- if (RenderStyle* endStyle = renderStyleOfEnclosingTextNode(end)) {
+ if (auto* endStyle = renderStyleOfEnclosingTextNode(end)) {
bool isEndAndEndOfLastParagraphOnSameNode = renderStyleOfEnclosingTextNode(m_endOfLastParagraph) && end.deprecatedNode() == m_endOfLastParagraph.deprecatedNode();
// Include \n at the end of line if we're at an empty paragraph
if (endStyle->preserveNewline() && start == end && end.offsetInContainerNode() < end.containerNode()->maxCharacterOffset()) {
@@ -226,8 +236,8 @@ void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded(const
m_endOfLastParagraph = end;
}
- // If end is in the middle of a text node, split.
- if (!endStyle->collapseWhiteSpace() && end.offsetInContainerNode() && end.offsetInContainerNode() < end.containerNode()->maxCharacterOffset()) {
+ // If end is in the middle of a text node and the text node is editable, split.
+ if (endStyle->userModify() != READ_ONLY && !endStyle->collapseWhiteSpace() && end.offsetInContainerNode() && end.offsetInContainerNode() < end.containerNode()->maxCharacterOffset()) {
RefPtr<Text> endContainer = end.containerText();
splitTextNode(endContainer, end.offsetInContainerNode());
if (isStartAndEndOnSameNode)
@@ -236,18 +246,18 @@ void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded(const
if (m_endOfLastParagraph.offsetInContainerNode() == end.offsetInContainerNode())
m_endOfLastParagraph = lastPositionInOrAfterNode(endContainer->previousSibling());
else
- m_endOfLastParagraph = Position(endContainer, m_endOfLastParagraph.offsetInContainerNode() - end.offsetInContainerNode());
+ m_endOfLastParagraph = Position(endContainer.get(), m_endOfLastParagraph.offsetInContainerNode() - end.offsetInContainerNode());
}
end = lastPositionInNode(endContainer->previousSibling());
}
}
}
-VisiblePosition ApplyBlockElementCommand::endOfNextParagrahSplittingTextNodesIfNeeded(VisiblePosition& endOfCurrentParagraph, Position& start, Position& end)
+VisiblePosition ApplyBlockElementCommand::endOfNextParagraphSplittingTextNodesIfNeeded(VisiblePosition& endOfCurrentParagraph, Position& start, Position& end)
{
VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
Position position = endOfNextParagraph.deepEquivalent();
- RenderStyle* style = renderStyleOfEnclosingTextNode(position);
+ auto* style = renderStyleOfEnclosingTextNode(position);
if (!style)
return endOfNextParagraph;
@@ -260,20 +270,20 @@ VisiblePosition ApplyBlockElementCommand::endOfNextParagrahSplittingTextNodesIfN
// Avoid this by splitting "\n"
splitTextNode(text, 1);
- if (text == start.containerNode() && text->previousSibling() && text->previousSibling()->isTextNode()) {
+ if (text == start.containerNode() && is<Text>(text->previousSibling())) {
ASSERT(start.offsetInContainerNode() < position.offsetInContainerNode());
- start = Position(toText(text->previousSibling()), start.offsetInContainerNode());
+ start = Position(downcast<Text>(text->previousSibling()), start.offsetInContainerNode());
}
- if (text == end.containerNode() && text->previousSibling() && text->previousSibling()->isTextNode()) {
+ if (text == end.containerNode() && is<Text>(text->previousSibling())) {
ASSERT(end.offsetInContainerNode() < position.offsetInContainerNode());
- end = Position(toText(text->previousSibling()), end.offsetInContainerNode());
+ end = Position(downcast<Text>(text->previousSibling()), end.offsetInContainerNode());
}
if (text == m_endOfLastParagraph.containerNode()) {
if (m_endOfLastParagraph.offsetInContainerNode() < position.offsetInContainerNode()) {
// We can only fix endOfLastParagraph if the previous node was still text and hasn't been modified by script.
- if (text->previousSibling()->isTextNode()
- && static_cast<unsigned>(m_endOfLastParagraph.offsetInContainerNode()) <= toText(text->previousSibling())->length())
- m_endOfLastParagraph = Position(toText(text->previousSibling()), m_endOfLastParagraph.offsetInContainerNode());
+ if (is<Text>(*text->previousSibling())
+ && static_cast<unsigned>(m_endOfLastParagraph.offsetInContainerNode()) <= downcast<Text>(text->previousSibling())->length())
+ m_endOfLastParagraph = Position(downcast<Text>(text->previousSibling()), m_endOfLastParagraph.offsetInContainerNode());
} else
m_endOfLastParagraph = Position(text.get(), m_endOfLastParagraph.offsetInContainerNode() - 1);
}
@@ -281,12 +291,12 @@ VisiblePosition ApplyBlockElementCommand::endOfNextParagrahSplittingTextNodesIfN
return Position(text.get(), position.offsetInContainerNode() - 1);
}
-PassRefPtr<Element> ApplyBlockElementCommand::createBlockElement()
+Ref<HTMLElement> ApplyBlockElementCommand::createBlockElement()
{
- RefPtr<Element> element = createHTMLElement(document(), m_tagName);
+ auto element = createHTMLElement(document(), m_tagName);
if (m_inlineStyle.length())
element->setAttribute(styleAttr, m_inlineStyle);
- return element.release();
+ return element;
}
}
diff --git a/Source/WebCore/editing/ApplyBlockElementCommand.h b/Source/WebCore/editing/ApplyBlockElementCommand.h
index cd9986d9c..6cc6df497 100644
--- a/Source/WebCore/editing/ApplyBlockElementCommand.h
+++ b/Source/WebCore/editing/ApplyBlockElementCommand.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ApplyBlockElementCommand_h
-#define ApplyBlockElementCommand_h
+#pragma once
#include "CompositeEditCommand.h"
#include "QualifiedName.h"
@@ -42,21 +41,19 @@ protected:
ApplyBlockElementCommand(Document&, const QualifiedName& tagName);
virtual void formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection);
- PassRefPtr<Element> createBlockElement();
+ Ref<HTMLElement> createBlockElement();
const QualifiedName tagName() const { return m_tagName; }
private:
- virtual void doApply() override;
+ void doApply() override;
virtual void formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<Element>&) = 0;
- RenderStyle* renderStyleOfEnclosingTextNode(const Position&);
+ const RenderStyle* renderStyleOfEnclosingTextNode(const Position&);
void rangeForParagraphSplittingTextNodesIfNeeded(const VisiblePosition&, Position&, Position&);
- VisiblePosition endOfNextParagrahSplittingTextNodesIfNeeded(VisiblePosition&, Position&, Position&);
+ VisiblePosition endOfNextParagraphSplittingTextNodesIfNeeded(VisiblePosition&, Position&, Position&);
QualifiedName m_tagName;
AtomicString m_inlineStyle;
Position m_endOfLastParagraph;
};
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/ApplyStyleCommand.cpp b/Source/WebCore/editing/ApplyStyleCommand.cpp
index 9e1bfe92f..efc1f8839 100644
--- a/Source/WebCore/editing/ApplyStyleCommand.cpp
+++ b/Source/WebCore/editing/ApplyStyleCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -34,8 +34,10 @@
#include "ElementIterator.h"
#include "Frame.h"
#include "HTMLFontElement.h"
+#include "HTMLIFrameElement.h"
#include "HTMLInterchange.h"
#include "HTMLNames.h"
+#include "HTMLSpanElement.h"
#include "NodeList.h"
#include "NodeTraversal.h"
#include "RenderObject.h"
@@ -43,9 +45,11 @@
#include "StyleProperties.h"
#include "StyleResolver.h"
#include "Text.h"
+#include "TextIterator.h"
#include "TextNodeTraversal.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
+#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/StringBuilder.h>
@@ -55,22 +59,21 @@ using namespace HTMLNames;
static int toIdentifier(PassRefPtr<CSSValue> value)
{
- return (value && value->isPrimitiveValue()) ? static_pointer_cast<CSSPrimitiveValue>(value)->getValueID() : 0;
+ return (value && value->isPrimitiveValue()) ? static_pointer_cast<CSSPrimitiveValue>(value)->valueID() : 0;
}
static String& styleSpanClassString()
{
- DEFINE_STATIC_LOCAL(String, styleSpanClassString, ((AppleStyleSpanClass)));
+ static NeverDestroyed<String> styleSpanClassString(AppleStyleSpanClass);
return styleSpanClassString;
}
-bool isLegacyAppleStyleSpan(const Node *node)
+bool isLegacyAppleStyleSpan(const Node* node)
{
- if (!node || !node->isHTMLElement())
+ if (!is<HTMLSpanElement>(node))
return false;
- const HTMLElement* elem = toHTMLElement(node);
- return elem->hasLocalName(spanAttr) && elem->getAttribute(classAttr) == styleSpanClassString();
+ return downcast<HTMLSpanElement>(*node).attributeWithoutSynchronization(classAttr) == styleSpanClassString();
}
static bool hasNoAttributeOrOnlyStyleAttribute(const StyledElement* element, ShouldStyleAttributeBeEmpty shouldStyleAttributeBeEmpty)
@@ -79,7 +82,7 @@ static bool hasNoAttributeOrOnlyStyleAttribute(const StyledElement* element, Sho
return true;
unsigned matchedAttributes = 0;
- if (element->getAttribute(classAttr) == styleSpanClassString())
+ if (element->attributeWithoutSynchronization(classAttr) == styleSpanClassString())
matchedAttributes++;
if (element->hasAttribute(styleAttr) && (shouldStyleAttributeBeEmpty == AllowNonEmptyStyleAttribute
|| !element->inlineStyle() || element->inlineStyle()->isEmpty()))
@@ -91,87 +94,77 @@ static bool hasNoAttributeOrOnlyStyleAttribute(const StyledElement* element, Sho
bool isStyleSpanOrSpanWithOnlyStyleAttribute(const Element* element)
{
- if (!element || !element->hasTagName(spanTag))
+ if (!is<HTMLSpanElement>(element))
return false;
- return hasNoAttributeOrOnlyStyleAttribute(toHTMLElement(element), AllowNonEmptyStyleAttribute);
+ return hasNoAttributeOrOnlyStyleAttribute(downcast<HTMLSpanElement>(element), AllowNonEmptyStyleAttribute);
}
static inline bool isSpanWithoutAttributesOrUnstyledStyleSpan(const Element* element)
{
- if (!element || !element->isHTMLElement() || !element->hasTagName(spanTag))
+ if (!is<HTMLSpanElement>(element))
return false;
- return hasNoAttributeOrOnlyStyleAttribute(toHTMLElement(element), StyleAttributeShouldBeEmpty);
+ return hasNoAttributeOrOnlyStyleAttribute(downcast<HTMLSpanElement>(element), StyleAttributeShouldBeEmpty);
}
bool isEmptyFontTag(const Element* element, ShouldStyleAttributeBeEmpty shouldStyleAttributeBeEmpty)
{
- if (!element || !element->hasTagName(fontTag))
+ if (!is<HTMLFontElement>(element))
return false;
- return hasNoAttributeOrOnlyStyleAttribute(toHTMLElement(element), shouldStyleAttributeBeEmpty);
+ return hasNoAttributeOrOnlyStyleAttribute(downcast<HTMLFontElement>(element), shouldStyleAttributeBeEmpty);
}
-static PassRefPtr<Element> createFontElement(Document& document)
+static RefPtr<HTMLElement> createFontElement(Document& document)
{
return createHTMLElement(document, fontTag);
}
-PassRefPtr<HTMLElement> createStyleSpanElement(Document& document)
+RefPtr<HTMLElement> createStyleSpanElement(Document& document)
{
return createHTMLElement(document, spanTag);
}
ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* style, EditAction editingAction, EPropertyLevel propertyLevel)
- : CompositeEditCommand(document)
+ : CompositeEditCommand(document, editingAction)
, m_style(style->copy())
- , m_editingAction(editingAction)
, m_propertyLevel(propertyLevel)
, m_start(endingSelection().start().downstream())
, m_end(endingSelection().end().upstream())
, m_useEndingSelection(true)
- , m_styledInlineElement(0)
, m_removeOnly(false)
- , m_isInlineElementToRemoveFunction(0)
{
}
ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* style, const Position& start, const Position& end, EditAction editingAction, EPropertyLevel propertyLevel)
- : CompositeEditCommand(document)
+ : CompositeEditCommand(document, editingAction)
, m_style(style->copy())
- , m_editingAction(editingAction)
, m_propertyLevel(propertyLevel)
, m_start(start)
, m_end(end)
, m_useEndingSelection(false)
- , m_styledInlineElement(0)
, m_removeOnly(false)
- , m_isInlineElementToRemoveFunction(0)
{
}
ApplyStyleCommand::ApplyStyleCommand(PassRefPtr<Element> element, bool removeOnly, EditAction editingAction)
- : CompositeEditCommand(element->document())
+ : CompositeEditCommand(element->document(), editingAction)
, m_style(EditingStyle::create())
- , m_editingAction(editingAction)
, m_propertyLevel(PropertyDefault)
, m_start(endingSelection().start().downstream())
, m_end(endingSelection().end().upstream())
, m_useEndingSelection(true)
, m_styledInlineElement(element)
, m_removeOnly(removeOnly)
- , m_isInlineElementToRemoveFunction(0)
{
}
ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* style, IsInlineElementToRemoveFunction isInlineElementToRemoveFunction, EditAction editingAction)
- : CompositeEditCommand(document)
+ : CompositeEditCommand(document, editingAction)
, m_style(style->copy())
- , m_editingAction(editingAction)
, m_propertyLevel(PropertyDefault)
, m_start(endingSelection().start().downstream())
, m_end(endingSelection().end().upstream())
, m_useEndingSelection(true)
- , m_styledInlineElement(0)
, m_removeOnly(true)
, m_isInlineElementToRemoveFunction(isInlineElementToRemoveFunction)
{
@@ -228,11 +221,6 @@ void ApplyStyleCommand::doApply()
}
}
-EditAction ApplyStyleCommand::editingAction() const
-{
- return m_editingAction;
-}
-
void ApplyStyleCommand::applyBlockStyle(EditingStyle *style)
{
// update document layout once before removing styles
@@ -255,18 +243,10 @@ void ApplyStyleCommand::applyBlockStyle(EditingStyle *style)
if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || visibleEnd.isOrphan())
return;
-#if !PLATFORM(IOS)
- // Save and restore the selection endpoints using their indices in the document, since
-#else
// Save and restore the selection endpoints using their indices in the editable root, since
-#endif
// addBlockStyleIfNeeded may moveParagraphs, which can remove these endpoints.
// Calculate start and end indices from the start of the tree that they're in.
-#if !PLATFORM(IOS)
- Node* scope = highestAncestor(visibleStart.deepEquivalent().deprecatedNode());
-#else
- Node* scope = highestEditableRoot(visibleStart.deepEquivalent());
-#endif
+ auto* scope = highestEditableRoot(visibleStart.deepEquivalent());
if (!scope)
return;
@@ -282,18 +262,18 @@ void ApplyStyleCommand::applyBlockStyle(EditingStyle *style)
VisiblePosition beyondEnd(endOfParagraph(visibleEnd).next());
while (paragraphStart.isNotNull() && paragraphStart != beyondEnd) {
StyleChange styleChange(style, paragraphStart.deepEquivalent());
- if (styleChange.cssStyle().length() || m_removeOnly) {
+ if (styleChange.cssStyle() || m_removeOnly) {
RefPtr<Node> block = enclosingBlock(paragraphStart.deepEquivalent().deprecatedNode());
if (!m_removeOnly) {
RefPtr<Node> newBlock = moveParagraphContentsToNewBlockIfNecessary(paragraphStart.deepEquivalent());
if (newBlock)
block = newBlock;
}
- ASSERT(!block || block->isHTMLElement());
- if (block && block->isHTMLElement()) {
- removeCSSStyle(style, toHTMLElement(block.get()));
+ ASSERT(!block || is<HTMLElement>(*block));
+ if (is<HTMLElement>(block.get())) {
+ removeCSSStyle(style, downcast<HTMLElement>(block.get()));
if (!m_removeOnly)
- addBlockStyle(styleChange, toHTMLElement(block.get()));
+ addBlockStyle(styleChange, downcast<HTMLElement>(block.get()));
}
if (nextParagraphStart.isOrphan())
@@ -304,8 +284,8 @@ void ApplyStyleCommand::applyBlockStyle(EditingStyle *style)
nextParagraphStart = endOfParagraph(paragraphStart).next();
}
- startRange = TextIterator::rangeFromLocationAndLength(toContainerNode(scope), startIndex, 0, true);
- endRange = TextIterator::rangeFromLocationAndLength(toContainerNode(scope), endIndex, 0, true);
+ startRange = TextIterator::rangeFromLocationAndLength(scope, startIndex, 0, true);
+ endRange = TextIterator::rangeFromLocationAndLength(scope, endIndex, 0, true);
if (startRange && endRange)
updateStartEnd(startRange->startPosition(), endRange->startPosition());
}
@@ -333,7 +313,7 @@ void ApplyStyleCommand::applyRelativeFontStyleChange(EditingStyle* style)
}
// Join up any adjacent text nodes.
- if (start.deprecatedNode()->isTextNode()) {
+ if (is<Text>(*start.deprecatedNode())) {
joinChildTextNodes(start.deprecatedNode()->parentNode(), start, end);
start = startPosition();
end = endPosition();
@@ -342,7 +322,7 @@ void ApplyStyleCommand::applyRelativeFontStyleChange(EditingStyle* style)
if (start.isNull() || end.isNull())
return;
- if (end.deprecatedNode()->isTextNode() && start.deprecatedNode()->parentNode() != end.deprecatedNode()->parentNode()) {
+ if (is<Text>(*end.deprecatedNode()) && start.deprecatedNode()->parentNode() != end.deprecatedNode()->parentNode()) {
joinChildTextNodes(end.deprecatedNode()->parentNode(), start, end);
start = startPosition();
end = endPosition();
@@ -358,49 +338,69 @@ void ApplyStyleCommand::applyRelativeFontStyleChange(EditingStyle* style)
end = endPosition();
}
+ if (start.isNull() || end.isNull())
+ return;
+
if (isValidCaretPositionInTextNode(end)) {
splitTextAtEnd(start, end);
start = startPosition();
end = endPosition();
}
+ if (start.isNull() || end.isNull())
+ return;
+
// Calculate loop end point.
// If the end node is before the start node (can only happen if the end node is
// an ancestor of the start node), we gather nodes up to the next sibling of the end node
- Node *beyondEnd;
- if (start.deprecatedNode()->isDescendantOf(end.deprecatedNode()))
- beyondEnd = NodeTraversal::nextSkippingChildren(end.deprecatedNode());
+ Node* beyondEnd;
+ ASSERT(start.deprecatedNode());
+ ASSERT(end.deprecatedNode());
+ if (start.deprecatedNode()->isDescendantOf(*end.deprecatedNode()))
+ beyondEnd = NodeTraversal::nextSkippingChildren(*end.deprecatedNode());
else
- beyondEnd = NodeTraversal::next(end.deprecatedNode());
+ beyondEnd = NodeTraversal::next(*end.deprecatedNode());
start = start.upstream(); // Move upstream to ensure we do not add redundant spans.
Node* startNode = start.deprecatedNode();
- if (startNode->isTextNode() && start.deprecatedEditingOffset() >= caretMaxOffset(startNode)) // Move out of text node if range does not include its characters.
- startNode = NodeTraversal::next(startNode);
+
+ // Make sure we're not already at the end or the next NodeTraversal::next() will traverse past it.
+ if (startNode == beyondEnd)
+ return;
+
+ if (is<Text>(*startNode) && start.deprecatedEditingOffset() >= caretMaxOffset(*startNode)) {
+ // Move out of text node if range does not include its characters.
+ startNode = NodeTraversal::next(*startNode);
+ if (!startNode)
+ return;
+ }
// Store away font size before making any changes to the document.
// This ensures that changes to one node won't effect another.
HashMap<Node*, float> startingFontSizes;
- for (Node *node = startNode; node != beyondEnd; node = NodeTraversal::next(node))
+ for (Node* node = startNode; node != beyondEnd; node = NodeTraversal::next(*node)) {
+ ASSERT(node);
startingFontSizes.set(node, computedFontSize(node));
+ }
// These spans were added by us. If empty after font size changes, they can be removed.
Vector<RefPtr<HTMLElement>> unstyledSpans;
- Node* lastStyledNode = 0;
- for (Node* node = startNode; node != beyondEnd; node = NodeTraversal::next(node)) {
+ Node* lastStyledNode = nullptr;
+ for (Node* node = startNode; node != beyondEnd; node = NodeTraversal::next(*node)) {
+ ASSERT(node);
RefPtr<HTMLElement> element;
- if (node->isHTMLElement()) {
+ if (is<HTMLElement>(*node)) {
// Only work on fully selected nodes.
- if (!nodeFullySelected(node, start, end))
+ if (!nodeFullySelected(downcast<HTMLElement>(*node), start, end))
continue;
- element = toHTMLElement(node);
- } else if (node->isTextNode() && node->renderer() && node->parentNode() != lastStyledNode) {
+ element = &downcast<HTMLElement>(*node);
+ } else if (is<Text>(*node) && node->renderer() && node->parentNode() != lastStyledNode) {
// Last styled node was not parent node of this text node, but we wish to style this
// text node. To make this possible, add a style span to surround this text node.
- RefPtr<HTMLElement> span = createStyleSpanElement(document());
+ auto span = createStyleSpanElement(document());
surroundNodeRangeWithElement(node, node, span.get());
- element = span.release();
+ element = WTFMove(span);
} else {
// Only handle HTML elements and text nodes.
continue;
@@ -416,27 +416,26 @@ void ApplyStyleCommand::applyRelativeFontStyleChange(EditingStyle* style)
currentFontSize = computedFontSize(node);
}
if (currentFontSize != desiredFontSize) {
- inlineStyle->setProperty(CSSPropertyFontSize, cssValuePool().createValue(desiredFontSize, CSSPrimitiveValue::CSS_PX), false);
+ inlineStyle->setProperty(CSSPropertyFontSize, CSSValuePool::singleton().createValue(desiredFontSize, CSSPrimitiveValue::CSS_PX), false);
setNodeAttribute(element.get(), styleAttr, inlineStyle->asText());
}
if (inlineStyle->isEmpty()) {
removeNodeAttribute(element.get(), styleAttr);
if (isSpanWithoutAttributesOrUnstyledStyleSpan(element.get()))
- unstyledSpans.append(element.release());
+ unstyledSpans.append(WTFMove(element));
}
}
- size_t size = unstyledSpans.size();
- for (size_t i = 0; i < size; ++i)
- removeNodePreservingChildren(unstyledSpans[i].get());
+ for (auto& unstyledSpan : unstyledSpans)
+ removeNodePreservingChildren(unstyledSpan.get());
}
static ContainerNode* dummySpanAncestorForNode(const Node* node)
{
- while (node && (!node->isElementNode() || !isStyleSpanOrSpanWithOnlyStyleAttribute(toElement(node))))
+ while (node && (!is<Element>(*node) || !isStyleSpanOrSpanWithOnlyStyleAttribute(downcast<Element>(node))))
node = node->parentNode();
- return node ? node->parentNode() : 0;
+ return node ? node->parentNode() : nullptr;
}
void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(ContainerNode* dummySpanAncestor)
@@ -454,20 +453,21 @@ void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(ContainerNode* dummySpanA
if (isSpanWithoutAttributesOrUnstyledStyleSpan(&child))
toRemove.append(&child);
}
- for (unsigned i = 0; i < toRemove.size(); ++i)
- removeNodePreservingChildren(toRemove[i]);
+
+ for (auto& element : toRemove)
+ removeNodePreservingChildren(element);
}
HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool before, WritingDirection allowedDirection)
{
// We are allowed to leave the highest ancestor with unicode-bidi unsplit if it is unicode-bidi: embed and direction: allowedDirection.
// In that case, we return the unsplit ancestor. Otherwise, we return 0.
- Node* block = enclosingBlock(node);
- if (!block)
+ Element* block = enclosingBlock(node);
+ if (!block || block == node)
return 0;
- Node* highestAncestorWithUnicodeBidi = 0;
- Node* nextHighestAncestorWithUnicodeBidi = 0;
+ Node* highestAncestorWithUnicodeBidi = nullptr;
+ Node* nextHighestAncestorWithUnicodeBidi = nullptr;
int highestAncestorUnicodeBidi = 0;
for (Node* n = node->parentNode(); n != block; n = n->parentNode()) {
int unicodeBidi = toIdentifier(ComputedStyleExtractor(n).propertyValue(CSSPropertyUnicodeBidi));
@@ -481,25 +481,25 @@ HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool b
if (!highestAncestorWithUnicodeBidi)
return 0;
- HTMLElement* unsplitAncestor = 0;
+ HTMLElement* unsplitAncestor = nullptr;
WritingDirection highestAncestorDirection;
if (allowedDirection != NaturalWritingDirection
&& highestAncestorUnicodeBidi != CSSValueBidiOverride
- && highestAncestorWithUnicodeBidi->isHTMLElement()
+ && is<HTMLElement>(*highestAncestorWithUnicodeBidi)
&& EditingStyle::create(highestAncestorWithUnicodeBidi, EditingStyle::AllProperties)->textDirection(highestAncestorDirection)
&& highestAncestorDirection == allowedDirection) {
if (!nextHighestAncestorWithUnicodeBidi)
- return toHTMLElement(highestAncestorWithUnicodeBidi);
+ return downcast<HTMLElement>(highestAncestorWithUnicodeBidi);
- unsplitAncestor = toHTMLElement(highestAncestorWithUnicodeBidi);
+ unsplitAncestor = downcast<HTMLElement>(highestAncestorWithUnicodeBidi);
highestAncestorWithUnicodeBidi = nextHighestAncestorWithUnicodeBidi;
}
// Split every ancestor through highest ancestor with embedding.
RefPtr<Node> currentNode = node;
while (currentNode) {
- RefPtr<Element> parent = toElement(currentNode->parentNode());
+ RefPtr<Element> parent = downcast<Element>(currentNode->parentNode());
if (before ? currentNode->previousSibling() : currentNode->nextSibling())
splitElement(parent, before ? currentNode : currentNode->nextSibling());
if (parent == highestAncestorWithUnicodeBidi)
@@ -511,18 +511,18 @@ HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi(Node* node, bool b
void ApplyStyleCommand::removeEmbeddingUpToEnclosingBlock(Node* node, Node* unsplitAncestor)
{
- Node* block = enclosingBlock(node);
- if (!block)
+ Element* block = enclosingBlock(node);
+ if (!block || block == node)
return;
- Node* parent = 0;
- for (Node* n = node->parentNode(); n != block && n != unsplitAncestor; n = parent) {
- parent = n->parentNode();
- if (!n->isStyledElement())
+ Node* parent = nullptr;
+ for (Node* ancestor = node->parentNode(); ancestor != block && ancestor != unsplitAncestor; ancestor = parent) {
+ parent = ancestor->parentNode();
+ if (!is<StyledElement>(*ancestor))
continue;
- StyledElement* element = toStyledElement(n);
- int unicodeBidi = toIdentifier(ComputedStyleExtractor(element).propertyValue(CSSPropertyUnicodeBidi));
+ StyledElement& element = downcast<StyledElement>(*ancestor);
+ int unicodeBidi = toIdentifier(ComputedStyleExtractor(&element).propertyValue(CSSPropertyUnicodeBidi));
if (!unicodeBidi || unicodeBidi == CSSValueNormal)
continue;
@@ -530,17 +530,17 @@ void ApplyStyleCommand::removeEmbeddingUpToEnclosingBlock(Node* node, Node* unsp
// and all matching style rules in order to determine how to best set the unicode-bidi property to 'normal'.
// For now, it assumes that if the 'dir' attribute is present, then removing it will suffice, and
// otherwise it sets the property in the inline style declaration.
- if (element->hasAttribute(dirAttr)) {
+ if (element.hasAttributeWithoutSynchronization(dirAttr)) {
// FIXME: If this is a BDO element, we should probably just remove it if it has no
// other attributes, like we (should) do with B and I elements.
- removeNodeAttribute(element, dirAttr);
+ removeNodeAttribute(&element, dirAttr);
} else {
- RefPtr<MutableStyleProperties> inlineStyle = copyStyleOrCreateEmpty(element->inlineStyle());
+ RefPtr<MutableStyleProperties> inlineStyle = copyStyleOrCreateEmpty(element.inlineStyle());
inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal);
inlineStyle->removeProperty(CSSPropertyDirection);
- setNodeAttribute(element, styleAttr, inlineStyle->asText());
- if (isSpanWithoutAttributesOrUnstyledStyleSpan(element))
- removeNodePreservingChildren(element);
+ setNodeAttribute(&element, styleAttr, inlineStyle->asText());
+ if (isSpanWithoutAttributesOrUnstyledStyleSpan(&element))
+ removeNodePreservingChildren(&element);
}
}
}
@@ -557,8 +557,8 @@ static Node* highestEmbeddingAncestor(Node* startNode, Node* enclosingNode)
void ApplyStyleCommand::applyInlineStyle(EditingStyle* style)
{
- RefPtr<ContainerNode> startDummySpanAncestor = 0;
- RefPtr<ContainerNode> endDummySpanAncestor = 0;
+ RefPtr<ContainerNode> startDummySpanAncestor;
+ RefPtr<ContainerNode> endDummySpanAncestor;
// update document layout once before removing styles
// so that we avoid the expense of updating before each and every call
@@ -590,6 +590,9 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style)
startDummySpanAncestor = dummySpanAncestorForNode(start.deprecatedNode());
}
+ if (start.isNull() || end.isNull())
+ return;
+
// split the end node and containing element if the selection ends inside of it
bool splitEnd = isValidCaretPositionInTextNode(end);
if (splitEnd) {
@@ -602,6 +605,9 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style)
endDummySpanAncestor = dummySpanAncestorForNode(end.deprecatedNode());
}
+ if (start.isNull() || end.isNull())
+ return;
+
// Remove style from the selection.
// Use the upstream position of the start for removing style.
// This will ensure we remove all traces of the relevant styles from the selection
@@ -621,11 +627,11 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style)
// Avoid removing the dir attribute and the unicode-bidi and direction properties from the unsplit ancestors.
Position embeddingRemoveStart = removeStart;
- if (startUnsplitAncestor && nodeFullySelected(startUnsplitAncestor, removeStart, end))
+ if (startUnsplitAncestor && nodeFullySelected(*startUnsplitAncestor, removeStart, end))
embeddingRemoveStart = positionInParentAfterNode(startUnsplitAncestor);
Position embeddingRemoveEnd = end;
- if (endUnsplitAncestor && nodeFullySelected(endUnsplitAncestor, removeStart, end))
+ if (endUnsplitAncestor && nodeFullySelected(*endUnsplitAncestor, removeStart, end))
embeddingRemoveEnd = positionInParentBeforeNode(endUnsplitAncestor).downstream();
if (embeddingRemoveEnd != removeStart || embeddingRemoveEnd != end) {
@@ -654,6 +660,9 @@ void ApplyStyleCommand::applyInlineStyle(EditingStyle* style)
end = endPosition();
}
+ if (start.isNull() || end.isNull())
+ return;
+
// update document layout once before running the rest of the function
// so that we avoid the expense of updating before each and every call
// to check a computed style
@@ -692,42 +701,43 @@ void ApplyStyleCommand::fixRangeAndApplyInlineStyle(EditingStyle* style, const P
{
Node* startNode = start.deprecatedNode();
- if (start.deprecatedEditingOffset() >= caretMaxOffset(start.deprecatedNode())) {
- startNode = NodeTraversal::next(startNode);
+ if (start.deprecatedEditingOffset() >= caretMaxOffset(*startNode)) {
+ startNode = NodeTraversal::next(*startNode);
if (!startNode || comparePositions(end, firstPositionInOrBeforeNode(startNode)) < 0)
return;
}
Node* pastEndNode = end.deprecatedNode();
- if (end.deprecatedEditingOffset() >= caretMaxOffset(end.deprecatedNode()))
- pastEndNode = NodeTraversal::nextSkippingChildren(end.deprecatedNode());
+ if (end.deprecatedEditingOffset() >= caretMaxOffset(*pastEndNode))
+ pastEndNode = NodeTraversal::nextSkippingChildren(*pastEndNode);
// FIXME: Callers should perform this operation on a Range that includes the br
// if they want style applied to the empty line.
+ // FIXME: Should this be using startNode instead of start.deprecatedNode()?
if (start == end && start.deprecatedNode()->hasTagName(brTag))
- pastEndNode = NodeTraversal::next(start.deprecatedNode());
+ pastEndNode = NodeTraversal::next(*start.deprecatedNode());
// Start from the highest fully selected ancestor so that we can modify the fully selected node.
// e.g. When applying font-size: large on <font color="blue">hello</font>, we need to include the font element in our run
// to generate <font color="blue" size="4">hello</font> instead of <font color="blue"><font size="4">hello</font></font>
- RefPtr<Range> range = Range::create(startNode->document(), start, end);
- Element* editableRoot = startNode->rootEditableElement();
+ auto range = Range::create(startNode->document(), start, end);
+ auto* editableRoot = startNode->rootEditableElement();
if (startNode != editableRoot) {
- while (editableRoot && startNode->parentNode() != editableRoot && isNodeVisiblyContainedWithin(startNode->parentNode(), range.get()))
+ while (editableRoot && startNode->parentNode() != editableRoot && isNodeVisiblyContainedWithin(*startNode->parentNode(), range))
startNode = startNode->parentNode();
}
applyInlineStyleToNodeRange(style, startNode, pastEndNode);
}
-static bool containsNonEditableRegion(Node* node)
+static bool containsNonEditableRegion(Node& node)
{
- if (!node->hasEditableStyle())
+ if (!node.hasEditableStyle())
return true;
Node* sibling = NodeTraversal::nextSkippingChildren(node);
- for (Node* descendent = node->firstChild(); descendent && descendent != sibling; descendent = NodeTraversal::next(descendent)) {
- if (!descendent->hasEditableStyle())
+ for (Node* descendant = node.firstChild(); descendant && descendant != sibling; descendant = NodeTraversal::next(*descendant)) {
+ if (!descendant->hasEditableStyle())
return true;
}
@@ -745,7 +755,7 @@ struct InlineRunToApplyStyle {
bool startAndEndAreStillInDocument()
{
- return start && end && start->inDocument() && end->inDocument();
+ return start && end && start->isConnected() && end->isConnected();
}
RefPtr<Node> start;
@@ -766,35 +776,35 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, PassRef
Vector<InlineRunToApplyStyle> runs;
RefPtr<Node> node = startNode;
for (RefPtr<Node> next; node && node != pastEndNode; node = next) {
- next = NodeTraversal::next(node.get());
+ next = NodeTraversal::next(*node);
if (!node->renderer() || !node->hasEditableStyle())
continue;
- if (!node->hasRichlyEditableStyle() && node->isHTMLElement()) {
+ if (!node->hasRichlyEditableStyle() && is<HTMLElement>(*node)) {
// 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.get()))
+ if (pastEndNode && pastEndNode->isDescendantOf(*node))
break;
// Add to this element's inline style and skip over its contents.
- HTMLElement* element = toHTMLElement(node.get());
- RefPtr<MutableStyleProperties> inlineStyle = copyStyleOrCreateEmpty(element->inlineStyle());
+ HTMLElement& element = downcast<HTMLElement>(*node);
+ RefPtr<MutableStyleProperties> inlineStyle = copyStyleOrCreateEmpty(element.inlineStyle());
if (MutableStyleProperties* otherStyle = style->style())
inlineStyle->mergeAndOverrideOnConflict(*otherStyle);
- setNodeAttribute(element, styleAttr, inlineStyle->asText());
- next = NodeTraversal::nextSkippingChildren(node.get());
+ setNodeAttribute(&element, styleAttr, inlineStyle->asText());
+ next = NodeTraversal::nextSkippingChildren(*node);
continue;
}
if (isBlock(node.get()))
continue;
- if (node->childNodeCount()) {
- if (node->contains(pastEndNode.get()) || containsNonEditableRegion(node.get()) || !node->parentNode()->hasEditableStyle())
+ if (node->hasChildNodes()) {
+ if (node->contains(pastEndNode.get()) || containsNonEditableRegion(*node) || !node->parentNode()->hasEditableStyle())
continue;
- if (editingIgnoresContent(node.get())) {
- next = NodeTraversal::nextSkippingChildren(node.get());
+ if (editingIgnoresContent(*node)) {
+ next = NodeTraversal::nextSkippingChildren(*node);
continue;
}
}
@@ -802,37 +812,35 @@ void ApplyStyleCommand::applyInlineStyleToNodeRange(EditingStyle* style, PassRef
Node* runStart = node.get();
Node* runEnd = node.get();
Node* sibling = node->nextSibling();
- while (sibling && sibling != pastEndNode && !sibling->contains(pastEndNode.get())
- && (!isBlock(sibling) || sibling->hasTagName(brTag))
- && !containsNonEditableRegion(sibling)) {
+ while (sibling && sibling != pastEndNode && !sibling->contains(pastEndNode.get()) && (!isBlock(sibling) || sibling->hasTagName(brTag)) && !containsNonEditableRegion(*sibling)) {
runEnd = sibling;
sibling = runEnd->nextSibling();
}
- next = NodeTraversal::nextSkippingChildren(runEnd);
+ next = NodeTraversal::nextSkippingChildren(*runEnd);
- Node* pastEndNode = NodeTraversal::nextSkippingChildren(runEnd);
+ Node* pastEndNode = NodeTraversal::nextSkippingChildren(*runEnd);
if (!shouldApplyInlineStyleToRun(style, runStart, pastEndNode))
continue;
runs.append(InlineRunToApplyStyle(runStart, runEnd, pastEndNode));
}
- for (size_t i = 0; i < runs.size(); i++) {
- removeConflictingInlineStyleFromRun(style, runs[i].start, runs[i].end, runs[i].pastEndNode);
- runs[i].positionForStyleComputation = positionToComputeInlineStyleChange(runs[i].start, runs[i].dummyElement);
+ for (auto& run : runs) {
+ removeConflictingInlineStyleFromRun(style, run.start, run.end, run.pastEndNode);
+ if (run.startAndEndAreStillInDocument())
+ run.positionForStyleComputation = positionToComputeInlineStyleChange(run.start, run.dummyElement);
}
document().updateLayoutIgnorePendingStylesheets();
- for (size_t i = 0; i < runs.size(); i++)
- runs[i].change = StyleChange(style, runs[i].positionForStyleComputation);
+ for (auto& run : runs)
+ run.change = StyleChange(style, run.positionForStyleComputation);
- for (size_t i = 0; i < runs.size(); i++) {
- InlineRunToApplyStyle& run = runs[i];
+ for (auto& run : runs) {
if (run.dummyElement)
removeNode(run.dummyElement);
if (run.startAndEndAreStillInDocument())
- applyInlineStyleChange(run.start.release(), run.end.release(), run.change, AddStyledElement);
+ applyInlineStyleChange(WTFMove(run.start), WTFMove(run.end), run.change, AddStyledElement);
}
}
@@ -846,13 +854,13 @@ bool ApplyStyleCommand::shouldApplyInlineStyleToRun(EditingStyle* style, Node* r
{
ASSERT(style && runStart);
- for (Node* node = runStart; node && node != pastEndNode; node = NodeTraversal::next(node)) {
- if (node->childNodeCount())
+ for (Node* node = runStart; node && node != pastEndNode; node = NodeTraversal::next(*node)) {
+ if (node->hasChildNodes())
continue;
// We don't consider m_isInlineElementToRemoveFunction here because we never apply style when m_isInlineElementToRemoveFunction is specified
if (!style->styleIsPresentInComputedStyleOfNode(node))
return true;
- if (m_styledInlineElement && !enclosingNodeWithTag(positionBeforeNode(node), m_styledInlineElement->tagQName()))
+ if (m_styledInlineElement && !enclosingElementWithTag(positionBeforeNode(node), m_styledInlineElement->tagQName()))
return true;
}
return false;
@@ -862,20 +870,21 @@ void ApplyStyleCommand::removeConflictingInlineStyleFromRun(EditingStyle* style,
{
ASSERT(runStart && runEnd);
RefPtr<Node> next = runStart;
- for (RefPtr<Node> node = next; node && node->inDocument() && node != pastEndNode; node = next) {
- if (editingIgnoresContent(node.get())) {
+ for (RefPtr<Node> node = next; node && node->isConnected() && node != pastEndNode; node = next) {
+ if (editingIgnoresContent(*node)) {
ASSERT(!node->contains(pastEndNode.get()));
- next = NodeTraversal::nextSkippingChildren(node.get());
+ next = NodeTraversal::nextSkippingChildren(*node);
} else
- next = NodeTraversal::next(node.get());
- if (!node->isHTMLElement())
+ next = NodeTraversal::next(*node);
+
+ if (!is<HTMLElement>(*node))
continue;
RefPtr<Node> previousSibling = node->previousSibling();
RefPtr<Node> nextSibling = node->nextSibling();
RefPtr<ContainerNode> parent = node->parentNode();
- removeInlineStyleFromElement(style, toHTMLElement(node.get()), RemoveAlways);
- if (!node->inDocument()) {
+ removeInlineStyleFromElement(style, downcast<HTMLElement>(node.get()), RemoveAlways);
+ if (!node->isConnected()) {
// FIXME: We might need to update the start and the end of current selection here but need a test.
if (runStart == node)
runStart = previousSibling ? previousSibling->nextSibling() : parent->firstChild();
@@ -889,7 +898,7 @@ bool ApplyStyleCommand::removeInlineStyleFromElement(EditingStyle* style, PassRe
{
ASSERT(element);
- if (!element->parentNode() || !element->parentNode()->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable))
+ if (!element->parentNode() || !isEditableNode(*element->parentNode()))
return false;
if (isStyledInlineElementToRemove(element.get())) {
@@ -905,7 +914,7 @@ bool ApplyStyleCommand::removeInlineStyleFromElement(EditingStyle* style, PassRe
if (removeImplicitlyStyledElement(style, element.get(), mode, extractedStyle))
removed = true;
- if (!element->inDocument())
+ if (!element->isConnected())
return removed;
// If the node was converted to a span, the span may still contain relevant
@@ -922,7 +931,7 @@ void ApplyStyleCommand::replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*&
removeNodePreservingChildren(elem);
else {
HTMLElement* newSpanElement = replaceElementWithSpanPreservingChildrenAndAttributes(elem);
- ASSERT(newSpanElement && newSpanElement->inDocument());
+ ASSERT(newSpanElement && newSpanElement->isConnected());
elem = newSpanElement;
}
}
@@ -947,8 +956,8 @@ bool ApplyStyleCommand::removeImplicitlyStyledElement(EditingStyle* style, HTMLE
extractedStyle, attributes, mode == RemoveAlways ? EditingStyle::ExtractMatchingStyle : EditingStyle::DoNotExtractMatchingStyle))
return false;
- for (size_t i = 0; i < attributes.size(); i++)
- removeNodeAttribute(element, attributes[i]);
+ for (auto& attribute : attributes)
+ removeNodeAttribute(element, attribute);
if (isEmptyFontTag(element) || isSpanWithoutAttributesOrUnstyledStyleSpan(element))
removeNodePreservingChildren(element);
@@ -964,17 +973,14 @@ bool ApplyStyleCommand::removeCSSStyle(EditingStyle* style, HTMLElement* element
if (mode == RemoveNone)
return style->conflictsWithInlineStyleOfElement(element);
- Vector<CSSPropertyID> properties;
- if (!style->conflictsWithInlineStyleOfElement(element, extractedStyle, properties))
+ RefPtr<MutableStyleProperties> newInlineStyle;
+ if (!style->conflictsWithInlineStyleOfElement(element, newInlineStyle, extractedStyle))
return false;
- // FIXME: We should use a mass-removal function here but we don't have an undoable one yet.
- for (size_t i = 0; i < properties.size(); i++)
- removeCSSProperty(element, properties[i]);
-
- // No need to serialize <foo style=""> if we just removed the last css property
- if (element->inlineStyle()->isEmpty())
+ if (newInlineStyle->isEmpty())
removeNodeAttribute(element, styleAttr);
+ else
+ setNodeAttribute(element, styleAttr, newInlineStyle->asText());
if (isSpanWithoutAttributesOrUnstyledStyleSpan(element))
removeNodePreservingChildren(element);
@@ -985,17 +991,17 @@ bool ApplyStyleCommand::removeCSSStyle(EditingStyle* style, HTMLElement* element
HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle(EditingStyle* style, Node* node)
{
if (!node)
- return 0;
+ return nullptr;
- HTMLElement* result = 0;
+ HTMLElement* result = nullptr;
Node* unsplittableElement = unsplittableElementForPosition(firstPositionInOrBeforeNode(node));
- for (Node *n = node; n; n = n->parentNode()) {
- if (n->isHTMLElement() && shouldRemoveInlineStyleFromElement(style, toHTMLElement(n)))
- result = toHTMLElement(n);
+ for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
+ if (is<HTMLElement>(*ancestor) && shouldRemoveInlineStyleFromElement(style, downcast<HTMLElement>(ancestor)))
+ result = downcast<HTMLElement>(ancestor);
// Should stop at the editable root (cannot cross editing boundary) and
// also stop at the unsplittable element to be consistent with other UAs
- if (n == unsplittableElement)
+ if (ancestor == unsplittableElement)
break;
}
@@ -1008,19 +1014,19 @@ void ApplyStyleCommand::applyInlineStyleToPushDown(Node* node, EditingStyle* sty
node->document().updateStyleIfNeeded();
- if (!style || style->isEmpty() || !node->renderer() || node->hasTagName(iframeTag))
+ if (!style || style->isEmpty() || !node->renderer() || is<HTMLIFrameElement>(*node))
return;
RefPtr<EditingStyle> newInlineStyle = style;
- if (node->isHTMLElement() && toHTMLElement(node)->inlineStyle()) {
+ if (is<HTMLElement>(*node) && downcast<HTMLElement>(node)->inlineStyle()) {
newInlineStyle = style->copy();
- newInlineStyle->mergeInlineStyleOfElement(toHTMLElement(node), EditingStyle::OverrideValues);
+ newInlineStyle->mergeInlineStyleOfElement(downcast<HTMLElement>(node), EditingStyle::OverrideValues);
}
// Since addInlineStyleIfNeeded can't add styles to block-flow render objects, add style attribute instead.
// FIXME: applyInlineStyleToRange should be used here instead.
- if ((node->renderer()->isRenderBlockFlow() || node->childNodeCount()) && node->isHTMLElement()) {
- setNodeAttribute(toHTMLElement(node), styleAttr, newInlineStyle->style()->asText());
+ if ((node->renderer()->isRenderBlockFlow() || node->hasChildNodes()) && is<HTMLElement>(*node)) {
+ setNodeAttribute(downcast<HTMLElement>(node), styleAttr, newInlineStyle->style()->asText());
return;
}
@@ -1051,31 +1057,31 @@ void ApplyStyleCommand::pushDownInlineStyleAroundNode(EditingStyle* style, Node*
getChildNodes(*current.get(), currentChildren);
RefPtr<StyledElement> styledElement;
- if (current->isStyledElement() && isStyledInlineElementToRemove(toElement(current.get()))) {
- styledElement = toStyledElement(current.get());
+ if (is<StyledElement>(*current) && isStyledInlineElementToRemove(downcast<Element>(current.get()))) {
+ styledElement = downcast<StyledElement>(current.get());
elementsToPushDown.append(*styledElement);
}
RefPtr<EditingStyle> styleToPushDown = EditingStyle::create();
- if (current->isHTMLElement())
- removeInlineStyleFromElement(style, toHTMLElement(current.get()), RemoveIfNeeded, styleToPushDown.get());
+ if (is<HTMLElement>(*current))
+ removeInlineStyleFromElement(style, downcast<HTMLElement>(current.get()), RemoveIfNeeded, styleToPushDown.get());
// The inner loop will go through children on each level
// FIXME: we should aggregate inline child elements together so that we don't wrap each child separately.
- for (size_t i = 0; i < currentChildren.size(); ++i) {
- Node& child = currentChildren[i].get();
+ for (Ref<Node>& childRef : currentChildren) {
+ Node& child = childRef;
if (!child.parentNode())
continue;
if (!child.contains(targetNode) && elementsToPushDown.size()) {
- for (size_t i = 0; i < elementsToPushDown.size(); i++) {
- RefPtr<Element> wrapper = elementsToPushDown[i]->cloneElementWithoutChildren();
+ for (auto& element : elementsToPushDown) {
+ RefPtr<Element> wrapper = element->cloneElementWithoutChildren(document());
wrapper->removeAttribute(styleAttr);
surroundNodeRangeWithElement(&child, &child, wrapper);
}
}
// Apply style to all nodes containing targetNode and their siblings but NOT to targetNode
- // But if we've removed styledElement then go ahead and always apply the style.
+ // But if we've removed styledElement then always apply the style.
if (&child != targetNode || styledElement)
applyInlineStyleToPushDown(&child, styleToPushDown.get());
@@ -1087,12 +1093,12 @@ void ApplyStyleCommand::pushDownInlineStyleAroundNode(EditingStyle* style, Node*
}
}
-void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &start, const Position &end)
+void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position& start, const Position& end)
{
ASSERT(start.isNotNull());
ASSERT(end.isNotNull());
- ASSERT(start.anchorNode()->inDocument());
- ASSERT(end.anchorNode()->inDocument());
+ ASSERT(start.anchorNode()->isConnected());
+ ASSERT(end.anchorNode()->isConnected());
ASSERT(comparePositions(start, end) <= 0);
// FIXME: We should assert that start/end are not in the middle of a text node.
@@ -1100,15 +1106,14 @@ void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &s
// If the pushDownStart is at the end of a text node, then this node is not fully selected.
// Move it to the next deep quivalent position to avoid removing the style from this node.
// e.g. if pushDownStart was at Position("hello", 5) in <b>hello<div>world</div></b>, we want Position("world", 0) instead.
- Node* pushDownStartContainer = pushDownStart.containerNode();
- if (pushDownStartContainer && pushDownStartContainer->isTextNode()
- && pushDownStart.computeOffsetInContainerNode() == pushDownStartContainer->maxCharacterOffset())
+ auto* pushDownStartContainer = pushDownStart.containerNode();
+ if (is<Text>(pushDownStartContainer) && pushDownStart.computeOffsetInContainerNode() == pushDownStartContainer->maxCharacterOffset())
pushDownStart = nextVisuallyDistinctCandidate(pushDownStart);
// If pushDownEnd is at the start of a text node, then this node is not fully selected.
// Move it to the previous deep equivalent position to avoid removing the style from this node.
Position pushDownEnd = end.upstream();
- Node* pushDownEndContainer = pushDownEnd.containerNode();
- if (pushDownEndContainer && pushDownEndContainer->isTextNode() && !pushDownEnd.computeOffsetInContainerNode())
+ auto* pushDownEndContainer = pushDownEnd.containerNode();
+ if (is<Text>(pushDownEndContainer) && !pushDownEnd.computeOffsetInContainerNode())
pushDownEnd = previousVisuallyDistinctCandidate(pushDownEnd);
pushDownInlineStyleAroundNode(style, pushDownStart.deprecatedNode());
@@ -1125,32 +1130,32 @@ void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &s
RefPtr<Node> node = start.deprecatedNode();
while (node) {
RefPtr<Node> next;
- if (editingIgnoresContent(node.get())) {
+ if (editingIgnoresContent(*node)) {
ASSERT(node == end.deprecatedNode() || !node->contains(end.deprecatedNode()));
- next = NodeTraversal::nextSkippingChildren(node.get());
+ next = NodeTraversal::nextSkippingChildren(*node);
} else
- next = NodeTraversal::next(node.get());
+ next = NodeTraversal::next(*node);
- if (node->isHTMLElement() && nodeFullySelected(node.get(), start, end)) {
- RefPtr<HTMLElement> elem = toHTMLElement(node.get());
- RefPtr<Node> prev = NodeTraversal::previousPostOrder(elem.get());
- RefPtr<Node> next = NodeTraversal::next(elem.get());
+ if (is<HTMLElement>(*node) && nodeFullySelected(downcast<HTMLElement>(*node), start, end)) {
+ Ref<HTMLElement> element = downcast<HTMLElement>(*node);
+ RefPtr<Node> prev = NodeTraversal::previousPostOrder(element);
+ RefPtr<Node> next = NodeTraversal::next(element);
RefPtr<EditingStyle> styleToPushDown;
RefPtr<Node> childNode;
- if (isStyledInlineElementToRemove(elem.get())) {
+ if (isStyledInlineElementToRemove(element.ptr())) {
styleToPushDown = EditingStyle::create();
- childNode = elem->firstChild();
+ childNode = element->firstChild();
}
- removeInlineStyleFromElement(style, elem.get(), RemoveIfNeeded, styleToPushDown.get());
- if (!elem->inDocument()) {
- if (s.deprecatedNode() == elem) {
+ removeInlineStyleFromElement(style, element.ptr(), RemoveIfNeeded, styleToPushDown.get());
+ if (!element->isConnected()) {
+ if (s.deprecatedNode() == element.ptr()) {
// Since elem must have been fully selected, and it is at the start
// of the selection, it is clear we can set the new s offset to 0.
ASSERT(s.anchorType() == Position::PositionIsBeforeAnchor || s.offsetInContainerNode() <= 0);
s = firstPositionInOrBeforeNode(next.get());
}
- if (e.deprecatedNode() == elem) {
+ if (e.deprecatedNode() == element.ptr()) {
// 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.
@@ -1172,32 +1177,27 @@ void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &s
updateStartEnd(s, e);
}
-bool ApplyStyleCommand::nodeFullySelected(Node *node, const Position &start, const Position &end) const
+bool ApplyStyleCommand::nodeFullySelected(Element& element, const Position& start, const Position& end) const
{
- ASSERT(node);
- ASSERT(node->isElementNode());
-
// The tree may have changed and Position::upstream() relies on an up-to-date layout.
- node->document().updateLayoutIgnorePendingStylesheets();
+ element.document().updateLayoutIgnorePendingStylesheets();
- return comparePositions(firstPositionInOrBeforeNode(node), start) >= 0
- && comparePositions(lastPositionInOrAfterNode(node).upstream(), end) <= 0;
+ return comparePositions(firstPositionInOrBeforeNode(&element), start) >= 0
+ && comparePositions(lastPositionInOrAfterNode(&element).upstream(), end) <= 0;
}
-bool ApplyStyleCommand::nodeFullyUnselected(Node *node, const Position &start, const Position &end) const
+bool ApplyStyleCommand::nodeFullyUnselected(Element& element, const Position& start, const Position& end) const
{
- ASSERT(node);
- ASSERT(node->isElementNode());
-
- bool isFullyBeforeStart = comparePositions(lastPositionInOrAfterNode(node).upstream(), start) < 0;
- bool isFullyAfterEnd = comparePositions(firstPositionInOrBeforeNode(node), end) > 0;
+ // The tree may have changed and Position::upstream() relies on an up-to-date layout.
+ element.document().updateLayoutIgnorePendingStylesheets();
- return isFullyBeforeStart || isFullyAfterEnd;
+ return comparePositions(lastPositionInOrAfterNode(&element).upstream(), start) < 0
+ || comparePositions(firstPositionInOrBeforeNode(&element), end) > 0;
}
void ApplyStyleCommand::splitTextAtStart(const Position& start, const Position& end)
{
- ASSERT(start.containerNode()->isTextNode());
+ ASSERT(is<Text>(start.containerNode()));
Position newEnd;
if (end.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode())
@@ -1212,23 +1212,23 @@ void ApplyStyleCommand::splitTextAtStart(const Position& start, const Position&
void ApplyStyleCommand::splitTextAtEnd(const Position& start, const Position& end)
{
- ASSERT(end.containerNode()->isTextNode());
+ ASSERT(is<Text>(end.containerNode()));
bool shouldUpdateStart = start.anchorType() == Position::PositionIsOffsetInAnchor && start.containerNode() == end.containerNode();
- Text* text = toText(end.deprecatedNode());
- splitTextNode(text, end.offsetInContainerNode());
+ Text& text = downcast<Text>(*end.deprecatedNode());
+ splitTextNode(&text, end.offsetInContainerNode());
- Node* prevNode = text->previousSibling();
- if (!prevNode || !prevNode->isTextNode())
+ Node* prevNode = text.previousSibling();
+ if (!is<Text>(prevNode))
return;
- Position newStart = shouldUpdateStart ? Position(toText(prevNode), start.offsetInContainerNode()) : start;
+ Position newStart = shouldUpdateStart ? Position(downcast<Text>(prevNode), start.offsetInContainerNode()) : start;
updateStartEnd(newStart, lastPositionInNode(prevNode));
}
void ApplyStyleCommand::splitTextElementAtStart(const Position& start, const Position& end)
{
- ASSERT(start.containerNode()->isTextNode());
+ ASSERT(is<Text>(start.containerNode()));
Position newEnd;
if (start.containerNode() == end.containerNode())
@@ -1242,7 +1242,7 @@ void ApplyStyleCommand::splitTextElementAtStart(const Position& start, const Pos
void ApplyStyleCommand::splitTextElementAtEnd(const Position& start, const Position& end)
{
- ASSERT(end.containerNode()->isTextNode());
+ ASSERT(is<Text>(end.containerNode()));
bool shouldUpdateStart = start.containerNode() == end.containerNode();
splitTextNodeContainingElement(end.containerText(), end.offsetInContainerNode());
@@ -1251,33 +1251,33 @@ void ApplyStyleCommand::splitTextElementAtEnd(const Position& start, const Posit
if (!parentElement || !parentElement->previousSibling())
return;
Node* firstTextNode = parentElement->previousSibling()->lastChild();
- if (!firstTextNode || !firstTextNode->isTextNode())
+ if (!is<Text>(firstTextNode))
return;
- Position newStart = shouldUpdateStart ? Position(toText(firstTextNode), start.offsetInContainerNode()) : start;
+ Position newStart = shouldUpdateStart ? Position(downcast<Text>(firstTextNode), start.offsetInContainerNode()) : start;
updateStartEnd(newStart, positionAfterNode(firstTextNode));
}
bool ApplyStyleCommand::shouldSplitTextElement(Element* element, EditingStyle* style)
{
- if (!element || !element->isHTMLElement())
+ if (!is<HTMLElement>(element))
return false;
- return shouldRemoveInlineStyleFromElement(style, toHTMLElement(element));
+ return shouldRemoveInlineStyleFromElement(style, &downcast<HTMLElement>(*element));
}
bool ApplyStyleCommand::isValidCaretPositionInTextNode(const Position& position)
{
Node* node = position.containerNode();
- if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node->isTextNode())
+ if (position.anchorType() != Position::PositionIsOffsetInAnchor || !is<Text>(node))
return false;
int offsetInText = position.offsetInContainerNode();
- return offsetInText > caretMinOffset(node) && offsetInText < caretMaxOffset(node);
+ return offsetInText > caretMinOffset(*node) && offsetInText < caretMaxOffset(*node);
}
bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position& start, const Position& end)
{
- Node* startNode = start.containerNode();
+ auto* startNode = start.containerNode();
int startOffset = start.computeOffsetInContainerNode();
if (startOffset)
return false;
@@ -1289,29 +1289,23 @@ bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position& start,
return false;
startNode = startNode->parentNode();
- startOffset = 0;
}
- if (!startNode->isElementNode())
+ auto* previousSibling = startNode->previousSibling();
+ if (!previousSibling || !areIdenticalElements(*startNode, *previousSibling))
return false;
- Node* previousSibling = startNode->previousSibling();
-
- if (previousSibling && areIdenticalElements(startNode, previousSibling)) {
- Element* previousElement = toElement(previousSibling);
- Element* element = toElement(startNode);
- Node* startChild = element->firstChild();
- ASSERT(startChild);
- mergeIdenticalElements(previousElement, element);
+ auto& previousElement = downcast<Element>(*previousSibling);
+ auto& element = downcast<Element>(*startNode);
+ auto* startChild = element.firstChild();
+ ASSERT(startChild);
+ mergeIdenticalElements(&previousElement, &element);
- int startOffsetAdjustment = startChild->nodeIndex();
- int endOffsetAdjustment = startNode == end.deprecatedNode() ? startOffsetAdjustment : 0;
- updateStartEnd(Position(startNode, startOffsetAdjustment, Position::PositionIsOffsetInAnchor),
- Position(end.deprecatedNode(), end.deprecatedEditingOffset() + endOffsetAdjustment, Position::PositionIsOffsetInAnchor));
- return true;
- }
-
- return false;
+ int startOffsetAdjustment = startChild->computeNodeIndex();
+ int endOffsetAdjustment = startNode == end.deprecatedNode() ? startOffsetAdjustment : 0;
+ updateStartEnd({ startNode, startOffsetAdjustment, Position::PositionIsOffsetInAnchor},
+ { end.deprecatedNode(), end.deprecatedEditingOffset() + endOffsetAdjustment, Position::PositionIsOffsetInAnchor });
+ return true;
}
bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position& start, const Position& end)
@@ -1326,25 +1320,24 @@ bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position& start, const
endNode = end.deprecatedNode()->parentNode();
}
- if (!endNode->isElementNode() || endNode->hasTagName(brTag))
+ if (endNode->hasTagName(brTag))
return false;
Node* nextSibling = endNode->nextSibling();
- if (nextSibling && areIdenticalElements(endNode, nextSibling)) {
- Element* nextElement = toElement(nextSibling);
- Element* element = toElement(endNode);
- Node* nextChild = nextElement->firstChild();
+ if (!nextSibling || !areIdenticalElements(*endNode, *nextSibling))
+ return false;
- mergeIdenticalElements(element, nextElement);
+ auto& nextElement = downcast<Element>(*nextSibling);
+ auto& element = downcast<Element>(*endNode);
+ Node* nextChild = nextElement.firstChild();
- bool shouldUpdateStart = start.containerNode() == endNode;
- int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodeCount();
- updateStartEnd(shouldUpdateStart ? Position(nextElement, start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor) : start,
- Position(nextElement, endOffset, Position::PositionIsOffsetInAnchor));
- return true;
- }
+ mergeIdenticalElements(&element, &nextElement);
- return false;
+ bool shouldUpdateStart = start.containerNode() == endNode;
+ int endOffset = nextChild ? nextChild->computeNodeIndex() : nextElement.countChildNodes();
+ updateStartEnd(shouldUpdateStart ? Position(&nextElement, start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor) : start,
+ { &nextElement, endOffset, Position::PositionIsOffsetInAnchor });
+ return true;
}
void ApplyStyleCommand::surroundNodeRangeWithElement(PassRefPtr<Node> passedStartNode, PassRefPtr<Node> endNode, PassRefPtr<Element> elementToInsert)
@@ -1360,7 +1353,7 @@ void ApplyStyleCommand::surroundNodeRangeWithElement(PassRefPtr<Node> passedStar
RefPtr<Node> node = startNode;
while (node) {
RefPtr<Node> next = node->nextSibling();
- if (node->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable)) {
+ if (isEditableNode(*node)) {
removeNode(node);
appendNode(node, element);
}
@@ -1371,15 +1364,15 @@ void ApplyStyleCommand::surroundNodeRangeWithElement(PassRefPtr<Node> passedStar
RefPtr<Node> nextSibling = element->nextSibling();
RefPtr<Node> previousSibling = element->previousSibling();
- if (nextSibling && nextSibling->isElementNode() && nextSibling->hasEditableStyle()
- && areIdenticalElements(element.get(), toElement(nextSibling.get())))
- mergeIdenticalElements(element.get(), toElement(nextSibling.get()));
-
- if (previousSibling && previousSibling->isElementNode() && previousSibling->hasEditableStyle()) {
- Node* mergedElement = previousSibling->nextSibling();
- if (mergedElement->isElementNode() && mergedElement->hasEditableStyle()
- && areIdenticalElements(toElement(previousSibling.get()), toElement(mergedElement)))
- mergeIdenticalElements(toElement(previousSibling.get()), toElement(mergedElement));
+
+ if (nextSibling && nextSibling->hasEditableStyle() && areIdenticalElements(*element, *nextSibling))
+ mergeIdenticalElements(element.get(), downcast<Element>(nextSibling.get()));
+
+ if (is<Element>(previousSibling.get()) && previousSibling->hasEditableStyle()) {
+ auto* mergedElement = previousSibling->nextSibling();
+ ASSERT(mergedElement);
+ if (mergedElement->hasEditableStyle() && areIdenticalElements(*previousSibling, *mergedElement))
+ mergeIdenticalElements(downcast<Element>(previousSibling.get()), downcast<Element>(mergedElement));
}
// FIXME: We should probably call updateStartEnd if the start or end was in the node
@@ -1389,12 +1382,13 @@ void ApplyStyleCommand::surroundNodeRangeWithElement(PassRefPtr<Node> passedStar
void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElement* block)
{
+ ASSERT(styleChange.cssStyle());
// Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for
// inline content.
if (!block)
return;
- String cssStyle = styleChange.cssStyle();
+ String cssStyle = styleChange.cssStyle()->asText();
StringBuilder cssText;
cssText.append(cssStyle);
if (const StyleProperties* decl = block->inlineStyle()) {
@@ -1407,7 +1401,7 @@ void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElemen
void ApplyStyleCommand::addInlineStyleIfNeeded(EditingStyle* style, PassRefPtr<Node> passedStart, PassRefPtr<Node> passedEnd, EAddStyledElement addStyledElement)
{
- if (!passedStart || !passedEnd || !passedStart->inDocument() || !passedEnd->inDocument())
+ if (!passedStart || !passedEnd || !passedStart->isConnected() || !passedEnd->isConnected())
return;
RefPtr<Node> start = passedStart;
@@ -1423,7 +1417,7 @@ void ApplyStyleCommand::addInlineStyleIfNeeded(EditingStyle* style, PassRefPtr<N
Position ApplyStyleCommand::positionToComputeInlineStyleChange(PassRefPtr<Node> startNode, RefPtr<Node>& dummyElement)
{
// It's okay to obtain the style at the startNode because we've removed all relevant styles from the current run.
- if (!startNode->isElementNode()) {
+ if (!is<Element>(*startNode)) {
dummyElement = createStyleSpanElement(document());
insertNodeAt(dummyElement, positionBeforeNode(startNode.get()));
return firstPositionInOrBeforeNode(dummyElement.get());
@@ -1436,22 +1430,25 @@ void ApplyStyleCommand::applyInlineStyleChange(PassRefPtr<Node> passedStart, Pas
{
RefPtr<Node> startNode = passedStart;
RefPtr<Node> endNode = passedEnd;
- ASSERT(startNode->inDocument());
- ASSERT(endNode->inDocument());
+ ASSERT(startNode->isConnected());
+ ASSERT(endNode->isConnected());
// Find appropriate font and span elements top-down.
- HTMLElement* fontContainer = 0;
- HTMLElement* styleContainer = 0;
- for (Node* container = startNode.get(); container && startNode == endNode; container = container->firstChild()) {
- if (container->isHTMLElement() && container->hasTagName(fontTag))
- fontContainer = toHTMLElement(container);
- bool styleContainerIsNotSpan = !styleContainer || !styleContainer->hasTagName(spanTag);
- if (container->isHTMLElement() && (container->hasTagName(spanTag) || (styleContainerIsNotSpan && container->childNodeCount())))
- styleContainer = toHTMLElement(container);
- if (!container->firstChild())
+ HTMLFontElement* fontContainer = nullptr;
+ HTMLElement* styleContainer = nullptr;
+ while (startNode == endNode) {
+ if (is<HTMLElement>(*startNode)) {
+ auto& container = downcast<HTMLElement>(*startNode);
+ if (is<HTMLFontElement>(container))
+ fontContainer = &downcast<HTMLFontElement>(container);
+ if (is<HTMLSpanElement>(container) || (!is<HTMLSpanElement>(styleContainer) && container.hasChildNodes()))
+ styleContainer = &container;
+ }
+ auto* startNodeFirstChild = startNode->firstChild();
+ if (!startNodeFirstChild)
break;
- startNode = container->firstChild();
- endNode = container->lastChild();
+ endNode = startNode->lastChild();
+ startNode = startNodeFirstChild;
}
// Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes.
@@ -1464,33 +1461,29 @@ void ApplyStyleCommand::applyInlineStyleChange(PassRefPtr<Node> passedStart, Pas
if (styleChange.applyFontSize())
setNodeAttribute(fontContainer, sizeAttr, styleChange.fontSize());
} else {
- RefPtr<Element> fontElement = createFontElement(document());
+ auto fontElement = createFontElement(document());
if (styleChange.applyFontColor())
- fontElement->setAttribute(colorAttr, styleChange.fontColor());
+ fontElement->setAttributeWithoutSynchronization(colorAttr, styleChange.fontColor());
if (styleChange.applyFontFace())
- fontElement->setAttribute(faceAttr, styleChange.fontFace());
+ fontElement->setAttributeWithoutSynchronization(faceAttr, styleChange.fontFace());
if (styleChange.applyFontSize())
- fontElement->setAttribute(sizeAttr, styleChange.fontSize());
+ fontElement->setAttributeWithoutSynchronization(sizeAttr, styleChange.fontSize());
surroundNodeRangeWithElement(startNode, endNode, fontElement.get());
}
}
- if (styleChange.cssStyle().length()) {
+ if (auto styleToMerge = styleChange.cssStyle()) {
if (styleContainer) {
- if (const StyleProperties* existingStyle = styleContainer->inlineStyle()) {
- String existingText = existingStyle->asText();
- StringBuilder cssText;
- cssText.append(existingText);
- if (!existingText.isEmpty())
- cssText.append(' ');
- cssText.append(styleChange.cssStyle());
- setNodeAttribute(styleContainer, styleAttr, cssText.toString());
+ if (auto existingStyle = styleContainer->inlineStyle()) {
+ auto inlineStyle = EditingStyle::create(existingStyle);
+ inlineStyle->overrideWithStyle(styleToMerge);
+ setNodeAttribute(styleContainer, styleAttr, inlineStyle->style()->asText());
} else
- setNodeAttribute(styleContainer, styleAttr, styleChange.cssStyle());
+ setNodeAttribute(styleContainer, styleAttr, styleToMerge->asText());
} else {
- RefPtr<Element> styleElement = createStyleSpanElement(document());
- styleElement->setAttribute(styleAttr, styleChange.cssStyle());
- surroundNodeRangeWithElement(startNode, endNode, styleElement.release());
+ auto styleElement = createStyleSpanElement(document());
+ styleElement->setAttribute(styleAttr, styleToMerge->asText());
+ surroundNodeRangeWithElement(startNode, endNode, WTFMove(styleElement));
}
}
@@ -1512,7 +1505,7 @@ void ApplyStyleCommand::applyInlineStyleChange(PassRefPtr<Node> passedStart, Pas
surroundNodeRangeWithElement(startNode, endNode, createHTMLElement(document(), supTag));
if (m_styledInlineElement && addStyledElement == AddStyledElement)
- surroundNodeRangeWithElement(startNode, endNode, m_styledInlineElement->cloneElementWithoutChildren());
+ surroundNodeRangeWithElement(startNode, endNode, m_styledInlineElement->cloneElementWithoutChildren(document()));
}
float ApplyStyleCommand::computedFontSize(Node* node)
@@ -1520,9 +1513,8 @@ float ApplyStyleCommand::computedFontSize(Node* node)
if (!node)
return 0;
- RefPtr<CSSValue> value = ComputedStyleExtractor(node).propertyValue(CSSPropertyFontSize);
- ASSERT(value && value->isPrimitiveValue());
- return toCSSPrimitiveValue(value.get())->getFloatValue(CSSPrimitiveValue::CSS_PX);
+ auto value = ComputedStyleExtractor(node).propertyValue(CSSPropertyFontSize);
+ return downcast<CSSPrimitiveValue>(*value).floatValue(CSSPrimitiveValue::CSS_PX);
}
void ApplyStyleCommand::joinChildTextNodes(Node* node, const Position& start, const Position& end)
@@ -1534,21 +1526,20 @@ void ApplyStyleCommand::joinChildTextNodes(Node* node, const Position& start, co
Position newEnd = end;
Vector<RefPtr<Text>> textNodes;
- for (Text* textNode = TextNodeTraversal::firstChild(node); textNode; textNode = TextNodeTraversal::nextSibling(textNode))
+ for (Text* textNode = TextNodeTraversal::firstChild(*node); textNode; textNode = TextNodeTraversal::nextSibling(*textNode))
textNodes.append(textNode);
- for (size_t i = 0; i < textNodes.size(); ++i) {
- Text* childText = textNodes[i].get();
+ for (auto& childText : textNodes) {
Node* next = childText->nextSibling();
- if (!next || !next->isTextNode())
+ if (!is<Text>(next))
continue;
- Text* nextText = toText(next);
+ Text& nextText = downcast<Text>(*next);
if (start.anchorType() == Position::PositionIsOffsetInAnchor && next == start.containerNode())
- newStart = Position(childText, childText->length() + start.offsetInContainerNode());
+ newStart = Position(childText.get(), childText->length() + start.offsetInContainerNode());
if (end.anchorType() == Position::PositionIsOffsetInAnchor && next == end.containerNode())
- newEnd = Position(childText, childText->length() + end.offsetInContainerNode());
- String textToMove = nextText->data();
+ newEnd = Position(childText.get(), childText->length() + end.offsetInContainerNode());
+ String textToMove = nextText.data();
insertTextIntoNode(childText, childText->length(), textToMove);
removeNode(next);
// don't move child node pointer. it may want to merge with more text nodes.
diff --git a/Source/WebCore/editing/ApplyStyleCommand.h b/Source/WebCore/editing/ApplyStyleCommand.h
index cda88c81e..a485498f9 100644
--- a/Source/WebCore/editing/ApplyStyleCommand.h
+++ b/Source/WebCore/editing/ApplyStyleCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ApplyStyleCommand_h
-#define ApplyStyleCommand_h
+#pragma once
#include "CompositeEditCommand.h"
#include "HTMLElement.h"
@@ -48,21 +47,21 @@ public:
enum EAddStyledElement { AddStyledElement, DoNotAddStyledElement };
typedef bool (*IsInlineElementToRemoveFunction)(const Element*);
- static PassRefPtr<ApplyStyleCommand> create(Document& document, const EditingStyle* style, EditAction action = EditActionChangeAttributes, EPropertyLevel level = PropertyDefault)
+ static Ref<ApplyStyleCommand> create(Document& document, const EditingStyle* style, EditAction action = EditActionChangeAttributes, EPropertyLevel level = PropertyDefault)
{
- return adoptRef(new ApplyStyleCommand(document, style, action, level));
+ return adoptRef(*new ApplyStyleCommand(document, style, action, level));
}
- static PassRefPtr<ApplyStyleCommand> create(Document& document, const EditingStyle* style, const Position& start, const Position& end, EditAction action = EditActionChangeAttributes, EPropertyLevel level = PropertyDefault)
+ static Ref<ApplyStyleCommand> create(Document& document, const EditingStyle* style, const Position& start, const Position& end, EditAction action = EditActionChangeAttributes, EPropertyLevel level = PropertyDefault)
{
- return adoptRef(new ApplyStyleCommand(document, style, start, end, action, level));
+ return adoptRef(*new ApplyStyleCommand(document, style, start, end, action, level));
}
- static PassRefPtr<ApplyStyleCommand> create(PassRefPtr<Element> element, bool removeOnly = false, EditAction action = EditActionChangeAttributes)
+ static Ref<ApplyStyleCommand> create(PassRefPtr<Element> element, bool removeOnly = false, EditAction action = EditActionChangeAttributes)
{
- return adoptRef(new ApplyStyleCommand(element, removeOnly, action));
+ return adoptRef(*new ApplyStyleCommand(element, removeOnly, action));
}
- static PassRefPtr<ApplyStyleCommand> create(Document& document, const EditingStyle* style, IsInlineElementToRemoveFunction isInlineElementToRemoveFunction, EditAction action = EditActionChangeAttributes)
+ static Ref<ApplyStyleCommand> create(Document& document, const EditingStyle* style, IsInlineElementToRemoveFunction isInlineElementToRemoveFunction, EditAction action = EditActionChangeAttributes)
{
- return adoptRef(new ApplyStyleCommand(document, style, isInlineElementToRemoveFunction, action));
+ return adoptRef(*new ApplyStyleCommand(document, style, isInlineElementToRemoveFunction, action));
}
private:
@@ -71,24 +70,24 @@ private:
ApplyStyleCommand(PassRefPtr<Element>, bool removeOnly, EditAction);
ApplyStyleCommand(Document&, const EditingStyle*, bool (*isInlineElementToRemove)(const Element*), EditAction);
- virtual void doApply() override;
- virtual EditAction editingAction() const override;
+ void doApply() override;
+ bool shouldDispatchInputEvents() const final { return false; }
// style-removal helpers
bool isStyledInlineElementToRemove(Element*) const;
bool shouldApplyInlineStyleToRun(EditingStyle*, Node* runStart, Node* pastEndNode);
void removeConflictingInlineStyleFromRun(EditingStyle*, RefPtr<Node>& runStart, RefPtr<Node>& runEnd, PassRefPtr<Node> pastEndNode);
- bool removeInlineStyleFromElement(EditingStyle*, PassRefPtr<HTMLElement>, InlineStyleRemovalMode = RemoveIfNeeded, EditingStyle* extractedStyle = 0);
+ bool removeInlineStyleFromElement(EditingStyle*, PassRefPtr<HTMLElement>, InlineStyleRemovalMode = RemoveIfNeeded, EditingStyle* extractedStyle = nullptr);
inline bool shouldRemoveInlineStyleFromElement(EditingStyle* style, HTMLElement* element) {return removeInlineStyleFromElement(style, element, RemoveNone);}
void replaceWithSpanOrRemoveIfWithoutAttributes(HTMLElement*&);
bool removeImplicitlyStyledElement(EditingStyle*, HTMLElement*, InlineStyleRemovalMode, EditingStyle* extractedStyle);
- bool removeCSSStyle(EditingStyle*, HTMLElement*, InlineStyleRemovalMode = RemoveIfNeeded, EditingStyle* extractedStyle = 0);
+ bool removeCSSStyle(EditingStyle*, HTMLElement*, InlineStyleRemovalMode = RemoveIfNeeded, EditingStyle* extractedStyle = nullptr);
HTMLElement* highestAncestorWithConflictingInlineStyle(EditingStyle*, Node*);
void applyInlineStyleToPushDown(Node*, EditingStyle*);
void pushDownInlineStyleAroundNode(EditingStyle*, Node*);
void removeInlineStyle(EditingStyle* , const Position& start, const Position& end);
- bool nodeFullySelected(Node*, const Position& start, const Position& end) const;
- bool nodeFullyUnselected(Node*, const Position& start, const Position& end) const;
+ bool nodeFullySelected(Element&, const Position& start, const Position& end) const;
+ bool nodeFullyUnselected(Element&, const Position& start, const Position& end) const;
// style-application helpers
void applyBlockStyle(EditingStyle*);
@@ -122,22 +121,19 @@ private:
Position endPosition();
RefPtr<EditingStyle> m_style;
- EditAction m_editingAction;
EPropertyLevel m_propertyLevel;
Position m_start;
Position m_end;
bool m_useEndingSelection;
RefPtr<Element> m_styledInlineElement;
bool m_removeOnly;
- IsInlineElementToRemoveFunction m_isInlineElementToRemoveFunction;
+ IsInlineElementToRemoveFunction m_isInlineElementToRemoveFunction { nullptr };
};
enum ShouldStyleAttributeBeEmpty { AllowNonEmptyStyleAttribute, StyleAttributeShouldBeEmpty };
bool isEmptyFontTag(const Element*, ShouldStyleAttributeBeEmpty = StyleAttributeShouldBeEmpty);
bool isLegacyAppleStyleSpan(const Node*);
bool isStyleSpanOrSpanWithOnlyStyleAttribute(const Element*);
-PassRefPtr<HTMLElement> createStyleSpanElement(Document&);
+RefPtr<HTMLElement> createStyleSpanElement(Document&);
} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/editing/BreakBlockquoteCommand.cpp b/Source/WebCore/editing/BreakBlockquoteCommand.cpp
index dde7fcb93..f1af20efb 100644
--- a/Source/WebCore/editing/BreakBlockquoteCommand.cpp
+++ b/Source/WebCore/editing/BreakBlockquoteCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -26,7 +26,7 @@
#include "config.h"
#include "BreakBlockquoteCommand.h"
-#include "HTMLElement.h"
+#include "HTMLBRElement.h"
#include "HTMLNames.h"
#include "NodeTraversal.h"
#include "RenderListItem.h"
@@ -70,25 +70,25 @@ void BreakBlockquoteCommand::doApply()
if (!topBlockquote || !topBlockquote->parentNode() || !topBlockquote->isElementNode())
return;
- RefPtr<Element> breakNode = createBreakElement(document());
+ auto breakNode = HTMLBRElement::create(document());
bool isLastVisPosInNode = isLastVisiblePositionInNode(visiblePos, topBlockquote);
// If the position is at the beginning of the top quoted content, we don't need to break the quote.
// Instead, insert the break before the blockquote, unless the position is as the end of the the quoted content.
if (isFirstVisiblePositionInNode(visiblePos, topBlockquote) && !isLastVisPosInNode) {
- insertNodeBefore(breakNode.get(), topBlockquote);
- setEndingSelection(VisibleSelection(positionBeforeNode(breakNode.get()), DOWNSTREAM, endingSelection().isDirectional()));
+ insertNodeBefore(breakNode.copyRef(), topBlockquote);
+ setEndingSelection(VisibleSelection(positionBeforeNode(breakNode.ptr()), DOWNSTREAM, endingSelection().isDirectional()));
rebalanceWhitespace();
return;
}
// Insert a break after the top blockquote.
- insertNodeAfter(breakNode.get(), topBlockquote);
+ insertNodeAfter(breakNode.copyRef(), topBlockquote);
// If we're inserting the break at the end of the quoted content, we don't need to break the quote.
if (isLastVisPosInNode) {
- setEndingSelection(VisibleSelection(positionBeforeNode(breakNode.get()), DOWNSTREAM, endingSelection().isDirectional()));
+ setEndingSelection(VisibleSelection(positionBeforeNode(breakNode.ptr()), DOWNSTREAM, endingSelection().isDirectional()));
rebalanceWhitespace();
return;
}
@@ -104,23 +104,23 @@ void BreakBlockquoteCommand::doApply()
// startNode is the first node that we need to move to the new blockquote.
Node* startNode = pos.deprecatedNode();
-
+ ASSERT(startNode);
// Split at pos if in the middle of a text node.
- if (startNode->isTextNode()) {
- Text* textNode = toText(startNode);
- if ((unsigned)pos.deprecatedEditingOffset() >= textNode->length()) {
- startNode = NodeTraversal::next(startNode);
+ if (is<Text>(*startNode)) {
+ Text& textNode = downcast<Text>(*startNode);
+ if ((unsigned)pos.deprecatedEditingOffset() >= textNode.length()) {
+ startNode = NodeTraversal::next(*startNode);
ASSERT(startNode);
} else if (pos.deprecatedEditingOffset() > 0)
- splitTextNode(textNode, pos.deprecatedEditingOffset());
+ splitTextNode(&textNode, pos.deprecatedEditingOffset());
} else if (pos.deprecatedEditingOffset() > 0) {
- Node* childAtOffset = startNode->childNode(pos.deprecatedEditingOffset());
- startNode = childAtOffset ? childAtOffset : NodeTraversal::next(startNode);
+ Node* childAtOffset = startNode->traverseToChildAt(pos.deprecatedEditingOffset());
+ startNode = childAtOffset ? childAtOffset : NodeTraversal::next(*startNode);
ASSERT(startNode);
}
// If there's nothing inside topBlockquote to move, we're finished.
- if (!startNode->isDescendantOf(topBlockquote)) {
+ if (!startNode->isDescendantOf(*topBlockquote)) {
setEndingSelection(VisibleSelection(VisiblePosition(firstPositionInOrBeforeNode(startNode)), endingSelection().isDirectional()));
return;
}
@@ -131,8 +131,8 @@ void BreakBlockquoteCommand::doApply()
ancestors.append(node);
// Insert a clone of the top blockquote after the break.
- RefPtr<Element> clonedBlockquote = toElement(topBlockquote)->cloneElementWithoutChildren();
- insertNodeAfter(clonedBlockquote.get(), breakNode.get());
+ RefPtr<Element> clonedBlockquote = downcast<Element>(*topBlockquote).cloneElementWithoutChildren(document());
+ insertNodeAfter(clonedBlockquote.get(), breakNode.copyRef());
// Clone startNode's ancestors into the cloned blockquote.
// On exiting this loop, clonedAncestor is the lowest ancestor
@@ -140,7 +140,7 @@ void BreakBlockquoteCommand::doApply()
// or clonedBlockquote if ancestors is empty).
RefPtr<Element> clonedAncestor = clonedBlockquote;
for (size_t i = ancestors.size(); i != 0; --i) {
- RefPtr<Element> clonedChild = ancestors[i - 1]->cloneElementWithoutChildren();
+ RefPtr<Element> clonedChild = ancestors[i - 1]->cloneElementWithoutChildren(document());
// Preserve list item numbering in cloned lists.
if (clonedChild->isElementNode() && clonedChild->hasTagName(olTag)) {
Node* listChildNode = i > 1 ? ancestors[i - 2].get() : startNode;
@@ -148,8 +148,8 @@ void BreakBlockquoteCommand::doApply()
// find the first one so that we know where to start numbering.
while (listChildNode && !listChildNode->hasTagName(liTag))
listChildNode = listChildNode->nextSibling();
- if (listChildNode && listChildNode->renderer() && listChildNode->renderer()->isListItem())
- setNodeAttribute(clonedChild, startAttr, AtomicString::number(toRenderListItem(listChildNode->renderer())->value()));
+ if (listChildNode && is<RenderListItem>(listChildNode->renderer()))
+ setNodeAttribute(clonedChild, startAttr, AtomicString::number(downcast<RenderListItem>(*listChildNode->renderer()).value()));
}
appendNode(clonedChild.get(), clonedAncestor.get());
@@ -180,7 +180,7 @@ void BreakBlockquoteCommand::doApply()
addBlockPlaceholderIfNeeded(clonedBlockquote.get());
// Put the selection right before the break.
- setEndingSelection(VisibleSelection(positionBeforeNode(breakNode.get()), DOWNSTREAM, endingSelection().isDirectional()));
+ setEndingSelection(VisibleSelection(positionBeforeNode(breakNode.ptr()), DOWNSTREAM, endingSelection().isDirectional()));
rebalanceWhitespace();
}
diff --git a/Source/WebCore/editing/BreakBlockquoteCommand.h b/Source/WebCore/editing/BreakBlockquoteCommand.h
index 90c2c6b6c..57da07015 100644
--- a/Source/WebCore/editing/BreakBlockquoteCommand.h
+++ b/Source/WebCore/editing/BreakBlockquoteCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef BreakBlockquoteCommand_h
-#define BreakBlockquoteCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -32,16 +31,14 @@ namespace WebCore {
class BreakBlockquoteCommand : public CompositeEditCommand {
public:
- static PassRefPtr<BreakBlockquoteCommand> create(Document& document)
+ static Ref<BreakBlockquoteCommand> create(Document& document)
{
- return adoptRef(new BreakBlockquoteCommand(document));
+ return adoptRef(*new BreakBlockquoteCommand(document));
}
private:
explicit BreakBlockquoteCommand(Document&);
- virtual void doApply() override;
+ void doApply() override;
};
} // namespace WebCore
-
-#endif
diff --git a/Source/WebCore/editing/ClipboardAccessPolicy.h b/Source/WebCore/editing/ClipboardAccessPolicy.h
new file mode 100644
index 000000000..f1c6c52f2
--- /dev/null
+++ b/Source/WebCore/editing/ClipboardAccessPolicy.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 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. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#pragma once
+
+namespace WebCore {
+
+enum class ClipboardAccessPolicy {
+ Allow,
+ Deny,
+ RequiresUserGesture
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/editing/CompositeEditCommand.cpp b/Source/WebCore/editing/CompositeEditCommand.cpp
index 9cd2ba987..61ddbf50e 100644
--- a/Source/WebCore/editing/CompositeEditCommand.cpp
+++ b/Source/WebCore/editing/CompositeEditCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -26,8 +26,11 @@
#include "config.h"
#include "CompositeEditCommand.h"
+#include "AXObjectCache.h"
#include "AppendNodeCommand.h"
#include "ApplyStyleCommand.h"
+#include "BreakBlockquoteCommand.h"
+#include "DataTransfer.h"
#include "DeleteFromTextNodeCommand.h"
#include "DeleteSelectionCommand.h"
#include "Document.h"
@@ -36,10 +39,13 @@
#include "Editor.h"
#include "EditorInsertAction.h"
#include "ElementTraversal.h"
-#include "ExceptionCodePlaceholder.h"
+#include "Event.h"
#include "Frame.h"
-#include "HTMLElement.h"
+#include "HTMLBRElement.h"
+#include "HTMLDivElement.h"
+#include "HTMLLIElement.h"
#include "HTMLNames.h"
+#include "HTMLSpanElement.h"
#include "InlineTextBox.h"
#include "InsertIntoTextNodeCommand.h"
#include "InsertLineBreakCommand.h"
@@ -52,15 +58,17 @@
#include "RemoveCSSPropertyCommand.h"
#include "RemoveNodeCommand.h"
#include "RemoveNodePreservingChildrenCommand.h"
+#include "RenderBlockFlow.h"
+#include "RenderText.h"
+#include "RenderedDocumentMarker.h"
#include "ReplaceNodeWithSpanCommand.h"
#include "ReplaceSelectionCommand.h"
-#include "RenderBlock.h"
-#include "RenderText.h"
#include "ScopedEventQueue.h"
#include "SetNodeAttributeCommand.h"
#include "SplitElementCommand.h"
#include "SplitTextNodeCommand.h"
#include "SplitTextNodeContainingElementCommand.h"
+#include "StaticRange.h"
#include "Text.h"
#include "TextIterator.h"
#include "VisibleUnits.h"
@@ -68,22 +76,127 @@
#include "htmlediting.h"
#include "markup.h"
-#if ENABLE(DELETION_UI)
-#include "DeleteButtonController.h"
-#endif
-
-#if PLATFORM(IOS)
-#include "BreakBlockquoteCommand.h"
-#endif
-
namespace WebCore {
using namespace HTMLNames;
-PassRefPtr<EditCommandComposition> EditCommandComposition::create(Document& document,
+int AccessibilityUndoReplacedText::indexForVisiblePosition(const VisiblePosition& position, RefPtr<ContainerNode>& scope) const
+{
+ if (position.deepEquivalent().isNull())
+ return -1;
+ return WebCore::indexForVisiblePosition(position, scope);
+}
+
+void AccessibilityUndoReplacedText::configureRangeDeletedByReapplyWithEndingSelection(const VisibleSelection& selection)
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ if (selection.isNone())
+ return;
+ m_rangeDeletedByReapply.endIndex.value = indexForVisiblePosition(selection.end(), m_rangeDeletedByReapply.endIndex.scope);
+}
+
+void AccessibilityUndoReplacedText::configureRangeDeletedByReapplyWithStartingSelection(const VisibleSelection& selection)
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ if (selection.isNone())
+ return;
+ if (m_rangeDeletedByReapply.startIndex.value == -1)
+ m_rangeDeletedByReapply.startIndex.value = indexForVisiblePosition(selection.start(), m_rangeDeletedByReapply.startIndex.scope);
+}
+
+void AccessibilityUndoReplacedText::setRangeDeletedByUnapply(const VisiblePositionIndexRange& range)
+{
+ if (m_rangeDeletedByUnapply.isNull())
+ m_rangeDeletedByUnapply = range;
+}
+
+void AccessibilityUndoReplacedText::captureTextForUnapply()
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ m_replacedText = textDeletedByReapply();
+}
+
+void AccessibilityUndoReplacedText::captureTextForReapply()
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ m_replacedText = textDeletedByUnapply();
+}
+
+static String stringForVisiblePositionIndexRange(const VisiblePositionIndexRange& range)
+{
+ if (range.isNull())
+ return String();
+ VisiblePosition start = visiblePositionForIndex(range.startIndex.value, range.startIndex.scope.get());
+ VisiblePosition end = visiblePositionForIndex(range.endIndex.value, range.endIndex.scope.get());
+ return AccessibilityObject::stringForVisiblePositionRange(VisiblePositionRange(start, end));
+}
+
+String AccessibilityUndoReplacedText::textDeletedByUnapply()
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return String();
+ return stringForVisiblePositionIndexRange(m_rangeDeletedByUnapply);
+}
+
+String AccessibilityUndoReplacedText::textDeletedByReapply()
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return String();
+ return stringForVisiblePositionIndexRange(m_rangeDeletedByReapply);
+}
+
+static void postTextStateChangeNotification(AXObjectCache* cache, const VisiblePosition& position, const String& deletedText, const String& insertedText)
+{
+ ASSERT(cache);
+ auto* node = highestEditableRoot(position.deepEquivalent(), HasEditableAXRole);
+ if (!node)
+ return;
+ if (insertedText.length() && deletedText.length())
+ cache->postTextReplacementNotification(node, AXTextEditTypeDelete, insertedText, AXTextEditTypeInsert, deletedText, position);
+ else if (deletedText.length())
+ cache->postTextStateChangeNotification(node, AXTextEditTypeInsert, deletedText, position);
+ else if (insertedText.length())
+ cache->postTextStateChangeNotification(node, AXTextEditTypeDelete, insertedText, position);
+}
+
+void AccessibilityUndoReplacedText::postTextStateChangeNotificationForUnapply(AXObjectCache* cache)
+{
+ if (!cache)
+ return;
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ if (m_rangeDeletedByUnapply.isNull())
+ return;
+ VisiblePosition position = visiblePositionForIndex(m_rangeDeletedByUnapply.endIndex.value, m_rangeDeletedByUnapply.endIndex.scope.get());
+ if (position.isNull())
+ return;
+ postTextStateChangeNotification(cache, position, textDeletedByUnapply(), m_replacedText);
+ m_replacedText = String();
+}
+
+void AccessibilityUndoReplacedText::postTextStateChangeNotificationForReapply(AXObjectCache* cache)
+{
+ if (!cache)
+ return;
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ if (m_rangeDeletedByReapply.isNull())
+ return;
+ VisiblePosition position = visiblePositionForIndex(m_rangeDeletedByReapply.startIndex.value, m_rangeDeletedByReapply.startIndex.scope.get());
+ if (position.isNull())
+ return;
+ postTextStateChangeNotification(cache, position, textDeletedByReapply(), m_replacedText);
+ m_replacedText = String();
+}
+
+Ref<EditCommandComposition> EditCommandComposition::create(Document& document,
const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction)
{
- return adoptRef(new EditCommandComposition(document, startingSelection, endingSelection, editAction));
+ return adoptRef(*new EditCommandComposition(document, startingSelection, endingSelection, editAction));
}
EditCommandComposition::EditCommandComposition(Document& document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction editAction)
@@ -94,13 +207,17 @@ EditCommandComposition::EditCommandComposition(Document& document, const Visible
, m_endingRootEditableElement(endingSelection.rootEditableElement())
, m_editAction(editAction)
{
+ m_replacedText.configureRangeDeletedByReapplyWithStartingSelection(startingSelection);
}
void EditCommandComposition::unapply()
{
ASSERT(m_document);
RefPtr<Frame> frame = m_document->frame();
- ASSERT(frame);
+ if (!frame)
+ return;
+
+ m_replacedText.captureTextForUnapply();
// Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
// Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
@@ -115,40 +232,43 @@ void EditCommandComposition::unapply()
frame->editor().cancelComposition();
#endif
- {
-#if ENABLE(DELETION_UI)
- DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(frame.get());
-#endif
+ if (!frame->editor().willUnapplyEditing(*this))
+ return;
- size_t size = m_commands.size();
- for (size_t i = size; i; --i)
- m_commands[i - 1]->doUnapply();
- }
+ size_t size = m_commands.size();
+ for (size_t i = size; i; --i)
+ m_commands[i - 1]->doUnapply();
+
+ frame->editor().unappliedEditing(*this);
- frame->editor().unappliedEditing(this);
+ if (AXObjectCache::accessibilityEnabled())
+ m_replacedText.postTextStateChangeNotificationForUnapply(m_document->existingAXObjectCache());
}
void EditCommandComposition::reapply()
{
ASSERT(m_document);
RefPtr<Frame> frame = m_document->frame();
- ASSERT(frame);
+ if (!frame)
+ return;
+
+ m_replacedText.captureTextForReapply();
// Changes to the document may have been made since the last editing operation that require a layout, as in <rdar://problem/5658603>.
// Low level operations, like RemoveNodeCommand, don't require a layout because the high level operations that use them perform one
// if one is necessary (like for the creation of VisiblePositions).
m_document->updateLayoutIgnorePendingStylesheets();
- {
-#if ENABLE(DELETION_UI)
- DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(frame.get());
-#endif
- size_t size = m_commands.size();
- for (size_t i = 0; i != size; ++i)
- m_commands[i]->doReapply();
- }
-
- frame->editor().reappliedEditing(this);
+ if (!frame->editor().willReapplyEditing(*this))
+ return;
+
+ for (auto& command : m_commands)
+ command->doReapply();
+
+ frame->editor().reappliedEditing(*this);
+
+ if (AXObjectCache::accessibilityEnabled())
+ m_replacedText.postTextStateChangeNotificationForReapply(m_document->existingAXObjectCache());
}
void EditCommandComposition::append(SimpleEditCommand* command)
@@ -160,20 +280,26 @@ void EditCommandComposition::setStartingSelection(const VisibleSelection& select
{
m_startingSelection = selection;
m_startingRootEditableElement = selection.rootEditableElement();
+ m_replacedText.configureRangeDeletedByReapplyWithStartingSelection(selection);
}
void EditCommandComposition::setEndingSelection(const VisibleSelection& selection)
{
m_endingSelection = selection;
m_endingRootEditableElement = selection.rootEditableElement();
+ m_replacedText.configureRangeDeletedByReapplyWithEndingSelection(selection);
+}
+
+void EditCommandComposition::setRangeDeletedByUnapply(const VisiblePositionIndexRange& range)
+{
+ m_replacedText.setRangeDeletedByUnapply(range);
}
#ifndef NDEBUG
void EditCommandComposition::getNodesInCommand(HashSet<Node*>& nodes)
{
- size_t size = m_commands.size();
- for (size_t i = 0; i < size; ++i)
- m_commands[i]->getNodesInCommand(nodes);
+ for (auto& command : m_commands)
+ command->getNodesInCommand(nodes);
}
#endif
@@ -182,8 +308,8 @@ void applyCommand(PassRefPtr<CompositeEditCommand> command)
command->apply();
}
-CompositeEditCommand::CompositeEditCommand(Document& document)
- : EditCommand(document)
+CompositeEditCommand::CompositeEditCommand(Document& document, EditAction editingAction)
+ : EditCommand(document, editingAction)
{
}
@@ -192,20 +318,39 @@ CompositeEditCommand::~CompositeEditCommand()
ASSERT(isTopLevelCommand() || !m_composition);
}
+bool CompositeEditCommand::willApplyCommand()
+{
+ return frame().editor().willApplyEditing(*this, targetRangesForBindings());
+}
+
void CompositeEditCommand::apply()
{
if (!endingSelection().isContentRichlyEditable()) {
switch (editingAction()) {
- case EditActionTyping:
+ case EditActionTypingDeleteSelection:
+ case EditActionTypingDeleteBackward:
+ case EditActionTypingDeleteForward:
+ case EditActionTypingDeleteWordBackward:
+ case EditActionTypingDeleteWordForward:
+ case EditActionTypingDeleteLineBackward:
+ case EditActionTypingDeleteLineForward:
+ case EditActionTypingDeletePendingComposition:
+ case EditActionTypingDeleteFinalComposition:
+ case EditActionTypingInsertText:
+ case EditActionTypingInsertLineBreak:
+ case EditActionTypingInsertParagraph:
+ case EditActionTypingInsertPendingComposition:
+ case EditActionTypingInsertFinalComposition:
case EditActionPaste:
- case EditActionDrag:
+ case EditActionDeleteByDrag:
case EditActionSetWritingDirection:
case EditActionCut:
case EditActionUnspecified:
-#if PLATFORM(IOS)
+ case EditActionInsert:
+ case EditActionInsertReplacement:
+ case EditActionInsertFromDrop:
case EditActionDelete:
case EditActionDictation:
-#endif
break;
default:
ASSERT_NOT_REACHED();
@@ -219,29 +364,66 @@ void CompositeEditCommand::apply()
// if one is necessary (like for the creation of VisiblePositions).
document().updateLayoutIgnorePendingStylesheets();
+ if (!willApplyCommand())
+ return;
+
{
- EventQueueScope scope;
-#if ENABLE(DELETION_UI)
- DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(&frame());
-#endif
+ EventQueueScope eventQueueScope;
doApply();
}
- // Only need to call appliedEditing for top-level commands,
- // and TypingCommands do it on their own (see TypingCommand::typingAddedToOpenCommand).
- if (!isTypingCommand())
- frame().editor().appliedEditing(this);
+ didApplyCommand();
setShouldRetainAutocorrectionIndicator(false);
}
-EditCommandComposition* CompositeEditCommand::ensureComposition()
+void CompositeEditCommand::didApplyCommand()
+{
+ frame().editor().appliedEditing(this);
+}
+
+Vector<RefPtr<StaticRange>> CompositeEditCommand::targetRanges() const
+{
+ ASSERT(!isEditingTextAreaOrTextInput());
+ auto firstRange = frame().selection().selection().firstRange();
+ if (!firstRange)
+ return { };
+
+ RefPtr<StaticRange> range = StaticRange::createFromRange(*firstRange);
+ return { 1, range };
+}
+
+Vector<RefPtr<StaticRange>> CompositeEditCommand::targetRangesForBindings() const
{
- CompositeEditCommand* command = this;
- while (command && command->parent())
- command = command->parent();
+ if (isEditingTextAreaOrTextInput())
+ return { };
+
+ return targetRanges();
+}
+
+RefPtr<DataTransfer> CompositeEditCommand::inputEventDataTransfer() const
+{
+ return nullptr;
+}
+
+EditCommandComposition* CompositeEditCommand::composition() const
+{
+ for (auto* command = this; command; command = command->parent()) {
+ if (auto composition = command->m_composition) {
+ ASSERT(!command->parent());
+ return composition.get();
+ }
+ }
+ return nullptr;
+}
+
+EditCommandComposition& CompositeEditCommand::ensureComposition()
+{
+ auto* command = this;
+ while (auto* parent = command->parent())
+ command = parent;
if (!command->m_composition)
command->m_composition = EditCommandComposition::create(document(), startingSelection(), endingSelection(), editingAction());
- return command->m_composition.get();
+ return *command->m_composition;
}
bool CompositeEditCommand::isCreateLinkCommand() const
@@ -268,6 +450,11 @@ void CompositeEditCommand::setShouldRetainAutocorrectionIndicator(bool)
{
}
+String CompositeEditCommand::inputEventTypeName() const
+{
+ return inputTypeNameForEditingAction(editingAction());
+}
+
//
// sugary-sweet convenience functions to help create and apply edit commands in composite commands
//
@@ -277,10 +464,10 @@ void CompositeEditCommand::applyCommandToComposite(PassRefPtr<EditCommand> prpCo
command->setParent(this);
command->doApply();
if (command->isSimpleEditCommand()) {
- command->setParent(0);
- ensureComposition()->append(toSimpleEditCommand(command.get()));
+ command->setParent(nullptr);
+ ensureComposition().append(toSimpleEditCommand(command.get()));
}
- m_commands.append(command.release());
+ m_commands.append(WTFMove(command));
}
void CompositeEditCommand::applyCommandToComposite(PassRefPtr<CompositeEditCommand> command, const VisibleSelection& selection)
@@ -316,7 +503,7 @@ void CompositeEditCommand::removeStyledElement(PassRefPtr<Element> element)
void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea)
{
- applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea));
+ applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea, editingAction()));
}
void CompositeEditCommand::insertLineBreak()
@@ -326,14 +513,15 @@ void CompositeEditCommand::insertLineBreak()
bool CompositeEditCommand::isRemovableBlock(const Node* node)
{
- if (!node->hasTagName(divTag))
+ ASSERT(node);
+ if (!is<HTMLDivElement>(*node))
return false;
Node* parentNode = node->parentNode();
if (parentNode && parentNode->firstChild() != parentNode->lastChild())
return false;
- if (!toElement(node)->hasAttributes())
+ if (!downcast<HTMLDivElement>(*node).hasAttributes())
return true;
return false;
@@ -341,17 +529,17 @@ bool CompositeEditCommand::isRemovableBlock(const Node* node)
void CompositeEditCommand::insertNodeBefore(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
{
- ASSERT(!refChild->hasTagName(bodyTag));
- applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChild, shouldAssumeContentIsAlwaysEditable));
+ applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChild, shouldAssumeContentIsAlwaysEditable, editingAction()));
}
void CompositeEditCommand::insertNodeAfter(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild)
{
ASSERT(insertChild);
ASSERT(refChild);
- ASSERT(!refChild->hasTagName(bodyTag));
ContainerNode* parent = refChild->parentNode();
- ASSERT(parent);
+ if (!parent)
+ return;
+
ASSERT(!parent->isShadowRoot());
if (parent->lastChild() == refChild)
appendNode(insertChild, parent);
@@ -370,21 +558,21 @@ void CompositeEditCommand::insertNodeAt(PassRefPtr<Node> insertChild, const Posi
Node* refChild = p.deprecatedNode();
int offset = p.deprecatedEditingOffset();
- if (canHaveChildrenForEditing(refChild)) {
+ if (canHaveChildrenForEditing(*refChild)) {
Node* child = refChild->firstChild();
for (int i = 0; child && i < offset; i++)
child = child->nextSibling();
if (child)
insertNodeBefore(insertChild, child);
else
- appendNode(insertChild, toContainerNode(refChild));
- } else if (caretMinOffset(refChild) >= offset)
+ appendNode(insertChild, downcast<ContainerNode>(refChild));
+ } else if (caretMinOffset(*refChild) >= offset)
insertNodeBefore(insertChild, refChild);
- else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) {
- splitTextNode(toText(refChild), offset);
+ else if (is<Text>(*refChild) && caretMaxOffset(*refChild) > offset) {
+ splitTextNode(downcast<Text>(refChild), offset);
// Mutation events (bug 22634) from the text node insertion may have removed the refChild
- if (!refChild->inDocument())
+ if (!refChild->isConnected())
return;
insertNodeBefore(insertChild, refChild);
} else
@@ -393,39 +581,39 @@ void CompositeEditCommand::insertNodeAt(PassRefPtr<Node> insertChild, const Posi
void CompositeEditCommand::appendNode(PassRefPtr<Node> node, PassRefPtr<ContainerNode> parent)
{
- ASSERT(canHaveChildrenForEditing(parent.get()));
- applyCommandToComposite(AppendNodeCommand::create(parent, node));
+ ASSERT(canHaveChildrenForEditing(*parent));
+ ASSERT(node);
+ applyCommandToComposite(AppendNodeCommand::create(parent, *node, editingAction()));
}
void CompositeEditCommand::removeChildrenInRange(PassRefPtr<Node> node, unsigned from, unsigned to)
{
Vector<RefPtr<Node>> children;
- Node* child = node->childNode(from);
+ Node* child = node->traverseToChildAt(from);
for (unsigned i = from; child && i < to; i++, child = child->nextSibling())
children.append(child);
- size_t size = children.size();
- for (size_t i = 0; i < size; ++i)
- removeNode(children[i].release());
+ for (auto& child : children)
+ removeNode(WTFMove(child));
}
void CompositeEditCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
{
if (!node || !node->nonShadowBoundaryParentNode())
return;
- applyCommandToComposite(RemoveNodeCommand::create(node, shouldAssumeContentIsAlwaysEditable));
+ applyCommandToComposite(RemoveNodeCommand::create(*node, shouldAssumeContentIsAlwaysEditable, editingAction()));
}
void CompositeEditCommand::removeNodePreservingChildren(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
{
- applyCommandToComposite(RemoveNodePreservingChildrenCommand::create(node, shouldAssumeContentIsAlwaysEditable));
+ applyCommandToComposite(RemoveNodePreservingChildrenCommand::create(node, shouldAssumeContentIsAlwaysEditable, editingAction()));
}
void CompositeEditCommand::removeNodeAndPruneAncestors(PassRefPtr<Node> node)
{
RefPtr<ContainerNode> parent = node->parentNode();
removeNode(node);
- prune(parent.release());
+ prune(WTFMove(parent));
}
void CompositeEditCommand::moveRemainingSiblingsToNewParent(Node* node, Node* pastLastNodeToMove, PassRefPtr<Element> prpNewParent)
@@ -436,13 +624,13 @@ void CompositeEditCommand::moveRemainingSiblingsToNewParent(Node* node, Node* pa
for (; node && node != pastLastNodeToMove; node = node->nextSibling())
nodesToRemove.append(*node);
- for (unsigned i = 0; i < nodesToRemove.size(); i++) {
- removeNode(&nodesToRemove[i].get());
- appendNode(&nodesToRemove[i].get(), newParent);
+ for (auto& nodeToRemove : nodesToRemove) {
+ removeNode(nodeToRemove.ptr());
+ appendNode(nodeToRemove.ptr(), newParent);
}
}
-void CompositeEditCommand::updatePositionForNodeRemovalPreservingChildren(Position& position, Node* node)
+void CompositeEditCommand::updatePositionForNodeRemovalPreservingChildren(Position& position, Node& node)
{
int offset = (position.anchorType() == Position::PositionIsOffsetInAnchor) ? position.offsetInContainerNode() : 0;
updatePositionForNodeRemoval(position, node);
@@ -460,14 +648,14 @@ HTMLElement* CompositeEditCommand::replaceElementWithSpanPreservingChildrenAndAt
// Returning a raw pointer here is OK because the command is retained by
// applyCommandToComposite (thus retaining the span), and the span is also
// in the DOM tree, and thus alive whie it has a parent.
- ASSERT(command->spanElement()->inDocument());
+ ASSERT(command->spanElement()->isConnected());
return command->spanElement();
}
void CompositeEditCommand::prune(PassRefPtr<Node> node)
{
if (RefPtr<Node> highestNodeToRemove = highestNodeToRemoveInPruning(node.get()))
- removeNode(highestNodeToRemove.release());
+ removeNode(WTFMove(highestNodeToRemove));
}
void CompositeEditCommand::splitTextNode(PassRefPtr<Text> node, unsigned offset)
@@ -492,7 +680,7 @@ void CompositeEditCommand::mergeIdenticalElements(PassRefPtr<Element> prpFirst,
applyCommandToComposite(MergeIdenticalElementsCommand::create(first, second));
}
-void CompositeEditCommand::wrapContentsInDummySpan(PassRefPtr<Element> element)
+void CompositeEditCommand::wrapContentsInDummySpan(Element& element)
{
applyCommandToComposite(WrapContentsInDummySpanCommand::create(element));
}
@@ -502,7 +690,6 @@ void CompositeEditCommand::splitTextNodeContainingElement(PassRefPtr<Text> text,
applyCommandToComposite(SplitTextNodeContainingElementCommand::create(text, offset));
}
-#if PLATFORM(IOS)
void CompositeEditCommand::inputText(const String& text, bool selectInsertedText)
{
unsigned offset = 0;
@@ -543,25 +730,24 @@ void CompositeEditCommand::inputText(const String& text, bool selectInsertedText
if (selectInsertedText)
setEndingSelection(VisibleSelection(visiblePositionForIndex(startIndex, scope.get()), visiblePositionForIndex(startIndex + length, scope.get())));
}
-#endif
void CompositeEditCommand::insertTextIntoNode(PassRefPtr<Text> node, unsigned offset, const String& text)
{
if (!text.isEmpty())
- applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, text));
+ applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, text, editingAction()));
}
void CompositeEditCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned offset, unsigned count)
{
- applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count));
+ applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count, editingAction()));
}
void CompositeEditCommand::replaceTextInNode(PassRefPtr<Text> prpNode, unsigned offset, unsigned count, const String& replacementText)
{
RefPtr<Text> node(prpNode);
- applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count));
+ applyCommandToComposite(DeleteFromTextNodeCommand::create(WTFMove(node), offset, count));
if (!replacementText.isEmpty())
- applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, replacementText));
+ applyCommandToComposite(InsertIntoTextNodeCommand::create(WTFMove(node), offset, replacementText, editingAction()));
}
Position CompositeEditCommand::replaceSelectedTextInNode(const String& text)
@@ -574,60 +760,61 @@ Position CompositeEditCommand::replaceSelectedTextInNode(const String& text)
RefPtr<Text> textNode = start.containerText();
replaceTextInNode(textNode, start.offsetInContainerNode(), end.offsetInContainerNode() - start.offsetInContainerNode(), text);
- return Position(textNode.release(), start.offsetInContainerNode() + text.length());
+ return Position(textNode.get(), start.offsetInContainerNode() + text.length());
}
-static void copyMarkers(const Vector<DocumentMarker*>& markerPointers, Vector<DocumentMarker>& markers)
+static Vector<RenderedDocumentMarker> copyMarkers(const Vector<RenderedDocumentMarker*>& markerPointers)
{
- size_t arraySize = markerPointers.size();
- markers.reserveCapacity(arraySize);
- for (size_t i = 0; i < arraySize; ++i)
- markers.append(*markerPointers[i]);
+ Vector<RenderedDocumentMarker> markers;
+ markers.reserveInitialCapacity(markerPointers.size());
+ for (auto& markerPointer : markerPointers)
+ markers.uncheckedAppend(*markerPointer);
+
+ return markers;
}
void CompositeEditCommand::replaceTextInNodePreservingMarkers(PassRefPtr<Text> prpNode, unsigned offset, unsigned count, const String& replacementText)
{
RefPtr<Text> node(prpNode);
DocumentMarkerController& markerController = document().markers();
- Vector<DocumentMarker> markers;
- copyMarkers(markerController.markersInRange(Range::create(document(), node, offset, node, offset + count).get(), DocumentMarker::AllMarkers()), markers);
+ auto markers = copyMarkers(markerController.markersInRange(Range::create(document(), node, offset, node, offset + count).ptr(), DocumentMarker::AllMarkers()));
replaceTextInNode(node, offset, count, replacementText);
RefPtr<Range> newRange = Range::create(document(), node, offset, node, offset + replacementText.length());
- for (size_t i = 0; i < markers.size(); ++i)
+ for (const auto& marker : markers)
#if PLATFORM(IOS)
- markerController.addMarker(newRange.get(), markers[i].type(), markers[i].description(), markers[i].alternatives(), markers[i].metadata());
+ markerController.addMarker(newRange.get(), marker.type(), marker.description(), marker.alternatives(), marker.metadata());
#else
- markerController.addMarker(newRange.get(), markers[i].type(), markers[i].description());
+ markerController.addMarker(newRange.get(), marker.type(), marker.description());
#endif // PLATFORM(IOS)
}
-Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
+Position CompositeEditCommand::positionOutsideTabSpan(const Position& position)
{
- if (!isTabSpanTextNode(pos.anchorNode()))
- return pos;
+ if (!isTabSpanTextNode(position.anchorNode()))
+ return position;
- switch (pos.anchorType()) {
+ switch (position.anchorType()) {
case Position::PositionIsBeforeChildren:
case Position::PositionIsAfterChildren:
ASSERT_NOT_REACHED();
- return pos;
+ return position;
case Position::PositionIsOffsetInAnchor:
break;
case Position::PositionIsBeforeAnchor:
- return positionInParentBeforeNode(pos.anchorNode());
+ return positionInParentBeforeNode(position.anchorNode());
case Position::PositionIsAfterAnchor:
- return positionInParentAfterNode(pos.anchorNode());
+ return positionInParentAfterNode(position.anchorNode());
}
- Node* tabSpan = tabSpanNode(pos.containerNode());
+ auto* tabSpan = tabSpanNode(position.containerNode());
- if (pos.offsetInContainerNode() <= caretMinOffset(pos.containerNode()))
+ if (position.offsetInContainerNode() <= caretMinOffset(*position.containerNode()))
return positionInParentBeforeNode(tabSpan);
- if (pos.offsetInContainerNode() >= caretMaxOffset(pos.containerNode()))
+ if (position.offsetInContainerNode() >= caretMaxOffset(*position.containerNode()))
return positionInParentAfterNode(tabSpan);
- splitTextNodeContainingElement(toText(pos.containerNode()), pos.offsetInContainerNode());
+ splitTextNodeContainingElement(&downcast<Text>(*position.containerNode()), position.offsetInContainerNode());
return positionInParentBeforeNode(tabSpan);
}
@@ -637,10 +824,20 @@ void CompositeEditCommand::insertNodeAtTabSpanPosition(PassRefPtr<Node> node, co
insertNodeAt(node, positionOutsideTabSpan(pos));
}
+static EditAction deleteSelectionEditingActionForEditingAction(EditAction editingAction)
+{
+ switch (editingAction) {
+ case EditActionCut:
+ return EditActionCut;
+ default:
+ return EditActionDelete;
+ }
+}
+
void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
{
if (endingSelection().isRange())
- applyCommandToComposite(DeleteSelectionCommand::create(document(), smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
+ applyCommandToComposite(DeleteSelectionCommand::create(document(), smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup, deleteSelectionEditingActionForEditingAction(editingAction())));
}
void CompositeEditCommand::deleteSelection(const VisibleSelection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
@@ -656,7 +853,7 @@ void CompositeEditCommand::removeCSSProperty(PassRefPtr<StyledElement> element,
void CompositeEditCommand::removeNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute)
{
- setNodeAttribute(element, attribute, AtomicString());
+ setNodeAttribute(element, attribute, nullAtom);
}
void CompositeEditCommand::setNodeAttribute(PassRefPtr<Element> element, const QualifiedName& attribute, const AtomicString& value)
@@ -664,34 +861,33 @@ void CompositeEditCommand::setNodeAttribute(PassRefPtr<Element> element, const Q
applyCommandToComposite(SetNodeAttributeCommand::create(element, attribute, value));
}
-static inline bool containsOnlyWhitespace(const String& text)
+static inline bool containsOnlyDeprecatedEditingWhitespace(const String& text)
{
for (unsigned i = 0; i < text.length(); ++i) {
- if (!isWhitespace(text.deprecatedCharacters()[i]))
+ if (!deprecatedIsEditingWhitespace(text[i]))
return false;
}
-
return true;
}
bool CompositeEditCommand::shouldRebalanceLeadingWhitespaceFor(const String& text) const
{
- return containsOnlyWhitespace(text);
+ return containsOnlyDeprecatedEditingWhitespace(text);
}
bool CompositeEditCommand::canRebalance(const Position& position) const
{
Node* node = position.containerNode();
- if (position.anchorType() != Position::PositionIsOffsetInAnchor || !node || !node->isTextNode())
+ if (position.anchorType() != Position::PositionIsOffsetInAnchor || !is<Text>(node))
return false;
- Text* textNode = toText(node);
- if (textNode->length() == 0)
+ Text& textNode = downcast<Text>(*node);
+ if (!textNode.length())
return false;
node->document().updateStyleIfNeeded();
- RenderObject* renderer = textNode->renderer();
+ RenderObject* renderer = textNode.renderer();
if (renderer && !renderer->style().collapseWhiteSpace())
return false;
@@ -707,14 +903,14 @@ void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
// If the rebalance is for the single offset, and neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing.
int offset = position.deprecatedEditingOffset();
- String text = toText(node)->data();
- if (!isWhitespace(text[offset])) {
+ String text = downcast<Text>(*node).data();
+ if (!deprecatedIsEditingWhitespace(text[offset])) {
offset--;
- if (offset < 0 || !isWhitespace(text[offset]))
+ if (offset < 0 || !deprecatedIsEditingWhitespace(text[offset]))
return;
}
- rebalanceWhitespaceOnTextSubstring(toText(node), position.offsetInContainerNode(), position.offsetInContainerNode());
+ rebalanceWhitespaceOnTextSubstring(downcast<Text>(node), position.offsetInContainerNode(), position.offsetInContainerNode());
}
void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(PassRefPtr<Text> prpTextNode, int startOffset, int endOffset)
@@ -726,19 +922,19 @@ void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(PassRefPtr<Text> p
// Set upstream and downstream to define the extent of the whitespace surrounding text[offset].
int upstream = startOffset;
- while (upstream > 0 && isWhitespace(text[upstream - 1]))
+ while (upstream > 0 && deprecatedIsEditingWhitespace(text[upstream - 1]))
upstream--;
int downstream = endOffset;
- while ((unsigned)downstream < text.length() && isWhitespace(text[downstream]))
+ while ((unsigned)downstream < text.length() && deprecatedIsEditingWhitespace(text[downstream]))
downstream++;
int length = downstream - upstream;
if (!length)
return;
- VisiblePosition visibleUpstreamPos(Position(textNode, upstream));
- VisiblePosition visibleDownstreamPos(Position(textNode, downstream));
+ VisiblePosition visibleUpstreamPos(Position(textNode.get(), upstream));
+ VisiblePosition visibleDownstreamPos(Position(textNode.get(), downstream));
String string = text.substring(upstream, length);
String rebalancedString = stringWithRebalancedWhitespace(string,
@@ -748,19 +944,19 @@ void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(PassRefPtr<Text> p
isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length());
if (string != rebalancedString)
- replaceTextInNodePreservingMarkers(textNode.release(), upstream, length, rebalancedString);
+ replaceTextInNodePreservingMarkers(WTFMove(textNode), upstream, length, rebalancedString);
}
void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& position)
{
Node* node = position.deprecatedNode();
- if (!node || !node->isTextNode())
+ if (!is<Text>(node))
return;
- Text* textNode = toText(node);
+ Text& textNode = downcast<Text>(*node);
- if (textNode->length() == 0)
+ if (!textNode.length())
return;
- RenderObject* renderer = textNode->renderer();
+ RenderObject* renderer = textNode.renderer();
if (renderer && !renderer->style().collapseWhiteSpace())
return;
@@ -773,10 +969,10 @@ void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& positio
VisiblePosition previousVisiblePos(visiblePos.previous());
Position previous(previousVisiblePos.deepEquivalent());
- if (isCollapsibleWhitespace(previousVisiblePos.characterAfter()) && previous.deprecatedNode()->isTextNode() && !previous.deprecatedNode()->hasTagName(brTag))
- replaceTextInNodePreservingMarkers(toText(previous.deprecatedNode()), previous.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
- if (isCollapsibleWhitespace(visiblePos.characterAfter()) && position.deprecatedNode()->isTextNode() && !position.deprecatedNode()->hasTagName(brTag))
- replaceTextInNodePreservingMarkers(toText(position.deprecatedNode()), position.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
+ if (deprecatedIsCollapsibleWhitespace(previousVisiblePos.characterAfter()) && is<Text>(*previous.deprecatedNode()) && !is<HTMLBRElement>(*previous.deprecatedNode()))
+ replaceTextInNodePreservingMarkers(downcast<Text>(previous.deprecatedNode()), previous.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
+ if (deprecatedIsCollapsibleWhitespace(visiblePos.characterAfter()) && is<Text>(*position.deprecatedNode()) && !is<HTMLBRElement>(*position.deprecatedNode()))
+ replaceTextInNodePreservingMarkers(downcast<Text>(position.deprecatedNode()), position.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
}
void CompositeEditCommand::rebalanceWhitespace()
@@ -824,7 +1020,7 @@ void CompositeEditCommand::deleteInsignificantText(PassRefPtr<Text> textNode, un
return;
unsigned removed = 0;
- InlineTextBox* prevBox = 0;
+ InlineTextBox* prevBox = nullptr;
String str;
// This loop structure works to process all gaps preceding a box,
@@ -853,7 +1049,7 @@ void CompositeEditCommand::deleteInsignificantText(PassRefPtr<Text> textNode, un
if (++sortedTextBoxesPosition < sortedTextBoxes.size())
box = sortedTextBoxes[sortedTextBoxesPosition];
else
- box = 0;
+ box = nullptr;
}
}
@@ -880,15 +1076,14 @@ void CompositeEditCommand::deleteInsignificantText(const Position& start, const
return;
Vector<RefPtr<Text>> nodes;
- for (Node* node = start.deprecatedNode(); node; node = NodeTraversal::next(node)) {
- if (node->isTextNode())
- nodes.append(toText(node));
+ for (Node* node = start.deprecatedNode(); node; node = NodeTraversal::next(*node)) {
+ if (is<Text>(*node))
+ nodes.append(downcast<Text>(node));
if (node == end.deprecatedNode())
break;
}
- for (size_t i = 0; i < nodes.size(); ++i) {
- Text* textNode = nodes[i].get();
+ for (auto& textNode : nodes) {
int startOffset = textNode == start.deprecatedNode() ? start.deprecatedEditingOffset() : 0;
int endOffset = textNode == end.deprecatedNode() ? end.deprecatedEditingOffset() : static_cast<int>(textNode->length());
deleteInsignificantText(textNode, startOffset, endOffset);
@@ -901,52 +1096,51 @@ void CompositeEditCommand::deleteInsignificantTextDownstream(const Position& pos
deleteInsignificantText(pos, end);
}
-PassRefPtr<Node> CompositeEditCommand::appendBlockPlaceholder(PassRefPtr<Element> container)
+RefPtr<Node> CompositeEditCommand::appendBlockPlaceholder(PassRefPtr<Element> container)
{
if (!container)
- return 0;
+ return nullptr;
document().updateLayoutIgnorePendingStylesheets();
// Should assert isBlockFlow || isInlineFlow when deletion improves. See 4244964.
ASSERT(container->renderer());
- RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
- appendNode(placeholder, container);
- return placeholder.release();
+ auto placeholder = createBlockPlaceholderElement(document());
+ appendNode(placeholder.ptr(), container);
+ return WTFMove(placeholder);
}
-PassRefPtr<Node> CompositeEditCommand::insertBlockPlaceholder(const Position& pos)
+RefPtr<Node> CompositeEditCommand::insertBlockPlaceholder(const Position& pos)
{
if (pos.isNull())
- return 0;
+ return nullptr;
// Should assert isBlockFlow || isInlineFlow when deletion improves. See 4244964.
ASSERT(pos.deprecatedNode()->renderer());
- RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
- insertNodeAt(placeholder, pos);
- return placeholder.release();
+ auto placeholder = createBlockPlaceholderElement(document());
+ insertNodeAt(placeholder.ptr(), pos);
+ return WTFMove(placeholder);
}
-PassRefPtr<Node> CompositeEditCommand::addBlockPlaceholderIfNeeded(Element* container)
+RefPtr<Node> CompositeEditCommand::addBlockPlaceholderIfNeeded(Element* container)
{
if (!container)
- return 0;
+ return nullptr;
document().updateLayoutIgnorePendingStylesheets();
- RenderObject* renderer = container->renderer();
- if (!renderer || !renderer->isRenderBlockFlow())
- return 0;
+ auto* renderer = container->renderer();
+ if (!is<RenderBlockFlow>(renderer))
+ return nullptr;
- // append the placeholder to make sure it follows
- // any unrendered blocks
- RenderBlock* block = toRenderBlock(renderer);
- if (block->height() == 0 || (block->isListItem() && block->isEmpty()))
+ // Append the placeholder to make sure it follows any unrendered blocks.
+ auto& blockFlow = downcast<RenderBlockFlow>(*renderer);
+ if (!blockFlow.height() || (blockFlow.isListItem() && !blockFlow.firstChild()))
return appendBlockPlaceholder(container);
- return 0;
+ return nullptr;
}
// Assumes that the position is at a placeholder and does the removal without much checking.
@@ -955,28 +1149,28 @@ void CompositeEditCommand::removePlaceholderAt(const Position& p)
ASSERT(lineBreakExistsAtPosition(p));
// We are certain that the position is at a line break, but it may be a br or a preserved newline.
- if (p.anchorNode()->hasTagName(brTag)) {
+ if (is<HTMLBRElement>(*p.anchorNode())) {
removeNode(p.anchorNode());
return;
}
- deleteTextFromNode(toText(p.anchorNode()), p.offsetInContainerNode(), 1);
+ deleteTextFromNode(downcast<Text>(p.anchorNode()), p.offsetInContainerNode(), 1);
}
-PassRefPtr<Node> CompositeEditCommand::insertNewDefaultParagraphElementAt(const Position& position)
+Ref<HTMLElement> CompositeEditCommand::insertNewDefaultParagraphElementAt(const Position& position)
{
- RefPtr<Element> paragraphElement = createDefaultParagraphElement(document());
- paragraphElement->appendChild(createBreakElement(document()), IGNORE_EXCEPTION);
- insertNodeAt(paragraphElement, position);
- return paragraphElement.release();
+ auto paragraphElement = createDefaultParagraphElement(document());
+ paragraphElement->appendChild(HTMLBRElement::create(document()));
+ insertNodeAt(paragraphElement.ptr(), position);
+ return paragraphElement;
}
// If the paragraph is not entirely within it's own block, create one and move the paragraph into
// it, and return that block. Otherwise return 0.
-PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position& pos)
+RefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position& pos)
{
if (pos.isNull())
- return 0;
+ return nullptr;
document().updateLayoutIgnorePendingStylesheets();
@@ -994,7 +1188,7 @@ PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessar
// If there are no VisiblePositions in the same block as pos then
// upstreamStart will be outside the paragraph
if (comparePositions(pos, upstreamStart) < 0)
- return 0;
+ return nullptr;
// Perform some checks to see if we need to perform work in this function.
if (isBlock(upstreamStart.deprecatedNode())) {
@@ -1003,35 +1197,38 @@ PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessar
if (upstreamStart.deprecatedNode() == editableRootForPosition(upstreamStart)) {
// If the block is the root editable element and it contains no visible content, create a new
// block but don't try and move content into it, since there's nothing for moveParagraphs to move.
- if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(toRenderElement(*upstreamStart.deprecatedNode()->renderer())))
+ if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(downcast<RenderElement>(*upstreamStart.deprecatedNode()->renderer())))
return insertNewDefaultParagraphElementAt(upstreamStart);
} else if (isBlock(upstreamEnd.deprecatedNode())) {
if (!upstreamEnd.deprecatedNode()->isDescendantOf(upstreamStart.deprecatedNode())) {
// If the paragraph end is a descendant of paragraph start, then we need to run
// the rest of this function. If not, we can bail here.
- return 0;
+ return nullptr;
}
} else if (enclosingBlock(upstreamEnd.deprecatedNode()) != upstreamStart.deprecatedNode()) {
- // The visibleEnd. It must be an ancestor of the paragraph start.
- // We can bail as we have a full block to work with.
- ASSERT(upstreamStart.deprecatedNode()->isDescendantOf(enclosingBlock(upstreamEnd.deprecatedNode())));
- return 0;
+ // The visibleEnd. If it is an ancestor of the paragraph start, then
+ // we can bail as we have a full block to work with.
+ if (upstreamStart.deprecatedNode()->isDescendantOf(enclosingBlock(upstreamEnd.deprecatedNode())))
+ return nullptr;
} else if (isEndOfEditableOrNonEditableContent(visibleEnd)) {
// At the end of the editable region. We can bail here as well.
- return 0;
+ return nullptr;
}
}
- RefPtr<Node> newBlock = insertNewDefaultParagraphElementAt(upstreamStart);
+ // If upstreamStart is not editable, then we can bail here.
+ if (!isEditablePosition(upstreamStart))
+ return nullptr;
+ auto newBlock = insertNewDefaultParagraphElementAt(upstreamStart);
bool endWasBr = visibleParagraphEnd.deepEquivalent().deprecatedNode()->hasTagName(brTag);
- moveParagraphs(visibleParagraphStart, visibleParagraphEnd, VisiblePosition(firstPositionInNode(newBlock.get())));
+ moveParagraphs(visibleParagraphStart, visibleParagraphEnd, VisiblePosition(firstPositionInNode(newBlock.ptr())));
if (newBlock->lastChild() && newBlock->lastChild()->hasTagName(brTag) && !endWasBr)
removeNode(newBlock->lastChild());
- return newBlock.release();
+ return WTFMove(newBlock);
}
void CompositeEditCommand::pushAnchorElementDown(Element& anchorElement)
@@ -1041,7 +1238,7 @@ void CompositeEditCommand::pushAnchorElementDown(Element& anchorElement)
setEndingSelection(VisibleSelection::selectionFromContentsOfNode(&anchorElement));
applyStyledElement(&anchorElement);
// Clones of anchorElement have been pushed down, now remove it.
- if (anchorElement.inDocument())
+ if (anchorElement.isConnected())
removeNodePreservingChildren(&anchorElement);
}
@@ -1059,7 +1256,7 @@ void CompositeEditCommand::cloneParagraphUnderNewElement(const Position& start,
if (outerNode->isRootEditableElement()) {
lastNode = blockElement;
} else {
- lastNode = outerNode->cloneNode(isTableElement(outerNode.get()));
+ lastNode = outerNode->cloneNode(isRenderedTable(outerNode.get()));
appendNode(lastNode, blockElement);
}
@@ -1074,9 +1271,9 @@ void CompositeEditCommand::cloneParagraphUnderNewElement(const Position& start,
for (size_t i = ancestors.size(); i != 0; --i) {
Node* item = ancestors[i - 1].get();
- RefPtr<Node> child = item->cloneNode(isTableElement(item));
- appendNode(child, toElement(lastNode.get()));
- lastNode = child.release();
+ auto child = item->cloneNode(isRenderedTable(item));
+ appendNode(child.ptr(), downcast<Element>(lastNode.get()));
+ lastNode = WTFMove(child);
}
}
@@ -1092,7 +1289,7 @@ void CompositeEditCommand::cloneParagraphUnderNewElement(const Position& start,
}
RefPtr<Node> startNode = start.deprecatedNode();
- for (RefPtr<Node> node = NodeTraversal::nextSkippingChildren(startNode.get(), outerNode.get()); node; node = NodeTraversal::nextSkippingChildren(node.get(), outerNode.get())) {
+ for (RefPtr<Node> node = NodeTraversal::nextSkippingChildren(*startNode, outerNode.get()); node; node = NodeTraversal::nextSkippingChildren(*node, outerNode.get())) {
// Move lastNode up in the tree as much as node was moved up in the
// tree by NodeTraversal::nextSkippingChildren, so that the relative depth between
// node and the original start node is maintained in the clone.
@@ -1101,10 +1298,10 @@ void CompositeEditCommand::cloneParagraphUnderNewElement(const Position& start,
lastNode = lastNode->parentNode();
}
- RefPtr<Node> clonedNode = node->cloneNode(true);
- insertNodeAfter(clonedNode, lastNode);
- lastNode = clonedNode.release();
- if (node == end.deprecatedNode() || end.deprecatedNode()->isDescendantOf(node.get()))
+ auto clonedNode = node->cloneNode(true);
+ insertNodeAfter(clonedNode.ptr(), lastNode);
+ lastNode = WTFMove(clonedNode);
+ if (node == end.deprecatedNode() || end.deprecatedNode()->isDescendantOf(*node))
break;
}
}
@@ -1120,12 +1317,13 @@ void CompositeEditCommand::cloneParagraphUnderNewElement(const Position& start,
void CompositeEditCommand::cleanupAfterDeletion(VisiblePosition destination)
{
VisiblePosition caretAfterDelete = endingSelection().visibleStart();
- if (caretAfterDelete != destination && isStartOfParagraph(caretAfterDelete) && isEndOfParagraph(caretAfterDelete)) {
+ if (!caretAfterDelete.equals(destination) && isStartOfParagraph(caretAfterDelete) && isEndOfParagraph(caretAfterDelete)) {
// Note: We want the rightmost candidate.
Position position = caretAfterDelete.deepEquivalent().downstream();
Node* node = position.deprecatedNode();
+ ASSERT(node);
// Normally deletion will leave a br as a placeholder.
- if (node->hasTagName(brTag))
+ if (is<HTMLBRElement>(*node))
removeNodeAndPruneAncestors(node);
// If the selection to move was empty and in an empty block that
// doesn't require a placeholder to prop itself open (like a bordered
@@ -1143,11 +1341,11 @@ void CompositeEditCommand::cleanupAfterDeletion(VisiblePosition destination)
else if (lineBreakExistsAtPosition(position)) {
// There is a preserved '\n' at caretAfterDelete.
// We can safely assume this is a text node.
- Text* textNode = toText(node);
- if (textNode->length() == 1)
+ Text& textNode = downcast<Text>(*node);
+ if (textNode.length() == 1)
removeNodeAndPruneAncestors(node);
else
- deleteTextFromNode(textNode, position.deprecatedEditingOffset(), 1);
+ deleteTextFromNode(&textNode, position.deprecatedEditingOffset(), 1);
}
}
}
@@ -1160,6 +1358,9 @@ void CompositeEditCommand::cleanupAfterDeletion(VisiblePosition destination)
void CompositeEditCommand::moveParagraphWithClones(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, Element* blockElement, Node* outerNode)
{
+ if (startOfParagraphToMove.isNull() || endOfParagraphToMove.isNull())
+ return;
+
ASSERT(outerNode);
ASSERT(blockElement);
@@ -1192,10 +1393,11 @@ void CompositeEditCommand::moveParagraphWithClones(const VisiblePosition& startO
beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent());
afterParagraph = VisiblePosition(afterParagraph.deepEquivalent());
- if (beforeParagraph.isNotNull() && !isTableElement(beforeParagraph.deepEquivalent().deprecatedNode())
- && ((!isEndOfParagraph(beforeParagraph) && !isStartOfParagraph(beforeParagraph)) || beforeParagraph == afterParagraph)) {
+ if (beforeParagraph.isNotNull() && !isRenderedTable(beforeParagraph.deepEquivalent().deprecatedNode())
+ && ((!isEndOfParagraph(beforeParagraph) && !isStartOfParagraph(beforeParagraph)) || beforeParagraph == afterParagraph)
+ && isEditablePosition(beforeParagraph.deepEquivalent())) {
// FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal.
- insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquivalent());
+ insertNodeAt(HTMLBRElement::create(document()), beforeParagraph.deepEquivalent());
}
}
@@ -1260,7 +1462,7 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
RefPtr<DocumentFragment> fragment;
// This used to use a ternary for initialization, but that confused some versions of GCC, see bug 37912
if (startOfParagraphToMove != endOfParagraphToMove)
- fragment = createFragmentFromMarkup(document(), createMarkup(*range, 0, DoNotAnnotateForInterchange, true), "");
+ fragment = createFragmentFromMarkup(document(), createMarkup(*range, 0, DoNotAnnotateForInterchange, true), emptyString());
// A non-empty paragraph's style is moved when we copy and move it. We don't move
// anything if we're given an empty paragraph, but an empty paragraph can have style
@@ -1283,9 +1485,9 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
frame().editor().clearMisspellingsAndBadGrammar(endingSelection());
deleteSelection(false, false, false, false);
- ASSERT(destination.deepEquivalent().anchorNode()->inDocument());
+ ASSERT(destination.deepEquivalent().anchorNode()->isConnected());
cleanupAfterDeletion(destination);
- ASSERT(destination.deepEquivalent().anchorNode()->inDocument());
+ ASSERT(destination.deepEquivalent().anchorNode()->isConnected());
// Add a br if pruning an empty block level element caused a collapse. For example:
// foo^
@@ -1298,7 +1500,7 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
afterParagraph = VisiblePosition(afterParagraph.deepEquivalent());
if (beforeParagraph.isNotNull() && (!isEndOfParagraph(beforeParagraph) || beforeParagraph == afterParagraph)) {
// FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal.
- insertNodeAt(createBreakElement(document()), beforeParagraph.deepEquivalent());
+ insertNodeAt(HTMLBRElement::create(document()), beforeParagraph.deepEquivalent());
// Need an updateLayout here in case inserting the br has split a text node.
document().updateLayoutIgnorePendingStylesheets();
}
@@ -1311,7 +1513,7 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::MovingParagraph;
if (!preserveStyle)
options |= ReplaceSelectionCommand::MatchStyle;
- applyCommandToComposite(ReplaceSelectionCommand::create(document(), fragment, options));
+ applyCommandToComposite(ReplaceSelectionCommand::create(document(), WTFMove(fragment), options));
frame().editor().markMisspellingsAndBadGrammar(endingSelection());
@@ -1333,49 +1535,59 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
}
}
-// FIXME: Send an appropriate shouldDeleteRange call.
-bool CompositeEditCommand::breakOutOfEmptyListItem()
+std::optional<VisibleSelection> CompositeEditCommand::shouldBreakOutOfEmptyListItem() const
{
- RefPtr<Node> emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart());
+ auto emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart());
if (!emptyListItem)
- return false;
-
- RefPtr<EditingStyle> style = EditingStyle::create(endingSelection().start());
- style->mergeTypingStyle(document());
+ return std::nullopt;
- RefPtr<ContainerNode> listNode = emptyListItem->parentNode();
+ auto listNode = emptyListItem->parentNode();
// FIXME: Can't we do something better when the immediate parent wasn't a list node?
if (!listNode
|| (!listNode->hasTagName(ulTag) && !listNode->hasTagName(olTag))
|| !listNode->hasEditableStyle()
|| listNode == emptyListItem->rootEditableElement())
+ return std::nullopt;
+
+ return VisibleSelection(endingSelection().start().previous(BackwardDeletion), endingSelection().end());
+}
+
+// FIXME: Send an appropriate shouldDeleteRange call.
+bool CompositeEditCommand::breakOutOfEmptyListItem()
+{
+ if (!shouldBreakOutOfEmptyListItem())
return false;
- RefPtr<Element> newBlock = 0;
+ auto emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart());
+ auto listNode = emptyListItem->parentNode();
+ auto style = EditingStyle::create(endingSelection().start());
+ style->mergeTypingStyle(document());
+
+ RefPtr<Element> newBlock;
if (ContainerNode* blockEnclosingList = listNode->parentNode()) {
- if (blockEnclosingList->hasTagName(liTag)) { // listNode is inside another list item
- if (visiblePositionAfterNode(blockEnclosingList) == visiblePositionAfterNode(listNode.get())) {
+ if (is<HTMLLIElement>(*blockEnclosingList)) { // listNode is inside another list item
+ if (visiblePositionAfterNode(*blockEnclosingList) == visiblePositionAfterNode(*listNode)) {
// If listNode appears at the end of the outer list item, then move listNode outside of this list item
// e.g. <ul><li>hello <ul><li><br></li></ul> </li></ul> should become <ul><li>hello</li> <ul><li><br></li></ul> </ul> after this section
// If listNode does NOT appear at the end, then we should consider it as a regular paragraph.
// e.g. <ul><li> <ul><li><br></li></ul> hello</li></ul> should become <ul><li> <div><br></div> hello</li></ul> at the end
- splitElement(toElement(blockEnclosingList), listNode);
+ splitElement(downcast<HTMLLIElement>(blockEnclosingList), listNode);
removeNodePreservingChildren(listNode->parentNode());
- newBlock = createListItemElement(document());
+ newBlock = HTMLLIElement::create(document());
}
// If listNode does NOT appear at the end of the outer list item, then behave as if in a regular paragraph.
} else if (blockEnclosingList->hasTagName(olTag) || blockEnclosingList->hasTagName(ulTag))
- newBlock = createListItemElement(document());
+ newBlock = HTMLLIElement::create(document());
}
if (!newBlock)
newBlock = createDefaultParagraphElement(document());
- RefPtr<Node> previousListNode = emptyListItem->isElementNode() ? ElementTraversal::previousSibling(emptyListItem.get()): emptyListItem->previousSibling();
- RefPtr<Node> nextListNode = emptyListItem->isElementNode() ? ElementTraversal::nextSibling(emptyListItem.get()): emptyListItem->nextSibling();
- if (isListItem(nextListNode.get()) || isListElement(nextListNode.get())) {
+ RefPtr<Node> previousListNode = emptyListItem->isElementNode() ? ElementTraversal::previousSibling(*emptyListItem): emptyListItem->previousSibling();
+ RefPtr<Node> nextListNode = emptyListItem->isElementNode() ? ElementTraversal::nextSibling(*emptyListItem): emptyListItem->nextSibling();
+ if (isListItem(nextListNode.get()) || isListHTMLElement(nextListNode.get())) {
// If emptyListItem follows another list item or nested list, split the list node.
- if (isListItem(previousListNode.get()) || isListElement(previousListNode.get()))
- splitElement(toElement(listNode.get()), emptyListItem);
+ if (isListItem(previousListNode.get()) || isListHTMLElement(previousListNode.get()))
+ splitElement(downcast<Element>(listNode), emptyListItem);
// 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.
@@ -1386,7 +1598,7 @@ bool CompositeEditCommand::breakOutOfEmptyListItem()
// 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(isListItem(previousListNode.get()) || isListElement(previousListNode.get()) ? emptyListItem.get() : listNode.get());
+ removeNode(isListItem(previousListNode.get()) || isListHTMLElement(previousListNode.get()) ? emptyListItem : listNode);
}
appendBlockPlaceholder(newBlock);
@@ -1394,7 +1606,7 @@ bool CompositeEditCommand::breakOutOfEmptyListItem()
style->prepareToApplyAt(endingSelection().start());
if (!style->isEmpty())
- applyStyle(style.get());
+ applyStyle(style.ptr());
return true;
}
@@ -1410,16 +1622,16 @@ bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph()
Node* highestBlockquote = highestEnclosingNodeOfType(caret.deepEquivalent(), &isMailBlockquote);
if (!highestBlockquote)
return false;
-
+
if (!isStartOfParagraph(caret) || !isEndOfParagraph(caret))
return false;
-
+
VisiblePosition previous(caret.previous(CannotCrossEditingBoundary));
// Only move forward if there's nothing before the caret, or if there's unquoted content before it.
if (enclosingNodeOfType(previous.deepEquivalent(), &isMailBlockquote))
return false;
- RefPtr<Node> br = createBreakElement(document());
+ RefPtr<Node> br = HTMLBRElement::create(document());
// We want to replace this quoted paragraph with an unquoted one, so insert a br
// to hold the caret before the highest blockquote.
insertNodeBefore(br, highestBlockquote);
@@ -1427,7 +1639,7 @@ bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph()
// If the br we inserted collapsed, for example foo<br><blockquote>...</blockquote>, insert
// a second one.
if (!isStartOfParagraph(atBR))
- insertNodeBefore(createBreakElement(document()), br);
+ insertNodeBefore(HTMLBRElement::create(document()), br);
setEndingSelection(VisibleSelection(atBR, endingSelection().isDirectional()));
// If this is an empty paragraph there must be a line break here.
@@ -1440,13 +1652,13 @@ bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph()
if (caretPos.deprecatedNode()->hasTagName(brTag))
removeNodeAndPruneAncestors(caretPos.deprecatedNode());
- else if (caretPos.deprecatedNode()->isTextNode()) {
+ else if (is<Text>(*caretPos.deprecatedNode())) {
ASSERT(caretPos.deprecatedEditingOffset() == 0);
- Text* textNode = toText(caretPos.deprecatedNode());
- ContainerNode* parentNode = textNode->parentNode();
+ Text& textNode = downcast<Text>(*caretPos.deprecatedNode());
+ ContainerNode* parentNode = textNode.parentNode();
// The preserved newline must be the first thing in the node, since otherwise the previous
// paragraph would be quoted, and we verified that it wasn't above.
- deleteTextFromNode(textNode, 0, 1);
+ deleteTextFromNode(&textNode, 0, 1);
prune(parentNode);
}
@@ -1516,7 +1728,7 @@ Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Posi
// Splits the tree parent by parent until we reach the specified ancestor. We use VisiblePositions
// to determine if the split is necessary. Returns the last split node.
-PassRefPtr<Node> CompositeEditCommand::splitTreeToNode(Node* start, Node* end, bool shouldSplitAncestor)
+RefPtr<Node> CompositeEditCommand::splitTreeToNode(Node* start, Node* end, bool shouldSplitAncestor)
{
ASSERT(start);
ASSERT(end);
@@ -1528,21 +1740,21 @@ PassRefPtr<Node> CompositeEditCommand::splitTreeToNode(Node* start, Node* end, b
RefPtr<Node> endNode = end;
for (node = start; node && node->parentNode() != endNode; node = node->parentNode()) {
- if (!node->parentNode()->isElementNode())
+ if (!is<Element>(*node->parentNode()))
break;
// Do not split a node when doing so introduces an empty node.
VisiblePosition positionInParent = firstPositionInNode(node->parentNode());
VisiblePosition positionInNode = firstPositionInOrBeforeNode(node.get());
if (positionInParent != positionInNode)
- splitElement(toElement(node->parentNode()), node);
+ splitElement(downcast<Element>(node->parentNode()), node);
}
- return node.release();
+ return node;
}
-PassRefPtr<Element> createBlockPlaceholderElement(Document& document)
+Ref<Element> createBlockPlaceholderElement(Document& document)
{
- return document.createElement(brTag, false);
+ return HTMLBRElement::create(document);
}
} // namespace WebCore
diff --git a/Source/WebCore/editing/CompositeEditCommand.h b/Source/WebCore/editing/CompositeEditCommand.h
index 1cd46e2ee..0221dc999 100644
--- a/Source/WebCore/editing/CompositeEditCommand.h
+++ b/Source/WebCore/editing/CompositeEditCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,9 +23,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CompositeEditCommand_h
-#define CompositeEditCommand_h
+#pragma once
+#include "AXObjectCache.h"
#include "EditCommand.h"
#include "CSSPropertyNames.h"
#include "UndoStep.h"
@@ -34,17 +34,42 @@
namespace WebCore {
class EditingStyle;
+class DataTransfer;
class HTMLElement;
+class StaticRange;
class StyledElement;
class Text;
+class AccessibilityUndoReplacedText {
+public:
+ AccessibilityUndoReplacedText() { }
+ void configureRangeDeletedByReapplyWithStartingSelection(const VisibleSelection&);
+ void configureRangeDeletedByReapplyWithEndingSelection(const VisibleSelection&);
+ void setRangeDeletedByUnapply(const VisiblePositionIndexRange&);
+
+ void captureTextForUnapply();
+ void captureTextForReapply();
+
+ void postTextStateChangeNotificationForUnapply(AXObjectCache*);
+ void postTextStateChangeNotificationForReapply(AXObjectCache*);
+
+private:
+ int indexForVisiblePosition(const VisiblePosition&, RefPtr<ContainerNode>&) const;
+ String textDeletedByUnapply();
+ String textDeletedByReapply();
+
+ String m_replacedText;
+ VisiblePositionIndexRange m_rangeDeletedByUnapply;
+ VisiblePositionIndexRange m_rangeDeletedByReapply;
+};
+
class EditCommandComposition : public UndoStep {
public:
- static PassRefPtr<EditCommandComposition> create(Document&, const VisibleSelection&, const VisibleSelection&, EditAction);
+ static Ref<EditCommandComposition> create(Document&, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction);
- virtual void unapply() override;
- virtual void reapply() override;
- virtual EditAction editingAction() const override { return m_editAction; }
+ void unapply() override;
+ void reapply() override;
+ EditAction editingAction() const override { return m_editAction; }
void append(SimpleEditCommand*);
bool wasCreateLinkCommand() const { return m_editAction == EditActionCreateLink; }
@@ -54,6 +79,7 @@ public:
void setEndingSelection(const VisibleSelection&);
Element* startingRootEditableElement() const { return m_startingRootEditableElement.get(); }
Element* endingRootEditableElement() const { return m_endingRootEditableElement.get(); }
+ void setRangeDeletedByUnapply(const VisiblePositionIndexRange&);
#ifndef NDEBUG
virtual void getNodesInCommand(HashSet<Node*>&);
@@ -68,6 +94,7 @@ private:
Vector<RefPtr<SimpleEditCommand>> m_commands;
RefPtr<Element> m_startingRootEditableElement;
RefPtr<Element> m_endingRootEditableElement;
+ AccessibilityUndoReplacedText m_replacedText;
EditAction m_editAction;
};
@@ -77,8 +104,8 @@ public:
void apply();
bool isFirstCommand(EditCommand* command) { return !m_commands.isEmpty() && m_commands.first() == command; }
- EditCommandComposition* composition() { return m_composition.get(); }
- EditCommandComposition* ensureComposition();
+ EditCommandComposition* composition() const;
+ EditCommandComposition& ensureComposition();
virtual bool isCreateLinkCommand() const;
virtual bool isTypingCommand() const;
@@ -87,9 +114,21 @@ public:
virtual bool shouldRetainAutocorrectionIndicator() const;
virtual void setShouldRetainAutocorrectionIndicator(bool);
virtual bool shouldStopCaretBlinking() const { return false; }
+ virtual String inputEventTypeName() const;
+ virtual String inputEventData() const { return { }; }
+ virtual bool isBeforeInputEventCancelable() const { return true; }
+ virtual bool shouldDispatchInputEvents() const { return true; }
+ Vector<RefPtr<StaticRange>> targetRangesForBindings() const;
+ virtual RefPtr<DataTransfer> inputEventDataTransfer() const;
protected:
- explicit CompositeEditCommand(Document&);
+ explicit CompositeEditCommand(Document&, EditAction = EditActionUnspecified);
+
+ // If willApplyCommand returns false, we won't proceed with applying the command.
+ virtual bool willApplyCommand();
+ virtual void didApplyCommand();
+
+ virtual Vector<RefPtr<StaticRange>> targetRanges() const;
//
// sugary-sweet convenience functions to help create and apply edit commands in composite commands
@@ -104,9 +143,7 @@ protected:
void deleteSelection(bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = true, bool sanitizeMarkup = true);
void deleteSelection(const VisibleSelection&, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = true, bool sanitizeMarkup = true);
virtual void deleteTextFromNode(PassRefPtr<Text>, unsigned offset, unsigned count);
-#if PLATFORM(IOS)
void inputText(const String&, bool selectInsertedText = false);
-#endif
bool isRemovableBlock(const Node*);
void insertNodeAfter(PassRefPtr<Node>, PassRefPtr<Node> refChild);
void insertNodeAt(PassRefPtr<Node>, const Position&);
@@ -130,7 +167,7 @@ protected:
void removeNodePreservingChildren(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable);
void removeNodeAndPruneAncestors(PassRefPtr<Node>);
void moveRemainingSiblingsToNewParent(Node*, Node* pastLastNodeToMove, PassRefPtr<Element> prpNewParent);
- void updatePositionForNodeRemovalPreservingChildren(Position&, Node*);
+ void updatePositionForNodeRemovalPreservingChildren(Position&, Node&);
void prune(PassRefPtr<Node>);
void replaceTextInNode(PassRefPtr<Text>, unsigned offset, unsigned count, const String& replacementText);
Position replaceSelectedTextInNode(const String&);
@@ -140,20 +177,20 @@ protected:
void splitElement(PassRefPtr<Element>, PassRefPtr<Node> atChild);
void splitTextNode(PassRefPtr<Text>, unsigned offset);
void splitTextNodeContainingElement(PassRefPtr<Text>, unsigned offset);
- void wrapContentsInDummySpan(PassRefPtr<Element>);
+ void wrapContentsInDummySpan(Element&);
void deleteInsignificantText(PassRefPtr<Text>, unsigned start, unsigned end);
void deleteInsignificantText(const Position& start, const Position& end);
void deleteInsignificantTextDownstream(const Position&);
- PassRefPtr<Node> appendBlockPlaceholder(PassRefPtr<Element>);
- PassRefPtr<Node> insertBlockPlaceholder(const Position&);
- PassRefPtr<Node> addBlockPlaceholderIfNeeded(Element*);
+ RefPtr<Node> appendBlockPlaceholder(PassRefPtr<Element>);
+ RefPtr<Node> insertBlockPlaceholder(const Position&);
+ RefPtr<Node> addBlockPlaceholderIfNeeded(Element*);
void removePlaceholderAt(const Position&);
- PassRefPtr<Node> insertNewDefaultParagraphElementAt(const Position&);
+ Ref<HTMLElement> insertNewDefaultParagraphElementAt(const Position&);
- PassRefPtr<Node> moveParagraphContentsToNewBlockIfNecessary(const Position&);
+ RefPtr<Node> moveParagraphContentsToNewBlockIfNecessary(const Position&);
void pushAnchorElementDown(Element&);
@@ -163,17 +200,18 @@ protected:
void cloneParagraphUnderNewElement(const Position& start, const Position& end, Node* outerNode, Element* blockElement);
void cleanupAfterDeletion(VisiblePosition destination = VisiblePosition());
+ std::optional<VisibleSelection> shouldBreakOutOfEmptyListItem() const;
bool breakOutOfEmptyListItem();
bool breakOutOfEmptyMailBlockquotedParagraph();
Position positionAvoidingSpecialElementBoundary(const Position&);
- PassRefPtr<Node> splitTreeToNode(Node*, Node*, bool splitAncestor = false);
+ RefPtr<Node> splitTreeToNode(Node*, Node*, bool splitAncestor = false);
Vector<RefPtr<EditCommand>> m_commands;
private:
- virtual bool isCompositeEditCommand() const override { return true; }
+ bool isCompositeEditCommand() const override { return true; }
RefPtr<EditCommandComposition> m_composition;
};
@@ -188,5 +226,3 @@ inline CompositeEditCommand* toCompositeEditCommand(EditCommand* command)
}
} // namespace WebCore
-
-#endif // CompositeEditCommand_h
diff --git a/Source/WebCore/editing/CreateLinkCommand.cpp b/Source/WebCore/editing/CreateLinkCommand.cpp
index e8d02ca9b..3a0520873 100644
--- a/Source/WebCore/editing/CreateLinkCommand.cpp
+++ b/Source/WebCore/editing/CreateLinkCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -43,16 +43,16 @@ void CreateLinkCommand::doApply()
if (endingSelection().isNone())
return;
- RefPtr<HTMLAnchorElement> anchorElement = HTMLAnchorElement::create(document());
+ auto anchorElement = HTMLAnchorElement::create(document());
anchorElement->setHref(m_url);
if (endingSelection().isRange())
- applyStyledElement(anchorElement.release());
+ applyStyledElement(WTFMove(anchorElement));
else {
- insertNodeAt(anchorElement.get(), endingSelection().start());
- RefPtr<Text> textNode = Text::create(document(), m_url);
- appendNode(textNode.release(), anchorElement.get());
- setEndingSelection(VisibleSelection(positionInParentBeforeNode(anchorElement.get()), positionInParentAfterNode(anchorElement.get()), DOWNSTREAM, endingSelection().isDirectional()));
+ insertNodeAt(anchorElement.ptr(), endingSelection().start());
+ auto textNode = Text::create(document(), m_url);
+ appendNode(WTFMove(textNode), anchorElement.ptr());
+ setEndingSelection(VisibleSelection(positionInParentBeforeNode(anchorElement.ptr()), positionInParentAfterNode(anchorElement.ptr()), DOWNSTREAM, endingSelection().isDirectional()));
}
}
diff --git a/Source/WebCore/editing/CreateLinkCommand.h b/Source/WebCore/editing/CreateLinkCommand.h
index ba79c0966..39b98d0dc 100644
--- a/Source/WebCore/editing/CreateLinkCommand.h
+++ b/Source/WebCore/editing/CreateLinkCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef CreateLinkCommand_h
-#define CreateLinkCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -32,22 +31,20 @@ namespace WebCore {
class CreateLinkCommand : public CompositeEditCommand {
public:
- static PassRefPtr<CreateLinkCommand> create(Document& document, const String& linkURL)
+ static Ref<CreateLinkCommand> create(Document& document, const String& linkURL)
{
- return adoptRef(new CreateLinkCommand(document, linkURL));
+ return adoptRef(*new CreateLinkCommand(document, linkURL));
}
- bool isCreateLinkCommand() const { return true; }
+ bool isCreateLinkCommand() const override { return true; }
private:
CreateLinkCommand(Document&, const String& linkURL);
- virtual void doApply();
- virtual EditAction editingAction() const { return EditActionCreateLink; }
+ void doApply() override;
+ EditAction editingAction() const override { return EditActionCreateLink; }
String m_url;
};
} // namespace WebCore
-
-#endif // CreateLinkCommand_h
diff --git a/Source/WebCore/editing/DeleteButton.cpp b/Source/WebCore/editing/DeleteButton.cpp
deleted file mode 100644
index f6b367141..000000000
--- a/Source/WebCore/editing/DeleteButton.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2006, 2010 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.
- */
-
-#include "config.h"
-#include "DeleteButton.h"
-
-#include "DeleteButtonController.h"
-#include "Document.h"
-#include "Event.h"
-#include "EventNames.h"
-#include "HTMLNames.h"
-
-namespace WebCore {
-
-using namespace HTMLNames;
-
-#if ENABLE(DELETION_UI)
-
-inline DeleteButton::DeleteButton(Document& document)
- : HTMLImageElement(imgTag, document)
-{
-}
-
-PassRefPtr<DeleteButton> DeleteButton::create(Document& document)
-{
- return adoptRef(new DeleteButton(document));
-}
-
-void DeleteButton::defaultEventHandler(Event* event)
-{
- if (event->type() == eventNames().clickEvent) {
- document().frame()->editor().deleteButtonController().deleteTarget();
- event->setDefaultHandled();
- return;
- }
-
- HTMLImageElement::defaultEventHandler(event);
-}
-#endif
-
-} // namespace
diff --git a/Source/WebCore/editing/DeleteButtonController.cpp b/Source/WebCore/editing/DeleteButtonController.cpp
deleted file mode 100644
index a78769c3f..000000000
--- a/Source/WebCore/editing/DeleteButtonController.cpp
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * 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 "DeleteButtonController.h"
-
-#include "CachedImage.h"
-#include "CSSPrimitiveValue.h"
-#include "CompositeEditCommand.h"
-#include "Document.h"
-#include "EditorClient.h"
-#include "htmlediting.h"
-#include "HTMLDivElement.h"
-#include "HTMLNames.h"
-#include "Image.h"
-#include "Node.h"
-#include "Page.h"
-#include "RemoveNodeCommand.h"
-#include "RenderBox.h"
-#include "StyleProperties.h"
-
-namespace WebCore {
-
-using namespace HTMLNames;
-
-#if ENABLE(DELETION_UI)
-
-const char* const DeleteButtonController::containerElementIdentifier = "WebKit-Editing-Delete-Container";
-const char* const DeleteButtonController::buttonElementIdentifier = "WebKit-Editing-Delete-Button";
-const char* const DeleteButtonController::outlineElementIdentifier = "WebKit-Editing-Delete-Outline";
-
-DeleteButtonController::DeleteButtonController(Frame& frame)
- : m_frame(frame)
- , m_wasStaticPositioned(false)
- , m_wasAutoZIndex(false)
- , m_disableStack(0)
-{
-}
-
-static bool isDeletableElement(const Node* node)
-{
- if (!node || !node->isHTMLElement() || !node->inDocument() || !node->hasEditableStyle())
- return false;
-
- // In general we want to only draw the UI around object of a certain area, but we still keep the min width/height to
- // make sure we don't end up with very thin or very short elements getting the UI.
- const int minimumArea = 2500;
- const int minimumWidth = 48;
- const int minimumHeight = 16;
- const unsigned minimumVisibleBorders = 1;
-
- RenderObject* renderer = node->renderer();
- if (!renderer || !renderer->isBox())
- return false;
-
- // Disallow the body element since it isn't practical to delete, and the deletion UI would be clipped.
- if (node->hasTagName(bodyTag))
- return false;
-
- // Disallow elements with any overflow clip, since the deletion UI would be clipped as well. <rdar://problem/6840161>
- if (renderer->hasOverflowClip())
- return false;
-
- // Disallow Mail blockquotes since the deletion UI would get in the way of editing for these.
- if (isMailBlockquote(node))
- return false;
-
- RenderBox* box = toRenderBox(renderer);
- IntRect borderBoundingBox = box->borderBoundingBox();
- if (borderBoundingBox.width() < minimumWidth || borderBoundingBox.height() < minimumHeight)
- return false;
-
- if ((borderBoundingBox.width() * borderBoundingBox.height()) < minimumArea)
- return false;
-
- if (box->isTable())
- return true;
-
- if (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(iframeTag))
- return true;
-
- if (box->isOutOfFlowPositioned())
- return true;
-
- if (box->isRenderBlock() && !box->isTableCell()) {
- const RenderStyle& style = box->style();
-
- // Allow blocks that have background images
- if (style.hasBackgroundImage()) {
- for (const FillLayer* background = style.backgroundLayers(); background; background = background->next()) {
- if (background->image() && background->image()->canRender(box, 1))
- return true;
- }
- }
-
- // Allow blocks with a minimum number of non-transparent borders
- unsigned visibleBorders = style.borderTop().isVisible() + style.borderBottom().isVisible() + style.borderLeft().isVisible() + style.borderRight().isVisible();
- if (visibleBorders >= minimumVisibleBorders)
- return true;
-
- // Allow blocks that have a different background from it's parent
- ContainerNode* parentNode = node->parentNode();
- if (!parentNode)
- return false;
-
- auto parentRenderer = parentNode->renderer();
- if (!parentRenderer)
- return false;
-
- const RenderStyle& parentStyle = parentRenderer->style();
-
- if (box->hasBackground() && (!parentRenderer->hasBackground() || style.visitedDependentColor(CSSPropertyBackgroundColor) != parentStyle.visitedDependentColor(CSSPropertyBackgroundColor)))
- return true;
- }
-
- return false;
-}
-
-static HTMLElement* enclosingDeletableElement(const VisibleSelection& selection)
-{
- if (!selection.isContentEditable())
- return 0;
-
- RefPtr<Range> range = selection.toNormalizedRange();
- if (!range)
- return 0;
-
- Node* container = range->commonAncestorContainer(ASSERT_NO_EXCEPTION);
- ASSERT(container);
-
- // The enclosingNodeOfType function only works on nodes that are editable
- // (which is strange, given its name).
- if (!container->hasEditableStyle())
- return 0;
-
- Node* element = enclosingNodeOfType(firstPositionInNode(container), &isDeletableElement);
- return element && element->isHTMLElement() ? toHTMLElement(element) : 0;
-}
-
-void DeleteButtonController::respondToChangedSelection(const VisibleSelection& oldSelection)
-{
- if (!enabled())
- return;
-
- HTMLElement* oldElement = enclosingDeletableElement(oldSelection);
- HTMLElement* newElement = enclosingDeletableElement(m_frame.selection().selection());
- if (oldElement == newElement)
- return;
-
- // If the base is inside a deletable element, give the element a delete widget.
- if (newElement)
- show(newElement);
- else
- hide();
-}
-
-void DeleteButtonController::deviceScaleFactorChanged()
-{
- if (!enabled())
- return;
-
- HTMLElement* currentTarget = m_target.get();
- hide();
-
- // Setting m_containerElement to 0 will force the deletionUI to be re-created with
- // artwork of the appropriate resolution in show().
- m_containerElement = 0;
- show(currentTarget);
-}
-
-void DeleteButtonController::createDeletionUI()
-{
- RefPtr<HTMLDivElement> container = HTMLDivElement::create(m_target->document());
- container->setIdAttribute(containerElementIdentifier);
-
- container->setInlineStyleProperty(CSSPropertyWebkitUserDrag, CSSValueNone);
- container->setInlineStyleProperty(CSSPropertyWebkitUserSelect, CSSValueNone);
- container->setInlineStyleProperty(CSSPropertyWebkitUserModify, CSSValueReadOnly);
- container->setInlineStyleProperty(CSSPropertyVisibility, CSSValueHidden);
- container->setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute);
- container->setInlineStyleProperty(CSSPropertyCursor, CSSValueDefault);
- container->setInlineStyleProperty(CSSPropertyTop, 0, CSSPrimitiveValue::CSS_PX);
- container->setInlineStyleProperty(CSSPropertyRight, 0, CSSPrimitiveValue::CSS_PX);
- container->setInlineStyleProperty(CSSPropertyBottom, 0, CSSPrimitiveValue::CSS_PX);
- container->setInlineStyleProperty(CSSPropertyLeft, 0, CSSPrimitiveValue::CSS_PX);
-
- RefPtr<HTMLDivElement> outline = HTMLDivElement::create(m_target->document());
- outline->setIdAttribute(outlineElementIdentifier);
-
- const int borderWidth = 4;
- const int borderRadius = 6;
-
- outline->setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute);
- outline->setInlineStyleProperty(CSSPropertyZIndex, ASCIILiteral("-1000000"));
- outline->setInlineStyleProperty(CSSPropertyTop, -borderWidth - m_target->renderBox()->borderTop(), CSSPrimitiveValue::CSS_PX);
- outline->setInlineStyleProperty(CSSPropertyRight, -borderWidth - m_target->renderBox()->borderRight(), CSSPrimitiveValue::CSS_PX);
- outline->setInlineStyleProperty(CSSPropertyBottom, -borderWidth - m_target->renderBox()->borderBottom(), CSSPrimitiveValue::CSS_PX);
- outline->setInlineStyleProperty(CSSPropertyLeft, -borderWidth - m_target->renderBox()->borderLeft(), CSSPrimitiveValue::CSS_PX);
- outline->setInlineStyleProperty(CSSPropertyBorderWidth, borderWidth, CSSPrimitiveValue::CSS_PX);
- outline->setInlineStyleProperty(CSSPropertyBorderStyle, CSSValueSolid);
- outline->setInlineStyleProperty(CSSPropertyBorderColor, ASCIILiteral("rgba(0, 0, 0, 0.6)"));
- outline->setInlineStyleProperty(CSSPropertyBorderRadius, borderRadius, CSSPrimitiveValue::CSS_PX);
- outline->setInlineStyleProperty(CSSPropertyVisibility, CSSValueVisible);
-
- ExceptionCode ec = 0;
- container->appendChild(outline.get(), ec);
- ASSERT(!ec);
- if (ec)
- return;
-
- RefPtr<DeleteButton> button = DeleteButton::create(m_target->document());
- button->setIdAttribute(buttonElementIdentifier);
-
- const int buttonWidth = 30;
- const int buttonHeight = 30;
- const int buttonBottomShadowOffset = 2;
-
- button->setInlineStyleProperty(CSSPropertyPosition, CSSValueAbsolute);
- button->setInlineStyleProperty(CSSPropertyZIndex, ASCIILiteral("1000000"));
- button->setInlineStyleProperty(CSSPropertyTop, (-buttonHeight / 2) - m_target->renderBox()->borderTop() - (borderWidth / 2) + buttonBottomShadowOffset, CSSPrimitiveValue::CSS_PX);
- button->setInlineStyleProperty(CSSPropertyLeft, (-buttonWidth / 2) - m_target->renderBox()->borderLeft() - (borderWidth / 2), CSSPrimitiveValue::CSS_PX);
- button->setInlineStyleProperty(CSSPropertyWidth, buttonWidth, CSSPrimitiveValue::CSS_PX);
- button->setInlineStyleProperty(CSSPropertyHeight, buttonHeight, CSSPrimitiveValue::CSS_PX);
- button->setInlineStyleProperty(CSSPropertyVisibility, CSSValueVisible);
-
- float deviceScaleFactor = WebCore::deviceScaleFactor(&m_frame);
- RefPtr<Image> buttonImage;
- if (deviceScaleFactor >= 2)
- buttonImage = Image::loadPlatformResource("deleteButton@2x");
- else
- buttonImage = Image::loadPlatformResource("deleteButton");
-
- if (buttonImage->isNull())
- return;
-
- button->setCachedImage(new CachedImage(buttonImage.get()));
-
- container->appendChild(button.get(), ec);
- ASSERT(!ec);
- if (ec)
- return;
-
- m_containerElement = container.release();
- m_outlineElement = outline.release();
- m_buttonElement = button.release();
-}
-
-void DeleteButtonController::show(HTMLElement* element)
-{
- hide();
-
- if (!enabled() || !element || !element->inDocument() || !isDeletableElement(element))
- return;
-
- EditorClient* client = m_frame.editor().client();
- if (!client || !client->shouldShowDeleteInterface(element))
- return;
-
- // we rely on the renderer having current information, so we should update the layout if needed
- m_frame.document()->updateLayoutIgnorePendingStylesheets();
-
- m_target = element;
-
- if (!m_containerElement) {
- createDeletionUI();
- if (!m_containerElement) {
- hide();
- return;
- }
- }
-
- ExceptionCode ec = 0;
- m_target->appendChild(m_containerElement.get(), ec);
- ASSERT(!ec);
- if (ec) {
- hide();
- return;
- }
-
- if (m_target->renderer()->style().position() == StaticPosition) {
- m_target->setInlineStyleProperty(CSSPropertyPosition, CSSValueRelative);
- m_wasStaticPositioned = true;
- }
-
- if (m_target->renderer()->style().hasAutoZIndex()) {
- m_target->setInlineStyleProperty(CSSPropertyZIndex, ASCIILiteral("0"));
- m_wasAutoZIndex = true;
- }
-}
-
-void DeleteButtonController::hide()
-{
- m_outlineElement = 0;
- m_buttonElement = 0;
-
- if (m_containerElement && m_containerElement->parentNode())
- m_containerElement->parentNode()->removeChild(m_containerElement.get(), IGNORE_EXCEPTION);
-
- if (m_target) {
- if (m_wasStaticPositioned)
- m_target->setInlineStyleProperty(CSSPropertyPosition, CSSValueStatic);
- if (m_wasAutoZIndex)
- m_target->setInlineStyleProperty(CSSPropertyZIndex, CSSValueAuto);
- }
-
- m_wasStaticPositioned = false;
- m_wasAutoZIndex = false;
-}
-
-void DeleteButtonController::enable()
-{
-#if !PLATFORM(IOS)
- ASSERT(m_disableStack > 0);
- if (m_disableStack > 0)
- m_disableStack--;
- if (enabled()) {
- // Determining if the element is deletable currently depends on style
- // because whether something is editable depends on style, so we need
- // to recalculate style before calling enclosingDeletableElement.
- m_frame.document()->updateStyleIfNeeded();
- show(enclosingDeletableElement(m_frame.selection().selection()));
- }
-#endif
-}
-
-void DeleteButtonController::disable()
-{
-#if !PLATFORM(IOS)
- if (enabled())
- hide();
- m_disableStack++;
-#endif
-}
-
-class RemoveTargetCommand : public CompositeEditCommand {
-public:
- static PassRefPtr<RemoveTargetCommand> create(Document& document, PassRefPtr<Node> target)
- {
- return adoptRef(new RemoveTargetCommand(document, target));
- }
-
-private:
- RemoveTargetCommand(Document& document, PassRefPtr<Node> target)
- : CompositeEditCommand(document)
- , m_target(target)
- { }
-
- void doApply()
- {
- removeNode(m_target);
- }
-
-private:
- RefPtr<Node> m_target;
-};
-
-void DeleteButtonController::deleteTarget()
-{
- if (!enabled() || !m_target)
- return;
-
- hide();
-
- // Because the deletion UI only appears when the selection is entirely
- // within the target, we unconditionally update the selection to be
- // a caret where the target had been.
- Position pos = positionInParentBeforeNode(m_target.get());
- ASSERT(m_frame.document());
- applyCommand(RemoveTargetCommand::create(*m_frame.document(), m_target));
- m_frame.selection().setSelection(VisiblePosition(pos));
-}
-#endif
-
-} // namespace WebCore
diff --git a/Source/WebCore/editing/DeleteButtonController.h b/Source/WebCore/editing/DeleteButtonController.h
deleted file mode 100644
index 447dd95ef..000000000
--- a/Source/WebCore/editing/DeleteButtonController.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2006, 2007 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 DeleteButtonController_h
-#define DeleteButtonController_h
-
-#if ENABLE(DELETION_UI)
-
-#include "DeleteButton.h"
-#include "Editor.h"
-#include "Frame.h"
-
-namespace WebCore {
-
-class DeleteButton;
-class HTMLElement;
-class RenderObject;
-class VisibleSelection;
-
-class DeleteButtonController {
- WTF_MAKE_NONCOPYABLE(DeleteButtonController); WTF_MAKE_FAST_ALLOCATED;
-public:
- explicit DeleteButtonController(Frame&);
-
- HTMLElement* containerElement() const { return m_containerElement.get(); }
-
- void respondToChangedSelection(const VisibleSelection& oldSelection);
-
- void deviceScaleFactorChanged();
-
- void show(HTMLElement*);
- void hide();
-
- void deleteTarget();
-
-private:
- static const char* const buttonElementIdentifier;
- static const char* const outlineElementIdentifier;
- static const char* const containerElementIdentifier;
-
- void enable();
- void disable();
- friend class DeleteButtonControllerDisableScope;
-
- void createDeletionUI();
- bool enabled() const { return (!m_disableStack); }
-
- Frame& m_frame;
- RefPtr<HTMLElement> m_target;
- RefPtr<HTMLElement> m_containerElement;
- RefPtr<HTMLElement> m_outlineElement;
- RefPtr<DeleteButton> m_buttonElement;
- bool m_wasStaticPositioned;
- bool m_wasAutoZIndex;
- unsigned m_disableStack;
-};
-
-class DeleteButtonControllerDisableScope {
-public:
- DeleteButtonControllerDisableScope(Frame* frame)
- : m_frame(frame)
- {
- if (frame)
- frame->editor().deleteButtonController().disable();
- }
-
- ~DeleteButtonControllerDisableScope()
- {
- if (m_frame)
- m_frame->editor().deleteButtonController().enable();
- }
-
-private:
- RefPtr<Frame> m_frame;
-};
-
-} // namespace WebCore
-
-#endif // ENABLE(DELETION_UI)
-
-#endif // DeleteButtonController_h
diff --git a/Source/WebCore/editing/DeleteFromTextNodeCommand.cpp b/Source/WebCore/editing/DeleteFromTextNodeCommand.cpp
index a9bcfbcf9..7aaac39ca 100644
--- a/Source/WebCore/editing/DeleteFromTextNodeCommand.cpp
+++ b/Source/WebCore/editing/DeleteFromTextNodeCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2005, 2008, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -25,16 +25,15 @@
#include "config.h"
#include "DeleteFromTextNodeCommand.h"
-#include "Document.h"
-#include "ExceptionCodePlaceholder.h"
-#include "AXObjectCache.h"
+#include "Document.h"
#include "Text.h"
+#include "htmlediting.h"
namespace WebCore {
-DeleteFromTextNodeCommand::DeleteFromTextNodeCommand(PassRefPtr<Text> node, unsigned offset, unsigned count)
- : SimpleEditCommand(node->document())
+DeleteFromTextNodeCommand::DeleteFromTextNodeCommand(RefPtr<Text>&& node, unsigned offset, unsigned count, EditAction editingAction)
+ : SimpleEditCommand(node->document(), editingAction)
, m_node(node)
, m_offset(offset)
, m_count(count)
@@ -48,19 +47,14 @@ void DeleteFromTextNodeCommand::doApply()
{
ASSERT(m_node);
- if (!m_node->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable))
+ if (!isEditableNode(*m_node))
return;
- ExceptionCode ec = 0;
- m_text = m_node->substringData(m_offset, m_count, ec);
- if (ec)
+ auto result = m_node->substringData(m_offset, m_count);
+ if (result.hasException())
return;
-
- // Need to notify this before actually deleting the text
- if (AXObjectCache* cache = document().existingAXObjectCache())
- cache->nodeTextChangeNotification(m_node.get(), AXObjectCache::AXTextDeleted, m_offset, m_text);
-
- m_node->deleteData(m_offset, m_count, ec);
+ m_text = result.releaseReturnValue();
+ m_node->deleteData(m_offset, m_count);
}
void DeleteFromTextNodeCommand::doUnapply()
@@ -70,10 +64,7 @@ void DeleteFromTextNodeCommand::doUnapply()
if (!m_node->hasEditableStyle())
return;
- m_node->insertData(m_offset, m_text, IGNORE_EXCEPTION);
-
- if (AXObjectCache* cache = document().existingAXObjectCache())
- cache->nodeTextChangeNotification(m_node.get(), AXObjectCache::AXTextInserted, m_offset, m_text);
+ m_node->insertData(m_offset, m_text);
}
#ifndef NDEBUG
diff --git a/Source/WebCore/editing/DeleteFromTextNodeCommand.h b/Source/WebCore/editing/DeleteFromTextNodeCommand.h
index 05b23cc76..90c5145d3 100644
--- a/Source/WebCore/editing/DeleteFromTextNodeCommand.h
+++ b/Source/WebCore/editing/DeleteFromTextNodeCommand.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2005, 2006, 2008, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DeleteFromTextNodeCommand_h
-#define DeleteFromTextNodeCommand_h
+#pragma once
#include "EditCommand.h"
@@ -34,19 +33,20 @@ class Text;
class DeleteFromTextNodeCommand : public SimpleEditCommand {
public:
- static PassRefPtr<DeleteFromTextNodeCommand> create(PassRefPtr<Text> node, unsigned offset, unsigned count)
+ static Ref<DeleteFromTextNodeCommand> create(RefPtr<Text>&& node, unsigned offset, unsigned count, EditAction editingAction = EditActionDelete)
{
- return adoptRef(new DeleteFromTextNodeCommand(node, offset, count));
+ return adoptRef(*new DeleteFromTextNodeCommand(WTFMove(node), offset, count, editingAction));
}
-private:
- DeleteFromTextNodeCommand(PassRefPtr<Text>, unsigned offset, unsigned count);
+protected:
+ DeleteFromTextNodeCommand(RefPtr<Text>&&, unsigned offset, unsigned count, EditAction);
- virtual void doApply() override;
- virtual void doUnapply() override;
+private:
+ void doApply() override;
+ void doUnapply() override;
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
RefPtr<Text> m_node;
@@ -56,5 +56,3 @@ private:
};
} // namespace WebCore
-
-#endif // DeleteFromTextNodeCommand_h
diff --git a/Source/WebCore/editing/DeleteSelectionCommand.cpp b/Source/WebCore/editing/DeleteSelectionCommand.cpp
index 2b5457fc3..0917b8bd1 100644
--- a/Source/WebCore/editing/DeleteSelectionCommand.cpp
+++ b/Source/WebCore/editing/DeleteSelectionCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -30,17 +30,20 @@
#include "DocumentMarkerController.h"
#include "Editor.h"
#include "EditorClient.h"
-#include "Element.h"
+#include "ElementIterator.h"
#include "Frame.h"
-#include "htmlediting.h"
-#include "HTMLInputElement.h"
+#include "HTMLBRElement.h"
+#include "HTMLLinkElement.h"
#include "HTMLNames.h"
+#include "HTMLStyleElement.h"
#include "HTMLTableElement.h"
#include "NodeTraversal.h"
#include "RenderTableCell.h"
#include "RenderText.h"
+#include "RenderedDocumentMarker.h"
#include "Text.h"
#include "VisibleUnits.h"
+#include "htmlediting.h"
namespace WebCore {
@@ -69,8 +72,8 @@ static bool isTableRowEmpty(Node* row)
return true;
}
-DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
- : CompositeEditCommand(document)
+DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup, EditAction editingAction)
+ : CompositeEditCommand(document, editingAction)
, m_hasSelectionToDelete(false)
, m_smartDelete(smartDelete)
, m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
@@ -80,15 +83,11 @@ DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDel
, m_pruneStartBlockIfNecessary(false)
, m_startsAtEmptyLine(false)
, m_sanitizeMarkup(sanitizeMarkup)
- , m_startBlock(0)
- , m_endBlock(0)
- , m_typingStyle(0)
- , m_deleteIntoBlockquoteStyle(0)
{
}
-DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
- : CompositeEditCommand(selection.start().anchorNode()->document())
+DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup, EditAction editingAction)
+ : CompositeEditCommand(selection.start().anchorNode()->document(), editingAction)
, m_hasSelectionToDelete(true)
, m_smartDelete(smartDelete)
, m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
@@ -99,17 +98,13 @@ DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection
, m_startsAtEmptyLine(false)
, m_sanitizeMarkup(sanitizeMarkup)
, m_selectionToDelete(selection)
- , m_startBlock(0)
- , m_endBlock(0)
- , m_typingStyle(0)
- , m_deleteIntoBlockquoteStyle(0)
{
}
void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end)
{
- Node* startSpecialContainer = 0;
- Node* endSpecialContainer = 0;
+ HTMLElement* startSpecialContainer = nullptr;
+ HTMLElement* endSpecialContainer = nullptr;
start = m_selectionToDelete.start();
end = m_selectionToDelete.end();
@@ -126,8 +121,8 @@ void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end)
return;
while (1) {
- startSpecialContainer = 0;
- endSpecialContainer = 0;
+ startSpecialContainer = nullptr;
+ endSpecialContainer = nullptr;
Position s = positionBeforeContainingSpecialElement(start, &startSpecialContainer);
Position e = positionAfterContainingSpecialElement(end, &endSpecialContainer);
@@ -182,6 +177,11 @@ void DeleteSelectionCommand::initializePositionData()
Position start, end;
initializeStartEnd(start, end);
+ if (!isEditablePosition(start, ContentIsEditable))
+ start = firstEditablePositionAfterPositionInRoot(start, highestEditableRoot(start));
+ if (!isEditablePosition(end, ContentIsEditable))
+ end = lastEditablePositionBeforePositionInRoot(end, highestEditableRoot(start));
+
m_upstreamStart = start.upstream();
m_downstreamStart = start.downstream();
m_upstreamEnd = end.upstream();
@@ -299,7 +299,7 @@ void DeleteSelectionCommand::saveTypingStyleState()
if (enclosingNodeOfType(m_selectionToDelete.start(), isMailBlockquote))
m_deleteIntoBlockquoteStyle = EditingStyle::create(m_selectionToDelete.end());
else
- m_deleteIntoBlockquoteStyle = 0;
+ m_deleteIntoBlockquoteStyle = nullptr;
}
bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
@@ -341,10 +341,41 @@ static Position firstEditablePositionInNode(Node* node)
ASSERT(node);
Node* next = node;
while (next && !next->hasEditableStyle())
- next = NodeTraversal::next(next, node);
+ next = NodeTraversal::next(*next, node);
return next ? firstPositionInOrBeforeNode(next) : Position();
}
+void DeleteSelectionCommand::insertBlockPlaceholderForTableCellIfNeeded(Element& element)
+{
+ // Make sure empty cell has some height.
+ auto* renderer = element.renderer();
+ if (!is<RenderTableCell>(renderer))
+ return;
+ if (downcast<RenderTableCell>(*renderer).contentHeight() > 0)
+ return;
+ insertBlockPlaceholder(firstEditablePositionInNode(&element));
+}
+
+void DeleteSelectionCommand::removeNodeUpdatingStates(Node& node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
+{
+ if (&node == m_startBlock && !isEndOfBlock(VisiblePosition(firstPositionInNode(m_startBlock.get())).previous()))
+ m_needPlaceholder = true;
+ else if (&node == m_endBlock && !isStartOfBlock(VisiblePosition(lastPositionInNode(m_startBlock.get())).next()))
+ m_needPlaceholder = true;
+
+ // FIXME: Update the endpoints of the range being deleted.
+ updatePositionForNodeRemoval(m_endingPosition, node);
+ updatePositionForNodeRemoval(m_leadingWhitespace, node);
+ updatePositionForNodeRemoval(m_trailingWhitespace, node);
+
+ CompositeEditCommand::removeNode(&node, shouldAssumeContentIsAlwaysEditable);
+}
+
+static inline bool shouldRemoveContentOnly(const Node& node)
+{
+ return isTableStructureNode(&node) || node.isRootEditableElement();
+}
+
void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
{
if (!node)
@@ -372,38 +403,34 @@ void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeConte
}
}
- if (isTableStructureNode(node.get()) || node->isRootEditableElement()) {
+ if (shouldRemoveContentOnly(*node)) {
// Do not remove an element of table structure; remove its contents.
// Likewise for the root editable element.
- Node* child = node->firstChild();
+ auto* child = NodeTraversal::next(*node, node.get());
while (child) {
- Node* remove = child;
- child = child->nextSibling();
- removeNode(remove, shouldAssumeContentIsAlwaysEditable);
+ if (shouldRemoveContentOnly(*child)) {
+ child = NodeTraversal::next(*child, node.get());
+ continue;
+ }
+ auto* remove = child;
+ child = NodeTraversal::nextSkippingChildren(*child, node.get());
+ removeNodeUpdatingStates(*remove, shouldAssumeContentIsAlwaysEditable);
}
- // Make sure empty cell has some height, if a placeholder can be inserted.
+ ASSERT(is<Element>(*node));
+ auto& element = downcast<Element>(*node);
document().updateLayoutIgnorePendingStylesheets();
- RenderObject *r = node->renderer();
- if (r && r->isTableCell() && toRenderTableCell(r)->contentHeight() <= 0) {
- Position firstEditablePosition = firstEditablePositionInNode(node.get());
- if (firstEditablePosition.isNotNull())
- insertBlockPlaceholder(firstEditablePosition);
+ // Check if we need to insert a placeholder for descendant table cells.
+ auto* descendant = ElementTraversal::next(element, &element);
+ while (descendant) {
+ auto* placeholderCandidate = descendant;
+ descendant = ElementTraversal::next(*descendant, &element);
+ insertBlockPlaceholderForTableCellIfNeeded(*placeholderCandidate);
}
+ insertBlockPlaceholderForTableCellIfNeeded(element);
return;
}
-
- if (node == m_startBlock && !isEndOfBlock(VisiblePosition(firstPositionInNode(m_startBlock.get())).previous()))
- m_needPlaceholder = true;
- else if (node == m_endBlock && !isStartOfBlock(VisiblePosition(lastPositionInNode(m_startBlock.get())).next()))
- m_needPlaceholder = true;
-
- // FIXME: Update the endpoints of the range being deleted.
- updatePositionForNodeRemoval(m_endingPosition, node.get());
- updatePositionForNodeRemoval(m_leadingWhitespace, node.get());
- updatePositionForNodeRemoval(m_trailingWhitespace, node.get());
-
- CompositeEditCommand::removeNode(node, shouldAssumeContentIsAlwaysEditable);
+ removeNodeUpdatingStates(*node, shouldAssumeContentIsAlwaysEditable);
}
static void updatePositionForTextRemoval(Node* node, int offset, int count, Position& position)
@@ -433,9 +460,9 @@ void DeleteSelectionCommand::makeStylingElementsDirectChildrenOfEditableRootToPr
RefPtr<Range> range = m_selectionToDelete.toNormalizedRange();
RefPtr<Node> node = range->firstNode();
while (node && node != range->pastLastNode()) {
- RefPtr<Node> nextNode = NodeTraversal::next(node.get());
- if ((node->hasTagName(styleTag) && !(toElement(node.get())->hasAttribute(scopedAttr))) || node->hasTagName(linkTag)) {
- nextNode = NodeTraversal::nextSkippingChildren(node.get());
+ RefPtr<Node> nextNode = NodeTraversal::next(*node);
+ if ((is<HTMLStyleElement>(*node) && !downcast<HTMLStyleElement>(*node).hasAttributeWithoutSynchronization(scopedAttr)) || is<HTMLLinkElement>(*node)) {
+ nextNode = NodeTraversal::nextSkippingChildren(*node);
RefPtr<ContainerNode> rootEditableElement = node->rootEditableElement();
if (rootEditableElement) {
removeNode(node);
@@ -457,21 +484,22 @@ void DeleteSelectionCommand::handleGeneralDelete()
makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss();
// Never remove the start block unless it's a table, in which case we won't merge content in.
- if (startNode == m_startBlock && startOffset == 0 && canHaveChildrenForEditing(startNode) && !isHTMLTableElement(startNode)) {
+ if (startNode == m_startBlock && !startOffset && canHaveChildrenForEditing(*startNode) && !is<HTMLTableElement>(*startNode)) {
startOffset = 0;
- startNode = NodeTraversal::next(startNode);
+ startNode = NodeTraversal::next(*startNode);
if (!startNode)
return;
}
- if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) {
- Text* text = toText(startNode);
- if (text->length() > (unsigned)caretMaxOffset(startNode))
- deleteTextFromNode(text, caretMaxOffset(startNode), text->length() - caretMaxOffset(startNode));
+ int startNodeCaretMaxOffset = caretMaxOffset(*startNode);
+ if (startOffset >= startNodeCaretMaxOffset && is<Text>(*startNode)) {
+ Text& text = downcast<Text>(*startNode);
+ if (text.length() > static_cast<unsigned>(startNodeCaretMaxOffset))
+ deleteTextFromNode(&text, startNodeCaretMaxOffset, text.length() - startNodeCaretMaxOffset);
}
- if (startOffset >= lastOffsetForEditing(startNode)) {
- startNode = NodeTraversal::nextSkippingChildren(startNode);
+ if (startOffset >= lastOffsetForEditing(*startNode)) {
+ startNode = NodeTraversal::nextSkippingChildren(*startNode);
startOffset = 0;
}
@@ -481,10 +509,10 @@ void DeleteSelectionCommand::handleGeneralDelete()
if (startNode == m_downstreamEnd.deprecatedNode()) {
if (m_downstreamEnd.deprecatedEditingOffset() - startOffset > 0) {
- if (startNode->isTextNode()) {
+ if (is<Text>(*startNode)) {
// in a text node that needs to be trimmed
- Text* text = toText(startNode);
- deleteTextFromNode(text, startOffset, m_downstreamEnd.deprecatedEditingOffset() - startOffset);
+ Text& text = downcast<Text>(*startNode);
+ deleteTextFromNode(&text, startOffset, m_downstreamEnd.deprecatedEditingOffset() - startOffset);
} else {
removeChildrenInRange(startNode, startOffset, m_downstreamEnd.deprecatedEditingOffset());
m_endingPosition = m_upstreamStart;
@@ -501,51 +529,51 @@ void DeleteSelectionCommand::handleGeneralDelete()
RefPtr<Node> node(startNode);
if (startOffset > 0) {
- if (startNode->isTextNode()) {
+ if (is<Text>(*startNode)) {
// in a text node that needs to be trimmed
- Text* text = toText(node.get());
- deleteTextFromNode(text, startOffset, text->length() - startOffset);
- node = NodeTraversal::next(node.get());
+ Text& text = downcast<Text>(*node);
+ deleteTextFromNode(&text, startOffset, text.length() - startOffset);
+ node = NodeTraversal::next(*node);
} else {
- node = startNode->childNode(startOffset);
+ node = startNode->traverseToChildAt(startOffset);
}
- } else if (startNode == m_upstreamEnd.deprecatedNode() && startNode->isTextNode()) {
- Text* text = toText(m_upstreamEnd.deprecatedNode());
- deleteTextFromNode(text, 0, m_upstreamEnd.deprecatedEditingOffset());
+ } else if (startNode == m_upstreamEnd.deprecatedNode() && is<Text>(*startNode)) {
+ Text& text = downcast<Text>(*m_upstreamEnd.deprecatedNode());
+ deleteTextFromNode(&text, 0, m_upstreamEnd.deprecatedEditingOffset());
}
// handle deleting all nodes that are completely selected
while (node && node != m_downstreamEnd.deprecatedNode()) {
if (comparePositions(firstPositionInOrBeforeNode(node.get()), m_downstreamEnd) >= 0) {
// NodeTraversal::nextSkippingChildren just blew past the end position, so stop deleting
- node = 0;
- } else if (!m_downstreamEnd.deprecatedNode()->isDescendantOf(node.get())) {
- RefPtr<Node> nextNode = NodeTraversal::nextSkippingChildren(node.get());
+ node = nullptr;
+ } else if (!m_downstreamEnd.deprecatedNode()->isDescendantOf(*node)) {
+ RefPtr<Node> nextNode = NodeTraversal::nextSkippingChildren(*node);
// if we just removed a node from the end container, update end position so the
// check above will work
- updatePositionForNodeRemoval(m_downstreamEnd, node.get());
+ updatePositionForNodeRemoval(m_downstreamEnd, *node);
removeNode(node.get());
node = nextNode.get();
} else {
Node* n = node->lastDescendant();
- if (m_downstreamEnd.deprecatedNode() == n && m_downstreamEnd.deprecatedEditingOffset() >= caretMaxOffset(n)) {
+ if (m_downstreamEnd.deprecatedNode() == n && m_downstreamEnd.deprecatedEditingOffset() >= caretMaxOffset(*n)) {
removeNode(node.get());
- node = 0;
+ node = nullptr;
} else
- node = NodeTraversal::next(node.get());
+ node = NodeTraversal::next(*node);
}
}
- if (m_downstreamEnd.deprecatedNode() != startNode && !m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode()) && m_downstreamEnd.anchorNode()->inDocument() && m_downstreamEnd.deprecatedEditingOffset() >= caretMinOffset(m_downstreamEnd.deprecatedNode())) {
- if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildrenForEditing(m_downstreamEnd.deprecatedNode())) {
+ if (m_downstreamEnd.deprecatedNode() != startNode && !m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode()) && m_downstreamEnd.anchorNode()->isConnected() && m_downstreamEnd.deprecatedEditingOffset() >= caretMinOffset(*m_downstreamEnd.deprecatedNode())) {
+ if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildrenForEditing(*m_downstreamEnd.deprecatedNode())) {
// The node itself is fully selected, not just its contents. Delete it.
removeNode(m_downstreamEnd.deprecatedNode());
} else {
- if (m_downstreamEnd.deprecatedNode()->isTextNode()) {
+ if (is<Text>(*m_downstreamEnd.deprecatedNode())) {
// in a text node that needs to be trimmed
- Text* text = toText(m_downstreamEnd.deprecatedNode());
+ Text& text = downcast<Text>(*m_downstreamEnd.deprecatedNode());
if (m_downstreamEnd.deprecatedEditingOffset() > 0) {
- deleteTextFromNode(text, 0, m_downstreamEnd.deprecatedEditingOffset());
+ deleteTextFromNode(&text, 0, m_downstreamEnd.deprecatedEditingOffset());
}
// Remove children of m_downstreamEnd.deprecatedNode() that come after m_upstreamStart.
// Don't try to remove children if m_upstreamStart was inside m_downstreamEnd.deprecatedNode()
@@ -553,14 +581,14 @@ void DeleteSelectionCommand::handleGeneralDelete()
// know how many children to remove.
// FIXME: Make m_upstreamStart a position we update as we remove content, then we can
// always know which children to remove.
- } else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.anchorNode()->inDocument())) {
- int offset = 0;
+ } else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.anchorNode()->isConnected())) {
+ unsigned offset = 0;
if (m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode())) {
Node* n = m_upstreamStart.deprecatedNode();
while (n && n->parentNode() != m_downstreamEnd.deprecatedNode())
n = n->parentNode();
if (n)
- offset = n->nodeIndex() + 1;
+ offset = n->computeNodeIndex() + 1;
}
removeChildrenInRange(m_downstreamEnd.deprecatedNode(), offset, m_downstreamEnd.deprecatedEditingOffset());
m_downstreamEnd = createLegacyEditingPosition(m_downstreamEnd.deprecatedNode(), offset);
@@ -574,15 +602,15 @@ void DeleteSelectionCommand::fixupWhitespace()
{
document().updateLayoutIgnorePendingStylesheets();
// FIXME: isRenderedCharacter should be removed, and we should use VisiblePosition::characterAfter and VisiblePosition::characterBefore
- if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && m_leadingWhitespace.deprecatedNode()->isTextNode()) {
- Text* textNode = toText(m_leadingWhitespace.deprecatedNode());
- ASSERT(!textNode->renderer() || textNode->renderer()->style().collapseWhiteSpace());
- replaceTextInNodePreservingMarkers(textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
+ if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && is<Text>(*m_leadingWhitespace.deprecatedNode())) {
+ Text& textNode = downcast<Text>(*m_leadingWhitespace.deprecatedNode());
+ ASSERT(!textNode.renderer() || textNode.renderer()->style().collapseWhiteSpace());
+ replaceTextInNodePreservingMarkers(&textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
}
- if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && m_trailingWhitespace.deprecatedNode()->isTextNode()) {
- Text* textNode = toText(m_trailingWhitespace.deprecatedNode());
- ASSERT(!textNode->renderer() || textNode->renderer()->style().collapseWhiteSpace());
- replaceTextInNodePreservingMarkers(textNode, m_trailingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
+ if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && is<Text>(*m_trailingWhitespace.deprecatedNode())) {
+ Text& textNode = downcast<Text>(*m_trailingWhitespace.deprecatedNode());
+ ASSERT(!textNode.renderer() || textNode.renderer()->style().collapseWhiteSpace());
+ replaceTextInNodePreservingMarkers(&textNode, m_trailingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
}
}
@@ -605,9 +633,9 @@ void DeleteSelectionCommand::mergeParagraphs()
ASSERT(!m_pruneStartBlockIfNecessary);
// FIXME: Deletion should adjust selection endpoints as it removes nodes so that we never get into this state (4099839).
- if (!m_downstreamEnd.anchorNode()->inDocument() || !m_upstreamStart.anchorNode()->inDocument())
+ if (!m_downstreamEnd.anchorNode()->isConnected() || !m_upstreamStart.anchorNode()->isConnected())
return;
-
+
// FIXME: The deletion algorithm shouldn't let this happen.
if (comparePositions(m_upstreamStart, m_downstreamEnd) > 0)
return;
@@ -629,7 +657,7 @@ void DeleteSelectionCommand::mergeParagraphs()
// We need to merge into m_upstreamStart's block, but it's been emptied out and collapsed by deletion.
if (!mergeDestination.deepEquivalent().deprecatedNode() || !mergeDestination.deepEquivalent().deprecatedNode()->isDescendantOf(enclosingBlock(m_upstreamStart.containerNode())) || m_startsAtEmptyLine) {
- insertNodeAt(createBreakElement(document()).get(), m_upstreamStart);
+ insertNodeAt(HTMLBRElement::create(document()).ptr(), m_upstreamStart);
mergeDestination = VisiblePosition(m_upstreamStart);
}
@@ -676,7 +704,7 @@ void DeleteSelectionCommand::mergeParagraphs()
void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows()
{
- if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_startTableRow) {
+ if (m_endTableRow && m_endTableRow->isConnected() && m_endTableRow != m_startTableRow) {
Node* row = m_endTableRow->previousSibling();
while (row && row != m_startTableRow) {
RefPtr<Node> previousRow = row->previousSibling();
@@ -689,7 +717,7 @@ void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows()
}
// Remove empty rows after the start row.
- if (m_startTableRow && m_startTableRow->inDocument() && m_startTableRow != m_endTableRow) {
+ if (m_startTableRow && m_startTableRow->isConnected() && m_startTableRow != m_endTableRow) {
Node* row = m_startTableRow->nextSibling();
while (row && row != m_endTableRow) {
RefPtr<Node> nextRow = row->nextSibling();
@@ -698,17 +726,18 @@ void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows()
row = nextRow.get();
}
}
-
- if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_startTableRow)
+
+ if (m_endTableRow && m_endTableRow->isConnected() && m_endTableRow != m_startTableRow) {
if (isTableRowEmpty(m_endTableRow.get())) {
// Don't remove m_endTableRow if it's where we're putting the ending selection.
- if (!m_endingPosition.deprecatedNode()->isDescendantOf(m_endTableRow.get())) {
+ if (!m_endingPosition.deprecatedNode()->isDescendantOf(*m_endTableRow)) {
// FIXME: We probably shouldn't remove m_endTableRow unless it's fully selected, even if it is empty.
// We'll need to start adjusting the selection endpoints during deletion to know whether or not m_endTableRow
// was fully selected here.
CompositeEditCommand::removeNode(m_endTableRow.get());
}
}
+ }
}
void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
@@ -725,11 +754,11 @@ void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
// If we deleted into a blockquote, but are now no longer in a blockquote, use the alternate typing style
if (m_deleteIntoBlockquoteStyle && !enclosingNodeOfType(m_endingPosition, isMailBlockquote, CanCrossEditingBoundary))
m_typingStyle = m_deleteIntoBlockquoteStyle;
- m_deleteIntoBlockquoteStyle = 0;
+ m_deleteIntoBlockquoteStyle = nullptr;
m_typingStyle->prepareToApplyAt(m_endingPosition);
if (m_typingStyle->isEmpty())
- m_typingStyle = 0;
+ m_typingStyle = nullptr;
// 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
@@ -763,9 +792,8 @@ String DeleteSelectionCommand::originalStringForAutocorrectionAtBeginningOfSelec
return String();
RefPtr<Range> rangeOfFirstCharacter = Range::create(document(), startOfSelection.deepEquivalent(), nextPosition.deepEquivalent());
- Vector<DocumentMarker*> markers = document().markers().markersInRange(rangeOfFirstCharacter.get(), DocumentMarker::Autocorrected);
- for (size_t i = 0; i < markers.size(); ++i) {
- const DocumentMarker* marker = markers[i];
+ Vector<RenderedDocumentMarker*> markers = document().markers().markersInRange(rangeOfFirstCharacter.get(), DocumentMarker::Autocorrected);
+ for (auto* marker : markers) {
int startOffset = marker->startOffset();
if (startOffset == startOfSelection.deepEquivalent().offsetInContainerNode())
return marker->description();
@@ -782,7 +810,7 @@ void DeleteSelectionCommand::removeRedundantBlocks()
while (node != rootNode) {
if (isRemovableBlock(node)) {
if (node == m_endingPosition.anchorNode())
- updatePositionForNodeRemovalPreservingChildren(m_endingPosition, node);
+ updatePositionForNodeRemovalPreservingChildren(m_endingPosition, *node);
CompositeEditCommand::removeNodePreservingChildren(node);
node = m_endingPosition.anchorNode();
@@ -821,9 +849,10 @@ void DeleteSelectionCommand::doApply()
// Don't need a placeholder when deleting a selection that starts just before a table
// and ends inside it (we do need placeholders to hold open empty cells, but that's
// handled elsewhere).
- if (Node* table = isLastPositionBeforeTable(m_selectionToDelete.visibleStart()))
- if (m_selectionToDelete.end().deprecatedNode()->isDescendantOf(table))
+ if (auto* table = isLastPositionBeforeTable(m_selectionToDelete.visibleStart())) {
+ if (m_selectionToDelete.end().deprecatedNode()->isDescendantOf(*table))
m_needPlaceholder = false;
+ }
}
@@ -853,32 +882,23 @@ void DeleteSelectionCommand::doApply()
removePreviouslySelectedEmptyTableRows();
- RefPtr<Node> placeholder = m_needPlaceholder ? createBreakElement(document()).get() : 0;
-
- if (placeholder) {
+ if (m_needPlaceholder) {
if (m_sanitizeMarkup)
removeRedundantBlocks();
- insertNodeAt(placeholder.get(), m_endingPosition);
- }
-
-#if PLATFORM(IOS)
- // This checking is due to iphone shows the last entered character momentarily, removing and adding back the
- // space when deleting password cause space been showed insecurely.
- bool isSecure = NO;
- Node* node = m_endingPosition.deprecatedNode();
- if (node && node->isTextNode()) {
- Text* textNode = static_cast<Text*>(node);
- if (textNode->length() > 0) {
- RenderObject* renderer = textNode->renderer();
- isSecure = renderer->style().textSecurity() != TSNONE;
- }
+ insertNodeAt(HTMLBRElement::create(document()), m_endingPosition);
}
-
- if (!isSecure)
+
+ bool shouldRebalaceWhiteSpace = true;
+ if (!frame().editor().behavior().shouldRebalanceWhiteSpacesInSecureField()) {
+ Node* node = m_endingPosition.deprecatedNode();
+ if (is<Text>(node)) {
+ Text& textNode = downcast<Text>(*node);
+ if (textNode.length() && textNode.renderer())
+ shouldRebalaceWhiteSpace = textNode.renderer()->style().textSecurity() == TSNONE;
+ }
+ }
+ if (shouldRebalaceWhiteSpace)
rebalanceWhitespaceAt(m_endingPosition);
-#else
- rebalanceWhitespaceAt(m_endingPosition);
-#endif
calculateTypingStyleAfterDelete();
@@ -889,14 +909,6 @@ void DeleteSelectionCommand::doApply()
clearTransientState();
}
-EditAction DeleteSelectionCommand::editingAction() const
-{
- // Note that DeleteSelectionCommand is also used when the user presses the Delete key,
- // but in that case there's a TypingCommand that supplies the editingAction(), so
- // the Undo menu correctly shows "Undo Typing"
- return EditActionCut;
-}
-
// Normally deletion doesn't preserve the typing style that was present before it. For example,
// type a character, Bold, then delete the character and start typing. The Bold typing style shouldn't
// stick around. Deletion should preserve a typing style that *it* sets, however.
diff --git a/Source/WebCore/editing/DeleteSelectionCommand.h b/Source/WebCore/editing/DeleteSelectionCommand.h
index 4c0f723de..c17f6deea 100644
--- a/Source/WebCore/editing/DeleteSelectionCommand.h
+++ b/Source/WebCore/editing/DeleteSelectionCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DeleteSelectionCommand_h
-#define DeleteSelectionCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -34,25 +33,24 @@ class EditingStyle;
class DeleteSelectionCommand : public CompositeEditCommand {
public:
- static PassRefPtr<DeleteSelectionCommand> create(Document& document, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false, bool sanitizeMarkup = true)
+ static Ref<DeleteSelectionCommand> create(Document& document, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false, bool sanitizeMarkup = true, EditAction editingAction = EditActionDelete)
{
- return adoptRef(new DeleteSelectionCommand(document, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
+ return adoptRef(*new DeleteSelectionCommand(document, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup, editingAction));
}
- static PassRefPtr<DeleteSelectionCommand> create(const VisibleSelection& selection, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false, bool sanitizeMarkup = true)
+ static Ref<DeleteSelectionCommand> create(const VisibleSelection& selection, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false, bool sanitizeMarkup = true, EditAction editingAction = EditActionDelete)
{
- return adoptRef(new DeleteSelectionCommand(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup));
+ return adoptRef(*new DeleteSelectionCommand(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements, sanitizeMarkup, editingAction));
}
protected:
- DeleteSelectionCommand(Document&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool santizeMarkup);
+ DeleteSelectionCommand(Document&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool santizeMarkup, EditAction = EditActionDelete);
private:
- DeleteSelectionCommand(const VisibleSelection&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup);
+ DeleteSelectionCommand(const VisibleSelection&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup, EditAction);
- virtual void doApply();
- virtual EditAction editingAction() const;
+ void doApply() override;
- virtual bool preservesTypingStyle() const;
+ bool preservesTypingStyle() const override;
void initializeStartEnd(Position&, Position&);
void setStartingSelectionOnSmartDelete(const Position&, const Position&);
@@ -68,13 +66,16 @@ private:
void calculateTypingStyleAfterDelete();
void clearTransientState();
void makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss();
- virtual void removeNode(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable);
- virtual void deleteTextFromNode(PassRefPtr<Text>, unsigned, unsigned);
+ void removeNode(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable = DoNotAssumeContentIsAlwaysEditable) override;
+ void deleteTextFromNode(PassRefPtr<Text>, unsigned, unsigned) override;
void removeRedundantBlocks();
// This function provides access to original string after the correction has been deleted.
String originalStringForAutocorrectionAtBeginningOfSelection();
+ void removeNodeUpdatingStates(Node&, ShouldAssumeContentIsAlwaysEditable);
+ void insertBlockPlaceholderForTableCellIfNeeded(Element&);
+
bool m_hasSelectionToDelete;
bool m_smartDelete;
bool m_mergeBlocksAfterDelete;
@@ -106,5 +107,3 @@ private:
};
} // namespace WebCore
-
-#endif // DeleteSelectionCommand_h
diff --git a/Source/WebCore/editing/DictationAlternative.h b/Source/WebCore/editing/DictationAlternative.h
index 08fcea386..fa5cc943e 100644
--- a/Source/WebCore/editing/DictationAlternative.h
+++ b/Source/WebCore/editing/DictationAlternative.h
@@ -23,15 +23,14 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DictationAlternative_h
-#define DictationAlternative_h
+#pragma once
#include <stdint.h>
namespace WebCore {
struct DictationAlternative {
- DictationAlternative(unsigned start, unsigned length, uint64_t context);
- DictationAlternative();
+ WEBCORE_EXPORT DictationAlternative(unsigned start, unsigned length, uint64_t context);
+ WEBCORE_EXPORT DictationAlternative();
unsigned rangeStart;
unsigned rangeLength;
@@ -39,6 +38,4 @@ struct DictationAlternative {
uint64_t dictationContext;
};
-}
-
-#endif // DictationAlternative_h
+} // namespace WebCore
diff --git a/Source/WebCore/editing/DictationCommand.cpp b/Source/WebCore/editing/DictationCommand.cpp
index 722882591..9b15a8186 100644
--- a/Source/WebCore/editing/DictationCommand.cpp
+++ b/Source/WebCore/editing/DictationCommand.cpp
@@ -55,17 +55,17 @@ private:
class DictationMarkerSupplier : public TextInsertionMarkerSupplier {
public:
- static PassRefPtr<DictationMarkerSupplier> create(const Vector<DictationAlternative>& alternatives)
+ static Ref<DictationMarkerSupplier> create(const Vector<DictationAlternative>& alternatives)
{
- return adoptRef(new DictationMarkerSupplier(alternatives));
+ return adoptRef(*new DictationMarkerSupplier(alternatives));
}
- virtual void addMarkersToTextNode(Text* textNode, unsigned offsetOfInsertion, const String& textToBeInserted)
+ void addMarkersToTextNode(Text* textNode, unsigned offsetOfInsertion, const String& textToBeInserted) override
{
- DocumentMarkerController& markerController = textNode->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));
+ auto& markerController = textNode->document().markers();
+ for (auto& alternative : m_alternatives) {
+ DocumentMarker::DictationData data { alternative.dictationContext, textToBeInserted.substring(alternative.rangeStart, alternative.rangeLength) };
+ markerController.addMarkerToNode(textNode, alternative.rangeStart + offsetOfInsertion, alternative.rangeLength, DocumentMarker::DictationAlternatives, WTFMove(data));
markerController.addMarkerToNode(textNode, alternative.rangeStart + offsetOfInsertion, alternative.rangeLength, DocumentMarker::SpellCheckingExemption);
}
}
@@ -86,9 +86,9 @@ DictationCommand::DictationCommand(Document& document, const String& text, const
{
}
-void DictationCommand::insertText(Document* document, const String& text, const Vector<DictationAlternative>& alternatives, const VisibleSelection& selectionForInsertion)
+void DictationCommand::insertText(Document& document, const String& text, const Vector<DictationAlternative>& alternatives, const VisibleSelection& selectionForInsertion)
{
- RefPtr<Frame> frame = document->frame();
+ RefPtr<Frame> frame = document.frame();
ASSERT(frame);
VisibleSelection currentSelection = frame->selection().selection();
@@ -97,25 +97,26 @@ void DictationCommand::insertText(Document* document, const String& text, const
RefPtr<DictationCommand> cmd;
if (newText == text)
- cmd = DictationCommand::create(*document, newText, alternatives);
+ 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);
+ cmd = DictationCommand::create(document, newText, Vector<DictationAlternative>());
+ applyTextInsertionCommand(frame.get(), *cmd, selectionForInsertion, currentSelection);
}
void DictationCommand::doApply()
{
DictationCommandLineOperation operation(this);
forEachLineInString(m_textToInsert, operation);
+ postTextStateChangeNotification(AXTextEditTypeDictation, m_textToInsert);
}
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));
+ RefPtr<InsertTextCommand> command = InsertTextCommand::createWithMarkerSupplier(document(), m_textToInsert.substring(lineStart, lineLength), DictationMarkerSupplier::create(alternativesInLine), EditActionDictation);
applyCommandToComposite(command, endingSelection());
}
@@ -124,13 +125,12 @@ void DictationCommand::insertParagraphSeparator()
if (!canAppendNewLineFeedToSelection(endingSelection()))
return;
- applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
+ applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), false, false, EditActionDictation));
}
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];
+ for (auto& alternative : m_alternatives) {
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
index 1dee19bf2..13c188772 100644
--- a/Source/WebCore/editing/DictationCommand.h
+++ b/Source/WebCore/editing/DictationCommand.h
@@ -23,30 +23,27 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DictationCommand_h
-#define DictationCommand_h
+#pragma once
#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; }
+ static void insertText(Document&, const String&, const Vector<DictationAlternative>& alternatives, const VisibleSelection&);
+ bool isDictationCommand() const override { return true; }
private:
- static PassRefPtr<DictationCommand> create(Document& document, const String& text, const Vector<DictationAlternative>& alternatives)
+ static Ref<DictationCommand> create(Document& document, const String& text, const Vector<DictationAlternative>& alternatives)
{
- return adoptRef(new DictationCommand(document, text, alternatives));
+ return adoptRef(*new DictationCommand(document, text, alternatives));
}
DictationCommand(Document&, const String& text, const Vector<DictationAlternative>& alternatives);
- virtual void doApply();
+ void doApply() override;
void insertTextRunWithoutNewlines(size_t lineStart, size_t lineLength);
void insertParagraphSeparator();
@@ -55,6 +52,5 @@ private:
String m_textToInsert;
Vector<DictationAlternative> m_alternatives;
};
-}
-#endif // DictationCommand_h
+} // namespace WebCore
diff --git a/Source/WebCore/editing/DictionaryPopupInfo.h b/Source/WebCore/editing/DictionaryPopupInfo.h
new file mode 100644
index 000000000..f1c65a48d
--- /dev/null
+++ b/Source/WebCore/editing/DictionaryPopupInfo.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014-2015 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. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#pragma once
+
+#include "FloatPoint.h"
+#include "TextIndicator.h"
+#include <wtf/RetainPtr.h>
+
+OBJC_CLASS NSAttributedString;
+OBJC_CLASS NSDictionary;
+
+namespace WebCore {
+
+struct DictionaryPopupInfo {
+ FloatPoint origin;
+ TextIndicatorData textIndicator;
+#if PLATFORM(COCOA)
+ RetainPtr<NSDictionary> options;
+ RetainPtr<NSAttributedString> attributedString;
+#endif
+};
+
+} // namespace WebCore
diff --git a/Source/WebCore/editing/EditAction.h b/Source/WebCore/editing/EditAction.h
index 3d397599b..0aba4a5aa 100644
--- a/Source/WebCore/editing/EditAction.h
+++ b/Source/WebCore/editing/EditAction.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,12 +23,14 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef EditAction_h
-#define EditAction_h
+#pragma once
namespace WebCore {
typedef enum {
EditActionUnspecified,
+ EditActionInsert,
+ EditActionInsertReplacement,
+ EditActionInsertFromDrop,
EditActionSetColor,
EditActionSetBackgroundColor,
EditActionTurnOffKerning,
@@ -53,25 +55,35 @@ namespace WebCore {
EditActionUnderline,
EditActionOutline,
EditActionUnscript,
- EditActionDrag,
+ EditActionDeleteByDrag,
EditActionCut,
EditActionBold,
EditActionItalics,
-#if PLATFORM(IOS)
EditActionDelete,
EditActionDictation,
-#endif
EditActionPaste,
EditActionPasteFont,
EditActionPasteRuler,
- EditActionTyping,
+ EditActionTypingDeleteSelection,
+ EditActionTypingDeleteBackward,
+ EditActionTypingDeleteForward,
+ EditActionTypingDeleteWordBackward,
+ EditActionTypingDeleteWordForward,
+ EditActionTypingDeleteLineBackward,
+ EditActionTypingDeleteLineForward,
+ EditActionTypingDeletePendingComposition,
+ EditActionTypingDeleteFinalComposition,
+ EditActionTypingInsertText,
+ EditActionTypingInsertLineBreak,
+ EditActionTypingInsertParagraph,
+ EditActionTypingInsertPendingComposition,
+ EditActionTypingInsertFinalComposition,
EditActionCreateLink,
EditActionUnlink,
EditActionFormatBlock,
- EditActionInsertList,
+ EditActionInsertOrderedList,
+ EditActionInsertUnorderedList,
EditActionIndent,
EditActionOutdent
} EditAction;
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/EditCommand.cpp b/Source/WebCore/editing/EditCommand.cpp
index 104195d03..5ded31647 100644
--- a/Source/WebCore/editing/EditCommand.cpp
+++ b/Source/WebCore/editing/EditCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -26,29 +26,107 @@
#include "config.h"
#include "EditCommand.h"
+#include "AXObjectCache.h"
#include "CompositeEditCommand.h"
#include "Document.h"
#include "Editor.h"
#include "Element.h"
-#include "EventNames.h"
#include "Frame.h"
+#include "HTMLInputElement.h"
+#include "HTMLTextAreaElement.h"
#include "NodeTraversal.h"
#include "htmlediting.h"
namespace WebCore {
-EditCommand::EditCommand(Document& document)
+String inputTypeNameForEditingAction(EditAction action)
+{
+ switch (action) {
+ case EditActionJustify:
+ case EditActionAlignLeft:
+ return ASCIILiteral("formatJustifyLeft");
+ case EditActionAlignRight:
+ return ASCIILiteral("formatJustifyRight");
+ case EditActionCenter:
+ return ASCIILiteral("formatJustifyCenter");
+ case EditActionSubscript:
+ return ASCIILiteral("formatSubscript");
+ case EditActionSuperscript:
+ return ASCIILiteral("formatSuperscript");
+ case EditActionUnderline:
+ return ASCIILiteral("formatUnderline");
+ case EditActionSetColor:
+ return ASCIILiteral("formatForeColor");
+ case EditActionDeleteByDrag:
+ return ASCIILiteral("deleteByDrag");
+ case EditActionCut:
+ return ASCIILiteral("deleteByCut");
+ case EditActionBold:
+ return ASCIILiteral("formatBold");
+ case EditActionItalics:
+ return ASCIILiteral("formatItalic");
+ case EditActionPaste:
+ return ASCIILiteral("insertFromPaste");
+ case EditActionDelete:
+ case EditActionTypingDeleteSelection:
+ return ASCIILiteral("deleteContent");
+ case EditActionTypingDeleteBackward:
+ return ASCIILiteral("deleteContentBackward");
+ case EditActionTypingDeleteForward:
+ return ASCIILiteral("deleteContentForward");
+ case EditActionTypingDeleteWordBackward:
+ return ASCIILiteral("deleteWordBackward");
+ case EditActionTypingDeleteWordForward:
+ return ASCIILiteral("deleteWordForward");
+ case EditActionTypingDeleteLineBackward:
+ return ASCIILiteral("deleteHardLineBackward");
+ case EditActionTypingDeleteLineForward:
+ return ASCIILiteral("deleteHardLineForward");
+ case EditActionTypingDeletePendingComposition:
+ return ASCIILiteral("deleteCompositionText");
+ case EditActionTypingDeleteFinalComposition:
+ return ASCIILiteral("deleteByComposition");
+ case EditActionInsert:
+ case EditActionTypingInsertText:
+ return ASCIILiteral("insertText");
+ case EditActionInsertReplacement:
+ return ASCIILiteral("insertReplacementText");
+ case EditActionInsertFromDrop:
+ return ASCIILiteral("insertFromDrop");
+ case EditActionTypingInsertLineBreak:
+ return ASCIILiteral("insertLineBreak");
+ case EditActionTypingInsertParagraph:
+ return ASCIILiteral("insertParagraph");
+ case EditActionInsertOrderedList:
+ return ASCIILiteral("insertOrderedList");
+ case EditActionInsertUnorderedList:
+ return ASCIILiteral("insertUnorderedList");
+ case EditActionTypingInsertPendingComposition:
+ return ASCIILiteral("insertCompositionText");
+ case EditActionTypingInsertFinalComposition:
+ return ASCIILiteral("insertFromComposition");
+ case EditActionIndent:
+ return ASCIILiteral("formatIndent");
+ case EditActionOutdent:
+ return ASCIILiteral("formatOutdent");
+ case EditActionSetWritingDirection:
+ return ASCIILiteral("formatSetInlineTextDirection");
+ default:
+ return emptyString();
+ }
+}
+
+EditCommand::EditCommand(Document& document, EditAction editingAction)
: m_document(document)
- , m_parent(0)
+ , m_editingAction(editingAction)
{
ASSERT(document.frame());
- setStartingSelection(m_document->frame()->editor().avoidIntersectionWithDeleteButtonController(m_document->frame()->selection().selection()));
+ setStartingSelection(m_document->frame()->selection().selection());
setEndingSelection(m_startingSelection);
}
EditCommand::EditCommand(Document& document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection)
: m_document(document)
- , m_parent(0)
{
ASSERT(document.frame());
setStartingSelection(startingSelection);
@@ -65,9 +143,15 @@ Frame& EditCommand::frame()
return *document().frame();
}
+const Frame& EditCommand::frame() const
+{
+ ASSERT(document().frame());
+ return *document().frame();
+}
+
EditAction EditCommand::editingAction() const
{
- return EditActionUnspecified;
+ return m_editingAction;
}
static inline EditCommandComposition* compositionIfPossible(EditCommand* command)
@@ -77,13 +161,28 @@ static inline EditCommandComposition* compositionIfPossible(EditCommand* command
return toCompositeEditCommand(command)->composition();
}
+bool EditCommand::isEditingTextAreaOrTextInput() const
+{
+ auto* frame = m_document->frame();
+ if (!frame)
+ return false;
+
+ auto* container = frame->selection().selection().start().containerNode();
+ if (!container)
+ return false;
+
+ auto* ancestor = container->shadowHost();
+ if (!ancestor)
+ return false;
+
+ return is<HTMLTextAreaElement>(*ancestor) || (is<HTMLInputElement>(*ancestor) && downcast<HTMLInputElement>(*ancestor).isText());
+}
+
void EditCommand::setStartingSelection(const VisibleSelection& s)
{
for (EditCommand* cmd = this; ; cmd = cmd->m_parent) {
- if (EditCommandComposition* composition = compositionIfPossible(cmd)) {
- ASSERT(cmd->isTopLevelCommand());
+ if (auto* composition = compositionIfPossible(cmd))
composition->setStartingSelection(s);
- }
cmd->m_startingSelection = s;
if (!cmd->m_parent || cmd->m_parent->isFirstCommand(cmd))
break;
@@ -93,10 +192,8 @@ void EditCommand::setStartingSelection(const VisibleSelection& s)
void EditCommand::setEndingSelection(const VisibleSelection &s)
{
for (EditCommand* cmd = this; cmd; cmd = cmd->m_parent) {
- if (EditCommandComposition* composition = compositionIfPossible(cmd)) {
- ASSERT(cmd->isTopLevelCommand());
+ if (auto* composition = compositionIfPossible(cmd))
composition->setEndingSelection(s);
- }
cmd->m_endingSelection = s;
}
}
@@ -104,7 +201,6 @@ void EditCommand::setEndingSelection(const VisibleSelection &s)
void EditCommand::setParent(CompositeEditCommand* parent)
{
ASSERT((parent && !m_parent) || (!parent && m_parent));
- ASSERT(!parent || !isCompositeEditCommand() || !toCompositeEditCommand(this)->composition());
m_parent = parent;
if (parent) {
m_startingSelection = parent->m_endingSelection;
@@ -112,6 +208,31 @@ void EditCommand::setParent(CompositeEditCommand* parent)
}
}
+void EditCommand::postTextStateChangeNotification(AXTextEditType type, const String& text)
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ postTextStateChangeNotification(type, text, frame().selection().selection().start());
+}
+
+void EditCommand::postTextStateChangeNotification(AXTextEditType type, const String& text, const VisiblePosition& position)
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ if (!text.length())
+ return;
+ auto* cache = document().existingAXObjectCache();
+ if (!cache)
+ return;
+ auto* node = highestEditableRoot(position.deepEquivalent(), HasEditableAXRole);
+ cache->postTextStateChangeNotification(node, type, text, position);
+}
+
+SimpleEditCommand::SimpleEditCommand(Document& document, EditAction editingAction)
+ : EditCommand(document, editingAction)
+{
+}
+
void SimpleEditCommand::doReapply()
{
doApply();
@@ -120,7 +241,7 @@ void SimpleEditCommand::doReapply()
#ifndef NDEBUG
void SimpleEditCommand::addNodeAndDescendants(Node* startNode, HashSet<Node*>& nodes)
{
- for (Node* node = startNode; node; node = NodeTraversal::next(node, startNode))
+ for (Node* node = startNode; node; node = NodeTraversal::next(*node, startNode))
nodes.add(node);
}
#endif
diff --git a/Source/WebCore/editing/EditCommand.h b/Source/WebCore/editing/EditCommand.h
index c563e226b..a054589c2 100644
--- a/Source/WebCore/editing/EditCommand.h
+++ b/Source/WebCore/editing/EditCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,9 +23,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef EditCommand_h
-#define EditCommand_h
+#pragma once
+#include "AXTextStateChangeIntent.h"
#include "EditAction.h"
#include "VisibleSelection.h"
@@ -40,6 +40,8 @@ class Document;
class Element;
class Frame;
+String inputTypeNameForEditingAction(EditAction);
+
class EditCommand : public RefCounted<EditCommand> {
public:
virtual ~EditCommand();
@@ -51,10 +53,7 @@ public:
const VisibleSelection& startingSelection() const { return m_startingSelection; }
const VisibleSelection& endingSelection() const { return m_endingSelection; }
-#if PLATFORM(IOS)
- virtual bool isInsertTextCommand() const { return false; }
-#endif
-
+ virtual bool isInsertTextCommand() const { return false; }
virtual bool isSimpleEditCommand() const { return false; }
virtual bool isCompositeEditCommand() const { return false; }
virtual bool isEditCommandComposition() const { return false; }
@@ -63,20 +62,28 @@ public:
virtual void doApply() = 0;
protected:
- explicit EditCommand(Document&);
+ explicit EditCommand(Document&, EditAction = EditActionUnspecified);
EditCommand(Document&, const VisibleSelection&, const VisibleSelection&);
+ const Frame& frame() const;
Frame& frame();
- Document& document() { return m_document.get(); }
+ const Document& document() const { return m_document; }
+ Document& document() { return m_document; }
CompositeEditCommand* parent() const { return m_parent; }
void setStartingSelection(const VisibleSelection&);
- void setEndingSelection(const VisibleSelection&);
+ WEBCORE_EXPORT void setEndingSelection(const VisibleSelection&);
+
+ bool isEditingTextAreaOrTextInput() const;
+
+ void postTextStateChangeNotification(AXTextEditType, const String&);
+ void postTextStateChangeNotification(AXTextEditType, const String&, const VisiblePosition&);
private:
Ref<Document> m_document;
VisibleSelection m_startingSelection;
VisibleSelection m_endingSelection;
- CompositeEditCommand* m_parent;
+ CompositeEditCommand* m_parent { nullptr };
+ EditAction m_editingAction { EditActionUnspecified };
};
enum ShouldAssumeContentIsAlwaysEditable {
@@ -94,14 +101,14 @@ public:
#endif
protected:
- explicit SimpleEditCommand(Document& document) : EditCommand(document) { }
+ explicit SimpleEditCommand(Document&, EditAction = EditActionUnspecified);
#ifndef NDEBUG
void addNodeAndDescendants(Node*, HashSet<Node*>&);
#endif
private:
- virtual bool isSimpleEditCommand() const override { return true; }
+ bool isSimpleEditCommand() const override { return true; }
};
inline SimpleEditCommand* toSimpleEditCommand(EditCommand* command)
@@ -112,5 +119,3 @@ inline SimpleEditCommand* toSimpleEditCommand(EditCommand* command)
}
} // namespace WebCore
-
-#endif // EditCommand_h
diff --git a/Source/WebCore/editing/EditingAllInOne.cpp b/Source/WebCore/editing/EditingAllInOne.cpp
new file mode 100644
index 000000000..781bf5867
--- /dev/null
+++ b/Source/WebCore/editing/EditingAllInOne.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+// 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>
+#include <BreakBlockquoteCommand.cpp>
+#include <CompositeEditCommand.cpp>
+#include <CreateLinkCommand.cpp>
+#include <DeleteFromTextNodeCommand.cpp>
+#include <DeleteSelectionCommand.cpp>
+#include <DictationAlternative.cpp>
+#include <DictationCommand.cpp>
+#include <EditCommand.cpp>
+#include <EditingStyle.cpp>
+#include <Editor.cpp>
+#include <EditorCommand.cpp>
+#include <FormatBlockCommand.cpp>
+#include <FrameSelection.cpp>
+#include <HTMLInterchange.cpp>
+#include <IndentOutdentCommand.cpp>
+#include <InsertIntoTextNodeCommand.cpp>
+#include <InsertLineBreakCommand.cpp>
+#include <InsertListCommand.cpp>
+#include <InsertNodeBeforeCommand.cpp>
+#include <InsertParagraphSeparatorCommand.cpp>
+#include <InsertTextCommand.cpp>
+#include <MarkupAccumulator.cpp>
+#include <MergeIdenticalElementsCommand.cpp>
+#include <ModifySelectionListLevel.cpp>
+#include <MoveSelectionCommand.cpp>
+#include <RemoveCSSPropertyCommand.cpp>
+#include <RemoveFormatCommand.cpp>
+#include <RemoveNodeCommand.cpp>
+#include <RemoveNodePreservingChildrenCommand.cpp>
+#include <RenderedPosition.cpp>
+#include <ReplaceNodeWithSpanCommand.cpp>
+#include <ReplaceRangeWithTextCommand.cpp>
+#include <ReplaceSelectionCommand.cpp>
+#include <SetNodeAttributeCommand.cpp>
+#include <SetSelectionCommand.cpp>
+#include <SimplifyMarkupCommand.cpp>
+#include <SmartReplace.cpp>
+#if USE(CF)
+#include <SmartReplaceCF.cpp>
+#endif
+#include <SpellingCorrectionCommand.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>
+#include <VisiblePosition.cpp>
+#include <VisibleSelection.cpp>
+#include <VisibleUnits.cpp>
+#include <WrapContentsInDummySpanCommand.cpp>
+#include <htmlediting.cpp>
+#include <markup.cpp>
diff --git a/Source/WebCore/editing/EditingBehavior.h b/Source/WebCore/editing/EditingBehavior.h
index cff6caba8..27fb1f076 100644
--- a/Source/WebCore/editing/EditingBehavior.h
+++ b/Source/WebCore/editing/EditingBehavior.h
@@ -18,8 +18,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef EditingBehavior_h
-#define EditingBehavior_h
+#pragma once
#include "EditingBehaviorTypes.h"
@@ -46,22 +45,22 @@ public:
// On Windows, selections should always be considered as directional, regardless if it is
// mouse-based or keyboard-based.
- bool shouldConsiderSelectionAsDirectional() const { return m_type != EditingMacBehavior; }
+ bool shouldConsiderSelectionAsDirectional() const { return m_type != EditingMacBehavior && m_type != EditingIOSBehavior; }
// On Mac, when revealing a selection (for example as a result of a Find operation on the Browser),
// content should be scrolled such that the selection gets certer aligned.
- bool shouldCenterAlignWhenSelectionIsRevealed() const { return m_type == EditingMacBehavior; }
+ bool shouldCenterAlignWhenSelectionIsRevealed() const { return m_type == EditingMacBehavior || m_type == EditingIOSBehavior; }
// On Mac, style is considered present when present at the beginning of selection. On other platforms,
// style has to be present throughout the selection.
- bool shouldToggleStyleBasedOnStartOfSelection() const { return m_type == EditingMacBehavior; }
+ bool shouldToggleStyleBasedOnStartOfSelection() const { return m_type == EditingMacBehavior || m_type == EditingIOSBehavior; }
// Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the base
// in place and moving the extent. Matches NSTextView.
- bool shouldAlwaysGrowSelectionWhenExtendingToBoundary() const { return m_type == EditingMacBehavior; }
+ bool shouldAlwaysGrowSelectionWhenExtendingToBoundary() const { return m_type == EditingMacBehavior || m_type == EditingIOSBehavior; }
// On Mac, when processing a contextual click, the object being clicked upon should be selected.
- bool shouldSelectOnContextualMenuClick() const { return m_type == EditingMacBehavior; }
+ bool shouldSelectOnContextualMenuClick() const { return m_type == EditingMacBehavior || m_type == EditingIOSBehavior; }
// On Linux, should be able to get and insert spelling suggestions without selecting the misspelled word.
bool shouldAllowSpellingSuggestionsWithoutSelection() const
@@ -78,17 +77,24 @@ public:
// On Mac, selecting backwards by word/line from the middle of a word/line, and then going
// forward leaves the caret back in the middle with no selection, instead of directly selecting
// to the other end of the line/word (Unix/Windows behavior).
- bool shouldExtendSelectionByWordOrLineAcrossCaret() const { return m_type != EditingMacBehavior; }
+ bool shouldExtendSelectionByWordOrLineAcrossCaret() const { return m_type != EditingMacBehavior && m_type != EditingIOSBehavior; }
// 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; }
+ // On iOS the last entered character in a secure filed is shown momentarily, removing and adding back the
+ // space when deleting password cause space been showed insecurely.
+ bool shouldRebalanceWhiteSpacesInSecureField() const { return m_type != EditingIOSBehavior; }
+
+ bool shouldSelectBasedOnDictionaryLookup() const { return m_type == EditingMacBehavior; }
+
+ // Linux and Windows always extend selections from the extent endpoint.
+ bool shouldAlwaysExtendSelectionFromExtentEndpoint() const { return m_type != EditingMacBehavior && m_type != EditingIOSBehavior; }
+
private:
EditingBehaviorType m_type;
};
} // namespace WebCore
-
-#endif // EditingBehavior_h
diff --git a/Source/WebCore/editing/EditingBehaviorTypes.h b/Source/WebCore/editing/EditingBehaviorTypes.h
index 11345da5d..63ff1d06f 100644
--- a/Source/WebCore/editing/EditingBehaviorTypes.h
+++ b/Source/WebCore/editing/EditingBehaviorTypes.h
@@ -18,8 +18,7 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef EditingBehaviorTypes_h
-#define EditingBehaviorTypes_h
+#pragma once
namespace WebCore {
@@ -39,9 +38,8 @@ namespace WebCore {
enum EditingBehaviorType {
EditingMacBehavior,
EditingWindowsBehavior,
- EditingUnixBehavior
+ EditingUnixBehavior,
+ EditingIOSBehavior
};
} // WebCore namespace
-
-#endif // EditingBehaviorTypes_h
diff --git a/Source/WebCore/editing/EditingBoundary.h b/Source/WebCore/editing/EditingBoundary.h
index ece7e036d..4947c610e 100644
--- a/Source/WebCore/editing/EditingBoundary.h
+++ b/Source/WebCore/editing/EditingBoundary.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef EditingBoundary_h
-#define EditingBoundary_h
+#pragma once
namespace WebCore {
@@ -39,6 +38,4 @@ enum EditableType {
HasEditableAXRole
};
-}
-
-#endif // EditingBoundary_h
+} // namespace WebCore
diff --git a/Source/WebCore/editing/EditingStyle.cpp b/Source/WebCore/editing/EditingStyle.cpp
index 364ff8054..be2f6fbbf 100644
--- a/Source/WebCore/editing/EditingStyle.cpp
+++ b/Source/WebCore/editing/EditingStyle.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, 2008, 2009, 2013 Apple Computer, Inc.
+ * Copyright (C) 2007, 2008, 2009, 2013 Apple Inc.
* Copyright (C) 2010, 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,7 @@
#include "HTMLFontElement.h"
#include "HTMLInterchange.h"
#include "HTMLNames.h"
+#include "HTMLSpanElement.h"
#include "Node.h"
#include "NodeTraversal.h"
#include "QualifiedName.h"
@@ -60,10 +61,9 @@ static const CSSPropertyID editingProperties[] = {
CSSPropertyFontFamily,
CSSPropertyFontSize,
CSSPropertyFontStyle,
- CSSPropertyFontVariant,
+ CSSPropertyFontVariantCaps,
CSSPropertyFontWeight,
CSSPropertyLetterSpacing,
- CSSPropertyLineHeight,
CSSPropertyOrphans,
CSSPropertyTextAlign,
CSSPropertyTextIndent,
@@ -71,13 +71,12 @@ static const CSSPropertyID editingProperties[] = {
CSSPropertyWhiteSpace,
CSSPropertyWidows,
CSSPropertyWordSpacing,
-#if PLATFORM(IOS)
+#if ENABLE(TOUCH_EVENTS)
CSSPropertyWebkitTapHighlightColor,
- CSSPropertyWebkitCompositionFillColor,
#endif
CSSPropertyWebkitTextDecorationsInEffect,
CSSPropertyWebkitTextFillColor,
-#if ENABLE(IOS_TEXT_AUTOSIZING)
+#if ENABLE(TEXT_AUTOSIZING)
CSSPropertyWebkitTextSizeAdjust,
#endif
CSSPropertyWebkitTextStrokeColor,
@@ -102,8 +101,8 @@ static PassRefPtr<MutableStyleProperties> copyEditingProperties(StyleDeclaration
static inline bool isEditingProperty(int id)
{
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(editingProperties); ++i) {
- if (editingProperties[i] == id)
+ for (auto& editingProperty : editingProperties) {
+ if (editingProperty == id)
return true;
}
return false;
@@ -129,59 +128,53 @@ static PassRefPtr<MutableStyleProperties> copyPropertiesFromComputedStyle(Node*
return copyPropertiesFromComputedStyle(computedStyle, propertiesToInclude);
}
-static PassRefPtr<CSSValue> extractPropertyValue(const StyleProperties* style, CSSPropertyID propertyID)
+static PassRefPtr<CSSValue> extractPropertyValue(const StyleProperties& style, CSSPropertyID propertyID)
{
- return style ? style->getPropertyCSSValue(propertyID) : PassRefPtr<CSSValue>();
+ return style.getPropertyCSSValue(propertyID);
}
-static PassRefPtr<CSSValue> extractPropertyValue(ComputedStyleExtractor* computedStyle, CSSPropertyID propertyID)
+static PassRefPtr<CSSValue> extractPropertyValue(ComputedStyleExtractor& computedStyle, CSSPropertyID propertyID)
{
- return computedStyle->propertyValue(propertyID);
+ return computedStyle.propertyValue(propertyID);
}
template<typename T>
-int identifierForStyleProperty(T* style, CSSPropertyID propertyID)
+int identifierForStyleProperty(T& style, CSSPropertyID propertyID)
{
RefPtr<CSSValue> value = extractPropertyValue(style, propertyID);
- if (!value || !value->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(value.get()))
return 0;
- return toCSSPrimitiveValue(value.get())->getValueID();
+ return downcast<CSSPrimitiveValue>(*value).valueID();
}
-template<typename T> PassRefPtr<MutableStyleProperties> getPropertiesNotIn(StyleProperties* styleWithRedundantProperties, T* baseStyle);
+template<typename T> PassRefPtr<MutableStyleProperties> getPropertiesNotIn(StyleProperties& styleWithRedundantProperties, T& baseStyle);
enum LegacyFontSizeMode { AlwaysUseLegacyFontSize, UseLegacyFontSizeOnlyIfPixelValuesMatch };
static int legacyFontSizeFromCSSValue(Document*, CSSPrimitiveValue*, bool shouldUseFixedFontDefaultSize, LegacyFontSizeMode);
-static bool isTransparentColorValue(CSSValue*);
static bool hasTransparentBackgroundColor(StyleProperties*);
-static PassRefPtr<CSSValue> backgroundColorInEffect(Node*);
+static RefPtr<CSSValue> backgroundColorInEffect(Node*);
class HTMLElementEquivalent {
WTF_MAKE_FAST_ALLOCATED;
public:
- static PassOwnPtr<HTMLElementEquivalent> create(CSSPropertyID propertyID, CSSValueID primitiveValue, const QualifiedName& tagName)
- {
- return adoptPtr(new HTMLElementEquivalent(propertyID, primitiveValue, tagName));
- }
+ HTMLElementEquivalent(CSSPropertyID, CSSValueID primitiveValue, const QualifiedName& tagName);
virtual ~HTMLElementEquivalent() { }
- virtual bool matches(const Element* element) const { return !m_tagName || element->hasTagName(*m_tagName); }
+ virtual bool matches(const Element& element) const { return !m_tagName || element.hasTagName(*m_tagName); }
virtual bool hasAttribute() const { return false; }
- virtual bool propertyExistsInStyle(const StyleProperties* style) const { return style->getPropertyCSSValue(m_propertyID); }
- virtual bool valueIsPresentInStyle(Element*, StyleProperties*) const;
+ virtual bool propertyExistsInStyle(const EditingStyle& style) const { return style.m_mutableStyle && style.m_mutableStyle->getPropertyCSSValue(m_propertyID); }
+ virtual bool valueIsPresentInStyle(Element&, const EditingStyle&) const;
virtual void addToStyle(Element*, EditingStyle*) const;
protected:
HTMLElementEquivalent(CSSPropertyID);
HTMLElementEquivalent(CSSPropertyID, const QualifiedName& tagName);
- HTMLElementEquivalent(CSSPropertyID, CSSValueID primitiveValue, const QualifiedName& tagName);
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.
+ const QualifiedName* m_tagName { nullptr }; // We can store a pointer because HTML tag names are const global.
};
HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id)
: m_propertyID(id)
- , m_tagName(0)
{
}
@@ -199,10 +192,10 @@ HTMLElementEquivalent::HTMLElementEquivalent(CSSPropertyID id, CSSValueID primit
ASSERT(primitiveValue != CSSValueInvalid);
}
-bool HTMLElementEquivalent::valueIsPresentInStyle(Element* element, StyleProperties* style) const
+bool HTMLElementEquivalent::valueIsPresentInStyle(Element& element, const EditingStyle& style) const
{
- RefPtr<CSSValue> value = style->getPropertyCSSValue(m_propertyID);
- return matches(element) && value && value->isPrimitiveValue() && toCSSPrimitiveValue(value.get())->getValueID() == m_primitiveValue->getValueID();
+ RefPtr<CSSValue> value = style.m_mutableStyle->getPropertyCSSValue(m_propertyID);
+ return matches(element) && is<CSSPrimitiveValue>(value.get()) && downcast<CSSPrimitiveValue>(*value).valueID() == m_primitiveValue->valueID();
}
void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const
@@ -212,57 +205,60 @@ void HTMLElementEquivalent::addToStyle(Element*, EditingStyle* style) const
class HTMLTextDecorationEquivalent : public HTMLElementEquivalent {
public:
- static PassOwnPtr<HTMLElementEquivalent> create(CSSValueID primitiveValue, const QualifiedName& tagName)
+ HTMLTextDecorationEquivalent(CSSValueID primitiveValue, const QualifiedName& tagName)
+ : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName)
+ , m_isUnderline(primitiveValue == CSSValueUnderline)
{
- return adoptPtr(new HTMLTextDecorationEquivalent(primitiveValue, tagName));
}
- virtual bool propertyExistsInStyle(const StyleProperties*) const;
- virtual bool valueIsPresentInStyle(Element*, StyleProperties*) const;
-
-private:
- HTMLTextDecorationEquivalent(CSSValueID primitiveValue, const QualifiedName& tagName);
-};
-HTMLTextDecorationEquivalent::HTMLTextDecorationEquivalent(CSSValueID primitiveValue, const QualifiedName& tagName)
- : HTMLElementEquivalent(CSSPropertyTextDecoration, primitiveValue, tagName)
- // m_propertyID is used in HTMLElementEquivalent::addToStyle
-{
-}
+ bool propertyExistsInStyle(const EditingStyle& style) const override
+ {
+ if (changeInStyle(style) != TextDecorationChange::None)
+ return true;
-bool HTMLTextDecorationEquivalent::propertyExistsInStyle(const StyleProperties* style) const
-{
- return style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect) || style->getPropertyCSSValue(CSSPropertyTextDecoration);
-}
+ if (!style.m_mutableStyle)
+ return false;
-bool HTMLTextDecorationEquivalent::valueIsPresentInStyle(Element* element, StyleProperties* style) const
-{
- RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
- if (!styleValue)
- styleValue = style->getPropertyCSSValue(CSSPropertyTextDecoration);
- return matches(element) && styleValue && styleValue->isValueList() && toCSSValueList(styleValue.get())->hasValue(m_primitiveValue.get());
-}
+ auto& mutableStyle = *style.m_mutableStyle;
+ return mutableStyle.getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect)
+ || mutableStyle.getPropertyCSSValue(CSSPropertyTextDecoration);
+ }
-class HTMLAttributeEquivalent : public HTMLElementEquivalent {
-public:
- static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& tagName, const QualifiedName& attrName)
+ bool valueIsPresentInStyle(Element& element, const EditingStyle& style) const override
{
- return adoptPtr(new HTMLAttributeEquivalent(propertyID, tagName, attrName));
+ if (!matches(element))
+ return false;
+ auto change = changeInStyle(style);
+ if (change != TextDecorationChange::None)
+ return change == TextDecorationChange::Add;
+ RefPtr<CSSValue> styleValue = style.m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
+ if (!styleValue)
+ styleValue = style.m_mutableStyle->getPropertyCSSValue(CSSPropertyTextDecoration);
+ return is<CSSValueList>(styleValue.get()) && downcast<CSSValueList>(*styleValue).hasValue(m_primitiveValue.get());
}
- static PassOwnPtr<HTMLAttributeEquivalent> create(CSSPropertyID propertyID, const QualifiedName& attrName)
+
+private:
+ TextDecorationChange changeInStyle(const EditingStyle& style) const
{
- return adoptPtr(new HTMLAttributeEquivalent(propertyID, attrName));
+ return m_isUnderline ? style.underlineChange() : style.strikeThroughChange();
}
- bool matches(const Element* elem) const { return HTMLElementEquivalent::matches(elem) && elem->hasAttribute(m_attrName); }
- virtual bool hasAttribute() const { return true; }
- virtual bool valueIsPresentInStyle(Element*, StyleProperties*) const;
- virtual void addToStyle(Element*, EditingStyle*) const;
+ bool m_isUnderline;
+};
+
+class HTMLAttributeEquivalent : public HTMLElementEquivalent {
+public:
+ HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName);
+ HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName);
+
+ bool matches(const Element& element) const override { return HTMLElementEquivalent::matches(element) && element.hasAttribute(m_attrName); }
+ bool hasAttribute() const override { return true; }
+ bool valueIsPresentInStyle(Element&, const EditingStyle&) const override;
+ void addToStyle(Element*, EditingStyle*) const override;
virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
inline const QualifiedName& attributeName() const { return m_attrName; }
protected:
- HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& tagName, const QualifiedName& attrName);
- HTMLAttributeEquivalent(CSSPropertyID, const QualifiedName& attrName);
const QualifiedName& m_attrName; // We can store a reference because HTML attribute names are const global.
};
@@ -278,10 +274,10 @@ HTMLAttributeEquivalent::HTMLAttributeEquivalent(CSSPropertyID id, const Qualifi
{
}
-bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element* element, StyleProperties* style) const
+bool HTMLAttributeEquivalent::valueIsPresentInStyle(Element& element, const EditingStyle& style) const
{
- RefPtr<CSSValue> value = attributeValueAsCSSValue(element);
- RefPtr<CSSValue> styleValue = style->getPropertyCSSValue(m_propertyID);
+ RefPtr<CSSValue> value = attributeValueAsCSSValue(&element);
+ RefPtr<CSSValue> styleValue = style.m_mutableStyle->getPropertyCSSValue(m_propertyID);
return compareCSSValuePtr(value, styleValue);
}
@@ -295,25 +291,21 @@ void HTMLAttributeEquivalent::addToStyle(Element* element, EditingStyle* style)
PassRefPtr<CSSValue> HTMLAttributeEquivalent::attributeValueAsCSSValue(Element* element) const
{
ASSERT(element);
- if (!element->hasAttribute(m_attrName))
- return 0;
+ const AtomicString& value = element->getAttribute(m_attrName);
+ if (value.isNull())
+ return nullptr;
RefPtr<MutableStyleProperties> dummyStyle;
dummyStyle = MutableStyleProperties::create();
- dummyStyle->setProperty(m_propertyID, element->getAttribute(m_attrName));
+ dummyStyle->setProperty(m_propertyID, value);
return dummyStyle->getPropertyCSSValue(m_propertyID);
}
class HTMLFontSizeEquivalent : public HTMLAttributeEquivalent {
public:
- static PassOwnPtr<HTMLFontSizeEquivalent> create()
- {
- return adoptPtr(new HTMLFontSizeEquivalent());
- }
- virtual PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const;
-
-private:
HTMLFontSizeEquivalent();
+
+ PassRefPtr<CSSValue> attributeValueAsCSSValue(Element*) const override;
};
HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
@@ -324,11 +316,12 @@ HTMLFontSizeEquivalent::HTMLFontSizeEquivalent()
PassRefPtr<CSSValue> HTMLFontSizeEquivalent::attributeValueAsCSSValue(Element* element) const
{
ASSERT(element);
- if (!element->hasAttribute(m_attrName))
- return 0;
+ const AtomicString& value = element->getAttribute(m_attrName);
+ if (value.isNull())
+ return nullptr;
CSSValueID size;
- if (!HTMLFontElement::cssValueFromFontSizeNumber(element->getAttribute(m_attrName), size))
- return 0;
+ if (!HTMLFontElement::cssValueFromFontSizeNumber(value, size))
+ return nullptr;
return CSSPrimitiveValue::createIdentifier(size);
}
@@ -336,27 +329,33 @@ float EditingStyle::NoFontDelta = 0.0f;
EditingStyle::EditingStyle()
: m_shouldUseFixedDefaultFontSize(false)
- , m_fontSizeDelta(NoFontDelta)
+ , m_underlineChange(static_cast<unsigned>(TextDecorationChange::None))
+ , m_strikeThroughChange(static_cast<unsigned>(TextDecorationChange::None))
{
}
EditingStyle::EditingStyle(Node* node, PropertiesToInclude propertiesToInclude)
- : m_shouldUseFixedDefaultFontSize(false)
- , m_fontSizeDelta(NoFontDelta)
+ : EditingStyle()
{
init(node, propertiesToInclude);
}
EditingStyle::EditingStyle(const Position& position, PropertiesToInclude propertiesToInclude)
- : m_shouldUseFixedDefaultFontSize(false)
- , m_fontSizeDelta(NoFontDelta)
+ : EditingStyle()
{
init(position.deprecatedNode(), propertiesToInclude);
}
+EditingStyle::EditingStyle(const CSSStyleDeclaration* style)
+ : EditingStyle()
+{
+ if (style)
+ m_mutableStyle = style->copyProperties();
+ extractFontSizeDelta();
+}
+
EditingStyle::EditingStyle(const StyleProperties* style)
- : m_shouldUseFixedDefaultFontSize(false)
- , m_fontSizeDelta(NoFontDelta)
+ : EditingStyle()
{
if (style)
m_mutableStyle = style->mutableCopy();
@@ -364,46 +363,51 @@ EditingStyle::EditingStyle(const StyleProperties* style)
}
EditingStyle::EditingStyle(CSSPropertyID propertyID, const String& value)
- : m_mutableStyle(0)
- , m_shouldUseFixedDefaultFontSize(false)
- , m_fontSizeDelta(NoFontDelta)
+ : EditingStyle()
{
setProperty(propertyID, value);
+ extractFontSizeDelta();
+}
+
+EditingStyle::EditingStyle(CSSPropertyID propertyID, CSSValueID value)
+ : EditingStyle()
+{
+ m_mutableStyle = MutableStyleProperties::create();
+ m_mutableStyle->setProperty(propertyID, value);
+ extractFontSizeDelta();
}
EditingStyle::~EditingStyle()
{
}
-static RGBA32 cssValueToRGBA(CSSValue* colorValue)
+static Color cssValueToColor(CSSValue* colorValue)
{
- if (!colorValue || !colorValue->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(colorValue))
return Color::transparent;
- CSSPrimitiveValue* primitiveColor = toCSSPrimitiveValue(colorValue);
- if (primitiveColor->isRGBColor())
- return primitiveColor->getRGBA32Value();
+ CSSPrimitiveValue& primitiveColor = downcast<CSSPrimitiveValue>(*colorValue);
+ if (primitiveColor.isRGBColor())
+ return primitiveColor.color();
- RGBA32 rgba = 0;
- CSSParser::parseColor(rgba, colorValue->cssText());
- return rgba;
+ return CSSParser::parseColor(colorValue->cssText());
}
template<typename T>
-static inline RGBA32 textColorFromStyle(T* style)
+static inline Color textColorFromStyle(T& style)
{
- return cssValueToRGBA(extractPropertyValue(style, CSSPropertyColor).get());
+ return cssValueToColor(extractPropertyValue(style, CSSPropertyColor).get());
}
template<typename T>
-static inline RGBA32 backgroundColorFromStyle(T* style)
+static inline Color backgroundColorFromStyle(T& style)
{
- return cssValueToRGBA(extractPropertyValue(style, CSSPropertyBackgroundColor).get());
+ return cssValueToColor(extractPropertyValue(style, CSSPropertyBackgroundColor).get());
}
-static inline RGBA32 rgbaBackgroundColorInEffect(Node* node)
+static inline Color rgbaBackgroundColorInEffect(Node* node)
{
- return cssValueToRGBA(backgroundColorInEffect(node).get());
+ return cssValueToColor(backgroundColorInEffect(node).get());
}
static int textAlignResolvingStartAndEnd(int textAlign, int direction)
@@ -429,7 +433,7 @@ static int textAlignResolvingStartAndEnd(int textAlign, int direction)
}
template<typename T>
-static int textAlignResolvingStartAndEnd(T* style)
+static int textAlignResolvingStartAndEnd(T& style)
{
return textAlignResolvingStartAndEnd(identifierForStyleProperty(style, CSSPropertyTextAlign), identifierForStyleProperty(style, CSSPropertyDirection));
}
@@ -455,7 +459,7 @@ void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
}
if (node && node->computedStyle()) {
- RenderStyle* renderStyle = node->computedStyle();
+ auto* renderStyle = node->computedStyle();
removeTextFillAndStrokeColorsIfNeeded(renderStyle);
if (renderStyle->fontDescription().keywordSize())
m_mutableStyle->setProperty(CSSPropertyFontSize, computedStyleAtPosition.getFontSizeCSSValuePreferringKeyword()->cssText());
@@ -465,7 +469,7 @@ void EditingStyle::init(Node* node, PropertiesToInclude propertiesToInclude)
extractFontSizeDelta();
}
-void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(RenderStyle* renderStyle)
+void EditingStyle::removeTextFillAndStrokeColorsIfNeeded(const RenderStyle* renderStyle)
{
// If a node's text fill color is invalid, then its children use
// their font-color as their text fill color (they don't
@@ -497,23 +501,46 @@ void EditingStyle::extractFontSizeDelta()
// Get the adjustment amount out of the style.
RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta);
- if (!value || !value->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(value.get()))
return;
- CSSPrimitiveValue* primitiveValue = toCSSPrimitiveValue(value.get());
+ CSSPrimitiveValue& primitiveValue = downcast<CSSPrimitiveValue>(*value);
// Only PX handled now. If we handle more types in the future, perhaps
// a switch statement here would be more appropriate.
- if (!primitiveValue->isPx())
+ if (!primitiveValue.isPx())
return;
- m_fontSizeDelta = primitiveValue->getFloatValue();
+ m_fontSizeDelta = primitiveValue.floatValue();
m_mutableStyle->removeProperty(CSSPropertyWebkitFontSizeDelta);
}
bool EditingStyle::isEmpty() const
{
- return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta;
+ return (!m_mutableStyle || m_mutableStyle->isEmpty()) && m_fontSizeDelta == NoFontDelta
+ && underlineChange() == TextDecorationChange::None && strikeThroughChange() == TextDecorationChange::None;
+}
+
+Ref<MutableStyleProperties> EditingStyle::styleWithResolvedTextDecorations() const
+{
+ bool hasTextDecorationChanges = underlineChange() != TextDecorationChange::None || strikeThroughChange() != TextDecorationChange::None;
+ if (m_mutableStyle && !hasTextDecorationChanges)
+ return *m_mutableStyle;
+
+ Ref<MutableStyleProperties> style = m_mutableStyle ? m_mutableStyle->mutableCopy() : MutableStyleProperties::create();
+
+ Ref<CSSValueList> valueList = CSSValueList::createSpaceSeparated();
+ if (underlineChange() == TextDecorationChange::Add)
+ valueList->append(CSSValuePool::singleton().createIdentifierValue(CSSValueUnderline));
+ if (strikeThroughChange() == TextDecorationChange::Add)
+ valueList->append(CSSValuePool::singleton().createIdentifierValue(CSSValueLineThrough));
+
+ if (valueList->length())
+ style->setProperty(CSSPropertyTextDecoration, valueList.ptr());
+ else
+ style->setProperty(CSSPropertyTextDecoration, CSSValuePool::singleton().createIdentifierValue(CSSValueNone));
+
+ return style;
}
bool EditingStyle::textDirection(WritingDirection& writingDirection) const
@@ -522,16 +549,16 @@ bool EditingStyle::textDirection(WritingDirection& writingDirection) const
return false;
RefPtr<CSSValue> unicodeBidi = m_mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi);
- if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(unicodeBidi.get()))
return false;
- CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID();
+ CSSValueID unicodeBidiValue = downcast<CSSPrimitiveValue>(*unicodeBidi).valueID();
if (unicodeBidiValue == CSSValueEmbed) {
RefPtr<CSSValue> direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
- if (!direction || !direction->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(direction.get()))
return false;
- writingDirection = toCSSPrimitiveValue(direction.get())->getValueID() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
+ writingDirection = downcast<CSSPrimitiveValue>(*direction).valueID() == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
return true;
}
@@ -558,11 +585,62 @@ void EditingStyle::overrideWithStyle(const StyleProperties* style)
return mergeStyle(style, OverrideValues);
}
+static void applyTextDecorationChangeToValueList(CSSValueList& valueList, TextDecorationChange change, Ref<CSSPrimitiveValue>&& value)
+{
+ switch (change) {
+ case TextDecorationChange::None:
+ break;
+ case TextDecorationChange::Add:
+ valueList.append(WTFMove(value));
+ break;
+ case TextDecorationChange::Remove:
+ valueList.removeAll(&value.get());
+ break;
+ }
+}
+
+void EditingStyle::overrideTypingStyleAt(const EditingStyle& style, const Position& position)
+{
+ mergeStyle(style.m_mutableStyle.get(), OverrideValues);
+
+ m_fontSizeDelta += style.m_fontSizeDelta;
+
+ prepareToApplyAt(position, EditingStyle::PreserveWritingDirection);
+
+ auto underlineChange = style.underlineChange();
+ auto strikeThroughChange = style.strikeThroughChange();
+ if (underlineChange == TextDecorationChange::None && strikeThroughChange == TextDecorationChange::None)
+ return;
+
+ if (!m_mutableStyle)
+ m_mutableStyle = MutableStyleProperties::create();
+
+ auto& cssValuePool = CSSValuePool::singleton();
+ Ref<CSSPrimitiveValue> underline = cssValuePool.createIdentifierValue(CSSValueUnderline);
+ Ref<CSSPrimitiveValue> lineThrough = cssValuePool.createIdentifierValue(CSSValueLineThrough);
+ RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect);
+ RefPtr<CSSValueList> valueList;
+ if (value && value->isValueList()) {
+ valueList = downcast<CSSValueList>(*value).copy();
+ applyTextDecorationChangeToValueList(*valueList, underlineChange, WTFMove(underline));
+ applyTextDecorationChangeToValueList(*valueList, strikeThroughChange, WTFMove(lineThrough));
+ } else {
+ valueList = CSSValueList::createSpaceSeparated();
+ if (underlineChange == TextDecorationChange::Add)
+ valueList->append(WTFMove(underline));
+ if (strikeThroughChange == TextDecorationChange::Add)
+ valueList->append(WTFMove(lineThrough));
+ }
+ m_mutableStyle->setProperty(CSSPropertyWebkitTextDecorationsInEffect, valueList.get());
+}
+
void EditingStyle::clear()
{
- m_mutableStyle.clear();
+ m_mutableStyle = nullptr;
m_shouldUseFixedDefaultFontSize = false;
m_fontSizeDelta = NoFontDelta;
+ setUnderlineChange(TextDecorationChange::None);
+ setStrikeThroughChange(TextDecorationChange::None);
}
PassRefPtr<EditingStyle> EditingStyle::copy() const
@@ -571,6 +649,8 @@ PassRefPtr<EditingStyle> EditingStyle::copy() const
if (m_mutableStyle)
copy->m_mutableStyle = m_mutableStyle->mutableCopy();
copy->m_shouldUseFixedDefaultFontSize = m_shouldUseFixedDefaultFontSize;
+ copy->m_underlineChange = m_underlineChange;
+ copy->m_strikeThroughChange = m_strikeThroughChange;
copy->m_fontSizeDelta = m_fontSizeDelta;
return copy;
}
@@ -615,8 +695,8 @@ void EditingStyle::removeStyleAddedByNode(Node* node)
return;
RefPtr<MutableStyleProperties> parentStyle = copyPropertiesFromComputedStyle(node->parentNode(), EditingPropertiesInEffect);
RefPtr<MutableStyleProperties> nodeStyle = copyPropertiesFromComputedStyle(node, EditingPropertiesInEffect);
- nodeStyle->removeEquivalentProperties(parentStyle.get());
- m_mutableStyle->removeEquivalentProperties(nodeStyle.get());
+ removeEquivalentProperties(*parentStyle);
+ removeEquivalentProperties(*nodeStyle);
}
void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
@@ -625,12 +705,13 @@ void EditingStyle::removeStyleConflictingWithStyleOfNode(Node* node)
return;
RefPtr<MutableStyleProperties> parentStyle = copyPropertiesFromComputedStyle(node->parentNode(), EditingPropertiesInEffect);
- RefPtr<MutableStyleProperties> nodeStyle = copyPropertiesFromComputedStyle(node, EditingPropertiesInEffect);
- nodeStyle->removeEquivalentProperties(parentStyle.get());
+ RefPtr<EditingStyle> nodeStyle = EditingStyle::create(node, EditingPropertiesInEffect);
+ nodeStyle->removeEquivalentProperties(*parentStyle);
- unsigned propertyCount = nodeStyle->propertyCount();
+ MutableStyleProperties* style = nodeStyle->style();
+ unsigned propertyCount = style->propertyCount();
for (unsigned i = 0; i < propertyCount; ++i)
- m_mutableStyle->removeProperty(nodeStyle->propertyAt(i).id());
+ m_mutableStyle->removeProperty(style->propertyAt(i).id());
}
void EditingStyle::collapseTextDecorationProperties()
@@ -662,13 +743,16 @@ TriState EditingStyle::triStateOfStyle(EditingStyle* style) const
{
if (!style || !style->m_mutableStyle)
return FalseTriState;
- return triStateOfStyle(style->m_mutableStyle.get(), DoNotIgnoreTextOnlyProperties);
+ return triStateOfStyle(*style->m_mutableStyle, DoNotIgnoreTextOnlyProperties);
}
template<typename T>
-TriState EditingStyle::triStateOfStyle(T* styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
+TriState EditingStyle::triStateOfStyle(T& styleToCompare, ShouldIgnoreTextOnlyProperties shouldIgnoreTextOnlyProperties) const
{
- RefPtr<MutableStyleProperties> difference = getPropertiesNotIn(m_mutableStyle.get(), styleToCompare);
+ if (!m_mutableStyle)
+ return TrueTriState;
+
+ RefPtr<MutableStyleProperties> difference = getPropertiesNotIn(*m_mutableStyle, styleToCompare);
if (shouldIgnoreTextOnlyProperties == IgnoreTextOnlyProperties)
difference->removePropertiesInSet(textOnlyProperties, WTF_ARRAY_LENGTH(textOnlyProperties));
@@ -691,10 +775,10 @@ TriState EditingStyle::triStateOfStyle(const VisibleSelection& selection) const
TriState state = FalseTriState;
bool nodeIsStart = true;
- for (Node* node = selection.start().deprecatedNode(); node; node = NodeTraversal::next(node)) {
+ for (Node* node = selection.start().deprecatedNode(); node; node = NodeTraversal::next(*node)) {
if (node->renderer() && node->hasEditableStyle()) {
ComputedStyleExtractor computedStyle(node);
- TriState nodeState = triStateOfStyle(&computedStyle, node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties);
+ TriState nodeState = triStateOfStyle(computedStyle, node->isTextNode() ? EditingStyle::DoNotIgnoreTextOnlyProperties : EditingStyle::IgnoreTextOnlyProperties);
if (nodeIsStart) {
state = nodeState;
nodeIsStart = false;
@@ -711,16 +795,67 @@ TriState EditingStyle::triStateOfStyle(const VisibleSelection& selection) const
return state;
}
-bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const
+static RefPtr<CSSValueList> textDecorationValueList(const StyleProperties& properties)
+{
+ RefPtr<CSSValue> value = properties.getPropertyCSSValue(CSSPropertyTextDecoration);
+ if (!is<CSSValueList>(value.get()))
+ return nullptr;
+ return downcast<CSSValueList>(value.get());
+}
+
+bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, RefPtr<MutableStyleProperties>* newInlineStylePtr, EditingStyle* extractedStyle) const
{
ASSERT(element);
- ASSERT(!conflictingProperties || conflictingProperties->isEmpty());
const StyleProperties* inlineStyle = element->inlineStyle();
- if (!m_mutableStyle || !inlineStyle)
+ if (!inlineStyle)
return false;
+ bool conflicts = false;
+ RefPtr<MutableStyleProperties> newInlineStyle;
+ if (newInlineStylePtr) {
+ newInlineStyle = inlineStyle->mutableCopy();
+ *newInlineStylePtr = newInlineStyle;
+ }
+
+ bool shouldRemoveUnderline = underlineChange() == TextDecorationChange::Remove;
+ bool shouldRemoveStrikeThrough = strikeThroughChange() == TextDecorationChange::Remove;
+ if (shouldRemoveUnderline || shouldRemoveStrikeThrough) {
+ if (RefPtr<CSSValueList> valueList = textDecorationValueList(*inlineStyle)) {
+ RefPtr<CSSValueList> newValueList = valueList->copy();
+ RefPtr<CSSValueList> extractedValueList = CSSValueList::createSpaceSeparated();
+
+ Ref<CSSPrimitiveValue> underline = CSSValuePool::singleton().createIdentifierValue(CSSValueUnderline);
+ if (shouldRemoveUnderline && valueList->hasValue(underline.ptr())) {
+ if (!newInlineStyle)
+ return true;
+ newValueList->removeAll(underline.ptr());
+ extractedValueList->append(WTFMove(underline));
+ }
- unsigned propertyCount = m_mutableStyle->propertyCount();
+ Ref<CSSPrimitiveValue> lineThrough = CSSValuePool::singleton().createIdentifierValue(CSSValueLineThrough);
+ if (shouldRemoveStrikeThrough && valueList->hasValue(lineThrough.ptr())) {
+ if (!newInlineStyle)
+ return true;
+ newValueList->removeAll(lineThrough.ptr());
+ extractedValueList->append(WTFMove(lineThrough));
+ }
+
+ if (extractedValueList->length()) {
+ conflicts = true;
+ if (newValueList->length())
+ newInlineStyle->setProperty(CSSPropertyTextDecoration, newValueList);
+ else
+ newInlineStyle->removeProperty(CSSPropertyTextDecoration);
+
+ if (extractedStyle) {
+ bool isImportant = inlineStyle->propertyIsImportant(CSSPropertyTextDecoration);
+ extractedStyle->setProperty(CSSPropertyTextDecoration, extractedValueList->cssText(), isImportant);
+ }
+ }
+ }
+ }
+
+ unsigned propertyCount = m_mutableStyle ? m_mutableStyle->propertyCount() : 0;
for (unsigned i = 0; i < propertyCount; ++i) {
CSSPropertyID propertyID = m_mutableStyle->propertyAt(i).id();
@@ -729,52 +864,53 @@ bool EditingStyle::conflictsWithInlineStyleOfElement(StyledElement* element, Edi
continue;
if (propertyID == CSSPropertyWebkitTextDecorationsInEffect && inlineStyle->getPropertyCSSValue(CSSPropertyTextDecoration)) {
- if (!conflictingProperties)
+ if (!newInlineStyle)
return true;
- conflictingProperties->append(CSSPropertyTextDecoration);
+ conflicts = true;
+ newInlineStyle->removeProperty(CSSPropertyTextDecoration);
if (extractedStyle)
extractedStyle->setProperty(CSSPropertyTextDecoration, inlineStyle->getPropertyValue(CSSPropertyTextDecoration), inlineStyle->propertyIsImportant(CSSPropertyTextDecoration));
- continue;
}
if (!inlineStyle->getPropertyCSSValue(propertyID))
continue;
if (propertyID == CSSPropertyUnicodeBidi && inlineStyle->getPropertyCSSValue(CSSPropertyDirection)) {
- if (!conflictingProperties)
+ if (!newInlineStyle)
return true;
- conflictingProperties->append(CSSPropertyDirection);
+ conflicts = true;
+ newInlineStyle->removeProperty(CSSPropertyDirection);
if (extractedStyle)
extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
}
- if (!conflictingProperties)
+ if (!newInlineStyle)
return true;
- conflictingProperties->append(propertyID);
-
+ conflicts = true;
+ newInlineStyle->removeProperty(propertyID);
if (extractedStyle)
extractedStyle->setProperty(propertyID, inlineStyle->getPropertyValue(propertyID), inlineStyle->propertyIsImportant(propertyID));
}
- return conflictingProperties && !conflictingProperties->isEmpty();
+ return conflicts;
}
-static const Vector<OwnPtr<HTMLElementEquivalent>>& htmlElementEquivalents()
+static const Vector<std::unique_ptr<HTMLElementEquivalent>>& htmlElementEquivalents()
{
- DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLElementEquivalent>>, HTMLElementEquivalents, ());
+ static NeverDestroyed<Vector<std::unique_ptr<HTMLElementEquivalent>>> HTMLElementEquivalents;
- if (!HTMLElementEquivalents.size()) {
- HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag));
- HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag));
- HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag));
- HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag));
- HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag));
- HTMLElementEquivalents.append(HTMLElementEquivalent::create(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag));
+ if (!HTMLElementEquivalents.get().size()) {
+ HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontWeight, CSSValueBold, HTMLNames::bTag));
+ HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontWeight, CSSValueBold, HTMLNames::strongTag));
+ HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyVerticalAlign, CSSValueSub, HTMLNames::subTag));
+ HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyVerticalAlign, CSSValueSuper, HTMLNames::supTag));
+ HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::iTag));
+ HTMLElementEquivalents.get().append(std::make_unique<HTMLElementEquivalent>(CSSPropertyFontStyle, CSSValueItalic, HTMLNames::emTag));
- HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueUnderline, HTMLNames::uTag));
- HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::sTag));
- HTMLElementEquivalents.append(HTMLTextDecorationEquivalent::create(CSSValueLineThrough, HTMLNames::strikeTag));
+ HTMLElementEquivalents.get().append(std::make_unique<HTMLTextDecorationEquivalent>(CSSValueUnderline, HTMLNames::uTag));
+ HTMLElementEquivalents.get().append(std::make_unique<HTMLTextDecorationEquivalent>(CSSValueLineThrough, HTMLNames::sTag));
+ HTMLElementEquivalents.get().append(std::make_unique<HTMLTextDecorationEquivalent>(CSSValueLineThrough, HTMLNames::strikeTag));
}
return HTMLElementEquivalents;
@@ -783,14 +919,13 @@ static const Vector<OwnPtr<HTMLElementEquivalent>>& htmlElementEquivalents()
bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, EditingStyle* extractedStyle, ShouldExtractMatchingStyle shouldExtractMatchingStyle) const
{
- if (!m_mutableStyle)
+ if (isEmpty())
return false;
- const Vector<OwnPtr<HTMLElementEquivalent>>& HTMLElementEquivalents = htmlElementEquivalents();
- for (size_t i = 0; i < HTMLElementEquivalents.size(); ++i) {
- const HTMLElementEquivalent* equivalent = HTMLElementEquivalents[i].get();
- if (equivalent->matches(element) && equivalent->propertyExistsInStyle(m_mutableStyle.get())
- && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(element, m_mutableStyle.get()))) {
+ const Vector<std::unique_ptr<HTMLElementEquivalent>>& HTMLElementEquivalents = htmlElementEquivalents();
+ for (auto& equivalent : HTMLElementEquivalents) {
+ if (equivalent->matches(*element) && equivalent->propertyExistsInStyle(*this)
+ && (shouldExtractMatchingStyle == ExtractMatchingStyle || !equivalent->valueIsPresentInStyle(*element, *this))) {
if (extractedStyle)
equivalent->addToStyle(element, extractedStyle);
return true;
@@ -799,19 +934,19 @@ bool EditingStyle::conflictsWithImplicitStyleOfElement(HTMLElement* element, Edi
return false;
}
-static const Vector<OwnPtr<HTMLAttributeEquivalent>>& htmlAttributeEquivalents()
+static const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& htmlAttributeEquivalents()
{
- DEFINE_STATIC_LOCAL(Vector<OwnPtr<HTMLAttributeEquivalent>>, HTMLAttributeEquivalents, ());
+ static NeverDestroyed<Vector<std::unique_ptr<HTMLAttributeEquivalent>>> HTMLAttributeEquivalents;
- if (!HTMLAttributeEquivalents.size()) {
+ if (!HTMLAttributeEquivalents.get().size()) {
// elementIsStyledSpanOrHTMLEquivalent depends on the fact each HTMLAttriuteEquivalent matches exactly one attribute
// of exactly one element except dirAttr.
- HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
- HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
- HTMLAttributeEquivalents.append(HTMLFontSizeEquivalent::create());
+ HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyColor, HTMLNames::fontTag, HTMLNames::colorAttr));
+ HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyFontFamily, HTMLNames::fontTag, HTMLNames::faceAttr));
+ HTMLAttributeEquivalents.get().append(std::make_unique<HTMLFontSizeEquivalent>());
- HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyDirection, HTMLNames::dirAttr));
- HTMLAttributeEquivalents.append(HTMLAttributeEquivalent::create(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
+ HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyDirection, HTMLNames::dirAttr));
+ HTMLAttributeEquivalents.get().append(std::make_unique<HTMLAttributeEquivalent>(CSSPropertyUnicodeBidi, HTMLNames::dirAttr));
}
return HTMLAttributeEquivalents;
@@ -820,13 +955,12 @@ static const Vector<OwnPtr<HTMLAttributeEquivalent>>& htmlAttributeEquivalents()
bool EditingStyle::conflictsWithImplicitStyleOfAttributes(HTMLElement* element) const
{
ASSERT(element);
- if (!m_mutableStyle)
+ if (isEmpty())
return false;
- const Vector<OwnPtr<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = htmlAttributeEquivalents();
- for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
- if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->propertyExistsInStyle(m_mutableStyle.get())
- && !HTMLAttributeEquivalents[i]->valueIsPresentInStyle(element, m_mutableStyle.get()))
+ const Vector<std::unique_ptr<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = htmlAttributeEquivalents();
+ for (auto& equivalent : HTMLAttributeEquivalents) {
+ if (equivalent->matches(*element) && equivalent->propertyExistsInStyle(*this) && !equivalent->valueIsPresentInStyle(*element, *this))
return true;
}
@@ -842,17 +976,14 @@ bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* elem
if (!m_mutableStyle)
return false;
- const Vector<OwnPtr<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = htmlAttributeEquivalents();
bool removed = false;
- for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
- const HTMLAttributeEquivalent* equivalent = HTMLAttributeEquivalents[i].get();
-
+ for (auto& equivalent : htmlAttributeEquivalents()) {
// unicode-bidi and direction are pushed down separately so don't push down with other styles.
if (shouldPreserveWritingDirection == PreserveWritingDirection && equivalent->attributeName() == HTMLNames::dirAttr)
continue;
- if (!equivalent->matches(element) || !equivalent->propertyExistsInStyle(m_mutableStyle.get())
- || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(element, m_mutableStyle.get())))
+ if (!equivalent->matches(*element) || !equivalent->propertyExistsInStyle(*this)
+ || (shouldExtractMatchingStyle == DoNotExtractMatchingStyle && equivalent->valueIsPresentInStyle(*element, *this)))
continue;
if (extractedStyle)
@@ -866,10 +997,28 @@ bool EditingStyle::extractConflictingImplicitStyleOfAttributes(HTMLElement* elem
bool EditingStyle::styleIsPresentInComputedStyleOfNode(Node* node) const
{
- if (!m_mutableStyle)
+ if (isEmpty())
return true;
ComputedStyleExtractor computedStyle(node);
- return getPropertiesNotIn(m_mutableStyle.get(), &computedStyle)->isEmpty();
+
+ bool shouldAddUnderline = underlineChange() == TextDecorationChange::Add;
+ bool shouldAddLineThrough = strikeThroughChange() == TextDecorationChange::Add;
+ if (shouldAddUnderline || shouldAddLineThrough) {
+ bool hasUnderline = false;
+ bool hasLineThrough = false;
+ if (RefPtr<CSSValue> value = computedStyle.propertyValue(CSSPropertyTextDecoration)) {
+ if (value->isValueList()) {
+ auto& cssValuePool = CSSValuePool::singleton();
+ const CSSValueList& valueList = downcast<CSSValueList>(*value);
+ hasUnderline = valueList.hasValue(cssValuePool.createIdentifierValue(CSSValueUnderline).ptr());
+ hasLineThrough = valueList.hasValue(cssValuePool.createIdentifierValue(CSSValueLineThrough).ptr());
+ }
+ }
+ if ((shouldAddUnderline && !hasUnderline) || (shouldAddLineThrough && !hasLineThrough))
+ return false;
+ }
+
+ return !m_mutableStyle || getPropertiesNotIn(*m_mutableStyle, computedStyle)->isEmpty();
}
bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* element)
@@ -878,10 +1027,8 @@ bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* elemen
if (element->hasTagName(HTMLNames::spanTag))
elementIsSpanOrElementEquivalent = true;
else {
- const Vector<OwnPtr<HTMLElementEquivalent>>& HTMLElementEquivalents = htmlElementEquivalents();
- size_t i;
- for (i = 0; i < HTMLElementEquivalents.size(); ++i) {
- if (HTMLElementEquivalents[i]->matches(element)) {
+ for (auto& equivalent : htmlElementEquivalents()) {
+ if (equivalent->matches(*element)) {
elementIsSpanOrElementEquivalent = true;
break;
}
@@ -892,16 +1039,15 @@ bool EditingStyle::elementIsStyledSpanOrHTMLEquivalent(const HTMLElement* elemen
return elementIsSpanOrElementEquivalent; // span, b, etc... without any attributes
unsigned matchedAttributes = 0;
- const Vector<OwnPtr<HTMLAttributeEquivalent>>& HTMLAttributeEquivalents = htmlAttributeEquivalents();
- for (size_t i = 0; i < HTMLAttributeEquivalents.size(); ++i) {
- if (HTMLAttributeEquivalents[i]->matches(element) && HTMLAttributeEquivalents[i]->attributeName() != HTMLNames::dirAttr)
+ for (auto& equivalent : htmlAttributeEquivalents()) {
+ if (equivalent->matches(*element) && equivalent->attributeName() != HTMLNames::dirAttr)
matchedAttributes++;
}
if (!elementIsSpanOrElementEquivalent && !matchedAttributes)
return false; // element is not a span, a html element equivalent, or font element.
- if (element->getAttribute(HTMLNames::classAttr) == AppleStyleSpanClass)
+ if (element->attributeWithoutSynchronization(HTMLNames::classAttr) == AppleStyleSpanClass)
matchedAttributes++;
if (element->hasAttribute(HTMLNames::styleAttr)) {
@@ -938,22 +1084,22 @@ void EditingStyle::prepareToApplyAt(const Position& position, ShouldPreserveWrit
direction = m_mutableStyle->getPropertyCSSValue(CSSPropertyDirection);
}
- m_mutableStyle->removeEquivalentProperties(styleAtPosition);
+ removeEquivalentProperties(*styleAtPosition);
- if (textAlignResolvingStartAndEnd(m_mutableStyle.get()) == textAlignResolvingStartAndEnd(styleAtPosition))
+ if (textAlignResolvingStartAndEnd(*m_mutableStyle) == textAlignResolvingStartAndEnd(*styleAtPosition))
m_mutableStyle->removeProperty(CSSPropertyTextAlign);
- if (textColorFromStyle(m_mutableStyle.get()) == textColorFromStyle(styleAtPosition))
+ if (textColorFromStyle(*m_mutableStyle) == textColorFromStyle(*styleAtPosition))
m_mutableStyle->removeProperty(CSSPropertyColor);
if (hasTransparentBackgroundColor(m_mutableStyle.get())
- || cssValueToRGBA(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
+ || cssValueToColor(m_mutableStyle->getPropertyCSSValue(CSSPropertyBackgroundColor).get()) == rgbaBackgroundColorInEffect(position.containerNode()))
m_mutableStyle->removeProperty(CSSPropertyBackgroundColor);
- if (unicodeBidi && unicodeBidi->isPrimitiveValue()) {
- m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSValueID>(toCSSPrimitiveValue(unicodeBidi.get())->getValueID()));
- if (direction && direction->isPrimitiveValue())
- m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSValueID>(toCSSPrimitiveValue(direction.get())->getValueID()));
+ if (is<CSSPrimitiveValue>(unicodeBidi.get())) {
+ m_mutableStyle->setProperty(CSSPropertyUnicodeBidi, static_cast<CSSValueID>(downcast<CSSPrimitiveValue>(*unicodeBidi).valueID()));
+ if (is<CSSPrimitiveValue>(direction.get()))
+ m_mutableStyle->setProperty(CSSPropertyDirection, static_cast<CSSValueID>(downcast<CSSPrimitiveValue>(*direction).valueID()));
}
}
@@ -986,10 +1132,14 @@ void EditingStyle::mergeInlineStyleOfElement(StyledElement* element, CSSProperty
}
static inline bool elementMatchesAndPropertyIsNotInInlineStyleDecl(const HTMLElementEquivalent* equivalent, const StyledElement* element,
- EditingStyle::CSSPropertyOverrideMode mode, StyleProperties* style)
+ EditingStyle::CSSPropertyOverrideMode mode, EditingStyle& style)
{
- return equivalent->matches(element) && (!element->inlineStyle() || !equivalent->propertyExistsInStyle(element->inlineStyle()))
- && (mode == EditingStyle::OverrideValues || !equivalent->propertyExistsInStyle(style));
+ if (!equivalent->matches(*element))
+ return false;
+ if (mode != EditingStyle::OverrideValues && equivalent->propertyExistsInStyle(style))
+ return false;
+
+ return !element->inlineStyle() || !equivalent->propertyExistsInStyle(EditingStyle::create(element->inlineStyle()).get());
}
static PassRefPtr<MutableStyleProperties> extractEditingProperties(const StyleProperties* style, EditingStyle::PropertiesToInclude propertiesToInclude)
@@ -1020,26 +1170,23 @@ void EditingStyle::mergeInlineAndImplicitStyleOfElement(StyledElement* element,
styleFromRules->m_mutableStyle = extractEditingProperties(styleFromRules->m_mutableStyle.get(), propertiesToInclude);
mergeStyle(styleFromRules->m_mutableStyle.get(), mode);
- const Vector<OwnPtr<HTMLElementEquivalent>>& elementEquivalents = htmlElementEquivalents();
- for (size_t i = 0; i < elementEquivalents.size(); ++i) {
- if (elementMatchesAndPropertyIsNotInInlineStyleDecl(elementEquivalents[i].get(), element, mode, m_mutableStyle.get()))
- elementEquivalents[i]->addToStyle(element, this);
+ for (auto& equivalent : htmlElementEquivalents()) {
+ if (elementMatchesAndPropertyIsNotInInlineStyleDecl(equivalent.get(), element, mode, *this))
+ equivalent->addToStyle(element, this);
}
- const Vector<OwnPtr<HTMLAttributeEquivalent>>& attributeEquivalents = htmlAttributeEquivalents();
- for (size_t i = 0; i < attributeEquivalents.size(); ++i) {
- if (attributeEquivalents[i]->attributeName() == HTMLNames::dirAttr)
+ for (auto& equivalent : htmlAttributeEquivalents()) {
+ if (equivalent->attributeName() == HTMLNames::dirAttr)
continue; // We don't want to include directionality
- if (elementMatchesAndPropertyIsNotInInlineStyleDecl(attributeEquivalents[i].get(), element, mode, m_mutableStyle.get()))
- attributeEquivalents[i]->addToStyle(element, this);
+ if (elementMatchesAndPropertyIsNotInInlineStyleDecl(equivalent.get(), element, mode, *this))
+ equivalent->addToStyle(element, this);
}
}
-PassRefPtr<EditingStyle> EditingStyle::wrappingStyleForSerialization(Node* context, bool shouldAnnotate)
+Ref<EditingStyle> EditingStyle::wrappingStyleForSerialization(Node* context, bool shouldAnnotate)
{
- RefPtr<EditingStyle> wrappingStyle;
if (shouldAnnotate) {
- wrappingStyle = EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect);
+ auto wrappingStyle = EditingStyle::create(context, EditingStyle::EditingPropertiesInEffect);
// Styles that Mail blockquotes contribute should only be placed on the Mail blockquote,
// to help us differentiate those styles from ones that the user has applied.
@@ -1049,33 +1196,34 @@ PassRefPtr<EditingStyle> EditingStyle::wrappingStyleForSerialization(Node* conte
// Call collapseTextDecorationProperties first or otherwise it'll copy the value over from in-effect to text-decorations.
wrappingStyle->collapseTextDecorationProperties();
- return wrappingStyle.release();
+ return wrappingStyle;
}
- wrappingStyle = EditingStyle::create();
+ auto wrappingStyle = EditingStyle::create();
// When not annotating for interchange, we only preserve inline style declarations.
for (Node* node = context; node && !node->isDocumentNode(); node = node->parentNode()) {
- if (node->isStyledElement() && !isMailBlockquote(node)) {
- wrappingStyle->mergeInlineAndImplicitStyleOfElement(toStyledElement(node), EditingStyle::DoNotOverrideValues,
+ if (is<StyledElement>(*node) && !isMailBlockquote(node)) {
+ wrappingStyle->mergeInlineAndImplicitStyleOfElement(downcast<StyledElement>(node), EditingStyle::DoNotOverrideValues,
EditingStyle::EditingPropertiesInEffect);
}
}
- return wrappingStyle.release();
+ return wrappingStyle;
}
-static void mergeTextDecorationValues(CSSValueList* mergedValue, const CSSValueList* valueToMerge)
+static void mergeTextDecorationValues(CSSValueList& mergedValue, const CSSValueList& valueToMerge)
{
- RefPtr<CSSPrimitiveValue> underline = cssValuePool().createIdentifierValue(CSSValueUnderline);
- RefPtr<CSSPrimitiveValue> lineThrough = cssValuePool().createIdentifierValue(CSSValueLineThrough);
+ auto& cssValuePool = CSSValuePool::singleton();
+ Ref<CSSPrimitiveValue> underline = cssValuePool.createIdentifierValue(CSSValueUnderline);
+ Ref<CSSPrimitiveValue> lineThrough = cssValuePool.createIdentifierValue(CSSValueLineThrough);
- if (valueToMerge->hasValue(underline.get()) && !mergedValue->hasValue(underline.get()))
- mergedValue->append(underline.get());
+ if (valueToMerge.hasValue(underline.ptr()) && !mergedValue.hasValue(underline.ptr()))
+ mergedValue.append(WTFMove(underline));
- if (valueToMerge->hasValue(lineThrough.get()) && !mergedValue->hasValue(lineThrough.get()))
- mergedValue->append(lineThrough.get());
+ if (valueToMerge.hasValue(lineThrough.ptr()) && !mergedValue.hasValue(lineThrough.ptr()))
+ mergedValue.append(WTFMove(lineThrough));
}
void EditingStyle::mergeStyle(const StyleProperties* style, CSSPropertyOverrideMode mode)
@@ -1093,17 +1241,20 @@ void EditingStyle::mergeStyle(const StyleProperties* style, CSSPropertyOverrideM
StyleProperties::PropertyReference property = style->propertyAt(i);
RefPtr<CSSValue> value = m_mutableStyle->getPropertyCSSValue(property.id());
- // text decorations never override values
- if ((property.id() == CSSPropertyTextDecoration || property.id() == CSSPropertyWebkitTextDecorationsInEffect) && property.value()->isValueList() && value) {
- if (value->isValueList()) {
- mergeTextDecorationValues(toCSSValueList(value.get()), toCSSValueList(property.value()));
+ // text decorations never override values.
+ if ((property.id() == CSSPropertyTextDecoration || property.id() == CSSPropertyWebkitTextDecorationsInEffect)
+ && is<CSSValueList>(*property.value()) && value) {
+ if (is<CSSValueList>(*value)) {
+ auto newValue = downcast<CSSValueList>(*value).copy();
+ mergeTextDecorationValues(newValue, downcast<CSSValueList>(*property.value()));
+ m_mutableStyle->setProperty(property.id(), WTFMove(newValue), property.isImportant());
continue;
}
- value = 0; // text-decoration: none is equivalent to not having the property
+ value = nullptr; // text-decoration: none is equivalent to not having the property.
}
if (mode == OverrideValues || (mode == DoNotOverrideValues && !value))
- m_mutableStyle->setProperty(property.id(), property.value()->cssText(), property.isImportant());
+ m_mutableStyle->setProperty(property.id(), property.value(), property.isImportant());
}
int oldFontSizeDelta = m_fontSizeDelta;
@@ -1111,16 +1262,15 @@ void EditingStyle::mergeStyle(const StyleProperties* style, CSSPropertyOverrideM
m_fontSizeDelta += oldFontSizeDelta;
}
-static PassRefPtr<MutableStyleProperties> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
+static Ref<MutableStyleProperties> styleFromMatchedRulesForElement(Element* element, unsigned rulesToInclude)
{
- RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
- Vector<RefPtr<StyleRuleBase>> matchedRules = element->document().ensureStyleResolver().styleRulesForElement(element, rulesToInclude);
- for (unsigned i = 0; i < matchedRules.size(); ++i) {
- if (matchedRules[i]->isStyleRule())
- style->mergeAndOverrideOnConflict(static_pointer_cast<StyleRule>(matchedRules[i])->properties());
+ auto style = MutableStyleProperties::create();
+ for (auto& matchedRule : element->styleResolver().styleRulesForElement(element, rulesToInclude)) {
+ if (matchedRule->isStyleRule())
+ style->mergeAndOverrideOnConflict(static_pointer_cast<StyleRule>(matchedRule)->properties());
}
- return style.release();
+ return style;
}
void EditingStyle::mergeStyleFromRules(StyledElement* element)
@@ -1151,18 +1301,18 @@ void EditingStyle::mergeStyleFromRulesForSerialization(StyledElement* element)
for (unsigned i = 0; i < propertyCount; ++i) {
StyleProperties::PropertyReference property = m_mutableStyle->propertyAt(i);
CSSValue* value = property.value();
- if (!value->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(*value))
continue;
- if (toCSSPrimitiveValue(value)->isPercentage()) {
- if (RefPtr<CSSValue> computedPropertyValue = computedStyle.propertyValue(property.id()))
- fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue.release()));
+ if (downcast<CSSPrimitiveValue>(*value).isPercentage()) {
+ if (auto computedPropertyValue = computedStyle.propertyValue(property.id()))
+ fromComputedStyle->addParsedProperty(CSSProperty(property.id(), WTFMove(computedPropertyValue)));
}
}
}
m_mutableStyle->mergeAndOverrideOnConflict(*fromComputedStyle);
}
-static void removePropertiesInStyle(MutableStyleProperties* styleToRemovePropertiesFrom, StyleProperties* style)
+static void removePropertiesInStyle(MutableStyleProperties* styleToRemovePropertiesFrom, MutableStyleProperties* style)
{
unsigned propertyCount = style->propertyCount();
Vector<CSSPropertyID> propertiesToRemove(propertyCount);
@@ -1181,7 +1331,7 @@ void EditingStyle::removeStyleFromRulesAndContext(StyledElement* element, Node*
// 1. Remove style from matched rules because style remain without repeating it in inline style declaration
RefPtr<MutableStyleProperties> styleFromMatchedRules = styleFromMatchedRulesForElement(element, StyleResolver::AllButEmptyCSSRules);
if (styleFromMatchedRules && !styleFromMatchedRules->isEmpty())
- m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), styleFromMatchedRules.get());
+ m_mutableStyle = getPropertiesNotIn(*m_mutableStyle, *styleFromMatchedRules);
// 2. Remove style present in context and not overriden by matched rules.
RefPtr<EditingStyle> computedStyle = EditingStyle::create(context, EditingPropertiesInEffect);
@@ -1190,15 +1340,15 @@ void EditingStyle::removeStyleFromRulesAndContext(StyledElement* element, Node*
computedStyle->m_mutableStyle->setProperty(CSSPropertyBackgroundColor, CSSValueTransparent);
removePropertiesInStyle(computedStyle->m_mutableStyle.get(), styleFromMatchedRules.get());
- m_mutableStyle = getPropertiesNotIn(m_mutableStyle.get(), computedStyle->m_mutableStyle.get());
+ m_mutableStyle = getPropertiesNotIn(*m_mutableStyle, *computedStyle->m_mutableStyle);
}
// 3. If this element is a span and has display: inline or float: none, remove them unless they are overriden by rules.
// These rules are added by serialization code to wrap text nodes.
if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) {
- if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && identifierForStyleProperty(m_mutableStyle.get(), CSSPropertyDisplay) == CSSValueInline)
+ if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyDisplay) && identifierForStyleProperty(*m_mutableStyle, CSSPropertyDisplay) == CSSValueInline)
m_mutableStyle->removeProperty(CSSPropertyDisplay);
- if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && identifierForStyleProperty(m_mutableStyle.get(), CSSPropertyFloat) == CSSValueNone)
+ if (!styleFromMatchedRules->getPropertyCSSValue(CSSPropertyFloat) && identifierForStyleProperty(*m_mutableStyle, CSSPropertyFloat) == CSSValueNone)
m_mutableStyle->removeProperty(CSSPropertyFloat);
}
}
@@ -1208,11 +1358,24 @@ void EditingStyle::removePropertiesInElementDefaultStyle(Element* element)
if (!m_mutableStyle || m_mutableStyle->isEmpty())
return;
- RefPtr<StyleProperties> defaultStyle = styleFromMatchedRulesForElement(element, StyleResolver::UAAndUserCSSRules);
+ RefPtr<MutableStyleProperties> defaultStyle = styleFromMatchedRulesForElement(element, StyleResolver::UAAndUserCSSRules);
removePropertiesInStyle(m_mutableStyle.get(), defaultStyle.get());
}
+template<typename T>
+void EditingStyle::removeEquivalentProperties(T& style)
+{
+ Vector<CSSPropertyID> propertiesToRemove;
+ for (auto& property : m_mutableStyle->m_propertyVector) {
+ if (style.propertyMatches(property.id(), property.value()))
+ propertiesToRemove.append(property.id());
+ }
+ // FIXME: This should use mass removal.
+ for (auto& property : propertiesToRemove)
+ m_mutableStyle->removeProperty(property);
+}
+
void EditingStyle::forceInline()
{
if (!m_mutableStyle)
@@ -1221,12 +1384,41 @@ void EditingStyle::forceInline()
m_mutableStyle->setProperty(CSSPropertyDisplay, CSSValueInline, propertyIsImportant);
}
+bool EditingStyle::convertPositionStyle()
+{
+ if (!m_mutableStyle)
+ return false;
+
+ auto& cssValuePool = CSSValuePool::singleton();
+ RefPtr<CSSPrimitiveValue> sticky = cssValuePool.createIdentifierValue(CSSValueWebkitSticky);
+ if (m_mutableStyle->propertyMatches(CSSPropertyPosition, sticky.get())) {
+ m_mutableStyle->setProperty(CSSPropertyPosition, cssValuePool.createIdentifierValue(CSSValueStatic), m_mutableStyle->propertyIsImportant(CSSPropertyPosition));
+ return false;
+ }
+ RefPtr<CSSPrimitiveValue> fixed = cssValuePool.createIdentifierValue(CSSValueFixed);
+ if (m_mutableStyle->propertyMatches(CSSPropertyPosition, fixed.get())) {
+ m_mutableStyle->setProperty(CSSPropertyPosition, cssValuePool.createIdentifierValue(CSSValueAbsolute), m_mutableStyle->propertyIsImportant(CSSPropertyPosition));
+ return true;
+ }
+ RefPtr<CSSPrimitiveValue> absolute = cssValuePool.createIdentifierValue(CSSValueAbsolute);
+ if (m_mutableStyle->propertyMatches(CSSPropertyPosition, absolute.get()))
+ return true;
+ return false;
+}
+
+bool EditingStyle::isFloating()
+{
+ RefPtr<CSSValue> v = m_mutableStyle->getPropertyCSSValue(CSSPropertyFloat);
+ RefPtr<CSSPrimitiveValue> noneValue = CSSValuePool::singleton().createIdentifierValue(CSSValueNone);
+ return v && !v->equals(*noneValue);
+}
+
int EditingStyle::legacyFontSize(Document* document) const
{
RefPtr<CSSValue> cssValue = m_mutableStyle->getPropertyCSSValue(CSSPropertyFontSize);
- if (!cssValue || !cssValue->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(cssValue.get()))
return 0;
- return legacyFontSizeFromCSSValue(document, toCSSPrimitiveValue(cssValue.get()),
+ return legacyFontSizeFromCSSValue(document, downcast<CSSPrimitiveValue>(cssValue.get()),
m_shouldUseFixedDefaultFontSize, AlwaysUseLegacyFontSize);
}
@@ -1257,7 +1449,7 @@ PassRefPtr<EditingStyle> EditingStyle::styleAtSelectionStart(const VisibleSelect
// and find the background color of the common ancestor.
if (shouldUseBackgroundColorInEffect && (selection.isRange() || hasTransparentBackgroundColor(style->m_mutableStyle.get()))) {
RefPtr<Range> range(selection.toNormalizedRange());
- if (PassRefPtr<CSSValue> value = backgroundColorInEffect(range->commonAncestorContainer(IGNORE_EXCEPTION)))
+ if (auto value = backgroundColorInEffect(range->commonAncestorContainer()))
style->setProperty(CSSPropertyBackgroundColor, value->cssText());
}
@@ -1282,15 +1474,15 @@ WritingDirection EditingStyle::textDirectionForSelection(const VisibleSelection&
end = selection.end().upstream();
Node* pastLast = Range::create(*end.document(), position.parentAnchoredEquivalent(), end.parentAnchoredEquivalent())->pastLastNode();
- for (Node* n = node; n && n != pastLast; n = NodeTraversal::next(n)) {
+ for (Node* n = node; n && n != pastLast; n = NodeTraversal::next(*n)) {
if (!n->isStyledElement())
continue;
RefPtr<CSSValue> unicodeBidi = ComputedStyleExtractor(n).propertyValue(CSSPropertyUnicodeBidi);
- if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(unicodeBidi.get()))
continue;
- CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID();
+ CSSValueID unicodeBidiValue = downcast<CSSPrimitiveValue>(*unicodeBidi).valueID();
if (unicodeBidiValue == CSSValueEmbed || unicodeBidiValue == CSSValueBidiOverride)
return NaturalWritingDirection;
}
@@ -1316,10 +1508,10 @@ WritingDirection EditingStyle::textDirectionForSelection(const VisibleSelection&
ComputedStyleExtractor computedStyle(node);
RefPtr<CSSValue> unicodeBidi = computedStyle.propertyValue(CSSPropertyUnicodeBidi);
- if (!unicodeBidi || !unicodeBidi->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(unicodeBidi.get()))
continue;
- CSSValueID unicodeBidiValue = toCSSPrimitiveValue(unicodeBidi.get())->getValueID();
+ CSSValueID unicodeBidiValue = downcast<CSSPrimitiveValue>(*unicodeBidi).valueID();
if (unicodeBidiValue == CSSValueNormal)
continue;
@@ -1328,10 +1520,10 @@ WritingDirection EditingStyle::textDirectionForSelection(const VisibleSelection&
ASSERT(unicodeBidiValue == CSSValueEmbed);
RefPtr<CSSValue> direction = computedStyle.propertyValue(CSSPropertyDirection);
- if (!direction || !direction->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(direction.get()))
continue;
- CSSValueID directionValue = toCSSPrimitiveValue(direction.get())->getValueID();
+ CSSValueID directionValue = downcast<CSSPrimitiveValue>(*direction).valueID();
if (directionValue != CSSValueLtr && directionValue != CSSValueRtl)
continue;
@@ -1339,7 +1531,7 @@ WritingDirection EditingStyle::textDirectionForSelection(const VisibleSelection&
return NaturalWritingDirection;
// In the range case, make sure that the embedding element persists until the end of the range.
- if (selection.isRange() && !end.deprecatedNode()->isDescendantOf(node))
+ if (selection.isRange() && !end.deprecatedNode()->isDescendantOf(*node))
return NaturalWritingDirection;
foundDirection = directionValue == CSSValueLtr ? LeftToRightWritingDirection : RightToLeftWritingDirection;
@@ -1373,8 +1565,8 @@ StyleChange::StyleChange(EditingStyle* style, const Position& position)
, m_applySubscript(false)
, m_applySuperscript(false)
{
- Document* document = position.anchorNode() ? &position.anchorNode()->document() : 0;
- if (!style || !style->style() || !document || !document->frame())
+ Document* document = position.deprecatedNode() ? &position.deprecatedNode()->document() : 0;
+ if (!style || style->isEmpty() || !document || !document->frame())
return;
Node* node = position.containerNode();
@@ -1384,11 +1576,44 @@ StyleChange::StyleChange(EditingStyle* style, const Position& position)
ComputedStyleExtractor computedStyle(node);
// FIXME: take care of background-color in effect
- RefPtr<MutableStyleProperties> mutableStyle = getPropertiesNotIn(style->style(), &computedStyle);
+ RefPtr<MutableStyleProperties> mutableStyle = style->style() ?
+ getPropertiesNotIn(*style->style(), computedStyle) : MutableStyleProperties::create();
reconcileTextDecorationProperties(mutableStyle.get());
- if (!document->frame()->editor().shouldStyleWithCSS())
- extractTextStyles(document, mutableStyle.get(), computedStyle.useFixedFontDefaultSize());
+ bool shouldStyleWithCSS = document->frame()->editor().shouldStyleWithCSS();
+ if (!shouldStyleWithCSS)
+ extractTextStyles(document, *mutableStyle, computedStyle.useFixedFontDefaultSize());
+
+ bool shouldAddUnderline = style->underlineChange() == TextDecorationChange::Add;
+ bool shouldAddStrikeThrough = style->strikeThroughChange() == TextDecorationChange::Add;
+ if (shouldAddUnderline || shouldAddStrikeThrough) {
+ RefPtr<CSSValue> value = computedStyle.propertyValue(CSSPropertyWebkitTextDecorationsInEffect);
+ if (!is<CSSValueList>(value.get()))
+ value = computedStyle.propertyValue(CSSPropertyTextDecoration);
+
+ RefPtr<CSSValueList> valueList;
+ if (is<CSSValueList>(value.get()))
+ valueList = downcast<CSSValueList>(value.get());
+
+ auto& cssValuePool = CSSValuePool::singleton();
+ Ref<CSSValue> underline = cssValuePool.createIdentifierValue(CSSValueUnderline);
+ bool hasUnderline = valueList && valueList->hasValue(underline.ptr());
+
+ Ref<CSSValue> lineThrough = cssValuePool.createIdentifierValue(CSSValueLineThrough);
+ bool hasLineThrough = valueList && valueList->hasValue(lineThrough.ptr());
+
+ if (shouldStyleWithCSS) {
+ valueList = valueList ? valueList->copy() : CSSValueList::createSpaceSeparated();
+ if (shouldAddUnderline && !hasUnderline)
+ valueList->append(WTFMove(underline));
+ if (shouldAddStrikeThrough && !hasLineThrough)
+ valueList->append(WTFMove(lineThrough));
+ mutableStyle->setProperty(CSSPropertyTextDecoration, valueList.get());
+ } else {
+ m_applyUnderline = shouldAddUnderline && !hasUnderline;
+ m_applyLineThrough = shouldAddStrikeThrough && !hasLineThrough;
+ }
+ }
// Changing the whitespace style in a tab span would collapse the tab into a space.
if (isTabSpanTextNode(position.deprecatedNode()) || isTabSpanNode((position.deprecatedNode())))
@@ -1399,43 +1624,59 @@ StyleChange::StyleChange(EditingStyle* style, const Position& position)
if (mutableStyle->getPropertyCSSValue(CSSPropertyUnicodeBidi) && !style->style()->getPropertyCSSValue(CSSPropertyDirection))
mutableStyle->setProperty(CSSPropertyDirection, style->style()->getPropertyValue(CSSPropertyDirection));
- // Save the result for later
- m_cssStyle = mutableStyle->asText().stripWhiteSpace();
+ if (!mutableStyle->isEmpty())
+ m_cssStyle = mutableStyle;
+}
+
+bool StyleChange::operator==(const StyleChange& other)
+{
+ if (m_applyBold != other.m_applyBold
+ || m_applyItalic != other.m_applyItalic
+ || m_applyUnderline != other.m_applyUnderline
+ || m_applyLineThrough != other.m_applyLineThrough
+ || m_applySubscript != other.m_applySubscript
+ || m_applySuperscript != other.m_applySuperscript
+ || m_applyFontColor != other.m_applyFontColor
+ || m_applyFontFace != other.m_applyFontFace
+ || m_applyFontSize != other.m_applyFontSize)
+ return false;
+
+ return (!m_cssStyle && !other.m_cssStyle)
+ || (m_cssStyle && other.m_cssStyle && m_cssStyle->asText() == other.m_cssStyle->asText());
}
-static void setTextDecorationProperty(MutableStyleProperties* style, const CSSValueList* newTextDecoration, CSSPropertyID propertyID)
+static void setTextDecorationProperty(MutableStyleProperties& style, const CSSValueList* newTextDecoration, CSSPropertyID propertyID)
{
if (newTextDecoration->length())
- style->setProperty(propertyID, newTextDecoration->cssText(), style->propertyIsImportant(propertyID));
+ style.setProperty(propertyID, newTextDecoration->cssText(), style.propertyIsImportant(propertyID));
else {
// text-decoration: none is redundant since it does not remove any text decorations.
- style->removeProperty(propertyID);
+ style.removeProperty(propertyID);
}
}
-void StyleChange::extractTextStyles(Document* document, MutableStyleProperties* style, bool shouldUseFixedFontDefaultSize)
+void StyleChange::extractTextStyles(Document* document, MutableStyleProperties& style, bool shouldUseFixedFontDefaultSize)
{
- ASSERT(style);
-
if (identifierForStyleProperty(style, CSSPropertyFontWeight) == CSSValueBold) {
- style->removeProperty(CSSPropertyFontWeight);
+ style.removeProperty(CSSPropertyFontWeight);
m_applyBold = true;
}
int fontStyle = identifierForStyleProperty(style, CSSPropertyFontStyle);
if (fontStyle == CSSValueItalic || fontStyle == CSSValueOblique) {
- style->removeProperty(CSSPropertyFontStyle);
+ style.removeProperty(CSSPropertyFontStyle);
m_applyItalic = true;
}
// Assuming reconcileTextDecorationProperties has been called, there should not be -webkit-text-decorations-in-effect
// Furthermore, text-decoration: none has been trimmed so that text-decoration property is always a CSSValueList.
- RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(CSSPropertyTextDecoration);
- if (textDecoration && textDecoration->isValueList()) {
- RefPtr<CSSPrimitiveValue> underline = cssValuePool().createIdentifierValue(CSSValueUnderline);
- RefPtr<CSSPrimitiveValue> lineThrough = cssValuePool().createIdentifierValue(CSSValueLineThrough);
+ RefPtr<CSSValue> textDecoration = style.getPropertyCSSValue(CSSPropertyTextDecoration);
+ if (is<CSSValueList>(textDecoration.get())) {
+ auto& cssValuePool = CSSValuePool::singleton();
+ RefPtr<CSSPrimitiveValue> underline = cssValuePool.createIdentifierValue(CSSValueUnderline);
+ RefPtr<CSSPrimitiveValue> lineThrough = cssValuePool.createIdentifierValue(CSSValueLineThrough);
- RefPtr<CSSValueList> newTextDecoration = toCSSValueList(textDecoration.get())->copy();
+ RefPtr<CSSValueList> newTextDecoration = downcast<CSSValueList>(*textDecoration).copy();
if (newTextDecoration->removeAll(underline.get()))
m_applyUnderline = true;
if (newTextDecoration->removeAll(lineThrough.get()))
@@ -1448,61 +1689,58 @@ void StyleChange::extractTextStyles(Document* document, MutableStyleProperties*
int verticalAlign = identifierForStyleProperty(style, CSSPropertyVerticalAlign);
switch (verticalAlign) {
case CSSValueSub:
- style->removeProperty(CSSPropertyVerticalAlign);
+ style.removeProperty(CSSPropertyVerticalAlign);
m_applySubscript = true;
break;
case CSSValueSuper:
- style->removeProperty(CSSPropertyVerticalAlign);
+ style.removeProperty(CSSPropertyVerticalAlign);
m_applySuperscript = true;
break;
}
- if (style->getPropertyCSSValue(CSSPropertyColor)) {
+ if (style.getPropertyCSSValue(CSSPropertyColor)) {
m_applyFontColor = Color(textColorFromStyle(style)).serialized();
- style->removeProperty(CSSPropertyColor);
+ style.removeProperty(CSSPropertyColor);
}
- m_applyFontFace = style->getPropertyValue(CSSPropertyFontFamily);
- // Remove single quotes for Outlook 2007 compatibility. See https://bugs.webkit.org/show_bug.cgi?id=79448
- m_applyFontFace.replaceWithLiteral('\'', "");
- style->removeProperty(CSSPropertyFontFamily);
+ m_applyFontFace = style.getPropertyValue(CSSPropertyFontFamily);
+ // Remove quotes for Outlook 2007 compatibility. See https://bugs.webkit.org/show_bug.cgi?id=79448
+ m_applyFontFace.replaceWithLiteral('\"', "");
+ style.removeProperty(CSSPropertyFontFamily);
- if (RefPtr<CSSValue> fontSize = style->getPropertyCSSValue(CSSPropertyFontSize)) {
- if (!fontSize->isPrimitiveValue())
- style->removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
- else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, toCSSPrimitiveValue(fontSize.get()),
+ if (RefPtr<CSSValue> fontSize = style.getPropertyCSSValue(CSSPropertyFontSize)) {
+ if (!is<CSSPrimitiveValue>(*fontSize))
+ style.removeProperty(CSSPropertyFontSize); // Can't make sense of the number. Put no font size.
+ else if (int legacyFontSize = legacyFontSizeFromCSSValue(document, downcast<CSSPrimitiveValue>(fontSize.get()),
shouldUseFixedFontDefaultSize, UseLegacyFontSizeOnlyIfPixelValuesMatch)) {
m_applyFontSize = String::number(legacyFontSize);
- style->removeProperty(CSSPropertyFontSize);
+ style.removeProperty(CSSPropertyFontSize);
}
}
}
-static void diffTextDecorations(MutableStyleProperties* style, CSSPropertyID propertID, CSSValue* refTextDecoration)
+static void diffTextDecorations(MutableStyleProperties& style, CSSPropertyID propertID, CSSValue* refTextDecoration)
{
- RefPtr<CSSValue> textDecoration = style->getPropertyCSSValue(propertID);
- if (!textDecoration || !textDecoration->isValueList() || !refTextDecoration || !refTextDecoration->isValueList())
+ RefPtr<CSSValue> textDecoration = style.getPropertyCSSValue(propertID);
+ if (!is<CSSValueList>(textDecoration.get()) || !is<CSSValueList>(refTextDecoration))
return;
- RefPtr<CSSValueList> newTextDecoration = toCSSValueList(textDecoration.get())->copy();
- CSSValueList* valuesInRefTextDecoration = toCSSValueList(refTextDecoration);
+ RefPtr<CSSValueList> newTextDecoration = downcast<CSSValueList>(*textDecoration).copy();
- for (size_t i = 0; i < valuesInRefTextDecoration->length(); i++)
- newTextDecoration->removeAll(valuesInRefTextDecoration->item(i));
+ for (auto& value : downcast<CSSValueList>(*refTextDecoration))
+ newTextDecoration->removeAll(&value.get());
setTextDecorationProperty(style, newTextDecoration.get(), propertID);
}
-static bool fontWeightIsBold(CSSValue* fontWeight)
+static bool fontWeightIsBold(CSSValue& fontWeight)
{
- if (!fontWeight)
- return false;
- if (!fontWeight->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(fontWeight))
return false;
// Because b tag can only bold text, there are only two states in plain html: bold and not bold.
// Collapse all other values to either one of these two states for editing purposes.
- switch (toCSSPrimitiveValue(fontWeight)->getValueID()) {
+ switch (downcast<CSSPrimitiveValue>(fontWeight).valueID()) {
case CSSValue100:
case CSSValue200:
case CSSValue300:
@@ -1525,41 +1763,42 @@ static bool fontWeightIsBold(CSSValue* fontWeight)
}
template<typename T>
-static bool fontWeightIsBold(T* style)
+static bool fontWeightIsBold(T& style)
{
- return fontWeightIsBold(extractPropertyValue(style, CSSPropertyFontWeight).get());
+ RefPtr<CSSValue> fontWeight = extractPropertyValue(style, CSSPropertyFontWeight);
+ return fontWeight && fontWeightIsBold(*fontWeight);
}
template<typename T>
-static PassRefPtr<MutableStyleProperties> extractPropertiesNotIn(StyleProperties* styleWithRedundantProperties, T* baseStyle)
+static Ref<MutableStyleProperties> extractPropertiesNotIn(StyleProperties& styleWithRedundantProperties, T& baseStyle)
{
- ASSERT(styleWithRedundantProperties);
- RefPtr<MutableStyleProperties> result = styleWithRedundantProperties->mutableCopy();
-
+ RefPtr<EditingStyle> result = EditingStyle::create(&styleWithRedundantProperties);
result->removeEquivalentProperties(baseStyle);
+ ASSERT(result->style());
+ Ref<MutableStyleProperties> mutableStyle = *result->style();
RefPtr<CSSValue> baseTextDecorationsInEffect = extractPropertyValue(baseStyle, CSSPropertyWebkitTextDecorationsInEffect);
- diffTextDecorations(result.get(), CSSPropertyTextDecoration, baseTextDecorationsInEffect.get());
- diffTextDecorations(result.get(), CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
+ diffTextDecorations(mutableStyle, CSSPropertyTextDecoration, baseTextDecorationsInEffect.get());
+ diffTextDecorations(mutableStyle, CSSPropertyWebkitTextDecorationsInEffect, baseTextDecorationsInEffect.get());
- if (extractPropertyValue(baseStyle, CSSPropertyFontWeight) && fontWeightIsBold(result.get()) == fontWeightIsBold(baseStyle))
- result->removeProperty(CSSPropertyFontWeight);
+ if (extractPropertyValue(baseStyle, CSSPropertyFontWeight) && fontWeightIsBold(mutableStyle) == fontWeightIsBold(baseStyle))
+ mutableStyle->removeProperty(CSSPropertyFontWeight);
- if (extractPropertyValue(baseStyle, CSSPropertyColor) && textColorFromStyle(result.get()) == textColorFromStyle(baseStyle))
- result->removeProperty(CSSPropertyColor);
+ if (extractPropertyValue(baseStyle, CSSPropertyColor) && textColorFromStyle(mutableStyle) == textColorFromStyle(baseStyle))
+ mutableStyle->removeProperty(CSSPropertyColor);
if (extractPropertyValue(baseStyle, CSSPropertyTextAlign)
- && textAlignResolvingStartAndEnd(result.get()) == textAlignResolvingStartAndEnd(baseStyle))
- result->removeProperty(CSSPropertyTextAlign);
+ && textAlignResolvingStartAndEnd(mutableStyle) == textAlignResolvingStartAndEnd(baseStyle))
+ mutableStyle->removeProperty(CSSPropertyTextAlign);
- if (extractPropertyValue(baseStyle, CSSPropertyBackgroundColor) && backgroundColorFromStyle(result.get()) == backgroundColorFromStyle(baseStyle))
- result->removeProperty(CSSPropertyBackgroundColor);
+ if (extractPropertyValue(baseStyle, CSSPropertyBackgroundColor) && backgroundColorFromStyle(mutableStyle) == backgroundColorFromStyle(baseStyle))
+ mutableStyle->removeProperty(CSSPropertyBackgroundColor);
- return result.release();
+ return mutableStyle;
}
template<typename T>
-PassRefPtr<MutableStyleProperties> getPropertiesNotIn(StyleProperties* styleWithRedundantProperties, T* baseStyle)
+PassRefPtr<MutableStyleProperties> getPropertiesNotIn(StyleProperties& styleWithRedundantProperties, T& baseStyle)
{
return extractPropertiesNotIn(styleWithRedundantProperties, baseStyle);
}
@@ -1574,7 +1813,7 @@ int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, boo
ASSERT(document); // FIXME: This method should take a Document&
if (isCSSValueLength(value)) {
- int pixelFontSize = value->getIntValue(CSSPrimitiveValue::CSS_PX);
+ int pixelFontSize = value->intValue(CSSPrimitiveValue::CSS_PX);
int legacyFontSize = Style::legacyFontSizeForPixelSize(pixelFontSize, shouldUseFixedFontDefaultSize, *document);
// Use legacy font size only if pixel value matches exactly to that of legacy font size.
int cssPrimitiveEquivalent = legacyFontSize - 1 + CSSValueXSmall;
@@ -1584,39 +1823,38 @@ int legacyFontSizeFromCSSValue(Document* document, CSSPrimitiveValue* value, boo
return 0;
}
- if (CSSValueXSmall <= value->getValueID() && value->getValueID() <= CSSValueWebkitXxxLarge)
- return value->getValueID() - CSSValueXSmall + 1;
+ if (CSSValueXSmall <= value->valueID() && value->valueID() <= CSSValueWebkitXxxLarge)
+ return value->valueID() - CSSValueXSmall + 1;
return 0;
}
-bool isTransparentColorValue(CSSValue* cssValue)
+static bool isTransparentColorValue(CSSValue* value)
{
- if (!cssValue)
+ if (!value)
return true;
- if (!cssValue->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(*value))
return false;
- CSSPrimitiveValue* value = toCSSPrimitiveValue(cssValue);
- if (value->isRGBColor())
- return !alphaChannel(value->getRGBA32Value());
- return value->getValueID() == CSSValueTransparent;
+ auto& primitiveValue = downcast<CSSPrimitiveValue>(*value);
+ if (primitiveValue.isRGBColor())
+ return !primitiveValue.color().isVisible();
+ return primitiveValue.valueID() == CSSValueTransparent;
}
bool hasTransparentBackgroundColor(StyleProperties* style)
{
- RefPtr<CSSValue> cssValue = style->getPropertyCSSValue(CSSPropertyBackgroundColor);
- return isTransparentColorValue(cssValue.get());
+ return isTransparentColorValue(style->getPropertyCSSValue(CSSPropertyBackgroundColor).get());
}
-PassRefPtr<CSSValue> backgroundColorInEffect(Node* node)
+RefPtr<CSSValue> backgroundColorInEffect(Node* node)
{
for (Node* ancestor = node; ancestor; ancestor = ancestor->parentNode()) {
- if (RefPtr<CSSValue> value = ComputedStyleExtractor(ancestor).propertyValue(CSSPropertyBackgroundColor)) {
+ if (auto value = ComputedStyleExtractor(ancestor).propertyValue(CSSPropertyBackgroundColor)) {
if (!isTransparentColorValue(value.get()))
- return value.release();
+ return value;
}
}
- return 0;
+ return nullptr;
}
}
diff --git a/Source/WebCore/editing/EditingStyle.h b/Source/WebCore/editing/EditingStyle.h
index 1fa1cde6a..b9e52e333 100644
--- a/Source/WebCore/editing/EditingStyle.h
+++ b/Source/WebCore/editing/EditingStyle.h
@@ -29,11 +29,11 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef EditingStyle_h
-#define EditingStyle_h
+#pragma once
#include "CSSPropertyNames.h"
#include "CSSValueKeywords.h"
+#include "StyleProperties.h"
#include "WritingDirection.h"
#include <wtf/Forward.h>
#include <wtf/RefCounted.h>
@@ -48,6 +48,7 @@ class CSSStyleDeclaration;
class CSSComputedStyleDeclaration;
class CSSPrimitiveValue;
class CSSValue;
+class ComputedStyleExtractor;
class Document;
class Element;
class HTMLElement;
@@ -60,6 +61,8 @@ class StyleProperties;
class StyledElement;
class VisibleSelection;
+enum class TextDecorationChange { None, Add, Remove };
+
class EditingStyle : public RefCounted<EditingStyle> {
public:
@@ -69,38 +72,50 @@ public:
enum ShouldExtractMatchingStyle { ExtractMatchingStyle, DoNotExtractMatchingStyle };
static float NoFontDelta;
- static PassRefPtr<EditingStyle> create()
+ static Ref<EditingStyle> create()
+ {
+ return adoptRef(*new EditingStyle);
+ }
+
+ static Ref<EditingStyle> create(Node* node, PropertiesToInclude propertiesToInclude = OnlyEditingInheritableProperties)
+ {
+ return adoptRef(*new EditingStyle(node, propertiesToInclude));
+ }
+
+ static Ref<EditingStyle> create(const Position& position, PropertiesToInclude propertiesToInclude = OnlyEditingInheritableProperties)
{
- return adoptRef(new EditingStyle());
+ return adoptRef(*new EditingStyle(position, propertiesToInclude));
}
- static PassRefPtr<EditingStyle> create(Node* node, PropertiesToInclude propertiesToInclude = OnlyEditingInheritableProperties)
+ static Ref<EditingStyle> create(const StyleProperties* style)
{
- return adoptRef(new EditingStyle(node, propertiesToInclude));
+ return adoptRef(*new EditingStyle(style));
}
- static PassRefPtr<EditingStyle> create(const Position& position, PropertiesToInclude propertiesToInclude = OnlyEditingInheritableProperties)
+ static Ref<EditingStyle> create(const CSSStyleDeclaration* style)
{
- return adoptRef(new EditingStyle(position, propertiesToInclude));
+ return adoptRef(*new EditingStyle(style));
}
- static PassRefPtr<EditingStyle> create(const StyleProperties* style)
+ static Ref<EditingStyle> create(CSSPropertyID propertyID, const String& value)
{
- return adoptRef(new EditingStyle(style));
+ return adoptRef(*new EditingStyle(propertyID, value));
}
- static PassRefPtr<EditingStyle> create(CSSPropertyID propertyID, const String& value)
+ static Ref<EditingStyle> create(CSSPropertyID propertyID, CSSValueID value)
{
- return adoptRef(new EditingStyle(propertyID, value));
+ return adoptRef(*new EditingStyle(propertyID, value));
}
- ~EditingStyle();
+ WEBCORE_EXPORT ~EditingStyle();
MutableStyleProperties* style() { return m_mutableStyle.get(); }
+ Ref<MutableStyleProperties> styleWithResolvedTextDecorations() const;
bool textDirection(WritingDirection&) const;
bool isEmpty() const;
void setStyle(PassRefPtr<MutableStyleProperties>);
void overrideWithStyle(const StyleProperties*);
+ void overrideTypingStyleAt(const EditingStyle&, const Position&);
void clear();
PassRefPtr<EditingStyle> copy() const;
PassRefPtr<EditingStyle> extractAndRemoveBlockProperties();
@@ -108,16 +123,18 @@ public:
void removeBlockProperties();
void removeStyleAddedByNode(Node*);
void removeStyleConflictingWithStyleOfNode(Node*);
+ template<typename T> void removeEquivalentProperties(T&);
void collapseTextDecorationProperties();
enum ShouldIgnoreTextOnlyProperties { IgnoreTextOnlyProperties, DoNotIgnoreTextOnlyProperties };
TriState triStateOfStyle(EditingStyle*) const;
TriState triStateOfStyle(const VisibleSelection&) const;
bool conflictsWithInlineStyleOfElement(StyledElement* element) const { return conflictsWithInlineStyleOfElement(element, 0, 0); }
- bool conflictsWithInlineStyleOfElement(StyledElement* element, EditingStyle* extractedStyle, Vector<CSSPropertyID>& conflictingProperties) const
+ bool conflictsWithInlineStyleOfElement(StyledElement* element, RefPtr<MutableStyleProperties>& newInlineStyle,
+ EditingStyle* extractedStyle) const
{
- return conflictsWithInlineStyleOfElement(element, extractedStyle, &conflictingProperties);
+ return conflictsWithInlineStyleOfElement(element, &newInlineStyle, extractedStyle);
}
- bool conflictsWithImplicitStyleOfElement(HTMLElement*, EditingStyle* extractedStyle = 0, ShouldExtractMatchingStyle = DoNotExtractMatchingStyle) const;
+ bool conflictsWithImplicitStyleOfElement(HTMLElement*, EditingStyle* extractedStyle = nullptr, ShouldExtractMatchingStyle = DoNotExtractMatchingStyle) const;
bool conflictsWithImplicitStyleOfAttributes(HTMLElement*) const;
bool extractConflictingImplicitStyleOfAttributes(HTMLElement*, ShouldPreserveWritingDirection, EditingStyle* extractedStyle,
Vector<QualifiedName>& conflictingAttributes, ShouldExtractMatchingStyle) const;
@@ -129,58 +146,62 @@ public:
void mergeTypingStyle(Document&);
enum CSSPropertyOverrideMode { OverrideValues, DoNotOverrideValues };
void mergeInlineStyleOfElement(StyledElement*, CSSPropertyOverrideMode, PropertiesToInclude = AllProperties);
- static PassRefPtr<EditingStyle> wrappingStyleForSerialization(Node* context, bool shouldAnnotate);
+ static Ref<EditingStyle> wrappingStyleForSerialization(Node* context, bool shouldAnnotate);
void mergeStyleFromRules(StyledElement*);
void mergeStyleFromRulesForSerialization(StyledElement*);
void removeStyleFromRulesAndContext(StyledElement*, Node* context);
void removePropertiesInElementDefaultStyle(Element*);
void forceInline();
+ bool convertPositionStyle();
+ bool isFloating();
int legacyFontSize(Document*) const;
float fontSizeDelta() const { return m_fontSizeDelta; }
bool hasFontSizeDelta() const { return m_fontSizeDelta != NoFontDelta; }
bool shouldUseFixedDefaultFontSize() const { return m_shouldUseFixedDefaultFontSize; }
+
+ void setUnderlineChange(TextDecorationChange change) { m_underlineChange = static_cast<unsigned>(change); }
+ TextDecorationChange underlineChange() const { return static_cast<TextDecorationChange>(m_underlineChange); }
+ void setStrikeThroughChange(TextDecorationChange change) { m_strikeThroughChange = static_cast<unsigned>(change); }
+ TextDecorationChange strikeThroughChange() const { return static_cast<TextDecorationChange>(m_strikeThroughChange); }
- static PassRefPtr<EditingStyle> styleAtSelectionStart(const VisibleSelection&, bool shouldUseBackgroundColorInEffect = false);
+ WEBCORE_EXPORT static PassRefPtr<EditingStyle> styleAtSelectionStart(const VisibleSelection&, bool shouldUseBackgroundColorInEffect = false);
static WritingDirection textDirectionForSelection(const VisibleSelection&, EditingStyle* typingStyle, bool& hasNestedOrMultipleEmbeddings);
+
private:
EditingStyle();
EditingStyle(Node*, PropertiesToInclude);
EditingStyle(const Position&, PropertiesToInclude);
+ WEBCORE_EXPORT explicit EditingStyle(const CSSStyleDeclaration*);
explicit EditingStyle(const StyleProperties*);
EditingStyle(CSSPropertyID, const String& value);
+ EditingStyle(CSSPropertyID, CSSValueID);
void init(Node*, PropertiesToInclude);
- void removeTextFillAndStrokeColorsIfNeeded(RenderStyle*);
+ void removeTextFillAndStrokeColorsIfNeeded(const RenderStyle*);
void setProperty(CSSPropertyID, const String& value, bool important = false);
void extractFontSizeDelta();
- template<typename T>
- TriState triStateOfStyle(T* styleToCompare, ShouldIgnoreTextOnlyProperties) const;
- bool conflictsWithInlineStyleOfElement(StyledElement*, EditingStyle* extractedStyle, Vector<CSSPropertyID>* conflictingProperties) const;
+ template<typename T> TriState triStateOfStyle(T& styleToCompare, ShouldIgnoreTextOnlyProperties) const;
+ bool conflictsWithInlineStyleOfElement(StyledElement*, RefPtr<MutableStyleProperties>* newInlineStyle, EditingStyle* extractedStyle) const;
void mergeInlineAndImplicitStyleOfElement(StyledElement*, CSSPropertyOverrideMode, PropertiesToInclude);
void mergeStyle(const StyleProperties*, CSSPropertyOverrideMode);
RefPtr<MutableStyleProperties> m_mutableStyle;
- bool m_shouldUseFixedDefaultFontSize;
- float m_fontSizeDelta;
+ unsigned m_shouldUseFixedDefaultFontSize : 1;
+ unsigned m_underlineChange : 2;
+ unsigned m_strikeThroughChange : 2;
+ float m_fontSizeDelta = NoFontDelta;
friend class HTMLElementEquivalent;
friend class HTMLAttributeEquivalent;
+ friend class HTMLTextDecorationEquivalent;
};
class StyleChange {
public:
- StyleChange()
- : m_applyBold(false)
- , m_applyItalic(false)
- , m_applyUnderline(false)
- , m_applyLineThrough(false)
- , m_applySubscript(false)
- , m_applySuperscript(false)
- { }
-
+ StyleChange() { }
StyleChange(EditingStyle*, const Position&);
- String cssStyle() const { return m_cssStyle; }
+ const StyleProperties* cssStyle() const { return m_cssStyle.get(); }
bool applyBold() const { return m_applyBold; }
bool applyItalic() const { return m_applyItalic; }
bool applyUnderline() const { return m_applyUnderline; }
@@ -195,38 +216,24 @@ public:
String fontFace() { return m_applyFontFace; }
String fontSize() { return m_applyFontSize; }
- bool operator==(const StyleChange& other)
- {
- return m_cssStyle == other.m_cssStyle
- && m_applyBold == other.m_applyBold
- && m_applyItalic == other.m_applyItalic
- && m_applyUnderline == other.m_applyUnderline
- && m_applyLineThrough == other.m_applyLineThrough
- && m_applySubscript == other.m_applySubscript
- && m_applySuperscript == other.m_applySuperscript
- && m_applyFontColor == other.m_applyFontColor
- && m_applyFontFace == other.m_applyFontFace
- && m_applyFontSize == other.m_applyFontSize;
- }
+ bool operator==(const StyleChange&);
bool operator!=(const StyleChange& other)
{
return !(*this == other);
}
private:
- void extractTextStyles(Document*, MutableStyleProperties*, bool shouldUseFixedFontDefaultSize);
-
- String m_cssStyle;
- bool m_applyBold;
- bool m_applyItalic;
- bool m_applyUnderline;
- bool m_applyLineThrough;
- bool m_applySubscript;
- bool m_applySuperscript;
+ void extractTextStyles(Document*, MutableStyleProperties&, bool shouldUseFixedFontDefaultSize);
+
+ RefPtr<MutableStyleProperties> m_cssStyle;
+ bool m_applyBold = false;
+ bool m_applyItalic = false;
+ bool m_applyUnderline = false;
+ bool m_applyLineThrough = false;
+ bool m_applySubscript = false;
+ bool m_applySuperscript = false;
String m_applyFontColor;
String m_applyFontFace;
String m_applyFontSize;
};
} // namespace WebCore
-
-#endif // EditingStyle_h
diff --git a/Source/WebCore/editing/Editor.cpp b/Source/WebCore/editing/Editor.cpp
index e599dbd8b..458c33bf0 100644
--- a/Source/WebCore/editing/Editor.cpp
+++ b/Source/WebCore/editing/Editor.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008, 2011, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -33,10 +33,10 @@
#include "CSSComputedStyleDeclaration.h"
#include "CSSPropertyNames.h"
#include "CachedResourceLoader.h"
-#include "Clipboard.h"
#include "ClipboardEvent.h"
#include "CompositionEvent.h"
#include "CreateLinkCommand.h"
+#include "DataTransfer.h"
#include "DeleteSelectionCommand.h"
#include "DictationAlternative.h"
#include "DictationCommand.h"
@@ -45,31 +45,39 @@
#include "EditorClient.h"
#include "EventHandler.h"
#include "EventNames.h"
-#include "ExceptionCodePlaceholder.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameTree.h"
#include "FrameView.h"
#include "GraphicsContext.h"
+#include "HTMLCollection.h"
#include "HTMLFormControlElement.h"
#include "HTMLFrameOwnerElement.h"
#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
#include "HTMLNames.h"
-#include "HTMLTextAreaElement.h"
+#include "HTMLSpanElement.h"
#include "HitTestResult.h"
#include "IndentOutdentCommand.h"
+#include "InputEvent.h"
#include "InsertListCommand.h"
+#include "InsertTextCommand.h"
#include "KeyboardEvent.h"
#include "KillRing.h"
+#include "Logging.h"
+#include "MainFrame.h"
#include "ModifySelectionListLevel.h"
#include "NodeList.h"
#include "NodeTraversal.h"
#include "Page.h"
#include "Pasteboard.h"
+#include "Range.h"
#include "RemoveFormatCommand.h"
#include "RenderBlock.h"
#include "RenderTextControl.h"
+#include "RenderedDocumentMarker.h"
#include "RenderedPosition.h"
+#include "ReplaceRangeWithTextCommand.h"
#include "ReplaceSelectionCommand.h"
#include "Settings.h"
#include "ShadowRoot.h"
@@ -78,6 +86,7 @@
#include "SpellChecker.h"
#include "SpellingCorrectionCommand.h"
#include "StyleProperties.h"
+#include "TelephoneNumberDetector.h"
#include "Text.h"
#include "TextCheckerClient.h"
#include "TextCheckingHelper.h"
@@ -89,28 +98,60 @@
#include "htmlediting.h"
#include "markup.h"
#include <wtf/unicode/CharacterNames.h>
-#include <wtf/unicode/Unicode.h>
-#if ENABLE(DELETION_UI)
-#include "DeleteButtonController.h"
-#endif
-
-#if PLATFORM(IOS)
-#include "DictationCommandIOS.h"
-#include <wtf/text/StringBuilder.h>
-#include <wtf/text/WTFString.h>
+#if PLATFORM(MAC)
+#include "ServicesOverlayController.h"
#endif
namespace WebCore {
-#if PLATFORM(IOS)
+static bool dispatchBeforeInputEvent(Element& element, const AtomicString& inputType, const String& data = { }, RefPtr<DataTransfer>&& dataTransfer = nullptr, const Vector<RefPtr<StaticRange>>& targetRanges = { }, bool cancelable = true)
+{
+ if (!element.document().settings().inputEventsEnabled())
+ return true;
+
+ return element.dispatchEvent(InputEvent::create(eventNames().beforeinputEvent, inputType, true, cancelable, element.document().defaultView(), data, WTFMove(dataTransfer), targetRanges, 0));
+}
+
+static void dispatchInputEvent(Element& element, const AtomicString& inputType, const String& data = { }, RefPtr<DataTransfer>&& dataTransfer = nullptr, const Vector<RefPtr<StaticRange>>& targetRanges = { })
+{
+ if (element.document().settings().inputEventsEnabled()) {
+ // FIXME: We should not be dispatching to the scoped queue here. Normally, input events are dispatched in CompositeEditCommand::apply after the end of the scope,
+ // but TypingCommands are special in that existing TypingCommands that are applied again fire input events *from within* the scope by calling typingAddedToOpenCommand.
+ // Instead, TypingCommands should always dispatch events synchronously after the end of the scoped queue in CompositeEditCommand::apply. To work around this for the
+ // time being, just revert back to calling dispatchScopedEvent.
+ element.dispatchScopedEvent(InputEvent::create(eventNames().inputEvent, inputType, true, false, element.document().defaultView(), data, WTFMove(dataTransfer), targetRanges, 0));
+ } else
+ element.dispatchInputEvent();
+}
+
+static String inputEventDataForEditingStyleAndAction(const StyleProperties* style, EditAction action)
+{
+ if (!style)
+ return { };
+
+ switch (action) {
+ case EditActionSetColor:
+ return style->getPropertyValue(CSSPropertyColor);
+ case EditActionSetWritingDirection:
+ return style->getPropertyValue(CSSPropertyDirection);
+ default:
+ return { };
+ }
+}
+
+static String inputEventDataForEditingStyleAndAction(EditingStyle& style, EditAction action)
+{
+ return inputEventDataForEditingStyleAndAction(style.style(), action);
+}
+
class ClearTextCommand : public DeleteSelectionCommand {
public:
ClearTextCommand(Document& document);
static void CreateAndApply(const RefPtr<Frame> frame);
private:
- virtual EditAction editingAction() const;
+ EditAction editingAction() const override;
};
ClearTextCommand::ClearTextCommand(Document& document)
@@ -133,73 +174,15 @@ void ClearTextCommand::CreateAndApply(const RefPtr<Frame> frame)
const VisibleSelection oldSelection = frame->selection().selection();
frame->selection().selectAll();
- RefPtr<ClearTextCommand> clearCommand = adoptRef(new ClearTextCommand(*frame->document()));
+ auto clearCommand = adoptRef(*new ClearTextCommand(*frame->document()));
clearCommand->setStartingSelection(oldSelection);
- applyCommand(clearCommand.release());
+ applyCommand(WTFMove(clearCommand));
}
-#endif
using namespace HTMLNames;
using namespace WTF;
using namespace Unicode;
-#if ENABLE(DELETION_UI)
-
-PassRefPtr<Range> Editor::avoidIntersectionWithDeleteButtonController(const Range* range) const
-{
- if (!range)
- return 0;
-
- Node* startContainer = range->startContainer();
- int startOffset = range->startOffset();
- Node* endContainer = range->endContainer();
- int endOffset = range->endOffset();
-
- if (!startContainer)
- return 0;
-
- ASSERT(endContainer);
-
- Element* element = m_deleteButtonController->containerElement();
- if (startContainer == element || startContainer->isDescendantOf(element)) {
- ASSERT(element->parentNode());
- startContainer = element->parentNode();
- startOffset = element->nodeIndex();
- }
- if (endContainer == element || endContainer->isDescendantOf(element)) {
- ASSERT(element->parentNode());
- endContainer = element->parentNode();
- endOffset = element->nodeIndex();
- }
-
- return Range::create(range->ownerDocument(), startContainer, startOffset, endContainer, endOffset);
-}
-
-VisibleSelection Editor::avoidIntersectionWithDeleteButtonController(const VisibleSelection& selection) const
-{
- if (selection.isNone())
- return selection;
-
- Element* element = m_deleteButtonController->containerElement();
- if (!element)
- return selection;
- VisibleSelection updatedSelection = selection;
-
- Position updatedBase = selection.base();
- updatePositionForNodeRemoval(updatedBase, element);
- if (updatedBase != selection.base())
- updatedSelection.setBase(updatedBase);
-
- Position updatedExtent = selection.extent();
- updatePositionForNodeRemoval(updatedExtent, element);
- if (updatedExtent != selection.extent())
- updatedSelection.setExtent(updatedExtent);
-
- return updatedSelection;
-}
-
-#endif
-
// When an event handler has moved the selection outside of a text control
// we should use the target control's selection for this editing operation.
VisibleSelection Editor::selectionForCommand(Event* event)
@@ -210,10 +193,10 @@ VisibleSelection Editor::selectionForCommand(Event* event)
// If the target is a text control, and the current selection is outside of its shadow tree,
// then use the saved selection for that text control.
HTMLTextFormControlElement* textFormControlOfSelectionStart = enclosingTextFormControl(selection.start());
- HTMLTextFormControlElement* textFromControlOfTarget = isHTMLTextFormControlElement(*event->target()->toNode()) ? toHTMLTextFormControlElement(event->target()->toNode()) : nullptr;
+ HTMLTextFormControlElement* textFromControlOfTarget = is<HTMLTextFormControlElement>(*event->target()->toNode()) ? downcast<HTMLTextFormControlElement>(event->target()->toNode()) : nullptr;
if (textFromControlOfTarget && (selection.start().isNull() || textFromControlOfTarget != textFormControlOfSelectionStart)) {
if (RefPtr<Range> range = textFromControlOfTarget->selection())
- return VisibleSelection(range.get(), DOWNSTREAM, selection.isDirectional());
+ return VisibleSelection(*range, DOWNSTREAM, selection.isDirectional());
}
return selection;
}
@@ -227,11 +210,10 @@ EditingBehavior Editor::behavior() const
EditorClient* Editor::client() const
{
if (Page* page = m_frame.page())
- return page->editorClient();
- return 0;
+ return &page->editorClient();
+ return nullptr;
}
-
TextCheckerClient* Editor::textChecker() const
{
if (EditorClient* owner = client())
@@ -239,59 +221,57 @@ TextCheckerClient* Editor::textChecker() const
return 0;
}
-void Editor::handleKeyboardEvent(KeyboardEvent* event)
+void Editor::handleKeyboardEvent(KeyboardEvent& event)
{
if (EditorClient* c = client())
- c->handleKeyboardEvent(event);
+ c->handleKeyboardEvent(&event);
}
-void Editor::handleInputMethodKeydown(KeyboardEvent* event)
+void Editor::handleInputMethodKeydown(KeyboardEvent& event)
{
if (EditorClient* c = client())
- c->handleInputMethodKeydown(event);
+ c->handleInputMethodKeydown(&event);
}
-bool Editor::handleTextEvent(TextEvent* event)
+bool Editor::handleTextEvent(TextEvent& event)
{
+ LOG(Editing, "Editor %p handleTextEvent (data %s)", this, event.data().utf8().data());
+
// Default event handling for Drag and Drop will be handled by DragController
// so we leave the event for it.
- if (event->isDrop())
+ if (event.isDrop())
return false;
- if (event->isPaste()) {
- if (event->pastingFragment())
+ if (event.isPaste()) {
+ if (event.pastingFragment()) {
#if PLATFORM(IOS)
- {
- if (client()->performsTwoStepPaste(event->pastingFragment()))
+ if (client()->performsTwoStepPaste(event.pastingFragment()))
return true;
#endif
- replaceSelectionWithFragment(event->pastingFragment(), false, event->shouldSmartReplace(), event->shouldMatchStyle());
-#if PLATFORM(IOS)
- }
-#endif
- else
- replaceSelectionWithText(event->data(), false, event->shouldSmartReplace());
+ replaceSelectionWithFragment(event.pastingFragment(), false, event.shouldSmartReplace(), event.shouldMatchStyle(), EditActionPaste, event.mailBlockquoteHandling());
+ } else
+ replaceSelectionWithText(event.data(), false, event.shouldSmartReplace(), EditActionPaste);
return true;
}
- String data = event->data();
+ String data = event.data();
if (data == "\n") {
- if (event->isLineBreak())
+ if (event.isLineBreak())
return insertLineBreak();
return insertParagraphSeparator();
}
- return insertTextWithoutSendingTextEvent(data, false, event);
+ return insertTextWithoutSendingTextEvent(data, false, &event);
}
bool Editor::canEdit() const
{
- return m_frame.selection().rootEditableElement();
+ return m_frame.selection().selection().rootEditableElement();
}
bool Editor::canEditRichly() const
{
- return m_frame.selection().isContentRichlyEditable();
+ return m_frame.selection().selection().isContentRichlyEditable();
}
// WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They
@@ -301,17 +281,17 @@ bool Editor::canEditRichly() const
bool Editor::canDHTMLCut()
{
- return !m_frame.selection().isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, ClipboardNumb);
+ return !m_frame.selection().selection().isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, DataTransferAccessPolicy::Numb);
}
bool Editor::canDHTMLCopy()
{
- return !m_frame.selection().isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, ClipboardNumb);
+ return !m_frame.selection().selection().isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, DataTransferAccessPolicy::Numb);
}
bool Editor::canDHTMLPaste()
{
- return !dispatchCPPEvent(eventNames().beforepasteEvent, ClipboardNumb);
+ return !dispatchCPPEvent(eventNames().beforepasteEvent, DataTransferAccessPolicy::Numb);
}
bool Editor::canCut() const
@@ -322,25 +302,23 @@ bool Editor::canCut() const
static HTMLImageElement* imageElementFromImageDocument(Document& document)
{
if (!document.isImageDocument())
- return 0;
+ return nullptr;
- HTMLElement* body = document.body();
+ HTMLElement* body = document.bodyOrFrameset();
if (!body)
- return 0;
+ return nullptr;
Node* node = body->firstChild();
- if (!node)
- return 0;
- if (!isHTMLImageElement(node))
- return 0;
- return toHTMLImageElement(node);
+ if (!is<HTMLImageElement>(node))
+ return nullptr;
+ return downcast<HTMLImageElement>(node);
}
bool Editor::canCopy() const
{
if (imageElementFromImageDocument(document()))
return true;
- FrameSelection& selection = m_frame.selection();
+ const VisibleSelection& selection = m_frame.selection().selection();
return selection.isRange() && !selection.isInPasswordField();
}
@@ -351,25 +329,23 @@ bool Editor::canPaste() const
bool Editor::canDelete() const
{
- FrameSelection& selection = m_frame.selection();
+ const VisibleSelection& selection = m_frame.selection().selection();
return selection.isRange() && selection.rootEditableElement();
}
bool Editor::canDeleteRange(Range* range) const
{
- Node* startContainer = range->startContainer();
- Node* endContainer = range->endContainer();
- if (!startContainer || !endContainer)
- return false;
+ Node& startContainer = range->startContainer();
+ Node& endContainer = range->endContainer();
- if (!startContainer->hasEditableStyle() || !endContainer->hasEditableStyle())
+ if (!startContainer.hasEditableStyle() || !endContainer.hasEditableStyle())
return false;
- if (range->collapsed(IGNORE_EXCEPTION)) {
+ if (range->collapsed()) {
VisiblePosition start(range->startPosition(), DOWNSTREAM);
VisiblePosition previous = start.previous();
// FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item.
- if (previous.isNull() || previous.deepEquivalent().deprecatedNode()->rootEditableElement() != startContainer->rootEditableElement())
+ if (previous.isNull() || previous.deepEquivalent().deprecatedNode()->rootEditableElement() != startContainer.rootEditableElement())
return false;
}
return true;
@@ -390,7 +366,7 @@ bool Editor::isSelectTrailingWhitespaceEnabled()
return client() && client()->isSelectTrailingWhitespaceEnabled();
}
-bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction)
+bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity granularity, bool shouldAddToKillRing, bool isTypingAction)
{
if (!canEdit())
return false;
@@ -400,8 +376,8 @@ bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity g
TypingCommand::deleteKeyPressed(document(), canSmartCopyOrDelete() ? TypingCommand::SmartDelete : 0, granularity);
revealSelectionAfterEditingOperation();
} else {
- if (killRing)
- addToKillRing(selectedRange().get(), false);
+ if (shouldAddToKillRing)
+ addRangeToKillRing(*selectedRange().get(), KillRingInsertionMode::AppendText);
deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
// Implicitly calls revealSelectionAfterEditingOperation().
}
@@ -409,8 +385,8 @@ bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity g
TypingCommand::Options options = 0;
if (canSmartCopyOrDelete())
options |= TypingCommand::SmartDelete;
- if (killRing)
- options |= TypingCommand::KillRing;
+ if (shouldAddToKillRing)
+ options |= TypingCommand::AddsToKillRing;
switch (direction) {
case DirectionForward:
case DirectionRight:
@@ -427,112 +403,39 @@ bool Editor::deleteWithDirection(SelectionDirection direction, TextGranularity g
// FIXME: We should to move this down into deleteKeyPressed.
// clear the "start new kill ring sequence" setting, because it was set to true
// when the selection was updated by deleting the range
- if (killRing)
+ if (shouldAddToKillRing)
setStartNewKillRingSequence(false);
return true;
}
-void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
+void Editor::deleteSelectionWithSmartDelete(bool smartDelete, EditAction editingAction)
{
if (m_frame.selection().isNone())
return;
- applyCommand(DeleteSelectionCommand::create(document(), smartDelete));
+ applyCommand(DeleteSelectionCommand::create(document(), smartDelete, true, false, false, true, editingAction));
}
-#if PLATFORM(IOS)
void Editor::clearText()
{
ClearTextCommand::CreateAndApply(&m_frame);
}
-void Editor::insertDictationPhrases(PassOwnPtr<Vector<Vector<String> > > dictationPhrases, RetainPtr<id> metadata)
-{
- if (m_frame.selection().isNone())
- return;
-
- if (dictationPhrases->isEmpty())
- return;
-
- applyCommand(DictationCommandIOS::create(document(), dictationPhrases, metadata));
-}
-
-void Editor::setDictationPhrasesAsChildOfElement(PassOwnPtr<Vector<Vector<String> > > dictationPhrases, RetainPtr<id> metadata, Element* element)
-{
- // Clear the composition.
- clear();
-
- // Clear the Undo stack, since the operations that follow are not Undoable, and will corrupt the stack. Some day
- // we could make them Undoable, and let callers clear the Undo stack explicitly if they wish.
- clearUndoRedoOperations();
-
- m_frame.selection().clear();
-
- element->removeChildren();
-
- if (dictationPhrases->isEmpty()) {
- client()->respondToChangedContents();
- return;
- }
-
- ExceptionCode ec;
- RefPtr<Range> context = document().createRange();
- context->selectNodeContents(element, ec);
-
- StringBuilder dictationPhrasesBuilder;
- size_t dictationPhraseCount = dictationPhrases->size();
- for (size_t i = 0; i < dictationPhraseCount; i++) {
- const String& firstInterpretation = dictationPhrases->at(i)[0];
- dictationPhrasesBuilder.append(firstInterpretation);
- }
- String serializedDictationPhrases = dictationPhrasesBuilder.toString();
-
- element->appendChild(createFragmentFromText(*context.get(), serializedDictationPhrases), ec);
-
- // We need a layout in order to add markers below.
- document().updateLayout();
-
- if (!element->firstChild()->isTextNode()) {
- // Shouldn't happen.
- ASSERT(element->firstChild()->isTextNode());
- return;
- }
-
- Text* textNode = static_cast<Text*>(element->firstChild());
- int previousDictationPhraseStart = 0;
- for (size_t i = 0; i < dictationPhraseCount; i++) {
- const Vector<String>& interpretations = dictationPhrases->at(i);
- int dictationPhraseLength = interpretations[0].length();
- int dictationPhraseEnd = previousDictationPhraseStart + dictationPhraseLength;
- if (interpretations.size() > 1) {
- RefPtr<Range> dictationPhraseRange = Range::create(document(), textNode, previousDictationPhraseStart, textNode, dictationPhraseEnd);
- document().markers().addDictationPhraseWithAlternativesMarker(dictationPhraseRange.get(), interpretations);
- }
- previousDictationPhraseStart = dictationPhraseEnd;
- }
-
- RefPtr<Range> resultRange = Range::create(document(), textNode, 0, textNode, textNode->length());
- document().markers().addDictationResultMarker(resultRange.get(), metadata);
-
- client()->respondToChangedContents();
-}
-#endif
-
void Editor::pasteAsPlainText(const String& pastingText, bool smartReplace)
{
Node* target = findEventTargetFromSelection();
if (!target)
return;
- target->dispatchEvent(TextEvent::createForPlainTextPaste(document().domWindow(), pastingText, smartReplace), IGNORE_EXCEPTION);
+ target->dispatchEvent(TextEvent::createForPlainTextPaste(document().domWindow(), pastingText, smartReplace));
}
-void Editor::pasteAsFragment(PassRefPtr<DocumentFragment> pastingFragment, bool smartReplace, bool matchStyle)
+void Editor::pasteAsFragment(Ref<DocumentFragment>&& pastingFragment, bool smartReplace, bool matchStyle, MailBlockquoteHandling respectsMailBlockquote)
{
Node* target = findEventTargetFromSelection();
if (!target)
return;
- target->dispatchEvent(TextEvent::createForFragmentPaste(document().domWindow(), pastingFragment, smartReplace, matchStyle), IGNORE_EXCEPTION);
+ target->dispatchEvent(TextEvent::createForFragmentPaste(document().domWindow(), WTFMove(pastingFragment), smartReplace, matchStyle, respectsMailBlockquote));
}
void Editor::pasteAsPlainTextBypassingDHTML()
@@ -543,7 +446,7 @@ void Editor::pasteAsPlainTextBypassingDHTML()
void Editor::pasteAsPlainTextWithPasteboard(Pasteboard& pasteboard)
{
String text = readPlainTextFromPasteboard(pasteboard);
- if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted))
+ if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertAction::Pasted))
pasteAsPlainText(text, canSmartReplaceWithPasteboard(pasteboard));
}
@@ -554,7 +457,7 @@ String Editor::readPlainTextFromPasteboard(Pasteboard& pasteboard)
return plainTextFromPasteboard(text);
}
-#if !(PLATFORM(MAC) && !PLATFORM(IOS))
+#if !PLATFORM(MAC)
String Editor::plainTextFromPasteboard(const PasteboardPlainText& text)
{
@@ -563,20 +466,6 @@ String Editor::plainTextFromPasteboard(const PasteboardPlainText& text)
#endif
-#if !PLATFORM(MAC) && !PLATFORM(EFL)
-void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
-{
- RefPtr<Range> range = selectedRange();
- if (!range)
- return;
-
- bool chosePlainText;
- RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, *range, allowPlainText, chosePlainText);
- if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
- pasteAsFragment(fragment, canSmartReplaceWithPasteboard(*pasteboard), chosePlainText);
-}
-#endif
-
bool Editor::canSmartReplaceWithPasteboard(Pasteboard& pasteboard)
{
return client() && client()->smartInsertDeleteEnabled() && pasteboard.canSmartReplace();
@@ -589,18 +478,23 @@ bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRef
if (fragment) {
Node* child = fragment->firstChild();
- if (child && fragment->lastChild() == child && child->isCharacterDataNode())
- return client()->shouldInsertText(toCharacterData(child)->data(), replacingDOMRange.get(), givenAction);
+ if (is<CharacterData>(child) && fragment->lastChild() == child)
+ return client()->shouldInsertText(downcast<CharacterData>(*child).data(), replacingDOMRange.get(), givenAction);
}
return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction);
}
-void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle)
+void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle, EditAction editingAction, MailBlockquoteHandling mailBlockquoteHandling)
{
- if (m_frame.selection().isNone() || !m_frame.selection().isContentEditable() || !fragment)
+ VisibleSelection selection = m_frame.selection().selection();
+ if (selection.isNone() || !selection.isContentEditable() || !fragment)
return;
+ AccessibilityReplacedText replacedText;
+ if (AXObjectCache::accessibilityEnabled() && editingAction == EditActionPaste)
+ replacedText = AccessibilityReplacedText(selection);
+
ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::PreventNesting | ReplaceSelectionCommand::SanitizeFragment;
if (selectReplacement)
options |= ReplaceSelectionCommand::SelectReplacement;
@@ -608,116 +502,51 @@ void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment,
options |= ReplaceSelectionCommand::SmartReplace;
if (matchStyle)
options |= ReplaceSelectionCommand::MatchStyle;
- applyCommand(ReplaceSelectionCommand::create(document(), fragment, options, EditActionPaste));
+ if (mailBlockquoteHandling == MailBlockquoteHandling::IgnoreBlockquote)
+ options |= ReplaceSelectionCommand::IgnoreMailBlockquote;
+
+ RefPtr<ReplaceSelectionCommand> command = ReplaceSelectionCommand::create(document(), fragment, options, editingAction);
+ applyCommand(command);
revealSelectionAfterEditingOperation();
- if (m_frame.selection().isInPasswordField() || !isContinuousSpellCheckingEnabled())
+ selection = m_frame.selection().selection();
+ if (selection.isInPasswordField())
return;
- Node* nodeToCheck = m_frame.selection().rootEditableElement();
+
+ if (AXObjectCache::accessibilityEnabled() && editingAction == EditActionPaste) {
+ String text = AccessibilityObject::stringForVisiblePositionRange(command->visibleSelectionForInsertedText());
+ replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypePaste, text, m_frame.selection().selection());
+ command->composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
+ }
+
+ if (!isContinuousSpellCheckingEnabled())
+ return;
+
+ Node* nodeToCheck = selection.rootEditableElement();
if (!nodeToCheck)
return;
RefPtr<Range> rangeToCheck = Range::create(document(), firstPositionInNode(nodeToCheck), lastPositionInNode(nodeToCheck));
- m_spellChecker->requestCheckingFor(SpellCheckRequest::create(resolveTextCheckingTypeMask(TextCheckingTypeSpelling | TextCheckingTypeGrammar), TextCheckingProcessBatch, rangeToCheck, rangeToCheck));
+ m_spellChecker->requestCheckingFor(SpellCheckRequest::create(resolveTextCheckingTypeMask(*nodeToCheck, TextCheckingTypeSpelling | TextCheckingTypeGrammar), TextCheckingProcessBatch, rangeToCheck, rangeToCheck));
}
-void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace)
+void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace, EditAction editingAction)
{
RefPtr<Range> range = selectedRange();
if (!range)
return;
- replaceSelectionWithFragment(createFragmentFromText(*range, text), selectReplacement, smartReplace, true);
+ replaceSelectionWithFragment(createFragmentFromText(*range, text), selectReplacement, smartReplace, true, editingAction);
}
-PassRefPtr<Range> Editor::selectedRange()
+RefPtr<Range> Editor::selectedRange()
{
return m_frame.selection().toNormalizedRange();
}
-#if PLATFORM(IOS)
-void Editor::confirmMarkedText()
-{
- // FIXME: This is a hacky workaround for the keyboard calling this method too late -
- // after the selection and focus have already changed. See <rdar://problem/5975559>
- Element* focused = document().focusedElement();
- Node* composition = compositionNode();
-
- if (composition && focused && focused != composition && !composition->isDescendantOrShadowDescendantOf(focused)) {
- cancelComposition();
- document().setFocusedElement(focused);
- } else
- confirmComposition();
-}
-
-void Editor::setTextAsChildOfElement(const String& text, Element* elem)
-{
- // Clear the composition
- clear();
-
- // Clear the Undo stack, since the operations that follow are not Undoable, and will corrupt the stack. Some day
- // we could make them Undoable, and let callers clear the Undo stack explicitly if they wish.
- clearUndoRedoOperations();
-
- // If the element is empty already and we're not adding text, we can early return and avoid clearing/setting
- // a selection at [0, 0] and the expense involved in creation VisiblePositions.
- if (!elem->firstChild() && text.isEmpty())
- return;
-
- // As a side effect this function sets a caret selection after the inserted content. Much of what
- // follows is more expensive if there is a selection, so clear it since it's going to change anyway.
- m_frame.selection().clear();
-
- // clear out all current children of element
- elem->removeChildren();
-
- if (text.length()) {
- // insert new text
- // remove element from tree while doing it
- // FIXME: The element we're inserting into is often the body element. It seems strange to be removing it
- // (even if it is only temporary). ReplaceSelectionCommand doesn't bother doing this when it inserts
- // content, why should we here?
- ExceptionCode ec;
- RefPtr<Node> parent = elem->parentNode();
- RefPtr<Node> siblingAfter = elem->nextSibling();
- if (parent)
- elem->remove(ec);
-
- RefPtr<Range> context = document().createRange();
- context->selectNodeContents(elem, ec);
- RefPtr<DocumentFragment> fragment = createFragmentFromText(*context.get(), text);
- elem->appendChild(fragment, ec);
-
- // restore element to document
- if (parent) {
- if (siblingAfter)
- parent->insertBefore(elem, siblingAfter.get(), ec);
- else
- parent->appendChild(elem, ec);
- }
- }
-
- // set the selection to the end
- VisibleSelection selection;
-
- Position pos = createLegacyEditingPosition(elem, elem->childNodeCount());
-
- VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
- if (visiblePos.isNull())
- return;
-
- selection.setBase(visiblePos);
- selection.setExtent(visiblePos);
-
- m_frame.selection().setSelection(selection);
-
- client()->respondToChangedContents();
-}
-#endif
-
bool Editor::shouldDeleteRange(Range* range) const
{
- if (!range || range->collapsed(IGNORE_EXCEPTION))
+ if (!range || range->collapsed())
return false;
if (!canDeleteRange(range))
@@ -728,45 +557,31 @@ bool Editor::shouldDeleteRange(Range* range) const
bool Editor::tryDHTMLCopy()
{
- if (m_frame.selection().isInPasswordField())
+ if (m_frame.selection().selection().isInPasswordField())
return false;
- return !dispatchCPPEvent(eventNames().copyEvent, ClipboardWritable);
+ return !dispatchCPPEvent(eventNames().copyEvent, DataTransferAccessPolicy::Writable);
}
bool Editor::tryDHTMLCut()
{
- if (m_frame.selection().isInPasswordField())
+ if (m_frame.selection().selection().isInPasswordField())
return false;
- return !dispatchCPPEvent(eventNames().cutEvent, ClipboardWritable);
+ return !dispatchCPPEvent(eventNames().cutEvent, DataTransferAccessPolicy::Writable);
}
bool Editor::tryDHTMLPaste()
{
- return !dispatchCPPEvent(eventNames().pasteEvent, ClipboardReadable);
+ return !dispatchCPPEvent(eventNames().pasteEvent, DataTransferAccessPolicy::Readable);
}
bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const
{
- return client() && client()->shouldInsertText(text, range, action);
-}
-
-void Editor::notifyComponentsOnChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options)
-{
-#if PLATFORM(IOS)
- // FIXME: Merge this to open source https://bugs.webkit.org/show_bug.cgi?id=38830
- if (m_ignoreCompositionSelectionChange)
- return;
-#endif
+ if (m_frame.mainFrame().loader().shouldSuppressKeyboardInput() && action == EditorInsertAction::Typed)
+ return false;
- if (client())
- client()->respondToChangedSelection(&m_frame);
- setStartNewKillRingSequence(true);
-#if ENABLE(DELETION_UI)
- m_deleteButtonController->respondToChangedSelection(oldSelection);
-#endif
- m_alternativeTextController->respondToChangedSelection(oldSelection, options);
+ return client() && client()->shouldInsertText(text, range, action);
}
void Editor::respondToChangedContents(const VisibleSelection& endingSelection)
@@ -797,8 +612,11 @@ bool Editor::hasBidiSelection() const
} else
startNode = m_frame.selection().selection().visibleStart().deepEquivalent().deprecatedNode();
+ if (!startNode)
+ return false;
+
auto renderer = startNode->renderer();
- while (renderer && !renderer->isRenderBlockFlow())
+ while (renderer && !is<RenderBlockFlow>(*renderer))
renderer = renderer->parent();
if (!renderer)
@@ -807,17 +625,17 @@ bool Editor::hasBidiSelection() const
if (!renderer->style().isLeftToRightDirection())
return true;
- return toRenderBlockFlow(renderer)->containsNonZeroBidiLevel();
+ return downcast<RenderBlockFlow>(*renderer).containsNonZeroBidiLevel();
}
TriState Editor::selectionUnorderedListState() const
{
if (m_frame.selection().isCaret()) {
- if (enclosingNodeWithTag(m_frame.selection().selection().start(), ulTag))
+ if (enclosingElementWithTag(m_frame.selection().selection().start(), ulTag))
return TrueTriState;
} else if (m_frame.selection().isRange()) {
- Node* startNode = enclosingNodeWithTag(m_frame.selection().selection().start(), ulTag);
- Node* endNode = enclosingNodeWithTag(m_frame.selection().selection().end(), ulTag);
+ auto* startNode = enclosingElementWithTag(m_frame.selection().selection().start(), ulTag);
+ auto* endNode = enclosingElementWithTag(m_frame.selection().selection().end(), ulTag);
if (startNode && endNode && startNode == endNode)
return TrueTriState;
}
@@ -828,11 +646,11 @@ TriState Editor::selectionUnorderedListState() const
TriState Editor::selectionOrderedListState() const
{
if (m_frame.selection().isCaret()) {
- if (enclosingNodeWithTag(m_frame.selection().selection().start(), olTag))
+ if (enclosingElementWithTag(m_frame.selection().selection().start(), olTag))
return TrueTriState;
} else if (m_frame.selection().isRange()) {
- Node* startNode = enclosingNodeWithTag(m_frame.selection().selection().start(), olTag);
- Node* endNode = enclosingNodeWithTag(m_frame.selection().selection().end(), olTag);
+ auto* startNode = enclosingElementWithTag(m_frame.selection().selection().start(), olTag);
+ auto* endNode = enclosingElementWithTag(m_frame.selection().selection().end(), olTag);
if (startNode && endNode && startNode == endNode)
return TrueTriState;
}
@@ -870,34 +688,34 @@ bool Editor::canDecreaseSelectionListLevel()
return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(&document());
}
-PassRefPtr<Node> Editor::increaseSelectionListLevel()
+RefPtr<Node> Editor::increaseSelectionListLevel()
{
if (!canEditRichly() || m_frame.selection().isNone())
- return 0;
+ return nullptr;
RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(&document());
revealSelectionAfterEditingOperation();
return newList;
}
-PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered()
+RefPtr<Node> Editor::increaseSelectionListLevelOrdered()
{
if (!canEditRichly() || m_frame.selection().isNone())
- return 0;
+ return nullptr;
RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(&document());
revealSelectionAfterEditingOperation();
- return newList.release();
+ return newList;
}
-PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered()
+RefPtr<Node> Editor::increaseSelectionListLevelUnordered()
{
if (!canEditRichly() || m_frame.selection().isNone())
- return 0;
+ return nullptr;
RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(&document());
revealSelectionAfterEditingOperation();
- return newList.release();
+ return newList;
}
void Editor::decreaseSelectionListLevel()
@@ -916,38 +734,34 @@ void Editor::removeFormattingAndStyle()
void Editor::clearLastEditCommand()
{
- m_lastEditCommand.clear();
+ m_lastEditCommand = nullptr;
}
-#if PLATFORM(IOS)
-// If the selection is adjusted from UIKit without closing the typing, the typing command may
-// have a stale selection.
-void Editor::ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping()
-{
- TypingCommand::ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping(&m_frame, m_frame.selection().selection());
-}
-#endif
// Returns whether caller should continue with "the default processing", which is the same as
// the event handler NOT setting the return value to false
-bool Editor::dispatchCPPEvent(const AtomicString& eventType, ClipboardAccessPolicy policy)
+bool Editor::dispatchCPPEvent(const AtomicString& eventType, DataTransferAccessPolicy policy)
{
Node* target = findEventTargetFromSelection();
if (!target)
return true;
- RefPtr<Clipboard> clipboard = Clipboard::createForCopyAndPaste(policy);
+ auto dataTransfer = DataTransfer::createForCopyAndPaste(policy);
- RefPtr<Event> event = ClipboardEvent::create(eventType, true, true, clipboard);
- target->dispatchEvent(event, IGNORE_EXCEPTION);
+ ClipboardEvent::Init init;
+ init.bubbles = true;
+ init.cancelable = true;
+ init.clipboardData = dataTransfer.ptr();
+ auto event = ClipboardEvent::create(eventType, init, Event::IsTrusted::Yes);
+ target->dispatchEvent(event);
bool noDefaultProcessing = event->defaultPrevented();
- if (noDefaultProcessing && policy == ClipboardWritable) {
- OwnPtr<Pasteboard> pasteboard = Pasteboard::createForCopyAndPaste();
+ if (noDefaultProcessing && policy == DataTransferAccessPolicy::Writable) {
+ auto pasteboard = Pasteboard::createForCopyAndPaste();
pasteboard->clear();
- pasteboard->writePasteboard(clipboard->pasteboard());
+ pasteboard->writePasteboard(dataTransfer->pasteboard());
}
- // invalidate clipboard here for security
- clipboard->setAccessPolicy(ClipboardNumb);
+ // invalidate dataTransfer here for security
+ dataTransfer->setAccessPolicy(DataTransferAccessPolicy::Numb);
return !noDefaultProcessing;
}
@@ -956,9 +770,9 @@ Node* Editor::findEventTargetFrom(const VisibleSelection& selection) const
{
Node* target = selection.start().element();
if (!target)
- target = document().body();
+ target = document().bodyOrFrameset();
if (!target)
- return 0;
+ return nullptr;
return target;
}
@@ -970,18 +784,39 @@ Node* Editor::findEventTargetFromSelection() const
void Editor::applyStyle(StyleProperties* style, EditAction editingAction)
{
- switch (m_frame.selection().selectionType()) {
- case VisibleSelection::NoSelection:
- // do nothing
- break;
+ if (style)
+ applyStyle(EditingStyle::create(style), editingAction);
+}
+
+void Editor::applyStyle(RefPtr<EditingStyle>&& style, EditAction editingAction)
+{
+ if (!style)
+ return;
+
+ auto selectionType = m_frame.selection().selection().selectionType();
+ if (selectionType == VisibleSelection::NoSelection)
+ return;
+
+ String inputTypeName = inputTypeNameForEditingAction(editingAction);
+ String inputEventData = inputEventDataForEditingStyleAndAction(*style, editingAction);
+ RefPtr<Element> element = m_frame.selection().selection().rootEditableElement();
+ if (element && !dispatchBeforeInputEvent(*element, inputTypeName, inputEventData))
+ return;
+
+ switch (selectionType) {
case VisibleSelection::CaretSelection:
- computeAndSetTypingStyle(style, editingAction);
+ computeAndSetTypingStyle(*style, editingAction);
break;
case VisibleSelection::RangeSelection:
- if (style)
- applyCommand(ApplyStyleCommand::create(document(), EditingStyle::create(style).get(), editingAction));
+ applyCommand(ApplyStyleCommand::create(document(), style.get(), editingAction));
+ break;
+ default:
break;
}
+
+ client()->didApplyStyle();
+ if (element)
+ dispatchInputEvent(*element, inputTypeName, inputEventData);
}
bool Editor::shouldApplyStyle(StyleProperties* style, Range* range)
@@ -991,16 +826,23 @@ bool Editor::shouldApplyStyle(StyleProperties* style, Range* range)
void Editor::applyParagraphStyle(StyleProperties* style, EditAction editingAction)
{
- switch (m_frame.selection().selectionType()) {
- case VisibleSelection::NoSelection:
- // do nothing
- break;
- case VisibleSelection::CaretSelection:
- case VisibleSelection::RangeSelection:
- if (style)
- applyCommand(ApplyStyleCommand::create(document(), EditingStyle::create(style).get(), editingAction, ApplyStyleCommand::ForceBlockProperties));
- break;
- }
+ if (!style)
+ return;
+
+ auto selectionType = m_frame.selection().selection().selectionType();
+ if (selectionType == VisibleSelection::NoSelection)
+ return;
+
+ String inputTypeName = inputTypeNameForEditingAction(editingAction);
+ String inputEventData = inputEventDataForEditingStyleAndAction(style, editingAction);
+ RefPtr<Element> element = m_frame.selection().selection().rootEditableElement();
+ if (element && !dispatchBeforeInputEvent(*element, inputTypeName, inputEventData))
+ return;
+
+ applyCommand(ApplyStyleCommand::create(document(), EditingStyle::create(style).ptr(), editingAction, ApplyStyleCommand::ForceBlockProperties));
+ client()->didApplyStyle();
+ if (element)
+ dispatchInputEvent(*element, inputTypeName, inputEventData);
}
void Editor::applyStyleToSelection(StyleProperties* style, EditAction editingAction)
@@ -1008,8 +850,21 @@ void Editor::applyStyleToSelection(StyleProperties* style, EditAction editingAct
if (!style || style->isEmpty() || !canEditRichly())
return;
- if (client() && client()->shouldApplyStyle(style, m_frame.selection().toNormalizedRange().get()))
- applyStyle(style, editingAction);
+ if (!client() || !client()->shouldApplyStyle(style, m_frame.selection().toNormalizedRange().get()))
+ return;
+ applyStyle(style, editingAction);
+}
+
+void Editor::applyStyleToSelection(Ref<EditingStyle>&& style, EditAction editingAction)
+{
+ if (style->isEmpty() || !canEditRichly())
+ return;
+
+ // FIXME: This is wrong for text decorations since m_mutableStyle is empty.
+ if (!client() || !client()->shouldApplyStyle(style->styleWithResolvedTextDecorations().ptr(), m_frame.selection().toNormalizedRange().get()))
+ return;
+
+ applyStyle(WTFMove(style), editingAction);
}
void Editor::applyParagraphStyleToSelection(StyleProperties* style, EditAction editingAction)
@@ -1054,93 +909,147 @@ void Editor::outdent()
applyCommand(IndentOutdentCommand::create(document(), IndentOutdentCommand::Outdent));
}
-static void dispatchEditableContentChangedEvents(PassRefPtr<Element> prpStartRoot, PassRefPtr<Element> prpEndRoot)
+static void notifyTextFromControls(Element* startRoot, Element* endRoot)
+{
+ HTMLTextFormControlElement* startingTextControl = enclosingTextFormControl(firstPositionInOrBeforeNode(startRoot));
+ HTMLTextFormControlElement* endingTextControl = enclosingTextFormControl(firstPositionInOrBeforeNode(endRoot));
+ if (startingTextControl)
+ startingTextControl->didEditInnerTextValue();
+ if (endingTextControl && startingTextControl != endingTextControl)
+ endingTextControl->didEditInnerTextValue();
+}
+
+static bool dispatchBeforeInputEvents(RefPtr<Element> startRoot, RefPtr<Element> endRoot, const AtomicString& inputTypeName, const String& data = { }, RefPtr<DataTransfer>&& dataTransfer = nullptr, const Vector<RefPtr<StaticRange>>& targetRanges = { }, bool cancelable = true)
{
- RefPtr<Element> startRoot = prpStartRoot;
- RefPtr<Element> endRoot = prpEndRoot;
+ bool continueWithDefaultBehavior = true;
if (startRoot)
- startRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), IGNORE_EXCEPTION);
+ continueWithDefaultBehavior &= dispatchBeforeInputEvent(*startRoot, inputTypeName, data, WTFMove(dataTransfer), targetRanges, cancelable);
if (endRoot && endRoot != startRoot)
- endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), IGNORE_EXCEPTION);
+ continueWithDefaultBehavior &= dispatchBeforeInputEvent(*endRoot, inputTypeName, data, WTFMove(dataTransfer), targetRanges, cancelable);
+ return continueWithDefaultBehavior;
+}
+
+static void dispatchInputEvents(RefPtr<Element> startRoot, RefPtr<Element> endRoot, const AtomicString& inputTypeName, const String& data = { }, RefPtr<DataTransfer>&& dataTransfer = nullptr, const Vector<RefPtr<StaticRange>>& targetRanges = { })
+{
+ if (startRoot)
+ dispatchInputEvent(*startRoot, inputTypeName, data, WTFMove(dataTransfer), targetRanges);
+ if (endRoot && endRoot != startRoot)
+ dispatchInputEvent(*endRoot, inputTypeName, data, WTFMove(dataTransfer), targetRanges);
+}
+
+bool Editor::willApplyEditing(CompositeEditCommand& command, Vector<RefPtr<StaticRange>>&& targetRanges) const
+{
+ if (!command.shouldDispatchInputEvents())
+ return true;
+
+ auto* composition = command.composition();
+ if (!composition)
+ return true;
+
+ return dispatchBeforeInputEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement(), command.inputEventTypeName(), command.inputEventData(), command.inputEventDataTransfer(), targetRanges, command.isBeforeInputEventCancelable());
}
void Editor::appliedEditing(PassRefPtr<CompositeEditCommand> cmd)
{
+ LOG(Editing, "Editor %p appliedEditing", this);
+
document().updateLayout();
EditCommandComposition* composition = cmd->composition();
ASSERT(composition);
VisibleSelection newSelection(cmd->endingSelection());
- m_alternativeTextController->respondToAppliedEditing(cmd.get());
+ notifyTextFromControls(composition->startingRootEditableElement(), composition->endingRootEditableElement());
- // Don't clear the typing style with this selection change. We do those things elsewhere if necessary.
- FrameSelection::SetSelectionOptions options = cmd->isDictationCommand() ? FrameSelection::DictationTriggered : 0;
- changeSelectionAfterCommand(newSelection, options);
- dispatchEditableContentChangedEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement());
+ if (cmd->isTopLevelCommand()) {
+ // Don't clear the typing style with this selection change. We do those things elsewhere if necessary.
+ FrameSelection::SetSelectionOptions options = cmd->isDictationCommand() ? FrameSelection::DictationTriggered : 0;
- if (!cmd->preservesTypingStyle())
- m_frame.selection().clearTypingStyle();
+ changeSelectionAfterCommand(newSelection, options);
+ }
- // Command will be equal to last edit command only in the case of typing
- if (m_lastEditCommand.get() == cmd)
- ASSERT(cmd->isTypingCommand());
- else {
- // Only register a new undo command if the command passed in is
- // different from the last command
- m_lastEditCommand = cmd;
- if (client())
- client()->registerUndoStep(m_lastEditCommand->ensureComposition());
+ if (cmd->shouldDispatchInputEvents())
+ dispatchInputEvents(composition->startingRootEditableElement(), composition->endingRootEditableElement(), cmd->inputEventTypeName(), cmd->inputEventData(), cmd->inputEventDataTransfer());
+
+ if (cmd->isTopLevelCommand()) {
+ updateEditorUINowIfScheduled();
+
+ m_alternativeTextController->respondToAppliedEditing(cmd.get());
+
+ if (!cmd->preservesTypingStyle())
+ m_frame.selection().clearTypingStyle();
+
+ // Command will be equal to last edit command only in the case of typing
+ if (m_lastEditCommand.get() == cmd)
+ ASSERT(cmd->isTypingCommand());
+ else {
+ // Only register a new undo command if the command passed in is
+ // different from the last command
+ m_lastEditCommand = cmd;
+ if (client())
+ client()->registerUndoStep(m_lastEditCommand->ensureComposition());
+ }
+ respondToChangedContents(newSelection);
}
+}
- respondToChangedContents(newSelection);
+bool Editor::willUnapplyEditing(const EditCommandComposition& composition) const
+{
+ return dispatchBeforeInputEvents(composition.startingRootEditableElement(), composition.endingRootEditableElement(), "historyUndo");
}
-void Editor::unappliedEditing(PassRefPtr<EditCommandComposition> cmd)
+void Editor::unappliedEditing(EditCommandComposition& composition)
{
document().updateLayout();
- VisibleSelection newSelection(cmd->startingSelection());
- changeSelectionAfterCommand(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle);
- dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
+ notifyTextFromControls(composition.startingRootEditableElement(), composition.endingRootEditableElement());
- m_alternativeTextController->respondToUnappliedEditing(cmd.get());
+ VisibleSelection newSelection(composition.startingSelection());
+ changeSelectionAfterCommand(newSelection, FrameSelection::defaultSetSelectionOptions());
+ dispatchInputEvents(composition.startingRootEditableElement(), composition.endingRootEditableElement(), "historyUndo");
- m_lastEditCommand = 0;
- if (client())
- client()->registerRedoStep(cmd);
+ updateEditorUINowIfScheduled();
+
+ m_alternativeTextController->respondToUnappliedEditing(&composition);
+
+ m_lastEditCommand = nullptr;
+ if (auto* client = this->client())
+ client->registerRedoStep(composition);
respondToChangedContents(newSelection);
}
-void Editor::reappliedEditing(PassRefPtr<EditCommandComposition> cmd)
+bool Editor::willReapplyEditing(const EditCommandComposition& composition) const
+{
+ return dispatchBeforeInputEvents(composition.startingRootEditableElement(), composition.endingRootEditableElement(), "historyRedo");
+}
+
+void Editor::reappliedEditing(EditCommandComposition& composition)
{
document().updateLayout();
- VisibleSelection newSelection(cmd->endingSelection());
- changeSelectionAfterCommand(newSelection, FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle);
- dispatchEditableContentChangedEvents(cmd->startingRootEditableElement(), cmd->endingRootEditableElement());
+ notifyTextFromControls(composition.startingRootEditableElement(), composition.endingRootEditableElement());
- m_lastEditCommand = 0;
- if (client())
- client()->registerUndoStep(cmd);
+ VisibleSelection newSelection(composition.endingSelection());
+ changeSelectionAfterCommand(newSelection, FrameSelection::defaultSetSelectionOptions());
+ dispatchInputEvents(composition.startingRootEditableElement(), composition.endingRootEditableElement(), "historyRedo");
+
+ updateEditorUINowIfScheduled();
+
+ m_lastEditCommand = nullptr;
+ if (auto* client = this->client())
+ client->registerUndoStep(composition);
respondToChangedContents(newSelection);
}
Editor::Editor(Frame& frame)
: m_frame(frame)
-#if ENABLE(DELETION_UI)
- , m_deleteButtonController(adoptPtr(new DeleteButtonController(frame)))
+ , m_killRing(std::make_unique<KillRing>())
+ , m_spellChecker(std::make_unique<SpellChecker>(frame))
+ , m_alternativeTextController(std::make_unique<AlternativeTextController>(frame))
+ , m_editorUIUpdateTimer(*this, &Editor::editorUIUpdateTimerFired)
+#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
+ , m_telephoneNumberDetectionUpdateTimer(*this, &Editor::scanSelectionForTelephoneNumbers)
#endif
- , m_ignoreCompositionSelectionChange(false)
- , m_shouldStartNewKillRingSequence(false)
- // This is off by default, since most editors want this behavior (this matches IE but not FF).
- , m_shouldStyleWithCSS(false)
- , m_killRing(adoptPtr(new KillRing))
- , m_spellChecker(adoptPtr(new SpellChecker(frame)))
- , m_alternativeTextController(adoptPtr(new AlternativeTextController(frame)))
- , m_areMarkedTextMatchesHighlighted(false)
- , m_defaultParagraphSeparator(EditorParagraphSeparatorIsDiv)
- , m_overwriteModeEnabled(false)
{
}
@@ -1150,19 +1059,19 @@ Editor::~Editor()
void Editor::clear()
{
- m_compositionNode = 0;
+ if (m_compositionNode) {
+ m_compositionNode = nullptr;
+ if (EditorClient* client = this->client())
+ client->discardedComposition(&m_frame);
+ }
m_customCompositionUnderlines.clear();
m_shouldStyleWithCSS = false;
m_defaultParagraphSeparator = EditorParagraphSeparatorIsDiv;
-
-#if ENABLE(DELETION_UI)
- m_deleteButtonController = adoptPtr(new DeleteButtonController(m_frame));
-#endif
}
-bool Editor::insertText(const String& text, Event* triggeringEvent)
+bool Editor::insertText(const String& text, Event* triggeringEvent, TextEventInputType inputType)
{
- return m_frame.eventHandler().handleTextInputEvent(text, triggeringEvent);
+ return m_frame.eventHandler().handleTextInputEvent(text, triggeringEvent, inputType);
}
bool Editor::insertTextForConfirmedComposition(const String& text)
@@ -1185,7 +1094,7 @@ bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectIn
return false;
RefPtr<Range> range = selection.toNormalizedRange();
- if (!shouldInsertText(text, range.get(), EditorInsertActionTyped))
+ if (!shouldInsertText(text, range.get(), EditorInsertAction::Typed))
return true;
updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0]));
@@ -1209,20 +1118,28 @@ bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectIn
// Insert the text
if (triggeringEvent && triggeringEvent->isDictation())
- DictationCommand::insertText(&document.get(), text, triggeringEvent->dictationAlternatives(), selection);
+ DictationCommand::insertText(document, 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);
+ if (triggeringEvent && triggeringEvent->isAutocompletion())
+ options |= TypingCommand::IsAutocompletion;
+ TypingCommand::insertText(document, text, selection, options, triggeringEvent && triggeringEvent->isComposition() ? TypingCommand::TextCompositionFinal : TypingCommand::TextCompositionNone);
}
// Reveal the current selection
if (Frame* editedFrame = document->frame())
- if (Page* page = editedFrame->page())
- page->focusController().focusedOrMainFrame().selection().revealSelection(ScrollAlignment::alignCenterIfNeeded);
+ if (Page* page = editedFrame->page()) {
+#if PLATFORM(IOS)
+ SelectionRevealMode revealMode = SelectionRevealMode::RevealUpToMainFrame;
+#else
+ SelectionRevealMode revealMode = SelectionRevealMode::Reveal;
+#endif
+ page->focusController().focusedOrMainFrame().selection().revealSelection(revealMode, ScrollAlignment::alignCenterIfNeeded);
+ }
}
}
@@ -1234,7 +1151,7 @@ bool Editor::insertLineBreak()
if (!canEdit())
return false;
- if (!shouldInsertText("\n", m_frame.selection().toNormalizedRange().get(), EditorInsertActionTyped))
+ if (!shouldInsertText("\n", m_frame.selection().toNormalizedRange().get(), EditorInsertAction::Typed))
return true;
VisiblePosition caret = m_frame.selection().selection().visibleStart();
@@ -1254,7 +1171,7 @@ bool Editor::insertParagraphSeparator()
if (!canEditRichly())
return insertLineBreak();
- if (!shouldInsertText("\n", m_frame.selection().toNormalizedRange().get(), EditorInsertActionTyped))
+ if (!shouldInsertText("\n", m_frame.selection().toNormalizedRange().get(), EditorInsertAction::Typed))
return true;
VisiblePosition caret = m_frame.selection().selection().visibleStart();
@@ -1266,6 +1183,14 @@ bool Editor::insertParagraphSeparator()
return true;
}
+bool Editor::insertParagraphSeparatorInQuotedContent()
+{
+ // FIXME: Why is this missing calls to canEdit, canEditRichly, etc.?
+ TypingCommand::insertParagraphSeparatorInQuotedContent(document());
+ revealSelectionAfterEditingOperation();
+ return true;
+}
+
void Editor::cut()
{
if (tryDHTMLCut())
@@ -1275,24 +1200,7 @@ void Editor::cut()
return;
}
- // FIXME: This should share more code with the copy function; there is a lot of overlap.
- RefPtr<Range> selection = selectedRange();
- willWriteSelectionToPasteboard(selection);
- if (shouldDeleteRange(selection.get())) {
- updateMarkersForWordsAffectedByEditing(true);
- if (enclosingTextFormControl(m_frame.selection().start()))
- Pasteboard::createForCopyAndPaste()->writePlainText(selectedTextForClipboard(), canSmartCopyOrDelete() ? Pasteboard::CanSmartReplace : Pasteboard::CannotSmartReplace);
- else {
-#if PLATFORM(MAC) || PLATFORM(EFL)
- writeSelectionToPasteboard(*Pasteboard::createForCopyAndPaste());
-#else
- // FIXME: Convert all other platforms to match Mac and delete this.
- Pasteboard::createForCopyAndPaste()->writeSelection(*selection, canSmartCopyOrDelete(), m_frame, IncludeImageAltTextForClipboard);
-#endif
- }
- didWriteSelectionToPasteboard();
- deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
- }
+ performCutOrCopy(CutAction);
}
void Editor::copy()
@@ -1304,28 +1212,64 @@ void Editor::copy()
return;
}
- willWriteSelectionToPasteboard(selectedRange());
- if (enclosingTextFormControl(m_frame.selection().start())) {
- Pasteboard::createForCopyAndPaste()->writePlainText(selectedTextForClipboard(),
- canSmartCopyOrDelete() ? Pasteboard::CanSmartReplace : Pasteboard::CannotSmartReplace);
- } else {
- if (HTMLImageElement* imageElement = imageElementFromImageDocument(document())) {
-#if PLATFORM(MAC) || PLATFORM(EFL)
+ performCutOrCopy(CopyAction);
+}
+
+void Editor::postTextStateChangeNotificationForCut(const String& text, const VisibleSelection& selection)
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ if (!text.length())
+ return;
+ AXObjectCache* cache = document().existingAXObjectCache();
+ if (!cache)
+ return;
+ cache->postTextStateChangeNotification(selection.start().anchorNode(), AXTextEditTypeCut, text, selection.start());
+}
+
+void Editor::performCutOrCopy(EditorActionSpecifier action)
+{
+ RefPtr<Range> selection = selectedRange();
+ willWriteSelectionToPasteboard(selection);
+ if (action == CutAction) {
+ if (!shouldDeleteRange(selection.get()))
+ return;
+
+ updateMarkersForWordsAffectedByEditing(true);
+ }
+
+ if (enclosingTextFormControl(m_frame.selection().selection().start()))
+ Pasteboard::createForCopyAndPaste()->writePlainText(selectedTextForDataTransfer(), canSmartCopyOrDelete() ? Pasteboard::CanSmartReplace : Pasteboard::CannotSmartReplace);
+ else {
+ HTMLImageElement* imageElement = nullptr;
+ if (action == CopyAction)
+ imageElement = imageElementFromImageDocument(document());
+
+ if (imageElement) {
+#if PLATFORM(COCOA) || PLATFORM(GTK)
writeImageToPasteboard(*Pasteboard::createForCopyAndPaste(), *imageElement, document().url(), document().title());
#else
Pasteboard::createForCopyAndPaste()->writeImage(*imageElement, document().url(), document().title());
#endif
} else {
-#if PLATFORM(MAC) || PLATFORM(EFL)
+#if PLATFORM(COCOA) || PLATFORM(GTK)
writeSelectionToPasteboard(*Pasteboard::createForCopyAndPaste());
#else
// FIXME: Convert all other platforms to match Mac and delete this.
- Pasteboard::createForCopyAndPaste()->writeSelection(*selectedRange(), canSmartCopyOrDelete(), m_frame, IncludeImageAltTextForClipboard);
+ Pasteboard::createForCopyAndPaste()->writeSelection(*selection, canSmartCopyOrDelete(), m_frame, IncludeImageAltTextForDataTransfer);
#endif
}
}
didWriteSelectionToPasteboard();
+ if (action == CutAction) {
+ String text;
+ if (AXObjectCache::accessibilityEnabled())
+ text = AccessibilityObject::stringForVisiblePositionRange(m_frame.selection().selection());
+ deleteSelectionWithSmartDelete(canSmartCopyOrDelete(), EditActionCut);
+ if (AXObjectCache::accessibilityEnabled())
+ postTextStateChangeNotificationForCut(text, m_frame.selection().selection());
+ }
}
void Editor::paste()
@@ -1340,9 +1284,8 @@ void Editor::paste(Pasteboard& pasteboard)
if (!canPaste())
return;
updateMarkersForWordsAffectedByEditing(false);
- CachedResourceLoader* loader = document().cachedResourceLoader();
- ResourceCacheValidationSuppressor validationSuppressor(loader);
- if (m_frame.selection().isContentRichlyEditable())
+ ResourceCacheValidationSuppressor validationSuppressor(document().cachedResourceLoader());
+ if (m_frame.selection().selection().isContentRichlyEditable())
pasteWithPasteboard(&pasteboard, true);
else
pasteAsPlainTextWithPasteboard(pasteboard);
@@ -1365,7 +1308,7 @@ void Editor::performDelete()
return;
}
- addToKillRing(selectedRange().get(), false);
+ addRangeToKillRing(*selectedRange().get(), KillRingInsertionMode::AppendText);
deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
// clear the "start new kill ring sequence" setting, because it was set to true
@@ -1383,12 +1326,12 @@ void Editor::simplifyMarkup(Node* startNode, Node* endNode)
// check if start node is before endNode
Node* node = startNode;
while (node && node != endNode)
- node = NodeTraversal::next(node);
+ node = NodeTraversal::next(*node);
if (!node)
return;
}
- applyCommand(SimplifyMarkupCommand::create(document(), startNode, (endNode) ? NodeTraversal::next(endNode) : 0));
+ applyCommand(SimplifyMarkupCommand::create(document(), startNode, endNode ? NodeTraversal::next(*endNode) : nullptr));
}
void Editor::copyURL(const URL& url, const String& title)
@@ -1402,7 +1345,7 @@ void Editor::copyURL(const URL& url, const String& title, Pasteboard& pasteboard
pasteboardURL.url = url;
pasteboardURL.title = title;
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
fillInUserVisibleForm(pasteboardURL);
#endif
@@ -1410,6 +1353,7 @@ void Editor::copyURL(const URL& url, const String& title, Pasteboard& pasteboard
}
#if !PLATFORM(IOS)
+
void Editor::copyImage(const HitTestResult& result)
{
Element* element = result.innerNonSharedElement();
@@ -1420,12 +1364,13 @@ void Editor::copyImage(const HitTestResult& result)
if (url.isEmpty())
url = result.absoluteImageURL();
-#if PLATFORM(MAC) || PLATFORM(EFL)
+#if PLATFORM(COCOA) || PLATFORM(GTK)
writeImageToPasteboard(*Pasteboard::createForCopyAndPaste(), *element, url, result.altDisplayString());
#else
Pasteboard::createForCopyAndPaste()->writeImage(*element, url, result.altDisplayString());
#endif
}
+
#endif
bool Editor::isContinuousSpellCheckingEnabled() const
@@ -1644,11 +1589,18 @@ void Editor::setBaseWritingDirection(WritingDirection direction)
#endif
Element* focusedElement = document().focusedElement();
- if (focusedElement && isHTMLTextFormControlElement(*focusedElement)) {
+ if (is<HTMLTextFormControlElement>(focusedElement)) {
if (direction == NaturalWritingDirection)
return;
- toHTMLElement(focusedElement)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl");
- focusedElement->dispatchInputEvent();
+
+ auto& focusedFormElement = downcast<HTMLTextFormControlElement>(*focusedElement);
+ auto directionValue = direction == LeftToRightWritingDirection ? "ltr" : "rtl";
+ auto writingDirectionInputTypeName = inputTypeNameForEditingAction(EditActionSetWritingDirection);
+ if (!dispatchBeforeInputEvent(focusedFormElement, writingDirectionInputTypeName, directionValue))
+ return;
+
+ focusedFormElement.setAttributeWithoutSynchronization(dirAttr, directionValue);
+ dispatchInputEvent(focusedFormElement, writingDirectionInputTypeName, directionValue);
document().updateStyleIfNeeded();
return;
}
@@ -1742,29 +1694,25 @@ void Editor::setComposition(const String& text, SetCompositionMode mode)
else
selectComposition();
+ m_compositionNode = nullptr;
+ m_customCompositionUnderlines.clear();
+
if (m_frame.selection().isNone()) {
setIgnoreCompositionSelectionChange(false);
return;
}
-
- // Dispatch a compositionend event to the focused node.
- // We should send this event before sending a TextEvent as written in Section 6.2.2 and 6.2.3 of
- // the DOM Event specification.
- if (Element* target = document().focusedElement()) {
- RefPtr<CompositionEvent> event = CompositionEvent::create(eventNames().compositionendEvent, document().domWindow(), text);
- target->dispatchEvent(event.release(), IGNORE_EXCEPTION);
- }
-
- // 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() && mode != CancelComposition)
- TypingCommand::deleteSelection(document(), 0);
- m_compositionNode = 0;
- m_customCompositionUnderlines.clear();
+ // Always delete the current composition before inserting the finalized composition text if we're confirming our composition.
+ // Our default behavior (if the beforeinput event is not prevented) is to insert the finalized composition text back in.
+ // We pass TypingCommand::TextCompositionPending here to indicate that we are deleting the pending composition.
+ if (mode != CancelComposition)
+ TypingCommand::deleteSelection(document(), 0, TypingCommand::TextCompositionPending);
insertTextForConfirmedComposition(text);
+ if (auto* target = document().focusedElement())
+ target->dispatchEvent(CompositionEvent::create(eventNames().compositionendEvent, document().domWindow(), text));
+
if (mode == CancelComposition) {
// An open typing command that disagrees about current selection would cause issues with typing later on.
TypingCommand::closeTyping(&m_frame);
@@ -1775,6 +1723,8 @@ void Editor::setComposition(const String& text, SetCompositionMode mode)
void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
{
+ Ref<Frame> protection(m_frame);
+
UserTypingGestureIndicator typingGestureIndicator(m_frame);
setIgnoreCompositionSelectionChange(true);
@@ -1791,6 +1741,18 @@ void Editor::setComposition(const String& text, const Vector<CompositionUnderlin
return;
}
+ String originalText = selectedText();
+ bool isStartingToRecomposeExistingRange = !text.isEmpty() && selectionStart < selectionEnd && !hasComposition();
+ if (isStartingToRecomposeExistingRange) {
+ // We pass TypingCommand::TextCompositionFinal here to indicate that we are removing composition text that has been finalized.
+ TypingCommand::deleteSelection(document(), 0, TypingCommand::TextCompositionFinal);
+ const VisibleSelection& currentSelection = m_frame.selection().selection();
+ if (currentSelection.isRange()) {
+ // If deletion was prevented, then we need to collapse the selection to the end so that the original text will not be recomposed.
+ m_frame.selection().setSelection({ currentSelection.end(), currentSelection.end() });
+ }
+ }
+
#if PLATFORM(IOS)
client()->startDelayingAndCoalescingContentChangeNotifications();
#endif
@@ -1817,47 +1779,46 @@ void Editor::setComposition(const String& text, const Vector<CompositionUnderlin
// We should send a compositionstart event only when the given text is not empty because this
// function doesn't create a composition node when the text is empty.
if (!text.isEmpty()) {
- target->dispatchEvent(CompositionEvent::create(eventNames().compositionstartEvent, document().domWindow(), selectedText()));
+ target->dispatchEvent(CompositionEvent::create(eventNames().compositionstartEvent, document().domWindow(), originalText));
event = CompositionEvent::create(eventNames().compositionupdateEvent, document().domWindow(), text);
}
- } else {
- if (!text.isEmpty())
- event = CompositionEvent::create(eventNames().compositionupdateEvent, document().domWindow(), text);
- else
- event = CompositionEvent::create(eventNames().compositionendEvent, document().domWindow(), text);
- }
- if (event.get())
- target->dispatchEvent(event, IGNORE_EXCEPTION);
+ } else if (!text.isEmpty())
+ event = CompositionEvent::create(eventNames().compositionupdateEvent, document().domWindow(), text);
+
+ if (event)
+ target->dispatchEvent(*event);
}
// 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())
- TypingCommand::deleteSelection(document(), TypingCommand::PreventSpellChecking);
+ if (text.isEmpty()) {
+ TypingCommand::deleteSelection(document(), TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionPending);
+ if (target)
+ target->dispatchEvent(CompositionEvent::create(eventNames().compositionendEvent, document().domWindow(), text));
+ }
- m_compositionNode = 0;
+ m_compositionNode = nullptr;
m_customCompositionUnderlines.clear();
if (!text.isEmpty()) {
- TypingCommand::insertText(document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate);
+ TypingCommand::insertText(document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionPending);
// Find out what node has the composition now.
- Position base = m_frame.selection().base().downstream();
- Position extent = m_frame.selection().extent();
+ Position base = m_frame.selection().selection().base().downstream();
+ Position extent = m_frame.selection().selection().extent();
Node* baseNode = base.deprecatedNode();
unsigned baseOffset = base.deprecatedEditingOffset();
Node* extentNode = extent.deprecatedNode();
unsigned extentOffset = extent.deprecatedEditingOffset();
- if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
- m_compositionNode = toText(baseNode);
+ if (is<Text>(baseNode) && baseNode == extentNode && baseOffset + text.length() == extentOffset) {
+ m_compositionNode = downcast<Text>(baseNode);
m_compositionStart = baseOffset;
m_compositionEnd = extentOffset;
m_customCompositionUnderlines = underlines;
- size_t numUnderlines = m_customCompositionUnderlines.size();
- for (size_t i = 0; i < numUnderlines; ++i) {
- m_customCompositionUnderlines[i].startOffset += baseOffset;
- m_customCompositionUnderlines[i].endOffset += baseOffset;
+ for (auto& underline : m_customCompositionUnderlines) {
+ underline.startOffset += baseOffset;
+ underline.endOffset += baseOffset;
}
if (baseNode->renderer())
baseNode->renderer()->repaint();
@@ -1907,8 +1868,11 @@ void Editor::learnSpelling()
}
#if !PLATFORM(IOS)
+
void Editor::advanceToNextMisspelling(bool startBeforeSelection)
{
+ Ref<Frame> protection(m_frame);
+
// The basic approach is to search in two phases - from the selection end to the end of the doc, and
// then we wrap and search from the doc start to (approximately) where we started.
@@ -1937,19 +1901,21 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// when spell checking the whole document before sending the message.
// In that case the document might not be editable, but there are editable pockets that need to be spell checked.
- position = firstEditablePositionAfterPositionInRoot(position, document().documentElement()).deepEquivalent();
+ position = VisiblePosition(firstEditablePositionAfterPositionInRoot(position, document().documentElement())).deepEquivalent();
if (position.isNull())
return;
Position rangeCompliantPosition = position.parentAnchoredEquivalent();
- spellingSearchRange->setStart(rangeCompliantPosition.deprecatedNode(), rangeCompliantPosition.deprecatedEditingOffset(), IGNORE_EXCEPTION);
+ if (rangeCompliantPosition.deprecatedNode())
+ spellingSearchRange->setStart(*rangeCompliantPosition.deprecatedNode(), rangeCompliantPosition.deprecatedEditingOffset());
startedWithSelection = false; // won't need to wrap
}
// topNode defines the whole range we want to operate on
- Node* topNode = highestEditableRoot(position);
+ auto* topNode = highestEditableRoot(position);
// FIXME: lastOffsetForEditing() is wrong here if editingIgnoresContent(highestEditableRoot()) returns true (e.g. a <table>)
- spellingSearchRange->setEnd(topNode, lastOffsetForEditing(topNode), IGNORE_EXCEPTION);
+ if (topNode)
+ spellingSearchRange->setEnd(*topNode, lastOffsetForEditing(*topNode));
// If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
// at a word boundary. Going back by one char and then forward by a word does the trick.
@@ -1960,7 +1926,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// else we were already at the start of the editable node
}
- if (spellingSearchRange->collapsed(IGNORE_EXCEPTION))
+ if (spellingSearchRange->collapsed())
return; // nothing to search in
// Get the spell checker if it is available
@@ -1970,7 +1936,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// We go to the end of our first range instead of the start of it, just to be sure
// we don't get foiled by any word boundary problems at the start. It means we might
// do a tiny bit more searching.
- Node* searchEndNodeAfterWrap = spellingSearchRange->endContainer();
+ Node& searchEndNodeAfterWrap = spellingSearchRange->endContainer();
int searchEndOffsetAfterWrap = spellingSearchRange->endOffset();
int misspellingOffset = 0;
@@ -1985,7 +1951,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
String foundItem;
RefPtr<Range> firstMisspellingRange;
if (unifiedTextCheckerEnabled()) {
- grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION);
+ grammarSearchRange = spellingSearchRange->cloneRange();
foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
if (isSpelling) {
misspelledWord = foundItem;
@@ -1998,12 +1964,12 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
#if USE(GRAMMAR_CHECKING)
- grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION);
+ grammarSearchRange = spellingSearchRange->cloneRange();
if (!misspelledWord.isEmpty()) {
// Stop looking at start of next misspelled word
- CharacterIterator chars(grammarSearchRange.get());
+ CharacterIterator chars(*grammarSearchRange);
chars.advance(misspellingOffset);
- grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset(), IGNORE_EXCEPTION);
+ grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset());
}
if (isGrammarCheckingEnabled())
@@ -2014,12 +1980,13 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// If we found neither bad grammar nor a misspelled word, wrap and try again (but don't bother if we started at the beginning of the
// block rather than at a selection).
if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
- spellingSearchRange->setStart(topNode, 0, IGNORE_EXCEPTION);
+ if (topNode)
+ spellingSearchRange->setStart(*topNode, 0);
// going until the end of the very first chunk we tested is far enough
- spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, IGNORE_EXCEPTION);
+ spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap);
if (unifiedTextCheckerEnabled()) {
- grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION);
+ grammarSearchRange = spellingSearchRange->cloneRange();
foundItem = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspellingOrBadGrammar(isGrammarCheckingEnabled(), isSpelling, foundOffset, grammarDetail);
if (isSpelling) {
misspelledWord = foundItem;
@@ -2032,12 +1999,12 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
misspelledWord = TextCheckingHelper(client(), spellingSearchRange).findFirstMisspelling(misspellingOffset, false, firstMisspellingRange);
#if USE(GRAMMAR_CHECKING)
- grammarSearchRange = spellingSearchRange->cloneRange(IGNORE_EXCEPTION);
+ grammarSearchRange = spellingSearchRange->cloneRange();
if (!misspelledWord.isEmpty()) {
// Stop looking at start of next misspelled word
- CharacterIterator chars(grammarSearchRange.get());
+ CharacterIterator chars(*grammarSearchRange);
chars.advance(misspellingOffset);
- grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset(), IGNORE_EXCEPTION);
+ grammarSearchRange->setEnd(chars.range()->startContainer(), chars.range()->startOffset());
}
if (isGrammarCheckingEnabled())
@@ -2060,7 +2027,7 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length);
- m_frame.selection().setSelection(VisibleSelection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
+ m_frame.selection().setSelection(VisibleSelection(*badGrammarRange, SEL_DEFAULT_AFFINITY));
m_frame.selection().revealSelection();
client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail);
@@ -2072,13 +2039,14 @@ void Editor::advanceToNextMisspelling(bool startBeforeSelection)
// a marker so we draw the red squiggle later.
RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length());
- m_frame.selection().setSelection(VisibleSelection(misspellingRange.get(), DOWNSTREAM));
+ m_frame.selection().setSelection(VisibleSelection(*misspellingRange, DOWNSTREAM));
m_frame.selection().revealSelection();
client()->updateSpellingUIWithMisspelledWord(misspelledWord);
document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling);
}
}
+
#endif // !PLATFORM(IOS)
String Editor::misspelledWordAtCaretOrRange(Node* clickedNode) const
@@ -2106,7 +2074,7 @@ String Editor::misspelledWordAtCaretOrRange(Node* clickedNode) const
int wordLength = word.length();
int misspellingLocation = -1;
int misspellingLength = 0;
- textChecker()->checkSpellingOfString(word.deprecatedCharacters(), wordLength, &misspellingLocation, &misspellingLength);
+ textChecker()->checkSpellingOfString(word, &misspellingLocation, &misspellingLength);
return misspellingLength == wordLength ? word : String();
}
@@ -2120,7 +2088,7 @@ String Editor::misspelledSelectionString() const
int misspellingLocation = -1;
int misspellingLength = 0;
- textChecker()->checkSpellingOfString(selectedString.deprecatedCharacters(), length, &misspellingLocation, &misspellingLength);
+ textChecker()->checkSpellingOfString(selectedString, &misspellingLocation, &misspellingLength);
// The selection only counts as misspelled if the selected text is exactly one misspelled word
if (misspellingLength != length)
@@ -2153,7 +2121,7 @@ Vector<String> Editor::guessesForMisspelledWord(const String& word) const
Vector<String> guesses;
if (client())
- textChecker()->getGuessesForWord(word, String(), guesses);
+ textChecker()->getGuessesForWord(word, String(), m_frame.selection().selection(), guesses);
return guesses;
}
@@ -2161,13 +2129,13 @@ Vector<String> Editor::guessesForMisspelledOrUngrammatical(bool& misspelled, boo
{
if (unifiedTextCheckerEnabled()) {
RefPtr<Range> range;
- FrameSelection& frameSelection = m_frame.selection();
- if (frameSelection.isCaret() && behavior().shouldAllowSpellingSuggestionsWithoutSelection()) {
- VisibleSelection wordSelection = VisibleSelection(frameSelection.base());
+ VisibleSelection selection = m_frame.selection().selection();
+ if (selection.isCaret() && behavior().shouldAllowSpellingSuggestionsWithoutSelection()) {
+ VisibleSelection wordSelection = VisibleSelection(selection.base());
wordSelection.expandUsingGranularity(WordGranularity);
range = wordSelection.toNormalizedRange();
} else
- range = frameSelection.toNormalizedRange();
+ range = selection.toNormalizedRange();
if (!range)
return Vector<String>();
return TextCheckingHelper(client(), range).guessesForMisspelledOrUngrammaticalRange(isGrammarCheckingEnabled(), misspelled, ungrammatical);
@@ -2224,6 +2192,8 @@ void Editor::markMisspellingsAndBadGrammar(const VisibleSelection &movingSelecti
void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping, bool doReplacement)
{
+ Ref<Frame> protection(m_frame);
+
#if PLATFORM(IOS)
UNUSED_PARAM(selectionAfterTyping);
UNUSED_PARAM(doReplacement);
@@ -2289,19 +2259,19 @@ void Editor::markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart,
// If autocorrected word is non empty, replace the misspelled word by this word.
if (!autocorrectedString.isEmpty()) {
- VisibleSelection newSelection(misspellingRange.get(), DOWNSTREAM);
+ VisibleSelection newSelection(*misspellingRange, DOWNSTREAM);
if (newSelection != m_frame.selection().selection()) {
if (!m_frame.selection().shouldChangeSelection(newSelection))
return;
m_frame.selection().setSelection(newSelection);
}
- if (!m_frame.editor().shouldInsertText(autocorrectedString, misspellingRange.get(), EditorInsertActionTyped))
+ if (!m_frame.editor().shouldInsertText(autocorrectedString, misspellingRange.get(), EditorInsertAction::Typed))
return;
- m_frame.editor().replaceSelectionWithText(autocorrectedString, false, false);
+ m_frame.editor().replaceSelectionWithText(autocorrectedString, false, false, EditActionInsert);
// Reset the charet one character further.
- m_frame.selection().moveTo(m_frame.selection().end());
+ m_frame.selection().moveTo(m_frame.selection().selection().end());
m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
}
@@ -2329,11 +2299,11 @@ void Editor::markMisspellingsOrBadGrammar(const VisibleSelection& selection, boo
return;
// If we're not in an editable node, bail.
- Node* editableNode = searchRange->startContainer();
- if (!editableNode || !editableNode->hasEditableStyle())
+ Node& editableNode = searchRange->startContainer();
+ if (!editableNode.hasEditableStyle())
return;
- if (!isSpellCheckingEnabledFor(editableNode))
+ if (!isSpellCheckingEnabledFor(&editableNode))
return;
// Get the spell checker if it is available
@@ -2362,15 +2332,19 @@ bool Editor::isSpellCheckingEnabledFor(Node* node) const
{
if (!node)
return false;
- const Element* focusedElement = node->isElementNode() ? toElement(node) : node->parentElement();
- if (!focusedElement)
+ Element* element = is<Element>(*node) ? downcast<Element>(node) : node->parentElement();
+ if (!element)
return false;
- return focusedElement->isSpellCheckingEnabled();
+ if (element->isInUserAgentShadowTree()) {
+ if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(firstPositionInOrBeforeNode(element)))
+ return textControl->isSpellCheckingEnabled();
+ }
+ return element->isSpellCheckingEnabled();
}
bool Editor::isSpellCheckingEnabledInFocusedNode() const
{
- return isSpellCheckingEnabledFor(m_frame.selection().start().deprecatedNode());
+ return isSpellCheckingEnabledFor(m_frame.selection().selection().start().deprecatedNode());
}
void Editor::markMisspellings(const VisibleSelection& selection, RefPtr<Range>& firstMisspellingRange)
@@ -2403,33 +2377,33 @@ void Editor::markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask textC
return;
// If we're not in an editable node, bail.
- Node* editableNode = spellingRange->startContainer();
- if (!editableNode || !editableNode->hasEditableStyle())
+ Node& editableNode = spellingRange->startContainer();
+ if (!editableNode.hasEditableStyle())
return;
- if (!isSpellCheckingEnabledFor(editableNode))
+ if (!isSpellCheckingEnabledFor(&editableNode))
return;
Range* rangeToCheck = shouldMarkGrammar ? grammarRange : spellingRange;
TextCheckingParagraph paragraphToCheck(rangeToCheck);
- if (paragraphToCheck.isRangeEmpty() || paragraphToCheck.isEmpty())
+ if (paragraphToCheck.isEmpty())
return;
RefPtr<Range> paragraphRange = paragraphToCheck.paragraphRange();
bool asynchronous = m_frame.settings().asynchronousSpellCheckingEnabled() && !shouldShowCorrectionPanel;
// In asynchronous mode, we intentionally check paragraph-wide sentence.
- RefPtr<SpellCheckRequest> request = SpellCheckRequest::create(resolveTextCheckingTypeMask(textCheckingOptions), TextCheckingProcessIncremental, asynchronous ? paragraphRange : rangeToCheck, paragraphRange);
+ const auto resolvedOptions = resolveTextCheckingTypeMask(editableNode, textCheckingOptions);
+ auto request = SpellCheckRequest::create(resolvedOptions, TextCheckingProcessIncremental, asynchronous ? paragraphRange : rangeToCheck, paragraphRange);
if (asynchronous) {
- m_spellChecker->requestCheckingFor(request);
+ m_spellChecker->requestCheckingFor(WTFMove(request));
return;
}
Vector<TextCheckingResult> results;
- checkTextOfParagraph(textChecker(), paragraphToCheck.textDeprecatedCharacters(), paragraphToCheck.textLength(),
- resolveTextCheckingTypeMask(textCheckingOptions), results);
- markAndReplaceFor(request, results);
+ checkTextOfParagraph(*textChecker(), paragraphToCheck.text(), resolvedOptions, results, m_frame.selection().selection());
+ markAndReplaceFor(WTFMove(request), results);
}
static bool isAutomaticTextReplacementType(TextCheckingType type)
@@ -2453,24 +2427,25 @@ static bool isAutomaticTextReplacementType(TextCheckingType type)
static void correctSpellcheckingPreservingTextCheckingParagraph(TextCheckingParagraph& paragraph, PassRefPtr<Range> rangeToReplace, const String& replacement, int resultLocation, int resultLength)
{
- ContainerNode* scope = toContainerNode(highestAncestor(paragraph.paragraphRange()->startContainer()));
+ auto& scope = downcast<ContainerNode>(paragraph.paragraphRange()->startContainer().rootNode());
size_t paragraphLocation;
size_t paragraphLength;
- TextIterator::getLocationAndLengthFromRange(scope, paragraph.paragraphRange().get(), paragraphLocation, paragraphLength);
+ TextIterator::getLocationAndLengthFromRange(&scope, paragraph.paragraphRange().get(), paragraphLocation, paragraphLength);
applyCommand(SpellingCorrectionCommand::create(rangeToReplace, replacement));
// TextCheckingParagraph may be orphaned after SpellingCorrectionCommand mutated DOM.
// See <rdar://10305315>, http://webkit.org/b/89526.
- RefPtr<Range> newParagraphRange = TextIterator::rangeFromLocationAndLength(scope, paragraphLocation, paragraphLength + replacement.length() - resultLength);
+ RefPtr<Range> newParagraphRange = TextIterator::rangeFromLocationAndLength(&scope, paragraphLocation, paragraphLength + replacement.length() - resultLength);
paragraph = TextCheckingParagraph(TextIterator::subrange(newParagraphRange.get(), resultLocation, replacement.length()), newParagraphRange);
}
void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vector<TextCheckingResult>& results)
{
+ Ref<Frame> protection(m_frame);
ASSERT(request);
TextCheckingTypeMask textCheckingOptions = request->data().mask();
@@ -2479,7 +2454,7 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
const bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
const bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
const bool shouldMarkLink = textCheckingOptions & TextCheckingTypeLink;
- const bool shouldPerformReplacement = textCheckingOptions & TextCheckingTypeReplacement;
+ const bool shouldPerformReplacement = textCheckingOptions & (TextCheckingTypeQuote | TextCheckingTypeDash | TextCheckingTypeReplacement);
const bool shouldShowCorrectionPanel = textCheckingOptions & TextCheckingTypeShowCorrectionPanel;
const bool shouldCheckForCorrection = shouldShowCorrectionPanel || (textCheckingOptions & TextCheckingTypeCorrection);
#if !USE(AUTOCORRECTION_PANEL)
@@ -2494,10 +2469,10 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
bool adjustSelectionForParagraphBoundaries = false;
if (shouldPerformReplacement || shouldMarkSpelling || shouldCheckForCorrection) {
- if (m_frame.selection().selectionType() == VisibleSelection::CaretSelection) {
+ if (m_frame.selection().selection().selectionType() == VisibleSelection::CaretSelection) {
// Attempt to save the caret position so we can restore it later if needed
- Position caretPosition = m_frame.selection().end();
- selectionOffset = paragraph.offsetTo(caretPosition, ASSERT_NO_EXCEPTION);
+ Position caretPosition = m_frame.selection().selection().end();
+ selectionOffset = paragraph.offsetTo(caretPosition).releaseReturnValue();
restoreSelectionAfterChange = true;
if (selectionOffset > 0 && (selectionOffset > paragraph.textLength() || paragraph.textCharAt(selectionOffset - 1) == newlineCharacter))
adjustSelectionForParagraphBoundaries = true;
@@ -2515,7 +2490,7 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
const int resultLength = results[i].length;
const int resultEndLocation = resultLocation + resultLength;
const String& replacement = results[i].replacement;
- const bool resultEndsAtAmbiguousBoundary = useAmbiguousBoundaryOffset && resultEndLocation == selectionOffset - 1;
+ const bool resultEndsAtAmbiguousBoundary = useAmbiguousBoundaryOffset && selectionOffset - 1 <= resultEndLocation;
// Only mark misspelling if:
// 1. Current text checking isn't done for autocorrection, in which case shouldMarkSpelling is false.
@@ -2528,16 +2503,14 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
RefPtr<Range> misspellingRange = paragraph.subrange(resultLocation, resultLength);
if (!m_alternativeTextController->isSpellingMarkerAllowed(misspellingRange))
continue;
- misspellingRange->startContainer()->document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling, replacement);
+ misspellingRange->startContainer().document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling, replacement);
} else if (shouldMarkGrammar && resultType == TextCheckingTypeGrammar && paragraph.checkingRangeCovers(resultLocation, resultLength)) {
ASSERT(resultLength > 0 && resultLocation >= 0);
- const Vector<GrammarDetail>& details = results[i].details;
- for (unsigned j = 0; j < details.size(); j++) {
- const GrammarDetail& detail = details[j];
+ for (auto& detail : results[i].details) {
ASSERT(detail.length > 0 && detail.location >= 0);
if (paragraph.checkingRangeCovers(resultLocation + detail.location, detail.length)) {
RefPtr<Range> badGrammarRange = paragraph.subrange(resultLocation + detail.location, detail.length);
- badGrammarRange->startContainer()->document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail.userDescription);
+ badGrammarRange->startContainer().document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail.userDescription);
}
}
} else if (resultEndLocation <= spellingRangeEndOffset && resultEndLocation >= paragraph.checkingStart()
@@ -2578,7 +2551,7 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
continue;
}
- VisibleSelection selectionToReplace(rangeToReplace.get(), DOWNSTREAM);
+ VisibleSelection selectionToReplace(*rangeToReplace, DOWNSTREAM);
if (selectionToReplace != m_frame.selection().selection()) {
if (!m_frame.selection().shouldChangeSelection(selectionToReplace))
continue;
@@ -2590,7 +2563,7 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
restoreSelectionAfterChange = false;
if (canEditRichly())
applyCommand(CreateLinkCommand::create(document(), replacement));
- } else if (canEdit() && shouldInsertText(replacement, rangeToReplace.get(), EditorInsertActionTyped)) {
+ } else if (canEdit() && shouldInsertText(replacement, rangeToReplace.get(), EditorInsertAction::Typed)) {
correctSpellcheckingPreservingTextCheckingParagraph(paragraph, rangeToReplace, replacement, resultLocation, resultLength);
if (AXObjectCache* cache = document().existingAXObjectCache()) {
@@ -2607,9 +2580,13 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
if (resultLocation < selectionOffset)
selectionOffset += replacement.length() - resultLength;
- // Add a marker so that corrections can easily be undone and won't be re-corrected.
- if (resultType == TextCheckingTypeCorrection)
- m_alternativeTextController->markCorrection(paragraph.subrange(resultLocation, replacement.length()), replacedString);
+ if (resultType == TextCheckingTypeCorrection) {
+ RefPtr<Range> replacementRange = paragraph.subrange(resultLocation, replacement.length());
+ m_alternativeTextController->recordAutocorrectionResponse(AutocorrectionResponse::Accepted, replacedString, replacementRange);
+
+ // Add a marker so that corrections can easily be undone and won't be re-corrected.
+ m_alternativeTextController->markCorrection(WTFMove(replacementRange), replacedString);
+ }
}
}
}
@@ -2625,7 +2602,7 @@ void Editor::markAndReplaceFor(PassRefPtr<SpellCheckRequest> request, const Vect
m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
} else {
// If this fails for any reason, the fallback is to go one position beyond the last replacement
- m_frame.selection().moveTo(m_frame.selection().end());
+ m_frame.selection().moveTo(m_frame.selection().selection().end());
m_frame.selection().modify(FrameSelection::AlterationMove, DirectionForward, CharacterGranularity);
}
}
@@ -2640,14 +2617,14 @@ void Editor::changeBackToReplacedString(const String& replacedString)
return;
RefPtr<Range> selection = selectedRange();
- if (!shouldInsertText(replacedString, selection.get(), EditorInsertActionPasted))
+ if (!shouldInsertText(replacedString, selection.get(), EditorInsertAction::Pasted))
return;
- m_alternativeTextController->recordAutocorrectionResponseReversed(replacedString, selection);
+ m_alternativeTextController->recordAutocorrectionResponse(AutocorrectionResponse::Reverted, replacedString, selection);
TextCheckingParagraph paragraph(selection);
- replaceSelectionWithText(replacedString, false, false);
+ replaceSelectionWithText(replacedString, false, false, EditActionInsert);
RefPtr<Range> changedRange = paragraph.subrange(paragraph.checkingStart(), replacedString.length());
- changedRange->startContainer()->document().markers().addMarker(changedRange.get(), DocumentMarker::Replacement, String());
+ changedRange->startContainer().document().markers().addMarker(changedRange.get(), DocumentMarker::Replacement, String());
m_alternativeTextController->markReversed(changedRange.get());
#else
ASSERT_NOT_REACHED();
@@ -2749,9 +2726,9 @@ void Editor::updateMarkersForWordsAffectedByEditing(bool doNotRemoveIfSelectionA
// of marker that contains the word in question, and remove marker on that whole range.
RefPtr<Range> wordRange = Range::create(document(), startOfFirstWord.deepEquivalent(), endOfLastWord.deepEquivalent());
- Vector<DocumentMarker*> markers = document().markers().markersInRange(wordRange.get(), DocumentMarker::DictationAlternatives);
- for (size_t i = 0; i < markers.size(); ++i)
- m_alternativeTextController->removeDictationAlternativesForMarker(markers[i]);
+ Vector<RenderedDocumentMarker*> markers = document().markers().markersInRange(wordRange.get(), DocumentMarker::DictationAlternatives);
+ for (auto* marker : markers)
+ m_alternativeTextController->removeDictationAlternativesForMarker(*marker);
#if PLATFORM(IOS)
document().markers().removeMarkers(wordRange.get(), DocumentMarker::Spelling | DocumentMarker::CorrectionIndicator | DocumentMarker::SpellCheckingExemption | DocumentMarker::DictationAlternatives | DocumentMarker::DictationPhraseWithAlternatives, DocumentMarkerController::RemovePartiallyOverlappingMarker);
@@ -2780,7 +2757,7 @@ PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint)
IntPoint framePoint = frameView->windowToContents(windowPoint);
VisibleSelection selection(frame->visiblePositionForPoint(framePoint));
- return avoidIntersectionWithDeleteButtonController(selection.toNormalizedRange().get());
+ return selection.toNormalizedRange();
}
void Editor::revealSelectionAfterEditingOperation(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
@@ -2788,33 +2765,39 @@ void Editor::revealSelectionAfterEditingOperation(const ScrollAlignment& alignme
if (m_ignoreCompositionSelectionChange)
return;
- m_frame.selection().revealSelection(alignment, revealExtentOption);
+#if PLATFORM(IOS)
+ SelectionRevealMode revealMode = SelectionRevealMode::RevealUpToMainFrame;
+#else
+ SelectionRevealMode revealMode = SelectionRevealMode::Reveal;
+#endif
+
+ m_frame.selection().revealSelection(revealMode, alignment, revealExtentOption);
}
-void Editor::setIgnoreCompositionSelectionChange(bool ignore)
+void Editor::setIgnoreCompositionSelectionChange(bool ignore, RevealSelection shouldRevealExistingSelection)
{
if (m_ignoreCompositionSelectionChange == ignore)
return;
m_ignoreCompositionSelectionChange = ignore;
#if PLATFORM(IOS)
- // FIXME: Merge this to open source https://bugs.webkit.org/show_bug.cgi?id=38830
+ // FIXME: Should suppress selection change notifications during a composition change <https://webkit.org/b/38830>
if (!ignore)
- notifyComponentsOnChangedSelection(m_frame.selection().selection(), 0);
+ respondToChangedSelection(m_frame.selection().selection(), 0);
#endif
- if (!ignore)
+ if (!ignore && shouldRevealExistingSelection == RevealSelection::Yes)
revealSelectionAfterEditingOperation(ScrollAlignment::alignToEdgeIfNeeded, RevealExtent);
}
-PassRefPtr<Range> Editor::compositionRange() const
+RefPtr<Range> Editor::compositionRange() const
{
if (!m_compositionNode)
- return 0;
+ return nullptr;
unsigned length = m_compositionNode->length();
unsigned start = std::min(m_compositionStart, length);
unsigned end = std::min(std::max(start, m_compositionEnd), length);
if (start >= end)
- return 0;
+ return nullptr;
return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
}
@@ -2822,10 +2805,11 @@ bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selecti
{
if (!m_compositionNode)
return false;
- Position start = m_frame.selection().start();
+ const VisibleSelection& selection = m_frame.selection().selection();
+ Position start = selection.start();
if (start.deprecatedNode() != m_compositionNode)
return false;
- Position end = m_frame.selection().end();
+ Position end = selection.end();
if (end.deprecatedNode() != m_compositionNode)
return false;
@@ -2860,7 +2844,7 @@ void Editor::transpose()
RefPtr<Range> range = makeRange(previous, next);
if (!range)
return;
- VisibleSelection newSelection(range.get(), DOWNSTREAM);
+ VisibleSelection newSelection(*range, DOWNSTREAM);
// Transpose the two characters.
String text = plainText(range.get());
@@ -2876,22 +2860,34 @@ void Editor::transpose()
}
// Insert the transposed characters.
- if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped))
+ if (!shouldInsertText(transposed, range.get(), EditorInsertAction::Typed))
return;
- replaceSelectionWithText(transposed, false, false);
+ replaceSelectionWithText(transposed, false, false, EditActionInsert);
+}
+
+void Editor::addRangeToKillRing(const Range& range, KillRingInsertionMode mode)
+{
+ addTextToKillRing(plainText(&range), mode);
}
-void Editor::addToKillRing(Range* range, bool prepend)
+void Editor::addTextToKillRing(const String& text, KillRingInsertionMode mode)
{
if (m_shouldStartNewKillRingSequence)
killRing().startNewSequence();
- String text = plainText(range);
- if (prepend)
+ m_shouldStartNewKillRingSequence = false;
+
+ // If the kill was from a backwards motion, prepend to the kill ring.
+ // This will ensure that alternating forward and backward kills will
+ // build up the original string in the kill ring without permuting it.
+ switch (mode) {
+ case KillRingInsertionMode::PrependText:
killRing().prepend(text);
- else
+ break;
+ case KillRingInsertionMode::AppendText:
killRing().append(text);
- m_shouldStartNewKillRingSequence = false;
+ break;
+ }
}
void Editor::startAlternativeTextUITimer()
@@ -2910,8 +2906,10 @@ void Editor::dismissCorrectionPanelAsIgnored()
m_alternativeTextController->dismiss(ReasonForDismissingAlternativeTextIgnored);
}
-void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions options)
+void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions options)
{
+ Ref<Frame> protection(m_frame);
+
// If the new selection is orphaned, then don't update the selection.
if (newSelection.start().isOrphan() || newSelection.end().isOrphan())
return;
@@ -2932,7 +2930,7 @@ void Editor::changeSelectionAfterCommand(const VisibleSelection& newSelection,
// does not call EditorClient::respondToChangedSelection(), which, on the Mac, sends selection change notifications and
// starts a new kill ring sequence, but we want to do these things (matches AppKit).
#if PLATFORM(IOS)
- // FIXME: Merge this to open source https://bugs.webkit.org/show_bug.cgi?id=38830
+ // FIXME: Should suppress selection change notifications during a composition change <https://webkit.org/b/38830>
if (m_ignoreCompositionSelectionChange)
return;
#endif
@@ -2945,11 +2943,9 @@ String Editor::selectedText() const
return selectedText(TextIteratorDefaultBehavior);
}
-String Editor::selectedTextForClipboard() const
+String Editor::selectedTextForDataTransfer() const
{
- if (m_frame.settings().selectionIncludesAltImageText())
- return selectedText(TextIteratorEmitsImageAltText);
- return selectedText();
+ return selectedText(TextIteratorEmitsImageAltText);
}
String Editor::selectedText(TextIteratorBehavior behavior) const
@@ -2969,12 +2965,9 @@ static inline void collapseCaretWidth(IntRect& rect)
IntRect Editor::firstRectForRange(Range* range) const
{
- ASSERT(range->startContainer());
- ASSERT(range->endContainer());
-
VisiblePosition startVisiblePosition(range->startPosition(), DOWNSTREAM);
- if (range->collapsed(ASSERT_NO_EXCEPTION)) {
+ if (range->collapsed()) {
// FIXME: Getting caret rect and removing caret width is a very roundabout way to get collapsed range location.
// In particular, width adjustment doesn't work for rotated text.
IntRect startCaretRect = RenderedPosition(startVisiblePosition).absoluteRect();
@@ -3014,22 +3007,20 @@ bool Editor::shouldChangeSelection(const VisibleSelection& oldSelection, const V
return client() && client()->shouldChangeSelectedRange(oldSelection.toNormalizedRange().get(), newSelection.toNormalizedRange().get(), affinity, stillSelecting);
}
-void Editor::computeAndSetTypingStyle(StyleProperties* style, EditAction editingAction)
+void Editor::computeAndSetTypingStyle(EditingStyle& style, EditAction editingAction)
{
- if (!style || style->isEmpty()) {
+ if (style.isEmpty()) {
m_frame.selection().clearTypingStyle();
return;
}
// Calculate the current typing style.
RefPtr<EditingStyle> typingStyle;
- if (m_frame.selection().typingStyle()) {
- typingStyle = m_frame.selection().typingStyle()->copy();
- typingStyle->overrideWithStyle(style);
- } else
- typingStyle = EditingStyle::create(style);
-
- typingStyle->prepareToApplyAt(m_frame.selection().selection().visibleStart().deepEquivalent(), EditingStyle::PreserveWritingDirection);
+ if (auto existingTypingStyle = m_frame.selection().typingStyle())
+ typingStyle = existingTypingStyle->copy();
+ else
+ typingStyle = EditingStyle::create();
+ typingStyle->overrideTypingStyleAt(style, m_frame.selection().selection().visibleStart().deepEquivalent());
// Handle block styles, substracting these from the typing style.
RefPtr<EditingStyle> blockStyle = typingStyle->extractAndRemoveBlockProperties();
@@ -3040,6 +3031,11 @@ void Editor::computeAndSetTypingStyle(StyleProperties* style, EditAction editing
m_frame.selection().setTypingStyle(typingStyle);
}
+void Editor::computeAndSetTypingStyle(StyleProperties& properties, EditAction editingAction)
+{
+ return computeAndSetTypingStyle(EditingStyle::create(&properties), editingAction);
+}
+
void Editor::textFieldDidBeginEditing(Element* e)
{
if (client())
@@ -3081,29 +3077,29 @@ void Editor::textDidChangeInTextArea(Element* e)
void Editor::applyEditingStyleToBodyElement() const
{
- RefPtr<NodeList> list = document().getElementsByTagName("body");
- unsigned len = list->length();
- for (unsigned i = 0; i < len; i++)
- applyEditingStyleToElement(toElement(list->item(i)));
+ auto collection = document().getElementsByTagName(HTMLNames::bodyTag.localName());
+ unsigned length = collection->length();
+ for (unsigned i = 0; i < length; ++i)
+ applyEditingStyleToElement(collection->item(i));
}
void Editor::applyEditingStyleToElement(Element* element) const
{
- if (!element)
- return;
- ASSERT(element->isStyledElement());
- if (!element->isStyledElement())
+ ASSERT(!element || is<StyledElement>(*element));
+ if (!is<StyledElement>(element))
return;
// Mutate using the CSSOM wrapper so we get the same event behavior as a script.
- CSSStyleDeclaration* style = toStyledElement(element)->style();
- style->setPropertyInternal(CSSPropertyWordWrap, "break-word", false, IGNORE_EXCEPTION);
- style->setPropertyInternal(CSSPropertyWebkitNbspMode, "space", false, IGNORE_EXCEPTION);
- style->setPropertyInternal(CSSPropertyWebkitLineBreak, "after-white-space", false, IGNORE_EXCEPTION);
+ CSSStyleDeclaration* style = downcast<StyledElement>(*element).cssomStyle();
+ style->setPropertyInternal(CSSPropertyWordWrap, "break-word", false);
+ style->setPropertyInternal(CSSPropertyWebkitNbspMode, "space", false);
+ style->setPropertyInternal(CSSPropertyWebkitLineBreak, "after-white-space", false);
}
bool Editor::findString(const String& target, FindOptions options)
{
+ Ref<Frame> protection(m_frame);
+
VisibleSelection selection = m_frame.selection().selection();
RefPtr<Range> resultRange = rangeOfString(target, selection.firstRange().get(), options);
@@ -3111,27 +3107,18 @@ bool Editor::findString(const String& target, FindOptions options)
if (!resultRange)
return false;
- m_frame.selection().setSelection(VisibleSelection(resultRange.get(), DOWNSTREAM));
- m_frame.selection().revealSelection();
- return true;
-}
-
-PassRefPtr<Range> Editor::findStringAndScrollToVisible(const String& target, Range* previousMatch, FindOptions options)
-{
- RefPtr<Range> nextMatch = rangeOfString(target, previousMatch, options);
- if (!nextMatch)
- return 0;
+ m_frame.selection().setSelection(VisibleSelection(*resultRange, DOWNSTREAM));
- nextMatch->firstNode()->renderer()->scrollRectToVisible(nextMatch->boundingBox(),
- ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded);
+ if (!(options & DoNotRevealSelection))
+ m_frame.selection().revealSelection();
- return nextMatch.release();
+ return true;
}
-PassRefPtr<Range> Editor::rangeOfString(const String& target, Range* referenceRange, FindOptions options)
+RefPtr<Range> Editor::rangeOfString(const String& target, Range* referenceRange, FindOptions options)
{
if (target.isEmpty())
- return 0;
+ return nullptr;
// Start from an edge of the reference range, if there's a reference range that's not in shadow content. Which edge
// is used depends on whether we're searching forward or backward, and whether startInSelection is set.
@@ -3146,19 +3133,19 @@ PassRefPtr<Range> Editor::rangeOfString(const String& target, Range* referenceRa
searchRange->setEnd(startInReferenceRange ? referenceRange->endPosition() : referenceRange->startPosition());
}
- RefPtr<Node> shadowTreeRoot = referenceRange && referenceRange->startContainer() ? referenceRange->startContainer()->nonBoundaryShadowTreeRootNode() : 0;
+ RefPtr<ShadowRoot> shadowTreeRoot = referenceRange ? referenceRange->startContainer().containingShadowRoot() : nullptr;
if (shadowTreeRoot) {
if (forward)
- searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount());
+ searchRange->setEnd(*shadowTreeRoot, shadowTreeRoot->countChildNodes());
else
- searchRange->setStart(shadowTreeRoot.get(), 0);
+ searchRange->setStart(*shadowTreeRoot, 0);
}
- RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options));
+ RefPtr<Range> resultRange = findPlainText(*searchRange, target, options);
// If we started in the reference range and the found range exactly matches the reference range, find again.
// Build a selection with the found range to remove collapsed whitespace.
// Compare ranges instead of selection objects to ignore the way that the current selection was made.
- if (startInReferenceRange && areRangesEqual(VisibleSelection(resultRange.get()).toNormalizedRange().get(), referenceRange)) {
+ if (startInReferenceRange && areRangesEqual(VisibleSelection(*resultRange).toNormalizedRange().get(), referenceRange)) {
searchRange = rangeOfContents(document());
if (forward)
searchRange->setStart(referenceRange->endPosition());
@@ -3167,48 +3154,49 @@ PassRefPtr<Range> Editor::rangeOfString(const String& target, Range* referenceRa
if (shadowTreeRoot) {
if (forward)
- searchRange->setEnd(shadowTreeRoot.get(), shadowTreeRoot->childNodeCount());
+ searchRange->setEnd(*shadowTreeRoot, shadowTreeRoot->countChildNodes());
else
- searchRange->setStart(shadowTreeRoot.get(), 0);
+ searchRange->setStart(*shadowTreeRoot, 0);
}
- resultRange = findPlainText(searchRange.get(), target, options);
+ resultRange = findPlainText(*searchRange, target, options);
}
// If nothing was found in the shadow tree, search in main content following the shadow tree.
- if (resultRange->collapsed(ASSERT_NO_EXCEPTION) && shadowTreeRoot) {
+ if (resultRange->collapsed() && shadowTreeRoot) {
searchRange = rangeOfContents(document());
- if (forward)
- searchRange->setStartAfter(shadowTreeRoot->shadowHost());
- else
- searchRange->setEndBefore(shadowTreeRoot->shadowHost());
+ if (shadowTreeRoot->shadowHost()) {
+ if (forward)
+ searchRange->setStartAfter(*shadowTreeRoot->shadowHost());
+ else
+ searchRange->setEndBefore(*shadowTreeRoot->shadowHost());
+ }
- resultRange = findPlainText(searchRange.get(), target, options);
+ resultRange = findPlainText(*searchRange, target, options);
}
// If we didn't find anything and we're wrapping, search again in the entire document (this will
// redundantly re-search the area already searched in some cases).
- if (resultRange->collapsed(ASSERT_NO_EXCEPTION) && options & WrapAround) {
+ if (resultRange->collapsed() && options & WrapAround) {
searchRange = rangeOfContents(document());
- resultRange = findPlainText(searchRange.get(), target, options);
+ resultRange = findPlainText(*searchRange, target, options);
// We used to return false here if we ended up with the same range that we started with
// (e.g., the reference range was already the only instance of this text). But we decided that
// this should be a success case instead, so we'll just fall through in that case.
}
- return resultRange->collapsed(ASSERT_NO_EXCEPTION) ? 0 : resultRange.release();
+ return resultRange->collapsed() ? nullptr : resultRange;
}
-static bool isFrameInRange(Frame* frame, Range* range)
+static bool isFrameInRange(Frame& frame, Range& range)
{
- bool inRange = false;
- for (HTMLFrameOwnerElement* ownerElement = frame->ownerElement(); ownerElement; ownerElement = ownerElement->document().ownerElement()) {
- if (&ownerElement->document() == &range->ownerDocument()) {
- inRange = range->intersectsNode(ownerElement, IGNORE_EXCEPTION);
- break;
+ for (auto* ownerElement = frame.ownerElement(); ownerElement; ownerElement = ownerElement->document().ownerElement()) {
+ if (&ownerElement->document() == &range.ownerDocument()) {
+ auto result = range.intersectsNode(*ownerElement);
+ return !result.hasException() && result.releaseReturnValue();
}
}
- return inRange;
+ return false;
}
unsigned Editor::countMatchesForText(const String& target, Range* range, FindOptions options, unsigned limit, bool markMatches, Vector<RefPtr<Range>>* matches)
@@ -3220,24 +3208,24 @@ unsigned Editor::countMatchesForText(const String& target, Range* range, FindOpt
if (range) {
if (&range->ownerDocument() == &document())
searchRange = range;
- else if (!isFrameInRange(&m_frame, range))
+ else if (!isFrameInRange(m_frame, *range))
return 0;
}
if (!searchRange)
searchRange = rangeOfContents(document());
- Node* originalEndContainer = searchRange->endContainer();
+ Node& originalEndContainer = searchRange->endContainer();
int originalEndOffset = searchRange->endOffset();
unsigned matchCount = 0;
do {
- RefPtr<Range> resultRange(findPlainText(searchRange.get(), target, options & ~Backwards));
- if (resultRange->collapsed(IGNORE_EXCEPTION)) {
- if (!resultRange->startContainer()->isInShadowTree())
+ RefPtr<Range> resultRange(findPlainText(*searchRange, target, options & ~Backwards));
+ if (resultRange->collapsed()) {
+ if (!resultRange->startContainer().isInShadowTree())
break;
- searchRange->setStartAfter(resultRange->startContainer()->shadowHost(), IGNORE_EXCEPTION);
- searchRange->setEnd(originalEndContainer, originalEndOffset, IGNORE_EXCEPTION);
+ searchRange->setStartAfter(*resultRange->startContainer().shadowHost());
+ searchRange->setEnd(originalEndContainer, originalEndOffset);
continue;
}
@@ -3256,32 +3244,13 @@ unsigned Editor::countMatchesForText(const String& target, Range* range, FindOpt
// result range. There is no need to use a VisiblePosition here,
// since findPlainText will use a TextIterator to go over the visible
// text nodes.
- searchRange->setStart(resultRange->endContainer(IGNORE_EXCEPTION), resultRange->endOffset(IGNORE_EXCEPTION), IGNORE_EXCEPTION);
+ searchRange->setStart(resultRange->endContainer(), resultRange->endOffset());
Node* shadowTreeRoot = searchRange->shadowRoot();
- if (searchRange->collapsed(IGNORE_EXCEPTION) && shadowTreeRoot)
- searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), IGNORE_EXCEPTION);
+ if (searchRange->collapsed() && shadowTreeRoot)
+ searchRange->setEnd(*shadowTreeRoot, shadowTreeRoot->countChildNodes());
} while (true);
- if (markMatches || matches) {
- // Do a "fake" paint in order to execute the code that computes the rendered rect for each text match.
- if (m_frame.view() && m_frame.contentRenderer()) {
- document().updateLayout(); // Ensure layout is up to date.
- // FIXME: unclear if we need LegacyIOSDocumentVisibleRect.
- // FIXME: this should probably look at paintsEntireContents()
- LayoutRect visibleRect = m_frame.view()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
- if (!visibleRect.isEmpty()) {
- GraphicsContext context((PlatformGraphicsContext*)0);
- context.setPaintingDisabled(true);
-
- PaintBehavior oldBehavior = m_frame.view()->paintBehavior();
- m_frame.view()->setPaintBehavior(oldBehavior | PaintBehaviorFlattenCompositingLayers);
- m_frame.view()->paintContents(&context, enclosingIntRect(visibleRect));
- m_frame.view()->setPaintBehavior(oldBehavior);
- }
- }
- }
-
return matchCount;
}
@@ -3294,11 +3263,149 @@ void Editor::setMarkedTextMatchesAreHighlighted(bool flag)
document().markers().repaintMarkers(DocumentMarker::TextMatch);
}
-void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions options)
+#if !PLATFORM(MAC)
+void Editor::selectionWillChange()
{
- m_alternativeTextController->stopPendingCorrection(oldSelection);
+}
+#endif
+
+void Editor::respondToChangedSelection(const VisibleSelection&, FrameSelection::SetSelectionOptions options)
+{
+#if PLATFORM(IOS)
+ // FIXME: Should suppress selection change notifications during a composition change <https://webkit.org/b/38830>
+ if (m_ignoreCompositionSelectionChange)
+ return;
+#endif
+
+ if (client())
+ client()->respondToChangedSelection(&m_frame);
+
+#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
+ if (shouldDetectTelephoneNumbers())
+ m_telephoneNumberDetectionUpdateTimer.startOneShot(0);
+#endif
+
+ setStartNewKillRingSequence(true);
- bool closeTyping = options & FrameSelection::CloseTyping;
+ if (m_editorUIUpdateTimer.isActive())
+ return;
+
+ // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself.
+ m_editorUIUpdateTimerShouldCheckSpellingAndGrammar = options & FrameSelection::CloseTyping
+ && !(options & FrameSelection::SpellCorrectionTriggered);
+ m_editorUIUpdateTimerWasTriggeredByDictation = options & FrameSelection::DictationTriggered;
+ m_editorUIUpdateTimer.startOneShot(0);
+}
+
+#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
+
+bool Editor::shouldDetectTelephoneNumbers()
+{
+ if (!m_frame.document())
+ return false;
+ return document().isTelephoneNumberParsingEnabled() && TelephoneNumberDetector::isSupported();
+}
+
+void Editor::scanSelectionForTelephoneNumbers()
+{
+ if (!shouldDetectTelephoneNumbers() || !client())
+ return;
+
+ m_detectedTelephoneNumberRanges.clear();
+
+ Vector<RefPtr<Range>> markedRanges;
+
+ FrameSelection& frameSelection = m_frame.selection();
+ if (!frameSelection.isRange()) {
+ m_frame.mainFrame().servicesOverlayController().selectedTelephoneNumberRangesChanged();
+ return;
+ }
+ RefPtr<Range> selectedRange = frameSelection.toNormalizedRange();
+
+ // Extend the range a few characters in each direction to detect incompletely selected phone numbers.
+ static const int charactersToExtend = 15;
+ const VisibleSelection& visibleSelection = frameSelection.selection();
+ Position start = visibleSelection.start();
+ Position end = visibleSelection.end();
+ for (int i = 0; i < charactersToExtend; ++i) {
+ start = start.previous(Character);
+ end = end.next(Character);
+ }
+
+ FrameSelection extendedSelection;
+ extendedSelection.setStart(start);
+ extendedSelection.setEnd(end);
+ RefPtr<Range> extendedRange = extendedSelection.toNormalizedRange();
+
+ if (!extendedRange) {
+ m_frame.mainFrame().servicesOverlayController().selectedTelephoneNumberRangesChanged();
+ return;
+ }
+
+ scanRangeForTelephoneNumbers(*extendedRange, extendedRange->text(), markedRanges);
+
+ // Only consider ranges with a detected telephone number if they overlap with the actual selection range.
+ for (auto& range : markedRanges) {
+ if (rangesOverlap(range.get(), selectedRange.get()))
+ m_detectedTelephoneNumberRanges.append(range);
+ }
+
+ m_frame.mainFrame().servicesOverlayController().selectedTelephoneNumberRangesChanged();
+}
+
+void Editor::scanRangeForTelephoneNumbers(Range& range, const StringView& stringView, Vector<RefPtr<Range>>& markedRanges)
+{
+ // Don't scan for phone numbers inside editable regions.
+ Node& startNode = range.startContainer();
+ if (startNode.hasEditableStyle())
+ return;
+
+ // relativeStartPosition and relativeEndPosition are the endpoints of the phone number range,
+ // relative to the scannerPosition
+ unsigned length = stringView.length();
+ unsigned scannerPosition = 0;
+ int relativeStartPosition = 0;
+ int relativeEndPosition = 0;
+
+ auto characters = stringView.upconvertedCharacters();
+
+ while (scannerPosition < length && TelephoneNumberDetector::find(&characters[scannerPosition], length - scannerPosition, &relativeStartPosition, &relativeEndPosition)) {
+ // The convention in the Data Detectors framework is that the end position is the first character NOT in the phone number
+ // (that is, the length of the range is relativeEndPosition - relativeStartPosition). So subtract 1 to get the same
+ // convention as the old WebCore phone number parser (so that the rest of the code is still valid if we want to go back
+ // to the old parser).
+ --relativeEndPosition;
+
+ ASSERT(scannerPosition + relativeEndPosition < length);
+
+ unsigned subrangeOffset = scannerPosition + relativeStartPosition;
+ unsigned subrangeLength = relativeEndPosition - relativeStartPosition + 1;
+
+ RefPtr<Range> subrange = TextIterator::subrange(&range, subrangeOffset, subrangeLength);
+
+ markedRanges.append(subrange);
+ range.ownerDocument().markers().addMarker(subrange.get(), DocumentMarker::TelephoneNumber);
+
+ scannerPosition += relativeEndPosition + 1;
+ }
+}
+
+#endif // ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
+
+void Editor::updateEditorUINowIfScheduled()
+{
+ if (!m_editorUIUpdateTimer.isActive())
+ return;
+ m_editorUIUpdateTimer.stop();
+ editorUIUpdateTimerFired();
+}
+
+void Editor::editorUIUpdateTimerFired()
+{
+ VisibleSelection oldSelection = m_oldSelectionForEditorUIUpdate;
+
+ m_alternativeTextController->stopPendingCorrection(oldSelection);
+
bool isContinuousSpellCheckingEnabled = this->isContinuousSpellCheckingEnabled();
bool isContinuousGrammarCheckingEnabled = isContinuousSpellCheckingEnabled && isGrammarCheckingEnabled();
if (isContinuousSpellCheckingEnabled) {
@@ -3325,13 +3432,10 @@ void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, Fra
newSelectedSentence = VisibleSelection(startOfSentence(newStart), endOfSentence(newStart));
}
- // Don't check spelling and grammar if the change of selection is triggered by spelling correction itself.
- bool shouldCheckSpellingAndGrammar = !(options & FrameSelection::SpellCorrectionTriggered);
-
// When typing we check spelling elsewhere, so don't redo it here.
// If this is a change in selection resulting from a delete operation,
// oldSelection may no longer be in the document.
- if (shouldCheckSpellingAndGrammar && closeTyping && oldSelection.isContentEditable() && oldSelection.start().deprecatedNode() && oldSelection.start().anchorNode()->inDocument()) {
+ if (m_editorUIUpdateTimerShouldCheckSpellingAndGrammar && oldSelection.isContentEditable() && oldSelection.start().deprecatedNode() && oldSelection.start().anchorNode()->isConnected()) {
VisiblePosition oldStart(oldSelection.visibleStart());
VisibleSelection oldAdjacentWords = VisibleSelection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
if (oldAdjacentWords != newAdjacentWords) {
@@ -3359,38 +3463,40 @@ void Editor::respondToChangedSelection(const VisibleSelection& oldSelection, Fra
if (!isContinuousGrammarCheckingEnabled)
document().markers().removeMarkers(DocumentMarker::Grammar);
- notifyComponentsOnChangedSelection(oldSelection, options);
+ if (!m_editorUIUpdateTimerWasTriggeredByDictation)
+ m_alternativeTextController->respondToChangedSelection(oldSelection);
+
+ m_oldSelectionForEditorUIUpdate = m_frame.selection().selection();
}
static Node* findFirstMarkable(Node* node)
{
while (node) {
if (!node->renderer())
- return 0;
+ return nullptr;
if (node->renderer()->isTextOrLineBreak())
return node;
- if (isHTMLTextFormControlElement(*node))
- node = toHTMLTextFormControlElement(node)->visiblePositionForIndex(1).deepEquivalent().deprecatedNode();
+ if (is<HTMLTextFormControlElement>(*node))
+ node = downcast<HTMLTextFormControlElement>(*node).visiblePositionForIndex(1).deepEquivalent().deprecatedNode();
else if (node->firstChild())
node = node->firstChild();
else
node = node->nextSibling();
}
- return 0;
+ return nullptr;
}
bool Editor::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, int from, int length) const
{
- Node* node = findFirstMarkable(m_frame.selection().start().deprecatedNode());
+ Node* node = findFirstMarkable(m_frame.selection().selection().start().deprecatedNode());
if (!node)
return false;
unsigned int startOffset = static_cast<unsigned int>(from);
unsigned int endOffset = static_cast<unsigned int>(from + length);
- Vector<DocumentMarker*> markers = document().markers().markersFor(node);
- for (size_t i = 0; i < markers.size(); ++i) {
- DocumentMarker* marker = markers[i];
+ Vector<RenderedDocumentMarker*> markers = document().markers().markersFor(node);
+ for (auto* marker : markers) {
if (marker->startOffset() <= startOffset && endOffset <= marker->endOffset() && marker->type() == markerType)
return true;
}
@@ -3398,8 +3504,18 @@ bool Editor::selectionStartHasMarkerFor(DocumentMarker::MarkerType markerType, i
return false;
}
-TextCheckingTypeMask Editor::resolveTextCheckingTypeMask(TextCheckingTypeMask textCheckingOptions)
+TextCheckingTypeMask Editor::resolveTextCheckingTypeMask(const Node& rootEditableElement, TextCheckingTypeMask textCheckingOptions)
{
+#if USE(AUTOMATIC_TEXT_REPLACEMENT) && !PLATFORM(IOS)
+ bool onlyAllowsTextReplacement = false;
+ if (auto* host = rootEditableElement.shadowHost())
+ onlyAllowsTextReplacement = is<HTMLInputElement>(host) && downcast<HTMLInputElement>(*host).isSpellcheckDisabledExceptTextReplacement();
+ if (onlyAllowsTextReplacement)
+ textCheckingOptions &= TextCheckingTypeReplacement;
+#else
+ UNUSED_PARAM(rootEditableElement);
+#endif
+
bool shouldMarkSpelling = textCheckingOptions & TextCheckingTypeSpelling;
bool shouldMarkGrammar = textCheckingOptions & TextCheckingTypeGrammar;
#if !PLATFORM(IOS)
@@ -3421,16 +3537,18 @@ TextCheckingTypeMask Editor::resolveTextCheckingTypeMask(TextCheckingTypeMask te
#if USE(AUTOMATIC_TEXT_REPLACEMENT)
bool shouldPerformReplacement = textCheckingOptions & TextCheckingTypeReplacement;
if (shouldPerformReplacement) {
- if (isAutomaticLinkDetectionEnabled())
- checkingTypes |= TextCheckingTypeLink;
- if (isAutomaticQuoteSubstitutionEnabled())
- checkingTypes |= TextCheckingTypeQuote;
- if (isAutomaticDashSubstitutionEnabled())
- checkingTypes |= TextCheckingTypeDash;
+ if (!onlyAllowsTextReplacement) {
+ if (isAutomaticLinkDetectionEnabled())
+ checkingTypes |= TextCheckingTypeLink;
+ if (isAutomaticQuoteSubstitutionEnabled())
+ checkingTypes |= TextCheckingTypeQuote;
+ if (isAutomaticDashSubstitutionEnabled())
+ checkingTypes |= TextCheckingTypeDash;
+ if (shouldMarkSpelling && isAutomaticSpellingCorrectionEnabled())
+ checkingTypes |= TextCheckingTypeCorrection;
+ }
if (isAutomaticTextReplacementEnabled())
checkingTypes |= TextCheckingTypeReplacement;
- if (shouldMarkSpelling && isAutomaticSpellingCorrectionEnabled())
- checkingTypes |= TextCheckingTypeCorrection;
}
#endif
#endif // !PLATFORM(IOS)
@@ -3438,11 +3556,68 @@ TextCheckingTypeMask Editor::resolveTextCheckingTypeMask(TextCheckingTypeMask te
return checkingTypes;
}
-void Editor::deviceScaleFactorChanged()
+static RefPtr<Range> candidateRangeForSelection(Frame& frame)
{
-#if ENABLE(DELETION_UI)
- m_deleteButtonController->deviceScaleFactorChanged();
-#endif
+ const VisibleSelection& selection = frame.selection().selection();
+ return selection.isCaret() ? wordRangeFromPosition(selection.start()) : frame.selection().toNormalizedRange();
+}
+
+static bool candidateWouldReplaceText(const VisibleSelection& selection)
+{
+ // If the character behind the caret in the current selection is anything but a space or a newline then we should
+ // replace the whole current word with the candidate.
+ UChar32 characterAfterSelection, characterBeforeSelection, twoCharacterBeforeSelection = 0;
+ charactersAroundPosition(selection.visibleStart(), characterAfterSelection, characterBeforeSelection, twoCharacterBeforeSelection);
+ return !(characterBeforeSelection == '\0' || characterBeforeSelection == '\n' || characterBeforeSelection == ' ');
+}
+
+String Editor::stringForCandidateRequest() const
+{
+ const VisibleSelection& selection = m_frame.selection().selection();
+ RefPtr<Range> rangeForCurrentlyTypedString = candidateRangeForSelection(m_frame);
+ if (rangeForCurrentlyTypedString && candidateWouldReplaceText(selection))
+ return plainText(rangeForCurrentlyTypedString.get());
+
+ return String();
+}
+
+RefPtr<Range> Editor::contextRangeForCandidateRequest() const
+{
+ const VisibleSelection& selection = m_frame.selection().selection();
+ return makeRange(startOfParagraph(selection.visibleStart()), endOfParagraph(selection.visibleEnd()));
+}
+
+RefPtr<Range> Editor::rangeForTextCheckingResult(const TextCheckingResult& result) const
+{
+ if (!result.length)
+ return nullptr;
+
+ RefPtr<Range> contextRange = contextRangeForCandidateRequest();
+ if (!contextRange)
+ return nullptr;
+
+ return TextIterator::subrange(contextRange.get(), result.location, result.length);
+}
+
+void Editor::handleAcceptedCandidate(TextCheckingResult acceptedCandidate)
+{
+ const VisibleSelection& selection = m_frame.selection().selection();
+
+ m_isHandlingAcceptedCandidate = true;
+
+ if (auto range = rangeForTextCheckingResult(acceptedCandidate)) {
+ if (shouldInsertText(acceptedCandidate.replacement, range.get(), EditorInsertAction::Typed)) {
+ Ref<ReplaceRangeWithTextCommand> replaceCommand = ReplaceRangeWithTextCommand::create(range.get(), acceptedCandidate.replacement);
+ applyCommand(replaceCommand.ptr());
+ }
+ } else
+ insertText(acceptedCandidate.replacement, nullptr);
+
+ RefPtr<Range> insertedCandidateRange = rangeExpandedByCharactersInDirectionAtWordBoundary(selection.visibleStart(), acceptedCandidate.replacement.length(), DirectionBackward);
+ if (insertedCandidateRange)
+ insertedCandidateRange->startContainer().document().markers().addMarker(insertedCandidateRange.get(), DocumentMarker::AcceptedCandidate, acceptedCandidate.replacement);
+
+ m_isHandlingAcceptedCandidate = false;
}
bool Editor::unifiedTextCheckerEnabled() const
@@ -3450,7 +3625,7 @@ bool Editor::unifiedTextCheckerEnabled() const
return WebCore::unifiedTextCheckerEnabled(&m_frame);
}
-Vector<String> Editor::dictationAlternativesForMarker(const DocumentMarker* marker)
+Vector<String> Editor::dictationAlternativesForMarker(const DocumentMarker& marker)
{
return m_alternativeTextController->dictationAlternativesForMarker(marker);
}
@@ -3472,4 +3647,108 @@ Document& Editor::document() const
return *m_frame.document();
}
+RefPtr<Range> Editor::adjustedSelectionRange()
+{
+ // FIXME: Why do we need to adjust the selection to include the anchor tag it's in?
+ // Whoever wrote this code originally forgot to leave us a comment explaining the rationale.
+ RefPtr<Range> range = selectedRange();
+ Node* commonAncestor = range->commonAncestorContainer();
+ ASSERT(commonAncestor);
+ auto* enclosingAnchor = enclosingElementWithTag(firstPositionInNode(commonAncestor), HTMLNames::aTag);
+ if (enclosingAnchor && comparePositions(firstPositionInOrBeforeNode(range->startPosition().anchorNode()), range->startPosition()) >= 0)
+ range->setStart(*enclosingAnchor, 0);
+ return range;
+}
+
+// FIXME: This figures out the current style by inserting a <span>!
+const RenderStyle* Editor::styleForSelectionStart(Frame* frame, Node*& nodeToRemove)
+{
+ nodeToRemove = nullptr;
+
+ if (frame->selection().isNone())
+ return nullptr;
+
+ Position position = adjustedSelectionStartForStyleComputation(frame->selection().selection());
+ if (!position.isCandidate() || position.isNull())
+ return nullptr;
+
+ RefPtr<EditingStyle> typingStyle = frame->selection().typingStyle();
+ if (!typingStyle || !typingStyle->style())
+ return &position.deprecatedNode()->renderer()->style();
+
+ auto styleElement = HTMLSpanElement::create(*frame->document());
+
+ String styleText = typingStyle->style()->asText() + " display: inline";
+ styleElement->setAttribute(HTMLNames::styleAttr, styleText);
+
+ styleElement->appendChild(frame->document()->createEditingTextNode(emptyString()));
+
+ auto positionNode = position.deprecatedNode();
+ if (!positionNode || !positionNode->parentNode() || positionNode->parentNode()->appendChild(styleElement).hasException())
+ return nullptr;
+
+ nodeToRemove = styleElement.ptr();
+
+ frame->document()->updateStyleIfNeeded();
+ return styleElement->renderer() ? &styleElement->renderer()->style() : nullptr;
+}
+
+const Font* Editor::fontForSelection(bool& hasMultipleFonts) const
+{
+ hasMultipleFonts = false;
+
+ if (!m_frame.selection().isRange()) {
+ Node* nodeToRemove;
+ auto* style = styleForSelectionStart(&m_frame, nodeToRemove); // sets nodeToRemove
+
+ const Font* font = nullptr;
+ if (style) {
+ font = &style->fontCascade().primaryFont();
+ if (nodeToRemove)
+ nodeToRemove->remove();
+ }
+
+ return font;
+ }
+
+ RefPtr<Range> range = m_frame.selection().toNormalizedRange();
+ if (!range)
+ return nullptr;
+
+ Node* startNode = adjustedSelectionStartForStyleComputation(m_frame.selection().selection()).deprecatedNode();
+ if (!startNode)
+ return nullptr;
+
+ const Font* font = nullptr;
+ Node* pastEnd = range->pastLastNode();
+ // In the loop below, node should eventually match pastEnd and not become null, but we've seen at least one
+ // unreproducible case where this didn't happen, so check for null also.
+ for (Node* node = startNode; node && node != pastEnd; node = NodeTraversal::next(*node)) {
+ auto renderer = node->renderer();
+ if (!renderer)
+ continue;
+ // FIXME: Are there any node types that have renderers, but that we should be skipping?
+ const Font& primaryFont = renderer->style().fontCascade().primaryFont();
+ if (!font)
+ font = &primaryFont;
+ else if (font != &primaryFont) {
+ hasMultipleFonts = true;
+ break;
+ }
+ }
+
+ return font;
+}
+
+Ref<DocumentFragment> Editor::createFragmentForImageAndURL(const String& url)
+{
+ auto imageElement = HTMLImageElement::create(*m_frame.document());
+ imageElement->setAttributeWithoutSynchronization(HTMLNames::srcAttr, url);
+
+ auto fragment = document().createDocumentFragment();
+ fragment->appendChild(imageElement);
+
+ return fragment;
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/editing/Editor.h b/Source/WebCore/editing/Editor.h
index 90396187c..f9b2ce401 100644
--- a/Source/WebCore/editing/Editor.h
+++ b/Source/WebCore/editing/Editor.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2008, 2013, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,11 +23,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Editor_h
-#define Editor_h
+#pragma once
-#include "ClipboardAccessPolicy.h"
#include "Color.h"
+#include "DataTransferAccessPolicy.h"
#include "DictationAlternative.h"
#include "DocumentMarker.h"
#include "EditAction.h"
@@ -37,20 +36,23 @@
#include "FindOptions.h"
#include "FrameSelection.h"
#include "TextChecking.h"
-#include "TextIterator.h"
+#include "TextEventInputType.h"
+#include "TextIteratorBehavior.h"
#include "VisibleSelection.h"
#include "WritingDirection.h"
+#include <memory>
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
OBJC_CLASS NSAttributedString;
OBJC_CLASS NSDictionary;
+OBJC_CLASS NSMutableDictionary;
#endif
namespace WebCore {
class AlternativeTextController;
class ArchiveResource;
-class Clipboard;
+class DataTransfer;
class CompositeEditCommand;
class DeleteButtonController;
class EditCommand;
@@ -60,12 +62,14 @@ class EditorInternalCommand;
class Frame;
class HTMLElement;
class HitTestResult;
+class KeyboardEvent;
class KillRing;
class Pasteboard;
class SharedBuffer;
-class SimpleFontData;
+class Font;
class SpellCheckRequest;
class SpellChecker;
+class StaticRange;
class StyleProperties;
class Text;
class TextCheckerClient;
@@ -89,160 +93,185 @@ struct CompositionUnderline {
enum EditorCommandSource { CommandFromMenuOrKeyBinding, CommandFromDOM, CommandFromDOMWithUserInterface };
enum EditorParagraphSeparator { EditorParagraphSeparatorIsDiv, EditorParagraphSeparatorIsP };
+enum class MailBlockquoteHandling {
+ RespectBlockquote,
+ IgnoreBlockquote,
+};
+
+#if PLATFORM(COCOA)
+
+struct FragmentAndResources {
+ RefPtr<DocumentFragment> fragment;
+ Vector<Ref<ArchiveResource>> resources;
+};
+
+#endif
+
class Editor {
+ WTF_MAKE_FAST_ALLOCATED;
public:
- static PassOwnPtr<Editor> create(Frame& frame) { return adoptPtr(new Editor(frame)); }
+ explicit Editor(Frame&);
~Editor();
- EditorClient* client() const;
- TextCheckerClient* textChecker() const;
+ WEBCORE_EXPORT EditorClient* client() const;
+ WEBCORE_EXPORT TextCheckerClient* textChecker() const;
CompositeEditCommand* lastEditCommand() { return m_lastEditCommand.get(); }
- void handleKeyboardEvent(KeyboardEvent*);
- void handleInputMethodKeydown(KeyboardEvent*);
- bool handleTextEvent(TextEvent*);
+ void handleKeyboardEvent(KeyboardEvent&);
+ void handleInputMethodKeydown(KeyboardEvent&);
+ bool handleTextEvent(TextEvent&);
- bool canEdit() const;
- bool canEditRichly() const;
+ WEBCORE_EXPORT bool canEdit() const;
+ WEBCORE_EXPORT bool canEditRichly() const;
bool canDHTMLCut();
bool canDHTMLCopy();
- bool canDHTMLPaste();
+ WEBCORE_EXPORT bool canDHTMLPaste();
bool tryDHTMLCopy();
bool tryDHTMLCut();
- bool tryDHTMLPaste();
+ WEBCORE_EXPORT bool tryDHTMLPaste();
- bool canCut() const;
- bool canCopy() const;
- bool canPaste() const;
- bool canDelete() const;
+ WEBCORE_EXPORT bool canCut() const;
+ WEBCORE_EXPORT bool canCopy() const;
+ WEBCORE_EXPORT bool canPaste() const;
+ WEBCORE_EXPORT bool canDelete() const;
bool canSmartCopyOrDelete();
- void cut();
- void copy();
- void paste();
+ WEBCORE_EXPORT void cut();
+ WEBCORE_EXPORT void copy();
+ WEBCORE_EXPORT void paste();
void paste(Pasteboard&);
- void pasteAsPlainText();
- void performDelete();
+ WEBCORE_EXPORT void pasteAsPlainText();
+ WEBCORE_EXPORT void performDelete();
- void copyURL(const URL&, const String& title);
+ WEBCORE_EXPORT void copyURL(const URL&, const String& title);
void copyURL(const URL&, const String& title, Pasteboard&);
#if !PLATFORM(IOS)
- void copyImage(const HitTestResult&);
+ WEBCORE_EXPORT void copyImage(const HitTestResult&);
#endif
String readPlainTextFromPasteboard(Pasteboard&);
- void indent();
- void outdent();
+ WEBCORE_EXPORT void indent();
+ WEBCORE_EXPORT void outdent();
void transpose();
bool shouldInsertFragment(PassRefPtr<DocumentFragment>, PassRefPtr<Range>, EditorInsertAction);
bool shouldInsertText(const String&, Range*, EditorInsertAction) const;
- bool shouldDeleteRange(Range*) const;
+ WEBCORE_EXPORT bool shouldDeleteRange(Range*) const;
bool shouldApplyStyle(StyleProperties*, Range*);
void respondToChangedContents(const VisibleSelection& endingSelection);
bool selectionStartHasStyle(CSSPropertyID, const String& value) const;
- TriState selectionHasStyle(CSSPropertyID, const String& value) const;
+ WEBCORE_EXPORT TriState selectionHasStyle(CSSPropertyID, const String& value) const;
String selectionStartCSSPropertyValue(CSSPropertyID);
TriState selectionUnorderedListState() const;
TriState selectionOrderedListState() const;
- PassRefPtr<Node> insertOrderedList();
- PassRefPtr<Node> insertUnorderedList();
- bool canIncreaseSelectionListLevel();
- bool canDecreaseSelectionListLevel();
- PassRefPtr<Node> increaseSelectionListLevel();
- PassRefPtr<Node> increaseSelectionListLevelOrdered();
- PassRefPtr<Node> increaseSelectionListLevelUnordered();
- void decreaseSelectionListLevel();
+ WEBCORE_EXPORT PassRefPtr<Node> insertOrderedList();
+ WEBCORE_EXPORT PassRefPtr<Node> insertUnorderedList();
+ WEBCORE_EXPORT bool canIncreaseSelectionListLevel();
+ WEBCORE_EXPORT bool canDecreaseSelectionListLevel();
+ WEBCORE_EXPORT RefPtr<Node> increaseSelectionListLevel();
+ WEBCORE_EXPORT RefPtr<Node> increaseSelectionListLevelOrdered();
+ WEBCORE_EXPORT RefPtr<Node> increaseSelectionListLevelUnordered();
+ WEBCORE_EXPORT void decreaseSelectionListLevel();
void removeFormattingAndStyle();
void clearLastEditCommand();
#if PLATFORM(IOS)
- void ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping();
+ WEBCORE_EXPORT void ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping();
#endif
- bool deleteWithDirection(SelectionDirection, TextGranularity, bool killRing, bool isTypingAction);
- void deleteSelectionWithSmartDelete(bool smartDelete);
-#if PLATFORM(IOS)
+ WEBCORE_EXPORT bool deleteWithDirection(SelectionDirection, TextGranularity, bool killRing, bool isTypingAction);
+ WEBCORE_EXPORT void deleteSelectionWithSmartDelete(bool smartDelete, EditAction = EditActionDelete);
void clearText();
- void removeUnchangeableStyles();
+#if PLATFORM(IOS)
+ WEBCORE_EXPORT void removeUnchangeableStyles();
#endif
- bool dispatchCPPEvent(const AtomicString&, ClipboardAccessPolicy);
+ bool dispatchCPPEvent(const AtomicString&, DataTransferAccessPolicy);
- void applyStyle(StyleProperties*, EditAction = EditActionUnspecified);
+ WEBCORE_EXPORT void applyStyle(StyleProperties*, EditAction = EditActionUnspecified);
+ void applyStyle(RefPtr<EditingStyle>&&, EditAction);
void applyParagraphStyle(StyleProperties*, EditAction = EditActionUnspecified);
- void applyStyleToSelection(StyleProperties*, EditAction);
+ WEBCORE_EXPORT void applyStyleToSelection(StyleProperties*, EditAction);
+ WEBCORE_EXPORT void applyStyleToSelection(Ref<EditingStyle>&&, EditAction);
void applyParagraphStyleToSelection(StyleProperties*, EditAction);
+ // Returns whether or not we should proceed with editing.
+ bool willApplyEditing(CompositeEditCommand&, Vector<RefPtr<StaticRange>>&&) const;
+ bool willUnapplyEditing(const EditCommandComposition&) const;
+ bool willReapplyEditing(const EditCommandComposition&) const;
+
void appliedEditing(PassRefPtr<CompositeEditCommand>);
- void unappliedEditing(PassRefPtr<EditCommandComposition>);
- void reappliedEditing(PassRefPtr<EditCommandComposition>);
+ void unappliedEditing(EditCommandComposition&);
+ void reappliedEditing(EditCommandComposition&);
void unappliedSpellCorrection(const VisibleSelection& selectionOfCorrected, const String& corrected, const String& correction);
+ // This is off by default, since most editors want this behavior (originally matched IE but not Firefox).
void setShouldStyleWithCSS(bool flag) { m_shouldStyleWithCSS = flag; }
bool shouldStyleWithCSS() const { return m_shouldStyleWithCSS; }
class Command {
public:
- Command();
+ WEBCORE_EXPORT Command();
Command(const EditorInternalCommand*, EditorCommandSource, PassRefPtr<Frame>);
- bool execute(const String& parameter = String(), Event* triggeringEvent = 0) const;
- bool execute(Event* triggeringEvent) const;
+ WEBCORE_EXPORT bool execute(const String& parameter = String(), Event* triggeringEvent = nullptr) const;
+ WEBCORE_EXPORT bool execute(Event* triggeringEvent) const;
- bool isSupported() const;
- bool isEnabled(Event* triggeringEvent = 0) const;
+ WEBCORE_EXPORT bool isSupported() const;
+ WEBCORE_EXPORT bool isEnabled(Event* triggeringEvent = nullptr) const;
- TriState state(Event* triggeringEvent = 0) const;
- String value(Event* triggeringEvent = 0) const;
+ WEBCORE_EXPORT TriState state(Event* triggeringEvent = nullptr) const;
+ String value(Event* triggeringEvent = nullptr) const;
- bool isTextInsertion() const;
+ WEBCORE_EXPORT bool isTextInsertion() const;
+ WEBCORE_EXPORT bool allowExecutionWhenDisabled() const;
private:
- const EditorInternalCommand* m_command;
+ const EditorInternalCommand* m_command { nullptr };
EditorCommandSource m_source;
RefPtr<Frame> m_frame;
};
- Command command(const String& commandName); // Command source is CommandFromMenuOrKeyBinding.
+ WEBCORE_EXPORT Command command(const String& commandName); // Command source is CommandFromMenuOrKeyBinding.
Command command(const String& commandName, EditorCommandSource);
- static bool commandIsSupportedFromMenuOrKeyBinding(const String& commandName); // Works without a frame.
+ WEBCORE_EXPORT static bool commandIsSupportedFromMenuOrKeyBinding(const String& commandName); // Works without a frame.
- bool insertText(const String&, Event* triggeringEvent);
+ WEBCORE_EXPORT bool insertText(const String&, Event* triggeringEvent, TextEventInputType = TextEventInputKeyboard);
bool insertTextForConfirmedComposition(const String& text);
- bool insertDictatedText(const String&, const Vector<DictationAlternative>& dictationAlternatives, Event* triggeringEvent);
+ WEBCORE_EXPORT bool insertDictatedText(const String&, const Vector<DictationAlternative>& dictationAlternatives, Event* triggeringEvent);
bool insertTextWithoutSendingTextEvent(const String&, bool selectInsertedText, TextEvent* triggeringEvent);
bool insertLineBreak();
bool insertParagraphSeparator();
+ WEBCORE_EXPORT bool insertParagraphSeparatorInQuotedContent();
- bool isContinuousSpellCheckingEnabled() const;
- void toggleContinuousSpellChecking();
+ WEBCORE_EXPORT bool isContinuousSpellCheckingEnabled() const;
+ WEBCORE_EXPORT void toggleContinuousSpellChecking();
bool isGrammarCheckingEnabled();
void toggleGrammarChecking();
void ignoreSpelling();
void learnSpelling();
int spellCheckerDocumentTag();
- bool isSelectionUngrammatical();
+ WEBCORE_EXPORT bool isSelectionUngrammatical();
String misspelledSelectionString() const;
String misspelledWordAtCaretOrRange(Node* clickedNode) const;
Vector<String> guessesForMisspelledWord(const String&) const;
Vector<String> guessesForMisspelledOrUngrammatical(bool& misspelled, bool& ungrammatical);
bool isSpellCheckingEnabledInFocusedNode() const;
bool isSpellCheckingEnabledFor(Node*) const;
- void markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping, bool doReplacement);
+ WEBCORE_EXPORT void markMisspellingsAfterTypingToWord(const VisiblePosition &wordStart, const VisibleSelection& selectionAfterTyping, bool doReplacement);
void markMisspellings(const VisibleSelection&, RefPtr<Range>& firstMisspellingRange);
void markBadGrammar(const VisibleSelection&);
void markMisspellingsAndBadGrammar(const VisibleSelection& spellingSelection, bool markGrammar, const VisibleSelection& grammarSelection);
void markAndReplaceFor(PassRefPtr<SpellCheckRequest>, const Vector<TextCheckingResult>&);
bool isOverwriteModeEnabled() const { return m_overwriteModeEnabled; }
- void toggleOverwriteModeEnabled();
+ WEBCORE_EXPORT void toggleOverwriteModeEnabled();
void markAllMisspellingsAndBadGrammarInRanges(TextCheckingTypeMask, Range* spellingRange, Range* grammarRange);
#if PLATFORM(IOS)
@@ -251,8 +280,8 @@ public:
void changeBackToReplacedString(const String& replacedString);
#if !PLATFORM(IOS)
- void advanceToNextMisspelling(bool startBeforeSelection = false);
-#endif // !PLATFORM(IOS)
+ WEBCORE_EXPORT void advanceToNextMisspelling(bool startBeforeSelection = false);
+#endif
void showSpellingGuessPanel();
bool spellingPanelIsShowing();
@@ -275,24 +304,24 @@ public:
void showColorPanel();
void toggleBold();
void toggleUnderline();
- void setBaseWritingDirection(WritingDirection);
+ WEBCORE_EXPORT void setBaseWritingDirection(WritingDirection);
// smartInsertDeleteEnabled and selectTrailingWhitespaceEnabled are
// mutually exclusive, meaning that enabling one will disable the other.
bool smartInsertDeleteEnabled();
bool isSelectTrailingWhitespaceEnabled();
- bool hasBidiSelection() const;
+ WEBCORE_EXPORT bool hasBidiSelection() const;
// international text input composition
bool hasComposition() const { return m_compositionNode; }
- void setComposition(const String&, const Vector<CompositionUnderline>&, unsigned selectionStart, unsigned selectionEnd);
- void confirmComposition();
- void confirmComposition(const String&); // if no existing composition, replaces selection
- void cancelComposition();
+ WEBCORE_EXPORT void setComposition(const String&, const Vector<CompositionUnderline>&, unsigned selectionStart, unsigned selectionEnd);
+ WEBCORE_EXPORT void confirmComposition();
+ WEBCORE_EXPORT void confirmComposition(const String&); // if no existing composition, replaces selection
+ WEBCORE_EXPORT void cancelComposition();
bool cancelCompositionIfSelectionIsInvalid();
- PassRefPtr<Range> compositionRange() const;
- bool getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const;
+ WEBCORE_EXPORT RefPtr<Range> compositionRange() const;
+ WEBCORE_EXPORT bool getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const;
// getting international text input composition state (for use by InlineTextBox)
Text* compositionNode() const { return m_compositionNode.get(); }
@@ -301,12 +330,11 @@ public:
bool compositionUsesCustomUnderlines() const { return !m_customCompositionUnderlines.isEmpty(); }
const Vector<CompositionUnderline>& customCompositionUnderlines() const { return m_customCompositionUnderlines; }
- void setIgnoreCompositionSelectionChange(bool);
+ enum class RevealSelection { No, Yes };
+ WEBCORE_EXPORT void setIgnoreCompositionSelectionChange(bool, RevealSelection shouldRevealExistingSelection = RevealSelection::Yes);
bool ignoreCompositionSelectionChange() const { return m_ignoreCompositionSelectionChange; }
- void setStartNewKillRingSequence(bool);
-
- PassRefPtr<Range> rangeForPoint(const IntPoint& windowPoint);
+ WEBCORE_EXPORT PassRefPtr<Range> rangeForPoint(const IntPoint& windowPoint);
void clear();
@@ -317,55 +345,60 @@ public:
EditingBehavior behavior() const;
- PassRefPtr<Range> selectedRange();
+ RefPtr<Range> selectedRange();
#if PLATFORM(IOS)
- void confirmMarkedText();
- void setTextAsChildOfElement(const String&, Element*);
- void setTextAlignmentForChangedBaseWritingDirection(WritingDirection);
- void insertDictationPhrases(PassOwnPtr<Vector<Vector<String> > > dictationPhrases, RetainPtr<id> metadata);
- void setDictationPhrasesAsChildOfElement(PassOwnPtr<Vector<Vector<String> > > dictationPhrases, RetainPtr<id> metadata, Element* element);
+ WEBCORE_EXPORT void confirmMarkedText();
+ WEBCORE_EXPORT void setTextAsChildOfElement(const String&, Element&);
+ WEBCORE_EXPORT void setTextAlignmentForChangedBaseWritingDirection(WritingDirection);
+ WEBCORE_EXPORT void insertDictationPhrases(Vector<Vector<String>>&& dictationPhrases, RetainPtr<id> metadata);
+ WEBCORE_EXPORT void setDictationPhrasesAsChildOfElement(const Vector<Vector<String>>& dictationPhrases, RetainPtr<id> metadata, Element&);
#endif
- void addToKillRing(Range*, bool prepend);
+ enum class KillRingInsertionMode { PrependText, AppendText };
+ void addRangeToKillRing(const Range&, KillRingInsertionMode);
+ void addTextToKillRing(const String&, KillRingInsertionMode);
+ void setStartNewKillRingSequence(bool);
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 handleAlternativeTextUIResult(const String& correction);
+ WEBCORE_EXPORT void handleAlternativeTextUIResult(const String& correction);
void dismissCorrectionPanelAsIgnored();
- void pasteAsFragment(PassRefPtr<DocumentFragment>, bool smartReplace, bool matchStyle);
- void pasteAsPlainText(const String&, bool smartReplace);
+ WEBCORE_EXPORT void pasteAsFragment(Ref<DocumentFragment>&&, bool smartReplace, bool matchStyle, MailBlockquoteHandling = MailBlockquoteHandling::RespectBlockquote);
+ WEBCORE_EXPORT void pasteAsPlainText(const String&, bool smartReplace);
// This is only called on the mac where paste is implemented primarily at the WebKit level.
- void pasteAsPlainTextBypassingDHTML();
+ WEBCORE_EXPORT void pasteAsPlainTextBypassingDHTML();
void clearMisspellingsAndBadGrammar(const VisibleSelection&);
void markMisspellingsAndBadGrammar(const VisibleSelection&);
Node* findEventTargetFrom(const VisibleSelection& selection) const;
- String selectedText() const;
- String selectedTextForClipboard() const;
- bool findString(const String&, FindOptions);
+ WEBCORE_EXPORT String selectedText() const;
+ String selectedTextForDataTransfer() const;
+ WEBCORE_EXPORT bool findString(const String&, FindOptions);
- PassRefPtr<Range> rangeOfString(const String&, Range*, FindOptions);
- PassRefPtr<Range> findStringAndScrollToVisible(const String&, Range*, FindOptions);
+ WEBCORE_EXPORT RefPtr<Range> rangeOfString(const String&, Range*, FindOptions);
const VisibleSelection& mark() const; // Mark, to be used as emacs uses it.
void setMark(const VisibleSelection&);
- void computeAndSetTypingStyle(StyleProperties* , EditAction = EditActionUnspecified);
- void applyEditingStyleToBodyElement() const;
+ void computeAndSetTypingStyle(EditingStyle& , EditAction = EditActionUnspecified);
+ WEBCORE_EXPORT void computeAndSetTypingStyle(StyleProperties& , EditAction = EditActionUnspecified);
+ WEBCORE_EXPORT void applyEditingStyleToBodyElement() const;
void applyEditingStyleToElement(Element*) const;
- IntRect firstRectForRange(Range*) const;
+ WEBCORE_EXPORT IntRect firstRectForRange(Range*) const;
+ void selectionWillChange();
void respondToChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions);
+ WEBCORE_EXPORT void updateEditorUINowIfScheduled();
bool shouldChangeSelection(const VisibleSelection& oldSelection, const VisibleSelection& newSelection, EAffinity, bool stillSelecting) const;
- unsigned countMatchesForText(const String&, Range*, FindOptions, unsigned limit, bool markMatches, Vector<RefPtr<Range>>*);
+ WEBCORE_EXPORT unsigned countMatchesForText(const String&, Range*, FindOptions, unsigned limit, bool markMatches, Vector<RefPtr<Range>>*);
bool markedTextMatchesAreHighlighted() const;
- void setMarkedTextMatchesAreHighlighted(bool);
+ WEBCORE_EXPORT void setMarkedTextMatchesAreHighlighted(bool);
void textFieldDidBeginEditing(Element*);
void textFieldDidEndEditing(Element*);
@@ -373,129 +406,166 @@ public:
bool doTextFieldCommandFromEvent(Element*, KeyboardEvent*);
void textWillBeDeletedInTextField(Element* input);
void textDidChangeInTextArea(Element*);
- WritingDirection baseWritingDirectionForSelectionStart() const;
+ WEBCORE_EXPORT WritingDirection baseWritingDirectionForSelectionStart() const;
- void replaceSelectionWithFragment(PassRefPtr<DocumentFragment>, bool selectReplacement, bool smartReplace, bool matchStyle);
- void replaceSelectionWithText(const String&, bool selectReplacement, bool smartReplace);
- bool selectionStartHasMarkerFor(DocumentMarker::MarkerType, int from, int length) const;
+ WEBCORE_EXPORT void replaceSelectionWithFragment(PassRefPtr<DocumentFragment>, bool selectReplacement, bool smartReplace, bool matchStyle, EditAction = EditActionInsert, MailBlockquoteHandling = MailBlockquoteHandling::RespectBlockquote);
+ WEBCORE_EXPORT void replaceSelectionWithText(const String&, bool selectReplacement, bool smartReplace, EditAction = EditActionInsert);
+ WEBCORE_EXPORT 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();
+ WEBCORE_EXPORT void simplifyMarkup(Node* startNode, Node* endNode);
EditorParagraphSeparator defaultParagraphSeparator() const { return m_defaultParagraphSeparator; }
void setDefaultParagraphSeparator(EditorParagraphSeparator separator) { m_defaultParagraphSeparator = separator; }
- Vector<String> dictationAlternativesForMarker(const DocumentMarker*);
+ Vector<String> dictationAlternativesForMarker(const DocumentMarker&);
void applyDictationAlternativelternative(const String& alternativeString);
- PassRefPtr<Range> avoidIntersectionWithDeleteButtonController(const Range*) const;
- VisibleSelection avoidIntersectionWithDeleteButtonController(const VisibleSelection&) const;
-
#if USE(APPKIT)
- void uppercaseWord();
- void lowercaseWord();
- void capitalizeWord();
+ WEBCORE_EXPORT void uppercaseWord();
+ WEBCORE_EXPORT void lowercaseWord();
+ WEBCORE_EXPORT void capitalizeWord();
#endif
#if USE(AUTOMATIC_TEXT_REPLACEMENT)
- void showSubstitutionsPanel();
- bool substitutionsPanelIsShowing();
- void toggleSmartInsertDelete();
- bool isAutomaticQuoteSubstitutionEnabled();
- void toggleAutomaticQuoteSubstitution();
- bool isAutomaticLinkDetectionEnabled();
- void toggleAutomaticLinkDetection();
- bool isAutomaticDashSubstitutionEnabled();
- void toggleAutomaticDashSubstitution();
- bool isAutomaticTextReplacementEnabled();
- void toggleAutomaticTextReplacement();
- bool isAutomaticSpellingCorrectionEnabled();
- void toggleAutomaticSpellingCorrection();
+ WEBCORE_EXPORT void showSubstitutionsPanel();
+ WEBCORE_EXPORT bool substitutionsPanelIsShowing();
+ WEBCORE_EXPORT void toggleSmartInsertDelete();
+ WEBCORE_EXPORT bool isAutomaticQuoteSubstitutionEnabled();
+ WEBCORE_EXPORT void toggleAutomaticQuoteSubstitution();
+ WEBCORE_EXPORT bool isAutomaticLinkDetectionEnabled();
+ WEBCORE_EXPORT void toggleAutomaticLinkDetection();
+ WEBCORE_EXPORT bool isAutomaticDashSubstitutionEnabled();
+ WEBCORE_EXPORT void toggleAutomaticDashSubstitution();
+ WEBCORE_EXPORT bool isAutomaticTextReplacementEnabled();
+ WEBCORE_EXPORT void toggleAutomaticTextReplacement();
+ WEBCORE_EXPORT bool isAutomaticSpellingCorrectionEnabled();
+ WEBCORE_EXPORT void toggleAutomaticSpellingCorrection();
#endif
-#if ENABLE(DELETION_UI)
- DeleteButtonController& deleteButtonController() const { return *m_deleteButtonController; }
-#endif
+ RefPtr<DocumentFragment> webContentFromPasteboard(Pasteboard&, Range& context, bool allowPlainText, bool& chosePlainText);
+
+ WEBCORE_EXPORT const Font* fontForSelection(bool& hasMultipleFonts) const;
+ WEBCORE_EXPORT static const RenderStyle* styleForSelectionStart(Frame* , Node *&nodeToRemove);
-#if PLATFORM(MAC)
- bool insertParagraphSeparatorInQuotedContent();
- const SimpleFontData* fontForSelection(bool&) const;
- NSDictionary* fontAttributesForSelectionStart() const;
- String stringSelectionForPasteboard();
+#if PLATFORM(COCOA)
+ void getTextDecorationAttributesRespectingTypingStyle(const RenderStyle&, NSMutableDictionary*) const;
+ WEBCORE_EXPORT RetainPtr<NSDictionary> fontAttributesForSelectionStart() const;
+ WEBCORE_EXPORT String stringSelectionForPasteboard();
String stringSelectionForPasteboardWithImageAltText();
- PassRefPtr<DocumentFragment> webContentFromPasteboard(Pasteboard&, Range& context, bool allowPlainText, bool& chosePlainText);
#if !PLATFORM(IOS)
bool canCopyExcludingStandaloneImages();
void takeFindStringFromSelection();
- void readSelectionFromPasteboard(const String& pasteboardName);
- PassRefPtr<SharedBuffer> dataSelectionForPasteboard(const String& pasteboardName);
+ WEBCORE_EXPORT void readSelectionFromPasteboard(const String& pasteboardName, MailBlockquoteHandling = MailBlockquoteHandling::RespectBlockquote);
+ WEBCORE_EXPORT void replaceNodeFromPasteboard(Node*, const String& pasteboardName);
+ WEBCORE_EXPORT RefPtr<SharedBuffer> dataSelectionForPasteboard(const String& pasteboardName);
+ WEBCORE_EXPORT void applyFontStyles(const String& fontFamily, double fontSize, unsigned fontTraits);
#endif // !PLATFORM(IOS)
+ WEBCORE_EXPORT void replaceSelectionWithAttributedString(NSAttributedString *, MailBlockquoteHandling = MailBlockquoteHandling::RespectBlockquote);
#endif
-#if PLATFORM(MAC) || PLATFORM(EFL)
- void writeSelectionToPasteboard(Pasteboard&);
- void writeImageToPasteboard(Pasteboard&, Element& imageElement, const URL&, const String& title);
+#if PLATFORM(COCOA) || PLATFORM(GTK)
+ WEBCORE_EXPORT void writeSelectionToPasteboard(Pasteboard&);
+ WEBCORE_EXPORT void writeImageToPasteboard(Pasteboard&, Element& imageElement, const URL&, const String& title);
#endif
+#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
+ void scanSelectionForTelephoneNumbers();
+ const Vector<RefPtr<Range>>& detectedTelephoneNumberRanges() const { return m_detectedTelephoneNumberRanges; }
+#endif
+
+ WEBCORE_EXPORT String stringForCandidateRequest() const;
+ WEBCORE_EXPORT void handleAcceptedCandidate(TextCheckingResult);
+ WEBCORE_EXPORT RefPtr<Range> contextRangeForCandidateRequest() const;
+ RefPtr<Range> rangeForTextCheckingResult(const TextCheckingResult&) const;
+ bool isHandlingAcceptedCandidate() const { return m_isHandlingAcceptedCandidate; }
+
+ void setIsGettingDictionaryPopupInfo(bool b) { m_isGettingDictionaryPopupInfo = b; }
+ bool isGettingDictionaryPopupInfo() const { return m_isGettingDictionaryPopupInfo; }
+
+ Ref<DocumentFragment> createFragmentForImageAndURL(const String&);
+
private:
class WebContentReader;
- explicit Editor(Frame&);
-
Document& document() const;
bool canDeleteRange(Range*) const;
bool canSmartReplaceWithPasteboard(Pasteboard&);
void pasteAsPlainTextWithPasteboard(Pasteboard&);
- void pasteWithPasteboard(Pasteboard*, bool allowPlainText);
+ void pasteWithPasteboard(Pasteboard*, bool allowPlainText, MailBlockquoteHandling = MailBlockquoteHandling::RespectBlockquote);
String plainTextFromPasteboard(const PasteboardPlainText&);
void revealSelectionAfterEditingOperation(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, RevealExtentOption = DoNotRevealExtent);
void markMisspellingsOrBadGrammar(const VisibleSelection&, bool checkSpelling, RefPtr<Range>& firstMisspellingRange);
- TextCheckingTypeMask resolveTextCheckingTypeMask(TextCheckingTypeMask);
+ TextCheckingTypeMask resolveTextCheckingTypeMask(const Node& rootEditableElement, TextCheckingTypeMask);
- String selectedText(TextIteratorBehavior) const;
+ WEBCORE_EXPORT String selectedText(TextIteratorBehavior) const;
void selectComposition();
enum SetCompositionMode { ConfirmComposition, CancelComposition };
void setComposition(const String&, SetCompositionMode);
void changeSelectionAfterCommand(const VisibleSelection& newSelection, FrameSelection::SetSelectionOptions);
- void notifyComponentsOnChangedSelection(const VisibleSelection& oldSelection, FrameSelection::SetSelectionOptions);
+
+ enum EditorActionSpecifier { CutAction, CopyAction };
+ void performCutOrCopy(EditorActionSpecifier);
+
+ void editorUIUpdateTimerFired();
Node* findEventTargetFromSelection() const;
bool unifiedTextCheckerEnabled() const;
-#if PLATFORM(MAC)
- PassRefPtr<SharedBuffer> selectionInWebArchiveFormat();
- PassRefPtr<Range> adjustedSelectionRange();
- PassRefPtr<DocumentFragment> createFragmentForImageResourceAndAddResource(PassRefPtr<ArchiveResource>);
- PassRefPtr<DocumentFragment> createFragmentAndAddResources(NSAttributedString *);
+ RefPtr<Range> adjustedSelectionRange();
+
+#if PLATFORM(COCOA)
+ RefPtr<SharedBuffer> selectionInWebArchiveFormat();
+ String selectionInHTMLFormat();
+ RefPtr<SharedBuffer> imageInWebArchiveFormat(Element&);
+ RefPtr<DocumentFragment> createFragmentForImageResourceAndAddResource(RefPtr<ArchiveResource>&&);
+ RefPtr<DocumentFragment> createFragmentAndAddResources(NSAttributedString *);
+ FragmentAndResources createFragment(NSAttributedString *);
void fillInUserVisibleForm(PasteboardURL&);
+
+ static RefPtr<SharedBuffer> dataInRTFDFormat(NSAttributedString *);
+ static RefPtr<SharedBuffer> dataInRTFFormat(NSAttributedString *);
#endif
+ void postTextStateChangeNotificationForCut(const String&, const VisibleSelection&);
+
Frame& m_frame;
-#if ENABLE(DELETION_UI)
- OwnPtr<DeleteButtonController> m_deleteButtonController;
-#endif
RefPtr<CompositeEditCommand> m_lastEditCommand;
RefPtr<Text> m_compositionNode;
unsigned m_compositionStart;
unsigned m_compositionEnd;
Vector<CompositionUnderline> m_customCompositionUnderlines;
- bool m_ignoreCompositionSelectionChange;
- bool m_shouldStartNewKillRingSequence;
- bool m_shouldStyleWithCSS;
- const OwnPtr<KillRing> m_killRing;
- const OwnPtr<SpellChecker> m_spellChecker;
- const OwnPtr<AlternativeTextController> m_alternativeTextController;
+ bool m_ignoreCompositionSelectionChange { false };
+ bool m_shouldStartNewKillRingSequence { false };
+ bool m_shouldStyleWithCSS { false };
+ const std::unique_ptr<KillRing> m_killRing;
+ const std::unique_ptr<SpellChecker> m_spellChecker;
+ const std::unique_ptr<AlternativeTextController> m_alternativeTextController;
VisibleSelection m_mark;
- bool m_areMarkedTextMatchesHighlighted;
- EditorParagraphSeparator m_defaultParagraphSeparator;
- bool m_overwriteModeEnabled;
+ bool m_areMarkedTextMatchesHighlighted { false };
+ EditorParagraphSeparator m_defaultParagraphSeparator { EditorParagraphSeparatorIsDiv };
+ bool m_overwriteModeEnabled { false };
+
+ VisibleSelection m_oldSelectionForEditorUIUpdate;
+ Timer m_editorUIUpdateTimer;
+ bool m_editorUIUpdateTimerShouldCheckSpellingAndGrammar { false };
+ bool m_editorUIUpdateTimerWasTriggeredByDictation { false };
+ bool m_isHandlingAcceptedCandidate { false };
+
+#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
+ bool shouldDetectTelephoneNumbers();
+ void scanRangeForTelephoneNumbers(Range&, const StringView&, Vector<RefPtr<Range>>& markedRanges);
+
+ Timer m_telephoneNumberDetectionUpdateTimer;
+ Vector<RefPtr<Range>> m_detectedTelephoneNumberRanges;
+#endif
+
+ bool m_isGettingDictionaryPopupInfo { false };
};
inline void Editor::setStartNewKillRingSequence(bool flag)
@@ -518,20 +588,10 @@ inline bool Editor::markedTextMatchesAreHighlighted() const
return m_areMarkedTextMatchesHighlighted;
}
-#if !ENABLE(DELETION_UI)
-
-inline PassRefPtr<Range> Editor::avoidIntersectionWithDeleteButtonController(const Range* range) const
-{
- return const_cast<Range*>(range);
-}
-
-inline VisibleSelection Editor::avoidIntersectionWithDeleteButtonController(const VisibleSelection& selection) const
-{
- return selection;
-}
-
-#endif
-
} // namespace WebCore
-#endif // Editor_h
+#if PLATFORM(COCOA)
+// This function is declared here but defined in the WebKitLegacy framework.
+// FIXME: Get rid of this and change this so it doesn't use WebKitLegacy.
+extern "C" void _WebCreateFragment(WebCore::Document&, NSAttributedString *, WebCore::FragmentAndResources&);
+#endif
diff --git a/Source/WebCore/editing/EditorCommand.cpp b/Source/WebCore/editing/EditorCommand.cpp
index 5a916153b..06637d7dc 100644
--- a/Source/WebCore/editing/EditorCommand.cpp
+++ b/Source/WebCore/editing/EditorCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006-2008, 2014, 2016 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) 2009 Igalia S.L.
*
@@ -12,10 +12,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -36,7 +36,6 @@
#include "EditorClient.h"
#include "Event.h"
#include "EventHandler.h"
-#include "ExceptionCodePlaceholder.h"
#include "FormatBlockCommand.h"
#include "Frame.h"
#include "FrameView.h"
@@ -57,6 +56,7 @@
#include "StyleProperties.h"
#include "TypingCommand.h"
#include "UnlinkCommand.h"
+#include "UserGestureIndicator.h"
#include "UserTypingGestureIndicator.h"
#include "htmlediting.h"
#include "markup.h"
@@ -74,17 +74,14 @@ public:
TriState (*state)(Frame&, Event*);
String (*value)(Frame&, Event*);
bool isTextInsertion;
- bool allowExecutionWhenDisabled;
+ bool (*allowExecutionWhenDisabled)(EditorCommandSource);
};
-typedef HashMap<String, const EditorInternalCommand*, CaseFoldingHash> CommandMap;
+typedef HashMap<String, const EditorInternalCommand*, ASCIICaseInsensitiveHash> CommandMap;
static const bool notTextInsertion = false;
static const bool isTextInsertion = true;
-static const bool allowExecutionWhenDisabled = true;
-static const bool doNotAllowExecutionWhenDisabled = false;
-
// Related to Editor::selectionForCommand.
// Certain operations continue to use the target control's selection even if the event handler
// already moved the selection outside of the text control.
@@ -98,77 +95,46 @@ static Frame* targetFrame(Frame& frame, Event* event)
return node->document().frame();
}
-static bool applyCommandToFrame(Frame& frame, EditorCommandSource source, EditAction action, StyleProperties* style)
+static bool applyCommandToFrame(Frame& frame, EditorCommandSource source, EditAction action, Ref<EditingStyle>&& style)
{
// FIXME: We don't call shouldApplyStyle when the source is DOM; is there a good reason for that?
switch (source) {
case CommandFromMenuOrKeyBinding:
- frame.editor().applyStyleToSelection(style, action);
+ frame.editor().applyStyleToSelection(WTFMove(style), action);
return true;
case CommandFromDOM:
case CommandFromDOMWithUserInterface:
- frame.editor().applyStyle(style);
+ frame.editor().applyStyle(WTFMove(style), EditActionUnspecified);
return true;
}
ASSERT_NOT_REACHED();
return false;
}
-static bool executeApplyStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const String& propertyValue)
+static bool isStylePresent(Editor& editor, CSSPropertyID propertyID, const char* onValue)
{
- RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
- style->setProperty(propertyID, propertyValue);
- return applyCommandToFrame(frame, source, action, style.get());
+ // Style is considered present when
+ // Mac: present at the beginning of selection
+ // Windows: present throughout the selection
+ if (editor.behavior().shouldToggleStyleBasedOnStartOfSelection())
+ return editor.selectionStartHasStyle(propertyID, onValue);
+ return editor.selectionHasStyle(propertyID, onValue) == TrueTriState;
}
-static bool executeApplyStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, CSSValueID propertyValue)
+static bool executeApplyStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const String& propertyValue)
{
- RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
- style->setProperty(propertyID, propertyValue);
- return applyCommandToFrame(frame, source, action, style.get());
+ return applyCommandToFrame(frame, source, action, EditingStyle::create(propertyID, propertyValue));
}
-// 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, CSSPropertyID propertyID, CSSValue* value)
+static bool executeApplyStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, CSSValueID propertyValue)
{
- RefPtr<EditingStyle> selectionStyle = EditingStyle::styleAtSelectionStart(frame.selection().selection());
- if (!selectionStyle || !selectionStyle->style())
- return false;
-
- RefPtr<CSSValue> selectedCSSValue = selectionStyle->style()->getPropertyCSSValue(propertyID);
- String newStyle = ASCIILiteral("none");
- if (selectedCSSValue->isValueList()) {
- RefPtr<CSSValueList> selectedCSSValueList = toCSSValueList(selectedCSSValue.get());
- if (!selectedCSSValueList->removeAll(value))
- selectedCSSValueList->append(value);
- if (selectedCSSValueList->length())
- newStyle = selectedCSSValueList->cssText();
-
- } else if (selectedCSSValue->cssText() == "none")
- newStyle = value->cssText();
-
- // FIXME: We shouldn't be having to convert new style into text. We should have setPropertyCSSValue.
- RefPtr<MutableStyleProperties> newMutableStyle = MutableStyleProperties::create();
- newMutableStyle->setProperty(propertyID, newStyle);
- return applyCommandToFrame(frame, source, action, newMutableStyle.get());
+ return applyCommandToFrame(frame, source, action, EditingStyle::create(propertyID, propertyValue));
}
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
- // other: present throughout the selection
-
- bool styleIsPresent;
- if (frame.editor().behavior().shouldToggleStyleBasedOnStartOfSelection())
- styleIsPresent = frame.editor().selectionStartHasStyle(propertyID, onValue);
- else
- styleIsPresent = frame.editor().selectionHasStyle(propertyID, onValue) == TrueTriState;
-
- RefPtr<EditingStyle> style = EditingStyle::create(propertyID, styleIsPresent ? offValue : onValue);
- return applyCommandToFrame(frame, source, action, style->style());
+ bool styleIsPresent = isStylePresent(frame.editor(), propertyID, onValue);
+ return applyCommandToFrame(frame, source, action, EditingStyle::create(propertyID, styleIsPresent ? offValue : onValue));
}
static bool executeApplyParagraphStyle(Frame& frame, EditorCommandSource source, EditAction action, CSSPropertyID propertyID, const String& propertyValue)
@@ -192,18 +158,16 @@ static bool executeApplyParagraphStyle(Frame& frame, EditorCommandSource source,
static bool executeInsertFragment(Frame& frame, PassRefPtr<DocumentFragment> fragment)
{
ASSERT(frame.document());
- applyCommand(ReplaceSelectionCommand::create(*frame.document(), fragment, ReplaceSelectionCommand::PreventNesting, EditActionUnspecified));
+ applyCommand(ReplaceSelectionCommand::create(*frame.document(), fragment, ReplaceSelectionCommand::PreventNesting, EditActionInsert));
return true;
}
-static bool executeInsertNode(Frame& frame, PassRefPtr<Node> content)
+static bool executeInsertNode(Frame& frame, Ref<Node>&& content)
{
- RefPtr<DocumentFragment> fragment = DocumentFragment::create(*frame.document());
- ExceptionCode ec = 0;
- fragment->appendChild(content, ec);
- if (ec)
+ auto fragment = DocumentFragment::create(*frame.document());
+ if (fragment->appendChild(content).hasException())
return false;
- return executeInsertFragment(frame, fragment.release());
+ return executeInsertFragment(frame, WTFMove(fragment));
}
static bool expandSelectionToGranularity(Frame& frame, TextGranularity granularity)
@@ -213,10 +177,10 @@ static bool expandSelectionToGranularity(Frame& frame, TextGranularity granulari
RefPtr<Range> newRange = selection.toNormalizedRange();
if (!newRange)
return false;
- if (newRange->collapsed(IGNORE_EXCEPTION))
+ if (newRange->collapsed())
return false;
- RefPtr<Range> oldRange = frame.selection().selection().toNormalizedRange();
- EAffinity affinity = frame.selection().affinity();
+ RefPtr<Range> oldRange = selection.toNormalizedRange();
+ EAffinity affinity = selection.affinity();
if (!frame.editor().client()->shouldChangeSelectedRange(oldRange.get(), newRange.get(), affinity, false))
return false;
frame.selection().setSelectedRange(newRange.get(), affinity, true);
@@ -251,22 +215,21 @@ static unsigned verticalScrollDistance(Frame& frame)
Element* focusedElement = frame.document()->focusedElement();
if (!focusedElement)
return 0;
- auto renderer = focusedElement->renderer();
- if (!renderer || !renderer->isBox())
+ auto* renderer = focusedElement->renderer();
+ if (!is<RenderBox>(renderer))
return 0;
const RenderStyle& style = renderer->style();
if (!(style.overflowY() == OSCROLL || style.overflowY() == OAUTO || focusedElement->hasEditableStyle()))
return 0;
- int height = std::min<int>(toRenderBox(renderer)->clientHeight(), frame.view()->visibleHeight());
- return static_cast<unsigned>(std::max(std::max<int>(height * Scrollbar::minFractionToStepWhenPaging(), height - Scrollbar::maxOverlapBetweenPages()), 1));
+ int height = std::min<int>(downcast<RenderBox>(*renderer).clientHeight(), frame.view()->visibleHeight());
+ return static_cast<unsigned>(Scrollbar::pageStep(height));
}
-static RefPtr<Range> unionDOMRanges(Range* a, Range* b)
+static RefPtr<Range> unionDOMRanges(Range& a, Range& b)
{
- Range* start = a->compareBoundaryPoints(Range::START_TO_START, b, ASSERT_NO_EXCEPTION) <= 0 ? a : b;
- Range* end = a->compareBoundaryPoints(Range::END_TO_END, b, ASSERT_NO_EXCEPTION) <= 0 ? b : a;
-
- return Range::create(a->ownerDocument(), start->startContainer(), start->startOffset(), end->endContainer(), end->endOffset());
+ Range& start = a.compareBoundaryPoints(Range::START_TO_START, b).releaseReturnValue() <= 0 ? a : b;
+ Range& end = a.compareBoundaryPoints(Range::END_TO_END, b).releaseReturnValue() <= 0 ? b : a;
+ return Range::create(a.ownerDocument(), &start.startContainer(), start.startOffset(), &end.endContainer(), end.endOffset());
}
// Execute command functions
@@ -302,19 +265,17 @@ static bool executeCut(Frame& frame, Event*, EditorCommandSource source, const S
return true;
}
-#if PLATFORM(IOS)
static bool executeClearText(Frame& frame, Event*, EditorCommandSource, const String&)
{
frame.editor().clearText();
return true;
}
-#endif
static bool executeDefaultParagraphSeparator(Frame& frame, Event*, EditorCommandSource, const String& value)
{
- if (equalIgnoringCase(value, "div"))
+ if (equalLettersIgnoringASCIICase(value, "div"))
frame.editor().setDefaultParagraphSeparator(EditorParagraphSeparatorIsDiv);
- else if (equalIgnoringCase(value, "p"))
+ else if (equalLettersIgnoringASCIICase(value, "p"))
frame.editor().setDefaultParagraphSeparator(EditorParagraphSeparatorIsP);
return true;
@@ -391,8 +352,8 @@ static bool executeDeleteToMark(Frame& frame, Event*, EditorCommandSource, const
{
RefPtr<Range> mark = frame.editor().mark().toNormalizedRange();
FrameSelection& selection = frame.selection();
- if (mark) {
- bool selected = selection.setSelectedRange(unionDOMRanges(mark.get(), frame.editor().selectedRange().get()).get(), DOWNSTREAM, true);
+ if (mark && frame.editor().selectedRange()) {
+ bool selected = selection.setSelectedRange(unionDOMRanges(*mark, *frame.editor().selectedRange()).get(), DOWNSTREAM, true);
ASSERT(selected);
if (!selected)
return false;
@@ -416,7 +377,7 @@ static bool executeDeleteWordForward(Frame& frame, Event*, EditorCommandSource,
static bool executeFindString(Frame& frame, Event*, EditorCommandSource, const String& value)
{
- return frame.editor().findString(value, CaseInsensitive | WrapAround);
+ return frame.editor().findString(value, CaseInsensitive | WrapAround | DoNotTraverseFlatTree);
}
static bool executeFontName(Frame& frame, Event*, EditorCommandSource source, const String& value)
@@ -444,18 +405,17 @@ static bool executeForeColor(Frame& frame, Event*, EditorCommandSource source, c
static bool executeFormatBlock(Frame& frame, Event*, EditorCommandSource, const String& value)
{
- String tagName = value.lower();
+ String tagName = value.convertToASCIILowercase();
if (tagName[0] == '<' && tagName[tagName.length() - 1] == '>')
tagName = tagName.substring(1, tagName.length() - 2);
- String localName, prefix;
- if (!Document::parseQualifiedName(tagName, prefix, localName, IGNORE_EXCEPTION))
+ auto qualifiedTagName = Document::parseQualifiedName(xhtmlNamespaceURI, tagName);
+ if (qualifiedTagName.hasException())
return false;
- QualifiedName qualifiedTagName(prefix, localName, xhtmlNamespaceURI);
ASSERT(frame.document());
- RefPtr<FormatBlockCommand> command = FormatBlockCommand::create(*frame.document(), qualifiedTagName);
- applyCommand(command);
+ auto command = FormatBlockCommand::create(*frame.document(), qualifiedTagName.releaseReturnValue());
+ applyCommand(command.copyRef());
return command->didApply();
}
@@ -492,35 +452,35 @@ static bool executeIndent(Frame& frame, Event*, EditorCommandSource, const Strin
static bool executeInsertBacktab(Frame& frame, Event* event, EditorCommandSource, const String&)
{
- return targetFrame(frame, event)->eventHandler().handleTextInputEvent("\t", event, TextEventInputBackTab);
+ return targetFrame(frame, event)->eventHandler().handleTextInputEvent(ASCIILiteral("\t"), event, TextEventInputBackTab);
}
static bool executeInsertHorizontalRule(Frame& frame, Event*, EditorCommandSource, const String& value)
{
- RefPtr<HTMLHRElement> rule = HTMLHRElement::create(*frame.document());
+ Ref<HTMLHRElement> rule = HTMLHRElement::create(*frame.document());
if (!value.isEmpty())
rule->setIdAttribute(value);
- return executeInsertNode(frame, rule.release());
+ return executeInsertNode(frame, WTFMove(rule));
}
static bool executeInsertHTML(Frame& frame, Event*, EditorCommandSource, const String& value)
{
- return executeInsertFragment(frame, createFragmentFromMarkup(*frame.document(), value, ""));
+ return executeInsertFragment(frame, createFragmentFromMarkup(*frame.document(), value, emptyString()));
}
static bool executeInsertImage(Frame& frame, Event*, EditorCommandSource, const String& value)
{
// FIXME: If userInterface is true, we should display a dialog box and let the user choose a local image.
- RefPtr<HTMLImageElement> image = HTMLImageElement::create(*frame.document());
+ Ref<HTMLImageElement> image = HTMLImageElement::create(*frame.document());
image->setSrc(value);
- return executeInsertNode(frame, image.release());
+ return executeInsertNode(frame, WTFMove(image));
}
static bool executeInsertLineBreak(Frame& frame, Event* event, EditorCommandSource source, const String&)
{
switch (source) {
case CommandFromMenuOrKeyBinding:
- return targetFrame(frame, event)->eventHandler().handleTextInputEvent("\n", event, TextEventInputLineBreak);
+ return targetFrame(frame, event)->eventHandler().handleTextInputEvent(ASCIILiteral("\n"), event, TextEventInputLineBreak);
case CommandFromDOM:
case CommandFromDOMWithUserInterface:
// Doesn't scroll to make the selection visible, or modify the kill ring.
@@ -536,7 +496,7 @@ static bool executeInsertLineBreak(Frame& frame, Event* event, EditorCommandSour
static bool executeInsertNewline(Frame& frame, Event* event, EditorCommandSource, const String&)
{
Frame* targetFrame = WebCore::targetFrame(frame, event);
- return targetFrame->eventHandler().handleTextInputEvent("\n", event, targetFrame->editor().canEditRichly() ? TextEventInputKeyboard : TextEventInputLineBreak);
+ return targetFrame->eventHandler().handleTextInputEvent(ASCIILiteral("\n"), event, targetFrame->editor().canEditRichly() ? TextEventInputKeyboard : TextEventInputLineBreak);
}
static bool executeInsertNewlineInQuotedContent(Frame& frame, Event*, EditorCommandSource, const String&)
@@ -560,7 +520,7 @@ static bool executeInsertParagraph(Frame& frame, Event*, EditorCommandSource, co
static bool executeInsertTab(Frame& frame, Event* event, EditorCommandSource, const String&)
{
- return targetFrame(frame, event)->eventHandler().handleTextInputEvent("\t", event);
+ return targetFrame(frame, event)->eventHandler().handleTextInputEvent(ASCIILiteral("\t"), event);
}
static bool executeInsertText(Frame& frame, Event*, EditorCommandSource, const String& value)
@@ -578,22 +538,22 @@ static bool executeInsertUnorderedList(Frame& frame, Event*, EditorCommandSource
static bool executeJustifyCenter(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- return executeApplyParagraphStyle(frame, source, EditActionCenter, CSSPropertyTextAlign, "center");
+ return executeApplyParagraphStyle(frame, source, EditActionCenter, CSSPropertyTextAlign, ASCIILiteral("center"));
}
static bool executeJustifyFull(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- return executeApplyParagraphStyle(frame, source, EditActionJustify, CSSPropertyTextAlign, "justify");
+ return executeApplyParagraphStyle(frame, source, EditActionJustify, CSSPropertyTextAlign, ASCIILiteral("justify"));
}
static bool executeJustifyLeft(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- return executeApplyParagraphStyle(frame, source, EditActionAlignLeft, CSSPropertyTextAlign, "left");
+ return executeApplyParagraphStyle(frame, source, EditActionAlignLeft, CSSPropertyTextAlign, ASCIILiteral("left"));
}
static bool executeJustifyRight(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- return executeApplyParagraphStyle(frame, source, EditActionAlignRight, CSSPropertyTextAlign, "right");
+ return executeApplyParagraphStyle(frame, source, EditActionAlignRight, CSSPropertyTextAlign, ASCIILiteral("right"));
}
static bool executeMakeTextWritingDirectionLeftToRight(Frame& frame, Event*, EditorCommandSource, const String&)
@@ -970,7 +930,7 @@ static bool executePrint(Frame& frame, Event*, EditorCommandSource, const String
Page* page = frame.page();
if (!page)
return false;
- page->chrome().print(&frame);
+ page->chrome().print(frame);
return true;
}
@@ -1045,7 +1005,7 @@ static bool executeSelectToMark(Frame& frame, Event*, EditorCommandSource, const
systemBeep();
return false;
}
- frame.selection().setSelectedRange(unionDOMRanges(mark.get(), selection.get()).get(), DOWNSTREAM, true);
+ frame.selection().setSelectedRange(unionDOMRanges(*mark, *selection).get(), DOWNSTREAM, true);
return true;
}
@@ -1060,36 +1020,44 @@ static bool executeSetMark(Frame& frame, Event*, EditorCommandSource, const Stri
return true;
}
+static TextDecorationChange textDecorationChangeForToggling(Editor& editor, CSSPropertyID propertyID, const char* onValue)
+{
+ return isStylePresent(editor, propertyID, onValue) ? TextDecorationChange::Remove : TextDecorationChange::Add;
+}
+
static bool executeStrikethrough(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- RefPtr<CSSPrimitiveValue> lineThrough = CSSPrimitiveValue::createIdentifier(CSSValueLineThrough);
- return executeToggleStyleInList(frame, source, EditActionUnderline, CSSPropertyWebkitTextDecorationsInEffect, lineThrough.get());
+ Ref<EditingStyle> style = EditingStyle::create();
+ style->setStrikeThroughChange(textDecorationChangeForToggling(frame.editor(), CSSPropertyWebkitTextDecorationsInEffect, ASCIILiteral("line-through")));
+ // FIXME: Needs a new EditAction!
+ return applyCommandToFrame(frame, source, EditActionUnderline, WTFMove(style));
}
static bool executeStyleWithCSS(Frame& frame, Event*, EditorCommandSource, const String& value)
{
- frame.editor().setShouldStyleWithCSS(!equalIgnoringCase(value, "false"));
+ frame.editor().setShouldStyleWithCSS(!equalLettersIgnoringASCIICase(value, "false"));
return true;
}
static bool executeUseCSS(Frame& frame, Event*, EditorCommandSource, const String& value)
{
- frame.editor().setShouldStyleWithCSS(equalIgnoringCase(value, "false"));
+ frame.editor().setShouldStyleWithCSS(equalLettersIgnoringASCIICase(value, "false"));
return true;
}
static bool executeSubscript(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- return executeToggleStyle(frame, source, EditActionSubscript, CSSPropertyVerticalAlign, "baseline", "sub");
+ return executeToggleStyle(frame, source, EditActionSubscript, CSSPropertyVerticalAlign, ASCIILiteral("baseline"), ASCIILiteral("sub"));
}
static bool executeSuperscript(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- return executeToggleStyle(frame, source, EditActionSuperscript, CSSPropertyVerticalAlign, "baseline", "super");
+ return executeToggleStyle(frame, source, EditActionSuperscript, CSSPropertyVerticalAlign, ASCIILiteral("baseline"), ASCIILiteral("super"));
}
static bool executeSwapWithMark(Frame& frame, Event*, EditorCommandSource, const String&)
{
+ Ref<Frame> protector(frame);
const VisibleSelection& mark = frame.editor().mark();
const VisibleSelection& selection = frame.selection().selection();
if (mark.isNone() || selection.isNone()) {
@@ -1101,7 +1069,7 @@ static bool executeSwapWithMark(Frame& frame, Event*, EditorCommandSource, const
return true;
}
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
static bool executeTakeFindStringFromSelection(Frame& frame, Event*, EditorCommandSource, const String&)
{
frame.editor().takeFindStringFromSelection();
@@ -1111,12 +1079,12 @@ static bool executeTakeFindStringFromSelection(Frame& frame, Event*, EditorComma
static bool executeToggleBold(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- return executeToggleStyle(frame, source, EditActionBold, CSSPropertyFontWeight, "normal", "bold");
+ return executeToggleStyle(frame, source, EditActionBold, CSSPropertyFontWeight, ASCIILiteral("normal"), ASCIILiteral("bold"));
}
static bool executeToggleItalic(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- return executeToggleStyle(frame, source, EditActionItalics, CSSPropertyFontStyle, "normal", "italic");
+ return executeToggleStyle(frame, source, EditActionItalics, CSSPropertyFontStyle, ASCIILiteral("normal"), ASCIILiteral("italic"));
}
static bool executeTranspose(Frame& frame, Event*, EditorCommandSource, const String&)
@@ -1127,8 +1095,10 @@ static bool executeTranspose(Frame& frame, Event*, EditorCommandSource, const St
static bool executeUnderline(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- RefPtr<CSSPrimitiveValue> underline = CSSPrimitiveValue::createIdentifier(CSSValueUnderline);
- return executeToggleStyleInList(frame, source, EditActionUnderline, CSSPropertyWebkitTextDecorationsInEffect, underline.get());
+ Ref<EditingStyle> style = EditingStyle::create();
+ TextDecorationChange change = textDecorationChangeForToggling(frame.editor(), CSSPropertyWebkitTextDecorationsInEffect, ASCIILiteral("underline"));
+ style->setUnderlineChange(change);
+ return applyCommandToFrame(frame, source, EditActionUnderline, WTFMove(style));
}
static bool executeUndo(Frame& frame, Event*, EditorCommandSource, const String&)
@@ -1146,7 +1116,7 @@ static bool executeUnlink(Frame& frame, Event*, EditorCommandSource, const Strin
static bool executeUnscript(Frame& frame, Event*, EditorCommandSource source, const String&)
{
- return executeApplyStyle(frame, source, EditActionUnscript, CSSPropertyVerticalAlign, "baseline");
+ return executeApplyStyle(frame, source, EditActionUnscript, CSSPropertyVerticalAlign, ASCIILiteral("baseline"));
}
static bool executeUnselect(Frame& frame, Event*, EditorCommandSource, const String&)
@@ -1181,12 +1151,30 @@ static bool supportedFromMenuOrKeyBinding(Frame*)
return false;
}
+static bool defaultValueForSupportedCopyCut(Frame& frame)
+{
+ auto& settings = frame.settings();
+ if (settings.javaScriptCanAccessClipboard())
+ return true;
+
+ switch (settings.clipboardAccessPolicy()) {
+ case ClipboardAccessPolicy::Allow:
+ case ClipboardAccessPolicy::RequiresUserGesture:
+ return true;
+ case ClipboardAccessPolicy::Deny:
+ return false;
+ }
+
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
static bool supportedCopyCut(Frame* frame)
{
if (!frame)
return false;
- bool defaultValue = frame->settings().javaScriptCanAccessClipboard();
+ bool defaultValue = defaultValueForSupportedCopyCut(*frame);
EditorClient* client = frame->editor().client();
return client ? client->canCopyCut(frame, defaultValue) : defaultValue;
@@ -1243,31 +1231,56 @@ static bool enableCaretInEditableText(Frame& frame, Event* event, EditorCommandS
return selection.isCaret() && selection.isContentEditable();
}
-static bool enabledCopy(Frame& frame, Event*, EditorCommandSource)
+static bool allowCopyCutFromDOM(Frame& frame)
{
-#if !PLATFORM(IOS)
- return frame.editor().canDHTMLCopy() || frame.editor().canCopy();
-#else
- return frame.editor().canCopy();
-#endif
+ auto& settings = frame.settings();
+ if (settings.javaScriptCanAccessClipboard())
+ return true;
+
+ switch (settings.clipboardAccessPolicy()) {
+ case ClipboardAccessPolicy::Allow:
+ return true;
+ case ClipboardAccessPolicy::Deny:
+ return false;
+ case ClipboardAccessPolicy::RequiresUserGesture:
+ return UserGestureIndicator::processingUserGesture();
+ }
+
+ ASSERT_NOT_REACHED();
+ return false;
}
-static bool enabledCut(Frame& frame, Event*, EditorCommandSource)
+static bool enabledCopy(Frame& frame, Event*, EditorCommandSource source)
{
-#if !PLATFORM(IOS)
- return frame.editor().canDHTMLCut() || frame.editor().canCut();
-#else
- return frame.editor().canCut();
-#endif
+ switch (source) {
+ case CommandFromMenuOrKeyBinding:
+ return frame.editor().canDHTMLCopy() || frame.editor().canCopy();
+ case CommandFromDOM:
+ case CommandFromDOMWithUserInterface:
+ return allowCopyCutFromDOM(frame) && (frame.editor().canDHTMLCopy() || frame.editor().canCopy());
+ }
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
+static bool enabledCut(Frame& frame, Event*, EditorCommandSource source)
+{
+ switch (source) {
+ case CommandFromMenuOrKeyBinding:
+ return frame.editor().canDHTMLCut() || frame.editor().canCut();
+ case CommandFromDOM:
+ case CommandFromDOMWithUserInterface:
+ return allowCopyCutFromDOM(frame) && (frame.editor().canDHTMLCut() || frame.editor().canCut());
+ }
+ ASSERT_NOT_REACHED();
+ return false;
}
-#if PLATFORM(IOS)
static bool enabledClearText(Frame& frame, Event*, EditorCommandSource)
{
UNUSED_PARAM(frame);
return false;
}
-#endif
static bool enabledInEditableText(Frame& frame, Event* event, EditorCommandSource)
{
@@ -1297,7 +1310,8 @@ static bool enabledInEditableTextOrCaretBrowsing(Frame& frame, Event* event, Edi
static bool enabledInRichlyEditableText(Frame& frame, Event*, EditorCommandSource)
{
- return frame.selection().isCaretOrRange() && frame.selection().isContentRichlyEditable() && frame.selection().rootEditableElement();
+ const VisibleSelection& selection = frame.selection().selection();
+ return selection.isCaretOrRange() && selection.isContentRichlyEditable() && selection.rootEditableElement();
}
static bool enabledPaste(Frame& frame, Event*, EditorCommandSource)
@@ -1307,12 +1321,12 @@ static bool enabledPaste(Frame& frame, Event*, EditorCommandSource)
static bool enabledRangeInEditableText(Frame& frame, Event*, EditorCommandSource)
{
- return frame.selection().isRange() && frame.selection().isContentEditable();
+ return frame.selection().isRange() && frame.selection().selection().isContentEditable();
}
static bool enabledRangeInRichlyEditableText(Frame& frame, Event*, EditorCommandSource)
{
- return frame.selection().isRange() && frame.selection().isContentRichlyEditable();
+ return frame.selection().isRange() && frame.selection().selection().isContentRichlyEditable();
}
static bool enabledRedo(Frame& frame, Event*, EditorCommandSource)
@@ -1320,7 +1334,7 @@ static bool enabledRedo(Frame& frame, Event*, EditorCommandSource)
return frame.editor().canRedo();
}
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
static bool enabledTakeFindStringFromSelection(Frame& frame, Event*, EditorCommandSource)
{
return frame.editor().canCopyExcludingStandaloneImages();
@@ -1341,12 +1355,12 @@ static TriState stateNone(Frame&, Event*)
static TriState stateBold(Frame& frame, Event*)
{
- return stateStyle(frame, CSSPropertyFontWeight, "bold");
+ return stateStyle(frame, CSSPropertyFontWeight, ASCIILiteral("bold"));
}
static TriState stateItalic(Frame& frame, Event*)
{
- return stateStyle(frame, CSSPropertyFontStyle, "italic");
+ return stateStyle(frame, CSSPropertyFontStyle, ASCIILiteral("italic"));
}
static TriState stateOrderedList(Frame& frame, Event*)
@@ -1356,7 +1370,7 @@ static TriState stateOrderedList(Frame& frame, Event*)
static TriState stateStrikethrough(Frame& frame, Event*)
{
- return stateStyle(frame, CSSPropertyWebkitTextDecorationsInEffect, "line-through");
+ return stateStyle(frame, CSSPropertyWebkitTextDecorationsInEffect, ASCIILiteral("line-through"));
}
static TriState stateStyleWithCSS(Frame& frame, Event*)
@@ -1366,12 +1380,12 @@ static TriState stateStyleWithCSS(Frame& frame, Event*)
static TriState stateSubscript(Frame& frame, Event*)
{
- return stateStyle(frame, CSSPropertyVerticalAlign, "sub");
+ return stateStyle(frame, CSSPropertyVerticalAlign, ASCIILiteral("sub"));
}
static TriState stateSuperscript(Frame& frame, Event*)
{
- return stateStyle(frame, CSSPropertyVerticalAlign, "super");
+ return stateStyle(frame, CSSPropertyVerticalAlign, ASCIILiteral("super"));
}
static TriState stateTextWritingDirectionLeftToRight(Frame& frame, Event*)
@@ -1391,7 +1405,7 @@ static TriState stateTextWritingDirectionRightToLeft(Frame& frame, Event*)
static TriState stateUnderline(Frame& frame, Event*)
{
- return stateStyle(frame, CSSPropertyWebkitTextDecorationsInEffect, "underline");
+ return stateStyle(frame, CSSPropertyWebkitTextDecorationsInEffect, ASCIILiteral("underline"));
}
static TriState stateUnorderedList(Frame& frame, Event*)
@@ -1401,22 +1415,22 @@ static TriState stateUnorderedList(Frame& frame, Event*)
static TriState stateJustifyCenter(Frame& frame, Event*)
{
- return stateStyle(frame, CSSPropertyTextAlign, "center");
+ return stateStyle(frame, CSSPropertyTextAlign, ASCIILiteral("center"));
}
static TriState stateJustifyFull(Frame& frame, Event*)
{
- return stateStyle(frame, CSSPropertyTextAlign, "justify");
+ return stateStyle(frame, CSSPropertyTextAlign, ASCIILiteral("justify"));
}
static TriState stateJustifyLeft(Frame& frame, Event*)
{
- return stateStyle(frame, CSSPropertyTextAlign, "left");
+ return stateStyle(frame, CSSPropertyTextAlign, ASCIILiteral("left"));
}
static TriState stateJustifyRight(Frame& frame, Event*)
{
- return stateStyle(frame, CSSPropertyTextAlign, "right");
+ return stateStyle(frame, CSSPropertyTextAlign, ASCIILiteral("right"));
}
// Value functions
@@ -1467,14 +1481,40 @@ static String valueForeColor(Frame& frame, Event*)
static String valueFormatBlock(Frame& frame, Event*)
{
const VisibleSelection& selection = frame.selection().selection();
- if (!selection.isNonOrphanedCaretOrRange() || !selection.isContentEditable())
- return "";
+ if (selection.isNoneOrOrphaned() || !selection.isContentEditable())
+ return emptyString();
Element* formatBlockElement = FormatBlockCommand::elementForFormatBlockCommand(selection.firstRange().get());
if (!formatBlockElement)
- return "";
+ return emptyString();
return formatBlockElement->localName();
}
+// allowExecutionWhenDisabled functions
+
+static bool allowExecutionWhenDisabled(EditorCommandSource)
+{
+ return true;
+}
+
+static bool doNotAllowExecutionWhenDisabled(EditorCommandSource)
+{
+ return false;
+}
+
+static bool allowExecutionWhenDisabledCopyCut(EditorCommandSource source)
+{
+ switch (source) {
+ case CommandFromMenuOrKeyBinding:
+ return true;
+ case CommandFromDOM:
+ case CommandFromDOMWithUserInterface:
+ return false;
+ }
+
+ ASSERT_NOT_REACHED();
+ return false;
+}
+
// Map of functions
struct CommandEntry {
@@ -1490,11 +1530,11 @@ static const CommandMap& createCommandMap()
{ "AlignLeft", { executeJustifyLeft, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "AlignRight", { executeJustifyRight, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "BackColor", { executeBackColor, supported, enabledInRichlyEditableText, stateNone, valueBackColor, notTextInsertion, doNotAllowExecutionWhenDisabled } },
- { "BackwardDelete", { executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, // FIXME: remove BackwardDelete when Safari for Windows stops using it.
{ "Bold", { executeToggleBold, supported, enabledInRichlyEditableText, stateBold, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
- { "Copy", { executeCopy, supportedCopyCut, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
+ { "ClearText", { executeClearText, supported, enabledClearText, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
+ { "Copy", { executeCopy, supportedCopyCut, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabledCopyCut } },
{ "CreateLink", { executeCreateLink, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
- { "Cut", { executeCut, supportedCopyCut, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
+ { "Cut", { executeCut, supportedCopyCut, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabledCopyCut } },
{ "DefaultParagraphSeparator", { executeDefaultParagraphSeparator, supported, enabled, stateNone, valueDefaultParagraphSeparator, notTextInsertion, doNotAllowExecutionWhenDisabled} },
{ "Delete", { executeDelete, supported, enabledDelete, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
{ "DeleteBackward", { executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
@@ -1627,12 +1667,9 @@ static const CommandMap& createCommandMap()
{ "PasteGlobalSelection", { executePasteGlobalSelection, supportedFromMenuOrKeyBinding, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
#endif
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
{ "TakeFindStringFromSelection", { executeTakeFindStringFromSelection, supportedFromMenuOrKeyBinding, enabledTakeFindStringFromSelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
#endif
-#if PLATFORM(IOS)
- { "ClearText", { executeClearText, supported, enabledClearText, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } },
-#endif
};
// These unsupported commands are listed here since they appear in the Microsoft
@@ -1683,9 +1720,9 @@ static const CommandMap& createCommandMap()
CommandMap& commandMap = *new CommandMap;
- for (size_t i = 0; i < WTF_ARRAY_LENGTH(commands); ++i) {
- ASSERT(!commandMap.get(commands[i].name));
- commandMap.set(commands[i].name, &commands[i].command);
+ for (auto& command : commands) {
+ ASSERT(!commandMap.get(command.name));
+ commandMap.set(command.name, &command.command);
}
return commandMap;
@@ -1713,7 +1750,6 @@ bool Editor::commandIsSupportedFromMenuOrKeyBinding(const String& commandName)
}
Editor::Command::Command()
- : m_command(0)
{
}
@@ -1733,10 +1769,14 @@ bool Editor::Command::execute(const String& parameter, Event* triggeringEvent) c
{
if (!isEnabled(triggeringEvent)) {
// Let certain commands be executed when performed explicitly even if they are disabled.
- if (!isSupported() || !m_frame || !m_command->allowExecutionWhenDisabled)
+ if (!allowExecutionWhenDisabled())
return false;
}
- m_frame->document()->updateLayoutIgnorePendingStylesheets();
+ auto document = m_frame->document();
+ document->updateLayoutIgnorePendingStylesheets();
+ if (m_frame->document() != document)
+ return false;
+
return m_command->execute(*m_frame, triggeringEvent, m_source, parameter);
}
@@ -1779,7 +1819,7 @@ String Editor::Command::value(Event* triggeringEvent) const
if (!isSupported() || !m_frame)
return String();
if (m_command->value == valueNull && m_command->state != stateNone)
- return m_command->state(*m_frame, triggeringEvent) == TrueTriState ? "true" : "false";
+ return m_command->state(*m_frame, triggeringEvent) == TrueTriState ? ASCIILiteral("true") : ASCIILiteral("false");
return m_command->value(*m_frame, triggeringEvent);
}
@@ -1788,4 +1828,11 @@ bool Editor::Command::isTextInsertion() const
return m_command && m_command->isTextInsertion;
}
+bool Editor::Command::allowExecutionWhenDisabled() const
+{
+ if (!isSupported() || !m_frame)
+ return false;
+ return m_command->allowExecutionWhenDisabled(m_source);
+}
+
} // namespace WebCore
diff --git a/Source/WebCore/editing/EditorDeleteAction.h b/Source/WebCore/editing/EditorDeleteAction.h
index 00bf6835a..1938aab50 100644
--- a/Source/WebCore/editing/EditorDeleteAction.h
+++ b/Source/WebCore/editing/EditorDeleteAction.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef EditorDeleteAction_h
-#define EditorDeleteAction_h
+#pragma once
namespace WebCore {
@@ -34,7 +33,4 @@ enum EditorDeleteAction {
forwardDeleteKeyAction
};
-} // namespace
-
-#endif
-
+} // namespace WebCore
diff --git a/Source/WebCore/editing/EditorInsertAction.h b/Source/WebCore/editing/EditorInsertAction.h
index b8b137d74..f32551e25 100644
--- a/Source/WebCore/editing/EditorInsertAction.h
+++ b/Source/WebCore/editing/EditorInsertAction.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,18 +23,14 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef EditorInsertAction_h
-#define EditorInsertAction_h
+#pragma once
namespace WebCore {
-// This must be kept in sync with WebViewInsertAction defined in WebEditingDelegate.h
-enum EditorInsertAction {
- EditorInsertActionTyped,
- EditorInsertActionPasted,
- EditorInsertActionDropped
+enum class EditorInsertAction {
+ Typed,
+ Pasted,
+ Dropped,
};
-} // namespace
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/FindOptions.h b/Source/WebCore/editing/FindOptions.h
index ae4aecfa5..bf735ec00 100644
--- a/Source/WebCore/editing/FindOptions.h
+++ b/Source/WebCore/editing/FindOptions.h
@@ -23,8 +23,7 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FindOptions_h
-#define FindOptions_h
+#pragma once
namespace WebCore {
@@ -36,11 +35,12 @@ enum FindOptionFlag {
TreatMedialCapitalAsWordStart = 1 << 2,
Backwards = 1 << 3,
WrapAround = 1 << 4,
- StartInSelection = 1 << 5
+ StartInSelection = 1 << 5,
+ DoNotRevealSelection = 1 << 6,
+ AtWordEnds = 1 << 7,
+ DoNotTraverseFlatTree = 1 << 8,
};
-typedef unsigned FindOptions;
+typedef unsigned short FindOptions;
} // namespace WebCore
-
-#endif // FindOptions_h
diff --git a/Source/WebCore/editing/FormatBlockCommand.cpp b/Source/WebCore/editing/FormatBlockCommand.cpp
index e94a4ee59..bb3a51d6e 100644
--- a/Source/WebCore/editing/FormatBlockCommand.cpp
+++ b/Source/WebCore/editing/FormatBlockCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -24,15 +24,16 @@
*/
#include "config.h"
-#include "Element.h"
#include "FormatBlockCommand.h"
+
#include "Document.h"
-#include "ExceptionCodePlaceholder.h"
-#include "htmlediting.h"
+#include "Element.h"
#include "HTMLElement.h"
#include "HTMLNames.h"
#include "Range.h"
#include "VisibleUnits.h"
+#include "htmlediting.h"
+#include <wtf/NeverDestroyed.h>
namespace WebCore {
@@ -40,9 +41,10 @@ using namespace HTMLNames;
static Node* enclosingBlockToSplitTreeTo(Node* startNode);
static bool isElementForFormatBlock(const QualifiedName& tagName);
+
static inline bool isElementForFormatBlock(Node* node)
{
- return node->isElementNode() && isElementForFormatBlock(toElement(node)->tagQName());
+ return is<Element>(*node) && isElementForFormatBlock(downcast<Element>(*node).tagQName());
}
FormatBlockCommand::FormatBlockCommand(Document& document, const QualifiedName& tagName)
@@ -72,8 +74,8 @@ void FormatBlockCommand::formatRange(const Position& start, const Position& end,
if (!root || !refNode)
return;
if (isElementForFormatBlock(refNode->tagQName()) && start == startOfBlock(start)
- && (end == endOfBlock(end) || isNodeVisiblyContainedWithin(refNode, range.get()))
- && refNode != root && !root->isDescendantOf(refNode)) {
+ && (end == endOfBlock(end) || isNodeVisiblyContainedWithin(*refNode, *range))
+ && refNode != root && !root->isDescendantOf(*refNode)) {
// Already in a block element that only contains the current paragraph
if (refNode->hasTagName(tagName()))
return;
@@ -99,50 +101,50 @@ void FormatBlockCommand::formatRange(const Position& start, const Position& end,
Element* FormatBlockCommand::elementForFormatBlockCommand(Range* range)
{
if (!range)
- return 0;
+ return nullptr;
- Node* commonAncestor = range->commonAncestorContainer(IGNORE_EXCEPTION);
+ Node* commonAncestor = range->commonAncestorContainer();
while (commonAncestor && !isElementForFormatBlock(commonAncestor))
commonAncestor = commonAncestor->parentNode();
if (!commonAncestor)
- return 0;
+ return nullptr;
- Element* rootEditableElement = range->startContainer()->rootEditableElement();
+ Element* rootEditableElement = range->startContainer().rootEditableElement();
if (!rootEditableElement || commonAncestor->contains(rootEditableElement))
- return 0;
+ return nullptr;
- return commonAncestor->isElementNode() ? toElement(commonAncestor) : 0;
+ return commonAncestor->isElementNode() ? downcast<Element>(commonAncestor) : nullptr;
}
bool isElementForFormatBlock(const QualifiedName& tagName)
{
- DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, blockTags, ());
- if (blockTags.isEmpty()) {
- blockTags.add(addressTag);
- blockTags.add(articleTag);
- blockTags.add(asideTag);
- blockTags.add(blockquoteTag);
- blockTags.add(ddTag);
- blockTags.add(divTag);
- blockTags.add(dlTag);
- blockTags.add(dtTag);
- blockTags.add(footerTag);
- blockTags.add(h1Tag);
- blockTags.add(h2Tag);
- blockTags.add(h3Tag);
- blockTags.add(h4Tag);
- blockTags.add(h5Tag);
- blockTags.add(h6Tag);
- blockTags.add(headerTag);
- blockTags.add(hgroupTag);
- blockTags.add(mainTag);
- blockTags.add(navTag);
- blockTags.add(pTag);
- blockTags.add(preTag);
- blockTags.add(sectionTag);
+ static NeverDestroyed<HashSet<QualifiedName>> blockTags;
+ if (blockTags.get().isEmpty()) {
+ blockTags.get().add(addressTag);
+ blockTags.get().add(articleTag);
+ blockTags.get().add(asideTag);
+ blockTags.get().add(blockquoteTag);
+ blockTags.get().add(ddTag);
+ blockTags.get().add(divTag);
+ blockTags.get().add(dlTag);
+ blockTags.get().add(dtTag);
+ blockTags.get().add(footerTag);
+ blockTags.get().add(h1Tag);
+ blockTags.get().add(h2Tag);
+ blockTags.get().add(h3Tag);
+ blockTags.get().add(h4Tag);
+ blockTags.get().add(h5Tag);
+ blockTags.get().add(h6Tag);
+ blockTags.get().add(headerTag);
+ blockTags.get().add(hgroupTag);
+ blockTags.get().add(mainTag);
+ blockTags.get().add(navTag);
+ blockTags.get().add(pTag);
+ blockTags.get().add(preTag);
+ blockTags.get().add(sectionTag);
}
- return blockTags.contains(tagName);
+ return blockTags.get().contains(tagName);
}
Node* enclosingBlockToSplitTreeTo(Node* startNode)
@@ -155,7 +157,7 @@ Node* enclosingBlockToSplitTreeTo(Node* startNode)
return n;
if (isBlock(n))
lastBlock = n;
- if (isListElement(n))
+ if (isListHTMLElement(n))
return n->parentNode()->hasEditableStyle() ? n->parentNode() : n;
}
return lastBlock;
diff --git a/Source/WebCore/editing/FormatBlockCommand.h b/Source/WebCore/editing/FormatBlockCommand.h
index 262536b94..42e4571a4 100644
--- a/Source/WebCore/editing/FormatBlockCommand.h
+++ b/Source/WebCore/editing/FormatBlockCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FormatBlockCommand_h
-#define FormatBlockCommand_h
+#pragma once
#include "ApplyBlockElementCommand.h"
#include "EditAction.h"
@@ -40,12 +39,12 @@ class VisiblePosition;
class FormatBlockCommand : public ApplyBlockElementCommand {
public:
- static PassRefPtr<FormatBlockCommand> create(Document& document, const QualifiedName& tagName)
+ static Ref<FormatBlockCommand> create(Document& document, const QualifiedName& tagName)
{
- return adoptRef(new FormatBlockCommand(document, tagName));
+ return adoptRef(*new FormatBlockCommand(document, tagName));
}
- virtual bool preservesTypingStyle() const { return true; }
+ bool preservesTypingStyle() const override { return true; }
static Element* elementForFormatBlockCommand(Range*);
bool didApply() const { return m_didApply; }
@@ -53,13 +52,11 @@ public:
private:
FormatBlockCommand(Document&, const QualifiedName& tagName);
- void formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection);
- void formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<Element>&);
- EditAction editingAction() const { return EditActionFormatBlock; }
+ void formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection) override;
+ void formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<Element>&) override;
+ EditAction editingAction() const override { return EditActionFormatBlock; }
bool m_didApply;
};
} // namespace WebCore
-
-#endif // FormatBlockCommand_h
diff --git a/Source/WebCore/editing/FrameSelection.cpp b/Source/WebCore/editing/FrameSelection.cpp
index c492cf8b7..847479fad 100644
--- a/Source/WebCore/editing/FrameSelection.cpp
+++ b/Source/WebCore/editing/FrameSelection.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2008, 2009, 2010, 2014-2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -26,6 +26,7 @@
#include "config.h"
#include "FrameSelection.h"
+#include "AXObjectCache.h"
#include "CharacterData.h"
#include "DeleteSelectionCommand.h"
#include "Document.h"
@@ -33,19 +34,20 @@
#include "EditorClient.h"
#include "Element.h"
#include "ElementIterator.h"
-#include "EventHandler.h"
-#include "ExceptionCode.h"
+#include "Event.h"
+#include "EventNames.h"
#include "FloatQuad.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameTree.h"
#include "FrameView.h"
#include "GraphicsContext.h"
+#include "HTMLBodyElement.h"
#include "HTMLFormElement.h"
-#include "HTMLFrameElementBase.h"
-#include "HTMLInputElement.h"
-#include "HTMLSelectElement.h"
+#include "HTMLFrameElement.h"
+#include "HTMLIFrameElement.h"
#include "HTMLNames.h"
+#include "HTMLSelectElement.h"
#include "HitTestRequest.h"
#include "HitTestResult.h"
#include "InlineTextBox.h"
@@ -74,8 +76,6 @@
#include "RenderStyle.h"
#endif
-#define EDIT_DEBUG 0
-
namespace WebCore {
using namespace HTMLNames;
@@ -110,17 +110,20 @@ FrameSelection::FrameSelection(Frame* frame)
: m_frame(frame)
, m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
, m_granularity(CharacterGranularity)
- , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired)
+ , m_caretBlinkTimer(*this, &FrameSelection::caretBlinkTimerFired)
+ , m_appearanceUpdateTimer(*this, &FrameSelection::appearanceUpdateTimerFired)
+ , m_caretInsidePositionFixed(false)
, m_absCaretBoundsDirty(true)
, m_caretPaint(true)
, m_isCaretBlinkingSuspended(false)
, m_focused(frame && frame->page() && frame->page()->focusController().focusedFrame() == frame)
, m_shouldShowBlockCursor(false)
+ , m_pendingSelectionUpdate(false)
+ , m_shouldRevealSelection(false)
+ , m_alwaysAlignCursorOnScrollWhenRevealingSelection(false)
#if PLATFORM(IOS)
, m_updateAppearanceEnabled(false)
, m_caretBlinks(true)
- , m_closeTypingSuppressions(0)
- , m_scrollingSuppressCount(0)
#endif
{
if (shouldAlwaysUseDirectionalSelection(m_frame))
@@ -135,35 +138,40 @@ Element* FrameSelection::rootEditableElementOrDocumentElement() const
void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
{
- SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
- setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align);
+ setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()),
+ defaultSetSelectionOptions(userTriggered), AXTextStateChangeIntent(), align);
}
void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
{
const bool selectionHasDirection = true;
- SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
- setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options);
+ setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered));
}
void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
{
- SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
- setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options);
+ setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), defaultSetSelectionOptions(userTriggered));
}
-void FrameSelection::moveTo(const Range *r, EAffinity affinity, EUserTriggered userTriggered)
+void FrameSelection::moveTo(const Range* range)
{
- SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
- VisibleSelection selection = r ? VisibleSelection(r->startPosition(), r->endPosition(), affinity) : VisibleSelection(Position(), Position(), affinity);
- setSelection(selection, options);
+ VisibleSelection selection = range ? VisibleSelection(range->startPosition(), range->endPosition()) : VisibleSelection();
+ setSelection(selection);
}
void FrameSelection::moveTo(const Position &base, const Position &extent, EAffinity affinity, EUserTriggered userTriggered)
{
const bool selectionHasDirection = true;
- SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
- setSelection(VisibleSelection(base, extent, affinity, selectionHasDirection), options);
+ setSelection(VisibleSelection(base, extent, affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered));
+}
+
+void FrameSelection::moveWithoutValidationTo(const Position& base, const Position& extent, bool selectionHasDirection, bool shouldSetFocus, const AXTextStateChangeIntent& intent)
+{
+ VisibleSelection newSelection;
+ newSelection.setWithoutValidation(base, extent);
+ newSelection.setIsDirectional(selectionHasDirection);
+ AXTextStateChangeIntent newIntent = intent.type == AXTextStateChangeTypeUnknown ? AXTextStateChangeIntent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false }) : intent;
+ setSelection(newSelection, defaultSetSelectionOptions() | (shouldSetFocus ? 0 : DoNotSetFocus), newIntent);
}
void DragCaretController::setCaretPosition(const VisiblePosition& position)
@@ -172,7 +180,7 @@ void DragCaretController::setCaretPosition(const VisiblePosition& position)
invalidateCaretRect(node);
m_position = position;
setCaretRectNeedsUpdate();
- Document* document = 0;
+ Document* document = nullptr;
if (Node* node = m_position.deepEquivalent().deprecatedNode()) {
invalidateCaretRect(node);
document = &node->document();
@@ -220,7 +228,7 @@ static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisibleP
}
}
-void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity,
+void FrameSelection::setSelectionByMouseIfDifferent(const VisibleSelection& passedNewSelection, TextGranularity granularity,
EndPointsAdjustmentMode endpointsAdjustmentMode)
{
VisibleSelection newSelection = passedNewSelection;
@@ -247,144 +255,196 @@ void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection&
if (m_selection == newSelection || !shouldChangeSelection(newSelection))
return;
- setSelection(newSelection, granularity);
+
+ AXTextStateChangeIntent intent;
+ if (AXObjectCache::accessibilityEnabled() && newSelection.isCaret())
+ intent = AXTextStateChangeIntent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false });
+ else
+ intent = AXTextStateChangeIntent();
+ setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent, intent, AlignCursorOnScrollIfNeeded, granularity);
}
-void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
+bool FrameSelection::setSelectionWithoutUpdatingAppearance(const VisibleSelection& newSelectionPossiblyWithoutDirection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
{
bool closeTyping = options & CloseTyping;
bool shouldClearTypingStyle = options & ClearTypingStyle;
- EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
- VisibleSelection s = newSelection;
+ VisibleSelection newSelection = newSelectionPossiblyWithoutDirection;
if (shouldAlwaysUseDirectionalSelection(m_frame))
- s.setIsDirectional(true);
+ newSelection.setIsDirectional(true);
if (!m_frame) {
- m_selection = s;
- return;
+ m_selection = newSelection;
+ return false;
}
// <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
// if document->frame() == m_frame we can get into an infinite loop
- if (s.base().anchorNode()) {
- Document& document = s.base().anchorNode()->document();
- if (document.frame() && document.frame() != m_frame && &document != m_frame->document()) {
- RefPtr<Frame> guard = document.frame();
- document.frame()->selection().setSelection(s, options, align, granularity);
- // It's possible that during the above set selection, this FrameSelection has been modified by
- // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since
- // the frame is about to be destroyed. If this is the case, clear our selection.
- if (guard->hasOneRef() && !m_selection.isNonOrphanedCaretOrRange())
- clear();
- return;
+ if (Document* newSelectionDocument = newSelection.base().document()) {
+ if (RefPtr<Frame> newSelectionFrame = newSelectionDocument->frame()) {
+ if (newSelectionFrame != m_frame && newSelectionDocument != m_frame->document()) {
+ newSelectionFrame->selection().setSelection(newSelection, options, AXTextStateChangeIntent(), align, granularity);
+ // It's possible that during the above set selection, this FrameSelection has been modified by
+ // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since
+ // the frame is about to be destroyed. If this is the case, clear our selection.
+ if (newSelectionFrame->hasOneRef() && m_selection.isNoneOrOrphaned())
+ clear();
+ return false;
+ }
}
}
m_granularity = granularity;
-#if PLATFORM(IOS)
- if (closeTyping && m_closeTypingSuppressions == 0)
-#else
if (closeTyping)
-#endif
TypingCommand::closeTyping(m_frame);
if (shouldClearTypingStyle)
clearTypingStyle();
- if (m_selection == s) {
- // Even if selection was not changed, selection offsets may have been changed.
- updateSelectionCachesIfSelectionIsInsideTextFormControl(userTriggered);
- return;
- }
-
VisibleSelection oldSelection = m_selection;
+ bool didMutateSelection = oldSelection != newSelection;
+ if (didMutateSelection)
+ m_frame->editor().selectionWillChange();
+
+ m_selection = newSelection;
+
+ // Selection offsets should increase when LF is inserted before the caret in InsertLineBreakCommand. See <https://webkit.org/b/56061>.
+ if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(newSelection.start()))
+ textControl->selectionChanged(options & FireSelectEvent);
+
+ if (!didMutateSelection)
+ return false;
- m_selection = s;
setCaretRectNeedsUpdate();
-
- if (!s.isNone() && !(options & DoNotSetFocus))
- setFocusedElementIfNeeded();
- if (!(options & DoNotUpdateAppearance)) {
-#if ENABLE(TEXT_CARET)
- m_frame->document()->updateLayoutIgnorePendingStylesheets();
-#else
- m_frame->document()->updateStyleIfNeeded();
-#endif
- updateAppearance();
- }
+ if (!newSelection.isNone() && !(options & DoNotSetFocus))
+ setFocusedElementIfNeeded();
// Always clear the x position used for vertical arrow navigation.
// It will be restored by the vertical arrow navigation code if necessary.
m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
selectFrameElementInParentIfFullySelected();
- updateSelectionCachesIfSelectionIsInsideTextFormControl(userTriggered);
m_frame->editor().respondToChangedSelection(oldSelection, options);
- if (userTriggered == UserTriggered) {
+ m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
+
+ return true;
+}
+
+void FrameSelection::setSelection(const VisibleSelection& selection, SetSelectionOptions options, AXTextStateChangeIntent intent, CursorAlignOnScroll align, TextGranularity granularity)
+{
+ RefPtr<Frame> protectedFrame(m_frame);
+ if (!setSelectionWithoutUpdatingAppearance(selection, options, align, granularity))
+ return;
+
+ Document* document = m_frame->document();
+ if (!document)
+ return;
+
+ m_shouldRevealSelection = options & RevealSelection;
+ m_alwaysAlignCursorOnScrollWhenRevealingSelection = align == AlignCursorOnScrollAlways;
+
+ m_pendingSelectionUpdate = true;
+
+ if (document->hasPendingStyleRecalc())
+ return;
+
+ FrameView* frameView = document->view();
+ if (frameView && frameView->layoutPending())
+ return;
+
+ updateAndRevealSelection(intent);
+}
+
+static void updateSelectionByUpdatingLayoutOrStyle(Frame& frame)
+{
+#if ENABLE(TEXT_CARET)
+ frame.document()->updateLayoutIgnorePendingStylesheets();
+#else
+ frame.document()->updateStyleIfNeeded();
+#endif
+}
+
+void FrameSelection::setNeedsSelectionUpdate()
+{
+ m_pendingSelectionUpdate = true;
+ if (RenderView* view = m_frame->contentRenderer())
+ view->clearSelection();
+}
+
+void FrameSelection::updateAndRevealSelection(const AXTextStateChangeIntent& intent)
+{
+ if (!m_pendingSelectionUpdate)
+ return;
+
+ m_pendingSelectionUpdate = false;
+
+ updateAppearance();
+
+ if (m_shouldRevealSelection) {
ScrollAlignment alignment;
if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed())
- alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
+ alignment = m_alwaysAlignCursorOnScrollWhenRevealingSelection ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
else
- alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
+ alignment = m_alwaysAlignCursorOnScrollWhenRevealingSelection ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
- revealSelection(alignment, RevealExtent);
+ revealSelection(SelectionRevealMode::Reveal, alignment, RevealExtent);
}
-#if HAVE(ACCESSIBILITY)
- notifyAccessibilityForSelectionChange();
+
+ notifyAccessibilityForSelectionChange(intent);
+
+ if (auto* client = m_frame->editor().client())
+ client->didChangeSelectionAndUpdateLayout();
+}
+
+void FrameSelection::updateDataDetectorsForSelection()
+{
+#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
+ m_frame->editor().scanSelectionForTelephoneNumbers();
#endif
- m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
}
-static bool removingNodeRemovesPosition(Node* node, const Position& position)
+static bool removingNodeRemovesPosition(Node& node, const Position& position)
{
if (!position.anchorNode())
return false;
- if (position.anchorNode() == node)
+ if (position.anchorNode() == &node)
return true;
- if (!node->isElementNode())
+ if (!is<Element>(node))
return false;
- Element* element = toElement(node);
- return element->containsIncludingShadowDOM(position.anchorNode());
-}
-
-static void clearRenderViewSelection(const Position& position)
-{
- Ref<Document> document(position.anchorNode()->document());
- document->updateStyleIfNeeded();
- if (RenderView* view = document->renderView())
- view->clearSelection();
+ return downcast<Element>(node).containsIncludingShadowDOM(position.anchorNode());
}
-void DragCaretController::nodeWillBeRemoved(Node* node)
+void DragCaretController::nodeWillBeRemoved(Node& node)
{
- if (!hasCaret() || (node && !node->inDocument()))
+ if (!hasCaret() || !node.isConnected())
return;
if (!removingNodeRemovesPosition(node, m_position.deepEquivalent()))
return;
- clearRenderViewSelection(m_position.deepEquivalent());
+ if (RenderView* view = node.document().renderView())
+ view->clearSelection();
+
clear();
}
-void FrameSelection::nodeWillBeRemoved(Node* node)
+void FrameSelection::nodeWillBeRemoved(Node& node)
{
// There can't be a selection inside a fragment, so if a fragment's node is being removed,
// the selection in the document that created the fragment needs no adjustment.
- if (isNone() || (node && !node->inDocument()))
+ if (isNone() || !node.isConnected())
return;
respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
}
-void FrameSelection::respondToNodeModification(Node* node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
+void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
{
bool clearRenderTreeSelection = false;
bool clearDOMTreeSelection = false;
@@ -416,19 +476,28 @@ void FrameSelection::respondToNodeModification(Node* node, bool baseRemoved, boo
else
m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
} else if (RefPtr<Range> range = m_selection.firstRange()) {
- ExceptionCode ec = 0;
- Range::CompareResults compareResult = range->compareNode(node, ec);
- if (!ec && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
- // If we did nothing here, when this node's renderer was destroyed, the rect that it
- // occupied would be invalidated, but, selection gaps that change as a result of
- // the removal wouldn't be invalidated.
- // FIXME: Don't do so much unnecessary invalidation.
- clearRenderTreeSelection = true;
+ auto compareNodeResult = range->compareNode(node);
+ if (!compareNodeResult.hasException()) {
+ auto compareResult = compareNodeResult.releaseReturnValue();
+ if (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE) {
+ // If we did nothing here, when this node's renderer was destroyed, the rect that it
+ // occupied would be invalidated, but, selection gaps that change as a result of
+ // the removal wouldn't be invalidated.
+ // FIXME: Don't do so much unnecessary invalidation.
+ clearRenderTreeSelection = true;
+ }
}
}
- if (clearRenderTreeSelection)
- clearRenderViewSelection(m_selection.start());
+ if (clearRenderTreeSelection) {
+ if (auto* renderView = node.document().renderView()) {
+ renderView->clearSelection();
+
+ // Trigger a selection update so the selection will be set again.
+ m_pendingSelectionUpdate = true;
+ renderView->setNeedsLayout();
+ }
+ }
if (clearDOMTreeSelection)
setSelection(VisibleSelection(), DoNotSetFocus);
@@ -457,7 +526,7 @@ static void updatePositionAfterAdoptingTextReplacement(Position& position, Chara
void FrameSelection::textWasReplaced(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
{
// The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
- if (isNone() || !node || !node->inDocument())
+ if (isNone() || !node || !node->isConnected())
return;
Position base = m_selection.base();
@@ -478,7 +547,6 @@ void FrameSelection::textWasReplaced(CharacterData* node, unsigned offset, unsig
else
newSelection.setWithoutValidation(start, end);
- m_frame->document()->updateLayout();
setSelection(newSelection, DoNotSetFocus);
}
}
@@ -490,8 +558,8 @@ TextDirection FrameSelection::directionOfEnclosingBlock()
TextDirection FrameSelection::directionOfSelection()
{
- InlineBox* startBox = 0;
- InlineBox* endBox = 0;
+ InlineBox* startBox = nullptr;
+ InlineBox* endBox = nullptr;
int unusedOffset;
// Cache the VisiblePositions because visibleStart() and visibleEnd()
// can cause layout, which has the potential to invalidate lineboxes.
@@ -558,14 +626,14 @@ void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direct
VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
{
- if (m_frame && m_frame->settings().editingBehaviorType() == EditingMacBehavior)
- return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
- // Linux and Windows always extend selections from the extent endpoint.
// FIXME: VisibleSelection should be fixed to ensure as an invariant that
// base/extent always point to the same nodes as start/end, but which points
// to which depends on the value of isBaseFirst. Then this can be changed
// to just return m_sel.extent().
- return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
+ if (m_frame && m_frame->editor().behavior().shouldAlwaysExtendSelectionFromExtentEndpoint())
+ return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
+
+ return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
}
VisiblePosition FrameSelection::startForPlatform() const
@@ -643,11 +711,9 @@ VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity
// FIXME: implement all of the above?
pos = modifyExtendingForward(granularity);
break;
-#if PLATFORM(IOS)
case DocumentGranularity:
ASSERT_NOT_REACHED();
break;
-#endif
}
#if ENABLE(USERSELECT_ALL)
adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
@@ -674,11 +740,9 @@ VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granulari
case ParagraphGranularity:
pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
break;
-#if PLATFORM(IOS)
case DocumentGranularity:
ASSERT_NOT_REACHED();
break;
-#endif
case SentenceBoundary:
pos = endOfSentence(endForPlatform());
break;
@@ -702,8 +766,10 @@ VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granulari
return pos;
}
-VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
+VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity, bool* reachedBoundary)
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
VisiblePosition pos;
switch (granularity) {
case CharacterGranularity:
@@ -713,13 +779,14 @@ VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
else
pos = VisiblePosition(m_selection.start(), m_selection.affinity());
} else
- pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
+ pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true, reachedBoundary);
break;
case WordGranularity: {
- // 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);
+ VisiblePosition currentPosition(m_selection.extent(), m_selection.affinity());
+ pos = rightWordPosition(currentPosition, skipsSpaceWhenMovingRight);
+ if (reachedBoundary)
+ *reachedBoundary = pos == currentPosition;
break;
}
case SentenceGranularity:
@@ -729,22 +796,38 @@ VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
case ParagraphBoundary:
case DocumentBoundary:
// FIXME: Implement all of the above.
- pos = modifyMovingForward(granularity);
+ pos = modifyMovingForward(granularity, reachedBoundary);
break;
case LineBoundary:
- pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
+ pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock(), reachedBoundary);
break;
-#if PLATFORM(IOS)
case DocumentGranularity:
ASSERT_NOT_REACHED();
break;
-#endif
}
return pos;
}
-VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
+VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity, bool* reachedBoundary)
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
+ VisiblePosition currentPosition;
+ switch (granularity) {
+ case WordGranularity:
+ case SentenceGranularity:
+ currentPosition = VisiblePosition(m_selection.extent(), m_selection.affinity());
+ break;
+ case LineGranularity:
+ case ParagraphGranularity:
+ case SentenceBoundary:
+ case ParagraphBoundary:
+ case DocumentBoundary:
+ currentPosition = endForPlatform();
+ break;
+ default:
+ break;
+ }
VisiblePosition pos;
// FIXME: Stay in editable content for the less common granularities.
switch (granularity) {
@@ -752,47 +835,59 @@ VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
if (isRange())
pos = VisiblePosition(m_selection.end(), m_selection.affinity());
else
- pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary);
+ pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary, reachedBoundary);
break;
case WordGranularity:
- pos = nextWordPositionForPlatform(VisiblePosition(m_selection.extent(), m_selection.affinity()));
+ pos = nextWordPositionForPlatform(currentPosition);
break;
case SentenceGranularity:
- pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
+ pos = nextSentencePosition(currentPosition);
break;
case LineGranularity: {
// down-arrowing from a range selection that ends at the start of a line needs
// to leave the selection at that line start (no need to call nextLinePosition!)
- pos = endForPlatform();
+ pos = currentPosition;
if (!isRange() || !isStartOfLine(pos))
pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
break;
}
case ParagraphGranularity:
- pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
+ pos = nextParagraphPosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
break;
-#if PLATFORM(IOS)
case DocumentGranularity:
ASSERT_NOT_REACHED();
break;
-#endif
case SentenceBoundary:
- pos = endOfSentence(endForPlatform());
+ pos = endOfSentence(currentPosition);
break;
case LineBoundary:
- pos = logicalEndOfLine(endForPlatform());
+ pos = logicalEndOfLine(endForPlatform(), reachedBoundary);
break;
case ParagraphBoundary:
- pos = endOfParagraph(endForPlatform());
+ pos = endOfParagraph(currentPosition);
break;
case DocumentBoundary:
- pos = endForPlatform();
+ pos = currentPosition;
if (isEditablePosition(pos.deepEquivalent()))
pos = endOfEditableContent(pos);
else
pos = endOfDocument(pos);
break;
}
+ switch (granularity) {
+ case WordGranularity:
+ case SentenceGranularity:
+ case LineGranularity:
+ case ParagraphGranularity:
+ case SentenceBoundary:
+ case ParagraphBoundary:
+ case DocumentBoundary:
+ if (reachedBoundary)
+ *reachedBoundary = pos == currentPosition;
+ break;
+ default:
+ break;
+ }
return pos;
}
@@ -832,11 +927,9 @@ VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
case DocumentBoundary:
pos = modifyExtendingBackward(granularity);
break;
-#if PLATFORM(IOS)
case DocumentGranularity:
ASSERT_NOT_REACHED();
break;
-#endif
}
#if ENABLE(USERSELECT_ALL)
adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
@@ -884,11 +977,9 @@ VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granular
else
pos = startOfDocument(pos);
break;
-#if PLATFORM(IOS)
case DocumentGranularity:
ASSERT_NOT_REACHED();
break;
-#endif
}
#if ENABLE(USERSELECT_ALL)
adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
@@ -896,8 +987,10 @@ VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granular
return pos;
}
-VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
+VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity, bool* reachedBoundary)
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
VisiblePosition pos;
switch (granularity) {
case CharacterGranularity:
@@ -907,11 +1000,14 @@ VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
else
pos = VisiblePosition(m_selection.end(), m_selection.affinity());
else
- pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
+ pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true, reachedBoundary);
break;
case WordGranularity: {
bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
- pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
+ VisiblePosition currentPosition(m_selection.extent(), m_selection.affinity());
+ pos = leftWordPosition(currentPosition, skipsSpaceWhenMovingRight);
+ if (reachedBoundary)
+ *reachedBoundary = pos == currentPosition;
break;
}
case SentenceGranularity:
@@ -921,63 +1017,91 @@ VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
case ParagraphBoundary:
case DocumentBoundary:
// FIXME: Implement all of the above.
- pos = modifyMovingBackward(granularity);
+ pos = modifyMovingBackward(granularity, reachedBoundary);
break;
case LineBoundary:
- pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
+ pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock(), reachedBoundary);
break;
-#if PLATFORM(IOS)
case DocumentGranularity:
ASSERT_NOT_REACHED();
break;
-#endif
}
return pos;
}
-VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity)
+VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity, bool* reachedBoundary)
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
+ VisiblePosition currentPosition;
+ switch (granularity) {
+ case WordGranularity:
+ case SentenceGranularity:
+ currentPosition = VisiblePosition(m_selection.extent(), m_selection.affinity());
+ break;
+ case LineGranularity:
+ case ParagraphGranularity:
+ case SentenceBoundary:
+ case ParagraphBoundary:
+ case DocumentBoundary:
+ currentPosition = startForPlatform();
+ break;
+ default:
+ break;
+ }
VisiblePosition pos;
switch (granularity) {
case CharacterGranularity:
if (isRange())
pos = VisiblePosition(m_selection.start(), m_selection.affinity());
else
- pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary);
+ pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary, reachedBoundary);
break;
case WordGranularity:
- pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
+ pos = previousWordPosition(currentPosition);
break;
case SentenceGranularity:
- pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
+ pos = previousSentencePosition(currentPosition);
break;
case LineGranularity:
- pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
+ pos = previousLinePosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
break;
case ParagraphGranularity:
- pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
+ pos = previousParagraphPosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
break;
case SentenceBoundary:
- pos = startOfSentence(startForPlatform());
+ pos = startOfSentence(currentPosition);
break;
case LineBoundary:
- pos = logicalStartOfLine(startForPlatform());
+ pos = logicalStartOfLine(startForPlatform(), reachedBoundary);
break;
case ParagraphBoundary:
- pos = startOfParagraph(startForPlatform());
+ pos = startOfParagraph(currentPosition);
break;
case DocumentBoundary:
- pos = startForPlatform();
+ pos = currentPosition;
if (isEditablePosition(pos.deepEquivalent()))
pos = startOfEditableContent(pos);
else
pos = startOfDocument(pos);
break;
-#if PLATFORM(IOS)
case DocumentGranularity:
ASSERT_NOT_REACHED();
break;
-#endif
+ }
+ switch (granularity) {
+ case WordGranularity:
+ case SentenceGranularity:
+ case LineGranularity:
+ case ParagraphGranularity:
+ case SentenceBoundary:
+ case ParagraphBoundary:
+ case DocumentBoundary:
+ if (reachedBoundary)
+ *reachedBoundary = pos == currentPosition;
+ break;
+ default:
+ break;
}
return pos;
}
@@ -985,7 +1109,137 @@ VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity
static bool isBoundary(TextGranularity granularity)
{
return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
-}
+}
+
+AXTextStateChangeIntent FrameSelection::textSelectionIntent(EAlteration alter, SelectionDirection direction, TextGranularity granularity)
+{
+ AXTextStateChangeIntent intent = AXTextStateChangeIntent();
+ bool flip = false;
+ if (alter == FrameSelection::AlterationMove) {
+ intent.type = AXTextStateChangeTypeSelectionMove;
+ flip = isRange() && directionOfSelection() == RTL;
+ } else
+ intent.type = AXTextStateChangeTypeSelectionExtend;
+ switch (granularity) {
+ case CharacterGranularity:
+ intent.selection.granularity = AXTextSelectionGranularityCharacter;
+ break;
+ case WordGranularity:
+ intent.selection.granularity = AXTextSelectionGranularityWord;
+ break;
+ case SentenceGranularity:
+ case SentenceBoundary:
+ intent.selection.granularity = AXTextSelectionGranularitySentence;
+ break;
+ case LineGranularity:
+ case LineBoundary:
+ intent.selection.granularity = AXTextSelectionGranularityLine;
+ break;
+ case ParagraphGranularity:
+ case ParagraphBoundary:
+ intent.selection.granularity = AXTextSelectionGranularityParagraph;
+ break;
+ case DocumentGranularity:
+ case DocumentBoundary:
+ intent.selection.granularity = AXTextSelectionGranularityDocument;
+ break;
+ }
+ bool boundary = false;
+ switch (granularity) {
+ case CharacterGranularity:
+ case WordGranularity:
+ case SentenceGranularity:
+ case LineGranularity:
+ case ParagraphGranularity:
+ case DocumentGranularity:
+ break;
+ case SentenceBoundary:
+ case LineBoundary:
+ case ParagraphBoundary:
+ case DocumentBoundary:
+ boundary = true;
+ break;
+ }
+ switch (direction) {
+ case DirectionRight:
+ case DirectionForward:
+ if (boundary)
+ intent.selection.direction = flip ? AXTextSelectionDirectionBeginning : AXTextSelectionDirectionEnd;
+ else
+ intent.selection.direction = flip ? AXTextSelectionDirectionPrevious : AXTextSelectionDirectionNext;
+ break;
+ case DirectionLeft:
+ case DirectionBackward:
+ if (boundary)
+ intent.selection.direction = flip ? AXTextSelectionDirectionEnd : AXTextSelectionDirectionBeginning;
+ else
+ intent.selection.direction = flip ? AXTextSelectionDirectionNext : AXTextSelectionDirectionPrevious;
+ break;
+ }
+ return intent;
+}
+
+static AXTextSelection textSelectionWithDirectionAndGranularity(SelectionDirection direction, TextGranularity granularity)
+{
+ // FIXME: Account for BIDI in DirectionRight & DirectionLeft. (In a RTL block, Right would map to Previous/Beginning and Left to Next/End.)
+ AXTextSelectionDirection intentDirection = AXTextSelectionDirectionUnknown;
+ switch (direction) {
+ case DirectionForward:
+ intentDirection = AXTextSelectionDirectionNext;
+ break;
+ case DirectionRight:
+ intentDirection = AXTextSelectionDirectionNext;
+ break;
+ case DirectionBackward:
+ intentDirection = AXTextSelectionDirectionPrevious;
+ break;
+ case DirectionLeft:
+ intentDirection = AXTextSelectionDirectionPrevious;
+ break;
+ }
+ AXTextSelectionGranularity intentGranularity = AXTextSelectionGranularityUnknown;
+ switch (granularity) {
+ case CharacterGranularity:
+ intentGranularity = AXTextSelectionGranularityCharacter;
+ break;
+ case WordGranularity:
+ intentGranularity = AXTextSelectionGranularityWord;
+ break;
+ case SentenceGranularity:
+ case SentenceBoundary: // FIXME: Boundary should affect direction.
+ intentGranularity = AXTextSelectionGranularitySentence;
+ break;
+ case LineGranularity:
+ intentGranularity = AXTextSelectionGranularityLine;
+ break;
+ case ParagraphGranularity:
+ case ParagraphBoundary: // FIXME: Boundary should affect direction.
+ intentGranularity = AXTextSelectionGranularityParagraph;
+ break;
+ case DocumentGranularity:
+ case DocumentBoundary: // FIXME: Boundary should affect direction.
+ intentGranularity = AXTextSelectionGranularityDocument;
+ break;
+ case LineBoundary:
+ intentGranularity = AXTextSelectionGranularityLine;
+ switch (direction) {
+ case DirectionForward:
+ intentDirection = AXTextSelectionDirectionEnd;
+ break;
+ case DirectionRight:
+ intentDirection = AXTextSelectionDirectionEnd;
+ break;
+ case DirectionBackward:
+ intentDirection = AXTextSelectionDirectionBeginning;
+ break;
+ case DirectionLeft:
+ intentDirection = AXTextSelectionDirectionBeginning;
+ break;
+ }
+ break;
+ }
+ return { intentDirection, intentGranularity, false };
+}
bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
{
@@ -1004,13 +1258,14 @@ bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, Tex
willBeModified(alter, direction);
+ bool reachedBoundary = false;
bool wasRange = m_selection.isRange();
Position originalStartPosition = m_selection.start();
VisiblePosition position;
switch (direction) {
case DirectionRight:
if (alter == AlterationMove)
- position = modifyMovingRight(granularity);
+ position = modifyMovingRight(granularity, &reachedBoundary);
else
position = modifyExtendingRight(granularity);
break;
@@ -1018,11 +1273,11 @@ bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, Tex
if (alter == AlterationExtend)
position = modifyExtendingForward(granularity);
else
- position = modifyMovingForward(granularity);
+ position = modifyMovingForward(granularity, &reachedBoundary);
break;
case DirectionLeft:
if (alter == AlterationMove)
- position = modifyMovingLeft(granularity);
+ position = modifyMovingLeft(granularity, &reachedBoundary);
else
position = modifyExtendingLeft(granularity);
break;
@@ -1030,10 +1285,15 @@ bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, Tex
if (alter == AlterationExtend)
position = modifyExtendingBackward(granularity);
else
- position = modifyMovingBackward(granularity);
+ position = modifyMovingBackward(granularity, &reachedBoundary);
break;
}
+ if (reachedBoundary && !isRange() && userTriggered == UserTriggered && m_frame && AXObjectCache::accessibilityEnabled()) {
+ notifyAccessibilityForSelectionChange({ AXTextStateChangeTypeSelectionBoundary, textSelectionWithDirectionAndGranularity(direction, granularity) });
+ return true;
+ }
+
if (position.isNull())
return false;
@@ -1041,6 +1301,11 @@ bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, Tex
if (!wasRange && alter == AlterationMove && position == originalStartPosition)
return false;
+ if (m_frame && AXObjectCache::accessibilityEnabled()) {
+ if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
+ cache->setTextSelectionIntent(textSelectionIntent(alter, direction, granularity));
+ }
+
// Some of the above operations set an xPosForVerticalArrowNavigation.
// Setting a selection will clear it, so save it to possibly restore later.
// Note: the START position type is arbitrary because it is unused, it would be
@@ -1241,8 +1506,8 @@ void FrameSelection::prepareForDestruction()
if (view)
view->clearSelection();
- setSelection(VisibleSelection(), CloseTyping | ClearTypingStyle | DoNotUpdateAppearance);
- m_previousCaretNode.clear();
+ setSelectionWithoutUpdatingAppearance(VisibleSelection(), defaultSetSelectionOptions(), AlignCursorOnScrollIfNeeded, CharacterGranularity);
+ m_previousCaretNode = nullptr;
}
void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
@@ -1264,25 +1529,25 @@ void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
{
const bool selectionHasDirection = true;
- setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
+ setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered));
}
void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
{
const bool selectionHasDirection = true;
- setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
+ setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered));
}
void FrameSelection::setBase(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
{
const bool selectionHasDirection = true;
- setSelection(VisibleSelection(pos, m_selection.extent(), affinity, selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
+ setSelection(VisibleSelection(pos, m_selection.extent(), affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered));
}
void FrameSelection::setExtent(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
{
const bool selectionHasDirection = true;
- setSelection(VisibleSelection(m_selection.base(), pos, affinity, selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
+ setSelection(VisibleSelection(m_selection.base(), pos, affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered));
}
void CaretBase::clearCaretRect()
@@ -1290,71 +1555,23 @@ void CaretBase::clearCaretRect()
m_caretLocalRect = LayoutRect();
}
-static inline bool caretRendersInsideNode(Node* node)
-{
- return node && !isTableElement(node) && !editingIgnoresContent(node);
-}
-
-static RenderObject* caretRenderer(Node* node)
-{
- if (!node)
- return 0;
-
- RenderObject* renderer = node->renderer();
- if (!renderer)
- return 0;
-
- // if caretNode is a block and caret is inside it then caret should be painted by that block
- bool paintedByBlock = renderer->isRenderBlockFlow() && caretRendersInsideNode(node);
- return paintedByBlock ? renderer : renderer->containingBlock();
-}
-
bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition)
{
- document->updateStyleIfNeeded();
- m_caretLocalRect = LayoutRect();
-
+ document->updateLayoutIgnorePendingStylesheets();
m_caretRectNeedsUpdate = false;
-
- if (caretPosition.isNull())
- return false;
-
- ASSERT(caretPosition.deepEquivalent().deprecatedNode()->renderer());
-
- // First compute a rect local to the renderer at the selection start.
- RenderObject* renderer;
- LayoutRect localRect = caretPosition.localCaretRect(renderer);
-
- // Get the renderer that will be responsible for painting the caret
- // (which is either the renderer we just found, or one of its containers).
- RenderObject* caretPainter = caretRenderer(caretPosition.deepEquivalent().deprecatedNode());
-
- // Compute an offset between the renderer and the caretPainter.
- bool unrooted = false;
- while (renderer != caretPainter) {
- RenderObject* containerObject = renderer->container();
- if (!containerObject) {
- unrooted = true;
- break;
- }
- localRect.move(renderer->offsetFromContainer(containerObject, localRect.location()));
- renderer = containerObject;
- }
-
- if (!unrooted)
- m_caretLocalRect = localRect;
-
- return true;
+ RenderBlock* renderer;
+ m_caretLocalRect = localCaretRectInRendererForCaretPainting(caretPosition, renderer);
+ return !m_caretLocalRect.isEmpty();
}
-RenderObject* FrameSelection::caretRenderer() const
+RenderBlock* FrameSelection::caretRendererWithoutUpdatingLayout() const
{
- return WebCore::caretRenderer(m_selection.start().deprecatedNode());
+ return rendererForCaretPainting(m_selection.start().deprecatedNode());
}
-RenderObject* DragCaretController::caretRenderer() const
+RenderBlock* DragCaretController::caretRenderer() const
{
- return WebCore::caretRenderer(m_position.deepEquivalent().deprecatedNode());
+ return rendererForCaretPainting(m_position.deepEquivalent().deprecatedNode());
}
static bool isNonOrphanedCaret(const VisibleSelection& selection)
@@ -1362,43 +1579,21 @@ static bool isNonOrphanedCaret(const VisibleSelection& selection)
return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan();
}
-LayoutRect FrameSelection::localCaretRect()
+IntRect FrameSelection::absoluteCaretBounds(bool* insideFixed)
{
- if (shouldUpdateCaretRect()) {
- if (!isNonOrphanedCaret(m_selection))
- clearCaretRect();
- else if (updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity())))
- m_absCaretBoundsDirty = true;
- }
-
- return localCaretRectWithoutUpdate();
-}
-
-IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const
-{
- RenderObject* caretPainter = caretRenderer(node);
- if (!caretPainter)
+ if (!m_frame)
return IntRect();
-
- LayoutRect localRect(rect);
- if (caretPainter->isBox())
- toRenderBox(caretPainter)->flipForWritingMode(localRect);
- return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
-}
-
-IntRect FrameSelection::absoluteCaretBounds()
-{
+ updateSelectionByUpdatingLayoutOrStyle(*m_frame);
recomputeCaretRect();
+ if (insideFixed)
+ *insideFixed = m_caretInsidePositionFixed;
return m_absCaretBounds;
}
static void repaintCaretForLocalRect(Node* node, const LayoutRect& rect)
{
- RenderObject* caretPainter = caretRenderer(node);
- if (!caretPainter)
- return;
-
- caretPainter->repaintRectangle(rect);
+ if (auto* caretPainter = rendererForCaretPainting(node))
+ caretPainter->repaintRectangle(rect);
}
bool FrameSelection::recomputeCaretRect()
@@ -1413,16 +1608,33 @@ bool FrameSelection::recomputeCaretRect()
if (!v)
return false;
- Node* caretNode = m_selection.start().deprecatedNode();
-
LayoutRect oldRect = localCaretRectWithoutUpdate();
- LayoutRect newRect = localCaretRect();
+
+ RefPtr<Node> caretNode = m_previousCaretNode;
+ if (shouldUpdateCaretRect()) {
+ if (!isNonOrphanedCaret(m_selection))
+ clearCaretRect();
+ else {
+ VisiblePosition visibleStart = m_selection.visibleStart();
+ if (updateCaretRect(m_frame->document(), visibleStart)) {
+ caretNode = visibleStart.deepEquivalent().deprecatedNode();
+ m_absCaretBoundsDirty = true;
+ }
+ }
+ }
+ LayoutRect newRect = localCaretRectWithoutUpdate();
if (caretNode == m_previousCaretNode && oldRect == newRect && !m_absCaretBoundsDirty)
return false;
IntRect oldAbsCaretBounds = m_absCaretBounds;
- m_absCaretBounds = absoluteBoundsForLocalRect(caretNode, localCaretRectWithoutUpdate());
+ bool isInsideFixed;
+ m_absCaretBounds = absoluteBoundsForLocalCaretRect(rendererForCaretPainting(caretNode.get()), newRect, &isInsideFixed);
+ m_caretInsidePositionFixed = isInsideFixed;
+
+ if (m_absCaretBoundsDirty && m_selection.isCaret()) // We should be able to always assert this condition.
+ ASSERT(m_absCaretBounds == m_selection.visibleStart().absoluteCaretBounds());
+
m_absCaretBoundsDirty = false;
if (caretNode == m_previousCaretNode && oldAbsCaretBounds == m_absCaretBounds)
@@ -1430,12 +1642,12 @@ bool FrameSelection::recomputeCaretRect()
#if ENABLE(TEXT_CARET)
if (RenderView* view = m_frame->document()->renderView()) {
- bool previousOrNewCaretNodeIsContentEditable = isContentEditable() || (m_previousCaretNode && m_previousCaretNode->isContentEditable());
+ bool previousOrNewCaretNodeIsContentEditable = m_selection.isContentEditable() || (m_previousCaretNode && m_previousCaretNode->isContentEditable());
if (shouldRepaintCaret(view, previousOrNewCaretNodeIsContentEditable)) {
if (m_previousCaretNode)
repaintCaretForLocalRect(m_previousCaretNode.get(), oldRect);
m_previousCaretNode = caretNode;
- repaintCaretForLocalRect(caretNode, newRect);
+ repaintCaretForLocalRect(caretNode.get(), newRect);
}
}
#endif
@@ -1477,42 +1689,59 @@ void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged)
return;
if (RenderView* view = node->document().renderView()) {
- if (shouldRepaintCaret(view, node->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable)))
+ if (shouldRepaintCaret(view, isEditableNode(*node)))
repaintCaretForLocalRect(node, localCaretRectWithoutUpdate());
}
}
-void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
+void FrameSelection::paintCaret(GraphicsContext& context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
{
if (m_selection.isCaret() && m_caretPaint)
CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect);
}
-void CaretBase::paintCaret(Node* node, GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
+#if ENABLE(TEXT_CARET)
+static inline bool disappearsIntoBackground(const Color& foreground, const Color& background)
+{
+ return background.blend(foreground) == background;
+}
+#endif
+
+void CaretBase::paintCaret(Node* node, GraphicsContext& context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
{
#if ENABLE(TEXT_CARET)
if (m_caretVisibility == Hidden)
return;
LayoutRect drawingRect = localCaretRectWithoutUpdate();
- RenderObject* renderer = caretRenderer(node);
- if (renderer && renderer->isBox())
- toRenderBox(renderer)->flipForWritingMode(drawingRect);
+ if (auto* renderer = rendererForCaretPainting(node))
+ renderer->flipForWritingMode(drawingRect);
drawingRect.moveBy(roundedIntPoint(paintOffset));
LayoutRect caret = intersection(drawingRect, clipRect);
if (caret.isEmpty())
return;
Color caretColor = Color::black;
- ColorSpace colorSpace = ColorSpaceDeviceRGB;
- Element* element = node->isElementNode() ? toElement(node) : node->parentElement();
+ Element* element = is<Element>(*node) ? downcast<Element>(node) : node->parentElement();
+ Element* rootEditableElement = node->rootEditableElement();
if (element && element->renderer()) {
- caretColor = element->renderer()->style().visitedDependentColor(CSSPropertyColor);
- colorSpace = element->renderer()->style().colorSpace();
+ bool setToRootEditableElement = false;
+ if (rootEditableElement && rootEditableElement->renderer()) {
+ const auto& rootEditableStyle = rootEditableElement->renderer()->style();
+ const auto& elementStyle = element->renderer()->style();
+ auto rootEditableBGColor = rootEditableStyle.visitedDependentColor(CSSPropertyBackgroundColor);
+ auto elementBGColor = elementStyle.visitedDependentColor(CSSPropertyBackgroundColor);
+ if (disappearsIntoBackground(elementBGColor, rootEditableBGColor)) {
+ caretColor = rootEditableStyle.visitedDependentColor(CSSPropertyColor);
+ setToRootEditableElement = true;
+ }
+ }
+ if (!setToRootEditableElement)
+ caretColor = element->renderer()->style().visitedDependentColor(CSSPropertyColor);
}
- context->fillRect(caret, caretColor, colorSpace);
+ context.fillRect(caret, caretColor);
#else
UNUSED_PARAM(node);
UNUSED_PARAM(context);
@@ -1521,30 +1750,30 @@ void CaretBase::paintCaret(Node* node, GraphicsContext* context, const LayoutPoi
#endif
}
-void FrameSelection::debugRenderer(RenderObject* r, bool selected) const
+void FrameSelection::debugRenderer(RenderObject* renderer, bool selected) const
{
- if (r->node()->isElementNode()) {
- Element* element = toElement(r->node());
- fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data());
- } else if (r->isText()) {
- RenderText* textRenderer = toRenderText(r);
- if (!textRenderer->textLength() || !textRenderer->firstTextBox()) {
+ if (is<Element>(*renderer->node())) {
+ Element& element = downcast<Element>(*renderer->node());
+ fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element.localName().string().utf8().data());
+ } else if (is<RenderText>(*renderer)) {
+ RenderText& textRenderer = downcast<RenderText>(*renderer);
+ if (!textRenderer.textLength() || !textRenderer.firstTextBox()) {
fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
return;
}
static const int max = 36;
- String text = textRenderer->text();
+ String text = textRenderer.text();
int textLength = text.length();
if (selected) {
int offset = 0;
- if (r->node() == m_selection.start().containerNode())
+ if (renderer->node() == m_selection.start().containerNode())
offset = m_selection.start().computeOffsetInContainerNode();
- else if (r->node() == m_selection.end().containerNode())
+ else if (renderer->node() == m_selection.end().containerNode())
offset = m_selection.end().computeOffsetInContainerNode();
int pos;
- InlineTextBox* box = textRenderer->findNextInlineTextBox(offset, pos);
+ InlineTextBox* box = textRenderer.findNextInlineTextBox(offset, pos);
text = text.substring(box->start(), box->len());
String show;
@@ -1588,22 +1817,21 @@ void FrameSelection::debugRenderer(RenderObject* r, bool selected) const
bool FrameSelection::contains(const LayoutPoint& point)
{
- Document* document = m_frame->document();
-
// Treat a collapsed selection like no selection.
if (!isRange())
return false;
- if (!document->renderView())
+
+ RenderView* renderView = m_frame->contentRenderer();
+ if (!renderView)
return false;
- HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent);
HitTestResult result(point);
- document->renderView()->hitTest(request, result);
+ renderView->hitTest(HitTestRequest(), result);
Node* innerNode = result.innerNode();
if (!innerNode || !innerNode->renderer())
return false;
- VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
+ VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint(), nullptr));
if (visiblePos.isNull())
return false;
@@ -1653,7 +1881,7 @@ void FrameSelection::selectFrameElementInParentIfFullySelected()
return;
// Create compute positions before and after the element.
- unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
+ unsigned ownerElementNodeIndex = ownerElement->computeNodeIndex();
VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
@@ -1669,29 +1897,38 @@ void FrameSelection::selectAll()
{
Document* document = m_frame->document();
- if (document->focusedElement() && document->focusedElement()->hasTagName(selectTag)) {
- HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedElement());
- if (selectElement->canSelectAll()) {
- selectElement->selectAll();
+ Element* focusedElement = document->focusedElement();
+ if (is<HTMLSelectElement>(focusedElement)) {
+ HTMLSelectElement& selectElement = downcast<HTMLSelectElement>(*focusedElement);
+ if (selectElement.canSelectAll()) {
+ selectElement.selectAll();
return;
}
}
- RefPtr<Node> root = 0;
- Node* selectStartTarget = 0;
- if (isContentEditable()) {
+ RefPtr<Node> root;
+ Node* selectStartTarget = nullptr;
+ if (m_selection.isContentEditable()) {
root = highestEditableRoot(m_selection.start());
if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
selectStartTarget = shadowRoot->shadowHost();
else
selectStartTarget = root.get();
} else {
- root = m_selection.nonBoundaryShadowTreeRootNode();
+ if (m_selection.isNone() && focusedElement) {
+ if (is<HTMLTextFormControlElement>(*focusedElement)) {
+ downcast<HTMLTextFormControlElement>(*focusedElement).select();
+ return;
+ }
+ root = focusedElement->nonBoundaryShadowTreeRootNode();
+ } else
+ root = m_selection.nonBoundaryShadowTreeRootNode();
+
if (root)
selectStartTarget = root->shadowHost();
else {
root = document->documentElement();
- selectStartTarget = document->body();
+ selectStartTarget = document->bodyOrFrameset();
}
}
if (!root)
@@ -1702,45 +1939,36 @@ void FrameSelection::selectAll()
VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
- if (shouldChangeSelection(newSelection))
- setSelection(newSelection);
-
- selectFrameElementInParentIfFullySelected();
- updateSelectionCachesIfSelectionIsInsideTextFormControl(UserTriggered);
+ if (shouldChangeSelection(newSelection)) {
+ AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionExtend, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityAll, false });
+ setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent, intent);
+ }
}
-bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping)
+bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping, EUserTriggered userTriggered)
{
- if (!range || !range->startContainer() || !range->endContainer())
+ if (!range)
return false;
- ASSERT(&range->startContainer()->document() == &range->endContainer()->document());
+ ASSERT(&range->startContainer().document() == &range->endContainer().document());
- m_frame->document()->updateLayoutIgnorePendingStylesheets();
-
- // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
- // they start at the beginning of the next line instead
- ExceptionCode ec = 0;
- bool collapsed = range->collapsed(ec);
- if (ec)
- return false;
+ VisibleSelection newSelection(*range, affinity);
- // FIXME: Can we provide extentAffinity?
- VisiblePosition visibleStart(range->startPosition(), collapsed ? affinity : DOWNSTREAM);
- VisiblePosition visibleEnd(range->endPosition(), SEL_DEFAULT_AFFINITY);
#if PLATFORM(IOS)
- if (range->startContainer() && visibleStart.isNull())
- return false;
- if (range->endContainer() && visibleEnd.isNull())
+ // FIXME: Why do we need this check only in iOS?
+ if (newSelection.isNone())
return false;
#endif
- setSelection(VisibleSelection(visibleStart, visibleEnd), ClearTypingStyle | (closeTyping ? CloseTyping : 0));
- return true;
-}
-bool FrameSelection::isInPasswordField() const
-{
- HTMLTextFormControlElement* textControl = enclosingTextFormControl(start());
- return textControl && isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPasswordField();
+ if (userTriggered == UserTriggered) {
+ FrameSelection trialFrameSelection;
+ trialFrameSelection.setSelection(newSelection, ClearTypingStyle | (closeTyping ? CloseTyping : 0));
+
+ if (!shouldChangeSelection(trialFrameSelection.selection()))
+ return false;
+ }
+
+ setSelection(newSelection, ClearTypingStyle | (closeTyping ? CloseTyping : 0));
+ return true;
}
void FrameSelection::focusedOrActiveStateChanged()
@@ -1767,17 +1995,14 @@ void FrameSelection::focusedOrActiveStateChanged()
setSelectionFromNone();
setCaretVisibility(activeAndFocused ? Visible : Hidden);
- // Update for caps lock state
- m_frame->eventHandler().capsLockStateMayHaveChanged();
-
// 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 (Element* element = document->focusedElement()) {
- element->setNeedsStyleRecalc();
+ element->invalidateStyleForSubtree();
if (RenderObject* renderer = element->renderer())
if (renderer && renderer->style().hasAppearance())
- renderer->theme().stateChanged(renderer, FocusState);
+ renderer->theme().stateChanged(*renderer, ControlStates::FocusState);
}
#endif
}
@@ -1817,17 +2042,14 @@ void FrameSelection::updateAppearance()
// Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case
// the FrameSelection will paint a blinking caret as usual).
- VisiblePosition forwardPosition;
- if (m_shouldShowBlockCursor && m_selection.isCaret()) {
- forwardPosition = modifyExtendingForward(CharacterGranularity);
- m_caretPaint = forwardPosition.isNull();
- }
+ VisibleSelection oldSelection = selection();
#if ENABLE(TEXT_CARET)
+ bool paintBlockCursor = m_shouldShowBlockCursor && m_selection.isCaret() && !isLogicalEndOfLine(m_selection.visibleEnd());
bool caretRectChangedOrCleared = recomputeCaretRect();
bool caretBrowsing = m_frame->settings().caretBrowsingEnabled();
- bool shouldBlink = caretIsVisible() && isCaret() && (isContentEditable() || caretBrowsing) && forwardPosition.isNull();
+ bool shouldBlink = !paintBlockCursor && caretIsVisible() && isCaret() && (oldSelection.isContentEditable() || caretBrowsing);
// If the caret moved, stop the blink timer so we can restart with a
// black caret in the new location.
@@ -1853,7 +2075,12 @@ void FrameSelection::updateAppearance()
// Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
// assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
- VisibleSelection selection(m_selection.visibleStart(), forwardPosition.isNotNull() ? forwardPosition : m_selection.visibleEnd());
+#if ENABLE(TEXT_CARET)
+ VisiblePosition endVisiblePosition = paintBlockCursor ? modifyExtendingForward(CharacterGranularity) : oldSelection.visibleEnd();
+ VisibleSelection selection(oldSelection.visibleStart(), endVisiblePosition);
+#else
+ VisibleSelection selection(oldSelection.visibleStart(), oldSelection.visibleEnd());
+#endif
if (!selection.isRange()) {
view->clearSelection();
@@ -1877,8 +2104,11 @@ void FrameSelection::updateAppearance()
// because we don't yet notify the FrameSelection of text removal.
if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
+ int startOffset = startPos.deprecatedEditingOffset();
RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
- view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
+ int endOffset = endPos.deprecatedEditingOffset();
+ ASSERT(startOffset >= 0 && endOffset >= 0);
+ view->setSelection(startRenderer, startOffset, endRenderer, endOffset);
}
}
@@ -1887,21 +2117,22 @@ void FrameSelection::setCaretVisibility(CaretVisibility visibility)
if (caretVisibility() == visibility)
return;
+ // FIXME: We shouldn't trigger a synchronous layout here.
+ if (m_frame)
+ updateSelectionByUpdatingLayoutOrStyle(*m_frame);
+
#if ENABLE(TEXT_CARET)
- m_frame->document()->updateLayoutIgnorePendingStylesheets();
if (m_caretPaint) {
m_caretPaint = false;
invalidateCaretRect();
}
CaretBase::setCaretVisibility(visibility);
-#else
- m_frame->document()->updateStyleIfNeeded();
#endif
updateAppearance();
}
-void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>&)
+void FrameSelection::caretBlinkTimerFired()
{
#if ENABLE(TEXT_CARET)
ASSERT(caretIsVisible());
@@ -1914,12 +2145,6 @@ void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>&)
#endif
}
-void FrameSelection::updateSelectionCachesIfSelectionIsInsideTextFormControl(EUserTriggered userTriggered)
-{
- if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(start()))
- textControl->selectionChanged(userTriggered == UserTriggered);
-}
-
// Helper function that tells whether a particular node is an element that has an entire
// Frame and FrameView, a <frame>, <iframe>, or <object>.
static bool isFrameElement(const Node* n)
@@ -1927,9 +2152,9 @@ static bool isFrameElement(const Node* n)
if (!n)
return false;
RenderObject* renderer = n->renderer();
- if (!renderer || !renderer->isWidget())
+ if (!is<RenderWidget>(renderer))
return false;
- Widget* widget = toRenderWidget(renderer)->widget();
+ Widget* widget = downcast<RenderWidget>(*renderer).widget();
return widget && widget->isFrameView();
}
@@ -1940,32 +2165,32 @@ void FrameSelection::setFocusedElementIfNeeded()
bool caretBrowsing = m_frame->settings().caretBrowsingEnabled();
if (caretBrowsing) {
- if (Element* anchor = enclosingAnchorElement(base())) {
- m_frame->page()->focusController().setFocusedElement(anchor, m_frame);
+ if (Element* anchor = enclosingAnchorElement(m_selection.base())) {
+ m_frame->page()->focusController().setFocusedElement(anchor, *m_frame);
return;
}
}
- if (Element* target = rootEditableElement()) {
+ if (Element* target = m_selection.rootEditableElement()) {
// Walk up the DOM tree to search for an element to focus.
while (target) {
// We don't want to set focus on a subframe when selecting in a parent frame,
// so add the !isFrameElement check here. There's probably a better way to make this
// work in the long term, but this is the safest fix at this time.
if (target->isMouseFocusable() && !isFrameElement(target)) {
- m_frame->page()->focusController().setFocusedElement(target, m_frame);
+ m_frame->page()->focusController().setFocusedElement(target, *m_frame);
return;
}
target = target->parentOrShadowHostElement();
}
- m_frame->document()->setFocusedElement(0);
+ m_frame->document()->setFocusedElement(nullptr);
}
if (caretBrowsing)
- m_frame->page()->focusController().setFocusedElement(0, m_frame);
+ m_frame->page()->focusController().setFocusedElement(nullptr, *m_frame);
}
-void DragCaretController::paintDragCaret(Frame* frame, GraphicsContext* p, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
+void DragCaretController::paintDragCaret(Frame* frame, GraphicsContext& p, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
{
#if ENABLE(TEXT_CARET)
if (m_position.deepEquivalent().deprecatedNode()->document().frame() == frame)
@@ -1994,12 +2219,12 @@ bool FrameSelection::shouldDeleteSelection(const VisibleSelection& selection) co
return m_frame->editor().client()->shouldDeleteRange(selection.toNormalizedRange().get());
}
-FloatRect FrameSelection::bounds(bool clipToVisibleContent) const
+FloatRect FrameSelection::selectionBounds(bool clipToVisibleContent) const
{
if (!m_frame->document())
return LayoutRect();
- m_frame->document()->updateStyleIfNeeded();
+ updateSelectionByUpdatingLayoutOrStyle(*m_frame);
RenderView* root = m_frame->contentRenderer();
FrameView* view = m_frame->view();
if (!root || !view)
@@ -2009,25 +2234,37 @@ FloatRect FrameSelection::bounds(bool clipToVisibleContent) const
return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect)) : selectionRect;
}
-void FrameSelection::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles) const
+void FrameSelection::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles, TextRectangleHeight textRectHeight) const
{
RenderView* root = m_frame->contentRenderer();
if (!root)
return;
- FloatRect visibleContentRect = m_frame->view()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
+ Vector<FloatRect> textRects;
+ getTextRectangles(textRects, textRectHeight);
- Vector<FloatQuad> quads;
- toNormalizedRange()->textQuads(quads, true);
+ FloatRect visibleContentRect = m_frame->view()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
- size_t size = quads.size();
- for (size_t i = 0; i < size; ++i) {
- FloatRect intersectionRect = intersection(quads[i].enclosingBoundingBox(), visibleContentRect);
+ for (const auto& rect : textRects) {
+ FloatRect intersectionRect = intersection(rect, visibleContentRect);
if (!intersectionRect.isEmpty())
rectangles.append(intersectionRect);
}
}
+void FrameSelection::getTextRectangles(Vector<FloatRect>& rectangles, TextRectangleHeight textRectHeight) const
+{
+ RefPtr<Range> range = toNormalizedRange();
+ if (!range)
+ return;
+
+ Vector<FloatQuad> quads;
+ range->absoluteTextQuads(quads, textRectHeight == TextRectangleHeight::SelectionHeight);
+
+ for (const auto& quad : quads)
+ rectangles.append(quad.boundingBox());
+}
+
// Scans logically forward from "start", including any child frames.
static HTMLFormElement* scanForForm(Element* start)
{
@@ -2037,12 +2274,12 @@ static HTMLFormElement* scanForForm(Element* start)
auto descendants = descendantsOfType<HTMLElement>(start->document());
for (auto it = descendants.from(*start), end = descendants.end(); it != end; ++it) {
HTMLElement& element = *it;
- if (isHTMLFormElement(&element))
- return toHTMLFormElement(&element);
- if (isHTMLFormControlElement(element))
- return toHTMLFormControlElement(element).form();
- if (isHTMLFrameElementBase(element)) {
- Document* contentDocument = toHTMLFrameElementBase(element).contentDocument();
+ if (is<HTMLFormElement>(element))
+ return &downcast<HTMLFormElement>(element);
+ if (is<HTMLFormControlElement>(element))
+ return downcast<HTMLFormControlElement>(element).form();
+ if (is<HTMLFrameElementBase>(element)) {
+ Document* contentDocument = downcast<HTMLFrameElementBase>(element).contentDocument();
if (!contentDocument)
continue;
if (HTMLFormElement* frameResult = scanForForm(contentDocument->documentElement()))
@@ -2058,7 +2295,7 @@ HTMLFormElement* FrameSelection::currentForm() const
// Start looking either at the active (first responder) node, or where the selection is.
Element* start = m_frame->document()->focusedElement();
if (!start)
- start = this->start().element();
+ start = m_selection.start().element();
if (!start)
return nullptr;
@@ -2071,40 +2308,43 @@ HTMLFormElement* FrameSelection::currentForm() const
return scanForForm(start);
}
-void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
+void FrameSelection::revealSelection(SelectionRevealMode revealMode, const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
{
- LayoutRect rect;
+ if (revealMode == SelectionRevealMode::DoNotReveal)
+ return;
- switch (selectionType()) {
+ LayoutRect rect;
+ bool insideFixed = false;
+ switch (m_selection.selectionType()) {
case VisibleSelection::NoSelection:
return;
case VisibleSelection::CaretSelection:
- rect = absoluteCaretBounds();
+ rect = absoluteCaretBounds(&insideFixed);
break;
case VisibleSelection::RangeSelection:
- rect = revealExtentOption == RevealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds(false));
+ rect = revealExtentOption == RevealExtent ? VisiblePosition(m_selection.extent()).absoluteCaretBounds() : enclosingIntRect(selectionBounds(false));
break;
}
- Position start = this->start();
+ Position start = m_selection.start();
ASSERT(start.deprecatedNode());
if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
#if PLATFORM(IOS)
if (RenderLayer* layer = start.deprecatedNode()->renderer()->enclosingLayer()) {
if (!m_scrollingSuppressCount) {
layer->setAdjustForIOSCaretWhenScrolling(true);
- layer->scrollRectToVisible(rect, alignment, alignment);
+ layer->scrollRectToVisible(revealMode, rect, insideFixed, alignment, alignment);
layer->setAdjustForIOSCaretWhenScrolling(false);
updateAppearance();
if (m_frame->page())
- m_frame->page()->chrome().client().notifyRevealedSelectionByScrollingFrame(m_frame);
+ m_frame->page()->chrome().client().notifyRevealedSelectionByScrollingFrame(*m_frame);
}
}
#else
// 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 (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment))
+ if (start.deprecatedNode()->renderer()->scrollRectToVisible(revealMode, rect, insideFixed, alignment, alignment))
updateAppearance();
#endif
}
@@ -2121,15 +2361,12 @@ void FrameSelection::setSelectionFromNone()
if (!isNone() || !(document->hasEditableStyle() || caretBrowsing))
return;
#else
- if (!document || !(isNone() || isStartOfDocument(VisiblePosition(selection().start(), selection().affinity()))) || !document->hasEditableStyle())
+ if (!document || !(isNone() || isStartOfDocument(VisiblePosition(m_selection.start(), m_selection.affinity()))) || !document->hasEditableStyle())
return;
#endif
- Node* node = document->documentElement();
- while (node && !node->hasTagName(bodyTag))
- node = NodeTraversal::next(node);
- if (node)
- setSelection(VisibleSelection(firstPositionInOrBeforeNode(node), DOWNSTREAM));
+ if (auto* body = document->body())
+ setSelection(VisibleSelection(firstPositionInOrBeforeNode(body), DOWNSTREAM));
}
bool FrameSelection::shouldChangeSelection(const VisibleSelection& newSelection) const
@@ -2159,7 +2396,33 @@ void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor)
updateAppearance();
}
-#ifndef NDEBUG
+void FrameSelection::updateAppearanceAfterLayout()
+{
+ m_appearanceUpdateTimer.stop();
+ updateAppearanceAfterLayoutOrStyleChange();
+}
+
+void FrameSelection::scheduleAppearanceUpdateAfterStyleChange()
+{
+ m_appearanceUpdateTimer.startOneShot(0_s);
+}
+
+void FrameSelection::appearanceUpdateTimerFired()
+{
+ updateAppearanceAfterLayoutOrStyleChange();
+}
+
+void FrameSelection::updateAppearanceAfterLayoutOrStyleChange()
+{
+ if (auto* client = m_frame->editor().client())
+ client->updateEditorStateAfterLayoutIfEditabilityChanged();
+
+ setCaretRectNeedsUpdate();
+ updateAndRevealSelection(AXTextStateChangeIntent());
+ updateDataDetectorsForSelection();
+}
+
+#if ENABLE(TREE_DEBUGGING)
void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
{
@@ -2179,7 +2442,7 @@ void FrameSelection::expandSelectionToElementContainingCaretSelection()
RefPtr<Range> range = elementRangeContainingCaretSelection();
if (!range)
return;
- VisibleSelection selection(range.get(), DOWNSTREAM);
+ VisibleSelection selection(*range, DOWNSTREAM);
setSelection(selection);
}
@@ -2202,7 +2465,7 @@ PassRefPtr<Range> FrameSelection::elementRangeContainingCaretSelection() const
return nullptr;
Position startPos = createLegacyEditingPosition(element, 0);
- Position endPos = createLegacyEditingPosition(element, element->childNodeCount());
+ Position endPos = createLegacyEditingPosition(element, element->countChildNodes());
VisiblePosition startVisiblePos(startPos, VP_DEFAULT_AFFINITY);
VisiblePosition endVisiblePos(endPos, VP_DEFAULT_AFFINITY);
@@ -2302,9 +2565,7 @@ int FrameSelection::wordOffsetInRange(const Range *range) const
// FIXME: This will only work in cases where the selection remains in
// the same node after it is expanded. Improve to handle more complicated
// cases.
- ExceptionCode ec = 0;
- int result = selection.start().deprecatedEditingOffset() - range->startOffset(ec);
- ASSERT(!ec);
+ int result = selection.start().deprecatedEditingOffset() - range->startOffset();
if (result < 0)
result = 0;
return result;
@@ -2314,12 +2575,9 @@ bool FrameSelection::spaceFollowsWordInRange(const Range *range) const
{
if (!range)
return false;
- ExceptionCode ec = 0;
- Node* node = range->endContainer(ec);
- ASSERT(!ec);
- int endOffset = range->endOffset(ec);
- ASSERT(!ec);
- VisiblePosition pos(createLegacyEditingPosition(node, endOffset), VP_DEFAULT_AFFINITY);
+ Node& node = range->endContainer();
+ int endOffset = range->endOffset();
+ VisiblePosition pos(createLegacyEditingPosition(&node, endOffset), VP_DEFAULT_AFFINITY);
return isSpaceOrNewline(pos.characterAfter());
}
@@ -2389,15 +2647,12 @@ PassRefPtr<Range> FrameSelection::rangeByExtendingCurrentSelection(int amount) c
return rangeByAlteringCurrentSelection(AlterationExtend, amount);
}
-void FrameSelection::selectRangeOnElement(unsigned location, unsigned length, Node* node)
+void FrameSelection::selectRangeOnElement(unsigned location, unsigned length, Node& node)
{
RefPtr<Range> resultRange = m_frame->document()->createRange();
- ExceptionCode ec = 0;
- resultRange->setStart(node, location, ec);
- ASSERT(!ec);
- resultRange->setEnd(node, location + length, ec);
- ASSERT(!ec);
- VisibleSelection selection = VisibleSelection(resultRange.get(), SEL_DEFAULT_AFFINITY);
+ resultRange->setStart(node, location);
+ resultRange->setEnd(node, location + length);
+ VisibleSelection selection = VisibleSelection(*resultRange, SEL_DEFAULT_AFFINITY);
setSelection(selection, true);
}
@@ -2433,7 +2688,7 @@ VisibleSelection FrameSelection::wordSelectionContainingCaretSelection(const Vis
VisibleSelection newSelection = frameSelection.selection();
newSelection.expandUsingGranularity(WordGranularity);
- frameSelection.setSelection(newSelection, frameSelection.granularity());
+ frameSelection.setSelection(newSelection, defaultSetSelectionOptions(), AXTextStateChangeIntent(), AlignCursorOnScrollIfNeeded, frameSelection.granularity());
Position startPos(frameSelection.selection().start());
Position endPos(frameSelection.selection().end());
@@ -2598,7 +2853,7 @@ void FrameSelection::setCaretColor(const Color& caretColor)
}
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void showTree(const WebCore::FrameSelection& sel)
{
diff --git a/Source/WebCore/editing/FrameSelection.h b/Source/WebCore/editing/FrameSelection.h
index 2962d64f4..c1ea10168 100644
--- a/Source/WebCore/editing/FrameSelection.h
+++ b/Source/WebCore/editing/FrameSelection.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,14 +23,15 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef FrameSelection_h
-#define FrameSelection_h
+#pragma once
+#include "AXTextStateChangeIntent.h"
#include "EditingStyle.h"
+#include "Element.h"
#include "IntRect.h"
#include "LayoutRect.h"
#include "Range.h"
-#include "ScrollBehavior.h"
+#include "ScrollAlignment.h"
#include "Timer.h"
#include "VisibleSelection.h"
#include <wtf/Noncopyable.h>
@@ -46,9 +47,9 @@ class Frame;
class GraphicsContext;
class HTMLFormElement;
class MutableStyleProperties;
+class RenderBlock;
class RenderObject;
class RenderView;
-class Settings;
class VisiblePosition;
enum EUserTriggered { NotUserTriggered = 0, UserTriggered = 1 };
@@ -68,9 +69,8 @@ protected:
void invalidateCaretRect(Node*, bool caretRectChanged = false);
void clearCaretRect();
bool updateCaretRect(Document*, const VisiblePosition& caretPosition);
- IntRect absoluteBoundsForLocalRect(Node*, const LayoutRect&) const;
bool shouldRepaintCaret(const RenderView*, bool isContentEditable) const;
- void paintCaret(Node*, GraphicsContext*, const LayoutPoint&, const LayoutRect& clipRect) const;
+ void paintCaret(Node*, GraphicsContext&, const LayoutPoint&, const LayoutRect& clipRect) const;
const LayoutRect& localCaretRectWithoutUpdate() const { return m_caretLocalRect; }
@@ -93,8 +93,8 @@ class DragCaretController : private CaretBase {
public:
DragCaretController();
- RenderObject* caretRenderer() const;
- void paintDragCaret(Frame*, GraphicsContext*, const LayoutPoint&, const LayoutRect& clipRect) const;
+ RenderBlock* caretRenderer() const;
+ void paintDragCaret(Frame*, GraphicsContext&, const LayoutPoint&, const LayoutRect& clipRect) const;
bool isContentEditable() const { return m_position.rootEditableElement(); }
bool isContentRichlyEditable() const;
@@ -104,7 +104,7 @@ public:
void setCaretPosition(const VisiblePosition&);
void clear() { setCaretPosition(VisiblePosition()); }
- void nodeWillBeRemoved(Node*);
+ void nodeWillBeRemoved(Node&);
private:
VisiblePosition m_position;
@@ -118,53 +118,45 @@ public:
enum CursorAlignOnScroll { AlignCursorOnScrollIfNeeded,
AlignCursorOnScrollAlways };
enum SetSelectionOption {
- // 1 << 0 is reserved for EUserTriggered
+ FireSelectEvent = 1 << 0,
CloseTyping = 1 << 1,
ClearTypingStyle = 1 << 2,
SpellCorrectionTriggered = 1 << 3,
DoNotSetFocus = 1 << 4,
DictationTriggered = 1 << 5,
- DoNotUpdateAppearance = 1 << 6,
+ RevealSelection = 1 << 6,
};
typedef unsigned SetSelectionOptions; // Union of values in SetSelectionOption and EUserTriggered
- static inline EUserTriggered selectionOptionsToUserTriggered(SetSelectionOptions options)
+ static inline SetSelectionOptions defaultSetSelectionOptions(EUserTriggered userTriggered = NotUserTriggered)
{
- return static_cast<EUserTriggered>(options & UserTriggered);
+ return CloseTyping | ClearTypingStyle | (userTriggered ? (RevealSelection | FireSelectEvent) : 0);
}
- explicit FrameSelection(Frame* = 0);
+ WEBCORE_EXPORT explicit FrameSelection(Frame* = nullptr);
- Element* rootEditableElement() const { return m_selection.rootEditableElement(); }
- Element* rootEditableElementOrDocumentElement() const;
-
- bool hasEditableStyle() const { return m_selection.hasEditableStyle(); }
- bool isContentEditable() const { return m_selection.isContentEditable(); }
- bool isContentRichlyEditable() const { return m_selection.isContentRichlyEditable(); }
+ WEBCORE_EXPORT Element* rootEditableElementOrDocumentElement() const;
- void moveTo(const Range*, EAffinity, EUserTriggered = NotUserTriggered);
- void moveTo(const VisiblePosition&, EUserTriggered = NotUserTriggered, CursorAlignOnScroll = AlignCursorOnScrollIfNeeded);
- void moveTo(const VisiblePosition&, const VisiblePosition&, EUserTriggered = NotUserTriggered);
+ WEBCORE_EXPORT void moveTo(const Range*);
+ WEBCORE_EXPORT void moveTo(const VisiblePosition&, EUserTriggered = NotUserTriggered, CursorAlignOnScroll = AlignCursorOnScrollIfNeeded);
+ WEBCORE_EXPORT void moveTo(const VisiblePosition&, const VisiblePosition&, EUserTriggered = NotUserTriggered);
void moveTo(const Position&, EAffinity, EUserTriggered = NotUserTriggered);
void moveTo(const Position&, const Position&, EAffinity, EUserTriggered = NotUserTriggered);
+ void moveWithoutValidationTo(const Position&, const Position&, bool selectionHasDirection, bool shouldSetFocus, const AXTextStateChangeIntent& = AXTextStateChangeIntent());
const VisibleSelection& selection() const { return m_selection; }
- void setSelection(const VisibleSelection&, SetSelectionOptions = CloseTyping | ClearTypingStyle, CursorAlignOnScroll = AlignCursorOnScrollIfNeeded, TextGranularity = CharacterGranularity);
- void setSelection(const VisibleSelection& selection, TextGranularity granularity) { setSelection(selection, CloseTyping | ClearTypingStyle, AlignCursorOnScrollIfNeeded, granularity); }
- bool setSelectedRange(Range*, EAffinity, bool closeTyping);
- void selectAll();
- void clear();
+ WEBCORE_EXPORT void setSelection(const VisibleSelection&, SetSelectionOptions = defaultSetSelectionOptions(), AXTextStateChangeIntent = AXTextStateChangeIntent(), CursorAlignOnScroll = AlignCursorOnScrollIfNeeded, TextGranularity = CharacterGranularity);
+ WEBCORE_EXPORT bool setSelectedRange(Range*, EAffinity, bool closeTyping, EUserTriggered = NotUserTriggered);
+ WEBCORE_EXPORT void selectAll();
+ WEBCORE_EXPORT void clear();
void prepareForDestruction();
- // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
- void selectFrameElementInParentIfFullySelected();
+ void updateAppearanceAfterLayout();
+ void scheduleAppearanceUpdateAfterStyleChange();
+ void setNeedsSelectionUpdate();
bool contains(const LayoutPoint&);
- VisibleSelection::SelectionType selectionType() const { return m_selection.selectionType(); }
-
- EAffinity affinity() const { return m_selection.affinity(); }
-
- bool modify(EAlteration, SelectionDirection, TextGranularity, EUserTriggered = NotUserTriggered);
+ WEBCORE_EXPORT bool modify(EAlteration, SelectionDirection, TextGranularity, EUserTriggered = NotUserTriggered);
enum VerticalDirection { DirectionUp, DirectionDown };
bool modify(EAlteration, unsigned verticalDistance, VerticalDirection, EUserTriggered = NotUserTriggered, CursorAlignOnScroll = AlignCursorOnScrollIfNeeded);
@@ -178,19 +170,11 @@ public:
void setExtent(const VisiblePosition&, EUserTriggered = NotUserTriggered);
void setExtent(const Position&, EAffinity, EUserTriggered = NotUserTriggered);
- Position base() const { return m_selection.base(); }
- Position extent() const { return m_selection.extent(); }
- Position start() const { return m_selection.start(); }
- Position end() const { return m_selection.end(); }
-
// Return the renderer that is responsible for painting the caret (in the selection start node)
- RenderObject* caretRenderer() const;
-
- // Caret rect local to the caret's renderer
- LayoutRect localCaretRect();
+ RenderBlock* caretRendererWithoutUpdatingLayout() const;
// Bounds of (possibly transformed) caret in absolute coords
- IntRect absoluteCaretBounds();
+ WEBCORE_EXPORT IntRect absoluteCaretBounds(bool* insideFixed = nullptr);
void setCaretRectNeedsUpdate() { CaretBase::setCaretRectNeedsUpdate(); }
void willBeModified(EAlteration, SelectionDirection);
@@ -199,20 +183,17 @@ public:
bool isCaret() const { return m_selection.isCaret(); }
bool isRange() const { return m_selection.isRange(); }
bool isCaretOrRange() const { return m_selection.isCaretOrRange(); }
- bool isInPasswordField() const;
bool isAll(EditingBoundaryCrossingRule rule = CannotCrossEditingBoundary) const { return m_selection.isAll(rule); }
- PassRefPtr<Range> toNormalizedRange() const { return m_selection.toNormalizedRange(); }
+ RefPtr<Range> toNormalizedRange() const { return m_selection.toNormalizedRange(); }
void debugRenderer(RenderObject*, bool selected) const;
- void nodeWillBeRemoved(Node*);
+ void nodeWillBeRemoved(Node&);
void textWasReplaced(CharacterData*, unsigned offset, unsigned oldLength, unsigned newLength);
void setCaretVisible(bool caretIsVisible) { setCaretVisibility(caretIsVisible ? Visible : Hidden); }
- bool recomputeCaretRect();
- void invalidateCaretRect();
- void paintCaret(GraphicsContext*, const LayoutPoint&, const LayoutRect& clipRect);
+ void paintCaret(GraphicsContext&, const LayoutPoint&, const LayoutRect& clipRect);
// Used to suspend caret blinking while the mouse is down.
void setCaretBlinkingSuspended(bool suspended) { m_isCaretBlinkingSuspended = suspended; }
@@ -221,41 +202,39 @@ public:
// Focus
void setFocused(bool);
bool isFocused() const { return m_focused; }
- bool isFocusedAndActive() const;
+ WEBCORE_EXPORT bool isFocusedAndActive() const;
void pageActivationChanged();
// Painting.
- void updateAppearance();
+ WEBCORE_EXPORT void updateAppearance();
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void formatForDebugger(char* buffer, unsigned length) const;
void showTreeForThis() const;
#endif
#if PLATFORM(IOS)
public:
- void expandSelectionToElementContainingCaretSelection();
- PassRefPtr<Range> elementRangeContainingCaretSelection() const;
- void expandSelectionToWordContainingCaretSelection();
- PassRefPtr<Range> wordRangeContainingCaretSelection();
- void expandSelectionToStartOfWordContainingCaretSelection();
- UChar characterInRelationToCaretSelection(int amount) const;
- UChar characterBeforeCaretSelection() const;
- UChar characterAfterCaretSelection() const;
- int wordOffsetInRange(const Range*) const;
- bool spaceFollowsWordInRange(const Range*) const;
- bool selectionAtDocumentStart() const;
- bool selectionAtSentenceStart() const;
- bool selectionAtWordStart() const;
- PassRefPtr<Range> rangeByMovingCurrentSelection(int amount) const;
- PassRefPtr<Range> rangeByExtendingCurrentSelection(int amount) const;
- void selectRangeOnElement(unsigned location, unsigned length, Node*);
- void suppressCloseTyping() { ++m_closeTypingSuppressions; }
- void restoreCloseTyping() { --m_closeTypingSuppressions; }
- void clearCurrentSelection();
+ WEBCORE_EXPORT void expandSelectionToElementContainingCaretSelection();
+ WEBCORE_EXPORT PassRefPtr<Range> elementRangeContainingCaretSelection() const;
+ WEBCORE_EXPORT void expandSelectionToWordContainingCaretSelection();
+ WEBCORE_EXPORT PassRefPtr<Range> wordRangeContainingCaretSelection();
+ WEBCORE_EXPORT void expandSelectionToStartOfWordContainingCaretSelection();
+ WEBCORE_EXPORT UChar characterInRelationToCaretSelection(int amount) const;
+ WEBCORE_EXPORT UChar characterBeforeCaretSelection() const;
+ WEBCORE_EXPORT UChar characterAfterCaretSelection() const;
+ WEBCORE_EXPORT int wordOffsetInRange(const Range*) const;
+ WEBCORE_EXPORT bool spaceFollowsWordInRange(const Range*) const;
+ WEBCORE_EXPORT bool selectionAtDocumentStart() const;
+ WEBCORE_EXPORT bool selectionAtSentenceStart() const;
+ WEBCORE_EXPORT bool selectionAtWordStart() const;
+ WEBCORE_EXPORT PassRefPtr<Range> rangeByMovingCurrentSelection(int amount) const;
+ WEBCORE_EXPORT PassRefPtr<Range> rangeByExtendingCurrentSelection(int amount) const;
+ WEBCORE_EXPORT void selectRangeOnElement(unsigned location, unsigned length, Node&);
+ WEBCORE_EXPORT void clearCurrentSelection();
void setCaretBlinks(bool caretBlinks = true);
- void setCaretColor(const Color&);
- static VisibleSelection wordSelectionContainingCaretSelection(const VisibleSelection&);
+ WEBCORE_EXPORT void setCaretColor(const Color&);
+ WEBCORE_EXPORT static VisibleSelection wordSelectionContainingCaretSelection(const VisibleSelection&);
void setUpdateAppearanceEnabled(bool enabled) { m_updateAppearanceEnabled = enabled; }
void suppressScrolling() { ++m_scrollingSuppressCount; }
void restoreScrolling()
@@ -272,24 +251,23 @@ public:
bool shouldChangeSelection(const VisibleSelection&) const;
bool shouldDeleteSelection(const VisibleSelection&) const;
enum EndPointsAdjustmentMode { AdjustEndpointsAtBidiBoundary, DoNotAdjsutEndpoints };
- void setNonDirectionalSelectionIfNeeded(const VisibleSelection&, TextGranularity, EndPointsAdjustmentMode = DoNotAdjsutEndpoints);
- void updateSelectionCachesIfSelectionIsInsideTextFormControl(EUserTriggered);
-
- void paintDragCaret(GraphicsContext*, const LayoutPoint&, const LayoutRect& clipRect) const;
+ void setSelectionByMouseIfDifferent(const VisibleSelection&, TextGranularity, EndPointsAdjustmentMode = DoNotAdjsutEndpoints);
EditingStyle* typingStyle() const;
- PassRefPtr<MutableStyleProperties> copyTypingStyle() const;
+ WEBCORE_EXPORT PassRefPtr<MutableStyleProperties> copyTypingStyle() const;
void setTypingStyle(PassRefPtr<EditingStyle>);
void clearTypingStyle();
- FloatRect bounds(bool clipToVisibleContent = true) const;
+ WEBCORE_EXPORT FloatRect selectionBounds(bool clipToVisibleContent = true) const;
- void getClippedVisibleTextRectangles(Vector<FloatRect>&) const;
+ enum class TextRectangleHeight { TextHeight, SelectionHeight };
+ WEBCORE_EXPORT void getClippedVisibleTextRectangles(Vector<FloatRect>&, TextRectangleHeight = TextRectangleHeight::SelectionHeight) const;
+ WEBCORE_EXPORT void getTextRectangles(Vector<FloatRect>&, TextRectangleHeight = TextRectangleHeight::SelectionHeight) const;
- HTMLFormElement* currentForm() const;
+ WEBCORE_EXPORT HTMLFormElement* currentForm() const;
- void revealSelection(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, RevealExtentOption = DoNotRevealExtent);
- void setSelectionFromNone();
+ WEBCORE_EXPORT void revealSelection(SelectionRevealMode = SelectionRevealMode::Reveal, const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, RevealExtentOption = DoNotRevealExtent);
+ WEBCORE_EXPORT void setSelectionFromNone();
bool shouldShowBlockCursor() const { return m_shouldShowBlockCursor; }
void setShouldShowBlockCursor(bool);
@@ -297,7 +275,12 @@ public:
private:
enum EPositionType { START, END, BASE, EXTENT };
- void respondToNodeModification(Node*, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved);
+ void updateAndRevealSelection(const AXTextStateChangeIntent&);
+ void updateDataDetectorsForSelection();
+
+ bool setSelectionWithoutUpdatingAppearance(const VisibleSelection&, SetSelectionOptions, CursorAlignOnScroll, TextGranularity);
+
+ void respondToNodeModification(Node&, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved);
TextDirection directionOfEnclosingBlock();
TextDirection directionOfSelection();
@@ -308,25 +291,37 @@ private:
VisiblePosition modifyExtendingRight(TextGranularity);
VisiblePosition modifyExtendingForward(TextGranularity);
- VisiblePosition modifyMovingRight(TextGranularity);
- VisiblePosition modifyMovingForward(TextGranularity);
+ VisiblePosition modifyMovingRight(TextGranularity, bool* reachedBoundary = nullptr);
+ VisiblePosition modifyMovingForward(TextGranularity, bool* reachedBoundary = nullptr);
VisiblePosition modifyExtendingLeft(TextGranularity);
VisiblePosition modifyExtendingBackward(TextGranularity);
- VisiblePosition modifyMovingLeft(TextGranularity);
- VisiblePosition modifyMovingBackward(TextGranularity);
+ VisiblePosition modifyMovingLeft(TextGranularity, bool* reachedBoundary = nullptr);
+ VisiblePosition modifyMovingBackward(TextGranularity, bool* reachedBoundary = nullptr);
LayoutUnit lineDirectionPointForBlockDirectionNavigation(EPositionType);
+ AXTextStateChangeIntent textSelectionIntent(EAlteration, SelectionDirection, TextGranularity);
#if HAVE(ACCESSIBILITY)
- void notifyAccessibilityForSelectionChange();
+ void notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&);
+#else
+ void notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&) { }
#endif
+ void updateSelectionCachesIfSelectionIsInsideTextFormControl(EUserTriggered);
+
+ void selectFrameElementInParentIfFullySelected();
+
void setFocusedElementIfNeeded();
void focusedOrActiveStateChanged();
- void caretBlinkTimerFired(Timer<FrameSelection>&);
+ void caretBlinkTimerFired();
+
+ void updateAppearanceAfterLayoutOrStyleChange();
+ void appearanceUpdateTimerFired();
void setCaretVisibility(CaretVisibility);
+ bool recomputeCaretRect();
+ void invalidateCaretRect();
bool dispatchSelectStart();
@@ -342,21 +337,25 @@ private:
RefPtr<EditingStyle> m_typingStyle;
- Timer<FrameSelection> m_caretBlinkTimer;
+ Timer m_caretBlinkTimer;
+ Timer m_appearanceUpdateTimer;
// The painted bounds of the caret in absolute coordinates
IntRect m_absCaretBounds;
+ bool m_caretInsidePositionFixed : 1;
bool m_absCaretBoundsDirty : 1;
bool m_caretPaint : 1;
bool m_isCaretBlinkingSuspended : 1;
bool m_focused : 1;
bool m_shouldShowBlockCursor : 1;
+ bool m_pendingSelectionUpdate : 1;
+ bool m_shouldRevealSelection : 1;
+ bool m_alwaysAlignCursorOnScrollWhenRevealingSelection : 1;
#if PLATFORM(IOS)
bool m_updateAppearanceEnabled : 1;
bool m_caretBlinks : 1;
Color m_caretColor;
- int m_closeTypingSuppressions;
- int m_scrollingSuppressCount;
+ int m_scrollingSuppressCount { 0 };
#endif
};
@@ -367,7 +366,7 @@ inline EditingStyle* FrameSelection::typingStyle() const
inline void FrameSelection::clearTypingStyle()
{
- m_typingStyle.clear();
+ m_typingStyle = nullptr;
}
inline void FrameSelection::setTypingStyle(PassRefPtr<EditingStyle> style)
@@ -375,9 +374,9 @@ inline void FrameSelection::setTypingStyle(PassRefPtr<EditingStyle> style)
m_typingStyle = style;
}
-#if !(PLATFORM(MAC) || PLATFORM(GTK) || PLATFORM(EFL))
+#if !(PLATFORM(COCOA) || PLATFORM(GTK))
#if HAVE(ACCESSIBILITY)
-inline void FrameSelection::notifyAccessibilityForSelectionChange()
+inline void FrameSelection::notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&)
{
}
#endif
@@ -385,10 +384,8 @@ inline void FrameSelection::notifyAccessibilityForSelectionChange()
} // namespace WebCore
-#ifndef NDEBUG
-// Outside the WebCore namespace for ease of invocation from gdb.
+#if ENABLE(TREE_DEBUGGING)
+// Outside the WebCore namespace for ease of invocation from the debugger.
void showTree(const WebCore::FrameSelection&);
void showTree(const WebCore::FrameSelection*);
#endif
-
-#endif // FrameSelection_h
diff --git a/Source/WebCore/editing/HTMLInterchange.cpp b/Source/WebCore/editing/HTMLInterchange.cpp
index 2f7295118..03f94abff 100644
--- a/Source/WebCore/editing/HTMLInterchange.cpp
+++ b/Source/WebCore/editing/HTMLInterchange.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -30,6 +30,7 @@
#include "RenderText.h"
#include "Text.h"
#include "TextIterator.h"
+#include "htmlediting.h"
#include <wtf/text/StringBuilder.h>
#include <wtf/unicode/CharacterNames.h>
@@ -50,10 +51,10 @@ String convertHTMLTextToInterchangeFormat(const String& in, const Text* node)
unsigned consumed = 0;
while (i < in.length()) {
consumed = 1;
- if (isCollapsibleWhitespace(in[i])) {
+ if (deprecatedIsCollapsibleWhitespace(in[i])) {
// count number of adjoining spaces
unsigned j = i + 1;
- while (j < in.length() && isCollapsibleWhitespace(in[j]))
+ while (j < in.length() && deprecatedIsCollapsibleWhitespace(in[j]))
j++;
unsigned count = j - i;
consumed = count;
diff --git a/Source/WebCore/editing/HTMLInterchange.h b/Source/WebCore/editing/HTMLInterchange.h
index 4029ea27f..657b1bf5b 100644
--- a/Source/WebCore/editing/HTMLInterchange.h
+++ b/Source/WebCore/editing/HTMLInterchange.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef HTMLInterchange_h
-#define HTMLInterchange_h
+#pragma once
#include <wtf/Forward.h>
@@ -42,6 +41,4 @@ enum EAnnotateForInterchange { DoNotAnnotateForInterchange, AnnotateForInterchan
String convertHTMLTextToInterchangeFormat(const String&, const Text*);
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/IndentOutdentCommand.cpp b/Source/WebCore/editing/IndentOutdentCommand.cpp
index 63958bb4d..0729fc774 100644
--- a/Source/WebCore/editing/IndentOutdentCommand.cpp
+++ b/Source/WebCore/editing/IndentOutdentCommand.cpp
@@ -10,12 +10,12 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (IndentOutdentCommandINCLUDING, BUT NOT LIMITED TO,
+ * 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
@@ -28,14 +28,15 @@
#include "Document.h"
#include "ElementTraversal.h"
-#include "HTMLElement.h"
+#include "HTMLBRElement.h"
#include "HTMLNames.h"
+#include "HTMLOListElement.h"
+#include "HTMLUListElement.h"
#include "InsertLineBreakCommand.h"
#include "InsertListCommand.h"
-#include "RenderObject.h"
+#include "RenderElement.h"
#include "SplitElementCommand.h"
#include "Text.h"
-#include "TextIterator.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
@@ -48,10 +49,9 @@ static bool isListOrIndentBlockquote(const Node* node)
return node && (node->hasTagName(ulTag) || node->hasTagName(olTag) || node->hasTagName(blockquoteTag));
}
-IndentOutdentCommand::IndentOutdentCommand(Document& document, EIndentType typeOfAction, int marginInPixels)
+IndentOutdentCommand::IndentOutdentCommand(Document& document, EIndentType typeOfAction)
: ApplyBlockElementCommand(document, blockquoteTag, "margin: 0 0 0 40px; border: none; padding: 0px;")
, m_typeOfAction(typeOfAction)
- , m_marginInPixels(marginInPixels)
{
}
@@ -66,15 +66,18 @@ bool IndentOutdentCommand::tryIndentingAsListItem(const Position& start, const P
// Find the block that we want to indent. If it's not a list item (e.g., a div inside a list item), we bail out.
RefPtr<Element> selectedListItem = enclosingBlock(lastNodeInSelectedParagraph);
- // FIXME: we need to deal with the case where there is no li (malformed HTML)
- if (!selectedListItem->hasTagName(liTag))
+ if (!selectedListItem || !selectedListItem->hasTagName(liTag))
return false;
// FIXME: previousElementSibling does not ignore non-rendered content like <span></span>. Should we?
- RefPtr<Element> previousList = ElementTraversal::previousSibling(selectedListItem.get());
- RefPtr<Element> nextList = ElementTraversal::nextSibling(selectedListItem.get());
+ RefPtr<Element> previousList = ElementTraversal::previousSibling(*selectedListItem);
+ RefPtr<Element> nextList = ElementTraversal::nextSibling(*selectedListItem);
- RefPtr<Element> newList = document().createElement(listNode->tagQName(), false);
+ RefPtr<Element> newList;
+ if (is<HTMLUListElement>(*listNode))
+ newList = HTMLUListElement::create(document());
+ else
+ newList = HTMLOListElement::create(document());
insertNodeBefore(newList, selectedListItem);
moveParagraphWithClones(start, end, newList.get(), selectedListItem.get());
@@ -109,7 +112,7 @@ void IndentOutdentCommand::indentIntoBlockquote(const Position& start, const Pos
// Create a new blockquote and insert it as a child of the root editable element. We accomplish
// this by splitting all parents of the current paragraph up to that point.
targetBlockquote = createBlockElement();
- if (outerBlock == start.containerNode())
+ if (outerBlock == nodeToSplitTo)
insertNodeAt(targetBlockquote, start);
else
insertNodeBefore(targetBlockquote, outerBlock);
@@ -124,7 +127,7 @@ void IndentOutdentCommand::outdentParagraph()
VisiblePosition visibleStartOfParagraph = startOfParagraph(endingSelection().visibleStart());
VisiblePosition visibleEndOfParagraph = endOfParagraph(visibleStartOfParagraph);
- Node* enclosingNode = enclosingNodeOfType(visibleStartOfParagraph.deepEquivalent(), &isListOrIndentBlockquote);
+ auto* enclosingNode = downcast<HTMLElement>(enclosingNodeOfType(visibleStartOfParagraph.deepEquivalent(), &isListOrIndentBlockquote));
if (!enclosingNode || !enclosingNode->parentNode()->hasEditableStyle()) // We can't outdent if there is no place to go!
return;
@@ -157,7 +160,7 @@ void IndentOutdentCommand::outdentParagraph()
if (splitPointParent->hasTagName(blockquoteTag)
&& !splitPoint->hasTagName(blockquoteTag)
&& splitPointParent->parentNode()->hasEditableStyle()) // We can't outdent if there is no place to go!
- splitElement(toElement(splitPointParent), splitPoint);
+ splitElement(downcast<Element>(splitPointParent), splitPoint);
}
}
@@ -165,24 +168,26 @@ void IndentOutdentCommand::outdentParagraph()
visibleStartOfParagraph = VisiblePosition(visibleStartOfParagraph.deepEquivalent());
visibleEndOfParagraph = VisiblePosition(visibleEndOfParagraph.deepEquivalent());
if (visibleStartOfParagraph.isNotNull() && !isStartOfParagraph(visibleStartOfParagraph))
- insertNodeAt(createBreakElement(document()), visibleStartOfParagraph.deepEquivalent());
+ insertNodeAt(HTMLBRElement::create(document()), visibleStartOfParagraph.deepEquivalent());
if (visibleEndOfParagraph.isNotNull() && !isEndOfParagraph(visibleEndOfParagraph))
- insertNodeAt(createBreakElement(document()), visibleEndOfParagraph.deepEquivalent());
+ insertNodeAt(HTMLBRElement::create(document()), visibleEndOfParagraph.deepEquivalent());
return;
}
- Node* enclosingBlockFlow = enclosingBlock(visibleStartOfParagraph.deepEquivalent().deprecatedNode());
+
+ auto* startOfParagraphNode = visibleStartOfParagraph.deepEquivalent().deprecatedNode();
+ auto* enclosingBlockFlow = enclosingBlock(startOfParagraphNode);
RefPtr<Node> splitBlockquoteNode = enclosingNode;
if (enclosingBlockFlow != enclosingNode)
- splitBlockquoteNode = splitTreeToNode(enclosingBlockFlow, enclosingNode, true);
+ splitBlockquoteNode = splitTreeToNode(startOfParagraphNode, enclosingNode, true);
else {
// We split the blockquote at where we start outdenting.
- Node* highestInlineNode = highestEnclosingNodeOfType(visibleStartOfParagraph.deepEquivalent(), isInline, CannotCrossEditingBoundary, enclosingBlockFlow);
- splitElement(toElement(enclosingNode), (highestInlineNode) ? highestInlineNode : visibleStartOfParagraph.deepEquivalent().deprecatedNode());
+ auto* highestInlineNode = highestEnclosingNodeOfType(visibleStartOfParagraph.deepEquivalent(), isInline, CannotCrossEditingBoundary, enclosingBlockFlow);
+ splitElement(enclosingNode, highestInlineNode ? highestInlineNode : visibleStartOfParagraph.deepEquivalent().deprecatedNode());
}
- RefPtr<Node> placeholder = createBreakElement(document());
- insertNodeBefore(placeholder, splitBlockquoteNode);
- moveParagraph(startOfParagraph(visibleStartOfParagraph), endOfParagraph(visibleEndOfParagraph), positionBeforeNode(placeholder.get()), true);
+ auto placeholder = HTMLBRElement::create(document());
+ insertNodeBefore(placeholder.copyRef(), splitBlockquoteNode);
+ moveParagraph(startOfParagraph(visibleStartOfParagraph), endOfParagraph(visibleEndOfParagraph), positionBeforeNode(placeholder.ptr()), true);
}
// FIXME: We should merge this function with ApplyBlockElementCommand::formatSelection
@@ -194,7 +199,7 @@ void IndentOutdentCommand::outdentRegion(const VisiblePosition& startOfSelection
outdentParagraph();
return;
}
-
+
Position originalSelectionEnd = endingSelection().end();
VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection);
VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next());
@@ -205,16 +210,16 @@ void IndentOutdentCommand::outdentRegion(const VisiblePosition& startOfSelection
setEndingSelection(VisibleSelection(originalSelectionEnd, DOWNSTREAM));
else
setEndingSelection(endOfCurrentParagraph);
-
+
outdentParagraph();
-
+
// outdentParagraph could move more than one paragraph if the paragraph
// is in a list item. As a result, endAfterSelection and endOfNextParagraph
// could refer to positions no longer in the document.
- if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().anchorNode()->inDocument())
+ if (endAfterSelection.isNotNull() && !endAfterSelection.deepEquivalent().anchorNode()->isConnected())
break;
-
- if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().anchorNode()->inDocument()) {
+
+ if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().anchorNode()->isConnected()) {
endOfCurrentParagraph = endingSelection().end();
endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next());
}
@@ -233,7 +238,7 @@ void IndentOutdentCommand::formatSelection(const VisiblePosition& startOfSelecti
void IndentOutdentCommand::formatRange(const Position& start, const Position& end, const Position&, RefPtr<Element>& blockquoteForNextIndent)
{
if (tryIndentingAsListItem(start, end))
- blockquoteForNextIndent = 0;
+ blockquoteForNextIndent = nullptr;
else
indentIntoBlockquote(start, end, blockquoteForNextIndent);
}
diff --git a/Source/WebCore/editing/IndentOutdentCommand.h b/Source/WebCore/editing/IndentOutdentCommand.h
index 4ebafe96b..c30f74396 100644
--- a/Source/WebCore/editing/IndentOutdentCommand.h
+++ b/Source/WebCore/editing/IndentOutdentCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef IndentOutdentCommand_h
-#define IndentOutdentCommand_h
+#pragma once
#include "ApplyBlockElementCommand.h"
#include "EditAction.h"
@@ -34,17 +33,17 @@ namespace WebCore {
class IndentOutdentCommand : public ApplyBlockElementCommand {
public:
enum EIndentType { Indent, Outdent };
- static PassRefPtr<IndentOutdentCommand> create(Document& document, EIndentType type, int marginInPixels = 0)
+ static Ref<IndentOutdentCommand> create(Document& document, EIndentType type)
{
- return adoptRef(new IndentOutdentCommand(document, type, marginInPixels));
+ return adoptRef(*new IndentOutdentCommand(document, type));
}
- virtual bool preservesTypingStyle() const { return true; }
+ bool preservesTypingStyle() const override { return true; }
private:
- IndentOutdentCommand(Document&, EIndentType, int marginInPixels);
+ IndentOutdentCommand(Document&, EIndentType);
- virtual EditAction editingAction() const { return m_typeOfAction == Indent ? EditActionIndent : EditActionOutdent; }
+ EditAction editingAction() const override { return m_typeOfAction == Indent ? EditActionIndent : EditActionOutdent; }
void indentRegion(const VisiblePosition&, const VisiblePosition&);
void outdentRegion(const VisiblePosition&, const VisiblePosition&);
@@ -52,13 +51,10 @@ private:
bool tryIndentingAsListItem(const Position&, const Position&);
void indentIntoBlockquote(const Position&, const Position&, RefPtr<Element>&);
- void formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection);
- void formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<Element>& blockquoteForNextIndent);
+ void formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection) override;
+ void formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<Element>& blockquoteForNextIndent) override;
EIndentType m_typeOfAction;
- int m_marginInPixels;
};
} // namespace WebCore
-
-#endif // IndentOutdentCommand_h
diff --git a/Source/WebCore/editing/InsertIntoTextNodeCommand.cpp b/Source/WebCore/editing/InsertIntoTextNodeCommand.cpp
index 370d4bf4e..9d2894e3e 100644
--- a/Source/WebCore/editing/InsertIntoTextNodeCommand.cpp
+++ b/Source/WebCore/editing/InsertIntoTextNodeCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -26,21 +26,21 @@
#include "config.h"
#include "InsertIntoTextNodeCommand.h"
-#include "AXObjectCache.h"
#include "Document.h"
-#include "ExceptionCodePlaceholder.h"
+#include "Frame.h"
#include "RenderText.h"
#include "Settings.h"
#include "Text.h"
+
#if PLATFORM(IOS)
#include "RenderText.h"
#endif
namespace WebCore {
-InsertIntoTextNodeCommand::InsertIntoTextNodeCommand(PassRefPtr<Text> node, unsigned offset, const String& text)
- : SimpleEditCommand(node->document())
- , m_node(node)
+InsertIntoTextNodeCommand::InsertIntoTextNodeCommand(RefPtr<Text>&& node, unsigned offset, const String& text, EditAction editingAction)
+ : SimpleEditCommand(node->document(), editingAction)
+ , m_node(WTFMove(node))
, m_offset(offset)
, m_text(text)
{
@@ -51,8 +51,7 @@ InsertIntoTextNodeCommand::InsertIntoTextNodeCommand(PassRefPtr<Text> node, unsi
void InsertIntoTextNodeCommand::doApply()
{
- // FIXME: EditCommand should always have a Frame, so going through Document for Settings shouldn't be necessary.
- bool passwordEchoEnabled = document().settings() && document().settings()->passwordEchoEnabled();
+ bool passwordEchoEnabled = frame().settings().passwordEchoEnabled();
if (passwordEchoEnabled)
document().updateLayoutIgnorePendingStylesheets();
@@ -60,42 +59,40 @@ void InsertIntoTextNodeCommand::doApply()
return;
if (passwordEchoEnabled) {
- RenderText* renderText = m_node->renderer();
- if (renderText && renderText->isSecure())
- renderText->momentarilyRevealLastTypedCharacter(m_offset + m_text.length() - 1);
+ if (RenderText* renderText = m_node->renderer())
+ renderText->momentarilyRevealLastTypedCharacter(m_offset + m_text.length());
}
- m_node->insertData(m_offset, m_text, IGNORE_EXCEPTION);
-
- if (AXObjectCache* cache = document().existingAXObjectCache())
- cache->nodeTextChangeNotification(m_node.get(), AXObjectCache::AXTextInserted, m_offset, m_text);
+ m_node->insertData(m_offset, m_text);
}
#if PLATFORM(IOS)
+
+// FIXME: Why would reapply be iOS-specific?
void InsertIntoTextNodeCommand::doReapply()
{
- ExceptionCode ec;
- m_node->insertData(m_offset, m_text, ec);
+ // FIXME: Shouldn't this have a hasEditableStyle check?
+
+ m_node->insertData(m_offset, m_text);
}
+
#endif
void InsertIntoTextNodeCommand::doUnapply()
{
if (!m_node->hasEditableStyle())
return;
-
- // Need to notify this before actually deleting the text
- if (AXObjectCache* cache = document().existingAXObjectCache())
- cache->nodeTextChangeNotification(m_node.get(), AXObjectCache::AXTextDeleted, m_offset, m_text);
- m_node->deleteData(m_offset, m_text.length(), IGNORE_EXCEPTION);
+ m_node->deleteData(m_offset, m_text.length());
}
#ifndef NDEBUG
+
void InsertIntoTextNodeCommand::getNodesInCommand(HashSet<Node*>& nodes)
{
addNodeAndDescendants(m_node.get(), nodes);
}
+
#endif
} // namespace WebCore
diff --git a/Source/WebCore/editing/InsertIntoTextNodeCommand.h b/Source/WebCore/editing/InsertIntoTextNodeCommand.h
index f56133464..5b8822288 100644
--- a/Source/WebCore/editing/InsertIntoTextNodeCommand.h
+++ b/Source/WebCore/editing/InsertIntoTextNodeCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef InsertIntoTextNodeCommand_h
-#define InsertIntoTextNodeCommand_h
+#pragma once
#include "EditCommand.h"
@@ -34,22 +33,25 @@ class Text;
class InsertIntoTextNodeCommand : public SimpleEditCommand {
public:
- static PassRefPtr<InsertIntoTextNodeCommand> create(PassRefPtr<Text> node, unsigned offset, const String& text)
+ static Ref<InsertIntoTextNodeCommand> create(RefPtr<Text>&& node, unsigned offset, const String& text, EditAction editingAction = EditActionInsert)
{
- return adoptRef(new InsertIntoTextNodeCommand(node, offset, text));
+ return adoptRef(*new InsertIntoTextNodeCommand(WTFMove(node), offset, text, editingAction));
}
-private:
- InsertIntoTextNodeCommand(PassRefPtr<Text> node, unsigned offset, const String& text);
+ const String& insertedText();
+
+protected:
+ InsertIntoTextNodeCommand(RefPtr<Text>&& node, unsigned offset, const String& text, EditAction editingAction);
- virtual void doApply() override;
- virtual void doUnapply() override;
+private:
+ void doApply() override;
+ void doUnapply() override;
#if PLATFORM(IOS)
- virtual void doReapply() override;
+ void doReapply() override;
#endif
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
RefPtr<Text> m_node;
@@ -57,6 +59,9 @@ private:
String m_text;
};
-} // namespace WebCore
+inline const String& InsertIntoTextNodeCommand::insertedText()
+{
+ return m_text;
+}
-#endif // InsertIntoTextNodeCommand_h
+} // namespace WebCore
diff --git a/Source/WebCore/editing/InsertLineBreakCommand.cpp b/Source/WebCore/editing/InsertLineBreakCommand.cpp
index 3cc8996d0..812c0b46f 100644
--- a/Source/WebCore/editing/InsertLineBreakCommand.cpp
+++ b/Source/WebCore/editing/InsertLineBreakCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005, 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -29,7 +29,8 @@
#include "Document.h"
#include "Frame.h"
#include "FrameSelection.h"
-#include "HTMLElement.h"
+#include "HTMLBRElement.h"
+#include "HTMLHRElement.h"
#include "HTMLNames.h"
#include "HTMLTableElement.h"
#include "RenderElement.h"
@@ -52,45 +53,45 @@ bool InsertLineBreakCommand::preservesTypingStyle() const
return true;
}
-void InsertLineBreakCommand::insertNodeAfterPosition(Node* node, const Position& pos)
+void InsertLineBreakCommand::insertNodeAfterPosition(Node* node, const Position& position)
{
// Insert the BR after the caret position. In the case the
// position is a block, do an append. We don't want to insert
// the BR *after* the block.
- Element* cb = deprecatedEnclosingBlockFlowElement(pos.deprecatedNode());
- if (cb == pos.deprecatedNode())
- appendNode(node, cb);
+ auto* element = deprecatedEnclosingBlockFlowElement(position.deprecatedNode());
+ if (element == position.deprecatedNode())
+ appendNode(node, element);
else
- insertNodeAfter(node, pos.deprecatedNode());
+ insertNodeAfter(node, position.deprecatedNode());
}
-void InsertLineBreakCommand::insertNodeBeforePosition(Node* node, const Position& pos)
+void InsertLineBreakCommand::insertNodeBeforePosition(Node* node, const Position& position)
{
// Insert the BR after the caret position. In the case the
// position is a block, do an append. We don't want to insert
// the BR *before* the block.
- Element* cb = deprecatedEnclosingBlockFlowElement(pos.deprecatedNode());
- if (cb == pos.deprecatedNode())
- appendNode(node, cb);
+ auto* element = deprecatedEnclosingBlockFlowElement(position.deprecatedNode());
+ if (element == position.deprecatedNode())
+ appendNode(node, element);
else
- insertNodeBefore(node, pos.deprecatedNode());
+ insertNodeBefore(node, position.deprecatedNode());
}
// Whether we should insert a break element or a '\n'.
-bool InsertLineBreakCommand::shouldUseBreakElement(const Position& insertionPos)
+bool InsertLineBreakCommand::shouldUseBreakElement(const Position& position)
{
// An editing position like [input, 0] actually refers to the position before
// the input element, and in that case we need to check the input element's
// parent's renderer.
- Position p(insertionPos.parentAnchoredEquivalent());
- return p.deprecatedNode()->renderer() && !p.deprecatedNode()->renderer()->style().preserveNewline();
+ auto* node = position.parentAnchoredEquivalent().deprecatedNode();
+ return node->renderer() && !node->renderer()->style().preserveNewline();
}
void InsertLineBreakCommand::doApply()
{
deleteSelection();
VisibleSelection selection = endingSelection();
- if (!selection.isNonOrphanedCaretOrRange())
+ if (selection.isNoneOrOrphaned())
return;
VisiblePosition caret(selection.visibleStart());
@@ -99,60 +100,59 @@ void InsertLineBreakCommand::doApply()
if (caret.isNull())
return;
- Position pos(caret.deepEquivalent());
+ Position position(caret.deepEquivalent());
- pos = positionAvoidingSpecialElementBoundary(pos);
-
- pos = positionOutsideTabSpan(pos);
+ position = positionAvoidingSpecialElementBoundary(position);
+ position = positionOutsideTabSpan(position);
RefPtr<Node> nodeToInsert;
- if (shouldUseBreakElement(pos))
- nodeToInsert = createBreakElement(document());
+ if (shouldUseBreakElement(position))
+ nodeToInsert = HTMLBRElement::create(document());
else
nodeToInsert = document().createTextNode("\n");
// FIXME: Need to merge text nodes when inserting just after or before text.
if (isEndOfParagraph(caret) && !lineBreakExistsAtVisiblePosition(caret)) {
- bool needExtraLineBreak = !pos.deprecatedNode()->hasTagName(hrTag) && !isHTMLTableElement(pos.deprecatedNode());
+ bool needExtraLineBreak = !is<HTMLHRElement>(*position.deprecatedNode()) && !is<HTMLTableElement>(*position.deprecatedNode());
- insertNodeAt(nodeToInsert.get(), pos);
+ insertNodeAt(nodeToInsert.get(), position);
if (needExtraLineBreak)
insertNodeBefore(nodeToInsert->cloneNode(false), nodeToInsert);
VisiblePosition endingPosition(positionBeforeNode(nodeToInsert.get()));
setEndingSelection(VisibleSelection(endingPosition, endingSelection().isDirectional()));
- } else if (pos.deprecatedEditingOffset() <= caretMinOffset(pos.deprecatedNode())) {
- insertNodeAt(nodeToInsert.get(), pos);
+ } else if (position.deprecatedEditingOffset() <= caretMinOffset(*position.deprecatedNode())) {
+ insertNodeAt(nodeToInsert.get(), position);
// Insert an extra br or '\n' if the just inserted one collapsed.
if (!isStartOfParagraph(positionBeforeNode(nodeToInsert.get())))
- insertNodeBefore(nodeToInsert->cloneNode(false).get(), nodeToInsert.get());
+ insertNodeBefore(nodeToInsert->cloneNode(false), nodeToInsert.get());
setEndingSelection(VisibleSelection(positionInParentAfterNode(nodeToInsert.get()), DOWNSTREAM, endingSelection().isDirectional()));
// If we're inserting after all of the rendered text in a text node, or into a non-text node,
// a simple insertion is sufficient.
- } else if (pos.deprecatedEditingOffset() >= caretMaxOffset(pos.deprecatedNode()) || !pos.deprecatedNode()->isTextNode()) {
- insertNodeAt(nodeToInsert.get(), pos);
+ } else if (position.deprecatedEditingOffset() >= caretMaxOffset(*position.deprecatedNode()) || !is<Text>(*position.deprecatedNode())) {
+ insertNodeAt(nodeToInsert.get(), position);
setEndingSelection(VisibleSelection(positionInParentAfterNode(nodeToInsert.get()), DOWNSTREAM, endingSelection().isDirectional()));
- } else if (pos.deprecatedNode()->isTextNode()) {
+ } else if (is<Text>(*position.deprecatedNode())) {
// Split a text node
- Text* textNode = toText(pos.deprecatedNode());
- splitTextNode(textNode, pos.deprecatedEditingOffset());
- insertNodeBefore(nodeToInsert, textNode);
- Position endingPosition = firstPositionInNode(textNode);
+ Text& textNode = downcast<Text>(*position.deprecatedNode());
+ splitTextNode(&textNode, position.deprecatedEditingOffset());
+ insertNodeBefore(nodeToInsert, &textNode);
+ Position endingPosition = firstPositionInNode(&textNode);
// Handle whitespace that occurs after the split
document().updateLayoutIgnorePendingStylesheets();
if (!endingPosition.isRenderedCharacter()) {
- Position positionBeforeTextNode(positionInParentBeforeNode(textNode));
+ Position positionBeforeTextNode(positionInParentBeforeNode(&textNode));
// Clear out all whitespace and insert one non-breaking space
deleteInsignificantTextDownstream(endingPosition);
- ASSERT(!textNode->renderer() || textNode->renderer()->style().collapseWhiteSpace());
+ ASSERT(!textNode.renderer() || textNode.renderer()->style().collapseWhiteSpace());
// Deleting insignificant whitespace will remove textNode if it contains nothing but insignificant whitespace.
- if (textNode->inDocument())
- insertTextIntoNode(textNode, 0, nonBreakingSpaceString());
+ if (textNode.isConnected())
+ insertTextIntoNode(&textNode, 0, nonBreakingSpaceString());
else {
RefPtr<Text> nbspNode = document().createTextNode(nonBreakingSpaceString());
insertNodeAt(nbspNode.get(), positionBeforeTextNode);
diff --git a/Source/WebCore/editing/InsertLineBreakCommand.h b/Source/WebCore/editing/InsertLineBreakCommand.h
index d48847a93..2b3d1bfa7 100644
--- a/Source/WebCore/editing/InsertLineBreakCommand.h
+++ b/Source/WebCore/editing/InsertLineBreakCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef InsertLineBreakCommand_h
-#define InsertLineBreakCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -32,17 +31,17 @@ namespace WebCore {
class InsertLineBreakCommand : public CompositeEditCommand {
public:
- static PassRefPtr<InsertLineBreakCommand> create(Document& document)
+ static Ref<InsertLineBreakCommand> create(Document& document)
{
- return adoptRef(new InsertLineBreakCommand(document));
+ return adoptRef(*new InsertLineBreakCommand(document));
}
private:
explicit InsertLineBreakCommand(Document&);
- virtual void doApply();
+ void doApply() override;
- virtual bool preservesTypingStyle() const;
+ bool preservesTypingStyle() const override;
void insertNodeAfterPosition(Node*, const Position&);
void insertNodeBeforePosition(Node*, const Position&);
@@ -50,5 +49,3 @@ private:
};
} // namespace WebCore
-
-#endif // InsertLineBreakCommand_h
diff --git a/Source/WebCore/editing/InsertListCommand.cpp b/Source/WebCore/editing/InsertListCommand.cpp
index d3ead9780..894a7cffc 100644
--- a/Source/WebCore/editing/InsertListCommand.cpp
+++ b/Source/WebCore/editing/InsertListCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -24,15 +24,16 @@
*/
#include "config.h"
-#include "Element.h"
-#include "ElementTraversal.h"
#include "InsertListCommand.h"
-#include "ExceptionCodePlaceholder.h"
-#include "htmlediting.h"
-#include "HTMLElement.h"
+
+#include "ElementTraversal.h"
+#include "HTMLBRElement.h"
+#include "HTMLLIElement.h"
#include "HTMLNames.h"
-#include "TextIterator.h"
+#include "HTMLUListElement.h"
+#include "Range.h"
#include "VisibleUnits.h"
+#include "htmlediting.h"
namespace WebCore {
@@ -46,7 +47,7 @@ static Node* enclosingListChild(Node* node, Node* listNode)
return listChild;
}
-PassRefPtr<HTMLElement> InsertListCommand::insertList(Document& document, Type type)
+RefPtr<HTMLElement> InsertListCommand::insertList(Document& document, Type type)
{
RefPtr<InsertListCommand> insertCommand = create(document, type);
insertCommand->apply();
@@ -55,15 +56,15 @@ PassRefPtr<HTMLElement> InsertListCommand::insertList(Document& document, Type t
HTMLElement* InsertListCommand::fixOrphanedListChild(Node* node)
{
- RefPtr<HTMLElement> listElement = createUnorderedListElement(document());
- insertNodeBefore(listElement, node);
+ auto listElement = HTMLUListElement::create(document());
+ insertNodeBefore(listElement.copyRef(), node);
removeNode(node);
- appendNode(node, listElement);
- m_listElement = listElement;
- return listElement.get();
+ appendNode(node, listElement.copyRef());
+ m_listElement = listElement.copyRef();
+ return listElement.ptr();
}
-PassRefPtr<HTMLElement> InsertListCommand::mergeWithNeighboringLists(PassRefPtr<HTMLElement> passedList)
+RefPtr<HTMLElement> InsertListCommand::mergeWithNeighboringLists(PassRefPtr<HTMLElement> passedList)
{
RefPtr<HTMLElement> list = passedList;
Element* previousList = list->previousElementSibling();
@@ -71,17 +72,17 @@ PassRefPtr<HTMLElement> InsertListCommand::mergeWithNeighboringLists(PassRefPtr<
mergeIdenticalElements(previousList, list);
if (!list)
- return 0;
- Element* sibling = ElementTraversal::nextSibling(list.get());
- if (!sibling || !sibling->isHTMLElement())
- return list.release();
+ return nullptr;
+ Element* sibling = ElementTraversal::nextSibling(*list);
+ if (!is<HTMLElement>(sibling))
+ return list;
- RefPtr<HTMLElement> nextList = toHTMLElement(sibling);
+ RefPtr<HTMLElement> nextList = downcast<HTMLElement>(sibling);
if (canMergeLists(list.get(), nextList.get())) {
mergeIdenticalElements(list, nextList);
- return nextList.release();
+ return nextList;
}
- return list.release();
+ return list;
}
bool InsertListCommand::selectionHasListOfType(const VisibleSelection& selection, const QualifiedName& listTag)
@@ -110,12 +111,9 @@ InsertListCommand::InsertListCommand(Document& document, Type type)
void InsertListCommand::doApply()
{
- if (!endingSelection().isNonOrphanedCaretOrRange())
+ if (endingSelection().isNoneOrOrphaned() || !endingSelection().isContentRichlyEditable())
return;
- if (!endingSelection().rootEditableElement())
- return;
-
VisiblePosition visibleEnd = endingSelection().visibleEnd();
VisiblePosition visibleStart = endingSelection().visibleStart();
// When a selection ends at the start of a paragraph, we rarely paint
@@ -126,10 +124,13 @@ void InsertListCommand::doApply()
// FIXME: We paint the gap before some paragraphs that are indented with left
// margin/padding, but not others. We should make the gap painting more consistent and
// then use a left margin/padding rule here.
- if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd, CanSkipOverEditingBoundary))
+ if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd, CanSkipOverEditingBoundary)) {
setEndingSelection(VisibleSelection(visibleStart, visibleEnd.previous(CannotCrossEditingBoundary), endingSelection().isDirectional()));
+ if (!endingSelection().rootEditableElement())
+ return;
+ }
- const QualifiedName& listTag = (m_type == OrderedList) ? olTag : ulTag;
+ auto& listTag = (m_type == OrderedList) ? olTag : ulTag;
if (endingSelection().isRange()) {
VisibleSelection selection = selectionForParagraphIteration(endingSelection());
ASSERT(selection.isRange());
@@ -148,7 +149,7 @@ void InsertListCommand::doApply()
// infinite loop and because there is no more work to be done.
// FIXME(<rdar://problem/5983974>): The endingSelection() may be incorrect here. Compute
// the new location of endOfSelection and use it as the end of the new selection.
- if (!startOfLastParagraph.deepEquivalent().anchorNode()->inDocument())
+ if (!startOfLastParagraph.deepEquivalent().anchorNode()->isConnected())
return;
setEndingSelection(startOfCurrentParagraph);
@@ -191,7 +192,12 @@ void InsertListCommand::doApply()
doApplyForSingleParagraph(false, listTag, endingSelection().firstRange().get());
}
-void InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const QualifiedName& listTag, Range* currentSelection)
+EditAction InsertListCommand::editingAction() const
+{
+ return m_type == OrderedList ? EditActionInsertOrderedList : EditActionInsertUnorderedList;
+}
+
+void InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const HTMLQualifiedName& listTag, Range* currentSelection)
{
// FIXME: This will produce unexpected results for a selection that starts just before a
// table and ends inside the first cell, selectionForParagraphIteration should probably
@@ -206,31 +212,32 @@ void InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const Qu
listNode = fixOrphanedListChild(listChildNode);
listNode = mergeWithNeighboringLists(listNode);
}
- if (!listNode->hasTagName(listTag))
+ if (!listNode->hasTagName(listTag)) {
// listChildNode will be removed from the list and a list of type m_type will be created.
switchListType = true;
+ }
// If the list is of the desired type, and we are not removing the list, then exit early.
if (!switchListType && forceCreateList)
return;
// If the entire list is selected, then convert the whole list.
- if (switchListType && isNodeVisiblyContainedWithin(listNode.get(), currentSelection)) {
- bool rangeStartIsInList = visiblePositionBeforeNode(listNode.get()) == currentSelection->startPosition();
- bool rangeEndIsInList = visiblePositionAfterNode(listNode.get()) == currentSelection->endPosition();
+ if (switchListType && isNodeVisiblyContainedWithin(*listNode, *currentSelection)) {
+ bool rangeStartIsInList = visiblePositionBeforeNode(*listNode) == currentSelection->startPosition();
+ bool rangeEndIsInList = visiblePositionAfterNode(*listNode) == currentSelection->endPosition();
RefPtr<HTMLElement> newList = createHTMLElement(document(), listTag);
insertNodeBefore(newList, listNode);
- Node* firstChildInList = enclosingListChild(VisiblePosition(firstPositionInNode(listNode.get())).deepEquivalent().deprecatedNode(), listNode.get());
- Node* outerBlock = isBlockFlowElement(firstChildInList) ? firstChildInList : listNode.get();
+ auto* firstChildInList = enclosingListChild(VisiblePosition(firstPositionInNode(listNode.get())).deepEquivalent().deprecatedNode(), listNode.get());
+ Node* outerBlock = firstChildInList && isBlockFlowElement(*firstChildInList) ? firstChildInList : listNode.get();
moveParagraphWithClones(firstPositionInNode(listNode.get()), lastPositionInNode(listNode.get()), newList.get(), outerBlock);
// Manually remove listNode because moveParagraphWithClones sometimes leaves it behind in the document.
// See the bug 33668 and editing/execCommand/insert-list-orphaned-item-with-nested-lists.html.
// FIXME: This might be a bug in moveParagraphWithClones or deleteSelection.
- if (listNode && listNode->inDocument())
+ if (listNode && listNode->isConnected())
removeNode(listNode);
newList = mergeWithNeighboringLists(newList);
@@ -238,9 +245,9 @@ void InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const Qu
// Restore the start and the end of current selection if they started inside listNode
// because moveParagraphWithClones could have removed them.
if (rangeStartIsInList && newList)
- currentSelection->setStart(newList, 0, IGNORE_EXCEPTION);
+ currentSelection->setStart(*newList, 0);
if (rangeEndIsInList && newList)
- currentSelection->setEnd(newList, lastOffsetInNode(newList.get()), IGNORE_EXCEPTION);
+ currentSelection->setEnd(*newList, lastOffsetInNode(newList.get()));
setEndingSelection(VisiblePosition(firstPositionInNode(newList.get())));
@@ -276,12 +283,12 @@ void InsertListCommand::unlistifyParagraph(const VisiblePosition& originalStart,
}
// When removing a list, we must always create a placeholder to act as a point of insertion
// for the list content being removed.
- RefPtr<Element> placeholder = createBreakElement(document());
+ RefPtr<Element> placeholder = HTMLBRElement::create(document());
RefPtr<Element> nodeToInsert = placeholder;
// If the content of the list item will be moved into another list, put it in a list item
// so that we don't create an orphaned list child.
if (enclosingList(listNode)) {
- nodeToInsert = createListItemElement(document());
+ nodeToInsert = HTMLLIElement::create(document());
appendNode(placeholder, nodeToInsert);
}
@@ -329,7 +336,7 @@ static Element* adjacentEnclosingList(const VisiblePosition& pos, const VisibleP
return listNode;
}
-PassRefPtr<HTMLElement> InsertListCommand::listifyParagraph(const VisiblePosition& originalStart, const QualifiedName& listTag)
+RefPtr<HTMLElement> InsertListCommand::listifyParagraph(const VisiblePosition& originalStart, const QualifiedName& listTag)
{
VisiblePosition start = startOfParagraph(originalStart, CanSkipOverEditingBoundary);
VisiblePosition end = endOfParagraph(start, CanSkipOverEditingBoundary);
@@ -338,29 +345,29 @@ PassRefPtr<HTMLElement> InsertListCommand::listifyParagraph(const VisiblePositio
return 0;
// Check for adjoining lists.
- RefPtr<HTMLElement> listItemElement = createListItemElement(document());
- RefPtr<HTMLElement> placeholder = createBreakElement(document());
- appendNode(placeholder, listItemElement);
+ auto listItemElement = HTMLLIElement::create(document());
+ auto placeholder = HTMLBRElement::create(document());
+ appendNode(placeholder.copyRef(), listItemElement.copyRef());
// Place list item into adjoining lists.
Element* previousList = adjacentEnclosingList(start.deepEquivalent(), start.previous(CannotCrossEditingBoundary), listTag);
Element* nextList = adjacentEnclosingList(start.deepEquivalent(), end.next(CannotCrossEditingBoundary), listTag);
RefPtr<HTMLElement> listElement;
if (previousList)
- appendNode(listItemElement, previousList);
+ appendNode(WTFMove(listItemElement), previousList);
else if (nextList)
- insertNodeAt(listItemElement, positionBeforeNode(nextList));
+ insertNodeAt(WTFMove(listItemElement), positionBeforeNode(nextList));
else {
// Create the list.
listElement = createHTMLElement(document(), listTag);
- appendNode(listItemElement, listElement);
+ appendNode(WTFMove(listItemElement), listElement);
if (start == end && isBlock(start.deepEquivalent().deprecatedNode())) {
// Inserting the list into an empty paragraph that isn't held open
// by a br or a '\n', will invalidate start and end. Insert
// a placeholder and then recompute start and end.
- RefPtr<Node> placeholder = insertBlockPlaceholder(start.deepEquivalent());
- start = positionBeforeNode(placeholder.get());
+ auto blockPlaceholder = insertBlockPlaceholder(start.deepEquivalent());
+ start = positionBeforeNode(blockPlaceholder.get());
end = start;
}
@@ -388,7 +395,7 @@ PassRefPtr<HTMLElement> InsertListCommand::listifyParagraph(const VisiblePositio
}
}
- moveParagraph(start, end, positionBeforeNode(placeholder.get()), true);
+ moveParagraph(start, end, positionBeforeNode(placeholder.ptr()), true);
if (listElement)
return mergeWithNeighboringLists(listElement);
diff --git a/Source/WebCore/editing/InsertListCommand.h b/Source/WebCore/editing/InsertListCommand.h
index a0d50d349..16c3370bf 100644
--- a/Source/WebCore/editing/InsertListCommand.h
+++ b/Source/WebCore/editing/InsertListCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,44 +23,42 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef InsertListCommand_h
-#define InsertListCommand_h
+#pragma once
#include "CompositeEditCommand.h"
namespace WebCore {
class HTMLElement;
+class HTMLQualifiedName;
-class InsertListCommand : public CompositeEditCommand {
+class InsertListCommand final : public CompositeEditCommand {
public:
enum Type { OrderedList, UnorderedList };
- static PassRefPtr<InsertListCommand> create(Document& document, Type listType)
+ static Ref<InsertListCommand> create(Document& document, Type listType)
{
- return adoptRef(new InsertListCommand(document, listType));
+ return adoptRef(*new InsertListCommand(document, listType));
}
- static PassRefPtr<HTMLElement> insertList(Document&, Type);
+ static RefPtr<HTMLElement> insertList(Document&, Type);
- virtual bool preservesTypingStyle() const { return true; }
+ bool preservesTypingStyle() const final { return true; }
private:
InsertListCommand(Document&, Type);
- virtual void doApply();
- virtual EditAction editingAction() const { return EditActionInsertList; }
+ void doApply() final;
+ EditAction editingAction() const final;
HTMLElement* fixOrphanedListChild(Node*);
bool selectionHasListOfType(const VisibleSelection& selection, const QualifiedName&);
- PassRefPtr<HTMLElement> mergeWithNeighboringLists(PassRefPtr<HTMLElement>);
- void doApplyForSingleParagraph(bool forceCreateList, const QualifiedName&, Range* currentSelection);
+ RefPtr<HTMLElement> mergeWithNeighboringLists(PassRefPtr<HTMLElement>);
+ void doApplyForSingleParagraph(bool forceCreateList, const HTMLQualifiedName&, Range* currentSelection);
void unlistifyParagraph(const VisiblePosition& originalStart, HTMLElement* listNode, Node* listChildNode);
- PassRefPtr<HTMLElement> listifyParagraph(const VisiblePosition& originalStart, const QualifiedName& listTag);
+ RefPtr<HTMLElement> listifyParagraph(const VisiblePosition& originalStart, const QualifiedName& listTag);
RefPtr<HTMLElement> m_listElement;
Type m_type;
};
} // namespace WebCore
-
-#endif // InsertListCommand_h
diff --git a/Source/WebCore/editing/InsertNodeBeforeCommand.cpp b/Source/WebCore/editing/InsertNodeBeforeCommand.cpp
index 7ac54e80a..dabf7c8a1 100644
--- a/Source/WebCore/editing/InsertNodeBeforeCommand.cpp
+++ b/Source/WebCore/editing/InsertNodeBeforeCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -26,16 +26,15 @@
#include "config.h"
#include "InsertNodeBeforeCommand.h"
-#include "AXObjectCache.h"
#include "Document.h"
-#include "ExceptionCodePlaceholder.h"
+#include "RenderElement.h"
+#include "Text.h"
#include "htmlediting.h"
namespace WebCore {
-InsertNodeBeforeCommand::InsertNodeBeforeCommand(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild,
- ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
- : SimpleEditCommand(refChild->document())
+InsertNodeBeforeCommand::InsertNodeBeforeCommand(RefPtr<Node>&& insertChild, RefPtr<Node>&& refChild, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction)
+ : SimpleEditCommand(refChild->document(), editingAction)
, m_insertChild(insertChild)
, m_refChild(refChild)
, m_shouldAssumeContentIsAlwaysEditable(shouldAssumeContentIsAlwaysEditable)
@@ -51,26 +50,19 @@ InsertNodeBeforeCommand::InsertNodeBeforeCommand(PassRefPtr<Node> insertChild, P
void InsertNodeBeforeCommand::doApply()
{
ContainerNode* parent = m_refChild->parentNode();
- if (!parent || (m_shouldAssumeContentIsAlwaysEditable == DoNotAssumeContentIsAlwaysEditable && !parent->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable)))
+ if (!parent || (m_shouldAssumeContentIsAlwaysEditable == DoNotAssumeContentIsAlwaysEditable && !isEditableNode(*parent)))
return;
- ASSERT(parent->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable));
+ ASSERT(isEditableNode(*parent));
- parent->insertBefore(m_insertChild.get(), m_refChild.get(), IGNORE_EXCEPTION);
-
- if (AXObjectCache* cache = document().existingAXObjectCache())
- cache->nodeTextChangeNotification(m_insertChild.get(), AXObjectCache::AXTextInserted, 0, m_insertChild->nodeValue());
+ parent->insertBefore(*m_insertChild, m_refChild.get());
}
void InsertNodeBeforeCommand::doUnapply()
{
- if (!m_insertChild->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable))
+ if (!isEditableNode(*m_insertChild))
return;
- // Need to notify this before actually deleting the text
- if (AXObjectCache* cache = document().existingAXObjectCache())
- cache->nodeTextChangeNotification(m_insertChild.get(), AXObjectCache::AXTextDeleted, 0, m_insertChild->nodeValue());
-
- m_insertChild->remove(IGNORE_EXCEPTION);
+ m_insertChild->remove();
}
#ifndef NDEBUG
diff --git a/Source/WebCore/editing/InsertNodeBeforeCommand.h b/Source/WebCore/editing/InsertNodeBeforeCommand.h
index 1ad6f762b..48eab3d09 100644
--- a/Source/WebCore/editing/InsertNodeBeforeCommand.h
+++ b/Source/WebCore/editing/InsertNodeBeforeCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef InsertNodeBeforeCommand_h
-#define InsertNodeBeforeCommand_h
+#pragma once
#include "EditCommand.h"
@@ -32,27 +31,26 @@ namespace WebCore {
class InsertNodeBeforeCommand : public SimpleEditCommand {
public:
- static PassRefPtr<InsertNodeBeforeCommand> create(PassRefPtr<Node> childToInsert, PassRefPtr<Node> childToInsertBefore,
- ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
+ static Ref<InsertNodeBeforeCommand> create(RefPtr<Node>&& childToInsert, RefPtr<Node>&& childToInsertBefore,
+ ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction = EditActionInsert)
{
- return adoptRef(new InsertNodeBeforeCommand(childToInsert, childToInsertBefore, shouldAssumeContentIsAlwaysEditable));
+ return adoptRef(*new InsertNodeBeforeCommand(WTFMove(childToInsert), WTFMove(childToInsertBefore), shouldAssumeContentIsAlwaysEditable, editingAction));
}
+protected:
+ InsertNodeBeforeCommand(RefPtr<Node>&& childToInsert, RefPtr<Node>&& childToInsertBefore, ShouldAssumeContentIsAlwaysEditable, EditAction);
+
private:
- InsertNodeBeforeCommand(PassRefPtr<Node> childToInsert, PassRefPtr<Node> childToInsertBefore, ShouldAssumeContentIsAlwaysEditable);
+ void doApply() override;
+ void doUnapply() override;
- virtual void doApply() override;
- virtual void doUnapply() override;
-
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
-
+
RefPtr<Node> m_insertChild;
RefPtr<Node> m_refChild;
ShouldAssumeContentIsAlwaysEditable m_shouldAssumeContentIsAlwaysEditable;
};
} // namespace WebCore
-
-#endif // InsertNodeBeforeCommand_h
diff --git a/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp b/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp
index 2e32b69b4..4d1916009 100644
--- a/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp
+++ b/Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005, 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -28,7 +28,7 @@
#include "Document.h"
#include "EditingStyle.h"
-#include "HTMLElement.h"
+#include "HTMLBRElement.h"
#include "HTMLFormElement.h"
#include "HTMLNames.h"
#include "InsertLineBreakCommand.h"
@@ -59,8 +59,8 @@ static Element* highestVisuallyEquivalentDivBelowRoot(Element* startBlock)
return curBlock;
}
-InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand(Document& document, bool mustUseDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea)
- : CompositeEditCommand(document)
+InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand(Document& document, bool mustUseDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea, EditAction editingAction)
+ : CompositeEditCommand(document, editingAction)
, m_mustUseDefaultParagraphElement(mustUseDefaultParagraphElement)
, m_pasteBlockqutoeIntoUnquotedArea(pasteBlockqutoeIntoUnquotedArea)
{
@@ -131,24 +131,24 @@ void InsertParagraphSeparatorCommand::getAncestorsInsideBlock(const Node* insert
}
}
-PassRefPtr<Element> InsertParagraphSeparatorCommand::cloneHierarchyUnderNewBlock(const Vector<RefPtr<Element>>& ancestors, PassRefPtr<Element> blockToInsert)
+RefPtr<Element> InsertParagraphSeparatorCommand::cloneHierarchyUnderNewBlock(const Vector<RefPtr<Element>>& ancestors, PassRefPtr<Element> blockToInsert)
{
// Make clones of ancestors in between the start node and the start block.
RefPtr<Element> parent = blockToInsert;
for (size_t i = ancestors.size(); i != 0; --i) {
- RefPtr<Element> child = ancestors[i - 1]->cloneElementWithoutChildren();
+ auto child = ancestors[i - 1]->cloneElementWithoutChildren(document());
// It should always be okay to remove id from the cloned elements, since the originals are not deleted.
child->removeAttribute(idAttr);
- appendNode(child, parent);
- parent = child.release();
+ appendNode(child.ptr(), parent);
+ parent = WTFMove(child);
}
- return parent.release();
+ return parent;
}
void InsertParagraphSeparatorCommand::doApply()
{
- if (!endingSelection().isNonOrphanedCaretOrRange())
+ if (endingSelection().isNoneOrOrphaned())
return;
Position insertionPosition = endingSelection().start();
@@ -169,7 +169,7 @@ void InsertParagraphSeparatorCommand::doApply()
if (!startBlock
|| !startBlock->nonShadowBoundaryParentNode()
|| isTableCell(startBlock.get())
- || isHTMLFormElement(startBlock.get())
+ || is<HTMLFormElement>(*startBlock)
// FIXME: If the node is hidden, we don't have a canonical position so we will do the wrong thing for tables and <hr>. https://bugs.webkit.org/show_bug.cgi?id=40342
|| (!canonicalPos.isNull() && canonicalPos.deprecatedNode()->renderer() && canonicalPos.deprecatedNode()->renderer()->isTable())
|| (!canonicalPos.isNull() && canonicalPos.deprecatedNode()->hasTagName(hrTag))) {
@@ -185,6 +185,9 @@ void InsertParagraphSeparatorCommand::doApply()
// Adjust the insertion position after the delete
insertionPosition = positionAvoidingSpecialElementBoundary(insertionPosition);
VisiblePosition visiblePos(insertionPosition, affinity);
+ if (visiblePos.isNull())
+ return;
+
calculateStyleBeforeInsertion(insertionPosition);
//---------------------------------------------------------------------
@@ -207,7 +210,7 @@ void InsertParagraphSeparatorCommand::doApply()
} else if (shouldUseDefaultParagraphElement(startBlock.get()))
blockToInsert = createDefaultParagraphElement(document());
else
- blockToInsert = startBlock->cloneElementWithoutChildren();
+ blockToInsert = startBlock->cloneElementWithoutChildren(document());
//---------------------------------------------------------------------
// Handle case when position is in the last visible position in its block,
@@ -227,7 +230,7 @@ void InsertParagraphSeparatorCommand::doApply()
// into an unquoted area. We then don't want the newline within the blockquote or else it will also be quoted.
if (m_pasteBlockqutoeIntoUnquotedArea) {
if (Node* highestBlockquote = highestEnclosingNodeOfType(canonicalPos, &isMailBlockquote))
- startBlock = toElement(highestBlockquote);
+ startBlock = downcast<Element>(highestBlockquote);
}
// Most of the time we want to stay at the nesting level of the startBlock (e.g., when nesting within lists). However,
@@ -265,9 +268,8 @@ void InsertParagraphSeparatorCommand::doApply()
// startBlock should always have children, otherwise isLastInBlock would be true and it's handled above.
ASSERT(startBlock->firstChild());
refNode = startBlock->firstChild();
- }
- else if (insertionPosition.deprecatedNode() == startBlock && nestNewBlock) {
- refNode = startBlock->childNode(insertionPosition.deprecatedEditingOffset());
+ } else if (insertionPosition.containerNode() == startBlock && nestNewBlock) {
+ refNode = startBlock->traverseToChildAt(insertionPosition.computeOffsetInContainerNode());
ASSERT(refNode); // must be true or we'd be in the end of block case
} else
refNode = insertionPosition.deprecatedNode();
@@ -297,7 +299,7 @@ void InsertParagraphSeparatorCommand::doApply()
// it if visiblePos is at the start of a paragraph so that the
// content will move down a line.
if (isStartOfParagraph(visiblePos)) {
- RefPtr<Element> br = createBreakElement(document());
+ RefPtr<Element> br = HTMLBRElement::create(document());
insertNodeAt(br.get(), insertionPosition);
insertionPosition = positionInParentAfterNode(br.get());
// If the insertion point is a break element, there is nothing else
@@ -319,7 +321,7 @@ void InsertParagraphSeparatorCommand::doApply()
// If the returned position lies either at the end or at the start of an element that is ignored by editing
// we should move to its upstream or downstream position.
- if (editingIgnoresContent(insertionPosition.deprecatedNode())) {
+ if (editingIgnoresContent(*insertionPosition.deprecatedNode())) {
if (insertionPosition.atLastEditingPositionForNode())
insertionPosition = insertionPosition.downstream();
else if (insertionPosition.atFirstEditingPositionForNode())
@@ -331,16 +333,16 @@ void InsertParagraphSeparatorCommand::doApply()
Position leadingWhitespace = insertionPosition.leadingWhitespacePosition(VP_DEFAULT_AFFINITY);
// FIXME: leadingWhitespacePosition is returning the position before preserved newlines for positions
// after the preserved newline, causing the newline to be turned into a nbsp.
- if (leadingWhitespace.isNotNull() && leadingWhitespace.deprecatedNode()->isTextNode()) {
- Text* textNode = toText(leadingWhitespace.deprecatedNode());
- ASSERT(!textNode->renderer() || textNode->renderer()->style().collapseWhiteSpace());
- replaceTextInNodePreservingMarkers(textNode, leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
+ if (is<Text>(leadingWhitespace.deprecatedNode())) {
+ Text& textNode = downcast<Text>(*leadingWhitespace.deprecatedNode());
+ ASSERT(!textNode.renderer() || textNode.renderer()->style().collapseWhiteSpace());
+ replaceTextInNodePreservingMarkers(&textNode, leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
}
// Split at pos if in the middle of a text node.
Position positionAfterSplit;
- if (insertionPosition.anchorType() == Position::PositionIsOffsetInAnchor && insertionPosition.containerNode()->isTextNode()) {
- RefPtr<Text> textNode = toText(insertionPosition.containerNode());
+ if (insertionPosition.anchorType() == Position::PositionIsOffsetInAnchor && is<Text>(*insertionPosition.containerNode())) {
+ RefPtr<Text> textNode = downcast<Text>(insertionPosition.containerNode());
bool atEnd = static_cast<unsigned>(insertionPosition.offsetInContainerNode()) >= textNode->length();
if (insertionPosition.deprecatedEditingOffset() > 0 && !atEnd) {
splitTextNode(textNode, insertionPosition.offsetInContainerNode());
@@ -368,7 +370,7 @@ void InsertParagraphSeparatorCommand::doApply()
// created. All of the nodes, starting at visiblePos, are about to be added to the new paragraph
// element. If the first node to be inserted won't be one that will hold an empty line open, add a br.
if (isEndOfParagraph(visiblePos) && !lineBreakExistsAtVisiblePosition(visiblePos))
- appendNode(createBreakElement(document()).get(), blockToInsert.get());
+ appendNode(HTMLBRElement::create(document()), blockToInsert.get());
// Move the start node and the siblings of the start node.
if (VisiblePosition(insertionPosition) != VisiblePosition(positionBeforeNode(blockToInsert.get()))) {
@@ -377,8 +379,8 @@ void InsertParagraphSeparatorCommand::doApply()
n = insertionPosition.computeNodeAfterPosition();
else {
Node* splitTo = insertionPosition.containerNode();
- if (splitTo->isTextNode() && insertionPosition.offsetInContainerNode() >= caretMaxOffset(splitTo))
- splitTo = NodeTraversal::next(splitTo, startBlock.get());
+ if (is<Text>(*splitTo) && insertionPosition.offsetInContainerNode() >= caretMaxOffset(*splitTo))
+ splitTo = NodeTraversal::next(*splitTo, startBlock.get());
ASSERT(splitTo);
splitTreeToNode(splitTo, startBlock.get());
@@ -399,8 +401,8 @@ void InsertParagraphSeparatorCommand::doApply()
// Clear out all whitespace and insert one non-breaking space
ASSERT(!positionAfterSplit.containerNode()->renderer() || positionAfterSplit.containerNode()->renderer()->style().collapseWhiteSpace());
deleteInsignificantTextDownstream(positionAfterSplit);
- if (positionAfterSplit.deprecatedNode()->isTextNode())
- insertTextIntoNode(toText(positionAfterSplit.containerNode()), 0, nonBreakingSpaceString());
+ if (is<Text>(*positionAfterSplit.deprecatedNode()))
+ insertTextIntoNode(downcast<Text>(positionAfterSplit.containerNode()), 0, nonBreakingSpaceString());
}
}
diff --git a/Source/WebCore/editing/InsertParagraphSeparatorCommand.h b/Source/WebCore/editing/InsertParagraphSeparatorCommand.h
index 815176d18..5efdd530d 100644
--- a/Source/WebCore/editing/InsertParagraphSeparatorCommand.h
+++ b/Source/WebCore/editing/InsertParagraphSeparatorCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef InsertParagraphSeparatorCommand_h
-#define InsertParagraphSeparatorCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -34,24 +33,24 @@ class EditingStyle;
class InsertParagraphSeparatorCommand : public CompositeEditCommand {
public:
- static PassRefPtr<InsertParagraphSeparatorCommand> create(Document& document, bool useDefaultParagraphElement = false, bool pasteBlockqutoeIntoUnquotedArea = false)
+ static Ref<InsertParagraphSeparatorCommand> create(Document& document, bool useDefaultParagraphElement = false, bool pasteBlockqutoeIntoUnquotedArea = false, EditAction editingAction = EditActionInsert)
{
- return adoptRef(new InsertParagraphSeparatorCommand(document, useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea));
+ return adoptRef(*new InsertParagraphSeparatorCommand(document, useDefaultParagraphElement, pasteBlockqutoeIntoUnquotedArea, editingAction));
}
private:
- InsertParagraphSeparatorCommand(Document&, bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea);
+ InsertParagraphSeparatorCommand(Document&, bool useDefaultParagraphElement, bool pasteBlockqutoeIntoUnquotedArea, EditAction);
- virtual void doApply();
+ void doApply() override;
void calculateStyleBeforeInsertion(const Position&);
void applyStyleAfterInsertion(Node* originalEnclosingBlock);
void getAncestorsInsideBlock(const Node* insertionNode, Element* outerBlock, Vector<RefPtr<Element>>& ancestors);
- PassRefPtr<Element> cloneHierarchyUnderNewBlock(const Vector<RefPtr<Element>>& ancestors, PassRefPtr<Element> blockToInsert);
+ RefPtr<Element> cloneHierarchyUnderNewBlock(const Vector<RefPtr<Element>>& ancestors, PassRefPtr<Element> blockToInsert);
bool shouldUseDefaultParagraphElement(Node*) const;
- virtual bool preservesTypingStyle() const;
+ bool preservesTypingStyle() const override;
RefPtr<EditingStyle> m_style;
@@ -59,6 +58,4 @@ private:
bool m_pasteBlockqutoeIntoUnquotedArea;
};
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/InsertTextCommand.cpp b/Source/WebCore/editing/InsertTextCommand.cpp
index 6e645d88d..36c9f3892 100644
--- a/Source/WebCore/editing/InsertTextCommand.cpp
+++ b/Source/WebCore/editing/InsertTextCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -27,9 +27,9 @@
#include "InsertTextCommand.h"
#include "Document.h"
-#include "Element.h"
#include "Editor.h"
#include "Frame.h"
+#include "HTMLElement.h"
#include "HTMLInterchange.h"
#include "Text.h"
#include "VisibleUnits.h"
@@ -37,16 +37,16 @@
namespace WebCore {
-InsertTextCommand::InsertTextCommand(Document& document, const String& text, bool selectInsertedText, RebalanceType rebalanceType)
- : CompositeEditCommand(document)
+InsertTextCommand::InsertTextCommand(Document& document, const String& text, bool selectInsertedText, RebalanceType rebalanceType, EditAction editingAction)
+ : CompositeEditCommand(document, editingAction)
, m_text(text)
, m_selectInsertedText(selectInsertedText)
, m_rebalanceType(rebalanceType)
{
}
-InsertTextCommand::InsertTextCommand(Document& document, const String& text, PassRefPtr<TextInsertionMarkerSupplier> markerSupplier)
- : CompositeEditCommand(document)
+InsertTextCommand::InsertTextCommand(Document& document, const String& text, PassRefPtr<TextInsertionMarkerSupplier> markerSupplier, EditAction editingAction)
+ : CompositeEditCommand(document, editingAction)
, m_text(text)
, m_selectInsertedText(false)
, m_rebalanceType(RebalanceLeadingAndTrailingWhitespaces)
@@ -58,7 +58,7 @@ Position InsertTextCommand::positionInsideTextNode(const Position& p)
{
Position pos = p;
if (isTabSpanTextNode(pos.anchorNode())) {
- RefPtr<Node> textNode = document().createEditingTextNode("");
+ RefPtr<Node> textNode = document().createEditingTextNode(emptyString());
insertNodeAtTabSpanPosition(textNode.get(), pos);
return firstPositionInNode(textNode.get());
}
@@ -66,7 +66,7 @@ Position InsertTextCommand::positionInsideTextNode(const Position& p)
// Prepare for text input by looking at the specified position.
// It may be necessary to insert a text node to receive characters.
if (!pos.containerNode()->isTextNode()) {
- RefPtr<Node> textNode = document().createEditingTextNode("");
+ RefPtr<Node> textNode = document().createEditingTextNode(emptyString());
insertNodeAt(textNode.get(), pos);
return firstPositionInNode(textNode.get());
}
@@ -120,7 +120,7 @@ bool InsertTextCommand::performOverwrite(const String& text, bool selectInserted
replaceTextInNode(textNode, start.offsetInContainerNode(), count, text);
- Position endPosition = Position(textNode.release(), start.offsetInContainerNode() + text.length());
+ Position endPosition = Position(textNode.get(), start.offsetInContainerNode() + text.length());
setEndingSelectionWithoutValidation(start, endPosition);
if (!selectInsertedText)
setEndingSelection(VisibleSelection(endingSelection().visibleEnd(), endingSelection().isDirectional()));
@@ -132,7 +132,7 @@ void InsertTextCommand::doApply()
{
ASSERT(m_text.find('\n') == notFound);
- if (!endingSelection().isNonOrphanedCaretOrRange())
+ if (endingSelection().isNoneOrOrphaned())
return;
// Delete the current selection.
@@ -176,7 +176,7 @@ void InsertTextCommand::doApply()
// and so deleteInsignificantText could remove it. Save the position before the node in case that happens.
Position positionBeforeStartNode(positionInParentBeforeNode(startPosition.containerNode()));
deleteInsignificantText(startPosition.upstream(), startPosition.downstream());
- if (!startPosition.anchorNode()->inDocument())
+ if (!startPosition.anchorNode()->isConnected())
startPosition = positionBeforeStartNode;
if (!startPosition.isCandidate())
startPosition = startPosition.downstream();
@@ -202,7 +202,7 @@ void InsertTextCommand::doApply()
const unsigned offset = startPosition.offsetInContainerNode();
insertTextIntoNode(textNode, offset, m_text);
- endPosition = Position(textNode, offset + m_text.length());
+ endPosition = Position(textNode.get(), offset + m_text.length());
if (m_markerSupplier)
m_markerSupplier->addMarkersToTextNode(textNode.get(), offset, m_text);
@@ -235,27 +235,29 @@ void InsertTextCommand::doApply()
Position InsertTextCommand::insertTab(const Position& pos)
{
Position insertPos = VisiblePosition(pos, DOWNSTREAM).deepEquivalent();
+ if (insertPos.isNull())
+ return pos;
Node* node = insertPos.containerNode();
unsigned int offset = node->isTextNode() ? insertPos.offsetInContainerNode() : 0;
// keep tabs coalesced in tab span
if (isTabSpanTextNode(node)) {
- RefPtr<Text> textNode = toText(node);
+ RefPtr<Text> textNode = downcast<Text>(node);
insertTextIntoNode(textNode, offset, "\t");
- return Position(textNode.release(), offset + 1);
+ return Position(textNode.get(), offset + 1);
}
// create new tab span
- RefPtr<Element> spanNode = createTabSpanElement(document());
+ auto spanNode = createTabSpanElement(document());
// place it
- if (!node->isTextNode()) {
- insertNodeAt(spanNode.get(), insertPos);
- } else {
- RefPtr<Text> textNode = toText(node);
+ if (!is<Text>(*node))
+ insertNodeAt(spanNode.ptr(), insertPos);
+ else {
+ RefPtr<Text> textNode = downcast<Text>(node);
if (offset >= textNode->length())
- insertNodeAfter(spanNode, textNode.release());
+ insertNodeAfter(spanNode.copyRef(), WTFMove(textNode));
else {
// split node to make room for the span
// NOTE: splitTextNode uses textNode for the
@@ -263,12 +265,12 @@ Position InsertTextCommand::insertTab(const Position& pos)
// insert the span before it.
if (offset > 0)
splitTextNode(textNode, offset);
- insertNodeBefore(spanNode, textNode.release());
+ insertNodeBefore(spanNode.copyRef(), WTFMove(textNode));
}
}
// return the position following the new tab
- return lastPositionInNode(spanNode.get());
+ return lastPositionInNode(spanNode.ptr());
}
}
diff --git a/Source/WebCore/editing/InsertTextCommand.h b/Source/WebCore/editing/InsertTextCommand.h
index 9cbff85be..bf7917bfb 100644
--- a/Source/WebCore/editing/InsertTextCommand.h
+++ b/Source/WebCore/editing/InsertTextCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef InsertTextCommand_h
-#define InsertTextCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -48,29 +47,28 @@ public:
RebalanceAllWhitespaces
};
- static PassRefPtr<InsertTextCommand> create(Document& document, const String& text, bool selectInsertedText = false,
- RebalanceType rebalanceType = RebalanceLeadingAndTrailingWhitespaces)
+ static Ref<InsertTextCommand> create(Document& document, const String& text, bool selectInsertedText = false,
+ RebalanceType rebalanceType = RebalanceLeadingAndTrailingWhitespaces, EditAction editingAction = EditActionInsert)
{
- return adoptRef(new InsertTextCommand(document, text, selectInsertedText, rebalanceType));
+ return adoptRef(*new InsertTextCommand(document, text, selectInsertedText, rebalanceType, editingAction));
}
- static PassRefPtr<InsertTextCommand> createWithMarkerSupplier(Document& document, const String& text, PassRefPtr<TextInsertionMarkerSupplier> markerSupplier)
+ static Ref<InsertTextCommand> createWithMarkerSupplier(Document& document, const String& text, PassRefPtr<TextInsertionMarkerSupplier> markerSupplier, EditAction editingAction = EditActionInsert)
{
- return adoptRef(new InsertTextCommand(document, text, markerSupplier));
+ return adoptRef(*new InsertTextCommand(document, text, markerSupplier, editingAction));
}
-private:
+protected:
+ InsertTextCommand(Document&, const String& text, PassRefPtr<TextInsertionMarkerSupplier>, EditAction);
+ InsertTextCommand(Document&, const String& text, bool selectInsertedText, RebalanceType, EditAction);
- InsertTextCommand(Document&, const String& text, bool selectInsertedText, RebalanceType);
- InsertTextCommand(Document&, const String& text, PassRefPtr<TextInsertionMarkerSupplier>);
+private:
void deleteCharacter();
- virtual void doApply();
+ void doApply() override;
-#if PLATFORM(IOS)
- virtual bool isInsertTextCommand() const override { return true; }
-#endif
+ bool isInsertTextCommand() const override { return true; }
Position positionInsideTextNode(const Position&);
Position insertTab(const Position&);
@@ -88,5 +86,3 @@ private:
};
} // namespace WebCore
-
-#endif // InsertTextCommand_h
diff --git a/Source/WebCore/editing/MarkupAccumulator.cpp b/Source/WebCore/editing/MarkupAccumulator.cpp
index 09b7f0717..d3bb0afac 100644
--- a/Source/WebCore/editing/MarkupAccumulator.cpp
+++ b/Source/WebCore/editing/MarkupAccumulator.cpp
@@ -40,64 +40,80 @@
#include "XLinkNames.h"
#include "XMLNSNames.h"
#include "XMLNames.h"
+#include <wtf/NeverDestroyed.h>
#include <wtf/unicode/CharacterNames.h>
namespace WebCore {
using namespace HTMLNames;
+struct EntityDescription {
+ const char* characters;
+ unsigned char length;
+ unsigned char mask;
+};
+
+static const EntityDescription entitySubstitutionList[] = {
+ { "", 0 , 0 },
+ { "&amp;", 5 , EntityAmp },
+ { "&lt;", 4, EntityLt },
+ { "&gt;", 4, EntityGt },
+ { "&quot;", 6, EntityQuot },
+ { "&nbsp;", 6, EntityNbsp },
+};
+
+enum EntitySubstitutionIndex {
+ EntitySubstitutionNullIndex = 0,
+ EntitySubstitutionAmpIndex = 1,
+ EntitySubstitutionLtIndex = 2,
+ EntitySubstitutionGtIndex = 3,
+ EntitySubstitutionQuotIndex = 4,
+ EntitySubstitutionNbspIndex = 5,
+};
+
+static const unsigned maximumEscapedentityCharacter = noBreakSpace;
+static const uint8_t entityMap[maximumEscapedentityCharacter + 1] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ EntitySubstitutionQuotIndex, // '"'.
+ 0, 0, 0,
+ EntitySubstitutionAmpIndex, // '&'.
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ EntitySubstitutionLtIndex, // '<'.
+ 0,
+ EntitySubstitutionGtIndex, // '>'.
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ EntitySubstitutionNbspIndex // noBreakSpace.
+};
+
+template<typename CharacterType>
+static inline void appendCharactersReplacingEntitiesInternal(StringBuilder& result, const String& source, unsigned offset, unsigned length, EntityMask entityMask)
+{
+ const CharacterType* text = source.characters<CharacterType>() + offset;
+
+ size_t positionAfterLastEntity = 0;
+ for (size_t i = 0; i < length; ++i) {
+ CharacterType character = text[i];
+ uint8_t substitution = character < WTF_ARRAY_LENGTH(entityMap) ? entityMap[character] : static_cast<uint8_t>(EntitySubstitutionNullIndex);
+ if (UNLIKELY(substitution != EntitySubstitutionNullIndex) && entitySubstitutionList[substitution].mask & entityMask) {
+ result.append(text + positionAfterLastEntity, i - positionAfterLastEntity);
+ result.append(entitySubstitutionList[substitution].characters, entitySubstitutionList[substitution].length);
+ positionAfterLastEntity = i + 1;
+ }
+ }
+ result.append(text + positionAfterLastEntity, length - positionAfterLastEntity);
+}
+
void MarkupAccumulator::appendCharactersReplacingEntities(StringBuilder& result, const String& source, unsigned offset, unsigned length, EntityMask entityMask)
{
- DEFINE_STATIC_LOCAL(const String, ampReference, (ASCIILiteral("&amp;")));
- DEFINE_STATIC_LOCAL(const String, ltReference, (ASCIILiteral("&lt;")));
- DEFINE_STATIC_LOCAL(const String, gtReference, (ASCIILiteral("&gt;")));
- DEFINE_STATIC_LOCAL(const String, quotReference, (ASCIILiteral("&quot;")));
- DEFINE_STATIC_LOCAL(const String, nbspReference, (ASCIILiteral("&nbsp;")));
-
- static const EntityDescription entityMaps[] = {
- { '&', ampReference, EntityAmp },
- { '<', ltReference, EntityLt },
- { '>', gtReference, EntityGt },
- { '"', quotReference, EntityQuot },
- { noBreakSpace, nbspReference, EntityNbsp },
- };
-
if (!(offset + length))
return;
ASSERT(offset + length <= source.length());
- if (source.is8Bit()) {
- const LChar* text = source.characters8() + offset;
-
- size_t positionAfterLastEntity = 0;
- for (size_t i = 0; i < length; ++i) {
- for (size_t entityIndex = 0; entityIndex < WTF_ARRAY_LENGTH(entityMaps); ++entityIndex) {
- if (text[i] == entityMaps[entityIndex].entity && entityMaps[entityIndex].mask & entityMask) {
- result.append(text + positionAfterLastEntity, i - positionAfterLastEntity);
- result.append(entityMaps[entityIndex].reference);
- positionAfterLastEntity = i + 1;
- break;
- }
- }
- }
- result.append(text + positionAfterLastEntity, length - positionAfterLastEntity);
- } else {
- const UChar* text = source.characters16() + offset;
-
- size_t positionAfterLastEntity = 0;
- for (size_t i = 0; i < length; ++i) {
- for (size_t entityIndex = 0; entityIndex < WTF_ARRAY_LENGTH(entityMaps); ++entityIndex) {
- if (text[i] == entityMaps[entityIndex].entity && entityMaps[entityIndex].mask & entityMask) {
- result.append(text + positionAfterLastEntity, i - positionAfterLastEntity);
- result.append(entityMaps[entityIndex].reference);
- positionAfterLastEntity = i + 1;
- break;
- }
- }
- }
- result.append(text + positionAfterLastEntity, length - positionAfterLastEntity);
- }
+ if (source.is8Bit())
+ appendCharactersReplacingEntitiesInternal<LChar>(result, source, offset, length, entityMask);
+ else
+ appendCharactersReplacingEntitiesInternal<UChar>(result, source, offset, length, entityMask);
}
MarkupAccumulator::MarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs resolveUrlsMethod, const Range* range, EFragmentSerialization fragmentSerialization)
@@ -113,20 +129,17 @@ MarkupAccumulator::~MarkupAccumulator()
{
}
-String MarkupAccumulator::serializeNodes(Node& targetNode, Node* nodeToSkip, EChildrenOnly childrenOnly, Vector<QualifiedName>* tagNamesToSkip)
+String MarkupAccumulator::serializeNodes(Node& targetNode, EChildrenOnly childrenOnly, Vector<QualifiedName>* tagNamesToSkip)
{
- serializeNodesWithNamespaces(targetNode, nodeToSkip, childrenOnly, 0, tagNamesToSkip);
+ serializeNodesWithNamespaces(targetNode, childrenOnly, 0, tagNamesToSkip);
return m_markup.toString();
}
-void MarkupAccumulator::serializeNodesWithNamespaces(Node& targetNode, Node* nodeToSkip, EChildrenOnly childrenOnly, const Namespaces* namespaces, Vector<QualifiedName>* tagNamesToSkip)
+void MarkupAccumulator::serializeNodesWithNamespaces(Node& targetNode, EChildrenOnly childrenOnly, const Namespaces* namespaces, Vector<QualifiedName>* tagNamesToSkip)
{
- if (&targetNode == nodeToSkip)
- return;
-
- if (tagNamesToSkip && targetNode.isElementNode()) {
- for (size_t i = 0; i < tagNamesToSkip->size(); ++i) {
- if (targetNode.hasTagName(tagNamesToSkip->at(i)))
+ if (tagNamesToSkip && is<Element>(targetNode)) {
+ for (auto& name : *tagNamesToSkip) {
+ if (downcast<Element>(targetNode).hasTagName(name))
return;
}
}
@@ -143,15 +156,12 @@ void MarkupAccumulator::serializeNodesWithNamespaces(Node& targetNode, Node* nod
if (!childrenOnly)
appendStartTag(targetNode, &namespaceHash);
- if (!(targetNode.document().isHTMLDocument() && elementCannotHaveEndTag(targetNode))) {
-#if ENABLE(TEMPLATE_ELEMENT)
- Node* current = targetNode.hasTagName(templateTag) ? toHTMLTemplateElement(targetNode).content()->firstChild() : targetNode.firstChild();
-#else
- Node* current = targetNode.firstChild();
-#endif
- for ( ; current; current = current->nextSibling())
- serializeNodesWithNamespaces(*current, nodeToSkip, IncludeNode, &namespaceHash, tagNamesToSkip);
- }
+ if (targetNode.document().isHTMLDocument() && elementCannotHaveEndTag(targetNode))
+ return;
+
+ Node* current = targetNode.hasTagName(templateTag) ? downcast<HTMLTemplateElement>(targetNode).content().firstChild() : targetNode.firstChild();
+ for ( ; current; current = current->nextSibling())
+ serializeNodesWithNamespaces(*current, IncludeNode, &namespaceHash, tagNamesToSkip);
if (!childrenOnly)
appendEndTag(targetNode);
@@ -186,16 +196,16 @@ void MarkupAccumulator::appendStartTag(const Node& node, Namespaces* namespaces)
m_nodes->append(const_cast<Node*>(&node));
}
-void MarkupAccumulator::appendEndTag(const Node& node)
+void MarkupAccumulator::appendEndTag(const Element& element)
{
- appendEndMarkup(m_markup, node);
+ appendEndMarkup(m_markup, element);
}
size_t MarkupAccumulator::totalLength(const Vector<String>& strings)
{
size_t length = 0;
- for (size_t i = 0; i < strings.size(); ++i)
- length += strings[i].length();
+ for (auto& string : strings)
+ length += string.length();
return length;
}
@@ -204,10 +214,10 @@ void MarkupAccumulator::concatenateMarkup(StringBuilder& result)
result.append(m_markup);
}
-void MarkupAccumulator::appendAttributeValue(StringBuilder& result, const String& attribute, bool documentIsHTML)
+void MarkupAccumulator::appendAttributeValue(StringBuilder& result, const String& attribute, bool isSerializingHTML)
{
appendCharactersReplacingEntities(result, attribute, 0, attribute.length(),
- documentIsHTML ? EntityMaskInHTMLAttributeValue : EntityMaskInAttributeValue);
+ isSerializingHTML ? EntityMaskInHTMLAttributeValue : EntityMaskInAttributeValue);
}
void MarkupAccumulator::appendCustomAttributes(StringBuilder&, const Element&, Namespaces*)
@@ -240,24 +250,6 @@ void MarkupAccumulator::appendQuotedURLAttributeValue(StringBuilder& result, con
result.append(quoteChar);
}
-void MarkupAccumulator::appendNodeValue(StringBuilder& result, const Node& node, const Range* range, EntityMask entityMask)
-{
- const String str = node.nodeValue();
- unsigned length = str.length();
- unsigned start = 0;
-
- if (range) {
- if (&node == range->endContainer())
- length = range->endOffset();
- if (&node == range->startContainer()) {
- start = range->startOffset();
- length -= start;
- }
- }
-
- appendCharactersReplacingEntities(result, str, start, length, entityMask);
-}
-
bool MarkupAccumulator::shouldAddNamespaceElement(const Element& element)
{
// Don't add namespace attribute if it is already defined for this elem.
@@ -265,8 +257,8 @@ bool MarkupAccumulator::shouldAddNamespaceElement(const Element& element)
if (prefix.isEmpty())
return !element.hasAttribute(xmlnsAtom);
- DEFINE_STATIC_LOCAL(String, xmlnsWithColon, (ASCIILiteral("xmlns:")));
- return !element.hasAttribute(xmlnsWithColon + prefix);
+ static NeverDestroyed<String> xmlnsWithColon(ASCIILiteral("xmlns:"));
+ return !element.hasAttribute(xmlnsWithColon.get() + prefix);
}
bool MarkupAccumulator::shouldAddNamespaceAttribute(const Attribute& attribute, Namespaces& namespaces)
@@ -333,7 +325,7 @@ EntityMask MarkupAccumulator::entityMaskForText(const Text& text) const
if (!text.document().isHTMLDocument())
return EntityMaskInPCDATA;
- const QualifiedName* parentName = 0;
+ const QualifiedName* parentName = nullptr;
if (text.parentElement())
parentName = &text.parentElement()->tagQName();
@@ -344,7 +336,20 @@ EntityMask MarkupAccumulator::entityMaskForText(const Text& text) const
void MarkupAccumulator::appendText(StringBuilder& result, const Text& text)
{
- appendNodeValue(result, text, m_range, entityMaskForText(text));
+ const String& textData = text.data();
+ unsigned start = 0;
+ unsigned length = textData.length();
+
+ if (m_range) {
+ if (&text == &m_range->endContainer())
+ length = m_range->endOffset();
+ if (&text == &m_range->startContainer()) {
+ start = m_range->startOffset();
+ length -= start;
+ }
+ }
+
+ appendCharactersReplacingEntities(result, textData, start, length, entityMaskForText(text));
}
static void appendComment(StringBuilder& result, const String& comment)
@@ -400,12 +405,6 @@ void MarkupAccumulator::appendDocumentType(StringBuilder& result, const Document
result.append(documentType.systemId());
result.append('"');
}
- if (!documentType.internalSubset().isEmpty()) {
- result.append(' ');
- result.append('[');
- result.append(documentType.internalSubset());
- result.append(']');
- }
result.append('>');
}
@@ -478,7 +477,7 @@ void MarkupAccumulator::generateUniquePrefix(QualifiedName& prefixedName, const
StringBuilder builder;
do {
builder.clear();
- builder.append("NS");
+ builder.appendLiteral("NS");
builder.appendNumber(++m_prefixLevel);
const AtomicString& name = builder.toAtomicString();
if (!namespaces.get(name.impl())) {
@@ -490,24 +489,29 @@ void MarkupAccumulator::generateUniquePrefix(QualifiedName& prefixedName, const
void MarkupAccumulator::appendAttribute(StringBuilder& result, const Element& element, const Attribute& attribute, Namespaces* namespaces)
{
- bool documentIsHTML = element.document().isHTMLDocument();
+ bool isSerializingHTML = element.document().isHTMLDocument() && !inXMLFragmentSerialization();
result.append(' ');
QualifiedName prefixedName = attribute.name();
- if (documentIsHTML && !attributeIsInSerializedNamespace(attribute))
+ if (isSerializingHTML && !attributeIsInSerializedNamespace(attribute))
result.append(attribute.name().localName());
else {
if (!attribute.namespaceURI().isEmpty()) {
- AtomicStringImpl* foundNS = namespaces && attribute.prefix().impl() ? namespaces->get(attribute.prefix().impl()) : 0;
- bool prefixIsAlreadyMappedToOtherNS = foundNS && foundNS != attribute.namespaceURI().impl();
- if (attribute.prefix().isEmpty() || !foundNS || prefixIsAlreadyMappedToOtherNS) {
- if (AtomicStringImpl* prefix = namespaces ? namespaces->get(attribute.namespaceURI().impl()) : 0)
- prefixedName.setPrefix(AtomicString(prefix));
- else {
- bool shouldBeDeclaredUsingAppendNamespace = !attribute.prefix().isEmpty() && !foundNS;
- if (!shouldBeDeclaredUsingAppendNamespace && attribute.localName() != xmlnsAtom && namespaces)
- generateUniquePrefix(prefixedName, *namespaces);
+ if (attribute.namespaceURI() == XMLNames::xmlNamespaceURI) {
+ // Always use xml as prefix if the namespace is the XML namespace.
+ prefixedName.setPrefix(xmlAtom);
+ } else {
+ AtomicStringImpl* foundNS = namespaces && attribute.prefix().impl() ? namespaces->get(attribute.prefix().impl()) : 0;
+ bool prefixIsAlreadyMappedToOtherNS = foundNS && foundNS != attribute.namespaceURI().impl();
+ if (attribute.prefix().isEmpty() || !foundNS || prefixIsAlreadyMappedToOtherNS) {
+ if (AtomicStringImpl* prefix = namespaces ? namespaces->get(attribute.namespaceURI().impl()) : 0)
+ prefixedName.setPrefix(AtomicString(prefix));
+ else {
+ bool shouldBeDeclaredUsingAppendNamespace = !attribute.prefix().isEmpty() && !foundNS;
+ if (!shouldBeDeclaredUsingAppendNamespace && attribute.localName() != xmlnsAtom && namespaces)
+ generateUniquePrefix(prefixedName, *namespaces);
+ }
}
}
}
@@ -520,11 +524,11 @@ void MarkupAccumulator::appendAttribute(StringBuilder& result, const Element& el
appendQuotedURLAttributeValue(result, element, attribute);
else {
result.append('"');
- appendAttributeValue(result, attribute.value(), documentIsHTML);
+ appendAttributeValue(result, attribute.value(), isSerializingHTML);
result.append('"');
}
- if ((inXMLFragmentSerialization() || !documentIsHTML) && namespaces && shouldAddNamespaceAttribute(attribute, *namespaces))
+ if (!isSerializingHTML && namespaces && shouldAddNamespaceAttribute(attribute, *namespaces))
appendNamespace(result, prefixedName.prefix(), prefixedName.namespaceURI(), *namespaces);
}
@@ -543,33 +547,29 @@ void MarkupAccumulator::appendStartMarkup(StringBuilder& result, const Node& nod
switch (node.nodeType()) {
case Node::TEXT_NODE:
- appendText(result, toText(node));
+ appendText(result, downcast<Text>(node));
break;
case Node::COMMENT_NODE:
- appendComment(result, toComment(node).data());
+ appendComment(result, downcast<Comment>(node).data());
break;
case Node::DOCUMENT_NODE:
- appendXMLDeclaration(result, toDocument(node));
+ appendXMLDeclaration(result, downcast<Document>(node));
break;
case Node::DOCUMENT_FRAGMENT_NODE:
break;
case Node::DOCUMENT_TYPE_NODE:
- appendDocumentType(result, toDocumentType(node));
+ appendDocumentType(result, downcast<DocumentType>(node));
break;
case Node::PROCESSING_INSTRUCTION_NODE:
- appendProcessingInstruction(result, toProcessingInstruction(node).target(), toProcessingInstruction(node).data());
+ appendProcessingInstruction(result, downcast<ProcessingInstruction>(node).target(), downcast<ProcessingInstruction>(node).data());
break;
case Node::ELEMENT_NODE:
- appendElement(result, toElement(node), namespaces);
+ appendElement(result, downcast<Element>(node), namespaces);
break;
case Node::CDATA_SECTION_NODE:
- appendCDATASection(result, toCDATASection(node).data());
+ appendCDATASection(result, downcast<CDATASection>(node).data());
break;
case Node::ATTRIBUTE_NODE:
- case Node::ENTITY_NODE:
- case Node::ENTITY_REFERENCE_NODE:
- case Node::NOTATION_NODE:
- case Node::XPATH_NAMESPACE_NODE:
ASSERT_NOT_REACHED();
break;
}
@@ -580,37 +580,44 @@ void MarkupAccumulator::appendStartMarkup(StringBuilder& result, const Node& nod
// 2. Elements w/ children never self-close because they use a separate end tag.
// 3. HTML elements which do not have a "forbidden" end tag will close with a separate end tag.
// 4. Other elements self-close.
-bool MarkupAccumulator::shouldSelfClose(const Node& node)
+bool MarkupAccumulator::shouldSelfClose(const Element& element)
{
- if (!inXMLFragmentSerialization() && node.document().isHTMLDocument())
+ if (!inXMLFragmentSerialization() && element.document().isHTMLDocument())
return false;
- if (node.hasChildNodes())
+ if (element.hasChildNodes())
return false;
- if (node.isHTMLElement() && !elementCannotHaveEndTag(node))
+ if (element.isHTMLElement() && !elementCannotHaveEndTag(element))
return false;
return true;
}
bool MarkupAccumulator::elementCannotHaveEndTag(const Node& node)
{
- if (!node.isHTMLElement())
+ if (!is<HTMLElement>(node))
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
- // which elements should be serialized w/o end tags.
- return toHTMLElement(node).ieForbidsInsertHTML();
+ // From https://html.spec.whatwg.org/#serialising-html-fragments:
+ // If current node is an area, base, basefont, bgsound, br, col, embed, frame, hr, img,
+ // input, keygen, link, meta, param, source, track or wbr element, then continue on to
+ // the next child node at this point.
+ static const HTMLQualifiedName* tags[] = { &areaTag, &baseTag, &basefontTag, &bgsoundTag, &brTag, &colTag, &embedTag,
+ &frameTag, &hrTag, &imgTag, &inputTag, &keygenTag, &linkTag, &metaTag, &paramTag, &sourceTag, &trackTag, &wbrTag };
+ auto& element = downcast<HTMLElement>(node);
+ for (auto* tag : tags) {
+ if (element.hasTagName(*tag))
+ return true;
+ }
+ return false;
}
-void MarkupAccumulator::appendEndMarkup(StringBuilder& result, const Node& node)
+void MarkupAccumulator::appendEndMarkup(StringBuilder& result, const Element& element)
{
- if (!node.isElementNode() || shouldSelfClose(node) || (!node.hasChildNodes() && elementCannotHaveEndTag(node)))
+ if (shouldSelfClose(element) || (!element.hasChildNodes() && elementCannotHaveEndTag(element)))
return;
result.append('<');
result.append('/');
- result.append(toElement(node).nodeNamePreservingCase());
+ result.append(element.nodeNamePreservingCase());
result.append('>');
}
diff --git a/Source/WebCore/editing/MarkupAccumulator.h b/Source/WebCore/editing/MarkupAccumulator.h
index 842337e14..38b73c62b 100644
--- a/Source/WebCore/editing/MarkupAccumulator.h
+++ b/Source/WebCore/editing/MarkupAccumulator.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,9 +23,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MarkupAccumulator_h
-#define MarkupAccumulator_h
+#pragma once
+#include "Element.h"
#include "markup.h"
#include <wtf/HashMap.h>
#include <wtf/text/StringBuilder.h>
@@ -53,22 +53,16 @@ enum EntityMask {
EntityMaskInPCDATA = EntityAmp | EntityLt | EntityGt,
EntityMaskInHTMLPCDATA = EntityMaskInPCDATA | EntityNbsp,
EntityMaskInAttributeValue = EntityAmp | EntityLt | EntityGt | EntityQuot,
- EntityMaskInHTMLAttributeValue = EntityMaskInAttributeValue | EntityNbsp,
-};
-
-struct EntityDescription {
- UChar entity;
- const String& reference;
- EntityMask mask;
+ EntityMaskInHTMLAttributeValue = EntityAmp | EntityQuot | EntityNbsp,
};
// FIXME: Noncopyable?
class MarkupAccumulator {
public:
- MarkupAccumulator(Vector<Node*>*, EAbsoluteURLs, const Range* = 0, EFragmentSerialization = HTMLFragmentSerialization);
+ MarkupAccumulator(Vector<Node*>*, EAbsoluteURLs, const Range* = nullptr, EFragmentSerialization = HTMLFragmentSerialization);
virtual ~MarkupAccumulator();
- String serializeNodes(Node& targetNode, Node* nodeToSkip, EChildrenOnly, Vector<QualifiedName>* tagNamesToSkip = nullptr);
+ String serializeNodes(Node& targetNode, EChildrenOnly, Vector<QualifiedName>* tagNamesToSkip = nullptr);
static void appendCharactersReplacingEntities(StringBuilder&, const String&, unsigned, unsigned, EntityMask);
@@ -78,22 +72,27 @@ protected:
void concatenateMarkup(StringBuilder&);
- virtual void appendString(const String&);
- virtual void appendEndTag(const Node&);
+ void appendString(const String&);
+ void appendEndTag(const Node& node)
+ {
+ if (is<Element>(node))
+ appendEndTag(downcast<Element>(node));
+ }
+
+ virtual void appendEndTag(const Element&);
virtual void appendCustomAttributes(StringBuilder&, const Element&, Namespaces*);
virtual void appendText(StringBuilder&, const Text&);
virtual void appendElement(StringBuilder&, const Element&, Namespaces*);
- void appendStartTag(const Node&, Namespaces* = 0);
+ void appendStartTag(const Node&, Namespaces* = nullptr);
void appendOpenTag(StringBuilder&, const Element&, Namespaces*);
void appendCloseTag(StringBuilder&, const Element&);
void appendStartMarkup(StringBuilder&, const Node&, Namespaces*);
- void appendEndMarkup(StringBuilder&, const Node&);
+ void appendEndMarkup(StringBuilder&, const Element&);
- void appendAttributeValue(StringBuilder&, const String&, bool);
- void appendNodeValue(StringBuilder&, const Node&, const Range*, EntityMask);
+ void appendAttributeValue(StringBuilder&, const String&, bool isSerializingHTML);
void appendNamespace(StringBuilder&, const AtomicString& prefix, const AtomicString& namespaceURI, Namespaces&, bool allowEmptyDefaultNS = false);
void appendXMLDeclaration(StringBuilder&, const Document&);
void appendDocumentType(StringBuilder&, const DocumentType&);
@@ -103,7 +102,7 @@ protected:
bool shouldAddNamespaceElement(const Element&);
bool shouldAddNamespaceAttribute(const Attribute&, Namespaces&);
- bool shouldSelfClose(const Node&);
+ bool shouldSelfClose(const Element&);
bool elementCannotHaveEndTag(const Node&);
EntityMask entityMaskForText(const Text&) const;
@@ -113,7 +112,7 @@ protected:
private:
String resolveURLIfNeeded(const Element&, const String&) const;
void appendQuotedURLAttributeValue(StringBuilder&, const Element&, const Attribute&);
- void serializeNodesWithNamespaces(Node& targetNode, Node* nodeToSkip, EChildrenOnly, const Namespaces*, Vector<QualifiedName>* tagNamesToSkip);
+ void serializeNodesWithNamespaces(Node& targetNode, EChildrenOnly, const Namespaces*, Vector<QualifiedName>* tagNamesToSkip);
bool inXMLFragmentSerialization() const { return m_fragmentSerialization == XMLFragmentSerialization; }
void generateUniquePrefix(QualifiedName&, const Namespaces&);
@@ -123,6 +122,4 @@ private:
unsigned m_prefixLevel;
};
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/MergeIdenticalElementsCommand.cpp b/Source/WebCore/editing/MergeIdenticalElementsCommand.cpp
index 1e639eb6d..76c376f51 100644
--- a/Source/WebCore/editing/MergeIdenticalElementsCommand.cpp
+++ b/Source/WebCore/editing/MergeIdenticalElementsCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -47,15 +47,14 @@ void MergeIdenticalElementsCommand::doApply()
m_atChild = m_element2->firstChild();
- Vector<RefPtr<Node>> children;
+ Vector<Ref<Node>> children;
for (Node* child = m_element1->firstChild(); child; child = child->nextSibling())
- children.append(child);
+ children.append(*child);
- size_t size = children.size();
- for (size_t i = 0; i < size; ++i)
- m_element2->insertBefore(children[i].release(), m_atChild.get(), IGNORE_EXCEPTION);
+ for (auto& child : children)
+ m_element2->insertBefore(child, m_atChild.get());
- m_element1->remove(IGNORE_EXCEPTION);
+ m_element1->remove();
}
void MergeIdenticalElementsCommand::doUnapply()
@@ -63,25 +62,21 @@ void MergeIdenticalElementsCommand::doUnapply()
ASSERT(m_element1);
ASSERT(m_element2);
- RefPtr<Node> atChild = m_atChild.release();
+ RefPtr<Node> atChild = WTFMove(m_atChild);
- ContainerNode* parent = m_element2->parentNode();
+ auto* parent = m_element2->parentNode();
if (!parent || !parent->hasEditableStyle())
return;
- ExceptionCode ec = 0;
-
- parent->insertBefore(m_element1.get(), m_element2.get(), ec);
- if (ec)
+ if (parent->insertBefore(*m_element1, m_element2.get()).hasException())
return;
- Vector<RefPtr<Node>> children;
+ Vector<Ref<Node>> children;
for (Node* child = m_element2->firstChild(); child && child != atChild; child = child->nextSibling())
- children.append(child);
+ children.append(*child);
- size_t size = children.size();
- for (size_t i = 0; i < size; ++i)
- m_element1->appendChild(children[i].release(), ec);
+ for (auto& child : children)
+ m_element1->appendChild(child);
}
#ifndef NDEBUG
diff --git a/Source/WebCore/editing/MergeIdenticalElementsCommand.h b/Source/WebCore/editing/MergeIdenticalElementsCommand.h
index cc9ddf534..d5972a17d 100644
--- a/Source/WebCore/editing/MergeIdenticalElementsCommand.h
+++ b/Source/WebCore/editing/MergeIdenticalElementsCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MergeIdenticalElementsCommand_h
-#define MergeIdenticalElementsCommand_h
+#pragma once
#include "EditCommand.h"
@@ -32,19 +31,19 @@ namespace WebCore {
class MergeIdenticalElementsCommand : public SimpleEditCommand {
public:
- static PassRefPtr<MergeIdenticalElementsCommand> create(PassRefPtr<Element> element1, PassRefPtr<Element> element2)
+ static Ref<MergeIdenticalElementsCommand> create(PassRefPtr<Element> element1, PassRefPtr<Element> element2)
{
- return adoptRef(new MergeIdenticalElementsCommand(element1, element2));
+ return adoptRef(*new MergeIdenticalElementsCommand(element1, element2));
}
private:
MergeIdenticalElementsCommand(PassRefPtr<Element>, PassRefPtr<Element>);
- virtual void doApply() override;
- virtual void doUnapply() override;
+ void doApply() override;
+ void doUnapply() override;
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
RefPtr<Element> m_element1;
@@ -53,5 +52,3 @@ private:
};
} // namespace WebCore
-
-#endif // MergeIdenticalElementsCommand_h
diff --git a/Source/WebCore/editing/ModifySelectionListLevel.cpp b/Source/WebCore/editing/ModifySelectionListLevel.cpp
index 54ae258e4..f3400fda6 100644
--- a/Source/WebCore/editing/ModifySelectionListLevel.cpp
+++ b/Source/WebCore/editing/ModifySelectionListLevel.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -29,7 +29,8 @@
#include "Document.h"
#include "Frame.h"
#include "FrameSelection.h"
-#include "HTMLElement.h"
+#include "HTMLOListElement.h"
+#include "HTMLUListElement.h"
#include "RenderObject.h"
#include "htmlediting.h"
@@ -79,7 +80,7 @@ static bool getStartEndListChildren(const VisibleSelection& selection, Node*& st
// if the selection ends on a list item with a sublist, include the entire sublist
if (endListChild->renderer()->isListItem()) {
RenderObject* r = endListChild->renderer()->nextSibling();
- if (r && isListElement(r->node()))
+ if (r && isListHTMLElement(r->node()))
endListChild = r->node();
}
@@ -176,9 +177,9 @@ void IncreaseSelectionListLevelCommand::doApply()
return;
Node* previousItem = startListChild->renderer()->previousSibling()->node();
- if (isListElement(previousItem)) {
+ if (isListHTMLElement(previousItem)) {
// move nodes up into preceding list
- appendSiblingNodeRange(startListChild, endListChild, toElement(previousItem));
+ appendSiblingNodeRange(startListChild, endListChild, downcast<Element>(previousItem));
m_listElement = previousItem;
} else {
// create a sublist for the preceding element and move nodes there
@@ -187,18 +188,18 @@ void IncreaseSelectionListLevelCommand::doApply()
case InheritedListType:
newParent = startListChild->parentElement();
if (newParent)
- newParent = newParent->cloneElementWithoutChildren();
+ newParent = newParent->cloneElementWithoutChildren(document());
break;
case OrderedList:
- newParent = createOrderedListElement(document());
+ newParent = HTMLOListElement::create(document());
break;
case UnorderedList:
- newParent = createUnorderedListElement(document());
+ newParent = HTMLUListElement::create(document());
break;
}
insertNodeBefore(newParent, startListChild);
appendSiblingNodeRange(startListChild, endListChild, newParent.get());
- m_listElement = newParent.release();
+ m_listElement = WTFMove(newParent);
}
}
@@ -209,13 +210,13 @@ bool IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(Document*
return canIncreaseListLevel(document->frame()->selection().selection(), startListChild, endListChild);
}
-PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(Document* document, Type type)
+RefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(Document* document, Type type)
{
ASSERT(document);
ASSERT(document->frame());
- RefPtr<IncreaseSelectionListLevelCommand> command = create(*document, type);
+ auto command = create(*document, type);
command->apply();
- return command->m_listElement.release();
+ return WTFMove(command->m_listElement);
}
PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(Document* document)
@@ -243,11 +244,11 @@ static bool canDecreaseListLevel(const VisibleSelection& selection, Node*& start
{
if (!getStartEndListChildren(selection, start, end))
return false;
-
+
// there must be a destination list to move the items to
- if (!isListElement(start->parentNode()->parentNode()))
+ if (!isListHTMLElement(start->parentNode()->parentNode()))
return false;
-
+
return true;
}
diff --git a/Source/WebCore/editing/ModifySelectionListLevel.h b/Source/WebCore/editing/ModifySelectionListLevel.h
index 061f3e587..cc06cf4de 100644
--- a/Source/WebCore/editing/ModifySelectionListLevel.h
+++ b/Source/WebCore/editing/ModifySelectionListLevel.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ModifySelectionListLevel_h
-#define ModifySelectionListLevel_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -42,7 +41,7 @@ protected:
void insertSiblingNodeRangeAfter(Node* startNode, Node* endNode, Node* refNode);
private:
- virtual bool preservesTypingStyle() const;
+ bool preservesTypingStyle() const override;
};
// IncreaseSelectionListLevelCommand moves the selected list items one level deeper.
@@ -55,16 +54,16 @@ public:
private:
enum Type { InheritedListType, OrderedList, UnorderedList };
- static PassRefPtr<Node> increaseSelectionListLevel(Document*, Type);
+ static RefPtr<Node> increaseSelectionListLevel(Document*, Type);
- static PassRefPtr<IncreaseSelectionListLevelCommand> create(Document& document, Type type)
+ static Ref<IncreaseSelectionListLevelCommand> create(Document& document, Type type)
{
- return adoptRef(new IncreaseSelectionListLevelCommand(document, type));
+ return adoptRef(*new IncreaseSelectionListLevelCommand(document, type));
}
IncreaseSelectionListLevelCommand(Document&, Type);
- virtual void doApply();
+ void doApply() override;
Type m_listType;
RefPtr<Node> m_listElement;
@@ -77,16 +76,14 @@ public:
static void decreaseSelectionListLevel(Document*);
private:
- static PassRefPtr<DecreaseSelectionListLevelCommand> create(Document& document)
+ static Ref<DecreaseSelectionListLevelCommand> create(Document& document)
{
- return adoptRef(new DecreaseSelectionListLevelCommand(document));
+ return adoptRef(*new DecreaseSelectionListLevelCommand(document));
}
explicit DecreaseSelectionListLevelCommand(Document&);
- virtual void doApply();
+ void doApply() override;
};
} // namespace WebCore
-
-#endif // ModifySelectionListLevel_h
diff --git a/Source/WebCore/editing/MoveSelectionCommand.cpp b/Source/WebCore/editing/MoveSelectionCommand.cpp
index 7580b7d37..db910caf1 100644
--- a/Source/WebCore/editing/MoveSelectionCommand.cpp
+++ b/Source/WebCore/editing/MoveSelectionCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2005, 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -26,6 +26,7 @@
#include "config.h"
#include "MoveSelectionCommand.h"
+#include "DeleteSelectionCommand.h"
#include "DocumentFragment.h"
#include "ReplaceSelectionCommand.h"
@@ -60,31 +61,43 @@ void MoveSelectionCommand::doApply()
pos.moveToOffset(pos.offsetInContainerNode() + selectionStart.offsetInContainerNode());
}
- deleteSelection(m_smartDelete);
+ {
+ auto deleteSelection = DeleteSelectionCommand::create(document(), m_smartDelete, true, false, true, true, EditActionDeleteByDrag);
+ deleteSelection->setParent(this);
+ deleteSelection->apply();
+ m_commands.append(WTFMove(deleteSelection));
+ }
// If the node for the destination has been removed as a result of the deletion,
// set the destination to the ending point after the deletion.
// Fixes: <rdar://problem/3910425> REGRESSION (Mail): Crash in ReplaceSelectionCommand;
// selection is empty, leading to null deref
- if (!pos.anchorNode()->inDocument())
+ if (!pos.anchorNode()->isConnected())
pos = endingSelection().start();
cleanupAfterDeletion(pos);
setEndingSelection(VisibleSelection(pos, endingSelection().affinity(), endingSelection().isDirectional()));
- if (!pos.anchorNode()->inDocument()) {
+ setStartingSelection(endingSelection());
+ if (!pos.anchorNode()->isConnected()) {
// Document was modified out from under us.
return;
}
ReplaceSelectionCommand::CommandOptions options = ReplaceSelectionCommand::SelectReplacement | ReplaceSelectionCommand::PreventNesting;
if (m_smartInsert)
options |= ReplaceSelectionCommand::SmartReplace;
- applyCommandToComposite(ReplaceSelectionCommand::create(document(), m_fragment, options));
+
+ {
+ auto replaceSelection = ReplaceSelectionCommand::create(document(), WTFMove(m_fragment), options, EditActionInsertFromDrop);
+ replaceSelection->setParent(this);
+ replaceSelection->apply();
+ m_commands.append(WTFMove(replaceSelection));
+ }
}
EditAction MoveSelectionCommand::editingAction() const
{
- return EditActionDrag;
+ return EditActionDeleteByDrag;
}
} // namespace WebCore
diff --git a/Source/WebCore/editing/MoveSelectionCommand.h b/Source/WebCore/editing/MoveSelectionCommand.h
index 6780caa42..8b3c338b1 100644
--- a/Source/WebCore/editing/MoveSelectionCommand.h
+++ b/Source/WebCore/editing/MoveSelectionCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef MoveSelectionCommand_h
-#define MoveSelectionCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -34,16 +33,17 @@ class DocumentFragment;
class MoveSelectionCommand : public CompositeEditCommand {
public:
- static PassRefPtr<MoveSelectionCommand> create(PassRefPtr<DocumentFragment> fragment, const Position& position, bool smartInsert = false, bool smartDelete = false)
+ static Ref<MoveSelectionCommand> create(PassRefPtr<DocumentFragment> fragment, const Position& position, bool smartInsert = false, bool smartDelete = false)
{
- return adoptRef(new MoveSelectionCommand(fragment, position, smartInsert, smartDelete));
+ return adoptRef(*new MoveSelectionCommand(fragment, position, smartInsert, smartDelete));
}
private:
MoveSelectionCommand(PassRefPtr<DocumentFragment>, const Position&, bool smartInsert, bool smartDelete);
- virtual void doApply();
- virtual EditAction editingAction() const;
+ void doApply() override;
+ EditAction editingAction() const override;
+ bool shouldDispatchInputEvents() const final { return false; }
RefPtr<DocumentFragment> m_fragment;
Position m_position;
@@ -52,5 +52,3 @@ private:
};
} // namespace WebCore
-
-#endif // MoveSelectionCommand_h
diff --git a/Source/WebCore/editing/RemoveCSSPropertyCommand.cpp b/Source/WebCore/editing/RemoveCSSPropertyCommand.cpp
index b22430bdb..3770565b9 100644
--- a/Source/WebCore/editing/RemoveCSSPropertyCommand.cpp
+++ b/Source/WebCore/editing/RemoveCSSPropertyCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -27,7 +27,6 @@
#include "RemoveCSSPropertyCommand.h"
#include "CSSStyleDeclaration.h"
-#include "ExceptionCodePlaceholder.h"
#include "StyleProperties.h"
#include "StyledElement.h"
#include <wtf/Assertions.h>
@@ -55,12 +54,12 @@ void RemoveCSSPropertyCommand::doApply()
// Mutate using the CSSOM wrapper so we get the same event behavior as a script.
// Setting to null string removes the property. We don't have internal version of removeProperty.
- m_element->style()->setPropertyInternal(m_property, String(), false, IGNORE_EXCEPTION);
+ m_element->cssomStyle()->setPropertyInternal(m_property, String(), false);
}
void RemoveCSSPropertyCommand::doUnapply()
{
- m_element->style()->setPropertyInternal(m_property, m_oldValue, m_important, IGNORE_EXCEPTION);
+ m_element->cssomStyle()->setPropertyInternal(m_property, m_oldValue, m_important);
}
#ifndef NDEBUG
diff --git a/Source/WebCore/editing/RemoveCSSPropertyCommand.h b/Source/WebCore/editing/RemoveCSSPropertyCommand.h
index abdab1531..185ebf5c6 100644
--- a/Source/WebCore/editing/RemoveCSSPropertyCommand.h
+++ b/Source/WebCore/editing/RemoveCSSPropertyCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RemoveCSSPropertyCommand_h
-#define RemoveCSSPropertyCommand_h
+#pragma once
#include "EditCommand.h"
#include "CSSPropertyNames.h"
@@ -35,20 +34,20 @@ class StyledElement;
class RemoveCSSPropertyCommand : public SimpleEditCommand {
public:
- static PassRefPtr<RemoveCSSPropertyCommand> create(Document& document, PassRefPtr<StyledElement> element, CSSPropertyID property)
+ static Ref<RemoveCSSPropertyCommand> create(Document& document, PassRefPtr<StyledElement> element, CSSPropertyID property)
{
- return adoptRef(new RemoveCSSPropertyCommand(document, element, property));
+ return adoptRef(*new RemoveCSSPropertyCommand(document, element, property));
}
private:
RemoveCSSPropertyCommand(Document&, PassRefPtr<StyledElement>, CSSPropertyID);
~RemoveCSSPropertyCommand();
- virtual void doApply() override;
- virtual void doUnapply() override;
+ void doApply() override;
+ void doUnapply() override;
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
RefPtr<StyledElement> m_element;
@@ -58,5 +57,3 @@ private:
};
} // namespace WebCore
-
-#endif // RemoveCSSPropertyCommand_h
diff --git a/Source/WebCore/editing/RemoveFormatCommand.cpp b/Source/WebCore/editing/RemoveFormatCommand.cpp
index eea0ae8eb..d8b82cf1a 100644
--- a/Source/WebCore/editing/RemoveFormatCommand.cpp
+++ b/Source/WebCore/editing/RemoveFormatCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2007 Apple Inc. All rights reserved.
* Copyright (C) 2010 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -33,6 +33,7 @@
#include "FrameSelection.h"
#include "HTMLNames.h"
#include "StyleProperties.h"
+#include <wtf/NeverDestroyed.h>
namespace WebCore {
@@ -45,44 +46,44 @@ RemoveFormatCommand::RemoveFormatCommand(Document& document)
static bool isElementForRemoveFormatCommand(const Element* element)
{
- DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, elements, ());
- if (elements.isEmpty()) {
- elements.add(acronymTag);
- elements.add(bTag);
- elements.add(bdoTag);
- elements.add(bigTag);
- elements.add(citeTag);
- elements.add(codeTag);
- elements.add(dfnTag);
- elements.add(emTag);
- elements.add(fontTag);
- elements.add(iTag);
- elements.add(insTag);
- elements.add(kbdTag);
- elements.add(nobrTag);
- elements.add(qTag);
- elements.add(sTag);
- elements.add(sampTag);
- elements.add(smallTag);
- elements.add(strikeTag);
- elements.add(strongTag);
- elements.add(subTag);
- elements.add(supTag);
- elements.add(ttTag);
- elements.add(uTag);
- elements.add(varTag);
+ static NeverDestroyed<HashSet<QualifiedName>> elements;
+ if (elements.get().isEmpty()) {
+ elements.get().add(acronymTag);
+ elements.get().add(bTag);
+ elements.get().add(bdoTag);
+ elements.get().add(bigTag);
+ elements.get().add(citeTag);
+ elements.get().add(codeTag);
+ elements.get().add(dfnTag);
+ elements.get().add(emTag);
+ elements.get().add(fontTag);
+ elements.get().add(iTag);
+ elements.get().add(insTag);
+ elements.get().add(kbdTag);
+ elements.get().add(nobrTag);
+ elements.get().add(qTag);
+ elements.get().add(sTag);
+ elements.get().add(sampTag);
+ elements.get().add(smallTag);
+ elements.get().add(strikeTag);
+ elements.get().add(strongTag);
+ elements.get().add(subTag);
+ elements.get().add(supTag);
+ elements.get().add(ttTag);
+ elements.get().add(uTag);
+ elements.get().add(varTag);
}
- return elements.contains(element->tagQName());
+ return elements.get().contains(element->tagQName());
}
void RemoveFormatCommand::doApply()
{
- if (!frame().selection().selection().isNonOrphanedCaretOrRange())
+ if (endingSelection().isNoneOrOrphaned())
return;
// Get the default style for this editable root, it's the style that we'll give the
// content that we're operating on.
- Node* root = frame().selection().rootEditableElement();
+ Node* root = endingSelection().rootEditableElement();
RefPtr<EditingStyle> defaultStyle = EditingStyle::create(root);
// We want to remove everything but transparent background.
diff --git a/Source/WebCore/editing/RemoveFormatCommand.h b/Source/WebCore/editing/RemoveFormatCommand.h
index 191814554..a493b3abf 100644
--- a/Source/WebCore/editing/RemoveFormatCommand.h
+++ b/Source/WebCore/editing/RemoveFormatCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RemoveFormatCommand_h
-#define RemoveFormatCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -32,18 +31,16 @@ namespace WebCore {
class RemoveFormatCommand : public CompositeEditCommand {
public:
- static PassRefPtr<RemoveFormatCommand> create(Document& document)
+ static Ref<RemoveFormatCommand> create(Document& document)
{
- return adoptRef(new RemoveFormatCommand(document));
+ return adoptRef(*new RemoveFormatCommand(document));
}
private:
explicit RemoveFormatCommand(Document&);
- virtual void doApply();
- virtual EditAction editingAction() const { return EditActionUnspecified; }
+ void doApply() override;
+ EditAction editingAction() const override { return EditActionUnspecified; }
};
} // namespace WebCore
-
-#endif // RemoveFormatCommand_h
diff --git a/Source/WebCore/editing/RemoveNodeCommand.cpp b/Source/WebCore/editing/RemoveNodeCommand.cpp
index 67d9c56d6..916a4f8c3 100644
--- a/Source/WebCore/editing/RemoveNodeCommand.cpp
+++ b/Source/WebCore/editing/RemoveNodeCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -26,19 +26,17 @@
#include "config.h"
#include "RemoveNodeCommand.h"
-#include "ExceptionCodePlaceholder.h"
-#include "Node.h"
#include "RenderElement.h"
+#include "htmlediting.h"
#include <wtf/Assertions.h>
namespace WebCore {
-RemoveNodeCommand::RemoveNodeCommand(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
- : SimpleEditCommand(node->document())
- , m_node(node)
+RemoveNodeCommand::RemoveNodeCommand(Ref<Node>&& node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction)
+ : SimpleEditCommand(node->document(), editingAction)
+ , m_node(WTFMove(node))
, m_shouldAssumeContentIsAlwaysEditable(shouldAssumeContentIsAlwaysEditable)
{
- ASSERT(m_node);
ASSERT(m_node->parentNode());
}
@@ -46,24 +44,24 @@ void RemoveNodeCommand::doApply()
{
ContainerNode* parent = m_node->parentNode();
if (!parent || (m_shouldAssumeContentIsAlwaysEditable == DoNotAssumeContentIsAlwaysEditable
- && !parent->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable) && parent->renderer()))
+ && !isEditableNode(*parent) && parent->renderer()))
return;
- ASSERT(parent->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable) || !parent->renderer());
+ ASSERT(isEditableNode(*parent) || !parent->renderer());
m_parent = parent;
m_refChild = m_node->nextSibling();
- m_node->remove(IGNORE_EXCEPTION);
+ m_node->remove();
}
void RemoveNodeCommand::doUnapply()
{
- RefPtr<ContainerNode> parent = m_parent.release();
- RefPtr<Node> refChild = m_refChild.release();
+ RefPtr<ContainerNode> parent = WTFMove(m_parent);
+ RefPtr<Node> refChild = WTFMove(m_refChild);
if (!parent || !parent->hasEditableStyle())
return;
- parent->insertBefore(m_node.get(), refChild.get(), IGNORE_EXCEPTION);
+ parent->insertBefore(m_node, refChild.get());
}
#ifndef NDEBUG
@@ -71,7 +69,7 @@ void RemoveNodeCommand::getNodesInCommand(HashSet<Node*>& nodes)
{
addNodeAndDescendants(m_parent.get(), nodes);
addNodeAndDescendants(m_refChild.get(), nodes);
- addNodeAndDescendants(m_node.get(), nodes);
+ addNodeAndDescendants(m_node.ptr(), nodes);
}
#endif
diff --git a/Source/WebCore/editing/RemoveNodeCommand.h b/Source/WebCore/editing/RemoveNodeCommand.h
index c790c38cc..0c74072d0 100644
--- a/Source/WebCore/editing/RemoveNodeCommand.h
+++ b/Source/WebCore/editing/RemoveNodeCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RemoveNodeCommand_h
-#define RemoveNodeCommand_h
+#pragma once
#include "EditCommand.h"
@@ -32,27 +31,25 @@ namespace WebCore {
class RemoveNodeCommand : public SimpleEditCommand {
public:
- static PassRefPtr<RemoveNodeCommand> create(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
+ static Ref<RemoveNodeCommand> create(Ref<Node>&& node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction = EditActionUnspecified)
{
- return adoptRef(new RemoveNodeCommand(node, shouldAssumeContentIsAlwaysEditable));
+ return adoptRef(*new RemoveNodeCommand(WTFMove(node), shouldAssumeContentIsAlwaysEditable, editingAction));
}
private:
- explicit RemoveNodeCommand(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable);
+ RemoveNodeCommand(Ref<Node>&&, ShouldAssumeContentIsAlwaysEditable, EditAction);
- virtual void doApply() override;
- virtual void doUnapply() override;
+ void doApply() override;
+ void doUnapply() override;
#ifndef NDEBUG
void getNodesInCommand(HashSet<Node*>&) override;
#endif
- RefPtr<Node> m_node;
+ Ref<Node> m_node;
RefPtr<ContainerNode> m_parent;
RefPtr<Node> m_refChild;
ShouldAssumeContentIsAlwaysEditable m_shouldAssumeContentIsAlwaysEditable;
};
} // namespace WebCore
-
-#endif // RemoveNodeCommand_h
diff --git a/Source/WebCore/editing/RemoveNodePreservingChildrenCommand.cpp b/Source/WebCore/editing/RemoveNodePreservingChildrenCommand.cpp
index 573533007..16f0f6310 100644
--- a/Source/WebCore/editing/RemoveNodePreservingChildrenCommand.cpp
+++ b/Source/WebCore/editing/RemoveNodePreservingChildrenCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -31,8 +31,8 @@
namespace WebCore {
-RemoveNodePreservingChildrenCommand::RemoveNodePreservingChildrenCommand(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
- : CompositeEditCommand(node->document())
+RemoveNodePreservingChildrenCommand::RemoveNodePreservingChildrenCommand(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction)
+ : CompositeEditCommand(node->document(), editingAction)
, m_node(node)
, m_shouldAssumeContentIsAlwaysEditable(shouldAssumeContentIsAlwaysEditable)
{
@@ -47,9 +47,9 @@ void RemoveNodePreservingChildrenCommand::doApply()
size_t size = children.size();
for (size_t i = 0; i < size; ++i) {
- RefPtr<Node> child = children[i].release();
+ auto child = WTFMove(children[i]);
removeNode(child, m_shouldAssumeContentIsAlwaysEditable);
- insertNodeBefore(child.release(), m_node, m_shouldAssumeContentIsAlwaysEditable);
+ insertNodeBefore(WTFMove(child), m_node, m_shouldAssumeContentIsAlwaysEditable);
}
removeNode(m_node, m_shouldAssumeContentIsAlwaysEditable);
}
diff --git a/Source/WebCore/editing/RemoveNodePreservingChildrenCommand.h b/Source/WebCore/editing/RemoveNodePreservingChildrenCommand.h
index 86a3b18e1..07ea088ba 100644
--- a/Source/WebCore/editing/RemoveNodePreservingChildrenCommand.h
+++ b/Source/WebCore/editing/RemoveNodePreservingChildrenCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RemoveNodePreservingChildrenCommand_h
-#define RemoveNodePreservingChildrenCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -32,20 +31,18 @@ namespace WebCore {
class RemoveNodePreservingChildrenCommand : public CompositeEditCommand {
public:
- static PassRefPtr<RemoveNodePreservingChildrenCommand> create(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
+ static Ref<RemoveNodePreservingChildrenCommand> create(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable, EditAction editingAction)
{
- return adoptRef(new RemoveNodePreservingChildrenCommand(node, shouldAssumeContentIsAlwaysEditable));
+ return adoptRef(*new RemoveNodePreservingChildrenCommand(node, shouldAssumeContentIsAlwaysEditable, editingAction));
}
private:
- explicit RemoveNodePreservingChildrenCommand(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable);
+ explicit RemoveNodePreservingChildrenCommand(PassRefPtr<Node>, ShouldAssumeContentIsAlwaysEditable, EditAction);
- virtual void doApply();
+ void doApply() override;
RefPtr<Node> m_node;
ShouldAssumeContentIsAlwaysEditable m_shouldAssumeContentIsAlwaysEditable;
};
} // namespace WebCore
-
-#endif // RemoveNodePreservingChildrenCommand_h
diff --git a/Source/WebCore/editing/RenderedPosition.cpp b/Source/WebCore/editing/RenderedPosition.cpp
index 4d628f9db..9938218b4 100644
--- a/Source/WebCore/editing/RenderedPosition.cpp
+++ b/Source/WebCore/editing/RenderedPosition.cpp
@@ -39,7 +39,7 @@ namespace WebCore {
static inline RenderObject* rendererFromPosition(const Position& position)
{
ASSERT(position.isNotNull());
- Node* rendererNode = 0;
+ Node* rendererNode = nullptr;
switch (position.anchorType()) {
case Position::PositionIsOffsetInAnchor:
rendererNode = position.computeNodeAfterPosition();
@@ -64,9 +64,7 @@ static inline RenderObject* rendererFromPosition(const Position& position)
}
RenderedPosition::RenderedPosition(const VisiblePosition& position)
- : m_renderer(0)
- , m_inlineBox(0)
- , m_offset(0)
+ : m_offset(0)
, m_prevLeafChild(uncachedInlineBox())
, m_nextLeafChild(uncachedInlineBox())
{
@@ -80,9 +78,7 @@ RenderedPosition::RenderedPosition(const VisiblePosition& position)
}
RenderedPosition::RenderedPosition(const Position& position, EAffinity affinity)
- : m_renderer(0)
- , m_inlineBox(0)
- , m_offset(0)
+ : m_offset(0)
, m_prevLeafChild(uncachedInlineBox())
, m_nextLeafChild(uncachedInlineBox())
{
@@ -227,7 +223,7 @@ IntRect RenderedPosition::absoluteRect(LayoutUnit* extraWidthToEndOfLine) const
if (isNull())
return IntRect();
- IntRect localRect = pixelSnappedIntRect(m_renderer->localCaretRect(m_inlineBox, m_offset, extraWidthToEndOfLine));
+ IntRect localRect = snappedIntRect(m_renderer->localCaretRect(m_inlineBox, m_offset, extraWidthToEndOfLine));
return localRect == IntRect() ? IntRect() : m_renderer->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
}
diff --git a/Source/WebCore/editing/RenderedPosition.h b/Source/WebCore/editing/RenderedPosition.h
index 4f7eb2832..729c1abdb 100644
--- a/Source/WebCore/editing/RenderedPosition.h
+++ b/Source/WebCore/editing/RenderedPosition.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef RenderedPosition_h
-#define RenderedPosition_h
+#pragma once
#include "InlineBox.h"
#include "TextAffinity.h"
@@ -67,7 +66,7 @@ public:
Position positionAtLeftBoundaryOfBiDiRun() const;
Position positionAtRightBoundaryOfBiDiRun() const;
- IntRect absoluteRect(LayoutUnit* extraWidthToEndOfLine = 0) const;
+ IntRect absoluteRect(LayoutUnit* extraWidthToEndOfLine = nullptr) const;
private:
bool operator==(const RenderedPosition&) const { return false; }
@@ -80,8 +79,8 @@ private:
bool atLeftBoundaryOfBidiRun(ShouldMatchBidiLevel, unsigned char bidiLevelOfRun) const;
bool atRightBoundaryOfBidiRun(ShouldMatchBidiLevel, unsigned char bidiLevelOfRun) const;
- RenderObject* m_renderer;
- InlineBox* m_inlineBox;
+ RenderObject* m_renderer { nullptr };
+ InlineBox* m_inlineBox { nullptr };
int m_offset;
static InlineBox* uncachedInlineBox() { return reinterpret_cast<InlineBox*>(1); }
@@ -92,9 +91,7 @@ private:
};
inline RenderedPosition::RenderedPosition()
- : m_renderer(0)
- , m_inlineBox(0)
- , m_offset(0)
+ : m_offset(0)
, m_prevLeafChild(uncachedInlineBox())
, m_nextLeafChild(uncachedInlineBox())
{
@@ -111,6 +108,4 @@ inline RenderedPosition::RenderedPosition(RenderObject* renderer, InlineBox* box
bool renderObjectContainsPosition(RenderObject*, const Position&);
-};
-
-#endif // RenderedPosition_h
+} // namespace WebCore
diff --git a/Source/WebCore/editing/ReplaceNodeWithSpanCommand.cpp b/Source/WebCore/editing/ReplaceNodeWithSpanCommand.cpp
index d417ed538..e1d0223e3 100644
--- a/Source/WebCore/editing/ReplaceNodeWithSpanCommand.cpp
+++ b/Source/WebCore/editing/ReplaceNodeWithSpanCommand.cpp
@@ -31,16 +31,11 @@
#include "config.h"
#include "ReplaceNodeWithSpanCommand.h"
+#include "HTMLSpanElement.h"
#include "htmlediting.h"
-#include "HTMLElement.h"
-#include "HTMLNames.h"
-
-#include <wtf/Assertions.h>
namespace WebCore {
-using namespace HTMLNames;
-
ReplaceNodeWithSpanCommand::ReplaceNodeWithSpanCommand(PassRefPtr<HTMLElement> element)
: SimpleEditCommand(element->document())
, m_elementToReplace(element)
@@ -48,36 +43,36 @@ ReplaceNodeWithSpanCommand::ReplaceNodeWithSpanCommand(PassRefPtr<HTMLElement> e
ASSERT(m_elementToReplace);
}
-static void swapInNodePreservingAttributesAndChildren(HTMLElement* newNode, HTMLElement* nodeToReplace)
+static void swapInNodePreservingAttributesAndChildren(HTMLElement& newNode, HTMLElement& nodeToReplace)
{
- ASSERT(nodeToReplace->inDocument());
- RefPtr<ContainerNode> parentNode = nodeToReplace->parentNode();
+ ASSERT(nodeToReplace.isConnected());
+ RefPtr<ContainerNode> parentNode = nodeToReplace.parentNode();
// FIXME: Fix this to send the proper MutationRecords when MutationObservers are present.
- newNode->cloneDataFromElement(*nodeToReplace);
+ newNode.cloneDataFromElement(nodeToReplace);
NodeVector children;
- getChildNodes(*nodeToReplace, children);
- for (size_t i = 0; i < children.size(); ++i)
- newNode->appendChild(&children[i].get(), ASSERT_NO_EXCEPTION);
+ getChildNodes(nodeToReplace, children);
+ for (auto& child : children)
+ newNode.appendChild(child);
- parentNode->insertBefore(newNode, nodeToReplace, ASSERT_NO_EXCEPTION);
- parentNode->removeChild(nodeToReplace, ASSERT_NO_EXCEPTION);
+ parentNode->insertBefore(newNode, &nodeToReplace);
+ parentNode->removeChild(nodeToReplace);
}
void ReplaceNodeWithSpanCommand::doApply()
{
- if (!m_elementToReplace->inDocument())
+ if (!m_elementToReplace->isConnected())
return;
if (!m_spanElement)
- m_spanElement = createHTMLElement(m_elementToReplace->document(), spanTag);
- swapInNodePreservingAttributesAndChildren(m_spanElement.get(), m_elementToReplace.get());
+ m_spanElement = HTMLSpanElement::create(m_elementToReplace->document());
+ swapInNodePreservingAttributesAndChildren(*m_spanElement, *m_elementToReplace);
}
void ReplaceNodeWithSpanCommand::doUnapply()
{
- if (!m_spanElement->inDocument())
+ if (!m_spanElement->isConnected())
return;
- swapInNodePreservingAttributesAndChildren(m_elementToReplace.get(), m_spanElement.get());
+ swapInNodePreservingAttributesAndChildren(*m_elementToReplace, *m_spanElement);
}
#ifndef NDEBUG
diff --git a/Source/WebCore/editing/ReplaceNodeWithSpanCommand.h b/Source/WebCore/editing/ReplaceNodeWithSpanCommand.h
index faa2da45a..b564c66ec 100644
--- a/Source/WebCore/editing/ReplaceNodeWithSpanCommand.h
+++ b/Source/WebCore/editing/ReplaceNodeWithSpanCommand.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ReplaceNodeWithSpanCommand_h
-#define ReplaceNodeWithSpanCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -40,9 +39,9 @@ class HTMLElement;
// More accurately, this is ReplaceElementWithSpanPreservingChildrenAndAttributesCommand
class ReplaceNodeWithSpanCommand : public SimpleEditCommand {
public:
- static PassRefPtr<ReplaceNodeWithSpanCommand> create(PassRefPtr<HTMLElement> element)
+ static Ref<ReplaceNodeWithSpanCommand> create(PassRefPtr<HTMLElement> element)
{
- return adoptRef(new ReplaceNodeWithSpanCommand(element));
+ return adoptRef(*new ReplaceNodeWithSpanCommand(element));
}
HTMLElement* spanElement() { return m_spanElement.get(); }
@@ -50,11 +49,11 @@ public:
private:
explicit ReplaceNodeWithSpanCommand(PassRefPtr<HTMLElement>);
- virtual void doApply() override;
- virtual void doUnapply() override;
+ void doApply() override;
+ void doUnapply() override;
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
RefPtr<HTMLElement> m_elementToReplace;
@@ -62,5 +61,3 @@ private:
};
} // namespace WebCore
-
-#endif // ReplaceNodeWithSpanCommand
diff --git a/Source/WebCore/editing/ReplaceRangeWithTextCommand.cpp b/Source/WebCore/editing/ReplaceRangeWithTextCommand.cpp
new file mode 100644
index 000000000..5b34d0643
--- /dev/null
+++ b/Source/WebCore/editing/ReplaceRangeWithTextCommand.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 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 "ReplaceRangeWithTextCommand.h"
+
+#include "AlternativeTextController.h"
+#include "DataTransfer.h"
+#include "Document.h"
+#include "DocumentFragment.h"
+#include "Editor.h"
+#include "Frame.h"
+#include "ReplaceSelectionCommand.h"
+#include "SetSelectionCommand.h"
+#include "StaticRange.h"
+#include "TextIterator.h"
+#include "markup.h"
+
+namespace WebCore {
+
+ReplaceRangeWithTextCommand::ReplaceRangeWithTextCommand(RefPtr<Range> rangeToBeReplaced, const String& text)
+ : CompositeEditCommand(rangeToBeReplaced->startContainer().document(), EditActionInsertReplacement)
+ , m_rangeToBeReplaced(rangeToBeReplaced)
+ , m_text(text)
+{
+}
+
+bool ReplaceRangeWithTextCommand::willApplyCommand()
+{
+ m_textFragment = createFragmentFromText(*m_rangeToBeReplaced, m_text);
+ return CompositeEditCommand::willApplyCommand();
+}
+
+void ReplaceRangeWithTextCommand::doApply()
+{
+ VisibleSelection selection = *m_rangeToBeReplaced;
+
+ if (!m_rangeToBeReplaced)
+ return;
+
+ if (!frame().selection().shouldChangeSelection(selection))
+ return;
+
+ String previousText = plainText(m_rangeToBeReplaced.get());
+ if (!previousText.length())
+ return;
+
+ applyCommandToComposite(SetSelectionCommand::create(selection, FrameSelection::defaultSetSelectionOptions()));
+ applyCommandToComposite(ReplaceSelectionCommand::create(document(), WTFMove(m_textFragment), ReplaceSelectionCommand::MatchStyle, EditActionPaste));
+}
+
+String ReplaceRangeWithTextCommand::inputEventData() const
+{
+ if (isEditingTextAreaOrTextInput())
+ return m_text;
+
+ return CompositeEditCommand::inputEventData();
+}
+
+RefPtr<DataTransfer> ReplaceRangeWithTextCommand::inputEventDataTransfer() const
+{
+ if (!isEditingTextAreaOrTextInput())
+ return DataTransfer::createForInputEvent(m_text, createMarkup(*m_textFragment));
+
+ return CompositeEditCommand::inputEventDataTransfer();
+}
+
+Vector<RefPtr<StaticRange>> ReplaceRangeWithTextCommand::targetRanges() const
+{
+ RefPtr<StaticRange> range = StaticRange::createFromRange(*m_rangeToBeReplaced);
+ return { 1, range };
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/editing/DeleteButton.h b/Source/WebCore/editing/ReplaceRangeWithTextCommand.h
index b98ba06a7..098307f65 100644
--- a/Source/WebCore/editing/DeleteButton.h
+++ b/Source/WebCore/editing/ReplaceRangeWithTextCommand.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,40 +10,46 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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.
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef DeleteButton_h
-#define DeleteButton_h
+#pragma once
-#include "HTMLImageElement.h"
+#include "CompositeEditCommand.h"
+#include "Range.h"
namespace WebCore {
-class DeleteButton : public HTMLImageElement {
-public:
- static PassRefPtr<DeleteButton> create(Document&);
+class DocumentFragment;
-#if !PLATFORM(IOS)
- virtual bool willRespondToMouseClickEvents() override { return true; }
-#endif // !PLATFORM(IOS)
+class ReplaceRangeWithTextCommand : public CompositeEditCommand {
+public:
+ static Ref<ReplaceRangeWithTextCommand> create(RefPtr<Range> rangeToBeReplaced, const String& text)
+ {
+ return adoptRef(*new ReplaceRangeWithTextCommand(rangeToBeReplaced, text));
+ }
private:
- explicit DeleteButton(Document&);
-
- virtual void defaultEventHandler(Event*);
+ ReplaceRangeWithTextCommand(RefPtr<Range> rangeToBeReplaced, const String& text);
+ bool willApplyCommand() final;
+ void doApply() override;
+ String inputEventData() const final;
+ RefPtr<DataTransfer> inputEventDataTransfer() const final;
+ Vector<RefPtr<StaticRange>> targetRanges() const final;
+
+ RefPtr<Range> m_rangeToBeReplaced;
+ RefPtr<DocumentFragment> m_textFragment;
+ String m_text;
};
-} // namespace
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/ReplaceSelectionCommand.cpp b/Source/WebCore/editing/ReplaceSelectionCommand.cpp
index 648692ad0..d4ddea69d 100644
--- a/Source/WebCore/editing/ReplaceSelectionCommand.cpp
+++ b/Source/WebCore/editing/ReplaceSelectionCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2005-2017 Apple Inc. All rights reserved.
* Copyright (C) 2009, 2010, 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -27,25 +27,31 @@
#include "config.h"
#include "ReplaceSelectionCommand.h"
+#include "AXObjectCache.h"
#include "ApplyStyleCommand.h"
#include "BeforeTextInsertedEvent.h"
#include "BreakBlockquoteCommand.h"
#include "CSSStyleDeclaration.h"
+#include "DOMWrapperWorld.h"
+#include "DataTransfer.h"
#include "Document.h"
#include "DocumentFragment.h"
-#include "Element.h"
#include "ElementIterator.h"
#include "EventNames.h"
-#include "ExceptionCodePlaceholder.h"
#include "Frame.h"
#include "FrameSelection.h"
+#include "HTMLBRElement.h"
+#include "HTMLBaseElement.h"
#include "HTMLInputElement.h"
+#include "HTMLLIElement.h"
+#include "HTMLLinkElement.h"
+#include "HTMLMetaElement.h"
#include "HTMLNames.h"
+#include "HTMLStyleElement.h"
#include "HTMLTitleElement.h"
#include "NodeList.h"
#include "NodeRenderStyle.h"
#include "RenderInline.h"
-#include "RenderObject.h"
#include "RenderText.h"
#include "SimplifyMarkupCommand.h"
#include "SmartReplace.h"
@@ -55,6 +61,7 @@
#include "VisibleUnits.h"
#include "htmlediting.h"
#include "markup.h"
+#include <wtf/NeverDestroyed.h>
#include <wtf/StdLibExtras.h>
namespace WebCore {
@@ -63,6 +70,8 @@ using namespace HTMLNames;
enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment };
+static void removeHeadContents(ReplacementFragment&);
+
// --- ReplacementFragment helper class
class ReplacementFragment {
@@ -84,7 +93,7 @@ public:
void removeNodePreservingChildren(PassRefPtr<Node>);
private:
- PassRefPtr<StyledElement> insertFragmentForTestRendering(Node* rootEditableNode);
+ Ref<HTMLElement> insertFragmentForTestRendering(Node* rootEditableNode);
void removeUnrenderedNodes(Node*);
void restoreAndRemoveTestRenderingNodesToFragment(StyledElement*);
void removeInterchangeNodes(Node*);
@@ -99,44 +108,46 @@ private:
bool m_hasInterchangeNewlineAtEnd;
};
-static bool isInterchangeNewlineNode(const Node *node)
+static bool isInterchangeNewlineNode(const Node* node)
{
- DEFINE_STATIC_LOCAL(String, interchangeNewlineClassString, (AppleInterchangeNewline));
- return node && node->hasTagName(brTag) &&
- static_cast<const Element *>(node)->getAttribute(classAttr) == interchangeNewlineClassString;
+ static NeverDestroyed<String> interchangeNewlineClassString(AppleInterchangeNewline);
+ return is<HTMLBRElement>(node) && downcast<HTMLBRElement>(*node).attributeWithoutSynchronization(classAttr) == interchangeNewlineClassString;
}
-static bool isInterchangeConvertedSpaceSpan(const Node *node)
+static bool isInterchangeConvertedSpaceSpan(const Node* node)
{
- DEFINE_STATIC_LOCAL(String, convertedSpaceSpanClassString, (AppleConvertedSpace));
- return node->isHTMLElement() &&
- static_cast<const HTMLElement *>(node)->getAttribute(classAttr) == convertedSpaceSpanClassString;
+ static NeverDestroyed<String> convertedSpaceSpanClassString(AppleConvertedSpace);
+ return is<HTMLElement>(node) && downcast<HTMLElement>(*node).attributeWithoutSynchronization(classAttr) == convertedSpaceSpanClassString;
}
-static Position positionAvoidingPrecedingNodes(Position pos)
+static Position positionAvoidingPrecedingNodes(Position position)
{
+ ASSERT(position.isNotNull());
+
// If we're already on a break, it's probably a placeholder and we shouldn't change our position.
- if (editingIgnoresContent(pos.deprecatedNode()))
- return pos;
+ if (editingIgnoresContent(*position.deprecatedNode()))
+ return position;
// We also stop when changing block flow elements because even though the visual position is the
// 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* enclosingBlockNode = enclosingBlock(pos.containerNode());
- for (Position nextPosition = pos; nextPosition.containerNode() != enclosingBlockNode; pos = nextPosition) {
- if (lineBreakExistsAtPosition(pos))
+ auto* enclosingBlockNode = enclosingBlock(position.containerNode());
+ for (Position nextPosition = position; nextPosition.containerNode() != enclosingBlockNode; position = nextPosition) {
+ if (lineBreakExistsAtPosition(position))
break;
- if (pos.containerNode()->nonShadowBoundaryParentNode())
- nextPosition = positionInParentAfterNode(pos.containerNode());
-
- if (nextPosition == pos
- || enclosingBlock(nextPosition.containerNode()) != enclosingBlockNode
- || VisiblePosition(pos) != VisiblePosition(nextPosition))
+ if (position.containerNode()->nonShadowBoundaryParentNode())
+ nextPosition = positionInParentAfterNode(position.containerNode());
+
+ if (nextPosition == position)
+ break;
+ if (enclosingBlock(nextPosition.containerNode()) != enclosingBlockNode)
+ break;
+ if (VisiblePosition(position) != VisiblePosition(nextPosition))
break;
}
- return pos;
+ return position;
}
ReplacementFragment::ReplacementFragment(Document& document, DocumentFragment* fragment, const VisibleSelection& selection)
@@ -157,10 +168,9 @@ ReplacementFragment::ReplacementFragment(Document& document, DocumentFragment* f
Node* shadowAncestorNode = editableRoot->deprecatedShadowAncestorNode();
- if (!editableRoot->getAttributeEventListener(eventNames().webkitBeforeTextInsertedEvent) &&
- // FIXME: Remove these checks once textareas and textfields actually register an event handler.
- !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextControl()) &&
- editableRoot->hasRichlyEditableStyle()) {
+ if (!editableRoot->attributeEventListener(eventNames().webkitBeforeTextInsertedEvent, mainThreadNormalWorld())
+ && !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextControl())
+ && editableRoot->hasRichlyEditableStyle()) {
removeInterchangeNodes(m_fragment.get());
return;
}
@@ -179,16 +189,16 @@ ReplacementFragment::ReplacementFragment(Document& document, DocumentFragment* f
restoreAndRemoveTestRenderingNodesToFragment(holder.get());
// Give the root a chance to change the text.
- RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
- editableRoot->dispatchEvent(evt, ASSERT_NO_EXCEPTION);
- if (text != evt->text() || !editableRoot->hasRichlyEditableStyle()) {
+ auto event = BeforeTextInsertedEvent::create(text);
+ editableRoot->dispatchEvent(event);
+ if (text != event->text() || !editableRoot->hasRichlyEditableStyle()) {
restoreAndRemoveTestRenderingNodesToFragment(holder.get());
RefPtr<Range> range = selection.toNormalizedRange();
if (!range)
return;
- m_fragment = createFragmentFromText(*range, evt->text());
+ m_fragment = createFragmentFromText(*range, event->text());
if (!m_fragment->firstChild())
return;
@@ -221,9 +231,9 @@ void ReplacementFragment::removeNodePreservingChildren(PassRefPtr<Node> node)
while (RefPtr<Node> n = node->firstChild()) {
removeNode(n);
- insertNodeBefore(n.release(), node.get());
+ insertNodeBefore(WTFMove(n), node.get());
}
- removeNode(node);
+ removeNode(WTFMove(node));
}
void ReplacementFragment::removeNode(PassRefPtr<Node> node)
@@ -235,7 +245,7 @@ void ReplacementFragment::removeNode(PassRefPtr<Node> node)
if (!parent)
return;
- parent->removeChild(node.get(), ASSERT_NO_EXCEPTION);
+ parent->removeChild(*node);
}
void ReplacementFragment::insertNodeBefore(PassRefPtr<Node> node, Node* refNode)
@@ -247,18 +257,18 @@ void ReplacementFragment::insertNodeBefore(PassRefPtr<Node> node, Node* refNode)
if (!parent)
return;
- parent->insertBefore(node, refNode, ASSERT_NO_EXCEPTION);
+ parent->insertBefore(*node, refNode);
}
-PassRefPtr<StyledElement> ReplacementFragment::insertFragmentForTestRendering(Node* rootEditableElement)
+Ref<HTMLElement> ReplacementFragment::insertFragmentForTestRendering(Node* rootEditableElement)
{
- RefPtr<StyledElement> holder = createDefaultParagraphElement(document());
+ auto holder = createDefaultParagraphElement(document());
- holder->appendChild(m_fragment, ASSERT_NO_EXCEPTION);
- rootEditableElement->appendChild(holder.get(), ASSERT_NO_EXCEPTION);
+ holder->appendChild(*m_fragment);
+ rootEditableElement->appendChild(holder);
document().updateLayoutIgnorePendingStylesheets();
- return holder.release();
+ return holder;
}
void ReplacementFragment::restoreAndRemoveTestRenderingNodesToFragment(StyledElement* holder)
@@ -267,8 +277,8 @@ void ReplacementFragment::restoreAndRemoveTestRenderingNodesToFragment(StyledEle
return;
while (RefPtr<Node> node = holder->firstChild()) {
- holder->removeChild(node.get(), ASSERT_NO_EXCEPTION);
- m_fragment->appendChild(node.get(), ASSERT_NO_EXCEPTION);
+ holder->removeChild(*node);
+ m_fragment->appendChild(*node);
}
removeNode(holder);
@@ -278,13 +288,13 @@ void ReplacementFragment::removeUnrenderedNodes(Node* holder)
{
Vector<RefPtr<Node>> unrendered;
- for (Node* node = holder->firstChild(); node; node = NodeTraversal::next(node, holder))
- if (!isNodeRendered(node) && !isTableStructureNode(node))
+ for (Node* node = holder->firstChild(); node; node = NodeTraversal::next(*node, holder)) {
+ if (!isNodeRendered(*node) && !isTableStructureNode(node))
unrendered.append(node);
+ }
- size_t n = unrendered.size();
- for (size_t i = 0; i < n; ++i)
- removeNode(unrendered[i]);
+ for (auto& node : unrendered)
+ removeNode(node);
}
void ReplacementFragment::removeInterchangeNodes(Node* container)
@@ -319,9 +329,9 @@ void ReplacementFragment::removeInterchangeNodes(Node* container)
node = container->firstChild();
while (node) {
- RefPtr<Node> next = NodeTraversal::next(node);
+ RefPtr<Node> next = NodeTraversal::next(*node);
if (isInterchangeConvertedSpaceSpan(node)) {
- next = NodeTraversal::nextSkippingChildren(node);
+ next = NodeTraversal::nextSkippingChildren(*node);
removeNodePreservingChildren(node);
}
node = next.get();
@@ -342,20 +352,20 @@ inline void ReplaceSelectionCommand::InsertedNodes::respondToNodeInsertion(Node*
inline void ReplaceSelectionCommand::InsertedNodes::willRemoveNodePreservingChildren(Node* node)
{
if (m_firstNodeInserted == node)
- m_firstNodeInserted = NodeTraversal::next(node);
+ m_firstNodeInserted = NodeTraversal::next(*node);
if (m_lastNodeInserted == node)
- m_lastNodeInserted = node->lastChild() ? node->lastChild() : NodeTraversal::nextSkippingChildren(node);
+ m_lastNodeInserted = node->lastChild() ? node->lastChild() : NodeTraversal::nextSkippingChildren(*node);
}
inline void ReplaceSelectionCommand::InsertedNodes::willRemoveNode(Node* node)
{
if (m_firstNodeInserted == node && m_lastNodeInserted == node) {
- m_firstNodeInserted = 0;
- m_lastNodeInserted = 0;
+ m_firstNodeInserted = nullptr;
+ m_lastNodeInserted = nullptr;
} else if (m_firstNodeInserted == node)
- m_firstNodeInserted = NodeTraversal::nextSkippingChildren(m_firstNodeInserted.get());
+ m_firstNodeInserted = NodeTraversal::nextSkippingChildren(*m_firstNodeInserted);
else if (m_lastNodeInserted == node)
- m_lastNodeInserted = NodeTraversal::previousSkippingChildren(m_lastNodeInserted.get());
+ m_lastNodeInserted = NodeTraversal::previousSkippingChildren(*m_lastNodeInserted);
}
inline void ReplaceSelectionCommand::InsertedNodes::didReplaceNode(Node* node, Node* newNode)
@@ -366,17 +376,17 @@ inline void ReplaceSelectionCommand::InsertedNodes::didReplaceNode(Node* node, N
m_lastNodeInserted = newNode;
}
-ReplaceSelectionCommand::ReplaceSelectionCommand(Document& document, PassRefPtr<DocumentFragment> fragment, CommandOptions options, EditAction editAction)
- : CompositeEditCommand(document)
+ReplaceSelectionCommand::ReplaceSelectionCommand(Document& document, RefPtr<DocumentFragment>&& fragment, CommandOptions options, EditAction editAction)
+ : CompositeEditCommand(document, editAction)
, m_selectReplacement(options & SelectReplacement)
, m_smartReplace(options & SmartReplace)
, m_matchStyle(options & MatchStyle)
, m_documentFragment(fragment)
, m_preventNesting(options & PreventNesting)
, m_movingParagraph(options & MovingParagraph)
- , m_editAction(editAction)
, m_sanitizeFragment(options & SanitizeFragment)
, m_shouldMergeEnd(false)
+ , m_ignoreMailBlockquote(options & IgnoreMailBlockquote)
{
}
@@ -428,7 +438,7 @@ bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph)
static bool isMailPasteAsQuotationNode(const Node* node)
{
- return node && node->hasTagName(blockquoteTag) && node->isElementNode() && toElement(node)->getAttribute(classAttr) == ApplePasteAsQuotation;
+ return node && node->hasTagName(blockquoteTag) && downcast<Element>(node)->attributeWithoutSynchronization(classAttr) == ApplePasteAsQuotation;
}
static bool isHeaderElement(const Node* a)
@@ -446,7 +456,7 @@ static bool isHeaderElement(const Node* a)
static bool haveSameTagName(Node* a, Node* b)
{
- return a && b && a->isElementNode() && b->isElementNode() && toElement(a)->tagName() == toElement(b)->tagName();
+ return is<Element>(a) && is<Element>(b) && downcast<Element>(*a).tagName() == downcast<Element>(*b).tagName();
}
bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const VisiblePosition& destination)
@@ -454,18 +464,19 @@ bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const V
if (source.isNull() || destination.isNull())
return false;
- Node* sourceNode = source.deepEquivalent().deprecatedNode();
- Node* destinationNode = destination.deepEquivalent().deprecatedNode();
- Node* sourceBlock = enclosingBlock(sourceNode);
- Node* destinationBlock = enclosingBlock(destinationNode);
- return !enclosingNodeOfType(source.deepEquivalent(), &isMailPasteAsQuotationNode) &&
- sourceBlock && (!sourceBlock->hasTagName(blockquoteTag) || isMailBlockquote(sourceBlock)) &&
- enclosingListChild(sourceBlock) == enclosingListChild(destinationNode) &&
- enclosingTableCell(source.deepEquivalent()) == enclosingTableCell(destination.deepEquivalent()) &&
- (!isHeaderElement(sourceBlock) || haveSameTagName(sourceBlock, destinationBlock)) &&
- // Don't merge to or from a position before or after a block because it would
- // be a no-op and cause infinite recursion.
- !isBlock(sourceNode) && !isBlock(destinationNode);
+ auto* sourceNode = source.deepEquivalent().deprecatedNode();
+ auto* destinationNode = destination.deepEquivalent().deprecatedNode();
+ auto* sourceBlock = enclosingBlock(sourceNode);
+ auto* destinationBlock = enclosingBlock(destinationNode);
+ return !enclosingNodeOfType(source.deepEquivalent(), &isMailPasteAsQuotationNode)
+ && sourceBlock
+ && (!sourceBlock->hasTagName(blockquoteTag) || isMailBlockquote(sourceBlock))
+ && enclosingListChild(sourceBlock) == enclosingListChild(destinationNode)
+ && enclosingTableCell(source.deepEquivalent()) == enclosingTableCell(destination.deepEquivalent())
+ && (!isHeaderElement(sourceBlock) || haveSameTagName(sourceBlock, destinationBlock))
+ // Don't merge to or from a position before or after a block because it would
+ // be a no-op and cause infinite recursion.
+ && !isBlock(sourceNode) && !isBlock(destinationNode);
}
// Style rules that match just inserted elements could change their appearance, like
@@ -477,29 +488,29 @@ void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline(Insert
for (RefPtr<Node> node = insertedNodes.firstNodeInserted(); node && node != pastEndNode; node = next) {
// FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance
- next = NodeTraversal::next(node.get());
- if (!node->isStyledElement())
+ next = NodeTraversal::next(*node);
+ if (!is<StyledElement>(*node))
continue;
- StyledElement* element = toStyledElement(node.get());
+ StyledElement* element = downcast<StyledElement>(node.get());
const StyleProperties* inlineStyle = element->inlineStyle();
RefPtr<EditingStyle> newInlineStyle = EditingStyle::create(inlineStyle);
if (inlineStyle) {
- if (element->isHTMLElement()) {
+ if (is<HTMLElement>(*element)) {
Vector<QualifiedName> attributes;
- HTMLElement* htmlElement = toHTMLElement(element);
+ HTMLElement& htmlElement = downcast<HTMLElement>(*element);
- if (newInlineStyle->conflictsWithImplicitStyleOfElement(htmlElement)) {
+ if (newInlineStyle->conflictsWithImplicitStyleOfElement(&htmlElement)) {
// e.g. <b style="font-weight: normal;"> is converted to <span style="font-weight: normal;">
- node = replaceElementWithSpanPreservingChildrenAndAttributes(htmlElement);
- element = toStyledElement(node.get());
- insertedNodes.didReplaceNode(htmlElement, node.get());
- } else if (newInlineStyle->extractConflictingImplicitStyleOfAttributes(htmlElement, EditingStyle::PreserveWritingDirection, 0, attributes,
+ node = replaceElementWithSpanPreservingChildrenAndAttributes(&htmlElement);
+ element = downcast<StyledElement>(node.get());
+ insertedNodes.didReplaceNode(&htmlElement, node.get());
+ } else if (newInlineStyle->extractConflictingImplicitStyleOfAttributes(&htmlElement, EditingStyle::PreserveWritingDirection, 0, attributes,
EditingStyle::DoNotExtractMatchingStyle)) {
// e.g. <font size="3" style="font-size: 20px;"> is converted to <font style="font-size: 20px;">
- for (size_t i = 0; i < attributes.size(); i++)
- removeNodeAttribute(element, attributes[i]);
+ for (auto& attribute : attributes)
+ removeNodeAttribute(element, attribute);
}
}
@@ -525,7 +536,7 @@ void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline(Insert
setNodeAttribute(element, styleAttr, newInlineStyle->style()->asText());
// FIXME: Tolerate differences in id, class, and style attributes.
- if (isNonTableCellHTMLBlockElement(element) && areIdenticalElements(element, element->parentNode())
+ if (element->parentNode() && isNonTableCellHTMLBlockElement(element) && areIdenticalElements(*element, *element->parentNode())
&& VisiblePosition(firstPositionInNode(element->parentNode())) == VisiblePosition(firstPositionInNode(element))
&& VisiblePosition(lastPositionInNode(element->parentNode())) == VisiblePosition(lastPositionInNode(element))) {
insertedNodes.willRemoveNodePreservingChildren(element);
@@ -533,7 +544,7 @@ void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline(Insert
continue;
}
- if (element->parentNode()->hasRichlyEditableStyle())
+ if (element->parentNode() && element->parentNode()->hasRichlyEditableStyle())
removeNodeAttribute(element, contenteditableAttr);
// WebKit used to not add display: inline and float: none on copy.
@@ -553,9 +564,9 @@ void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline(Insert
// Mutate using the CSSOM wrapper so we get the same event behavior as a script.
if (isBlock(element))
- element->style()->setPropertyInternal(CSSPropertyDisplay, "inline", false, IGNORE_EXCEPTION);
+ element->cssomStyle()->setPropertyInternal(CSSPropertyDisplay, "inline", false);
if (element->renderer() && element->renderer()->style().isFloating())
- element->style()->setPropertyInternal(CSSPropertyFloat, "none", false, IGNORE_EXCEPTION);
+ element->cssomStyle()->setPropertyInternal(CSSPropertyFloat, "none", false);
}
}
}
@@ -563,58 +574,58 @@ void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline(Insert
static bool isProhibitedParagraphChild(const AtomicString& name)
{
// https://dvcs.w3.org/hg/editing/raw-file/57abe6d3cb60/editing.html#prohibited-paragraph-child
- DEFINE_STATIC_LOCAL(HashSet<AtomicString>, elements, ());
- if (elements.isEmpty()) {
- elements.add(addressTag.localName());
- elements.add(articleTag.localName());
- elements.add(asideTag.localName());
- elements.add(blockquoteTag.localName());
- elements.add(captionTag.localName());
- elements.add(centerTag.localName());
- elements.add(colTag.localName());
- elements.add(colgroupTag.localName());
- elements.add(ddTag.localName());
- elements.add(detailsTag.localName());
- elements.add(dirTag.localName());
- elements.add(divTag.localName());
- elements.add(dlTag.localName());
- elements.add(dtTag.localName());
- elements.add(fieldsetTag.localName());
- elements.add(figcaptionTag.localName());
- elements.add(figureTag.localName());
- elements.add(footerTag.localName());
- elements.add(formTag.localName());
- elements.add(h1Tag.localName());
- elements.add(h2Tag.localName());
- elements.add(h3Tag.localName());
- elements.add(h4Tag.localName());
- elements.add(h5Tag.localName());
- elements.add(h6Tag.localName());
- elements.add(headerTag.localName());
- elements.add(hgroupTag.localName());
- elements.add(hrTag.localName());
- elements.add(liTag.localName());
- elements.add(listingTag.localName());
- elements.add(mainTag.localName()); // Missing in the specification.
- elements.add(menuTag.localName());
- elements.add(navTag.localName());
- elements.add(olTag.localName());
- elements.add(pTag.localName());
- elements.add(plaintextTag.localName());
- elements.add(preTag.localName());
- elements.add(sectionTag.localName());
- elements.add(summaryTag.localName());
- elements.add(tableTag.localName());
- elements.add(tbodyTag.localName());
- elements.add(tdTag.localName());
- elements.add(tfootTag.localName());
- elements.add(thTag.localName());
- elements.add(theadTag.localName());
- elements.add(trTag.localName());
- elements.add(ulTag.localName());
- elements.add(xmpTag.localName());
+ static NeverDestroyed<HashSet<AtomicString>> elements;
+ if (elements.get().isEmpty()) {
+ elements.get().add(addressTag.localName());
+ elements.get().add(articleTag.localName());
+ elements.get().add(asideTag.localName());
+ elements.get().add(blockquoteTag.localName());
+ elements.get().add(captionTag.localName());
+ elements.get().add(centerTag.localName());
+ elements.get().add(colTag.localName());
+ elements.get().add(colgroupTag.localName());
+ elements.get().add(ddTag.localName());
+ elements.get().add(detailsTag.localName());
+ elements.get().add(dirTag.localName());
+ elements.get().add(divTag.localName());
+ elements.get().add(dlTag.localName());
+ elements.get().add(dtTag.localName());
+ elements.get().add(fieldsetTag.localName());
+ elements.get().add(figcaptionTag.localName());
+ elements.get().add(figureTag.localName());
+ elements.get().add(footerTag.localName());
+ elements.get().add(formTag.localName());
+ elements.get().add(h1Tag.localName());
+ elements.get().add(h2Tag.localName());
+ elements.get().add(h3Tag.localName());
+ elements.get().add(h4Tag.localName());
+ elements.get().add(h5Tag.localName());
+ elements.get().add(h6Tag.localName());
+ elements.get().add(headerTag.localName());
+ elements.get().add(hgroupTag.localName());
+ elements.get().add(hrTag.localName());
+ elements.get().add(liTag.localName());
+ elements.get().add(listingTag.localName());
+ elements.get().add(mainTag.localName()); // Missing in the specification.
+ elements.get().add(menuTag.localName());
+ elements.get().add(navTag.localName());
+ elements.get().add(olTag.localName());
+ elements.get().add(pTag.localName());
+ elements.get().add(plaintextTag.localName());
+ elements.get().add(preTag.localName());
+ elements.get().add(sectionTag.localName());
+ elements.get().add(summaryTag.localName());
+ elements.get().add(tableTag.localName());
+ elements.get().add(tbodyTag.localName());
+ elements.get().add(tdTag.localName());
+ elements.get().add(tfootTag.localName());
+ elements.get().add(thTag.localName());
+ elements.get().add(theadTag.localName());
+ elements.get().add(trTag.localName());
+ elements.get().add(ulTag.localName());
+ elements.get().add(xmpTag.localName());
}
- return elements.contains(name);
+ return elements.get().contains(name);
}
void ReplaceSelectionCommand::makeInsertedContentRoundTrippableWithHTMLTreeBuilder(InsertedNodes& insertedNodes)
@@ -622,27 +633,34 @@ void ReplaceSelectionCommand::makeInsertedContentRoundTrippableWithHTMLTreeBuild
RefPtr<Node> pastEndNode = insertedNodes.pastLastLeaf();
RefPtr<Node> next;
for (RefPtr<Node> node = insertedNodes.firstNodeInserted(); node && node != pastEndNode; node = next) {
- next = NodeTraversal::next(node.get());
+ next = NodeTraversal::next(*node);
- if (!node->isHTMLElement())
+ if (!is<HTMLElement>(*node))
continue;
- if (isProhibitedParagraphChild(toHTMLElement(node.get())->localName())) {
- if (auto* paragraphElement = toHTMLElement(enclosingNodeWithTag(positionInParentBeforeNode(node.get()), pTag))) {
+ if (isProhibitedParagraphChild(downcast<HTMLElement>(*node).localName())) {
+ if (auto* paragraphElement = enclosingElementWithTag(positionInParentBeforeNode(node.get()), pTag)) {
auto* parent = paragraphElement->parentNode();
if (parent && parent->hasEditableStyle())
- moveNodeOutOfAncestor(node, paragraphElement);
+ moveNodeOutOfAncestor(node, paragraphElement, insertedNodes);
}
}
if (isHeaderElement(node.get())) {
- if (HTMLElement* headerElement = toHTMLElement(highestEnclosingNodeOfType(positionInParentBeforeNode(node.get()), isHeaderElement)))
- moveNodeOutOfAncestor(node, headerElement);
+ auto* headerElement = highestEnclosingNodeOfType(positionInParentBeforeNode(node.get()), isHeaderElement);
+ if (headerElement) {
+ if (headerElement->parentNode() && headerElement->parentNode()->isContentRichlyEditable())
+ moveNodeOutOfAncestor(node, headerElement, insertedNodes);
+ else {
+ HTMLElement* newSpanElement = replaceElementWithSpanPreservingChildrenAndAttributes(downcast<HTMLElement>(node.get()));
+ insertedNodes.didReplaceNode(node.get(), newSpanElement);
+ }
+ }
}
}
}
-void ReplaceSelectionCommand::moveNodeOutOfAncestor(PassRefPtr<Node> prpNode, PassRefPtr<Node> prpAncestor)
+void ReplaceSelectionCommand::moveNodeOutOfAncestor(PassRefPtr<Node> prpNode, PassRefPtr<Node> prpAncestor, InsertedNodes& insertedNodes)
{
RefPtr<Node> node = prpNode;
RefPtr<Node> ancestor = prpAncestor;
@@ -660,8 +678,10 @@ void ReplaceSelectionCommand::moveNodeOutOfAncestor(PassRefPtr<Node> prpNode, Pa
removeNode(node);
insertNodeBefore(node, nodeToSplitTo);
}
- if (!ancestor->firstChild())
- removeNode(ancestor.release());
+ if (!ancestor->firstChild()) {
+ insertedNodes.willRemoveNode(ancestor.get());
+ removeNode(WTFMove(ancestor));
+ }
}
static inline bool hasRenderedText(const Text& text)
@@ -674,9 +694,9 @@ void ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds(InsertedNodes& ins
document().updateLayoutIgnorePendingStylesheets();
Node* lastLeafInserted = insertedNodes.lastLeafInserted();
- if (lastLeafInserted && lastLeafInserted->isTextNode() && !hasRenderedText(toText(*lastLeafInserted))
- && !enclosingNodeWithTag(firstPositionInOrBeforeNode(lastLeafInserted), selectTag)
- && !enclosingNodeWithTag(firstPositionInOrBeforeNode(lastLeafInserted), scriptTag)) {
+ if (is<Text>(lastLeafInserted) && !hasRenderedText(downcast<Text>(*lastLeafInserted))
+ && !enclosingElementWithTag(firstPositionInOrBeforeNode(lastLeafInserted), selectTag)
+ && !enclosingElementWithTag(firstPositionInOrBeforeNode(lastLeafInserted), scriptTag)) {
insertedNodes.willRemoveNode(lastLeafInserted);
removeNode(lastLeafInserted);
}
@@ -684,7 +704,7 @@ void ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds(InsertedNodes& ins
// We don't have to make sure that firstNodeInserted isn't inside a select or script element
// because it is a top level node in the fragment and the user can't insert into those elements.
Node* firstNodeInserted = insertedNodes.firstNodeInserted();
- if (firstNodeInserted && firstNodeInserted->isTextNode() && !hasRenderedText(toText(*firstNodeInserted))) {
+ if (is<Text>(firstNodeInserted) && !hasRenderedText(downcast<Text>(*firstNodeInserted))) {
insertedNodes.willRemoveNode(firstNodeInserted);
removeNode(firstNodeInserted);
}
@@ -693,7 +713,7 @@ void ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds(InsertedNodes& ins
VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() const
{
// FIXME: Why is this hack here? What's special about <select> tags?
- Node* enclosingSelect = enclosingNodeWithTag(m_endOfInsertedContent, selectTag);
+ auto* enclosingSelect = enclosingElementWithTag(m_endOfInsertedContent, selectTag);
return enclosingSelect ? lastPositionInOrAfterNode(enclosingSelect) : m_endOfInsertedContent;
}
@@ -712,7 +732,7 @@ static void removeHeadContents(ReplacementFragment& fragment)
auto it = descendantsOfType<Element>(*fragment.fragment()).begin();
auto end = descendantsOfType<Element>(*fragment.fragment()).end();
while (it != end) {
- if (it->hasTagName(baseTag) || it->hasTagName(linkTag) || it->hasTagName(metaTag) || it->hasTagName(styleTag) || isHTMLTitleElement(*it)) {
+ if (is<HTMLBaseElement>(*it) || is<HTMLLinkElement>(*it) || is<HTMLMetaElement>(*it) || is<HTMLStyleElement>(*it) || is<HTMLTitleElement>(*it)) {
toRemove.append(&*it);
it.traverseNextSkippingChildren();
continue;
@@ -720,8 +740,8 @@ static void removeHeadContents(ReplacementFragment& fragment)
++it;
}
- for (unsigned i = 0; i < toRemove.size(); ++i)
- fragment.removeNode(toRemove[i]);
+ for (auto& element : toRemove)
+ fragment.removeNode(element);
}
// Remove style spans before insertion if they are unnecessary. It's faster because we'll
@@ -746,7 +766,7 @@ static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const
// FIXME: This string comparison is a naive way of comparing two styles.
// We should be taking the diff and check that the diff is empty.
- if (styleText != toElement(wrappingStyleSpan)->getAttribute(styleAttr))
+ if (styleText != downcast<Element>(*wrappingStyleSpan).getAttribute(styleAttr))
return false;
fragment.removeNodePreservingChildren(wrappingStyleSpan);
@@ -763,13 +783,13 @@ static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const
// or at copy time.
void ReplaceSelectionCommand::handleStyleSpans(InsertedNodes& insertedNodes)
{
- HTMLElement* wrappingStyleSpan = 0;
+ HTMLElement* wrappingStyleSpan = nullptr;
// The style span that contains the source document's default style should be at
// the top of the fragment, but Mail sometimes adds a wrapper (for Paste As Quotation),
// so search for the top level style span instead of assuming it's at the top.
- for (Node* node = insertedNodes.firstNodeInserted(); node; node = NodeTraversal::next(node)) {
+ for (Node* node = insertedNodes.firstNodeInserted(); node; node = NodeTraversal::next(*node)) {
if (isLegacyAppleStyleSpan(node)) {
- wrappingStyleSpan = toHTMLElement(node);
+ wrappingStyleSpan = downcast<HTMLElement>(node);
break;
}
}
@@ -831,7 +851,7 @@ void ReplaceSelectionCommand::mergeEndIfNeeded()
// Merging forward could result in deleting the destination anchor node.
// To avoid this, we add a placeholder node before the start of the paragraph.
if (endOfParagraph(startOfParagraphToMove) == destination) {
- RefPtr<Node> placeholder = createBreakElement(document());
+ RefPtr<Node> placeholder = HTMLBRElement::create(document());
insertNodeBefore(placeholder, startOfParagraphToMove.deepEquivalent().deprecatedNode());
destination = VisiblePosition(positionBeforeNode(placeholder.get()));
}
@@ -852,11 +872,11 @@ void ReplaceSelectionCommand::mergeEndIfNeeded()
static Node* enclosingInline(Node* node)
{
while (ContainerNode* parent = node->parentNode()) {
- if (isBlockFlowElement(parent) || parent->hasTagName(bodyTag))
+ if (isBlockFlowElement(*parent) || parent->hasTagName(bodyTag))
return node;
// Stop if any previous sibling is a block.
for (Node* sibling = node->previousSibling(); sibling; sibling = sibling->previousSibling()) {
- if (isBlockFlowElement(sibling))
+ if (isBlockFlowElement(*sibling))
return node;
}
node = parent;
@@ -876,7 +896,7 @@ static bool isInlineNodeWithStyle(const Node* node)
// We can skip over elements whose class attribute is
// one of our internal classes.
const HTMLElement* element = static_cast<const HTMLElement*>(node);
- const AtomicString& classAttributeValue = element->getAttribute(classAttr);
+ const AtomicString& classAttributeValue = element->attributeWithoutSynchronization(classAttr);
if (classAttributeValue == AppleTabSpanClass
|| classAttributeValue == AppleConvertedSpace
|| classAttributeValue == ApplePasteAsQuotation)
@@ -891,25 +911,28 @@ inline Node* nodeToSplitToAvoidPastingIntoInlineNodesWithStyle(const Position& i
return highestEnclosingNodeOfType(insertionPos, isInlineNodeWithStyle, CannotCrossEditingBoundary, containgBlock);
}
+bool ReplaceSelectionCommand::willApplyCommand()
+{
+ ensureReplacementFragment();
+ m_documentFragmentPlainText = m_documentFragment->textContent();
+ m_documentFragmentHTMLMarkup = createMarkup(*m_documentFragment);
+ return CompositeEditCommand::willApplyCommand();
+}
+
void ReplaceSelectionCommand::doApply()
{
VisibleSelection selection = endingSelection();
ASSERT(selection.isCaretOrRange());
ASSERT(selection.start().deprecatedNode());
- if (!selection.isNonOrphanedCaretOrRange() || !selection.start().deprecatedNode())
+ if (selection.isNoneOrOrphaned() || !selection.start().deprecatedNode() || !selection.isContentEditable())
return;
- if (!selection.rootEditableElement())
- return;
-
-#if PLATFORM(IOS)
// In plain text only regions, we create style-less fragments, so the inserted content will automatically
// match the style of the surrounding area and so we can avoid unnecessary work below for m_matchStyle.
if (!selection.isContentRichlyEditable())
m_matchStyle = false;
-#endif
- ReplacementFragment fragment(document(), m_documentFragment.get(), selection);
+ ReplacementFragment& fragment = *ensureReplacementFragment();
if (performTrivialReplace(fragment))
return;
@@ -932,12 +955,12 @@ void ReplaceSelectionCommand::doApply()
Node* startBlock = enclosingBlock(visibleStart.deepEquivalent().deprecatedNode());
Position insertionPos = selection.start();
- bool startIsInsideMailBlockquote = enclosingNodeOfType(insertionPos, isMailBlockquote, CanCrossEditingBoundary);
+ bool shouldHandleMailBlockquote = enclosingNodeOfType(insertionPos, isMailBlockquote, CanCrossEditingBoundary) && !m_ignoreMailBlockquote;
bool selectionIsPlainText = !selection.isContentRichlyEditable();
Element* currentRoot = selection.rootEditableElement();
- if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !startIsInsideMailBlockquote) ||
- startBlock == currentRoot || isListItem(startBlock) || selectionIsPlainText)
+ if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !shouldHandleMailBlockquote)
+ || startBlock == currentRoot || isListItem(startBlock) || selectionIsPlainText)
m_preventNesting = false;
if (selection.isRange()) {
@@ -947,10 +970,11 @@ void ReplaceSelectionCommand::doApply()
// will leave hanging block(s).
// Merge blocks if the start of the selection was in a Mail blockquote, since we handle
// that case specially to prevent nesting.
- bool mergeBlocksAfterDelete = startIsInsideMailBlockquote || isEndOfParagraph(visibleEnd) || isStartOfBlock(visibleStart);
+ bool mergeBlocksAfterDelete = shouldHandleMailBlockquote || isEndOfParagraph(visibleEnd) || isStartOfBlock(visibleStart);
// FIXME: We should only expand to include fully selected special elements if we are copying a
// selection and pasting it on top of itself.
- deleteSelection(false, mergeBlocksAfterDelete, true, false);
+ // FIXME: capturing the content of this delete would allow a replace accessibility notification instead of a simple insert
+ deleteSelection(false, mergeBlocksAfterDelete, true, false, true);
visibleStart = endingSelection().visibleStart();
if (fragment.hasInterchangeNewlineAtStart()) {
if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
@@ -976,7 +1000,7 @@ void ReplaceSelectionCommand::doApply()
// As long as the div styles are the same, visually you'd expect: <div>xbar</div><div>bar</div><div>bazx</div>,
// not <div>xbar<div>bar</div><div>bazx</div></div>.
// Don't do this if the selection started in a Mail blockquote.
- if (m_preventNesting && !startIsInsideMailBlockquote && !isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
+ if (m_preventNesting && !shouldHandleMailBlockquote && !isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
insertParagraphSeparator();
setEndingSelection(endingSelection().visibleStart().previous());
}
@@ -986,7 +1010,7 @@ void ReplaceSelectionCommand::doApply()
// We don't want any of the pasted content to end up nested in a Mail blockquote, so first break
// out of any surrounding Mail blockquotes. Unless we're inserting in a table, in which case
// breaking the blockquote will prevent the content from actually being inserted in the table.
- if (startIsInsideMailBlockquote && m_preventNesting && !(enclosingNodeOfType(insertionPos, &isTableStructureNode))) {
+ if (shouldHandleMailBlockquote && m_preventNesting && !(enclosingNodeOfType(insertionPos, &isTableStructureNode))) {
applyCommandToComposite(BreakBlockquoteCommand::create(document()));
// This will leave a br between the split.
Node* br = endingSelection().start().deprecatedNode();
@@ -1015,7 +1039,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 && insertionBlock && !isTableCell(insertionBlock.get()) && !startIsInsideMailBlockquote) {
+ if (m_preventNesting && insertionBlock && !isTableCell(insertionBlock.get()) && !shouldHandleMailBlockquote) {
ASSERT(insertionBlock != currentRoot);
VisiblePosition visibleInsertionPos(insertionPos);
if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInsertionPos) && fragment.hasInterchangeNewlineAtEnd()))
@@ -1031,8 +1055,6 @@ void ReplaceSelectionCommand::doApply()
// any work performed after this that queries or uses the typing style.
frame().selection().clearTypingStyle();
- removeHeadContents(fragment);
-
// We don't want the destination to end up inside nodes that weren't selected. To avoid that, we move the
// position forward without changing the visible position so we're still at the same visible location, but
// outside of preceding tags.
@@ -1090,16 +1112,16 @@ void ReplaceSelectionCommand::doApply()
fragment.removeNode(refNode);
Node* blockStart = enclosingBlock(insertionPos.deprecatedNode());
- if ((isListElement(refNode.get()) || (isLegacyAppleStyleSpan(refNode.get()) && isListElement(refNode->firstChild())))
+ if ((isListHTMLElement(refNode.get()) || (isLegacyAppleStyleSpan(refNode.get()) && isListHTMLElement(refNode->firstChild())))
&& blockStart && blockStart->renderer()->isListItem())
- refNode = insertAsListItems(toHTMLElement(refNode.get()), blockStart, insertionPos, insertedNodes);
+ refNode = insertAsListItems(downcast<HTMLElement>(refNode.get()), blockStart, insertionPos, insertedNodes);
else {
insertNodeAt(refNode, insertionPos);
insertedNodes.respondToNodeInsertion(refNode.get());
}
// Mutation events (bug 22634) may have already removed the inserted content
- if (!refNode->inDocument())
+ if (!refNode->isConnected())
return;
bool plainTextFragment = isPlainTextMarkup(refNode.get());
@@ -1111,7 +1133,7 @@ void ReplaceSelectionCommand::doApply()
insertedNodes.respondToNodeInsertion(node.get());
// Mutation events (bug 22634) may have already removed the inserted content
- if (!node->inDocument())
+ if (!node->isConnected())
return;
refNode = node;
@@ -1126,15 +1148,15 @@ void ReplaceSelectionCommand::doApply()
handleStyleSpans(insertedNodes);
// Mutation events (bug 20161) may have already removed the inserted content
- if (!insertedNodes.firstNodeInserted() || !insertedNodes.firstNodeInserted()->inDocument())
+ if (!insertedNodes.firstNodeInserted() || !insertedNodes.firstNodeInserted()->isConnected())
return;
VisiblePosition startOfInsertedContent = firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted());
// We inserted before the insertionBlock to prevent nesting, and the content before the insertionBlock wasn't in its own block and
// didn't have a br after it, so the inserted content ended up in the same paragraph.
- if (insertionBlock && insertionPos.deprecatedNode() == insertionBlock->parentNode() && (unsigned)insertionPos.deprecatedEditingOffset() < insertionBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent))
- insertNodeAt(createBreakElement(document()), startOfInsertedContent.deepEquivalent());
+ if (!startOfInsertedContent.isNull() && insertionBlock && insertionPos.deprecatedNode() == insertionBlock->parentNode() && (unsigned)insertionPos.deprecatedEditingOffset() < insertionBlock->computeNodeIndex() && !isStartOfParagraph(startOfInsertedContent))
+ insertNodeAt(HTMLBRElement::create(document()), startOfInsertedContent.deepEquivalent());
if (endBR && (plainTextFragment || shouldRemoveEndBR(endBR.get(), originalVisPosBeforeEndBR))) {
RefPtr<Node> parent = endBR->parentNode();
@@ -1161,7 +1183,7 @@ void ReplaceSelectionCommand::doApply()
// the start merge so that the start merge doesn't effect our decision.
m_shouldMergeEnd = shouldMergeEnd(selectionEndWasEndOfParagraph);
- if (shouldMergeStart(selectionStartWasStartOfParagraph, fragment.hasInterchangeNewlineAtStart(), startIsInsideMailBlockquote)) {
+ if (shouldMergeStart(selectionStartWasStartOfParagraph, fragment.hasInterchangeNewlineAtStart(), shouldHandleMailBlockquote)) {
VisiblePosition startOfParagraphToMove = positionAtStartOfInsertedContent();
VisiblePosition destination = startOfParagraphToMove.previous();
// We need to handle the case where we need to merge the end
@@ -1169,7 +1191,7 @@ void ReplaceSelectionCommand::doApply()
// We insert a placeholder before the newly inserted content to avoid being merged into the inline.
Node* destinationNode = destination.deepEquivalent().deprecatedNode();
if (m_shouldMergeEnd && destinationNode != enclosingInline(destinationNode) && enclosingInline(destinationNode)->nextSibling())
- insertNodeBefore(createBreakElement(document()), refNode.get());
+ insertNodeBefore(HTMLBRElement::create(document()), refNode.get());
// Merging the the first paragraph of inserted content with the content that came
// before the selection that was pasted into would also move content after
@@ -1180,9 +1202,9 @@ void ReplaceSelectionCommand::doApply()
// comes after and prevent that from happening.
VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent();
if (startOfParagraph(endOfInsertedContent) == startOfParagraphToMove) {
- insertNodeAt(createBreakElement(document()), endOfInsertedContent.deepEquivalent());
+ insertNodeAt(HTMLBRElement::create(document()), endOfInsertedContent.deepEquivalent());
// Mutation events (bug 22634) triggered by inserting the <br> might have removed the content we're about to move
- if (!startOfParagraphToMove.deepEquivalent().anchorNode()->inDocument())
+ if (!startOfParagraphToMove.deepEquivalent().anchorNode()->isConnected())
return;
}
@@ -1204,13 +1226,13 @@ void ReplaceSelectionCommand::doApply()
setEndingSelection(endOfInsertedContent);
Node* enclosingNode = enclosingBlock(endOfInsertedContent.deepEquivalent().deprecatedNode());
if (isListItem(enclosingNode)) {
- RefPtr<Node> newListItem = createListItemElement(document());
- insertNodeAfter(newListItem, enclosingNode);
- setEndingSelection(VisiblePosition(firstPositionInNode(newListItem.get())));
+ auto newListItem = HTMLLIElement::create(document());
+ insertNodeAfter(newListItem.copyRef(), enclosingNode);
+ setEndingSelection(VisiblePosition(firstPositionInNode(newListItem.ptr())));
} 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, !startIsInsideMailBlockquote && highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(),
+ insertParagraphSeparator(true, !shouldHandleMailBlockquote && highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(),
isMailBlockquote, CannotCrossEditingBoundary, insertedNodes.firstNodeInserted()->parentNode()));
}
@@ -1227,7 +1249,7 @@ void ReplaceSelectionCommand::doApply()
mergeEndIfNeeded();
if (Node* mailBlockquote = enclosingNodeOfType(positionAtStartOfInsertedContent().deepEquivalent(), isMailPasteAsQuotationNode))
- removeNodeAttribute(toElement(mailBlockquote), classAttr);
+ removeNodeAttribute(downcast<Element>(mailBlockquote), classAttr);
if (shouldPerformSmartReplace())
addSpacesForSmartReplace();
@@ -1240,11 +1262,27 @@ void ReplaceSelectionCommand::doApply()
completeHTMLReplacement(lastPositionToSelect);
}
+String ReplaceSelectionCommand::inputEventData() const
+{
+ if (isEditingTextAreaOrTextInput())
+ return m_documentFragment->textContent();
+
+ return CompositeEditCommand::inputEventData();
+}
+
+RefPtr<DataTransfer> ReplaceSelectionCommand::inputEventDataTransfer() const
+{
+ if (isEditingTextAreaOrTextInput())
+ return CompositeEditCommand::inputEventDataTransfer();
+
+ return DataTransfer::createForInputEvent(m_documentFragmentPlainText, m_documentFragmentHTMLMarkup);
+}
+
bool ReplaceSelectionCommand::shouldRemoveEndBR(Node* endBR, const VisiblePosition& originalVisPosBeforeEndBR)
{
- if (!endBR || !endBR->inDocument())
+ if (!endBR || !endBR->isConnected())
return false;
-
+
VisiblePosition visiblePos(positionBeforeNode(endBR));
// Don't remove the br if nothing was inserted.
@@ -1266,7 +1304,7 @@ bool ReplaceSelectionCommand::shouldPerformSmartReplace() const
return false;
Element* textControl = enclosingTextFormControl(positionAtStartOfInsertedContent().deepEquivalent());
- if (textControl && isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPasswordField())
+ if (is<HTMLInputElement>(textControl) && downcast<HTMLInputElement>(*textControl).isPasswordField())
return false; // Disable smart replace for password fields.
return true;
@@ -1284,7 +1322,7 @@ void ReplaceSelectionCommand::addSpacesForSmartReplace()
Position endUpstream = endOfInsertedContent.deepEquivalent().upstream();
Node* endNode = endUpstream.computeNodeBeforePosition();
- int endOffset = endNode && endNode->isTextNode() ? toText(endNode)->length() : 0;
+ int endOffset = is<Text>(endNode) ? downcast<Text>(*endNode).length() : 0;
if (endUpstream.anchorType() == Position::PositionIsOffsetInAnchor) {
endNode = endUpstream.containerNode();
endOffset = endUpstream.offsetInContainerNode();
@@ -1293,8 +1331,8 @@ void ReplaceSelectionCommand::addSpacesForSmartReplace()
bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && !isCharacterSmartReplaceExemptConsideringNonBreakingSpace(endOfInsertedContent.characterAfter(), false);
if (needsTrailingSpace && endNode) {
bool collapseWhiteSpace = !endNode->renderer() || endNode->renderer()->style().collapseWhiteSpace();
- if (endNode->isTextNode()) {
- insertTextIntoNode(toText(endNode), endOffset, collapseWhiteSpace ? nonBreakingSpaceString() : " ");
+ if (is<Text>(*endNode)) {
+ insertTextIntoNode(downcast<Text>(endNode), endOffset, collapseWhiteSpace ? nonBreakingSpaceString() : " ");
if (m_endOfInsertedContent.containerNode() == endNode)
m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offsetInContainerNode() + 1);
} else {
@@ -1317,8 +1355,8 @@ void ReplaceSelectionCommand::addSpacesForSmartReplace()
bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && !isCharacterSmartReplaceExemptConsideringNonBreakingSpace(startOfInsertedContent.previous().characterAfter(), true);
if (needsLeadingSpace && startNode) {
bool collapseWhiteSpace = !startNode->renderer() || startNode->renderer()->style().collapseWhiteSpace();
- if (startNode->isTextNode()) {
- insertTextIntoNode(toText(startNode), startOffset, collapseWhiteSpace ? nonBreakingSpaceString() : " ");
+ if (is<Text>(*startNode)) {
+ insertTextIntoNode(downcast<Text>(startNode), startOffset, collapseWhiteSpace ? nonBreakingSpaceString() : " ");
if (m_endOfInsertedContent.containerNode() == startNode && m_endOfInsertedContent.offsetInContainerNode())
m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offsetInContainerNode() + 1);
} else {
@@ -1357,6 +1395,9 @@ void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositi
else
return;
+ if (AXObjectCache::accessibilityEnabled() && editingAction() == EditActionPaste)
+ m_visibleSelectionForInsertedText = VisibleSelection(start, end);
+
if (m_selectReplacement)
setEndingSelection(VisibleSelection(start, end, SEL_DEFAULT_AFFINITY, endingSelection().isDirectional()));
else
@@ -1367,24 +1408,24 @@ void ReplaceSelectionCommand::mergeTextNodesAroundPosition(Position& position, P
{
bool positionIsOffsetInAnchor = position.anchorType() == Position::PositionIsOffsetInAnchor;
bool positionOnlyToBeUpdatedIsOffsetInAnchor = positionOnlyToBeUpdated.anchorType() == Position::PositionIsOffsetInAnchor;
- RefPtr<Text> text = 0;
- if (positionIsOffsetInAnchor && position.containerNode() && position.containerNode()->isTextNode())
- text = toText(position.containerNode());
+ RefPtr<Text> text;
+ if (positionIsOffsetInAnchor && is<Text>(position.containerNode()))
+ text = downcast<Text>(position.containerNode());
else {
Node* before = position.computeNodeBeforePosition();
- if (before && before->isTextNode())
- text = toText(before);
+ if (is<Text>(before))
+ text = downcast<Text>(before);
else {
Node* after = position.computeNodeAfterPosition();
- if (after && after->isTextNode())
- text = toText(after);
+ if (is<Text>(after))
+ text = downcast<Text>(after);
}
}
if (!text)
return;
- if (text->previousSibling() && text->previousSibling()->isTextNode()) {
- RefPtr<Text> previous = toText(text->previousSibling());
+ if (is<Text>(text->previousSibling())) {
+ Ref<Text> previous(downcast<Text>(*text->previousSibling()));
insertTextIntoNode(text, 0, previous->data());
if (positionIsOffsetInAnchor)
@@ -1395,43 +1436,38 @@ void ReplaceSelectionCommand::mergeTextNodesAroundPosition(Position& position, P
if (positionOnlyToBeUpdatedIsOffsetInAnchor) {
if (positionOnlyToBeUpdated.containerNode() == text)
positionOnlyToBeUpdated.moveToOffset(previous->length() + positionOnlyToBeUpdated.offsetInContainerNode());
- else if (positionOnlyToBeUpdated.containerNode() == previous)
- positionOnlyToBeUpdated.moveToPosition(text, positionOnlyToBeUpdated.offsetInContainerNode());
+ else if (positionOnlyToBeUpdated.containerNode() == previous.ptr())
+ positionOnlyToBeUpdated.moveToPosition(text.get(), positionOnlyToBeUpdated.offsetInContainerNode());
} else
updatePositionForNodeRemoval(positionOnlyToBeUpdated, previous.get());
- removeNode(previous);
+ removeNode(previous.ptr());
}
- if (text->nextSibling() && text->nextSibling()->isTextNode()) {
- RefPtr<Text> next = toText(text->nextSibling());
+ if (is<Text>(text->nextSibling())) {
+ Ref<Text> next(downcast<Text>(*text->nextSibling()));
unsigned originalLength = text->length();
insertTextIntoNode(text, originalLength, next->data());
if (!positionIsOffsetInAnchor)
updatePositionForNodeRemoval(position, next.get());
- if (positionOnlyToBeUpdatedIsOffsetInAnchor && positionOnlyToBeUpdated.containerNode() == next)
- positionOnlyToBeUpdated.moveToPosition(text, originalLength + positionOnlyToBeUpdated.offsetInContainerNode());
+ if (positionOnlyToBeUpdatedIsOffsetInAnchor && positionOnlyToBeUpdated.containerNode() == next.ptr())
+ positionOnlyToBeUpdated.moveToPosition(text.get(), originalLength + positionOnlyToBeUpdated.offsetInContainerNode());
else
updatePositionForNodeRemoval(positionOnlyToBeUpdated, next.get());
- removeNode(next);
+ removeNode(next.ptr());
}
}
-EditAction ReplaceSelectionCommand::editingAction() const
-{
- return m_editAction;
-}
-
// If the user is inserting a list into an existing list, instead of nesting the list,
// we put the list items into the existing list.
Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtr<HTMLElement> prpListElement, Node* insertionBlock, const Position& insertPos, InsertedNodes& insertedNodes)
{
RefPtr<HTMLElement> listElement = prpListElement;
- while (listElement->hasChildNodes() && isListElement(listElement->firstChild()) && listElement->childNodeCount() == 1)
- listElement = toHTMLElement(listElement->firstChild());
+ while (listElement->hasOneChild() && isListHTMLElement(listElement->firstChild()))
+ listElement = downcast<HTMLElement>(listElement->firstChild());
bool isStart = isStartOfParagraph(insertPos);
bool isEnd = isEndOfParagraph(insertPos);
@@ -1442,13 +1478,13 @@ Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtr<HTMLElement> prpList
// list items and insert these nodes between them.
if (isMiddle) {
int textNodeOffset = insertPos.offsetInContainerNode();
- if (insertPos.deprecatedNode()->isTextNode() && textNodeOffset > 0)
- splitTextNode(toText(insertPos.deprecatedNode()), textNodeOffset);
+ if (is<Text>(*insertPos.deprecatedNode()) && textNodeOffset > 0)
+ splitTextNode(downcast<Text>(insertPos.deprecatedNode()), textNodeOffset);
splitTreeToNode(insertPos.deprecatedNode(), lastNode, true);
}
while (RefPtr<Node> listItem = listElement->firstChild()) {
- listElement->removeChild(listItem.get(), ASSERT_NO_EXCEPTION);
+ listElement->removeChild(*listItem);
if (isStart || isMiddle) {
insertNodeBefore(listItem, lastNode);
insertedNodes.respondToNodeInsertion(listItem.get());
@@ -1459,7 +1495,7 @@ Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtr<HTMLElement> prpList
} else
ASSERT_NOT_REACHED();
}
- if (isStart || isMiddle)
+ if ((isStart || isMiddle) && lastNode->previousSibling())
lastNode = lastNode->previousSibling();
return lastNode;
}
@@ -1475,13 +1511,23 @@ void ReplaceSelectionCommand::updateNodesInserted(Node *node)
m_endOfInsertedContent = lastPositionInOrAfterNode(node->lastDescendant());
}
+ReplacementFragment* ReplaceSelectionCommand::ensureReplacementFragment()
+{
+ if (!m_replacementFragment) {
+ m_replacementFragment = std::make_unique<ReplacementFragment>(document(), m_documentFragment.get(), endingSelection());
+ removeHeadContents(*m_replacementFragment);
+ }
+
+ return m_replacementFragment.get();
+}
+
// During simple pastes, where we're just pasting a text node into a run of text, we insert the text node
// directly into the text node that holds the selection. This is much faster than the generalized code in
// ReplaceSelectionCommand, and works around <https://bugs.webkit.org/show_bug.cgi?id=6148> since we don't
// split text nodes.
bool ReplaceSelectionCommand::performTrivialReplace(const ReplacementFragment& fragment)
{
- if (!fragment.firstChild() || fragment.firstChild() != fragment.lastChild() || !fragment.firstChild()->isTextNode())
+ if (!is<Text>(fragment.firstChild()) || fragment.firstChild() != fragment.lastChild())
return false;
// FIXME: Would be nice to handle smart replace in the fast path.
@@ -1493,11 +1539,11 @@ bool ReplaceSelectionCommand::performTrivialReplace(const ReplacementFragment& f
return false;
RefPtr<Node> nodeAfterInsertionPos = endingSelection().end().downstream().anchorNode();
- Text* textNode = toText(fragment.firstChild());
+ Text& textNode = downcast<Text>(*fragment.firstChild());
// Our fragment creation code handles tabs, spaces, and newlines, so we don't have to worry about those here.
Position start = endingSelection().start();
- Position end = replaceSelectedTextInNode(textNode->data());
+ Position end = replaceSelectedTextInNode(textNode.data());
if (end.isNull())
return false;
@@ -1507,6 +1553,9 @@ bool ReplaceSelectionCommand::performTrivialReplace(const ReplacementFragment& f
VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, end);
+ if (AXObjectCache::accessibilityEnabled() && editingAction() == EditActionPaste)
+ m_visibleSelectionForInsertedText = VisibleSelection(start, end);
+
setEndingSelection(selectionAfterReplace);
return true;
diff --git a/Source/WebCore/editing/ReplaceSelectionCommand.h b/Source/WebCore/editing/ReplaceSelectionCommand.h
index f46d829fb..3d2f419ec 100644
--- a/Source/WebCore/editing/ReplaceSelectionCommand.h
+++ b/Source/WebCore/editing/ReplaceSelectionCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef ReplaceSelectionCommand_h
-#define ReplaceSelectionCommand_h
+#pragma once
#include "CompositeEditCommand.h"
#include "NodeTraversal.h"
@@ -42,22 +41,27 @@ public:
MatchStyle = 1 << 2,
PreventNesting = 1 << 3,
MovingParagraph = 1 << 4,
- SanitizeFragment = 1 << 5
+ SanitizeFragment = 1 << 5,
+ IgnoreMailBlockquote = 1 << 6,
};
typedef unsigned CommandOptions;
- static PassRefPtr<ReplaceSelectionCommand> create(Document& document, PassRefPtr<DocumentFragment> fragment, CommandOptions options, EditAction action = EditActionPaste)
+ static Ref<ReplaceSelectionCommand> create(Document& document, RefPtr<DocumentFragment>&& fragment, CommandOptions options, EditAction editingAction = EditActionInsert)
{
- return adoptRef(new ReplaceSelectionCommand(document, fragment, options, action));
+ return adoptRef(*new ReplaceSelectionCommand(document, WTFMove(fragment), options, editingAction));
}
+ VisibleSelection visibleSelectionForInsertedText() const { return m_visibleSelectionForInsertedText; }
+
private:
- ReplaceSelectionCommand(Document&, PassRefPtr<DocumentFragment>, CommandOptions, EditAction);
+ ReplaceSelectionCommand(Document&, RefPtr<DocumentFragment>&&, CommandOptions, EditAction);
+
+ String inputEventData() const final;
+ RefPtr<DataTransfer> inputEventDataTransfer() const final;
+ bool willApplyCommand() final;
+ void doApply() override;
- virtual void doApply();
- virtual EditAction editingAction() const;
-
class InsertedNodes {
public:
void respondToNodeInsertion(Node*);
@@ -67,7 +71,14 @@ private:
Node* firstNodeInserted() const { return m_firstNodeInserted.get(); }
Node* lastLeafInserted() const { return m_lastNodeInserted->lastDescendant(); }
- Node* pastLastLeaf() const { return m_lastNodeInserted ? NodeTraversal::next(lastLeafInserted()) : 0; }
+ Node* pastLastLeaf() const
+ {
+ if (m_lastNodeInserted) {
+ ASSERT(lastLeafInserted());
+ return NodeTraversal::next(*lastLeafInserted());
+ }
+ return nullptr;
+ }
private:
RefPtr<Node> m_firstNodeInserted;
@@ -89,7 +100,7 @@ private:
void removeRedundantStylesAndKeepStyleSpanInline(InsertedNodes&);
void makeInsertedContentRoundTrippableWithHTMLTreeBuilder(InsertedNodes&);
- void moveNodeOutOfAncestor(PassRefPtr<Node>, PassRefPtr<Node> ancestor);
+ void moveNodeOutOfAncestor(PassRefPtr<Node>, PassRefPtr<Node> ancestor, InsertedNodes&);
void handleStyleSpans(InsertedNodes&);
void handlePasteAsQuotationNode();
@@ -101,8 +112,10 @@ private:
void completeHTMLReplacement(const Position& lastPositionToSelect);
void mergeTextNodesAroundPosition(Position&, Position& positionOnlyToBeUpdated);
+ ReplacementFragment* ensureReplacementFragment();
bool performTrivialReplace(const ReplacementFragment&);
+ VisibleSelection m_visibleSelectionForInsertedText;
Position m_startOfInsertedContent;
Position m_endOfInsertedContent;
RefPtr<EditingStyle> m_insertionStyle;
@@ -110,13 +123,14 @@ private:
bool m_smartReplace;
bool m_matchStyle;
RefPtr<DocumentFragment> m_documentFragment;
+ std::unique_ptr<ReplacementFragment> m_replacementFragment;
+ String m_documentFragmentHTMLMarkup;
+ String m_documentFragmentPlainText;
bool m_preventNesting;
bool m_movingParagraph;
- EditAction m_editAction;
bool m_sanitizeFragment;
bool m_shouldMergeEnd;
+ bool m_ignoreMailBlockquote;
};
} // namespace WebCore
-
-#endif // ReplaceSelectionCommand_h
diff --git a/Source/WebCore/editing/SelectionRectGatherer.cpp b/Source/WebCore/editing/SelectionRectGatherer.cpp
new file mode 100644
index 000000000..fd6b24333
--- /dev/null
+++ b/Source/WebCore/editing/SelectionRectGatherer.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 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. AND ITS 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 APPLE INC. OR ITS 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 "SelectionRectGatherer.h"
+
+#if ENABLE(SERVICE_CONTROLS)
+
+#include "Editor.h"
+#include "EditorClient.h"
+#include "Frame.h"
+#include "MainFrame.h"
+#include "RenderView.h"
+#include "ServicesOverlayController.h"
+
+namespace WebCore {
+
+SelectionRectGatherer::SelectionRectGatherer(RenderView& renderView)
+ : m_renderView(renderView)
+ , m_isTextOnly(true)
+{
+}
+
+void SelectionRectGatherer::addRect(RenderLayerModelObject *repaintContainer, const LayoutRect& rect)
+{
+ if (!rect.isEmpty()) {
+ if (repaintContainer)
+ m_rects.append(LayoutRect(repaintContainer->localToAbsoluteQuad(FloatQuad(rect)).boundingBox()));
+ else
+ m_rects.append(rect);
+ }
+}
+
+void SelectionRectGatherer::addGapRects(RenderLayerModelObject *repaintContainer, const GapRects& rects)
+{
+ if (repaintContainer) {
+ GapRects absoluteGapRects;
+ absoluteGapRects.uniteLeft(LayoutRect(repaintContainer->localToAbsoluteQuad(FloatQuad(rects.left())).boundingBox()));
+ absoluteGapRects.uniteCenter(LayoutRect(repaintContainer->localToAbsoluteQuad(FloatQuad(rects.center())).boundingBox()));
+ absoluteGapRects.uniteRight(LayoutRect(repaintContainer->localToAbsoluteQuad(FloatQuad(rects.right())).boundingBox()));
+ m_gapRects.append(absoluteGapRects);
+ } else
+ m_gapRects.append(rects);
+}
+
+SelectionRectGatherer::Notifier::Notifier(SelectionRectGatherer& gatherer)
+ : m_gatherer(gatherer)
+{
+}
+
+SelectionRectGatherer::Notifier::~Notifier()
+{
+ m_gatherer.m_renderView.view().frame().mainFrame().servicesOverlayController().selectionRectsDidChange(m_gatherer.m_rects, m_gatherer.m_gapRects, m_gatherer.isTextOnly());
+}
+
+std::unique_ptr<SelectionRectGatherer::Notifier> SelectionRectGatherer::clearAndCreateNotifier()
+{
+ m_rects.clear();
+ m_gapRects.clear();
+ m_isTextOnly = true;
+
+ return std::make_unique<Notifier>(*this);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SERVICE_CONTROLS)
diff --git a/Source/WebCore/editing/SelectionRectGatherer.h b/Source/WebCore/editing/SelectionRectGatherer.h
new file mode 100644
index 000000000..047cf16f0
--- /dev/null
+++ b/Source/WebCore/editing/SelectionRectGatherer.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 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. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#pragma once
+
+#if ENABLE(SERVICE_CONTROLS)
+
+#include <wtf/Noncopyable.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class LayoutRect;
+class RenderLayerModelObject;
+class RenderView;
+
+struct GapRects;
+
+class SelectionRectGatherer {
+ WTF_MAKE_NONCOPYABLE(SelectionRectGatherer);
+
+public:
+ SelectionRectGatherer(RenderView&);
+
+ void addRect(RenderLayerModelObject *repaintContainer, const LayoutRect&);
+ void addGapRects(RenderLayerModelObject *repaintContainer, const GapRects&);
+ void setTextOnly(bool isTextOnly) { m_isTextOnly = isTextOnly; }
+ bool isTextOnly() const { return m_isTextOnly; }
+
+ class Notifier {
+ WTF_MAKE_NONCOPYABLE(Notifier);
+ public:
+ Notifier(SelectionRectGatherer&);
+ ~Notifier();
+
+ private:
+ SelectionRectGatherer& m_gatherer;
+ };
+
+ std::unique_ptr<Notifier> clearAndCreateNotifier();
+
+private:
+ RenderView& m_renderView;
+
+ // All rects are in RenderView coordinates.
+ Vector<LayoutRect> m_rects;
+ Vector<GapRects> m_gapRects;
+ bool m_isTextOnly;
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SERVICE_CONTROLS)
diff --git a/Source/WebCore/editing/SetNodeAttributeCommand.cpp b/Source/WebCore/editing/SetNodeAttributeCommand.cpp
index 113e209ab..1950f0f58 100644
--- a/Source/WebCore/editing/SetNodeAttributeCommand.cpp
+++ b/Source/WebCore/editing/SetNodeAttributeCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -49,7 +49,7 @@ void SetNodeAttributeCommand::doApply()
void SetNodeAttributeCommand::doUnapply()
{
m_element->setAttribute(m_attribute, m_oldValue);
- AtomicStringImpl* nullString = 0;
+ AtomicStringImpl* nullString = nullptr;
m_oldValue = nullString;
}
diff --git a/Source/WebCore/editing/SetNodeAttributeCommand.h b/Source/WebCore/editing/SetNodeAttributeCommand.h
index acda7c4da..f3ae409be 100644
--- a/Source/WebCore/editing/SetNodeAttributeCommand.h
+++ b/Source/WebCore/editing/SetNodeAttributeCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SetNodeAttributeCommand_h
-#define SetNodeAttributeCommand_h
+#pragma once
#include "EditCommand.h"
#include "QualifiedName.h"
@@ -33,19 +32,19 @@ namespace WebCore {
class SetNodeAttributeCommand : public SimpleEditCommand {
public:
- static PassRefPtr<SetNodeAttributeCommand> create(PassRefPtr<Element> element, const QualifiedName& attribute, const AtomicString& value)
+ static Ref<SetNodeAttributeCommand> create(PassRefPtr<Element> element, const QualifiedName& attribute, const AtomicString& value)
{
- return adoptRef(new SetNodeAttributeCommand(element, attribute, value));
+ return adoptRef(*new SetNodeAttributeCommand(element, attribute, value));
}
private:
SetNodeAttributeCommand(PassRefPtr<Element>, const QualifiedName& attribute, const AtomicString& value);
- virtual void doApply() override;
- virtual void doUnapply() override;
+ void doApply() override;
+ void doUnapply() override;
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
RefPtr<Element> m_element;
@@ -55,5 +54,3 @@ private:
};
} // namespace WebCore
-
-#endif // SetNodeAttributeCommand_h
diff --git a/Source/WebCore/editing/SetSelectionCommand.cpp b/Source/WebCore/editing/SetSelectionCommand.cpp
index f153a8d44..00a4d561d 100644
--- a/Source/WebCore/editing/SetSelectionCommand.cpp
+++ b/Source/WebCore/editing/SetSelectionCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -42,7 +42,7 @@ void SetSelectionCommand::doApply()
{
FrameSelection& selection = frame().selection();
- if (selection.shouldChangeSelection(m_selectionToSet) && m_selectionToSet.isNonOrphanedCaretOrRange()) {
+ if (selection.shouldChangeSelection(m_selectionToSet) && !m_selectionToSet.isNoneOrOrphaned()) {
selection.setSelection(m_selectionToSet, m_options);
setEndingSelection(m_selectionToSet);
}
@@ -52,7 +52,7 @@ void SetSelectionCommand::doUnapply()
{
FrameSelection& selection = frame().selection();
- if (selection.shouldChangeSelection(startingSelection()) && startingSelection().isNonOrphanedCaretOrRange())
+ if (selection.shouldChangeSelection(startingSelection()) && !startingSelection().isNoneOrOrphaned())
selection.setSelection(startingSelection(), m_options);
}
diff --git a/Source/WebCore/editing/SetSelectionCommand.h b/Source/WebCore/editing/SetSelectionCommand.h
index 229eb54f7..897c4d9ac 100644
--- a/Source/WebCore/editing/SetSelectionCommand.h
+++ b/Source/WebCore/editing/SetSelectionCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SetSelectionCommand_h
-#define SetSelectionCommand_h
+#pragma once
#include "EditCommand.h"
#include "FrameSelection.h"
@@ -33,19 +32,19 @@ namespace WebCore {
class SetSelectionCommand : public SimpleEditCommand {
public:
- static PassRefPtr<SetSelectionCommand> create(const VisibleSelection& selection, FrameSelection::SetSelectionOptions options)
+ static Ref<SetSelectionCommand> create(const VisibleSelection& selection, FrameSelection::SetSelectionOptions options)
{
- return adoptRef(new SetSelectionCommand(selection, options));
+ return adoptRef(*new SetSelectionCommand(selection, options));
}
private:
SetSelectionCommand(const VisibleSelection&, FrameSelection::SetSelectionOptions);
- virtual void doApply() override;
- virtual void doUnapply() override;
+ void doApply() override;
+ void doUnapply() override;
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override { }
+ void getNodesInCommand(HashSet<Node*>&) override { }
#endif
FrameSelection::SetSelectionOptions m_options;
@@ -53,5 +52,3 @@ private:
};
} // namespace WebCore
-
-#endif // SetSelectionCommand_h
diff --git a/Source/WebCore/editing/SimplifyMarkupCommand.cpp b/Source/WebCore/editing/SimplifyMarkupCommand.cpp
index 4106d8f6c..31570a5de 100644
--- a/Source/WebCore/editing/SimplifyMarkupCommand.cpp
+++ b/Source/WebCore/editing/SimplifyMarkupCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple Computer, Inc. All rights reserved.
+ * 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
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -52,16 +52,16 @@ void SimplifyMarkupCommand::doApply()
// 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 = NodeTraversal::next(node)) {
+ for (Node* node = m_firstNode.get(); node && node != m_nodeAfterLast; node = NodeTraversal::next(*node)) {
if (node->firstChild() || (node->isTextNode() && node->nextSibling()))
continue;
Node* startingNode = node->parentNode();
- RenderStyle* startingStyle = startingNode->renderStyle();
+ auto* startingStyle = startingNode->renderStyle();
if (!startingStyle)
continue;
Node* currentNode = startingNode;
- Node* topNodeWithStartingStyle = 0;
+ Node* topNodeWithStartingStyle = nullptr;
while (currentNode != rootNode) {
if (currentNode->parentNode() != rootNode && isRemovableBlock(currentNode))
nodesToRemove.append(currentNode);
@@ -70,16 +70,17 @@ void SimplifyMarkupCommand::doApply()
if (!currentNode)
break;
- if (!currentNode->renderer() || !currentNode->renderer()->isRenderInline() || toRenderInline(currentNode->renderer())->alwaysCreateLineBoxes())
+ auto* renderer = currentNode->renderer();
+ if (!is<RenderInline>(renderer) || downcast<RenderInline>(*renderer).alwaysCreateLineBoxes())
continue;
if (currentNode->firstChild() != currentNode->lastChild()) {
- topNodeWithStartingStyle = 0;
+ topNodeWithStartingStyle = nullptr;
break;
}
unsigned context;
- if (currentNode->renderStyle()->diff(startingStyle, context) == StyleDifferenceEqual)
+ if (currentNode->renderStyle()->diff(*startingStyle, context) == StyleDifferenceEqual)
topNodeWithStartingStyle = currentNode;
}
diff --git a/Source/WebCore/editing/SimplifyMarkupCommand.h b/Source/WebCore/editing/SimplifyMarkupCommand.h
index f1c5deea9..2d8257a45 100644
--- a/Source/WebCore/editing/SimplifyMarkupCommand.h
+++ b/Source/WebCore/editing/SimplifyMarkupCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SimplifyMarkupCommand_h
-#define SimplifyMarkupCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -32,15 +31,15 @@ namespace WebCore {
class SimplifyMarkupCommand : public CompositeEditCommand {
public:
- static PassRefPtr<SimplifyMarkupCommand> create(Document& document, Node* firstNode, Node* nodeAfterLast)
+ static Ref<SimplifyMarkupCommand> create(Document& document, Node* firstNode, Node* nodeAfterLast)
{
- return adoptRef(new SimplifyMarkupCommand(document, firstNode, nodeAfterLast));
+ return adoptRef(*new SimplifyMarkupCommand(document, firstNode, nodeAfterLast));
}
private:
SimplifyMarkupCommand(Document&, Node* firstNode, Node* nodeAfterLast);
- virtual void doApply();
+ void doApply() override;
int pruneSubsequentAncestorsToRemove(Vector<RefPtr<Node>>& nodesToRemove, size_t startNodeIndex);
RefPtr<Node> m_firstNode;
@@ -48,5 +47,3 @@ private:
};
} // namespace WebCore
-
-#endif // SimplifyMarkupCommand_h
diff --git a/Source/WebCore/editing/SmartReplace.cpp b/Source/WebCore/editing/SmartReplace.cpp
index 034dbfebb..0b4764cc0 100644
--- a/Source/WebCore/editing/SmartReplace.cpp
+++ b/Source/WebCore/editing/SmartReplace.cpp
@@ -11,7 +11,7 @@
* 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.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -34,15 +34,14 @@
#include <unicode/uset.h>
#include <wtf/Assertions.h>
-#include <wtf/text/WTFString.h>
+#include <wtf/text/StringView.h>
namespace WebCore {
static void addAllCodePoints(USet* smartSet, const String& string)
{
- const UChar* characters = string.characters();
for (size_t i = 0; i < string.length(); i++)
- uset_add(smartSet, characters[i]);
+ uset_add(smartSet, string[i]);
}
// This is mostly a port of the code in WebCore/editing/SmartReplaceCF.cpp
@@ -56,7 +55,7 @@ static USet* getSmartSet(bool isPreviousCharacter)
// Whitespace and newline (kCFCharacterSetWhitespaceAndNewline)
UErrorCode ec = U_ZERO_ERROR;
String whitespaceAndNewline = ASCIILiteral("[[:WSpace:] [\\u000A\\u000B\\u000C\\u000D\\u0085]]");
- smartSet = uset_openPattern(whitespaceAndNewline.characters(), whitespaceAndNewline.length(), &ec);
+ smartSet = uset_openPattern(StringView(whitespaceAndNewline).upconvertedCharacters(), whitespaceAndNewline.length(), &ec);
ASSERT(U_SUCCESS(ec));
// CJK ranges
@@ -83,7 +82,7 @@ static USet* getSmartSet(bool isPreviousCharacter)
// Punctuation (kCFCharacterSetPunctuation)
UErrorCode ec = U_ZERO_ERROR;
String punctuationClass = ASCIILiteral("[:P:]");
- USet* icuPunct = uset_openPattern(punctuationClass.characters(), punctuationClass.length(), &ec);
+ USet* icuPunct = uset_openPattern(StringView(punctuationClass).upconvertedCharacters(), punctuationClass.length(), &ec);
ASSERT(U_SUCCESS(ec));
uset_addAll(smartSet, icuPunct);
uset_close(icuPunct);
diff --git a/Source/WebCore/editing/SmartReplace.h b/Source/WebCore/editing/SmartReplace.h
index b072e58d7..dd2586753 100644
--- a/Source/WebCore/editing/SmartReplace.h
+++ b/Source/WebCore/editing/SmartReplace.h
@@ -10,7 +10,7 @@
* 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.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -26,15 +26,12 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SmartReplace_h
-#define SmartReplace_h
+#pragma once
-#include <wtf/unicode/Unicode.h>
+#include <unicode/utypes.h>
namespace WebCore {
-bool isCharacterSmartReplaceExempt(UChar32 c, bool isPreviousCharacter);
+WEBCORE_EXPORT bool isCharacterSmartReplaceExempt(UChar32 c, bool isPreviousCharacter);
} // namespace WebCore
-
-#endif // SmartReplace_h
diff --git a/Source/WebCore/editing/SmartReplaceCF.cpp b/Source/WebCore/editing/SmartReplaceCF.cpp
new file mode 100644
index 000000000..85e9bc4aa
--- /dev/null
+++ b/Source/WebCore/editing/SmartReplaceCF.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2007 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.
+ * 3. Neither the name of Apple Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "SmartReplace.h"
+
+#include <CoreFoundation/CFCharacterSet.h>
+#include <CoreFoundation/CFString.h>
+
+namespace WebCore {
+
+static CFMutableCharacterSetRef getSmartSet(bool isPreviousCharacter)
+{
+ static CFMutableCharacterSetRef preSmartSet;
+ static CFMutableCharacterSetRef postSmartSet;
+ CFMutableCharacterSetRef smartSet = isPreviousCharacter ? preSmartSet : postSmartSet;
+ if (!smartSet) {
+ smartSet = CFCharacterSetCreateMutable(kCFAllocatorDefault);
+ CFCharacterSetAddCharactersInString(smartSet, isPreviousCharacter ? CFSTR("([\"\'#$/-`{") : CFSTR(")].,;:?\'!\"%*-/}"));
+ CFCharacterSetUnion(smartSet, CFCharacterSetGetPredefined(kCFCharacterSetWhitespaceAndNewline));
+ // Adding CJK ranges
+ CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x1100, 256)); // Hangul Jamo (0x1100 - 0x11FF)
+ CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x2E80, 352)); // CJK & Kangxi Radicals (0x2E80 - 0x2FDF)
+ CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x2FF0, 464)); // Ideograph Descriptions, CJK Symbols, Hiragana, Katakana, Bopomofo, Hangul Compatibility Jamo, Kanbun, & Bopomofo Ext (0x2FF0 - 0x31BF)
+ CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x3200, 29392)); // Enclosed CJK, CJK Ideographs (Uni Han & Ext A), & Yi (0x3200 - 0xA4CF)
+ CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0xAC00, 11183)); // Hangul Syllables (0xAC00 - 0xD7AF)
+ CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0xF900, 352)); // CJK Compatibility Ideographs (0xF900 - 0xFA5F)
+ CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0xFE30, 32)); // CJK Compatibility From (0xFE30 - 0xFE4F)
+ CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0xFF00, 240)); // Half/Full Width Form (0xFF00 - 0xFFEF)
+ CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x20000, 0xA6D7)); // CJK Ideograph Exntension B
+ CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x2F800, 0x021E)); // CJK Compatibility Ideographs (0x2F800 - 0x2FA1D)
+
+ if (isPreviousCharacter)
+ preSmartSet = smartSet;
+ else {
+ CFCharacterSetUnion(smartSet, CFCharacterSetGetPredefined(kCFCharacterSetPunctuation));
+ postSmartSet = smartSet;
+ }
+ }
+ return smartSet;
+}
+
+bool isCharacterSmartReplaceExempt(UChar32 c, bool isPreviousCharacter)
+{
+ return CFCharacterSetIsLongCharacterMember(getSmartSet(isPreviousCharacter), c);
+}
+
+}
diff --git a/Source/WebCore/editing/SpellChecker.cpp b/Source/WebCore/editing/SpellChecker.cpp
index d2e4b82ab..7cb6237a4 100644
--- a/Source/WebCore/editing/SpellChecker.cpp
+++ b/Source/WebCore/editing/SpellChecker.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -30,9 +30,6 @@
#include "DocumentMarkerController.h"
#include "Editor.h"
#include "Frame.h"
-#include "HTMLInputElement.h"
-#include "HTMLTextAreaElement.h"
-#include "Node.h"
#include "Page.h"
#include "PositionIterator.h"
#include "RenderObject.h"
@@ -44,10 +41,9 @@
namespace WebCore {
SpellCheckRequest::SpellCheckRequest(PassRefPtr<Range> checkingRange, PassRefPtr<Range> paragraphRange, const String& text, TextCheckingTypeMask mask, TextCheckingProcessType processType)
- : m_checker(0)
- , m_checkingRange(checkingRange)
+ : m_checkingRange(checkingRange)
, m_paragraphRange(paragraphRange)
- , m_rootEditableElement(m_checkingRange->startContainer()->rootEditableElement())
+ , m_rootEditableElement(m_checkingRange->startContainer().rootEditableElement())
, m_requestData(unrequestedTextCheckingSequence, text, mask, processType)
{
}
@@ -57,16 +53,16 @@ SpellCheckRequest::~SpellCheckRequest()
}
// static
-PassRefPtr<SpellCheckRequest> SpellCheckRequest::create(TextCheckingTypeMask textCheckingOptions, TextCheckingProcessType processType, PassRefPtr<Range> checkingRange, PassRefPtr<Range> paragraphRange)
+RefPtr<SpellCheckRequest> SpellCheckRequest::create(TextCheckingTypeMask textCheckingOptions, TextCheckingProcessType processType, PassRefPtr<Range> checkingRange, PassRefPtr<Range> paragraphRange)
{
ASSERT(checkingRange);
ASSERT(paragraphRange);
String text = checkingRange->text();
if (!text.length())
- return PassRefPtr<SpellCheckRequest>();
+ return nullptr;
- return adoptRef(new SpellCheckRequest(checkingRange, paragraphRange, text, textCheckingOptions, processType));
+ return adoptRef(*new SpellCheckRequest(checkingRange, paragraphRange, text, textCheckingOptions, processType));
}
const TextCheckingRequestData& SpellCheckRequest::data() const
@@ -78,16 +74,20 @@ void SpellCheckRequest::didSucceed(const Vector<TextCheckingResult>& results)
{
if (!m_checker)
return;
+
+ Ref<SpellCheckRequest> protectedThis(*this);
m_checker->didCheckSucceed(m_requestData.sequence(), results);
- m_checker = 0;
+ m_checker = nullptr;
}
void SpellCheckRequest::didCancel()
{
if (!m_checker)
return;
+
+ Ref<SpellCheckRequest> protectedThis(*this);
m_checker->didCheckCancel(m_requestData.sequence());
- m_checker = 0;
+ m_checker = nullptr;
}
void SpellCheckRequest::setCheckerAndSequence(SpellChecker* requester, int sequence)
@@ -100,14 +100,14 @@ void SpellCheckRequest::setCheckerAndSequence(SpellChecker* requester, int seque
void SpellCheckRequest::requesterDestroyed()
{
- m_checker = 0;
+ m_checker = nullptr;
}
SpellChecker::SpellChecker(Frame& frame)
: m_frame(frame)
, m_lastRequestSequence(0)
, m_lastProcessedSequence(0)
- , m_timerToProcessQueuedRequest(this, &SpellChecker::timerFiredToProcessQueuedRequest)
+ , m_timerToProcessQueuedRequest(*this, &SpellChecker::timerFiredToProcessQueuedRequest)
{
}
@@ -115,19 +115,19 @@ SpellChecker::~SpellChecker()
{
if (m_processingRequest)
m_processingRequest->requesterDestroyed();
- for (RequestQueue::iterator i = m_requestQueue.begin(); i != m_requestQueue.end(); ++i)
- (*i)->requesterDestroyed();
+ for (auto& queue : m_requestQueue)
+ queue->requesterDestroyed();
}
TextCheckerClient* SpellChecker::client() const
{
Page* page = m_frame.page();
if (!page)
- return 0;
- return page->editorClient()->textChecker();
+ return nullptr;
+ return page->editorClient().textChecker();
}
-void SpellChecker::timerFiredToProcessQueuedRequest(Timer<SpellChecker>*)
+void SpellChecker::timerFiredToProcessQueuedRequest()
{
ASSERT(!m_requestQueue.isEmpty());
if (m_requestQueue.isEmpty())
@@ -150,8 +150,8 @@ bool SpellChecker::isCheckable(Range* range) const
{
if (!range || !range->firstNode() || !range->firstNode()->renderer())
return false;
- const Node* node = range->startContainer();
- if (node && node->isElementNode() && !toElement(node)->isSpellCheckingEnabled())
+ const Node& node = range->startContainer();
+ if (is<Element>(node) && !downcast<Element>(node).isSpellCheckingEnabled())
return false;
return true;
}
@@ -182,18 +182,18 @@ void SpellChecker::invokeRequest(PassRefPtr<SpellCheckRequest> request)
if (!client())
return;
m_processingRequest = request;
- client()->requestCheckingOfString(m_processingRequest);
+ client()->requestCheckingOfString(*m_processingRequest, m_frame.selection().selection());
}
void SpellChecker::enqueueRequest(PassRefPtr<SpellCheckRequest> request)
{
ASSERT(request);
- for (RequestQueue::iterator it = m_requestQueue.begin(); it != m_requestQueue.end(); ++it) {
- if (request->rootEditableElement() != (*it)->rootEditableElement())
+ for (auto& queue : m_requestQueue) {
+ if (request->rootEditableElement() != queue->rootEditableElement())
continue;
- *it = request;
+ queue = request;
return;
}
@@ -214,7 +214,7 @@ void SpellChecker::didCheck(int sequence, const Vector<TextCheckingResult>& resu
if (m_lastProcessedSequence < sequence)
m_lastProcessedSequence = sequence;
- m_processingRequest.clear();
+ m_processingRequest = nullptr;
if (!m_requestQueue.isEmpty())
m_timerToProcessQueuedRequest.startOneShot(0);
}
@@ -236,8 +236,7 @@ void SpellChecker::didCheckSucceed(int sequence, const Vector<TextCheckingResult
void SpellChecker::didCheckCancel(int sequence)
{
- Vector<TextCheckingResult> results;
- didCheck(sequence, results);
+ didCheck(sequence, Vector<TextCheckingResult>());
}
} // namespace WebCore
diff --git a/Source/WebCore/editing/SpellChecker.h b/Source/WebCore/editing/SpellChecker.h
index a0c3a247d..049d1c51f 100644
--- a/Source/WebCore/editing/SpellChecker.h
+++ b/Source/WebCore/editing/SpellChecker.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SpellChecker_h
-#define SpellChecker_h
+#pragma once
#include "Element.h"
#include "Range.h"
@@ -45,7 +44,7 @@ class SpellChecker;
class SpellCheckRequest : public TextCheckingRequest {
public:
- static PassRefPtr<SpellCheckRequest> create(TextCheckingTypeMask, TextCheckingProcessType, PassRefPtr<Range> checkingRange, PassRefPtr<Range> paragraphRange);
+ static RefPtr<SpellCheckRequest> create(TextCheckingTypeMask, TextCheckingProcessType, PassRefPtr<Range> checkingRange, PassRefPtr<Range> paragraphRange);
virtual ~SpellCheckRequest();
PassRefPtr<Range> checkingRange() const { return m_checkingRange; }
@@ -56,14 +55,14 @@ public:
void requesterDestroyed();
bool isStarted() const { return m_checker; }
- virtual const TextCheckingRequestData& data() const override;
- virtual void didSucceed(const Vector<TextCheckingResult>&) override;
- virtual void didCancel() override;
+ const TextCheckingRequestData& data() const override;
+ void didSucceed(const Vector<TextCheckingResult>&) override;
+ void didCancel() override;
private:
SpellCheckRequest(PassRefPtr<Range> checkingRange, PassRefPtr<Range> paragraphRange, const String&, TextCheckingTypeMask, TextCheckingProcessType);
- SpellChecker* m_checker;
+ SpellChecker* m_checker { nullptr };
RefPtr<Range> m_checkingRange;
RefPtr<Range> m_paragraphRange;
RefPtr<Element> m_rootEditableElement;
@@ -98,7 +97,7 @@ private:
bool canCheckAsynchronously(Range*) const;
TextCheckerClient* client() const;
- void timerFiredToProcessQueuedRequest(Timer<SpellChecker>*);
+ void timerFiredToProcessQueuedRequest();
void invokeRequest(PassRefPtr<SpellCheckRequest>);
void enqueueRequest(PassRefPtr<SpellCheckRequest>);
void didCheckSucceed(int sequence, const Vector<TextCheckingResult>&);
@@ -109,12 +108,10 @@ private:
int m_lastRequestSequence;
int m_lastProcessedSequence;
- Timer<SpellChecker> m_timerToProcessQueuedRequest;
+ Timer m_timerToProcessQueuedRequest;
RefPtr<SpellCheckRequest> m_processingRequest;
RequestQueue m_requestQueue;
};
} // namespace WebCore
-
-#endif // SpellChecker_h
diff --git a/Source/WebCore/editing/SpellingCorrectionCommand.cpp b/Source/WebCore/editing/SpellingCorrectionCommand.cpp
index c0ba961bd..c9bf2c099 100644
--- a/Source/WebCore/editing/SpellingCorrectionCommand.cpp
+++ b/Source/WebCore/editing/SpellingCorrectionCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -27,12 +27,15 @@
#include "SpellingCorrectionCommand.h"
#include "AlternativeTextController.h"
+#include "DataTransfer.h"
#include "Document.h"
#include "DocumentFragment.h"
#include "Editor.h"
#include "Frame.h"
#include "ReplaceSelectionCommand.h"
#include "SetSelectionCommand.h"
+#include "StaticRange.h"
+#include "TextIterator.h"
#include "markup.h"
namespace WebCore {
@@ -42,9 +45,9 @@ namespace WebCore {
// This information is needed by spell checking service to update user specific data.
class SpellingCorrectionRecordUndoCommand : public SimpleEditCommand {
public:
- static PassRefPtr<SpellingCorrectionRecordUndoCommand> create(Document& document, const String& corrected, const String& correction)
+ static Ref<SpellingCorrectionRecordUndoCommand> create(Document& document, const String& corrected, const String& correction)
{
- return adoptRef(new SpellingCorrectionRecordUndoCommand(document, corrected, correction));
+ return adoptRef(*new SpellingCorrectionRecordUndoCommand(document, corrected, correction));
}
private:
SpellingCorrectionRecordUndoCommand(Document& document, const String& corrected, const String& correction)
@@ -55,11 +58,11 @@ private:
{
}
- virtual void doApply() override
+ void doApply() override
{
}
- virtual void doUnapply() override
+ void doUnapply() override
{
if (!m_hasBeenUndone) {
frame().editor().unappliedSpellCorrection(startingSelection(), m_corrected, m_correction);
@@ -69,7 +72,7 @@ private:
}
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override
+ void getNodesInCommand(HashSet<Node*>&) override
{
}
#endif
@@ -81,13 +84,19 @@ private:
#endif
SpellingCorrectionCommand::SpellingCorrectionCommand(PassRefPtr<Range> rangeToBeCorrected, const String& correction)
- : CompositeEditCommand(rangeToBeCorrected->startContainer()->document())
+ : CompositeEditCommand(rangeToBeCorrected->startContainer().document(), EditActionInsertReplacement)
, m_rangeToBeCorrected(rangeToBeCorrected)
- , m_selectionToBeCorrected(m_rangeToBeCorrected.get())
+ , m_selectionToBeCorrected(*m_rangeToBeCorrected)
, m_correction(correction)
{
}
+bool SpellingCorrectionCommand::willApplyCommand()
+{
+ m_correctionFragment = createFragmentFromText(*m_rangeToBeCorrected, m_correction);
+ return CompositeEditCommand::willApplyCommand();
+}
+
void SpellingCorrectionCommand::doApply()
{
m_corrected = plainText(m_rangeToBeCorrected.get());
@@ -100,16 +109,34 @@ void SpellingCorrectionCommand::doApply()
if (!m_rangeToBeCorrected)
return;
- RefPtr<DocumentFragment> fragment = createFragmentFromText(*m_rangeToBeCorrected, m_correction);
- if (!fragment)
- return;
-
- applyCommandToComposite(SetSelectionCommand::create(m_selectionToBeCorrected, FrameSelection::SpellCorrectionTriggered | FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle));
+ applyCommandToComposite(SetSelectionCommand::create(m_selectionToBeCorrected, FrameSelection::defaultSetSelectionOptions() | FrameSelection::SpellCorrectionTriggered));
#if USE(AUTOCORRECTION_PANEL)
applyCommandToComposite(SpellingCorrectionRecordUndoCommand::create(document(), m_corrected, m_correction));
#endif
- applyCommandToComposite(ReplaceSelectionCommand::create(document(), fragment.release(), ReplaceSelectionCommand::MatchStyle, EditActionPaste));
+ applyCommandToComposite(ReplaceSelectionCommand::create(document(), WTFMove(m_correctionFragment), ReplaceSelectionCommand::MatchStyle, EditActionPaste));
+}
+
+String SpellingCorrectionCommand::inputEventData() const
+{
+ if (isEditingTextAreaOrTextInput())
+ return m_correction;
+
+ return CompositeEditCommand::inputEventData();
+}
+
+Vector<RefPtr<StaticRange>> SpellingCorrectionCommand::targetRanges() const
+{
+ RefPtr<StaticRange> range = StaticRange::createFromRange(*m_rangeToBeCorrected);
+ return { 1, range };
+}
+
+RefPtr<DataTransfer> SpellingCorrectionCommand::inputEventDataTransfer() const
+{
+ if (!isEditingTextAreaOrTextInput())
+ return DataTransfer::createForInputEvent(m_correction, createMarkup(*m_correctionFragment));
+
+ return CompositeEditCommand::inputEventDataTransfer();
}
bool SpellingCorrectionCommand::shouldRetainAutocorrectionIndicator() const
diff --git a/Source/WebCore/editing/SpellingCorrectionCommand.h b/Source/WebCore/editing/SpellingCorrectionCommand.h
index 5d170f47b..6c439a7a5 100644
--- a/Source/WebCore/editing/SpellingCorrectionCommand.h
+++ b/Source/WebCore/editing/SpellingCorrectionCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SpellingCorrectionCommand_h
-#define SpellingCorrectionCommand_h
+#pragma once
#include "CompositeEditCommand.h"
#include "Range.h"
@@ -33,21 +32,25 @@ namespace WebCore {
class SpellingCorrectionCommand : public CompositeEditCommand {
public:
- static PassRefPtr<SpellingCorrectionCommand> create(PassRefPtr<Range> rangeToBeCorrected, const String& correction)
+ static Ref<SpellingCorrectionCommand> create(PassRefPtr<Range> rangeToBeCorrected, const String& correction)
{
- return adoptRef(new SpellingCorrectionCommand(rangeToBeCorrected, correction));
+ return adoptRef(*new SpellingCorrectionCommand(rangeToBeCorrected, correction));
}
private:
SpellingCorrectionCommand(PassRefPtr<Range> rangeToBeCorrected, const String& correction);
- virtual void doApply() override;
- virtual bool shouldRetainAutocorrectionIndicator() const override;
+ bool willApplyCommand() final;
+ void doApply() override;
+ bool shouldRetainAutocorrectionIndicator() const override;
+
+ String inputEventData() const final;
+ Vector<RefPtr<StaticRange>> targetRanges() const final;
+ RefPtr<DataTransfer> inputEventDataTransfer() const final;
RefPtr<Range> m_rangeToBeCorrected;
VisibleSelection m_selectionToBeCorrected;
+ RefPtr<DocumentFragment> m_correctionFragment;
String m_corrected;
String m_correction;
};
} // namespace WebCore
-
-#endif // SpellingCorrectionCommand_h
diff --git a/Source/WebCore/editing/SplitElementCommand.cpp b/Source/WebCore/editing/SplitElementCommand.cpp
index 6ad8f1921..2d4e85498 100644
--- a/Source/WebCore/editing/SplitElementCommand.cpp
+++ b/Source/WebCore/editing/SplitElementCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -47,30 +47,26 @@ void SplitElementCommand::executeApply()
if (m_atChild->parentNode() != m_element2)
return;
- Vector<RefPtr<Node>> children;
+ Vector<Ref<Node>> children;
for (Node* node = m_element2->firstChild(); node != m_atChild; node = node->nextSibling())
- children.append(node);
-
- ExceptionCode ec = 0;
-
- ContainerNode* parent = m_element2->parentNode();
+ children.append(*node);
+
+ auto* parent = m_element2->parentNode();
if (!parent || !parent->hasEditableStyle())
return;
- parent->insertBefore(m_element1.get(), m_element2.get(), ec);
- if (ec)
+ if (parent->insertBefore(*m_element1, m_element2.get()).hasException())
return;
// Delete id attribute from the second element because the same id cannot be used for more than one element
m_element2->removeAttribute(HTMLNames::idAttr);
- size_t size = children.size();
- for (size_t i = 0; i < size; ++i)
- m_element1->appendChild(children[i], ec);
+ for (auto& child : children)
+ m_element1->appendChild(child);
}
void SplitElementCommand::doApply()
{
- m_element1 = m_element2->cloneElementWithoutChildren();
+ m_element1 = m_element2->cloneElementWithoutChildren(document());
executeApply();
}
@@ -80,21 +76,21 @@ void SplitElementCommand::doUnapply()
if (!m_element1 || !m_element1->hasEditableStyle() || !m_element2->hasEditableStyle())
return;
- Vector<RefPtr<Node>> children;
+ Vector<Ref<Node>> children;
for (Node* node = m_element1->firstChild(); node; node = node->nextSibling())
- children.append(node);
+ children.append(*node);
RefPtr<Node> refChild = m_element2->firstChild();
- size_t size = children.size();
- for (size_t i = 0; i < size; ++i)
- m_element2->insertBefore(children[i].get(), refChild.get(), IGNORE_EXCEPTION);
+ for (auto& child : children)
+ m_element2->insertBefore(child, refChild.get());
// Recover the id attribute of the original element.
- if (m_element1->hasAttribute(HTMLNames::idAttr))
- m_element2->setAttribute(HTMLNames::idAttr, m_element1->getAttribute(HTMLNames::idAttr));
+ const AtomicString& id = m_element1->getIdAttribute();
+ if (!id.isNull())
+ m_element2->setIdAttribute(id);
- m_element1->remove(IGNORE_EXCEPTION);
+ m_element1->remove();
}
void SplitElementCommand::doReapply()
diff --git a/Source/WebCore/editing/SplitElementCommand.h b/Source/WebCore/editing/SplitElementCommand.h
index 4f863fee2..ca1d0b2bc 100644
--- a/Source/WebCore/editing/SplitElementCommand.h
+++ b/Source/WebCore/editing/SplitElementCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SplitElementCommand_h
-#define SplitElementCommand_h
+#pragma once
#include "EditCommand.h"
@@ -32,21 +31,21 @@ namespace WebCore {
class SplitElementCommand : public SimpleEditCommand {
public:
- static PassRefPtr<SplitElementCommand> create(PassRefPtr<Element> element, PassRefPtr<Node> splitPointChild)
+ static Ref<SplitElementCommand> create(PassRefPtr<Element> element, PassRefPtr<Node> splitPointChild)
{
- return adoptRef(new SplitElementCommand(element, splitPointChild));
+ return adoptRef(*new SplitElementCommand(element, splitPointChild));
}
private:
SplitElementCommand(PassRefPtr<Element>, PassRefPtr<Node> splitPointChild);
- virtual void doApply() override;
- virtual void doUnapply() override;
- virtual void doReapply() override;
+ void doApply() override;
+ void doUnapply() override;
+ void doReapply() override;
void executeApply();
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
RefPtr<Element> m_element1;
@@ -55,5 +54,3 @@ private:
};
} // namespace WebCore
-
-#endif // SplitElementCommand_h
diff --git a/Source/WebCore/editing/SplitTextNodeCommand.cpp b/Source/WebCore/editing/SplitTextNodeCommand.cpp
index e7108f514..b0a4b21f9 100644
--- a/Source/WebCore/editing/SplitTextNodeCommand.cpp
+++ b/Source/WebCore/editing/SplitTextNodeCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -54,11 +54,14 @@ void SplitTextNodeCommand::doApply()
if (!parent || !parent->hasEditableStyle())
return;
- String prefixText = m_text2->substringData(0, m_offset, IGNORE_EXCEPTION);
+ auto result = m_text2->substringData(0, m_offset);
+ if (result.hasException())
+ return;
+ auto prefixText = result.releaseReturnValue();
if (prefixText.isEmpty())
return;
- m_text1 = Text::create(document(), prefixText);
+ m_text1 = Text::create(document(), WTFMove(prefixText));
ASSERT(m_text1);
document().markers().copyMarkers(m_text2.get(), 0, m_offset, m_text1.get(), 0);
@@ -74,10 +77,10 @@ void SplitTextNodeCommand::doUnapply()
String prefixText = m_text1->data();
- m_text2->insertData(0, prefixText, ASSERT_NO_EXCEPTION);
+ m_text2->insertData(0, prefixText);
document().markers().copyMarkers(m_text1.get(), 0, prefixText.length(), m_text2.get(), 0);
- m_text1->remove(ASSERT_NO_EXCEPTION);
+ m_text1->remove();
}
void SplitTextNodeCommand::doReapply()
@@ -94,19 +97,19 @@ void SplitTextNodeCommand::doReapply()
void SplitTextNodeCommand::insertText1AndTrimText2()
{
- ExceptionCode ec = 0;
- m_text2->parentNode()->insertBefore(m_text1.get(), m_text2.get(), ec);
- if (ec)
+ if (m_text2->parentNode()->insertBefore(*m_text1, m_text2.get()).hasException())
return;
- m_text2->deleteData(0, m_offset, ec);
+ m_text2->deleteData(0, m_offset);
}
#ifndef NDEBUG
+
void SplitTextNodeCommand::getNodesInCommand(HashSet<Node*>& nodes)
{
addNodeAndDescendants(m_text1.get(), nodes);
addNodeAndDescendants(m_text2.get(), nodes);
}
+
#endif
} // namespace WebCore
diff --git a/Source/WebCore/editing/SplitTextNodeCommand.h b/Source/WebCore/editing/SplitTextNodeCommand.h
index 282945551..567479e15 100644
--- a/Source/WebCore/editing/SplitTextNodeCommand.h
+++ b/Source/WebCore/editing/SplitTextNodeCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SplitTextNodeCommand_h
-#define SplitTextNodeCommand_h
+#pragma once
#include "EditCommand.h"
@@ -34,21 +33,21 @@ class Text;
class SplitTextNodeCommand : public SimpleEditCommand {
public:
- static PassRefPtr<SplitTextNodeCommand> create(PassRefPtr<Text> node, int offset)
+ static Ref<SplitTextNodeCommand> create(PassRefPtr<Text> node, int offset)
{
- return adoptRef(new SplitTextNodeCommand(node, offset));
+ return adoptRef(*new SplitTextNodeCommand(node, offset));
}
private:
SplitTextNodeCommand(PassRefPtr<Text>, int offset);
- virtual void doApply() override;
- virtual void doUnapply() override;
- virtual void doReapply() override;
+ void doApply() override;
+ void doUnapply() override;
+ void doReapply() override;
void insertText1AndTrimText2();
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
RefPtr<Text> m_text1;
@@ -57,5 +56,3 @@ private:
};
} // namespace WebCore
-
-#endif // SplitTextNodeCommand_h
diff --git a/Source/WebCore/editing/SplitTextNodeContainingElementCommand.cpp b/Source/WebCore/editing/SplitTextNodeContainingElementCommand.cpp
index 33ab429cc..ce8887469 100644
--- a/Source/WebCore/editing/SplitTextNodeContainingElementCommand.cpp
+++ b/Source/WebCore/editing/SplitTextNodeContainingElementCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -55,11 +55,11 @@ void SplitTextNodeContainingElementCommand::doApply()
RenderElement* parentRenderer = parent->renderer();
if (!parentRenderer || !parentRenderer->isInline()) {
- wrapContentsInDummySpan(parent);
+ wrapContentsInDummySpan(*parent);
Node* firstChild = parent->firstChild();
- if (!firstChild || !firstChild->isElementNode())
+ if (!is<Element>(firstChild))
return;
- parent = toElement(firstChild);
+ parent = downcast<Element>(firstChild);
}
splitElement(parent, m_text);
diff --git a/Source/WebCore/editing/SplitTextNodeContainingElementCommand.h b/Source/WebCore/editing/SplitTextNodeContainingElementCommand.h
index 4e6af4f73..5e69e12f0 100644
--- a/Source/WebCore/editing/SplitTextNodeContainingElementCommand.h
+++ b/Source/WebCore/editing/SplitTextNodeContainingElementCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef SplitTextNodeContainingElementCommand_h
-#define SplitTextNodeContainingElementCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -32,20 +31,18 @@ namespace WebCore {
class SplitTextNodeContainingElementCommand : public CompositeEditCommand {
public:
- static PassRefPtr<SplitTextNodeContainingElementCommand> create(PassRefPtr<Text> node, int offset)
+ static Ref<SplitTextNodeContainingElementCommand> create(PassRefPtr<Text> node, int offset)
{
- return adoptRef(new SplitTextNodeContainingElementCommand(node, offset));
+ return adoptRef(*new SplitTextNodeContainingElementCommand(node, offset));
}
private:
SplitTextNodeContainingElementCommand(PassRefPtr<Text>, int offset);
- virtual void doApply();
+ void doApply() override;
RefPtr<Text> m_text;
int m_offset;
};
} // namespace WebCore
-
-#endif // SplitTextNodeContainingElementCommand_h
diff --git a/Source/WebCore/editing/SurroundingText.h b/Source/WebCore/editing/SurroundingText.h
new file mode 100644
index 000000000..bd6e36650
--- /dev/null
+++ b/Source/WebCore/editing/SurroundingText.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <wtf/text/WTFString.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
diff --git a/Source/WebCore/editing/TextAffinity.h b/Source/WebCore/editing/TextAffinity.h
index 90e665d3b..940edca45 100644
--- a/Source/WebCore/editing/TextAffinity.h
+++ b/Source/WebCore/editing/TextAffinity.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004-2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,39 +23,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TextAffinity_h
-#define TextAffinity_h
-
-#ifdef __OBJC__
-#if !PLATFORM(IOS)
-#include <AppKit/NSTextView.h>
-#else
-#include "WAKAppKitStubs.h"
-#endif // !PLATFORM(IOS)
-#endif
+#pragma once
namespace WebCore {
-// These match the AppKit values for these concepts.
-// From NSTextView.h:
-// NSSelectionAffinityUpstream = 0
-// NSSelectionAffinityDownstream = 1
enum EAffinity { UPSTREAM = 0, DOWNSTREAM = 1 };
} // namespace WebCore
-
-#ifdef __OBJC__
-
-inline NSSelectionAffinity kit(WebCore::EAffinity affinity)
-{
- return static_cast<NSSelectionAffinity>(affinity);
-}
-
-inline WebCore::EAffinity core(NSSelectionAffinity affinity)
-{
- return static_cast<WebCore::EAffinity>(affinity);
-}
-
-#endif
-
-#endif // TextAffinity_h
diff --git a/Source/WebCore/editing/TextCheckingHelper.cpp b/Source/WebCore/editing/TextCheckingHelper.cpp
index 0e89cfccc..379d7d299 100644
--- a/Source/WebCore/editing/TextCheckingHelper.cpp
+++ b/Source/WebCore/editing/TextCheckingHelper.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2014 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -29,82 +29,92 @@
#include "Document.h"
#include "DocumentMarkerController.h"
+#include "ExceptionCode.h"
#include "Frame.h"
+#include "FrameSelection.h"
#include "Settings.h"
-#include "TextBreakIterator.h"
#include "TextCheckerClient.h"
#include "TextIterator.h"
#include "VisiblePosition.h"
#include "VisibleUnits.h"
+#include <unicode/ubrk.h>
#include <wtf/text/StringView.h>
+#include <wtf/text/TextBreakIterator.h>
namespace WebCore {
#if !USE(UNIFIED_TEXT_CHECKING)
#if USE(GRAMMAR_CHECKING)
-static void findBadGrammars(TextCheckerClient* client, const UChar* text, int start, int length, Vector<TextCheckingResult>& results)
-{
- int checkLocation = start;
- int checkLength = length;
- while (0 < checkLength) {
+static void findGrammaticalErrors(TextCheckerClient& client, StringView text, Vector<TextCheckingResult>& results)
+{
+ for (unsigned checkLocation = 0; checkLocation < text.length(); ) {
int badGrammarLocation = -1;
int badGrammarLength = 0;
Vector<GrammarDetail> badGrammarDetails;
- client->checkGrammarOfString(text + checkLocation, checkLength, badGrammarDetails, &badGrammarLocation, &badGrammarLength);
+ client.checkGrammarOfString(text.substring(checkLocation), badGrammarDetails, &badGrammarLocation, &badGrammarLength);
if (!badGrammarLength)
break;
- ASSERT(0 <= badGrammarLocation && badGrammarLocation <= checkLength);
- ASSERT(0 < badGrammarLength && badGrammarLocation + badGrammarLength <= checkLength);
+
+ ASSERT(badGrammarLocation >= 0);
+ ASSERT(static_cast<unsigned>(badGrammarLocation) <= text.length() - checkLocation);
+ ASSERT(badGrammarLength > 0);
+ ASSERT(static_cast<unsigned>(badGrammarLength) <= text.length() - checkLocation - badGrammarLocation);
+
TextCheckingResult badGrammar;
badGrammar.type = TextCheckingTypeGrammar;
badGrammar.location = checkLocation + badGrammarLocation;
badGrammar.length = badGrammarLength;
- badGrammar.details.swap(badGrammarDetails);
+ badGrammar.details = WTFMove(badGrammarDetails);
results.append(badGrammar);
- checkLocation += (badGrammarLocation + badGrammarLength);
- checkLength -= (badGrammarLocation + badGrammarLength);
+ checkLocation += badGrammarLocation + badGrammarLength;
}
}
+
#endif
-static void findMisspellings(TextCheckerClient* client, const UChar* text, int length, Vector<TextCheckingResult>& results)
+static void findMisspellings(TextCheckerClient& client, StringView text, Vector<TextCheckingResult>& results)
{
- TextBreakIterator* iterator = wordBreakIterator(StringView(text, length));
+ UBreakIterator* iterator = wordBreakIterator(text);
if (!iterator)
return;
- int wordStart = textBreakCurrent(iterator);
- while (0 <= wordStart) {
- int wordEnd = textBreakNext(iterator);
+ for (int wordStart = ubrk_current(iterator); wordStart >= 0; ) {
+ int wordEnd = ubrk_next(iterator);
if (wordEnd < 0)
break;
+
int wordLength = wordEnd - wordStart;
int misspellingLocation = -1;
int misspellingLength = 0;
- client->checkSpellingOfString(text + wordStart, wordLength, &misspellingLocation, &misspellingLength);
- if (0 < misspellingLength) {
- ASSERT(0 <= misspellingLocation && misspellingLocation <= wordLength);
- ASSERT(0 < misspellingLength && misspellingLocation + misspellingLength <= wordLength);
+ client.checkSpellingOfString(text.substring(wordStart, wordLength), &misspellingLocation, &misspellingLength);
+
+ if (misspellingLength > 0) {
+ ASSERT(misspellingLocation >= 0);
+ ASSERT(misspellingLocation <= wordLength);
+ ASSERT(misspellingLength > 0);
+ ASSERT(misspellingLocation + misspellingLength <= wordLength);
+
TextCheckingResult misspelling;
misspelling.type = TextCheckingTypeSpelling;
misspelling.location = wordStart + misspellingLocation;
misspelling.length = misspellingLength;
- misspelling.replacement = client->getAutoCorrectSuggestionForMisspelledWord(String(text + misspelling.location, misspelling.length));
+ misspelling.replacement = client.getAutoCorrectSuggestionForMisspelledWord(text.substring(misspelling.location, misspelling.length).toStringWithoutCopying());
results.append(misspelling);
}
wordStart = wordEnd;
}
}
+
#endif
-static PassRefPtr<Range> expandToParagraphBoundary(PassRefPtr<Range> range)
+static Ref<Range> expandToParagraphBoundary(PassRefPtr<Range> range)
{
- RefPtr<Range> paragraphRange = range->cloneRange(IGNORE_EXCEPTION);
- setStart(paragraphRange.get(), startOfParagraph(range->startPosition()));
- setEnd(paragraphRange.get(), endOfParagraph(range->endPosition()));
+ Ref<Range> paragraphRange = range->cloneRange();
+ setStart(paragraphRange.ptr(), startOfParagraph(range->startPosition()));
+ setEnd(paragraphRange.ptr(), endOfParagraph(range->endPosition()));
return paragraphRange;
}
@@ -139,7 +149,7 @@ void TextCheckingParagraph::expandRangeToNextEnd()
void TextCheckingParagraph::invalidateParagraphRangeValues()
{
m_checkingStart = m_checkingEnd = -1;
- m_offsetAsRange = 0;
+ m_offsetAsRange = nullptr;
m_text = String();
}
@@ -153,7 +163,7 @@ PassRefPtr<Range> TextCheckingParagraph::paragraphRange() const
{
ASSERT(m_checkingRange);
if (!m_paragraphRange)
- m_paragraphRange = expandToParagraphBoundary(checkingRange());
+ m_paragraphRange = expandToParagraphBoundary(m_checkingRange);
return m_paragraphRange;
}
@@ -163,28 +173,31 @@ PassRefPtr<Range> TextCheckingParagraph::subrange(int characterOffset, int chara
return TextIterator::subrange(paragraphRange().get(), characterOffset, characterCount);
}
-int TextCheckingParagraph::offsetTo(const Position& position, ExceptionCode& ec) const
+ExceptionOr<int> TextCheckingParagraph::offsetTo(const Position& position) const
{
ASSERT(m_checkingRange);
- RefPtr<Range> range = offsetAsRange()->cloneRange(ASSERT_NO_EXCEPTION);
- range->setEnd(position.containerNode(), position.computeOffsetInContainerNode(), ec);
- if (ec)
- return 0;
- return TextIterator::rangeLength(range.get());
+ if (!position.containerNode())
+ return Exception { TypeError };
+
+ auto range = offsetAsRange()->cloneRange();
+ auto result = range->setEnd(*position.containerNode(), position.computeOffsetInContainerNode());
+ if (result.hasException())
+ return result.releaseException();
+ return TextIterator::rangeLength(range.ptr());
}
bool TextCheckingParagraph::isEmpty() const
{
// Both predicates should have same result, but we check both just for sure.
// We need to investigate to remove this redundancy.
- return isRangeEmpty() || isTextEmpty();
+ return checkingStart() >= checkingEnd() || text().isEmpty();
}
PassRefPtr<Range> TextCheckingParagraph::offsetAsRange() const
{
ASSERT(m_checkingRange);
if (!m_offsetAsRange)
- m_offsetAsRange = Range::create(paragraphRange()->startContainer()->document(), paragraphRange()->startPosition(), checkingRange()->startPosition());
+ m_offsetAsRange = Range::create(paragraphRange()->startContainer().document(), paragraphRange()->startPosition(), m_checkingRange->startPosition());
return m_offsetAsRange;
}
@@ -209,7 +222,7 @@ int TextCheckingParagraph::checkingEnd() const
{
ASSERT(m_checkingRange);
if (m_checkingEnd == -1)
- m_checkingEnd = checkingStart() + TextIterator::rangeLength(checkingRange().get());
+ m_checkingEnd = checkingStart() + TextIterator::rangeLength(m_checkingRange.get());
return m_checkingEnd;
}
@@ -217,7 +230,7 @@ int TextCheckingParagraph::checkingLength() const
{
ASSERT(m_checkingRange);
if (-1 == m_checkingLength)
- m_checkingLength = TextIterator::rangeLength(checkingRange().get());
+ m_checkingLength = TextIterator::rangeLength(m_checkingRange.get());
return m_checkingLength;
}
@@ -233,68 +246,62 @@ TextCheckingHelper::~TextCheckingHelper()
{
}
-#if !PLATFORM(IOS)
String TextCheckingHelper::findFirstMisspelling(int& firstMisspellingOffset, bool markAll, RefPtr<Range>& firstMisspellingRange)
{
- WordAwareIterator it(m_range.get());
firstMisspellingOffset = 0;
-
+
String firstMisspelling;
int currentChunkOffset = 0;
- while (!it.atEnd()) {
- const UChar* chars = it.characters();
- int len = it.length();
-
- // Skip some work for one-space-char hunks
- if (!(len == 1 && chars[0] == ' ')) {
-
- int misspellingLocation = -1;
- int misspellingLength = 0;
- m_client->textChecker()->checkSpellingOfString(chars, len, &misspellingLocation, &misspellingLength);
-
- // 5490627 shows that there was some code path here where the String constructor below crashes.
- // We don't know exactly what combination of bad input caused this, so we're making this much
- // more robust against bad input on release builds.
- ASSERT(misspellingLength >= 0);
- ASSERT(misspellingLocation >= -1);
- ASSERT(!misspellingLength || misspellingLocation >= 0);
- ASSERT(misspellingLocation < len);
- ASSERT(misspellingLength <= len);
- ASSERT(misspellingLocation + misspellingLength <= len);
-
- if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < len && misspellingLength <= len && misspellingLocation + misspellingLength <= len) {
-
- // Compute range of misspelled word
- RefPtr<Range> misspellingRange = TextIterator::subrange(m_range.get(), currentChunkOffset + misspellingLocation, misspellingLength);
-
- // Remember first-encountered misspelling and its offset.
- if (!firstMisspelling) {
- firstMisspellingOffset = currentChunkOffset + misspellingLocation;
- firstMisspelling = String(chars + misspellingLocation, misspellingLength);
- firstMisspellingRange = misspellingRange;
- }
+ for (WordAwareIterator it(*m_range); !it.atEnd(); currentChunkOffset += it.text().length(), it.advance()) {
+ StringView text = it.text();
+ int textLength = text.length();
- // Store marker for misspelled word.
- misspellingRange->startContainer()->document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling);
+ // Skip some work for one-space-char hunks.
+ if (textLength == 1 && text[0] == ' ')
+ continue;
- // Bail out if we're marking only the first misspelling, and not all instances.
- if (!markAll)
- break;
+ int misspellingLocation = -1;
+ int misspellingLength = 0;
+ m_client->textChecker()->checkSpellingOfString(text, &misspellingLocation, &misspellingLength);
+
+ // 5490627 shows that there was some code path here where the String constructor below crashes.
+ // We don't know exactly what combination of bad input caused this, so we're making this much
+ // more robust against bad input on release builds.
+ ASSERT(misspellingLength >= 0);
+ ASSERT(misspellingLocation >= -1);
+ ASSERT(!misspellingLength || misspellingLocation >= 0);
+ ASSERT(misspellingLocation < textLength);
+ ASSERT(misspellingLength <= textLength);
+ ASSERT(misspellingLocation + misspellingLength <= textLength);
+
+ if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < textLength && misspellingLength <= textLength && misspellingLocation + misspellingLength <= textLength) {
+ // Compute range of misspelled word
+ RefPtr<Range> misspellingRange = TextIterator::subrange(m_range.get(), currentChunkOffset + misspellingLocation, misspellingLength);
+
+ // Remember first-encountered misspelling and its offset.
+ if (!firstMisspelling) {
+ firstMisspellingOffset = currentChunkOffset + misspellingLocation;
+ firstMisspelling = text.substring(misspellingLocation, misspellingLength).toString();
+ firstMisspellingRange = misspellingRange;
}
+
+ // Store marker for misspelled word.
+ misspellingRange->startContainer().document().markers().addMarker(misspellingRange.get(), DocumentMarker::Spelling);
+
+ // Bail out if we're marking only the first misspelling, and not all instances.
+ if (!markAll)
+ break;
}
-
- currentChunkOffset += len;
- it.advance();
}
-
+
return firstMisspelling;
}
String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, bool& outIsSpelling, int& outFirstFoundOffset, GrammarDetail& outGrammarDetail)
{
if (!unifiedTextCheckerEnabled())
- return "";
+ return emptyString();
String firstFoundItem;
String misspelledWord;
@@ -306,36 +313,36 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b
outGrammarDetail.location = -1;
outGrammarDetail.length = 0;
outGrammarDetail.guesses.clear();
- outGrammarDetail.userDescription = "";
+ outGrammarDetail.userDescription = emptyString();
// Expand the search range to encompass entire paragraphs, since text checking needs that much context.
// Determine the character offset from the start of the paragraph to the start of the original search range,
// since we will want to ignore results in this area.
- RefPtr<Range> paragraphRange = m_range->cloneRange(IGNORE_EXCEPTION);
- setStart(paragraphRange.get(), startOfParagraph(m_range->startPosition()));
- int totalRangeLength = TextIterator::rangeLength(paragraphRange.get());
- setEnd(paragraphRange.get(), endOfParagraph(m_range->startPosition()));
+ Ref<Range> paragraphRange = m_range->cloneRange();
+ setStart(paragraphRange.ptr(), startOfParagraph(m_range->startPosition()));
+ int totalRangeLength = TextIterator::rangeLength(paragraphRange.ptr());
+ setEnd(paragraphRange.ptr(), endOfParagraph(m_range->startPosition()));
- RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer()->document(), paragraphRange->startPosition(), m_range->startPosition());
- int rangeStartOffset = TextIterator::rangeLength(offsetAsRange.get());
+ Ref<Range> offsetAsRange = Range::create(paragraphRange->startContainer().document(), paragraphRange->startPosition(), m_range->startPosition());
+ int rangeStartOffset = TextIterator::rangeLength(offsetAsRange.ptr());
int totalLengthProcessed = 0;
bool firstIteration = true;
bool lastIteration = false;
while (totalLengthProcessed < totalRangeLength) {
// Iterate through the search range by paragraphs, checking each one for spelling and grammar.
- int currentLength = TextIterator::rangeLength(paragraphRange.get());
+ int currentLength = TextIterator::rangeLength(paragraphRange.ptr());
int currentStartOffset = firstIteration ? rangeStartOffset : 0;
int currentEndOffset = currentLength;
if (inSameParagraph(paragraphRange->startPosition(), m_range->endPosition())) {
// Determine the character offset from the end of the original search range to the end of the paragraph,
// since we will want to ignore results in this area.
- RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), paragraphRange->startPosition(), m_range->endPosition());
+ RefPtr<Range> endOffsetAsRange = Range::create(paragraphRange->startContainer().document(), paragraphRange->startPosition(), m_range->endPosition());
currentEndOffset = TextIterator::rangeLength(endOffsetAsRange.get());
lastIteration = true;
}
if (currentStartOffset < currentEndOffset) {
- String paragraphString = plainText(paragraphRange.get());
+ String paragraphString = plainText(paragraphRange.ptr());
if (paragraphString.length() > 0) {
bool foundGrammar = false;
int spellingLocation = 0;
@@ -345,36 +352,41 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b
Vector<TextCheckingResult> results;
TextCheckingTypeMask checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling;
- checkTextOfParagraph(m_client->textChecker(), paragraphString.deprecatedCharacters(), paragraphString.length(), checkingTypes, results);
-
- for (unsigned i = 0; i < results.size(); i++) {
- const TextCheckingResult* result = &results[i];
- if (result->type == TextCheckingTypeSpelling && result->location >= currentStartOffset && result->location + result->length <= currentEndOffset) {
- ASSERT(result->length > 0 && result->location >= 0);
- spellingLocation = result->location;
- misspelledWord = paragraphString.substring(result->location, result->length);
+ VisibleSelection currentSelection;
+ if (Frame* frame = paragraphRange->ownerDocument().frame())
+ currentSelection = frame->selection().selection();
+ checkTextOfParagraph(*m_client->textChecker(), paragraphString, checkingTypes, results, currentSelection);
+
+ for (auto& result : results) {
+ if (result.type == TextCheckingTypeSpelling && result.location >= currentStartOffset && result.location + result.length <= currentEndOffset) {
+ ASSERT(result.length > 0);
+ ASSERT(result.location >= 0);
+ spellingLocation = result.location;
+ misspelledWord = paragraphString.substring(result.location, result.length);
ASSERT(misspelledWord.length());
break;
}
- if (checkGrammar && result->type == TextCheckingTypeGrammar && result->location < currentEndOffset && result->location + result->length > currentStartOffset) {
- ASSERT(result->length > 0 && result->location >= 0);
+ if (checkGrammar && result.type == TextCheckingTypeGrammar && result.location < currentEndOffset && result.location + result.length > currentStartOffset) {
+ ASSERT(result.length > 0);
+ ASSERT(result.location >= 0);
// We can't stop after the first grammar result, since there might still be a spelling result after
// it begins but before the first detail in it, but we can stop if we find a second grammar result.
if (foundGrammar)
break;
- for (unsigned j = 0; j < result->details.size(); j++) {
- const GrammarDetail* detail = &result->details[j];
- ASSERT(detail->length > 0 && detail->location >= 0);
- if (result->location + detail->location >= currentStartOffset && result->location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result->location + detail->location < grammarDetailLocation)) {
+ for (unsigned j = 0; j < result.details.size(); j++) {
+ const GrammarDetail* detail = &result.details[j];
+ ASSERT(detail->length > 0);
+ ASSERT(detail->location >= 0);
+ if (result.location + detail->location >= currentStartOffset && result.location + detail->location + detail->length <= currentEndOffset && (!foundGrammar || result.location + detail->location < grammarDetailLocation)) {
grammarDetailIndex = j;
- grammarDetailLocation = result->location + detail->location;
+ grammarDetailLocation = result.location + detail->location;
foundGrammar = true;
}
}
if (foundGrammar) {
- grammarPhraseLocation = result->location;
- outGrammarDetail = result->details[grammarDetailIndex];
- badGrammarPhrase = paragraphString.substring(result->location, result->length);
+ grammarPhraseLocation = result.location;
+ outGrammarDetail = result.details[grammarDetailIndex];
+ badGrammarPhrase = paragraphString.substring(result.location, result.length);
ASSERT(badGrammarPhrase.length());
}
}
@@ -383,7 +395,7 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b
if (!misspelledWord.isEmpty() && (!checkGrammar || badGrammarPhrase.isEmpty() || spellingLocation <= grammarDetailLocation)) {
int spellingOffset = spellingLocation - currentStartOffset;
if (!firstIteration) {
- RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), m_range->startPosition(), paragraphRange->startPosition());
+ RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer().document(), m_range->startPosition(), paragraphRange->startPosition());
spellingOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get());
}
outIsSpelling = true;
@@ -394,7 +406,7 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b
if (checkGrammar && !badGrammarPhrase.isEmpty()) {
int grammarPhraseOffset = grammarPhraseLocation - currentStartOffset;
if (!firstIteration) {
- RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer()->document(), m_range->startPosition(), paragraphRange->startPosition());
+ RefPtr<Range> paragraphOffsetAsRange = Range::create(paragraphRange->startContainer().document(), m_range->startPosition(), paragraphRange->startPosition());
grammarPhraseOffset += TextIterator::rangeLength(paragraphOffsetAsRange.get());
}
outIsSpelling = false;
@@ -407,16 +419,16 @@ String TextCheckingHelper::findFirstMisspellingOrBadGrammar(bool checkGrammar, b
if (lastIteration || totalLengthProcessed + currentLength >= totalRangeLength)
break;
VisiblePosition newParagraphStart = startOfNextParagraph(paragraphRange->endPosition());
- setStart(paragraphRange.get(), newParagraphStart);
- setEnd(paragraphRange.get(), endOfParagraph(newParagraphStart));
+ setStart(paragraphRange.ptr(), newParagraphStart);
+ setEnd(paragraphRange.ptr(), endOfParagraph(newParagraphStart));
firstIteration = false;
totalLengthProcessed += currentLength;
}
return firstFoundItem;
}
-#endif // !PLATFORM(IOS)
#if USE(GRAMMAR_CHECKING)
+
int TextCheckingHelper::findFirstGrammarDetail(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int startOffset, int endOffset, bool markAll) const
{
// Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
@@ -425,7 +437,8 @@ int TextCheckingHelper::findFirstGrammarDetail(const Vector<GrammarDetail>& gram
int earliestDetailIndex = -1;
for (unsigned i = 0; i < grammarDetails.size(); i++) {
const GrammarDetail* detail = &grammarDetails[i];
- ASSERT(detail->length > 0 && detail->location >= 0);
+ ASSERT(detail->length > 0);
+ ASSERT(detail->location >= 0);
int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location;
@@ -439,7 +452,7 @@ int TextCheckingHelper::findFirstGrammarDetail(const Vector<GrammarDetail>& gram
if (markAll) {
RefPtr<Range> badGrammarRange = TextIterator::subrange(m_range.get(), badGrammarPhraseLocation - startOffset + detail->location, detail->length);
- badGrammarRange->startContainer()->document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
+ badGrammarRange->startContainer().document().markers().addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription);
}
// Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order)
@@ -458,7 +471,7 @@ String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail,
outGrammarDetail.location = -1;
outGrammarDetail.length = 0;
outGrammarDetail.guesses.clear();
- outGrammarDetail.userDescription = "";
+ outGrammarDetail.userDescription = emptyString();
outGrammarPhraseOffset = 0;
String firstBadGrammarPhrase;
@@ -469,12 +482,11 @@ String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail,
TextCheckingParagraph paragraph(m_range);
// Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range.
- int startOffset = 0;
- while (startOffset < paragraph.checkingEnd()) {
+ for (int startOffset = 0; startOffset < paragraph.checkingEnd(); ) {
Vector<GrammarDetail> grammarDetails;
int badGrammarPhraseLocation = -1;
int badGrammarPhraseLength = 0;
- m_client->textChecker()->checkGrammarOfString(paragraph.textDeprecatedCharacters() + startOffset, paragraph.textLength() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength);
+ m_client->textChecker()->checkGrammarOfString(StringView(paragraph.text()).substring(startOffset), grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength);
if (!badGrammarPhraseLength) {
ASSERT(badGrammarPhraseLocation == -1);
@@ -484,7 +496,6 @@ String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail,
ASSERT(badGrammarPhraseLocation >= 0);
badGrammarPhraseLocation += startOffset;
-
// Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
int badGrammarIndex = findFirstGrammarDetail(grammarDetails, badGrammarPhraseLocation, paragraph.checkingStart(), paragraph.checkingEnd(), markAll);
if (badGrammarIndex >= 0) {
@@ -511,13 +522,12 @@ String TextCheckingHelper::findFirstBadGrammar(GrammarDetail& outGrammarDetail,
return firstBadGrammarPhrase;
}
-
bool TextCheckingHelper::isUngrammatical() const
{
if (!m_client)
return false;
- if (!m_range || m_range->collapsed(IGNORE_EXCEPTION))
+ if (!m_range || m_range->collapsed())
return false;
// Returns true only if the passed range exactly corresponds to a bad grammar detail range. This is analogous
@@ -535,9 +545,10 @@ bool TextCheckingHelper::isUngrammatical() const
// Bad grammar, but phrase (e.g. sentence) starts beyond start of range.
if (grammarPhraseOffset > 0)
return false;
-
- ASSERT(grammarDetail.location >= 0 && grammarDetail.length > 0);
-
+
+ ASSERT(grammarDetail.location >= 0);
+ ASSERT(grammarDetail.length > 0);
+
// Bad grammar, but start of detail (e.g. ungrammatical word) doesn't match start of range
if (grammarDetail.location + grammarPhraseOffset)
return false;
@@ -554,7 +565,8 @@ bool TextCheckingHelper::isUngrammatical() const
return true;
}
-#endif
+
+#endif // USE(GRAMMAR_CHECKING)
Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool checkGrammar, bool& misspelled, bool& ungrammatical) const
{
@@ -565,7 +577,7 @@ Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool
misspelled = false;
ungrammatical = false;
- if (!m_client || !m_range || m_range->collapsed(IGNORE_EXCEPTION))
+ if (!m_client || !m_range || m_range->collapsed())
return guesses;
// Expand the range to encompass entire paragraphs, since text checking needs that much context.
@@ -575,14 +587,16 @@ Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool
Vector<TextCheckingResult> results;
TextCheckingTypeMask checkingTypes = checkGrammar ? (TextCheckingTypeSpelling | TextCheckingTypeGrammar) : TextCheckingTypeSpelling;
- checkTextOfParagraph(m_client->textChecker(), paragraph.textDeprecatedCharacters(), paragraph.textLength(), checkingTypes, results);
-
- for (unsigned i = 0; i < results.size(); i++) {
- const TextCheckingResult* result = &results[i];
- if (result->type == TextCheckingTypeSpelling && paragraph.checkingRangeMatches(result->location, result->length)) {
+ VisibleSelection currentSelection;
+ if (Frame* frame = m_range->ownerDocument().frame())
+ currentSelection = frame->selection().selection();
+ checkTextOfParagraph(*m_client->textChecker(), paragraph.text(), checkingTypes, results, currentSelection);
+
+ for (auto& result : results) {
+ if (result.type == TextCheckingTypeSpelling && paragraph.checkingRangeMatches(result.location, result.length)) {
String misspelledWord = paragraph.checkingSubstring();
ASSERT(misspelledWord.length());
- m_client->textChecker()->getGuessesForWord(misspelledWord, String(), guesses);
+ m_client->textChecker()->getGuessesForWord(misspelledWord, String(), currentSelection, guesses);
m_client->updateSpellingUIWithMisspelledWord(misspelledWord);
misspelled = true;
return guesses;
@@ -591,19 +605,18 @@ Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool
if (!checkGrammar)
return guesses;
-
- for (unsigned i = 0; i < results.size(); i++) {
- const TextCheckingResult* result = &results[i];
- if (result->type == TextCheckingTypeGrammar && paragraph.isCheckingRangeCoveredBy(result->location, result->length)) {
- for (unsigned j = 0; j < result->details.size(); j++) {
- const GrammarDetail* detail = &result->details[j];
- ASSERT(detail->length > 0 && detail->location >= 0);
- if (paragraph.checkingRangeMatches(result->location + detail->location, detail->length)) {
- String badGrammarPhrase = paragraph.textSubstring(result->location, result->length);
+
+ for (auto& result : results) {
+ if (result.type == TextCheckingTypeGrammar && paragraph.isCheckingRangeCoveredBy(result.location, result.length)) {
+ for (auto& detail : result.details) {
+ ASSERT(detail.length > 0);
+ ASSERT(detail.location >= 0);
+ if (paragraph.checkingRangeMatches(result.location + detail.location, detail.length)) {
+ String badGrammarPhrase = paragraph.textSubstring(result.location, result.length);
ASSERT(badGrammarPhrase.length());
- for (unsigned k = 0; k < detail->guesses.size(); k++)
- guesses.append(detail->guesses[k]);
- m_client->updateSpellingUIWithGrammarString(badGrammarPhrase, *detail);
+ for (auto& guess : detail.guesses)
+ guesses.append(guess);
+ m_client->updateSpellingUIWithGrammarString(badGrammarPhrase, detail);
ungrammatical = true;
return guesses;
}
@@ -613,8 +626,6 @@ Vector<String> TextCheckingHelper::guessesForMisspelledOrUngrammaticalRange(bool
return guesses;
}
-
-#if !PLATFORM(IOS)
void TextCheckingHelper::markAllMisspellings(RefPtr<Range>& firstMisspellingRange)
{
// Use the "markAll" feature of findFirstMisspelling. Ignore the return value and the "out parameter";
@@ -633,47 +644,41 @@ void TextCheckingHelper::markAllBadGrammar()
findFirstBadGrammar(ignoredGrammarDetail, ignoredOffset, true);
}
#endif
-#endif // !PLATFORM(IOS)
bool TextCheckingHelper::unifiedTextCheckerEnabled() const
{
return m_range && WebCore::unifiedTextCheckerEnabled(m_range->ownerDocument().frame());
}
-void checkTextOfParagraph(TextCheckerClient* client, const UChar* text, int length,
- TextCheckingTypeMask checkingTypes, Vector<TextCheckingResult>& results)
+void checkTextOfParagraph(TextCheckerClient& client, StringView text, TextCheckingTypeMask checkingTypes, Vector<TextCheckingResult>& results, const VisibleSelection& currentSelection)
{
#if USE(UNIFIED_TEXT_CHECKING)
- results = client->checkTextOfParagraph(StringView(text, length), checkingTypes);
+ results = client.checkTextOfParagraph(text, checkingTypes, currentSelection);
#else
- Vector<TextCheckingResult> spellingResult;
+ UNUSED_PARAM(currentSelection);
+
+ Vector<TextCheckingResult> mispellings;
if (checkingTypes & TextCheckingTypeSpelling)
- findMisspellings(client, text, length, spellingResult);
+ findMisspellings(client, text, mispellings);
#if USE(GRAMMAR_CHECKING)
- Vector<TextCheckingResult> grammarResult;
+ // Look for grammatical errors that occur before the first misspelling.
+ Vector<TextCheckingResult> grammaticalErrors;
if (checkingTypes & TextCheckingTypeGrammar) {
- // Only checks grammartical error before the first misspellings
- int grammarCheckLength = length;
- for (size_t i = 0; i < spellingResult.size(); ++i) {
- if (spellingResult[i].location < grammarCheckLength)
- grammarCheckLength = spellingResult[i].location;
- }
-
- findBadGrammars(client, text, 0, grammarCheckLength, grammarResult);
+ unsigned grammarCheckLength = text.length();
+ for (auto& mispelling : mispellings)
+ grammarCheckLength = std::min<unsigned>(grammarCheckLength, mispelling.location);
+ findGrammaticalErrors(client, text.substring(0, grammarCheckLength), grammaticalErrors);
}
- if (grammarResult.size())
- results.swap(grammarResult);
+ results = WTFMove(grammaticalErrors);
#endif
- if (spellingResult.size()) {
- if (results.isEmpty())
- results.swap(spellingResult);
- else
- results.appendVector(spellingResult);
- }
-#endif
+ if (results.isEmpty())
+ results = WTFMove(mispellings);
+ else
+ results.appendVector(mispellings);
+#endif // USE(UNIFIED_TEXT_CHECKING)
}
bool unifiedTextCheckerEnabled(const Frame* frame)
diff --git a/Source/WebCore/editing/TextCheckingHelper.h b/Source/WebCore/editing/TextCheckingHelper.h
index a90c9c8b3..d8df24e66 100644
--- a/Source/WebCore/editing/TextCheckingHelper.h
+++ b/Source/WebCore/editing/TextCheckingHelper.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2006, 2007, 2008, 2014 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
*
* This library is free software; you can redistribute it and/or
@@ -18,18 +18,17 @@
* Boston, MA 02110-1301, USA.
*/
-#ifndef TextCheckingHelper_h
-#define TextCheckingHelper_h
+#pragma once
#include "EditorClient.h"
-#include "ExceptionCode.h"
+#include "ExceptionOr.h"
#include "TextChecking.h"
-#include <wtf/text/WTFString.h>
namespace WebCore {
-class Range;
class Position;
+class Range;
+
struct TextCheckingResult;
class TextCheckingParagraph {
@@ -40,17 +39,18 @@ public:
int rangeLength() const;
PassRefPtr<Range> subrange(int characterOffset, int characterCount) const;
- int offsetTo(const Position&, ExceptionCode&) const;
+ ExceptionOr<int> offsetTo(const Position&) const;
void expandRangeToNextEnd();
+ // FIXME: Consider changing this to return a StringView.
+ const String& text() const;
+
+ // FIXME: Consider removing these and just having the caller use text() directly.
int textLength() const { return text().length(); }
String textSubstring(unsigned pos, unsigned len = UINT_MAX) const { return text().substring(pos, len); }
- const UChar* textDeprecatedCharacters() const { return text().deprecatedCharacters(); }
UChar textCharAt(int index) const { return text()[static_cast<unsigned>(index)]; }
bool isEmpty() const;
- bool isTextEmpty() const { return text().isEmpty(); }
- bool isRangeEmpty() const { return checkingStart() >= checkingEnd(); }
int checkingStart() const;
int checkingEnd() const;
@@ -64,9 +64,7 @@ public:
private:
void invalidateParagraphRangeValues();
- PassRefPtr<Range> checkingRange() const { return m_checkingRange; }
PassRefPtr<Range> offsetAsRange() const;
- const String& text() const;
RefPtr<Range> m_checkingRange;
mutable RefPtr<Range> m_paragraphRange;
@@ -103,11 +101,8 @@ private:
#endif
};
-void checkTextOfParagraph(TextCheckerClient*, const UChar* text, int length,
- TextCheckingTypeMask checkingTypes, Vector<TextCheckingResult>& results);
+void checkTextOfParagraph(TextCheckerClient&, StringView, TextCheckingTypeMask, Vector<TextCheckingResult>&, const VisibleSelection& currentSelection);
bool unifiedTextCheckerEnabled(const Frame*);
} // namespace WebCore
-
-#endif // TextCheckingHelper_h
diff --git a/Source/WebCore/editing/TextGranularity.h b/Source/WebCore/editing/TextGranularity.h
index aaa3d7d11..5e201fe8e 100644
--- a/Source/WebCore/editing/TextGranularity.h
+++ b/Source/WebCore/editing/TextGranularity.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TextGranularity_h
-#define TextGranularity_h
+#pragma once
namespace WebCore {
@@ -36,15 +35,11 @@ enum TextGranularity {
SentenceGranularity,
LineGranularity,
ParagraphGranularity,
-#if PLATFORM(IOS)
DocumentGranularity,
-#endif
SentenceBoundary,
LineBoundary,
ParagraphBoundary,
DocumentBoundary
};
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/TextInsertionBaseCommand.cpp b/Source/WebCore/editing/TextInsertionBaseCommand.cpp
index fd7219d60..cf9c2624a 100644
--- a/Source/WebCore/editing/TextInsertionBaseCommand.cpp
+++ b/Source/WebCore/editing/TextInsertionBaseCommand.cpp
@@ -35,21 +35,21 @@
namespace WebCore {
-TextInsertionBaseCommand::TextInsertionBaseCommand(Document& document)
- : CompositeEditCommand(document)
+TextInsertionBaseCommand::TextInsertionBaseCommand(Document& document, EditAction editingAction)
+ : CompositeEditCommand(document, editingAction)
{
}
-void TextInsertionBaseCommand::applyTextInsertionCommand(Frame* frame, PassRefPtr<TextInsertionBaseCommand> command, const VisibleSelection& selectionForInsertion, const VisibleSelection& endingSelection)
+void TextInsertionBaseCommand::applyTextInsertionCommand(Frame* frame, TextInsertionBaseCommand& command, const VisibleSelection& selectionForInsertion, const VisibleSelection& endingSelection)
{
bool changeSelection = selectionForInsertion != endingSelection;
if (changeSelection) {
- command->setStartingSelection(selectionForInsertion);
- command->setEndingSelection(selectionForInsertion);
+ command.setStartingSelection(selectionForInsertion);
+ command.setEndingSelection(selectionForInsertion);
}
- applyCommand(command);
+ applyCommand(&command);
if (changeSelection) {
- command->setEndingSelection(endingSelection);
+ command.setEndingSelection(endingSelection);
frame->selection().setSelection(endingSelection);
}
}
@@ -63,9 +63,9 @@ String dispatchBeforeTextInsertedEvent(const String& text, const VisibleSelectio
if (Node* startNode = selectionForInsertion.start().containerNode()) {
if (startNode->rootEditableElement()) {
// Send BeforeTextInsertedEvent. The event handler will update text if necessary.
- RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text);
- startNode->rootEditableElement()->dispatchEvent(evt, IGNORE_EXCEPTION);
- newText = evt->text();
+ Ref<BeforeTextInsertedEvent> event = BeforeTextInsertedEvent::create(text);
+ startNode->rootEditableElement()->dispatchEvent(event);
+ newText = event->text();
}
}
return newText;
@@ -77,8 +77,8 @@ bool canAppendNewLineFeedToSelection(const VisibleSelection& selection)
if (!node)
return false;
- RefPtr<BeforeTextInsertedEvent> event = BeforeTextInsertedEvent::create(String("\n"));
- node->dispatchEvent(event, IGNORE_EXCEPTION);
+ Ref<BeforeTextInsertedEvent> event = BeforeTextInsertedEvent::create(String("\n"));
+ node->dispatchEvent(event);
return event->text().length();
}
diff --git a/Source/WebCore/editing/TextInsertionBaseCommand.h b/Source/WebCore/editing/TextInsertionBaseCommand.h
index e549b5c17..10d9adb08 100644
--- a/Source/WebCore/editing/TextInsertionBaseCommand.h
+++ b/Source/WebCore/editing/TextInsertionBaseCommand.h
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TextInsertionBaseCommand_h
-#define TextInsertionBaseCommand_h
+#pragma once
#include "CompositeEditCommand.h"
#include <wtf/text/WTFString.h>
@@ -39,8 +38,8 @@ public:
virtual ~TextInsertionBaseCommand() { };
protected:
- explicit TextInsertionBaseCommand(Document&);
- static void applyTextInsertionCommand(Frame*, PassRefPtr<TextInsertionBaseCommand>, const VisibleSelection& selectionForInsertion, const VisibleSelection& endingSelection);
+ explicit TextInsertionBaseCommand(Document&, EditAction = EditActionUnspecified);
+ static void applyTextInsertionCommand(Frame*, TextInsertionBaseCommand&, const VisibleSelection& selectionForInsertion, const VisibleSelection& endingSelection);
};
String dispatchBeforeTextInsertedEvent(const String& text, const VisibleSelection& selectionForInsertion, bool insertionIsForUpdatingComposition);
@@ -66,6 +65,4 @@ void forEachLineInString(const String& string, const LineOperation& operation)
}
}
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/TextIterator.cpp b/Source/WebCore/editing/TextIterator.cpp
index 8baee8525..3cedb1c59 100644
--- a/Source/WebCore/editing/TextIterator.cpp
+++ b/Source/WebCore/editing/TextIterator.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2014 Apple Inc. All rights reserved.
* Copyright (C) 2005 Alexey Proskuryakov.
*
* Redistribution and use in source and binary forms, with or without
@@ -11,10 +11,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -28,11 +28,19 @@
#include "TextIterator.h"
#include "Document.h"
-#include "ExceptionCodePlaceholder.h"
-#include "Font.h"
+#include "FontCascade.h"
#include "Frame.h"
+#include "HTMLBodyElement.h"
#include "HTMLElement.h"
+#include "HTMLFrameOwnerElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLLegendElement.h"
+#include "HTMLMeterElement.h"
#include "HTMLNames.h"
+#include "HTMLParagraphElement.h"
+#include "HTMLProgressElement.h"
+#include "HTMLSlotElement.h"
+#include "HTMLTextAreaElement.h"
#include "HTMLTextFormControlElement.h"
#include "InlineTextBox.h"
#include "NodeTraversal.h"
@@ -43,18 +51,21 @@
#include "RenderTextControl.h"
#include "RenderTextFragment.h"
#include "ShadowRoot.h"
+#include "SimpleLineLayout.h"
+#include "SimpleLineLayoutResolver.h"
#include "TextBoundaries.h"
-#include "TextBreakIterator.h"
+#include <wtf/text/TextBreakIterator.h>
#include "TextControlInnerElements.h"
#include "VisiblePosition.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
#include <wtf/unicode/CharacterNames.h>
#if !UCONFIG_NO_COLLATION
-#include "TextBreakIteratorInternalICU.h"
#include <unicode/usearch.h>
+#include <wtf/text/TextBreakIteratorInternalICU.h>
#endif
using namespace WTF::Unicode;
@@ -76,9 +87,9 @@ public:
~SearchBuffer();
// Returns number of characters appended; guaranteed to be in the range [1, length].
- size_t append(const UChar*, size_t length);
+ size_t append(StringView);
bool needsMoreContext() const;
- void prependContext(const UChar*, size_t length);
+ void prependContext(StringView);
void reachedBreak();
// Result is the size in characters of what was found.
@@ -91,8 +102,10 @@ public:
private:
bool isBadMatch(const UChar*, size_t length) const;
bool isWordStartMatch(size_t start, size_t length) const;
+ bool isWordEndMatch(size_t start, size_t length) const;
- String m_target;
+ const String m_target;
+ const StringView::UpconvertedCharacters m_targetCharacters;
FindOptions m_options;
Vector<UChar> m_buffer;
@@ -101,7 +114,7 @@ private:
bool m_atBreak;
bool m_needsMoreContext;
- bool m_targetRequiresKanaWorkaround;
+ const bool m_targetRequiresKanaWorkaround;
Vector<UChar> m_normalizedTarget;
mutable Vector<UChar> m_normalizedMatch;
@@ -174,186 +187,193 @@ unsigned BitStack::size() const
// --------
-#if !ASSERT_DISABLED
-
-static unsigned depthCrossingShadowBoundaries(Node* node)
-{
- unsigned depth = 0;
- for (Node* parent = node->parentOrShadowHostNode(); parent; parent = parent->parentOrShadowHostNode())
- ++depth;
- return depth;
-}
-
-#endif
-
// This function is like Range::pastLastNode, except for the fact that it can climb up out of shadow trees.
-static Node* nextInPreOrderCrossingShadowBoundaries(Node* rangeEndContainer, int rangeEndOffset)
+static Node* nextInPreOrderCrossingShadowBoundaries(Node& rangeEndContainer, int rangeEndOffset)
{
- if (!rangeEndContainer)
- return 0;
- if (rangeEndOffset >= 0 && !rangeEndContainer->offsetInCharacters()) {
- if (Node* next = rangeEndContainer->childNode(rangeEndOffset))
+ if (rangeEndOffset >= 0 && !rangeEndContainer.offsetInCharacters()) {
+ if (Node* next = rangeEndContainer.traverseToChildAt(rangeEndOffset))
return next;
}
- for (Node* node = rangeEndContainer; node; node = node->parentOrShadowHostNode()) {
+ for (Node* node = &rangeEndContainer; node; node = node->parentOrShadowHostNode()) {
if (Node* next = node->nextSibling())
return next;
}
- return 0;
+ return nullptr;
}
-// --------
-
-static inline bool fullyClipsContents(Node* node)
+static inline bool fullyClipsContents(Node& node)
{
- RenderObject* renderer = node->renderer();
- if (!renderer || !renderer->isBox() || !renderer->hasOverflowClip())
+ auto* renderer = node.renderer();
+ if (!renderer) {
+ if (!is<Element>(node))
+ return false;
+ return !downcast<Element>(node).hasDisplayContents();
+ }
+ if (!is<RenderBox>(*renderer))
+ return false;
+ auto& box = downcast<RenderBox>(*renderer);
+ if (!box.hasOverflowClip())
return false;
- return toRenderBox(renderer)->size().isEmpty();
+
+ // Quirk to keep copy/paste in the CodeMirror editor version used in Jenkins working.
+ if (is<HTMLTextAreaElement>(node))
+ return box.size().isEmpty();
+
+ return box.contentSize().isEmpty();
}
-static inline bool ignoresContainerClip(Node* node)
+static inline bool ignoresContainerClip(Node& node)
{
- RenderObject* renderer = node->renderer();
+ auto* renderer = node.renderer();
if (!renderer || renderer->isTextOrLineBreak())
return false;
return renderer->style().hasOutOfFlowPosition();
}
-static void pushFullyClippedState(BitStack& stack, Node* node)
+static void pushFullyClippedState(BitStack& stack, Node& node)
{
- ASSERT(stack.size() == depthCrossingShadowBoundaries(node));
-
// Push true if this node full clips its contents, or if a parent already has fully
// clipped and this is not a node that ignores its container's clip.
stack.push(fullyClipsContents(node) || (stack.top() && !ignoresContainerClip(node)));
}
-static void setUpFullyClippedStack(BitStack& stack, Node* node)
+static void setUpFullyClippedStack(BitStack& stack, Node& node)
{
// Put the nodes in a vector so we can iterate in reverse order.
+ // FIXME: This (and TextIterator in general) should use ComposedTreeIterator.
Vector<Node*, 100> ancestry;
- for (Node* parent = node->parentOrShadowHostNode(); parent; parent = parent->parentOrShadowHostNode())
+ for (Node* parent = node.parentOrShadowHostNode(); parent; parent = parent->parentOrShadowHostNode())
ancestry.append(parent);
// Call pushFullyClippedState on each node starting with the earliest ancestor.
size_t size = ancestry.size();
for (size_t i = 0; i < size; ++i)
- pushFullyClippedState(stack, ancestry[size - i - 1]);
+ pushFullyClippedState(stack, *ancestry[size - i - 1]);
pushFullyClippedState(stack, node);
+}
- ASSERT(stack.size() == 1 + depthCrossingShadowBoundaries(node));
+static bool isClippedByFrameAncestor(const Document& document, TextIteratorBehavior behavior)
+{
+ if (!(behavior & TextIteratorClipsToFrameAncestors))
+ return false;
+
+ for (auto* owner = document.ownerElement(); owner; owner = owner->document().ownerElement()) {
+ BitStack ownerClipStack;
+ setUpFullyClippedStack(ownerClipStack, *owner);
+ if (ownerClipStack.top())
+ return true;
+ }
+ return false;
}
-
+
+// FIXME: editingIgnoresContent and isRendererReplacedElement try to do the same job.
+// It's not good to have both of them.
bool isRendererReplacedElement(RenderObject* renderer)
{
if (!renderer)
return false;
-#if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
- if (renderer->isImage() || renderer->isMedia())
- return true;
- if (renderer->isWidget()) {
- if (renderer->node() && renderer->node()->isElementNode()) {
- Element* element = toElement(renderer->node());
- if (element->hasTagName(videoTag) || !element->hasTagName(audioTag))
- return false; // See <rdar://problem/6893793>.
- }
- return true;
- }
-#else
- if (renderer->isImage() || renderer->isWidget() || renderer->isMedia())
- return true;
+ bool isAttachment = false;
+#if ENABLE(ATTACHMENT_ELEMENT)
+ isAttachment = renderer->isAttachment();
#endif
-
- if (renderer->node() && renderer->node()->isElementNode()) {
- Element* element = toElement(renderer->node());
- if (element->isFormControlElement() || element->hasTagName(legendTag)
- || element->hasTagName(meterTag) || element->hasTagName(progressTag))
+ if (renderer->isImage() || renderer->isWidget() || renderer->isMedia() || isAttachment)
+ return true;
+
+ if (is<Element>(renderer->node())) {
+ Element& element = downcast<Element>(*renderer->node());
+ if (is<HTMLFormControlElement>(element) || is<HTMLLegendElement>(element) || is<HTMLProgressElement>(element) || element.hasTagName(meterTag))
return true;
-
- if (equalIgnoringCase(element->getAttribute(roleAttr), "img"))
+ if (equalLettersIgnoringASCIICase(element.attributeWithoutSynchronization(roleAttr), "img"))
return true;
}
-
+
return false;
}
// --------
-TextIterator::TextIterator(const Range* r, TextIteratorBehavior behavior)
- : m_startContainer(0)
- , m_startOffset(0)
- , m_endContainer(0)
- , m_endOffset(0)
- , m_positionNode(0)
- , m_textCharacters(0)
- , m_textLength(0)
- , m_remainingTextBox(0)
- , m_firstLetterText(0)
- , m_sortedTextBoxesPosition(0)
- , m_emitsCharactersBetweenAllVisiblePositions(behavior & TextIteratorEmitsCharactersBetweenAllVisiblePositions)
- , m_entersTextControls(behavior & TextIteratorEntersTextControls)
- , m_emitsTextWithoutTranscoding(behavior & TextIteratorEmitsTextsWithoutTranscoding)
- , m_emitsOriginalText(behavior & TextIteratorEmitsOriginalText)
- , m_handledFirstLetter(false)
- , m_ignoresStyleVisibility(behavior & TextIteratorIgnoresStyleVisibility)
- , m_emitsObjectReplacementCharacters(behavior & TextIteratorEmitsObjectReplacementCharacters)
- , m_stopsOnFormControls(behavior & TextIteratorStopsOnFormControls)
- , m_shouldStop(false)
- , m_emitsImageAltText(behavior & TextIteratorEmitsImageAltText)
-{
- if (!r)
- return;
+inline void TextIteratorCopyableText::reset()
+{
+ m_singleCharacter = 0;
+ m_string = String();
+ m_offset = 0;
+ m_length = 0;
+}
+
+inline void TextIteratorCopyableText::set(String&& string)
+{
+ m_singleCharacter = 0;
+ m_string = WTFMove(string);
+ m_offset = 0;
+ m_length = m_string.length();
+}
+
+inline void TextIteratorCopyableText::set(String&& string, unsigned offset, unsigned length)
+{
+ ASSERT(offset < string.length());
+ ASSERT(length);
+ ASSERT(length <= string.length() - offset);
+
+ m_singleCharacter = 0;
+ m_string = WTFMove(string);
+ m_offset = offset;
+ m_length = length;
+}
+
+inline void TextIteratorCopyableText::set(UChar singleCharacter)
+{
+ m_singleCharacter = singleCharacter;
+ m_string = String();
+ m_offset = 0;
+ m_length = 0;
+}
- r->ownerDocument().updateLayoutIgnorePendingStylesheets();
+void TextIteratorCopyableText::appendToStringBuilder(StringBuilder& builder) const
+{
+ if (m_singleCharacter)
+ builder.append(m_singleCharacter);
+ else
+ builder.append(m_string, m_offset, m_length);
+}
+
+// --------
- // get and validate the range endpoints
- Node* startContainer = r->startContainer();
- if (!startContainer)
+TextIterator::TextIterator(const Range* range, TextIteratorBehavior behavior)
+ : m_behavior(behavior)
+{
+ // FIXME: Only m_positionNode above needs to be initialized if range is null.
+ if (!range)
return;
- int startOffset = r->startOffset();
- Node* endContainer = r->endContainer();
- int endOffset = r->endOffset();
+
+ range->ownerDocument().updateLayoutIgnorePendingStylesheets();
+
+ m_startContainer = &range->startContainer();
// Callers should be handing us well-formed ranges. If we discover that this isn't
// the case, we could consider changing this assertion to an early return.
- ASSERT(r->boundaryPointsValid());
+ ASSERT(range->boundaryPointsValid());
- // remember range - this does not change
- m_startContainer = startContainer;
- m_startOffset = startOffset;
- m_endContainer = endContainer;
- m_endOffset = endOffset;
+ m_startOffset = range->startOffset();
+ m_endContainer = &range->endContainer();
+ m_endOffset = range->endOffset();
- // set up the current node for processing
- m_node = r->firstNode();
+ // Set up the current node for processing.
+ m_node = range->firstNode();
if (!m_node)
return;
- setUpFullyClippedStack(m_fullyClippedStack, m_node);
- m_offset = m_node == m_startContainer ? m_startOffset : 0;
- m_handledNode = false;
- m_handledChildren = false;
- // calculate first out of bounds node
- m_pastEndNode = nextInPreOrderCrossingShadowBoundaries(endContainer, endOffset);
+ if (isClippedByFrameAncestor(m_node->document(), m_behavior))
+ return;
- // initialize node processing state
- m_needsAnotherNewline = false;
- m_textBox = 0;
+ setUpFullyClippedStack(m_fullyClippedStack, *m_node);
- // initialize record of previous node processing
- m_hasEmitted = false;
- m_lastTextNode = 0;
- m_lastTextNodeEndedWithCollapsedSpace = false;
- m_lastCharacter = 0;
+ m_offset = m_node == m_startContainer ? m_startOffset : 0;
+
+ m_pastEndNode = nextInPreOrderCrossingShadowBoundaries(*m_endContainer, m_endOffset);
-#ifndef NDEBUG
- // need this just because of the assert in advance()
m_positionNode = m_node;
-#endif
- // identify the first run
advance();
}
@@ -361,33 +381,95 @@ TextIterator::~TextIterator()
{
}
+static HTMLSlotElement* assignedAuthorSlot(Node& node)
+{
+ auto* slot = node.assignedSlot();
+ if (!slot || slot->containingShadowRoot()->mode() == ShadowRootMode::UserAgent)
+ return nullptr;
+ return slot;
+}
+
+static ShadowRoot* authorShadowRoot(Node& node)
+{
+ auto* shadowRoot = node.shadowRoot();
+ if (!shadowRoot || shadowRoot->mode() == ShadowRootMode::UserAgent)
+ return nullptr;
+ return shadowRoot;
+}
+
+// FIXME: Use ComposedTreeIterator instead. These functions are more expensive because they might do O(n) work.
+static inline Node* firstChildInFlatTreeIgnoringUserAgentShadow(Node& node)
+{
+ if (auto* shadowRoot = authorShadowRoot(node))
+ return shadowRoot->firstChild();
+ if (is<HTMLSlotElement>(node)) {
+ if (auto* assignedNodes = downcast<HTMLSlotElement>(node).assignedNodes())
+ return assignedNodes->at(0);
+ }
+ return node.firstChild();
+}
+
+static inline Node* nextSiblingInFlatTreeIgnoringUserAgentShadow(Node& node)
+{
+ if (auto* slot = assignedAuthorSlot(node)) {
+ auto* assignedNodes = slot->assignedNodes();
+ ASSERT(assignedNodes);
+ auto nodeIndex = assignedNodes->find(&node);
+ ASSERT(nodeIndex != notFound);
+ if (assignedNodes->size() > nodeIndex + 1)
+ return assignedNodes->at(nodeIndex + 1);
+ return nullptr;
+ }
+ return node.nextSibling();
+}
+
+static inline Node* firstChild(TextIteratorBehavior options, Node& node)
+{
+ if (UNLIKELY(options & TextIteratorTraversesFlatTree))
+ return firstChildInFlatTreeIgnoringUserAgentShadow(node);
+ return node.firstChild();
+}
+
+static inline Node* nextSibling(TextIteratorBehavior options, Node& node)
+{
+ if (UNLIKELY(options & TextIteratorTraversesFlatTree))
+ return nextSiblingInFlatTreeIgnoringUserAgentShadow(node);
+ return node.nextSibling();
+}
+
+static inline Node* parentNodeOrShadowHost(TextIteratorBehavior options, Node& node)
+{
+ if (UNLIKELY(options & TextIteratorTraversesFlatTree))
+ return node.parentInComposedTree();
+ return node.parentOrShadowHostNode();
+}
+
void TextIterator::advance()
{
- if (m_shouldStop)
- return;
+ ASSERT(!atEnd());
// reset the run information
- m_positionNode = 0;
- m_textLength = 0;
+ m_positionNode = nullptr;
+ m_copyableText.reset();
+ m_text = StringView();
// handle remembered node that needed a newline after the text node's newline
- if (m_needsAnotherNewline) {
+ if (m_nodeForAdditionalNewline) {
// Emit the extra newline, and position it *inside* m_node, after m_node's
// contents, in case it's a block, in the same way that we position the first
- // newline. The range for the emitted newline should start where the line
+ // newline. The range for the emitted newline should start where the line
// break begins.
// FIXME: It would be cleaner if we emitted two newlines during the last
// iteration, instead of using m_needsAnotherNewline.
- Node* baseNode = m_node->lastChild() ? m_node->lastChild() : m_node;
- emitCharacter('\n', baseNode->parentNode(), baseNode, 1, 1);
- m_needsAnotherNewline = false;
+ emitCharacter('\n', *m_nodeForAdditionalNewline->parentNode(), m_nodeForAdditionalNewline, 1, 1);
+ m_nodeForAdditionalNewline = nullptr;
return;
}
if (!m_textBox && m_remainingTextBox) {
m_textBox = m_remainingTextBox;
- m_remainingTextBox = 0;
- m_firstLetterText = 0;
+ m_remainingTextBox = nullptr;
+ m_firstLetterText = nullptr;
m_offset = 0;
}
// handle remembered text box
@@ -398,27 +480,24 @@ 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
// precedes the element
- if (m_node == m_endContainer && m_endOffset == 0) {
+ if (m_node == m_endContainer && !m_endOffset) {
representNodeOffsetZero();
- m_node = 0;
+ m_node = nullptr;
return;
}
- RenderObject* renderer = m_node->renderer();
+ auto* renderer = m_node->renderer();
if (!renderer) {
m_handledNode = true;
- m_handledChildren = true;
+ m_handledChildren = !((m_behavior & TextIteratorTraversesFlatTree) && is<Element>(*m_node) && downcast<Element>(*m_node).hasDisplayContents());
} else {
// handle current node according to its type
if (!m_handledNode) {
- if (renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) // FIXME: What about CDATA_SECTION_NODE?
+ if (renderer->isText() && m_node->isTextNode())
m_handledNode = handleTextNode();
else if (isRendererReplacedElement(renderer))
m_handledNode = handleReplacedElement();
@@ -431,28 +510,29 @@ void TextIterator::advance()
// find a new current node to handle in depth-first manner,
// calling exitNode() as we come back thru a parent node
- Node* next = m_handledChildren ? 0 : m_node->firstChild();
+ Node* next = m_handledChildren ? nullptr : firstChild(m_behavior, *m_node);
m_offset = 0;
if (!next) {
- next = m_node->nextSibling();
+ next = nextSibling(m_behavior, *m_node);
if (!next) {
- bool pastEnd = NodeTraversal::next(m_node) == m_pastEndNode;
- Node* parentNode = m_node->parentOrShadowHostNode();
+ bool pastEnd = NodeTraversal::next(*m_node) == m_pastEndNode;
+ Node* parentNode = parentNodeOrShadowHost(m_behavior, *m_node);
while (!next && parentNode) {
- if ((pastEnd && parentNode == m_endContainer) || m_endContainer->isDescendantOf(parentNode))
+ if ((pastEnd && parentNode == m_endContainer) || m_endContainer->isDescendantOf(*parentNode))
return;
bool haveRenderer = m_node->renderer();
+ Node* exitedNode = m_node;
m_node = parentNode;
m_fullyClippedStack.pop();
- parentNode = m_node->parentOrShadowHostNode();
+ parentNode = parentNodeOrShadowHost(m_behavior, *m_node);
if (haveRenderer)
- exitNode();
+ exitNode(exitedNode);
if (m_positionNode) {
m_handledNode = true;
m_handledChildren = true;
return;
}
- next = m_node->nextSibling();
+ next = nextSibling(m_behavior, *m_node);
}
}
m_fullyClippedStack.pop();
@@ -461,11 +541,11 @@ void TextIterator::advance()
// set the new current node
m_node = next;
if (m_node)
- pushFullyClippedState(m_fullyClippedStack, m_node);
+ pushFullyClippedState(m_fullyClippedStack, *m_node);
m_handledNode = false;
m_handledChildren = false;
m_handledFirstLetter = false;
- m_firstLetterText = 0;
+ m_firstLetterText = nullptr;
// how would this ever be?
if (m_positionNode)
@@ -473,122 +553,179 @@ void TextIterator::advance()
}
}
-UChar TextIterator::characterAt(unsigned index) const
+static bool hasVisibleTextNode(RenderText& renderer)
{
- ASSERT_WITH_SECURITY_IMPLICATION(index < static_cast<unsigned>(length()));
- if (!(index < static_cast<unsigned>(length())))
- return 0;
-
- if (!m_textCharacters)
- return string()[startOffset() + index];
+ if (renderer.style().visibility() == VISIBLE)
+ return true;
+ if (is<RenderTextFragment>(renderer)) {
+ if (auto firstLetter = downcast<RenderTextFragment>(renderer).firstLetter()) {
+ if (firstLetter->style().visibility() == VISIBLE)
+ return true;
+ }
+ }
+ return false;
+}
- return m_textCharacters[index];
+static unsigned textNodeOffsetInFlow(const Text& firstTextNodeInRange)
+{
+ // Calculate the text offset for simple lines.
+ RenderObject* renderer = firstTextNodeInRange.renderer();
+ if (!renderer)
+ return 0;
+ unsigned textOffset = 0;
+ for (renderer = renderer->previousSibling(); renderer; renderer = renderer->previousSibling()) {
+ if (is<RenderText>(renderer))
+ textOffset += downcast<RenderText>(renderer)->textLength();
+ }
+ return textOffset;
}
-void TextIterator::appendTextToStringBuilder(StringBuilder& builder) const
+static bool isNewLineOrTabCharacter(UChar character)
{
- if (!m_textCharacters)
- builder.append(string(), startOffset(), length());
- else
- builder.append(characters(), length());
+ return character == '\n' || character == '\t';
}
bool TextIterator::handleTextNode()
{
- if (m_fullyClippedStack.top() && !m_ignoresStyleVisibility)
+ Text& textNode = downcast<Text>(*m_node);
+
+ if (m_fullyClippedStack.top() && !(m_behavior & TextIteratorIgnoresStyleVisibility))
return false;
- RenderText* renderer = toRenderText(m_node->renderer());
-
- m_lastTextNode = m_node;
- String str = renderer->text();
+ auto& renderer = *textNode.renderer();
+ m_lastTextNode = &textNode;
+ String rendererText = renderer.text();
// handle pre-formatted text
- if (!renderer->style().collapseWhiteSpace()) {
+ if (!renderer.style().collapseWhiteSpace()) {
int runStart = m_offset;
if (m_lastTextNodeEndedWithCollapsedSpace && hasVisibleTextNode(renderer)) {
- emitCharacter(' ', m_node, 0, runStart, runStart);
+ emitCharacter(' ', textNode, nullptr, runStart, runStart);
return false;
}
- if (!m_handledFirstLetter && renderer->isTextFragment() && !m_offset) {
- handleTextNodeFirstLetter(static_cast<RenderTextFragment*>(renderer));
+ if (!m_handledFirstLetter && is<RenderTextFragment>(renderer) && !m_offset) {
+ handleTextNodeFirstLetter(downcast<RenderTextFragment>(renderer));
if (m_firstLetterText) {
String firstLetter = m_firstLetterText->text();
- emitText(m_node, m_firstLetterText, m_offset, m_offset + firstLetter.length());
- m_firstLetterText = 0;
- m_textBox = 0;
+ emitText(textNode, *m_firstLetterText, m_offset, m_offset + firstLetter.length());
+ m_firstLetterText = nullptr;
+ m_textBox = nullptr;
return false;
}
}
- if (renderer->style().visibility() != VISIBLE && !m_ignoresStyleVisibility)
+ if (renderer.style().visibility() != VISIBLE && !(m_behavior & TextIteratorIgnoresStyleVisibility))
return false;
- int strLength = str.length();
- int end = (m_node == m_endContainer) ? m_endOffset : INT_MAX;
- int runEnd = std::min(strLength, end);
+ int rendererTextLength = rendererText.length();
+ int end = (&textNode == m_endContainer) ? m_endOffset : INT_MAX;
+ int runEnd = std::min(rendererTextLength, end);
if (runStart >= runEnd)
return true;
- emitText(m_node, runStart, runEnd);
+ emitText(textNode, renderer, runStart, runEnd);
return true;
}
- if (renderer->simpleLineLayout()) {
- if (renderer->style().visibility() != VISIBLE && !m_ignoresStyleVisibility)
+ if (const auto* layout = renderer.simpleLineLayout()) {
+ if (renderer.style().visibility() != VISIBLE && !(m_behavior & TextIteratorIgnoresStyleVisibility))
return true;
- // This code aims to produce same results as handleTextBox() below so test results don't change. It does not make much logical sense.
- unsigned runEnd = m_offset;
- unsigned runStart = m_offset;
- while (runEnd < str.length() && (isCollapsibleWhitespace(str[runEnd]) || str[runEnd] == '\t'))
- ++runEnd;
- bool addSpaceForPrevious = m_lastTextNodeEndedWithCollapsedSpace && m_lastCharacter && !isCollapsibleWhitespace(m_lastCharacter);
- if (runEnd > runStart || addSpaceForPrevious) {
- if (runEnd == str.length()) {
- m_lastTextNodeEndedWithCollapsedSpace = true;
- return true;
+ ASSERT(renderer.parent());
+ ASSERT(is<RenderBlockFlow>(*renderer.parent()));
+ const auto& blockFlow = downcast<RenderBlockFlow>(*renderer.parent());
+ // Use the simple layout runs to iterate over the text content.
+ bool isNewTextNode = m_previousSimpleTextNodeInFlow && m_previousSimpleTextNodeInFlow != &textNode;
+ // Simple line layout run positions are all absolute to the parent flow.
+ // Offsetting is required when multiple renderers are present.
+ m_accumulatedSimpleTextLengthInFlow += isNewTextNode ? m_previousSimpleTextNodeInFlow->renderer()->text()->length() : 0;
+ m_previousSimpleTextNodeInFlow = &textNode;
+
+ unsigned endPosition = (m_node == m_endContainer) ? static_cast<unsigned>(m_endOffset) : rendererText.length();
+ if (!m_flowRunResolverCache || &m_flowRunResolverCache->flow() != &blockFlow) {
+ m_accumulatedSimpleTextLengthInFlow = m_flowRunResolverCache ? 0 : textNodeOffsetInFlow(textNode);
+ m_flowRunResolverCache = std::make_unique<SimpleLineLayout::RunResolver>(blockFlow, *layout);
+ }
+ // Skip to m_offset position.
+ auto range = m_flowRunResolverCache->rangeForRenderer(renderer);
+ auto it = range.begin();
+ auto end = range.end();
+ while (it != end && (*it).end() <= (static_cast<unsigned>(m_offset) + m_accumulatedSimpleTextLengthInFlow))
+ ++it;
+ if (m_nextRunNeedsWhitespace && rendererText[m_offset - 1] == '\n') {
+ emitCharacter(' ', textNode, nullptr, m_offset, m_offset + 1);
+ return it == end;
+ }
+ if (it == end) {
+ // Collapsed trailing whitespace.
+ m_offset = endPosition;
+ m_lastTextNodeEndedWithCollapsedSpace = true;
+ return true;
+ }
+ if (m_nextRunNeedsWhitespace) {
+ emitCharacter(' ', textNode, nullptr, m_offset, m_offset + 1);
+ return false;
+ }
+ const auto run = *it;
+ ASSERT(run.end() - run.start() <= rendererText.length());
+ // contentStart skips leading whitespace.
+ unsigned contentStart = std::max<unsigned>(m_offset, run.start() - m_accumulatedSimpleTextLengthInFlow);
+ unsigned contentEnd = std::min(endPosition, run.end() - m_accumulatedSimpleTextLengthInFlow);
+ ASSERT_WITH_SECURITY_IMPLICATION(contentStart <= contentEnd);
+ // Check if whitespace adjustment is needed when crossing renderer boundary.
+ if (isNewTextNode) {
+ bool lastCharacterIsNotWhitespace = m_lastCharacter && !renderer.style().isCollapsibleWhiteSpace(m_lastCharacter);
+ bool addTrailingWhitespaceForPrevious = m_lastTextNodeEndedWithCollapsedSpace && lastCharacterIsNotWhitespace;
+ bool leadingWhitespaceIsNeededForCurrent = contentStart > static_cast<unsigned>(m_offset) && lastCharacterIsNotWhitespace;
+ if (addTrailingWhitespaceForPrevious || leadingWhitespaceIsNeededForCurrent) {
+ emitCharacter(' ', textNode, nullptr, m_offset, m_offset + 1);
+ return false;
}
- bool addSpaceForCurrent = runStart || (m_lastCharacter && !isCollapsibleWhitespace(m_lastCharacter));
- if (addSpaceForCurrent || addSpaceForPrevious) {
- emitCharacter(' ', m_node, 0, runStart, runEnd);
- m_offset = runEnd;
+ }
+ // \n \t single whitespace characters need replacing so that the new line/tab characters don't show up.
+ unsigned stopPosition = contentStart;
+ while (stopPosition < contentEnd && !isNewLineOrTabCharacter(rendererText[stopPosition]))
+ ++stopPosition;
+ // Emit the text up to the new line/tab character.
+ if (stopPosition < contentEnd) {
+ if (stopPosition == contentStart) {
+ emitCharacter(' ', textNode, nullptr, contentStart, contentStart + 1);
+ m_offset = contentStart + 1;
return false;
}
- runStart = runEnd;
+ emitText(textNode, renderer, contentStart, stopPosition);
+ m_offset = stopPosition + 1;
+ m_nextRunNeedsWhitespace = true;
+ return false;
}
- while (runEnd < str.length() && !isCollapsibleWhitespace(str[runEnd]))
- ++runEnd;
- if (runStart < str.length())
- emitText(m_node, renderer, runStart, runEnd);
- m_offset = runEnd;
- return runEnd == str.length();
+ emitText(textNode, renderer, contentStart, contentEnd);
+ // When line ending with collapsed whitespace is present, we need to carry over one whitespace: foo(end of line)bar -> foo bar (otherwise we would end up with foobar).
+ m_nextRunNeedsWhitespace = run.isEndOfLine() && contentEnd < endPosition && renderer.style().isCollapsibleWhiteSpace(rendererText[contentEnd]);
+ m_offset = contentEnd;
+ return static_cast<unsigned>(m_offset) == endPosition;
}
- if (renderer->firstTextBox())
- m_textBox = renderer->firstTextBox();
+ if (renderer.firstTextBox())
+ m_textBox = renderer.firstTextBox();
- bool shouldHandleFirstLetter = !m_handledFirstLetter && renderer->isTextFragment() && !m_offset;
+ bool shouldHandleFirstLetter = !m_handledFirstLetter && is<RenderTextFragment>(renderer) && !m_offset;
if (shouldHandleFirstLetter)
- handleTextNodeFirstLetter(static_cast<RenderTextFragment*>(renderer));
+ handleTextNodeFirstLetter(downcast<RenderTextFragment>(renderer));
- if (!renderer->firstTextBox() && str.length() > 0 && !shouldHandleFirstLetter) {
- if (renderer->style().visibility() != VISIBLE && !m_ignoresStyleVisibility)
+ if (!renderer.firstTextBox() && rendererText.length() && !shouldHandleFirstLetter) {
+ if (renderer.style().visibility() != VISIBLE && !(m_behavior & TextIteratorIgnoresStyleVisibility))
return false;
m_lastTextNodeEndedWithCollapsedSpace = true; // entire block is collapsed space
return true;
}
- if (m_firstLetterText)
- renderer = m_firstLetterText;
-
// Used when text boxes are out of order (Hebrew/Arabic w/ embeded LTR text)
- if (renderer->containsReversedText()) {
+ auto& boxesRenderer = m_firstLetterText ? *m_firstLetterText : renderer;
+ if (boxesRenderer.containsReversedText()) {
m_sortedTextBoxes.clear();
- for (InlineTextBox* textBox = renderer->firstTextBox(); textBox; textBox = textBox->nextTextBox()) {
+ for (InlineTextBox* textBox = boxesRenderer.firstTextBox(); textBox; textBox = textBox->nextTextBox())
m_sortedTextBoxes.append(textBox);
- }
- std::sort(m_sortedTextBoxes.begin(), m_sortedTextBoxes.end(), InlineTextBox::compareByStart);
+ std::sort(m_sortedTextBoxes.begin(), m_sortedTextBoxes.end(), InlineTextBox::compareByStart);
m_sortedTextBoxesPosition = 0;
- m_textBox = m_sortedTextBoxes.isEmpty() ? 0 : m_sortedTextBoxes[0];
+ m_textBox = m_sortedTextBoxes.isEmpty() ? nullptr : m_sortedTextBoxes[0];
}
handleTextBox();
@@ -597,58 +734,62 @@ bool TextIterator::handleTextNode()
void TextIterator::handleTextBox()
{
- RenderText* renderer = m_firstLetterText ? m_firstLetterText : toRenderText(m_node->renderer());
- if (renderer->style().visibility() != VISIBLE && !m_ignoresStyleVisibility) {
- m_textBox = 0;
+ Text& textNode = downcast<Text>(*m_node);
+
+ auto& renderer = m_firstLetterText ? *m_firstLetterText : *textNode.renderer();
+ if (renderer.style().visibility() != VISIBLE && !(m_behavior & TextIteratorIgnoresStyleVisibility)) {
+ m_textBox = nullptr;
return;
}
- String str = renderer->text();
+ String rendererText = renderer.text();
unsigned start = m_offset;
- unsigned end = (m_node == m_endContainer) ? static_cast<unsigned>(m_endOffset) : UINT_MAX;
+ unsigned end = (&textNode == m_endContainer) ? static_cast<unsigned>(m_endOffset) : UINT_MAX;
while (m_textBox) {
unsigned textBoxStart = m_textBox->start();
unsigned runStart = std::max(textBoxStart, start);
// Check for collapsed space at the start of this run.
- InlineTextBox* firstTextBox = renderer->containsReversedText() ? (m_sortedTextBoxes.isEmpty() ? 0 : m_sortedTextBoxes[0]) : renderer->firstTextBox();
- bool needSpace = m_lastTextNodeEndedWithCollapsedSpace
- || (m_textBox == firstTextBox && textBoxStart == runStart && runStart > 0);
- if (needSpace && !isCollapsibleWhitespace(m_lastCharacter) && m_lastCharacter) {
- if (m_lastTextNode == m_node && runStart > 0 && str[runStart - 1] == ' ') {
+ InlineTextBox* firstTextBox = renderer.containsReversedText() ? (m_sortedTextBoxes.isEmpty() ? nullptr : m_sortedTextBoxes[0]) : renderer.firstTextBox();
+ bool needSpace = m_lastTextNodeEndedWithCollapsedSpace || (m_textBox == firstTextBox && textBoxStart == runStart && runStart);
+ if (needSpace && !renderer.style().isCollapsibleWhiteSpace(m_lastCharacter) && m_lastCharacter) {
+ if (m_lastTextNode == &textNode && runStart && rendererText[runStart - 1] == ' ') {
unsigned spaceRunStart = runStart - 1;
- while (spaceRunStart > 0 && str[spaceRunStart - 1] == ' ')
+ while (spaceRunStart && rendererText[spaceRunStart - 1] == ' ')
--spaceRunStart;
- emitText(m_node, renderer, spaceRunStart, spaceRunStart + 1);
+ emitText(textNode, renderer, spaceRunStart, spaceRunStart + 1);
} else
- emitCharacter(' ', m_node, 0, runStart, runStart);
+ emitCharacter(' ', textNode, nullptr, runStart, runStart);
return;
}
unsigned textBoxEnd = textBoxStart + m_textBox->len();
unsigned runEnd = std::min(textBoxEnd, end);
// Determine what the next text box will be, but don't advance yet
- InlineTextBox* nextTextBox = 0;
- if (renderer->containsReversedText()) {
+ InlineTextBox* nextTextBox = nullptr;
+ if (renderer.containsReversedText()) {
if (m_sortedTextBoxesPosition + 1 < m_sortedTextBoxes.size())
nextTextBox = m_sortedTextBoxes[m_sortedTextBoxesPosition + 1];
} else
nextTextBox = m_textBox->nextTextBox();
- ASSERT(!nextTextBox || &nextTextBox->renderer() == renderer);
+ ASSERT(!nextTextBox || &nextTextBox->renderer() == &renderer);
if (runStart < runEnd) {
// Handle either a single newline character (which becomes a space),
// or a run of characters that does not include a newline.
// This effectively translates newlines to spaces without copying the text.
- if (str[runStart] == '\n') {
- emitCharacter(' ', m_node, 0, runStart, runStart + 1);
+ if (rendererText[runStart] == '\n') {
+ emitCharacter(' ', textNode, nullptr, runStart, runStart + 1);
m_offset = runStart + 1;
} else {
- size_t subrunEnd = str.find('\n', runStart);
- if (subrunEnd == notFound || subrunEnd > runEnd)
+ size_t subrunEnd = rendererText.find('\n', runStart);
+ if (subrunEnd == notFound || subrunEnd > runEnd) {
subrunEnd = runEnd;
-
+ bool lastSpaceCollapsedByNextNonTextBox = !nextTextBox && (m_behavior & TextIteratorBehavesAsIfNodesFollowing) && rendererText.length() > runEnd;
+ if (lastSpaceCollapsedByNextNonTextBox)
+ ++subrunEnd; // runEnd stopped before last space. Increment by one to restore the space.
+ }
m_offset = subrunEnd;
- emitText(m_node, renderer, runStart, subrunEnd);
+ emitText(textNode, renderer, runStart, subrunEnd);
}
// If we are doing a subrun that doesn't go to the end of the text box,
@@ -657,23 +798,23 @@ void TextIterator::handleTextBox()
return;
// Advance and return
- unsigned nextRunStart = nextTextBox ? nextTextBox->start() : str.length();
+ unsigned nextRunStart = nextTextBox ? nextTextBox->start() : rendererText.length();
if (nextRunStart > runEnd)
m_lastTextNodeEndedWithCollapsedSpace = true; // collapsed space between runs or at the end
m_textBox = nextTextBox;
- if (renderer->containsReversedText())
+ if (renderer.containsReversedText())
++m_sortedTextBoxesPosition;
return;
}
// Advance and continue
m_textBox = nextTextBox;
- if (renderer->containsReversedText())
+ if (renderer.containsReversedText())
++m_sortedTextBoxesPosition;
}
if (!m_textBox && m_remainingTextBox) {
m_textBox = m_remainingTextBox;
- m_remainingTextBox = 0;
- m_firstLetterText = 0;
+ m_remainingTextBox = nullptr;
+ m_firstLetterText = nullptr;
m_offset = 0;
handleTextBox();
}
@@ -688,12 +829,12 @@ static inline RenderText* firstRenderTextInFirstLetter(RenderBoxModelObject* fir
return childrenOfType<RenderText>(*firstLetter).first();
}
-void TextIterator::handleTextNodeFirstLetter(RenderTextFragment* renderer)
+void TextIterator::handleTextNodeFirstLetter(RenderTextFragment& renderer)
{
- if (auto firstLetter = renderer->firstLetter()) {
- if (firstLetter->style().visibility() != VISIBLE && !m_ignoresStyleVisibility)
+ if (auto* firstLetter = renderer.firstLetter()) {
+ if (firstLetter->style().visibility() != VISIBLE && !(m_behavior & TextIteratorIgnoresStyleVisibility))
return;
- if (RenderText* firstLetterText = firstRenderTextInFirstLetter(firstLetter)) {
+ if (auto* firstLetterText = firstRenderTextInFirstLetter(firstLetter)) {
m_handledFirstLetter = true;
m_remainingTextBox = m_textBox;
m_textBox = firstLetterText->firstTextBox();
@@ -709,19 +850,19 @@ bool TextIterator::handleReplacedElement()
if (m_fullyClippedStack.top())
return false;
- RenderObject* renderer = m_node->renderer();
- if (renderer->style().visibility() != VISIBLE && !m_ignoresStyleVisibility)
+ auto& renderer = *m_node->renderer();
+ if (renderer.style().visibility() != VISIBLE && !(m_behavior & TextIteratorIgnoresStyleVisibility))
return false;
if (m_lastTextNodeEndedWithCollapsedSpace) {
- emitCharacter(' ', m_lastTextNode->parentNode(), m_lastTextNode, 1, 1);
+ emitCharacter(' ', *m_lastTextNode->parentNode(), m_lastTextNode, 1, 1);
return false;
}
- if (m_entersTextControls && renderer->isTextControl()) {
- if (TextControlInnerTextElement* innerTextElement = toRenderTextControl(renderer)->textFormControlElement().innerTextElement()) {
+ if ((m_behavior & TextIteratorEntersTextControls) && is<RenderTextControl>(renderer)) {
+ if (TextControlInnerTextElement* innerTextElement = downcast<RenderTextControl>(renderer).textFormControlElement().innerTextElement()) {
m_node = innerTextElement->containingShadowRoot();
- pushFullyClippedState(m_fullyClippedStack, m_node);
+ pushFullyClippedState(m_fullyClippedStack, *m_node);
m_offset = 0;
return false;
}
@@ -729,19 +870,19 @@ bool TextIterator::handleReplacedElement()
m_hasEmitted = true;
- if (m_emitsObjectReplacementCharacters && renderer && renderer->isReplaced()) {
- emitCharacter(objectReplacementCharacter, m_node->parentNode(), m_node, 0, 1);
+ if ((m_behavior & TextIteratorEmitsObjectReplacementCharacters) && renderer.isReplaced()) {
+ emitCharacter(objectReplacementCharacter, *m_node->parentNode(), m_node, 0, 1);
// Don't process subtrees for embedded objects. If the text there is required,
// it must be explicitly asked by specifying a range falling inside its boundaries.
m_handledChildren = true;
return true;
}
- if (m_emitsCharactersBetweenAllVisiblePositions) {
+ if (m_behavior & TextIteratorEmitsCharactersBetweenAllVisiblePositions) {
// We want replaced elements to behave like punctuation for boundary
// finding, and to simply take up space for the selection preservation
// code in moveParagraphs, so we use a comma.
- emitCharacter(',', m_node->parentNode(), m_node, 0, 1);
+ emitCharacter(',', *m_node->parentNode(), m_node, 0, 1);
return true;
}
@@ -749,174 +890,169 @@ bool TextIterator::handleReplacedElement()
m_positionOffsetBaseNode = m_node;
m_positionStartOffset = 0;
m_positionEndOffset = 1;
- m_textCharacters = 0;
- if (m_emitsImageAltText && renderer->isImage() && renderer->isRenderImage()) {
- m_text = toRenderImage(renderer)->altText();
- if (!m_text.isEmpty()) {
- m_textLength = m_text.length();
- m_lastCharacter = m_text[m_textLength - 1];
+ if ((m_behavior & TextIteratorEmitsImageAltText) && is<RenderImage>(renderer)) {
+ String altText = downcast<RenderImage>(renderer).altText();
+ if (unsigned length = altText.length()) {
+ m_lastCharacter = altText[length - 1];
+ m_copyableText.set(WTFMove(altText));
+ m_text = m_copyableText.text();
return true;
}
}
- m_textLength = 0;
+ m_copyableText.reset();
+ m_text = StringView();
m_lastCharacter = 0;
-
return true;
}
-bool TextIterator::hasVisibleTextNode(RenderText* renderer)
-{
- if (renderer->style().visibility() == VISIBLE)
- return true;
- if (renderer->isTextFragment()) {
- RenderTextFragment* fragment = static_cast<RenderTextFragment*>(renderer);
- if (fragment->firstLetter() && fragment->firstLetter()->style().visibility() == VISIBLE)
- return true;
- }
- return false;
-}
-
-static bool shouldEmitTabBeforeNode(Node* node)
+static bool shouldEmitTabBeforeNode(Node& node)
{
- RenderObject* r = node->renderer();
+ auto* renderer = node.renderer();
// Table cells are delimited by tabs.
- if (!r || !isTableCell(node))
+ if (!renderer || !isTableCell(&node))
return false;
- // Want a tab before every cell other than the first one
- RenderTableCell* rc = toRenderTableCell(r);
- RenderTable* t = rc->table();
- return t && (t->cellBefore(rc) || t->cellAbove(rc));
+ // Want a tab before every cell other than the first one.
+ RenderTableCell& cell = downcast<RenderTableCell>(*renderer);
+ RenderTable* table = cell.table();
+ return table && (table->cellBefore(&cell) || table->cellAbove(&cell));
}
static bool shouldEmitNewlineForNode(Node* node, bool emitsOriginalText)
{
- RenderObject* renderer = node->renderer();
-
- if (renderer ? !renderer->isBR() : !node->hasTagName(brTag))
+ auto* renderer = node->renderer();
+ if (!(renderer ? renderer->isBR() : node->hasTagName(brTag)))
return false;
- return emitsOriginalText || !(node->isInShadowTree() && node->shadowHost()->toInputElement());
+ return emitsOriginalText || !(node->isInShadowTree() && is<HTMLInputElement>(*node->shadowHost()));
+}
+
+static bool hasHeaderTag(HTMLElement& element)
+{
+ return element.hasTagName(h1Tag)
+ || element.hasTagName(h2Tag)
+ || element.hasTagName(h3Tag)
+ || element.hasTagName(h4Tag)
+ || element.hasTagName(h5Tag)
+ || element.hasTagName(h6Tag);
}
-static bool shouldEmitNewlinesBeforeAndAfterNode(Node* node)
+static bool shouldEmitNewlinesBeforeAndAfterNode(Node& node)
{
// Block flow (versus inline flow) is represented by having
// a newline both before and after the element.
- RenderObject* r = node->renderer();
- if (!r) {
- return (node->hasTagName(blockquoteTag)
- || node->hasTagName(ddTag)
- || node->hasTagName(divTag)
- || node->hasTagName(dlTag)
- || node->hasTagName(dtTag)
- || node->hasTagName(h1Tag)
- || node->hasTagName(h2Tag)
- || node->hasTagName(h3Tag)
- || node->hasTagName(h4Tag)
- || node->hasTagName(h5Tag)
- || node->hasTagName(h6Tag)
- || node->hasTagName(hrTag)
- || node->hasTagName(liTag)
- || node->hasTagName(listingTag)
- || node->hasTagName(olTag)
- || node->hasTagName(pTag)
- || node->hasTagName(preTag)
- || node->hasTagName(trTag)
- || node->hasTagName(ulTag));
+ auto* renderer = node.renderer();
+ if (!renderer) {
+ if (!is<HTMLElement>(node))
+ return false;
+ auto& element = downcast<HTMLElement>(node);
+ return hasHeaderTag(element)
+ || element.hasTagName(blockquoteTag)
+ || element.hasTagName(ddTag)
+ || element.hasTagName(divTag)
+ || element.hasTagName(dlTag)
+ || element.hasTagName(dtTag)
+ || element.hasTagName(hrTag)
+ || element.hasTagName(liTag)
+ || element.hasTagName(listingTag)
+ || element.hasTagName(olTag)
+ || element.hasTagName(pTag)
+ || element.hasTagName(preTag)
+ || element.hasTagName(trTag)
+ || element.hasTagName(ulTag);
}
// Need to make an exception for table cells, because they are blocks, but we
// want them tab-delimited rather than having newlines before and after.
- if (isTableCell(node))
+ if (isTableCell(&node))
return false;
// Need to make an exception for table row elements, because they are neither
// "inline" or "RenderBlock", but we want newlines for them.
- if (r->isTableRow()) {
- RenderTable* t = toRenderTableRow(r)->table();
- if (t && !t->isInline())
+ if (is<RenderTableRow>(*renderer)) {
+ RenderTable* table = downcast<RenderTableRow>(*renderer).table();
+ if (table && !table->isInline())
return true;
}
- return !r->isInline() && r->isRenderBlock()
- && !r->isFloatingOrOutOfFlowPositioned() && !r->isBody() && !r->isRubyText();
+ return !renderer->isInline()
+ && is<RenderBlock>(*renderer)
+ && !renderer->isFloatingOrOutOfFlowPositioned()
+ && !renderer->isBody()
+ && !renderer->isRubyText();
}
-static bool shouldEmitNewlineAfterNode(Node* node)
+static bool shouldEmitNewlineAfterNode(Node& node)
{
// FIXME: It should be better but slower to create a VisiblePosition here.
if (!shouldEmitNewlinesBeforeAndAfterNode(node))
return false;
// Check if this is the very last renderer in the document.
// If so, then we should not emit a newline.
- while ((node = NodeTraversal::nextSkippingChildren(node)))
- if (node->renderer())
+ Node* subsequentNode = &node;
+ while ((subsequentNode = NodeTraversal::nextSkippingChildren(*subsequentNode))) {
+ if (subsequentNode->renderer())
return true;
+ }
return false;
}
-static bool shouldEmitNewlineBeforeNode(Node* node)
+static bool shouldEmitNewlineBeforeNode(Node& node)
{
return shouldEmitNewlinesBeforeAndAfterNode(node);
}
-static bool shouldEmitExtraNewlineForNode(Node* node)
+static bool shouldEmitExtraNewlineForNode(Node& node)
{
// When there is a significant collapsed bottom margin, emit an extra
- // newline for a more realistic result. We end up getting the right
+ // newline for a more realistic result. We end up getting the right
// result even without margin collapsing. For example: <div><p>text</p></div>
// will work right even if both the <div> and the <p> have bottom margins.
- RenderObject* r = node->renderer();
- if (!r || !r->isBox())
+
+ auto* renderer = node.renderer();
+ if (!is<RenderBox>(renderer))
return false;
-
- // NOTE: We only do this for a select set of nodes, and fwiw WinIE appears
- // not to do this at all
- if (node->hasTagName(h1Tag)
- || node->hasTagName(h2Tag)
- || node->hasTagName(h3Tag)
- || node->hasTagName(h4Tag)
- || node->hasTagName(h5Tag)
- || node->hasTagName(h6Tag)
- || node->hasTagName(pTag)) {
- int bottomMargin = toRenderBox(r)->collapsedMarginAfter();
- int fontSize = toRenderBox(r)->style().fontDescription().computedPixelSize();
- if (bottomMargin * 2 >= fontSize)
- return true;
- }
-
- return false;
+
+ // NOTE: We only do this for a select set of nodes, and WinIE appears not to do this at all.
+ if (!is<HTMLElement>(node))
+ return false;
+
+ HTMLElement& element = downcast<HTMLElement>(node);
+ if (!hasHeaderTag(element) && !is<HTMLParagraphElement>(element))
+ return false;
+
+ int bottomMargin = downcast<RenderBox>(*renderer).collapsedMarginAfter();
+ int fontSize = downcast<RenderBox>(*renderer).style().fontDescription().computedPixelSize();
+ return bottomMargin * 2 >= fontSize;
}
-static int collapsedSpaceLength(RenderText* renderer, int textEnd)
+static int collapsedSpaceLength(RenderText& renderer, int textEnd)
{
- const UChar* characters = renderer->text()->deprecatedCharacters();
- int length = renderer->text()->length();
- for (int i = textEnd; i < length; ++i) {
- if (!renderer->style().isCollapsibleWhiteSpace(characters[i]))
+ StringImpl& text = *renderer.text();
+ unsigned length = text.length();
+ for (unsigned i = textEnd; i < length; ++i) {
+ if (!renderer.style().isCollapsibleWhiteSpace(text[i]))
return i - textEnd;
}
-
return length - textEnd;
}
-static int maxOffsetIncludingCollapsedSpaces(Node* node)
+static int maxOffsetIncludingCollapsedSpaces(Node& node)
{
int offset = caretMaxOffset(node);
-
- if (node->renderer() && node->renderer()->isText())
- offset += collapsedSpaceLength(toRenderText(node->renderer()), offset);
-
+ if (auto* renderer = node.renderer()) {
+ if (is<RenderText>(*renderer))
+ offset += collapsedSpaceLength(downcast<RenderText>(*renderer), offset);
+ }
return offset;
}
// Whether or not we should emit a character as we enter m_node (if it's a container) or as we hit it (if it's atomic).
bool TextIterator::shouldRepresentNodeOffsetZero()
{
- if (m_emitsCharactersBetweenAllVisiblePositions && m_node->renderer() && m_node->renderer()->isTable())
+ if ((m_behavior & TextIteratorEmitsCharactersBetweenAllVisiblePositions) && m_node->renderer() && m_node->renderer()->isTable())
return true;
// Leave element positioned flush with start of a paragraph
@@ -958,7 +1094,7 @@ bool TextIterator::shouldRepresentNodeOffsetZero()
// Additionally, if the range we are iterating over contains huge sections of unrendered content,
// we would create VisiblePositions on every call to this function without this check.
if (!m_node->renderer() || m_node->renderer()->style().visibility() != VISIBLE
- || (m_node->renderer()->isRenderBlockFlow() && !toRenderBlock(m_node->renderer())->height() && !m_node->hasTagName(bodyTag)))
+ || (is<RenderBlockFlow>(*m_node->renderer()) && !downcast<RenderBlockFlow>(*m_node->renderer()).height() && !is<HTMLBodyElement>(*m_node)))
return false;
// The startPos.isNotNull() check is needed because the start could be before the body,
@@ -970,9 +1106,9 @@ bool TextIterator::shouldRepresentNodeOffsetZero()
return startPos.isNotNull() && currPos.isNotNull() && !inSameLine(startPos, currPos);
}
-bool TextIterator::shouldEmitSpaceBeforeAndAfterNode(Node* node)
+bool TextIterator::shouldEmitSpaceBeforeAndAfterNode(Node& node)
{
- return node->renderer() && node->renderer()->isTable() && (node->renderer()->isInline() || m_emitsCharactersBetweenAllVisiblePositions);
+ return node.renderer() && node.renderer()->isTable() && (node.renderer()->isInline() || (m_behavior & TextIteratorEmitsCharactersBetweenAllVisiblePositions));
}
void TextIterator::representNodeOffsetZero()
@@ -980,34 +1116,34 @@ void TextIterator::representNodeOffsetZero()
// Emit a character to show the positioning of m_node.
// When we haven't been emitting any characters, shouldRepresentNodeOffsetZero() can
- // create VisiblePositions, which is expensive. So, we perform the inexpensive checks
+ // create VisiblePositions, which is expensive. So, we perform the inexpensive checks
// on m_node to see if it necessitates emitting a character first and will early return
// before encountering shouldRepresentNodeOffsetZero()s worse case behavior.
- if (shouldEmitTabBeforeNode(m_node)) {
+ if (shouldEmitTabBeforeNode(*m_node)) {
if (shouldRepresentNodeOffsetZero())
- emitCharacter('\t', m_node->parentNode(), m_node, 0, 0);
- } else if (shouldEmitNewlineBeforeNode(m_node)) {
+ emitCharacter('\t', *m_node->parentNode(), m_node, 0, 0);
+ } else if (shouldEmitNewlineBeforeNode(*m_node)) {
if (shouldRepresentNodeOffsetZero())
- emitCharacter('\n', m_node->parentNode(), m_node, 0, 0);
- } else if (shouldEmitSpaceBeforeAndAfterNode(m_node)) {
+ emitCharacter('\n', *m_node->parentNode(), m_node, 0, 0);
+ } else if (shouldEmitSpaceBeforeAndAfterNode(*m_node)) {
if (shouldRepresentNodeOffsetZero())
- emitCharacter(' ', m_node->parentNode(), m_node, 0, 0);
+ emitCharacter(' ', *m_node->parentNode(), m_node, 0, 0);
}
}
bool TextIterator::handleNonTextNode()
{
- if (shouldEmitNewlineForNode(m_node, m_emitsOriginalText))
- emitCharacter('\n', m_node->parentNode(), m_node, 0, 1);
- else if (m_emitsCharactersBetweenAllVisiblePositions && m_node->renderer() && m_node->renderer()->isHR())
- emitCharacter(' ', m_node->parentNode(), m_node, 0, 1);
+ if (shouldEmitNewlineForNode(m_node, m_behavior & TextIteratorEmitsOriginalText))
+ emitCharacter('\n', *m_node->parentNode(), m_node, 0, 1);
+ else if ((m_behavior & TextIteratorEmitsCharactersBetweenAllVisiblePositions) && m_node->renderer() && m_node->renderer()->isHR())
+ emitCharacter(' ', *m_node->parentNode(), m_node, 0, 1);
else
representNodeOffsetZero();
return true;
}
-void TextIterator::exitNode()
+void TextIterator::exitNode(Node* exitedNode)
{
// prevent emitting a newline when exiting a collapsed block at beginning of the range
// FIXME: !m_hasEmitted does not necessarily mean there was a collapsed block... it could
@@ -1019,212 +1155,160 @@ void TextIterator::exitNode()
// Emit with a position *inside* m_node, after m_node's contents, in
// case it is a block, because the run should start where the
// emitted character is positioned visually.
- Node* baseNode = m_node->lastChild() ? m_node->lastChild() : m_node;
+ Node* baseNode = exitedNode;
// FIXME: This shouldn't require the m_lastTextNode to be true, but we can't change that without making
- // the logic in _web_attributedStringFromRange match. We'll get that for free when we switch to use
+ // the logic in _web_attributedStringFromRange match. We'll get that for free when we switch to use
// TextIterator in _web_attributedStringFromRange.
// See <rdar://problem/5428427> for an example of how this mismatch will cause problems.
- if (m_lastTextNode && shouldEmitNewlineAfterNode(m_node)) {
+ if (m_lastTextNode && shouldEmitNewlineAfterNode(*m_node)) {
// use extra newline to represent margin bottom, as needed
- bool addNewline = shouldEmitExtraNewlineForNode(m_node);
+ bool addNewline = shouldEmitExtraNewlineForNode(*m_node);
// FIXME: We need to emit a '\n' as we leave an empty block(s) that
// contain a VisiblePosition when doing selection preservation.
if (m_lastCharacter != '\n') {
// insert a newline with a position following this block's contents.
- emitCharacter('\n', baseNode->parentNode(), baseNode, 1, 1);
+ emitCharacter('\n', *baseNode->parentNode(), baseNode, 1, 1);
// remember whether to later add a newline for the current node
- ASSERT(!m_needsAnotherNewline);
- m_needsAnotherNewline = addNewline;
+ ASSERT(!m_nodeForAdditionalNewline);
+ if (addNewline)
+ m_nodeForAdditionalNewline = baseNode;
} else if (addNewline)
// insert a newline with a position following this block's contents.
- emitCharacter('\n', baseNode->parentNode(), baseNode, 1, 1);
+ emitCharacter('\n', *baseNode->parentNode(), baseNode, 1, 1);
}
// If nothing was emitted, see if we need to emit a space.
- if (!m_positionNode && shouldEmitSpaceBeforeAndAfterNode(m_node))
- emitCharacter(' ', baseNode->parentNode(), baseNode, 1, 1);
+ if (!m_positionNode && shouldEmitSpaceBeforeAndAfterNode(*m_node))
+ emitCharacter(' ', *baseNode->parentNode(), baseNode, 1, 1);
}
-void TextIterator::emitCharacter(UChar c, Node* textNode, Node* offsetBaseNode, int textStartOffset, int textEndOffset)
+void TextIterator::emitCharacter(UChar character, Node& characterNode, Node* offsetBaseNode, int textStartOffset, int textEndOffset)
{
m_hasEmitted = true;
// remember information with which to construct the TextIterator::range()
- // NOTE: textNode is often not a text node, so the range will specify child nodes of positionNode
- m_positionNode = textNode;
+ m_positionNode = &characterNode;
m_positionOffsetBaseNode = offsetBaseNode;
m_positionStartOffset = textStartOffset;
m_positionEndOffset = textEndOffset;
-
- // remember information with which to construct the TextIterator::characters() and length()
- m_singleCharacterBuffer = c;
- m_textCharacters = &m_singleCharacterBuffer;
- m_textLength = 1;
- // remember some iteration state
+ m_copyableText.set(character);
+ m_text = m_copyableText.text();
+ m_lastCharacter = character;
m_lastTextNodeEndedWithCollapsedSpace = false;
- m_lastCharacter = c;
+ m_nextRunNeedsWhitespace = false;
}
-void TextIterator::emitText(Node* textNode, RenderObject* renderObject, int textStartOffset, int textEndOffset)
+void TextIterator::emitText(Text& textNode, RenderText& renderer, int textStartOffset, int textEndOffset)
{
- RenderText* renderer = toRenderText(renderObject);
- m_text = m_emitsOriginalText ? renderer->originalText() : (m_emitsTextWithoutTranscoding ? renderer->textWithoutConvertingBackslashToYenSymbol() : renderer->text());
- ASSERT(!m_text.isEmpty());
- ASSERT(0 <= textStartOffset && textStartOffset < static_cast<int>(m_text.length()));
- ASSERT(0 <= textEndOffset && textEndOffset <= static_cast<int>(m_text.length()));
+ ASSERT(textStartOffset >= 0);
+ ASSERT(textEndOffset >= 0);
ASSERT(textStartOffset <= textEndOffset);
- m_positionNode = textNode;
- m_positionOffsetBaseNode = 0;
+ // FIXME: This probably yields the wrong offsets when text-transform: lowercase turns a single character into two characters.
+ String string = (m_behavior & TextIteratorEmitsOriginalText) ? renderer.originalText()
+ : ((m_behavior & TextIteratorEmitsTextsWithoutTranscoding) ? renderer.textWithoutConvertingBackslashToYenSymbol() : renderer.text());
+
+ ASSERT(string.length() >= static_cast<unsigned>(textEndOffset));
+
+ m_positionNode = &textNode;
+ m_positionOffsetBaseNode = nullptr;
m_positionStartOffset = textStartOffset;
m_positionEndOffset = textEndOffset;
- m_textCharacters = 0;
- m_textLength = textEndOffset - textStartOffset;
- m_lastCharacter = m_text[textEndOffset - 1];
+
+ m_lastCharacter = string[textEndOffset - 1];
+ m_copyableText.set(WTFMove(string), textStartOffset, textEndOffset - textStartOffset);
+ m_text = m_copyableText.text();
m_lastTextNodeEndedWithCollapsedSpace = false;
+ m_nextRunNeedsWhitespace = false;
m_hasEmitted = true;
}
-void TextIterator::emitText(Node* textNode, int textStartOffset, int textEndOffset)
+Ref<Range> TextIterator::range() const
{
- emitText(textNode, m_node->renderer(), textStartOffset, textEndOffset);
-}
+ ASSERT(!atEnd());
-PassRefPtr<Range> TextIterator::range() const
-{
// use the current run information, if we have it
- if (m_positionNode) {
- if (m_positionOffsetBaseNode) {
- int index = m_positionOffsetBaseNode->nodeIndex();
- m_positionStartOffset += index;
- m_positionEndOffset += index;
- m_positionOffsetBaseNode = 0;
- }
- return Range::create(m_positionNode->document(), m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset);
+ if (m_positionOffsetBaseNode) {
+ unsigned index = m_positionOffsetBaseNode->computeNodeIndex();
+ m_positionStartOffset += index;
+ m_positionEndOffset += index;
+ m_positionOffsetBaseNode = nullptr;
}
-
- // otherwise, return the end of the overall range we were given
- if (m_endContainer)
- return Range::create(m_endContainer->document(), m_endContainer, m_endOffset, m_endContainer, m_endOffset);
-
- return 0;
+ return Range::create(m_positionNode->document(), m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset);
}
Node* TextIterator::node() const
{
- RefPtr<Range> textRange = range();
- if (!textRange)
- return 0;
+ Ref<Range> textRange = range();
- Node* node = textRange->startContainer();
- if (!node)
- return 0;
- if (node->offsetInCharacters())
- return node;
+ Node& node = textRange->startContainer();
+ if (node.offsetInCharacters())
+ return &node;
- return node->childNode(textRange->startOffset());
+ return node.traverseToChildAt(textRange->startOffset());
}
// --------
-SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range* r, TextIteratorBehavior behavior)
- : m_node(0)
- , m_offset(0)
- , m_handledNode(false)
- , m_handledChildren(false)
- , m_startNode(0)
- , m_startOffset(0)
- , m_endNode(0)
- , m_endOffset(0)
- , m_positionNode(0)
- , m_positionStartOffset(0)
- , m_positionEndOffset(0)
- , m_textCharacters(0)
- , m_textLength(0)
- , m_lastTextNode(0)
- , m_lastCharacter(0)
- , m_singleCharacterBuffer(0)
- , m_havePassedStartNode(false)
- , m_shouldHandleFirstLetter(false)
- , m_stopsOnFormControls(behavior & TextIteratorStopsOnFormControls)
- , m_shouldStop(false)
- , m_emitsOriginalText(false)
-{
- ASSERT(behavior == TextIteratorDefaultBehavior || behavior == TextIteratorStopsOnFormControls);
-
- if (!r)
- return;
-
- r->ownerDocument().updateLayoutIgnorePendingStylesheets();
+SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range& range)
+{
+ range.ownerDocument().updateLayoutIgnorePendingStylesheets();
- Node* startNode = r->startContainer();
- if (!startNode)
- return;
- Node* endNode = r->endContainer();
- int startOffset = r->startOffset();
- int endOffset = r->endOffset();
+ Node* startNode = &range.startContainer();
+ Node* endNode = &range.endContainer();
+ int startOffset = range.startOffset();
+ int endOffset = range.endOffset();
if (!startNode->offsetInCharacters()) {
- if (startOffset >= 0 && startOffset < static_cast<int>(startNode->childNodeCount())) {
- startNode = startNode->childNode(startOffset);
+ if (startOffset >= 0 && startOffset < static_cast<int>(startNode->countChildNodes())) {
+ startNode = startNode->traverseToChildAt(startOffset);
startOffset = 0;
}
}
if (!endNode->offsetInCharacters()) {
- if (endOffset > 0 && endOffset <= static_cast<int>(endNode->childNodeCount())) {
- endNode = endNode->childNode(endOffset - 1);
+ if (endOffset > 0 && endOffset <= static_cast<int>(endNode->countChildNodes())) {
+ endNode = endNode->traverseToChildAt(endOffset - 1);
endOffset = lastOffsetInNode(endNode);
}
}
m_node = endNode;
- setUpFullyClippedStack(m_fullyClippedStack, m_node);
+ setUpFullyClippedStack(m_fullyClippedStack, *m_node);
m_offset = endOffset;
m_handledNode = false;
m_handledChildren = endOffset == 0;
- m_startNode = startNode;
+ m_startContainer = startNode;
m_startOffset = startOffset;
- m_endNode = endNode;
+ m_endContainer = endNode;
m_endOffset = endOffset;
-#ifndef NDEBUG
- // Need this just because of the assert.
m_positionNode = endNode;
-#endif
- m_lastTextNode = 0;
+ m_lastTextNode = nullptr;
m_lastCharacter = '\n';
- m_havePassedStartNode = false;
+ m_havePassedStartContainer = false;
advance();
}
void SimplifiedBackwardsTextIterator::advance()
{
- ASSERT(m_positionNode);
+ ASSERT(!atEnd());
- if (m_shouldStop)
- return;
+ m_positionNode = nullptr;
+ m_copyableText.reset();
+ m_text = StringView();
- if (m_stopsOnFormControls && HTMLFormControlElement::enclosingFormControlElement(m_node)) {
- m_shouldStop = true;
- return;
- }
-
- m_positionNode = 0;
- m_textLength = 0;
-
- while (m_node && !m_havePassedStartNode) {
+ while (m_node && !m_havePassedStartContainer) {
// Don't handle node if we start iterating at [node, 0].
- if (!m_handledNode && !(m_node == m_endNode && m_endOffset == 0)) {
- RenderObject* renderer = m_node->renderer();
- if (renderer && renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) {
- // FIXME: What about CDATA_SECTION_NODE?
+ if (!m_handledNode && !(m_node == m_endContainer && !m_endOffset)) {
+ auto* renderer = m_node->renderer();
+ if (renderer && renderer->isText() && m_node->isTextNode()) {
if (renderer->style().visibility() == VISIBLE && m_offset > 0)
m_handledNode = handleTextNode();
} else if (renderer && (renderer->isImage() || renderer->isWidget())) {
@@ -1238,14 +1322,11 @@ void SimplifiedBackwardsTextIterator::advance()
if (!m_handledChildren && m_node->hasChildNodes()) {
m_node = m_node->lastChild();
- pushFullyClippedState(m_fullyClippedStack, m_node);
+ pushFullyClippedState(m_fullyClippedStack, *m_node);
} else {
// Exit empty containers as we pass over them or containers
// where [container, 0] is where we started iterating.
- if (!m_handledNode
- && canHaveChildrenForEditing(m_node)
- && m_node->parentNode()
- && (!m_node->lastChild() || (m_node == m_endNode && !m_endOffset))) {
+ if (!m_handledNode && canHaveChildrenForEditing(*m_node) && m_node->parentNode() && (!m_node->lastChild() || (m_node == m_endContainer && !m_endOffset))) {
exitNode();
if (m_positionNode) {
m_handledNode = true;
@@ -1269,14 +1350,14 @@ void SimplifiedBackwardsTextIterator::advance()
m_fullyClippedStack.pop();
if (advanceRespectingRange(m_node->previousSibling()))
- pushFullyClippedState(m_fullyClippedStack, m_node);
+ pushFullyClippedState(m_fullyClippedStack, *m_node);
else
- m_node = 0;
+ m_node = nullptr;
}
// For the purpose of word boundary detection,
// we should iterate all visible text and trailing (collapsed) whitespaces.
- m_offset = m_node ? maxOffsetIncludingCollapsedSpaces(m_node) : 0;
+ m_offset = m_node ? maxOffsetIncludingCollapsedSpaces(*m_node) : 0;
m_handledNode = false;
m_handledChildren = false;
@@ -1287,7 +1368,9 @@ void SimplifiedBackwardsTextIterator::advance()
bool SimplifiedBackwardsTextIterator::handleTextNode()
{
- m_lastTextNode = m_node;
+ Text& textNode = downcast<Text>(*m_node);
+
+ m_lastTextNode = &textNode;
int startOffset;
int offsetInNode;
@@ -1296,98 +1379,105 @@ bool SimplifiedBackwardsTextIterator::handleTextNode()
return true;
String text = renderer->text();
- if (!renderer->firstTextBox() && text.length() > 0)
+ if (!renderer->hasRenderedText() && text.length())
+ return true;
+
+ if (startOffset + offsetInNode == m_offset) {
+ ASSERT(!m_shouldHandleFirstLetter);
return true;
+ }
m_positionEndOffset = m_offset;
m_offset = startOffset + offsetInNode;
m_positionNode = m_node;
m_positionStartOffset = m_offset;
- ASSERT(0 <= m_positionStartOffset - offsetInNode && m_positionStartOffset - offsetInNode <= static_cast<int>(text.length()));
- ASSERT(1 <= m_positionEndOffset - offsetInNode && m_positionEndOffset - offsetInNode <= static_cast<int>(text.length()));
- ASSERT(m_positionStartOffset <= m_positionEndOffset);
-
- m_textLength = m_positionEndOffset - m_positionStartOffset;
- m_textCharacters = text.deprecatedCharacters() + (m_positionStartOffset - offsetInNode);
- ASSERT(m_textCharacters >= text.deprecatedCharacters());
- ASSERT(m_textCharacters + m_textLength <= text.deprecatedCharacters() + static_cast<int>(text.length()));
+ ASSERT(m_positionStartOffset < m_positionEndOffset);
+ ASSERT(m_positionStartOffset - offsetInNode >= 0);
+ ASSERT(m_positionEndOffset - offsetInNode > 0);
+ ASSERT(m_positionEndOffset - offsetInNode <= static_cast<int>(text.length()));
- m_lastCharacter = text[m_positionEndOffset - 1];
+ m_lastCharacter = text[m_positionEndOffset - offsetInNode - 1];
+ m_copyableText.set(WTFMove(text), m_positionStartOffset - offsetInNode, m_positionEndOffset - m_positionStartOffset);
+ m_text = m_copyableText.text();
return !m_shouldHandleFirstLetter;
}
RenderText* SimplifiedBackwardsTextIterator::handleFirstLetter(int& startOffset, int& offsetInNode)
{
- RenderText* renderer = toRenderText(m_node->renderer());
- startOffset = (m_node == m_startNode) ? m_startOffset : 0;
+ RenderText& renderer = downcast<RenderText>(*m_node->renderer());
+ startOffset = (m_node == m_startContainer) ? m_startOffset : 0;
- if (!renderer->isTextFragment()) {
+ if (!is<RenderTextFragment>(renderer)) {
offsetInNode = 0;
- return renderer;
+ return &renderer;
}
- RenderTextFragment* fragment = toRenderTextFragment(renderer);
- int offsetAfterFirstLetter = fragment->start();
+ RenderTextFragment& fragment = downcast<RenderTextFragment>(renderer);
+ int offsetAfterFirstLetter = fragment.start();
if (startOffset >= offsetAfterFirstLetter) {
ASSERT(!m_shouldHandleFirstLetter);
offsetInNode = offsetAfterFirstLetter;
- return renderer;
+ return &renderer;
}
- if (!m_shouldHandleFirstLetter && offsetAfterFirstLetter < m_offset) {
+ if (!m_shouldHandleFirstLetter && startOffset + offsetAfterFirstLetter < m_offset) {
m_shouldHandleFirstLetter = true;
offsetInNode = offsetAfterFirstLetter;
- return renderer;
+ return &renderer;
}
m_shouldHandleFirstLetter = false;
offsetInNode = 0;
- return firstRenderTextInFirstLetter(fragment->firstLetter());
+ RenderText* firstLetterRenderer = firstRenderTextInFirstLetter(fragment.firstLetter());
+
+ m_offset = firstLetterRenderer->caretMaxOffset();
+ m_offset += collapsedSpaceLength(*firstLetterRenderer, m_offset);
+
+ return firstLetterRenderer;
}
bool SimplifiedBackwardsTextIterator::handleReplacedElement()
{
- unsigned index = m_node->nodeIndex();
+ unsigned index = m_node->computeNodeIndex();
// We want replaced elements to behave like punctuation for boundary
// finding, and to simply take up space for the selection preservation
- // code in moveParagraphs, so we use a comma. Unconditionally emit
+ // code in moveParagraphs, so we use a comma. Unconditionally emit
// here because this iterator is only used for boundary finding.
- emitCharacter(',', m_node->parentNode(), index, index + 1);
+ emitCharacter(',', *m_node->parentNode(), index, index + 1);
return true;
}
bool SimplifiedBackwardsTextIterator::handleNonTextNode()
{
// We can use a linefeed in place of a tab because this simple iterator is only used to
- // find boundaries, not actual content. A linefeed breaks words, sentences, and paragraphs.
- if (shouldEmitNewlineForNode(m_node, m_emitsOriginalText) || shouldEmitNewlineAfterNode(m_node) || shouldEmitTabBeforeNode(m_node)) {
- unsigned index = m_node->nodeIndex();
+ // find boundaries, not actual content. A linefeed breaks words, sentences, and paragraphs.
+ if (shouldEmitNewlineForNode(m_node, m_behavior & TextIteratorEmitsOriginalText) || shouldEmitNewlineAfterNode(*m_node) || shouldEmitTabBeforeNode(*m_node)) {
+ unsigned index = m_node->computeNodeIndex();
// The start of this emitted range is wrong. Ensuring correctness would require
// VisiblePositions and so would be slow. previousBoundary expects this.
- emitCharacter('\n', m_node->parentNode(), index + 1, index + 1);
+ emitCharacter('\n', *m_node->parentNode(), index + 1, index + 1);
}
return true;
}
void SimplifiedBackwardsTextIterator::exitNode()
{
- if (shouldEmitNewlineForNode(m_node, m_emitsOriginalText) || shouldEmitNewlineBeforeNode(m_node) || shouldEmitTabBeforeNode(m_node)) {
+ if (shouldEmitNewlineForNode(m_node, m_behavior & TextIteratorEmitsOriginalText) || shouldEmitNewlineBeforeNode(*m_node) || shouldEmitTabBeforeNode(*m_node)) {
// The start of this emitted range is wrong. Ensuring correctness would require
// VisiblePositions and so would be slow. previousBoundary expects this.
- emitCharacter('\n', m_node, 0, 0);
+ emitCharacter('\n', *m_node, 0, 0);
}
}
-void SimplifiedBackwardsTextIterator::emitCharacter(UChar c, Node* node, int startOffset, int endOffset)
+void SimplifiedBackwardsTextIterator::emitCharacter(UChar c, Node& node, int startOffset, int endOffset)
{
- m_singleCharacterBuffer = c;
- m_positionNode = node;
+ m_positionNode = &node;
m_positionStartOffset = startOffset;
m_positionEndOffset = endOffset;
- m_textCharacters = &m_singleCharacterBuffer;
- m_textLength = 1;
+ m_copyableText.set(c);
+ m_text = m_copyableText.text();
m_lastCharacter = c;
}
@@ -1395,48 +1485,47 @@ bool SimplifiedBackwardsTextIterator::advanceRespectingRange(Node* next)
{
if (!next)
return false;
- m_havePassedStartNode |= m_node == m_startNode;
- if (m_havePassedStartNode)
+ m_havePassedStartContainer |= m_node == m_startContainer;
+ if (m_havePassedStartContainer)
return false;
m_node = next;
return true;
}
-PassRefPtr<Range> SimplifiedBackwardsTextIterator::range() const
+Ref<Range> SimplifiedBackwardsTextIterator::range() const
{
- if (m_positionNode)
- return Range::create(m_positionNode->document(), m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset);
-
- return Range::create(m_startNode->document(), m_startNode, m_startOffset, m_startNode, m_startOffset);
+ ASSERT(!atEnd());
+
+ return Range::create(m_positionNode->document(), m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset);
}
// --------
-CharacterIterator::CharacterIterator(const Range* r, TextIteratorBehavior behavior)
- : m_offset(0)
+CharacterIterator::CharacterIterator(const Range& range, TextIteratorBehavior behavior)
+ : m_underlyingIterator(&range, behavior)
+ , m_offset(0)
, m_runOffset(0)
, m_atBreak(true)
- , m_textIterator(r, behavior)
{
- while (!atEnd() && m_textIterator.length() == 0)
- m_textIterator.advance();
+ while (!atEnd() && !m_underlyingIterator.text().length())
+ m_underlyingIterator.advance();
}
-PassRefPtr<Range> CharacterIterator::range() const
+Ref<Range> CharacterIterator::range() const
{
- RefPtr<Range> r = m_textIterator.range();
- if (!m_textIterator.atEnd()) {
- if (m_textIterator.length() <= 1) {
+ Ref<Range> range = m_underlyingIterator.range();
+ if (!m_underlyingIterator.atEnd()) {
+ if (m_underlyingIterator.text().length() <= 1) {
ASSERT(m_runOffset == 0);
} else {
- Node* n = r->startContainer();
- ASSERT(n == r->endContainer());
- int offset = r->startOffset() + m_runOffset;
- r->setStart(n, offset, ASSERT_NO_EXCEPTION);
- r->setEnd(n, offset + 1, ASSERT_NO_EXCEPTION);
+ Node& node = range->startContainer();
+ ASSERT(&node == &range->endContainer());
+ int offset = range->startOffset() + m_runOffset;
+ range->setStart(node, offset);
+ range->setEnd(node, offset + 1);
}
}
- return r.release();
+ return range;
}
void CharacterIterator::advance(int count)
@@ -1448,94 +1537,85 @@ void CharacterIterator::advance(int count)
m_atBreak = false;
- // easy if there is enough left in the current m_textIterator run
- int remaining = m_textIterator.length() - m_runOffset;
+ // easy if there is enough left in the current m_underlyingIterator run
+ int remaining = m_underlyingIterator.text().length() - m_runOffset;
if (count < remaining) {
m_runOffset += count;
m_offset += count;
return;
}
- // exhaust the current m_textIterator run
+ // exhaust the current m_underlyingIterator run
count -= remaining;
m_offset += remaining;
- // move to a subsequent m_textIterator run
- for (m_textIterator.advance(); !atEnd(); m_textIterator.advance()) {
- int runLength = m_textIterator.length();
- if (runLength == 0)
+ // move to a subsequent m_underlyingIterator run
+ for (m_underlyingIterator.advance(); !atEnd(); m_underlyingIterator.advance()) {
+ int runLength = m_underlyingIterator.text().length();
+ if (!runLength)
m_atBreak = true;
else {
- // see whether this is m_textIterator to use
+ // see whether this is m_underlyingIterator to use
if (count < runLength) {
m_runOffset = count;
m_offset += count;
return;
}
- // exhaust this m_textIterator run
+ // exhaust this m_underlyingIterator run
count -= runLength;
m_offset += runLength;
}
}
- // ran to the end of the m_textIterator... no more runs left
+ // ran to the end of the m_underlyingIterator... no more runs left
m_atBreak = true;
m_runOffset = 0;
}
-String CharacterIterator::string(int numChars)
-{
- Vector<UChar> result;
- result.reserveInitialCapacity(numChars);
- while (numChars > 0 && !atEnd()) {
- int runSize = std::min(numChars, length());
- result.append(characters(), runSize);
- numChars -= runSize;
- advance(runSize);
- }
- return String::adopt(result);
-}
-
-static PassRefPtr<Range> characterSubrange(CharacterIterator& it, int offset, int length)
+static Ref<Range> characterSubrange(Document& document, CharacterIterator& it, int offset, int length)
{
it.advance(offset);
- RefPtr<Range> start = it.range();
+ if (it.atEnd())
+ return Range::create(document);
+
+ Ref<Range> start = it.range();
if (length > 1)
it.advance(length - 1);
- RefPtr<Range> end = it.range();
+ if (it.atEnd())
+ return Range::create(document);
- return Range::create(start->startContainer()->document(),
- start->startContainer(), start->startOffset(),
- end->endContainer(), end->endOffset());
+ Ref<Range> end = it.range();
+
+ return Range::create(document, &start->startContainer(), start->startOffset(), &end->endContainer(), end->endOffset());
}
-BackwardsCharacterIterator::BackwardsCharacterIterator(const Range* range, TextIteratorBehavior behavior)
- : m_offset(0)
+BackwardsCharacterIterator::BackwardsCharacterIterator(const Range& range)
+ : m_underlyingIterator(range)
+ , m_offset(0)
, m_runOffset(0)
, m_atBreak(true)
- , m_textIterator(range, behavior)
{
- while (!atEnd() && !m_textIterator.length())
- m_textIterator.advance();
+ while (!atEnd() && !m_underlyingIterator.text().length())
+ m_underlyingIterator.advance();
}
-PassRefPtr<Range> BackwardsCharacterIterator::range() const
+Ref<Range> BackwardsCharacterIterator::range() const
{
- RefPtr<Range> r = m_textIterator.range();
- if (!m_textIterator.atEnd()) {
- if (m_textIterator.length() <= 1)
+ Ref<Range> r = m_underlyingIterator.range();
+ if (!m_underlyingIterator.atEnd()) {
+ if (m_underlyingIterator.text().length() <= 1)
ASSERT(m_runOffset == 0);
else {
- Node* n = r->startContainer();
- ASSERT(n == r->endContainer());
+ Node& node = r->startContainer();
+ ASSERT(&node == &r->endContainer());
int offset = r->endOffset() - m_runOffset;
- r->setStart(n, offset - 1, ASSERT_NO_EXCEPTION);
- r->setEnd(n, offset, ASSERT_NO_EXCEPTION);
+ r->setStart(node, offset - 1);
+ r->setEnd(node, offset);
}
}
- return r.release();
+ return r;
}
void BackwardsCharacterIterator::advance(int count)
@@ -1547,7 +1627,7 @@ void BackwardsCharacterIterator::advance(int count)
m_atBreak = false;
- int remaining = m_textIterator.length() - m_runOffset;
+ int remaining = m_underlyingIterator.text().length() - m_runOffset;
if (count < remaining) {
m_runOffset += count;
m_offset += count;
@@ -1557,8 +1637,8 @@ void BackwardsCharacterIterator::advance(int count)
count -= remaining;
m_offset += remaining;
- for (m_textIterator.advance(); !atEnd(); m_textIterator.advance()) {
- int runLength = m_textIterator.length();
+ for (m_underlyingIterator.advance(); !atEnd(); m_underlyingIterator.advance()) {
+ int runLength = m_underlyingIterator.text().length();
if (runLength == 0)
m_atBreak = true;
else {
@@ -1579,18 +1659,13 @@ void BackwardsCharacterIterator::advance(int count)
// --------
-WordAwareIterator::WordAwareIterator(const Range* r)
- : m_previousText(0)
+WordAwareIterator::WordAwareIterator(const Range& range)
+ : m_underlyingIterator(&range)
, m_didLookAhead(true) // so we consider the first chunk from the text iterator
- , m_textIterator(r)
{
advance(); // get in position over the first chunk of text
}
-WordAwareIterator::~WordAwareIterator()
-{
-}
-
// We're always in one of these modes:
// - The current chunk in the text iterator is our current chunk
// (typically its a piece of whitespace, or text that ended with whitespace)
@@ -1602,74 +1677,59 @@ WordAwareIterator::~WordAwareIterator()
void WordAwareIterator::advance()
{
- m_previousText = 0;
- m_buffer.clear(); // toss any old buffer we built up
+ m_previousText.reset();
+ m_buffer.clear();
// If last time we did a look-ahead, start with that looked-ahead chunk now
if (!m_didLookAhead) {
- ASSERT(!m_textIterator.atEnd());
- m_textIterator.advance();
+ ASSERT(!m_underlyingIterator.atEnd());
+ m_underlyingIterator.advance();
}
m_didLookAhead = false;
// Go to next non-empty chunk
- while (!m_textIterator.atEnd() && m_textIterator.length() == 0)
- m_textIterator.advance();
- m_range = m_textIterator.range();
-
- if (m_textIterator.atEnd())
+ while (!m_underlyingIterator.atEnd() && !m_underlyingIterator.text().length())
+ m_underlyingIterator.advance();
+ if (m_underlyingIterator.atEnd())
return;
-
+
while (1) {
// If this chunk ends in whitespace we can just use it as our chunk.
- if (isSpaceOrNewline(m_textIterator.characters()[m_textIterator.length() - 1]))
+ if (isSpaceOrNewline(m_underlyingIterator.text()[m_underlyingIterator.text().length() - 1]))
return;
// If this is the first chunk that failed, save it in previousText before look ahead
- if (m_buffer.isEmpty()) {
- m_previousText = m_textIterator.characters();
- m_previousLength = m_textIterator.length();
- }
+ if (m_buffer.isEmpty())
+ m_previousText = m_underlyingIterator.copyableText();
- // Look ahead to next chunk. If it is whitespace or a break, we can use the previous stuff
- m_textIterator.advance();
- if (m_textIterator.atEnd() || m_textIterator.length() == 0 || isSpaceOrNewline(m_textIterator.characters()[0])) {
+ // Look ahead to next chunk. If it is whitespace or a break, we can use the previous stuff
+ m_underlyingIterator.advance();
+ if (m_underlyingIterator.atEnd() || !m_underlyingIterator.text().length() || isSpaceOrNewline(m_underlyingIterator.text()[0])) {
m_didLookAhead = true;
return;
}
if (m_buffer.isEmpty()) {
// Start gobbling chunks until we get to a suitable stopping point
- m_buffer.append(m_previousText, m_previousLength);
- m_previousText = 0;
+ append(m_buffer, m_previousText.text());
+ m_previousText.reset();
}
- m_buffer.append(m_textIterator.characters(), m_textIterator.length());
- int exception = 0;
- m_range->setEnd(m_textIterator.range()->endContainer(), m_textIterator.range()->endOffset(), exception);
+ append(m_buffer, m_underlyingIterator.text());
}
}
-int WordAwareIterator::length() const
+StringView WordAwareIterator::text() const
{
if (!m_buffer.isEmpty())
- return m_buffer.size();
- if (m_previousText)
- return m_previousLength;
- return m_textIterator.length();
-}
-
-const UChar* WordAwareIterator::characters() const
-{
- if (!m_buffer.isEmpty())
- return m_buffer.data();
- if (m_previousText)
- return m_previousText;
- return m_textIterator.characters();
+ return StringView(m_buffer.data(), m_buffer.size());
+ if (m_previousText.text().length())
+ return m_previousText.text();
+ return m_underlyingIterator.text();
}
// --------
-static inline UChar foldQuoteMarkOrSoftHyphen(UChar c)
+static inline UChar foldQuoteMark(UChar c)
{
switch (c) {
case hebrewPunctuationGershayim:
@@ -1680,37 +1740,29 @@ static inline UChar foldQuoteMarkOrSoftHyphen(UChar c)
case leftSingleQuotationMark:
case rightSingleQuotationMark:
return '\'';
- case softHyphen:
- // Replace soft hyphen with an ignorable character so that their presence or absence will
- // not affect string comparison.
- return 0;
default:
return c;
}
}
-static inline void foldQuoteMarksAndSoftHyphens(String& s)
+// FIXME: We'd like to tailor the searcher to fold quote marks for us instead
+// of doing it in a separate replacement pass here, but ICU doesn't offer a way
+// to add tailoring on top of the locale-specific tailoring as of this writing.
+static inline String foldQuoteMarks(String string)
{
- s.replace(hebrewPunctuationGeresh, '\'');
- s.replace(hebrewPunctuationGershayim, '"');
- s.replace(leftDoubleQuotationMark, '"');
- s.replace(leftSingleQuotationMark, '\'');
- s.replace(rightDoubleQuotationMark, '"');
- s.replace(rightSingleQuotationMark, '\'');
- // Replace soft hyphen with an ignorable character so that their presence or absence will
- // not affect string comparison.
- s.replace(softHyphen, 0);
+ string.replace(hebrewPunctuationGeresh, '\'');
+ string.replace(hebrewPunctuationGershayim, '"');
+ string.replace(leftDoubleQuotationMark, '"');
+ string.replace(leftSingleQuotationMark, '\'');
+ string.replace(rightDoubleQuotationMark, '"');
+ string.replace(rightSingleQuotationMark, '\'');
+
+ return string;
}
#if !UCONFIG_NO_COLLATION
-static inline void foldQuoteMarksAndSoftHyphens(UChar* data, size_t length)
-{
- for (size_t i = 0; i < length; ++i)
- data[i] = foldQuoteMarkOrSoftHyphen(data[i]);
-}
-
-static const size_t minimumSearchBufferSize = 8192;
+const size_t minimumSearchBufferSize = 8192;
#ifndef NDEBUG
static bool searcherInUse;
@@ -1922,7 +1974,9 @@ static inline bool isCombiningVoicedSoundMark(UChar character)
static inline bool containsKanaLetters(const String& pattern)
{
- const UChar* characters = pattern.deprecatedCharacters();
+ if (pattern.is8Bit())
+ return false;
+ const UChar* characters = pattern.characters16();
unsigned length = pattern.length();
for (unsigned i = 0; i < length; ++i) {
if (isKanaLetter(characters[i]))
@@ -1987,7 +2041,8 @@ static inline bool isSeparator(UChar32 character)
}
inline SearchBuffer::SearchBuffer(const String& target, FindOptions options)
- : m_target(target)
+ : m_target(foldQuoteMarks(target))
+ , m_targetCharacters(StringView(m_target).upconvertedCharacters())
, m_options(options)
, m_prefixLength(0)
, m_atBreak(true)
@@ -1996,18 +2051,13 @@ inline SearchBuffer::SearchBuffer(const String& target, FindOptions options)
{
ASSERT(!m_target.isEmpty());
- // FIXME: We'd like to tailor the searcher to fold quote marks for us instead
- // of doing it in a separate replacement pass here, but ICU doesn't offer a way
- // to add tailoring on top of the locale-specific tailoring as of this writing.
- foldQuoteMarksAndSoftHyphens(m_target);
-
size_t targetLength = m_target.length();
m_buffer.reserveInitialCapacity(std::max(targetLength * 8, minimumSearchBufferSize));
m_overlap = m_buffer.capacity() / 4;
if ((m_options & AtWordStarts) && targetLength) {
UChar32 targetFirstCharacter;
- U16_GET(m_target.deprecatedCharacters(), 0, 0, targetLength, targetFirstCharacter);
+ U16_GET(m_target, 0, 0u, targetLength, targetFirstCharacter);
// Characters in the separator category never really occur at the beginning of a word,
// so if the target begins with such a character, we just ignore the AtWordStart option.
if (isSeparator(targetFirstCharacter)) {
@@ -2044,12 +2094,12 @@ inline SearchBuffer::SearchBuffer(const String& target, FindOptions options)
usearch_setAttribute(searcher, USEARCH_ELEMENT_COMPARISON, comparator, &status);
ASSERT(status == U_ZERO_ERROR);
- usearch_setPattern(searcher, m_target.deprecatedCharacters(), targetLength, &status);
+ usearch_setPattern(searcher, m_targetCharacters, targetLength, &status);
ASSERT(status == U_ZERO_ERROR);
// The kana workaround requires a normalized copy of the target string.
if (m_targetRequiresKanaWorkaround)
- normalizeCharacters(m_target.deprecatedCharacters(), m_target.length(), m_normalizedTarget);
+ normalizeCharacters(m_targetCharacters, targetLength, m_normalizedTarget);
}
inline SearchBuffer::~SearchBuffer()
@@ -2064,9 +2114,9 @@ inline SearchBuffer::~SearchBuffer()
unlockSearcher();
}
-inline size_t SearchBuffer::append(const UChar* characters, size_t length)
+inline size_t SearchBuffer::append(StringView text)
{
- ASSERT(length);
+ ASSERT(text.length());
if (m_atBreak) {
m_buffer.shrink(0);
@@ -2079,10 +2129,11 @@ inline size_t SearchBuffer::append(const UChar* characters, size_t length)
}
size_t oldLength = m_buffer.size();
- size_t usableLength = std::min(m_buffer.capacity() - oldLength, length);
+ size_t usableLength = std::min<size_t>(m_buffer.capacity() - oldLength, text.length());
ASSERT(usableLength);
- m_buffer.append(characters, usableLength);
- foldQuoteMarksAndSoftHyphens(m_buffer.data() + oldLength, usableLength);
+ m_buffer.grow(oldLength + usableLength);
+ for (unsigned i = 0; i < usableLength; ++i)
+ m_buffer[oldLength + i] = foldQuoteMark(text[i]);
return usableLength;
}
@@ -2091,24 +2142,24 @@ inline bool SearchBuffer::needsMoreContext() const
return m_needsMoreContext;
}
-inline void SearchBuffer::prependContext(const UChar* characters, size_t length)
+inline void SearchBuffer::prependContext(StringView text)
{
ASSERT(m_needsMoreContext);
ASSERT(m_prefixLength == m_buffer.size());
- if (!length)
+ if (!text.length())
return;
m_atBreak = false;
- size_t wordBoundaryContextStart = length;
+ size_t wordBoundaryContextStart = text.length();
if (wordBoundaryContextStart) {
- U16_BACK_1(characters, 0, wordBoundaryContextStart);
- wordBoundaryContextStart = startOfLastWordBoundaryContext(characters, wordBoundaryContextStart);
+ U16_BACK_1(text, 0, wordBoundaryContextStart);
+ wordBoundaryContextStart = startOfLastWordBoundaryContext(text.substring(0, wordBoundaryContextStart));
}
- size_t usableLength = std::min(m_buffer.capacity() - m_prefixLength, length - wordBoundaryContextStart);
- m_buffer.insert(0, characters + length - usableLength, usableLength);
+ size_t usableLength = std::min(m_buffer.capacity() - m_prefixLength, text.length() - wordBoundaryContextStart);
+ WTF::append(m_buffer, text.substring(text.length() - usableLength, usableLength));
m_prefixLength += usableLength;
if (wordBoundaryContextStart || m_prefixLength == m_buffer.capacity())
@@ -2184,6 +2235,17 @@ inline bool SearchBuffer::isBadMatch(const UChar* match, size_t matchLength) con
}
}
}
+
+inline bool SearchBuffer::isWordEndMatch(size_t start, size_t length) const
+{
+ ASSERT(length);
+ ASSERT(m_options & AtWordEnds);
+
+ int endWord;
+ // Start searching at the end of matched search, so that multiple word matches succeed.
+ findEndWordBoundary(StringView(m_buffer.data(), m_buffer.size()), start + length - 1, &endWord);
+ return static_cast<size_t>(endWord) == (start + length);
+}
inline bool SearchBuffer::isWordStartMatch(size_t start, size_t length) const
{
@@ -2231,12 +2293,12 @@ inline bool SearchBuffer::isWordStartMatch(size_t start, size_t length) const
// Chinese and Japanese lack word boundary marks, and there is no clear agreement on what constitutes
// a word, so treat the position before any CJK character as a word start.
- if (Font::isCJKIdeographOrSymbol(firstCharacter))
+ if (FontCascade::isCJKIdeographOrSymbol(firstCharacter))
return true;
size_t wordBreakSearchStart = start + length;
while (wordBreakSearchStart > start)
- wordBreakSearchStart = findNextWordFromIndex(m_buffer.data(), m_buffer.size(), wordBreakSearchStart, false /* backwards */);
+ wordBreakSearchStart = findNextWordFromIndex(StringView(m_buffer.data(), m_buffer.size()), wordBreakSearchStart, false /* backwards */);
return wordBreakSearchStart == start;
}
@@ -2277,9 +2339,9 @@ nextMatch:
if (m_options & AtWordStarts) {
// Ensure that there is sufficient context before matchStart the next time around for
// determining if it is at a word boundary.
- int wordBoundaryContextStart = matchStart;
+ unsigned wordBoundaryContextStart = matchStart;
U16_BACK_1(m_buffer.data(), 0, wordBoundaryContextStart);
- wordBoundaryContextStart = startOfLastWordBoundaryContext(m_buffer.data(), wordBoundaryContextStart);
+ wordBoundaryContextStart = startOfLastWordBoundaryContext(StringView(m_buffer.data(), wordBoundaryContextStart));
overlap = std::min(size - 1, std::max(overlap, size - wordBoundaryContextStart));
}
memcpy(m_buffer.data(), m_buffer.data() + size - overlap, overlap * sizeof(UChar));
@@ -2292,7 +2354,9 @@ nextMatch:
ASSERT_WITH_SECURITY_IMPLICATION(matchStart + matchedLength <= size);
// If this match is "bad", move on to the next match.
- if (isBadMatch(m_buffer.data() + matchStart, matchedLength) || ((m_options & AtWordStarts) && !isWordStartMatch(matchStart, matchedLength))) {
+ if (isBadMatch(m_buffer.data() + matchStart, matchedLength)
+ || ((m_options & AtWordStarts) && !isWordStartMatch(matchStart, matchedLength))
+ || ((m_options & AtWordEnds) && !isWordEndMatch(matchStart, matchedLength))) {
matchStart = usearch_next(searcher, &status);
ASSERT(status == U_ZERO_ERROR);
goto nextMatch;
@@ -2319,7 +2383,7 @@ inline SearchBuffer::SearchBuffer(const String& target, FindOptions options)
{
ASSERT(!m_target.isEmpty());
m_target.replace(noBreakSpace, ' ');
- foldQuoteMarksAndSoftHyphens(m_target);
+ foldQuoteMarks(m_target);
}
inline SearchBuffer::~SearchBuffer()
@@ -2339,7 +2403,7 @@ inline bool SearchBuffer::atBreak() const
inline void SearchBuffer::append(UChar c, bool isStart)
{
- m_buffer[m_cursor] = c == noBreakSpace ? ' ' : foldQuoteMarkOrSoftHyphen(c);
+ m_buffer[m_cursor] = c == noBreakSpace ? ' ' : foldQuoteMark(c);
m_isCharacterStartBuffer[m_cursor] = isStart;
if (++m_cursor == m_target.length()) {
m_cursor = 0;
@@ -2421,109 +2485,108 @@ size_t SearchBuffer::length() const
// --------
-int TextIterator::rangeLength(const Range* r, bool forSelectionPreservation)
+int TextIterator::rangeLength(const Range* range, bool forSelectionPreservation)
{
- int length = 0;
- for (TextIterator it(r, forSelectionPreservation ? TextIteratorEmitsCharactersBetweenAllVisiblePositions : TextIteratorDefaultBehavior); !it.atEnd(); it.advance())
- length += it.length();
-
+ unsigned length = 0;
+ for (TextIterator it(range, forSelectionPreservation ? TextIteratorEmitsCharactersBetweenAllVisiblePositions : TextIteratorDefaultBehavior); !it.atEnd(); it.advance())
+ length += it.text().length();
return length;
}
-PassRefPtr<Range> TextIterator::subrange(Range* entireRange, int characterOffset, int characterCount)
+Ref<Range> TextIterator::subrange(Range* entireRange, int characterOffset, int characterCount)
+{
+ CharacterIterator entireRangeIterator(*entireRange);
+ return characterSubrange(entireRange->ownerDocument(), entireRangeIterator, characterOffset, characterCount);
+}
+
+static inline bool isInsideReplacedElement(TextIterator& iterator)
{
- CharacterIterator entireRangeIterator(entireRange);
- return characterSubrange(entireRangeIterator, characterOffset, characterCount);
+ ASSERT(!iterator.atEnd());
+ ASSERT(iterator.text().length() == 1);
+ Node* node = iterator.node();
+ return node && isRendererReplacedElement(node->renderer());
}
-PassRefPtr<Range> TextIterator::rangeFromLocationAndLength(ContainerNode* scope, int rangeLocation, int rangeLength, bool forSelectionPreservation)
+RefPtr<Range> TextIterator::rangeFromLocationAndLength(ContainerNode* scope, int rangeLocation, int rangeLength, bool forSelectionPreservation)
{
- RefPtr<Range> resultRange = scope->document().createRange();
+ Ref<Range> resultRange = scope->document().createRange();
int docTextPosition = 0;
int rangeEnd = rangeLocation + rangeLength;
bool startRangeFound = false;
- RefPtr<Range> textRunRange;
+ Ref<Range> textRunRange = rangeOfContents(*scope);
- TextIterator it(rangeOfContents(*scope).get(), forSelectionPreservation ? TextIteratorEmitsCharactersBetweenAllVisiblePositions : TextIteratorDefaultBehavior);
+ TextIterator it(textRunRange.ptr(), forSelectionPreservation ? TextIteratorEmitsCharactersBetweenAllVisiblePositions : TextIteratorDefaultBehavior);
// FIXME: the atEnd() check shouldn't be necessary, workaround for <http://bugs.webkit.org/show_bug.cgi?id=6289>.
- if (rangeLocation == 0 && rangeLength == 0 && it.atEnd()) {
- textRunRange = it.range();
-
- resultRange->setStart(textRunRange->startContainer(), 0, ASSERT_NO_EXCEPTION);
- resultRange->setEnd(textRunRange->startContainer(), 0, ASSERT_NO_EXCEPTION);
-
- return resultRange.release();
+ if (!rangeLocation && !rangeLength && it.atEnd()) {
+ resultRange->setStart(textRunRange->startContainer(), 0);
+ resultRange->setEnd(textRunRange->startContainer(), 0);
+ return WTFMove(resultRange);
}
for (; !it.atEnd(); it.advance()) {
- int len = it.length();
+ int length = it.text().length();
textRunRange = it.range();
-
- bool foundStart = rangeLocation >= docTextPosition && rangeLocation <= docTextPosition + len;
- bool foundEnd = rangeEnd >= docTextPosition && rangeEnd <= docTextPosition + len;
-
- // Fix textRunRange->endPosition(), but only if foundStart || foundEnd, because it is only
- // in those cases that textRunRange is used.
+
+ bool foundStart = rangeLocation >= docTextPosition && rangeLocation <= docTextPosition + length;
+ bool foundEnd = rangeEnd >= docTextPosition && rangeEnd <= docTextPosition + length;
+
if (foundEnd) {
// FIXME: This is a workaround for the fact that the end of a run is often at the wrong
- // position for emitted '\n's.
- if (len == 1 && it.characterAt(0) == '\n') {
+ // position for emitted '\n's or if the renderer of the current node is a replaced element.
+ if (length == 1 && (it.text()[0] == '\n' || isInsideReplacedElement(it))) {
it.advance();
if (!it.atEnd()) {
- RefPtr<Range> range = it.range();
- textRunRange->setEnd(range->startContainer(), range->startOffset(), ASSERT_NO_EXCEPTION);
+ Ref<Range> range = it.range();
+ textRunRange->setEnd(range->startContainer(), range->startOffset());
} else {
Position runStart = textRunRange->startPosition();
Position runEnd = VisiblePosition(runStart).next().deepEquivalent();
if (runEnd.isNotNull())
- textRunRange->setEnd(runEnd.containerNode(), runEnd.computeOffsetInContainerNode(), ASSERT_NO_EXCEPTION);
+ textRunRange->setEnd(*runEnd.containerNode(), runEnd.computeOffsetInContainerNode());
}
}
}
if (foundStart) {
startRangeFound = true;
- int exception = 0;
- if (textRunRange->startContainer()->isTextNode()) {
+ if (textRunRange->startContainer().isTextNode()) {
int offset = rangeLocation - docTextPosition;
- resultRange->setStart(textRunRange->startContainer(), offset + textRunRange->startOffset(), exception);
+ resultRange->setStart(textRunRange->startContainer(), offset + textRunRange->startOffset());
} else {
if (rangeLocation == docTextPosition)
- resultRange->setStart(textRunRange->startContainer(), textRunRange->startOffset(), exception);
+ resultRange->setStart(textRunRange->startContainer(), textRunRange->startOffset());
else
- resultRange->setStart(textRunRange->endContainer(), textRunRange->endOffset(), exception);
+ resultRange->setStart(textRunRange->endContainer(), textRunRange->endOffset());
}
}
if (foundEnd) {
- int exception = 0;
- if (textRunRange->startContainer()->isTextNode()) {
+ if (textRunRange->startContainer().isTextNode()) {
int offset = rangeEnd - docTextPosition;
- resultRange->setEnd(textRunRange->startContainer(), offset + textRunRange->startOffset(), exception);
+ resultRange->setEnd(textRunRange->startContainer(), offset + textRunRange->startOffset());
} else {
if (rangeEnd == docTextPosition)
- resultRange->setEnd(textRunRange->startContainer(), textRunRange->startOffset(), exception);
+ resultRange->setEnd(textRunRange->startContainer(), textRunRange->startOffset());
else
- resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset(), exception);
+ resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset());
}
- docTextPosition += len;
+ docTextPosition += length;
break;
}
- docTextPosition += len;
+
+ docTextPosition += length;
}
if (!startRangeFound)
- return 0;
+ return nullptr;
- if (rangeLength != 0 && rangeEnd > docTextPosition) { // rangeEnd is out of bounds
- int exception = 0;
- resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset(), exception);
- }
+ if (rangeLength && rangeEnd > docTextPosition) // rangeEnd is out of bounds
+ resultRange->setEnd(textRunRange->endContainer(), textRunRange->endOffset());
- return resultRange.release();
+ return WTFMove(resultRange);
}
bool TextIterator::getLocationAndLengthFromRange(Node* scope, const Range* range, size_t& location, size_t& length)
@@ -2531,26 +2594,23 @@ bool TextIterator::getLocationAndLengthFromRange(Node* scope, const Range* range
location = notFound;
length = 0;
- if (!range->startContainer())
- return false;
-
// The critical assumption is that this only gets called with ranges that
// concentrate on a given area containing the selection root. This is done
// because of text fields and textareas. The DOM for those is not
// directly in the document DOM, so ensure that the range does not cross a
// boundary of one of those.
- if (range->startContainer() != scope && !range->startContainer()->isDescendantOf(scope))
+ if (&range->startContainer() != scope && !range->startContainer().isDescendantOf(scope))
return false;
- if (range->endContainer() != scope && !range->endContainer()->isDescendantOf(scope))
+ if (&range->endContainer() != scope && !range->endContainer().isDescendantOf(scope))
return false;
- RefPtr<Range> testRange = Range::create(scope->document(), scope, 0, range->startContainer(), range->startOffset());
- ASSERT(testRange->startContainer() == scope);
- location = TextIterator::rangeLength(testRange.get());
+ Ref<Range> testRange = Range::create(scope->document(), scope, 0, &range->startContainer(), range->startOffset());
+ ASSERT(&testRange->startContainer() == scope);
+ location = TextIterator::rangeLength(testRange.ptr());
- testRange->setEnd(range->endContainer(), range->endOffset(), IGNORE_EXCEPTION);
- ASSERT(testRange->startContainer() == scope);
- length = TextIterator::rangeLength(testRange.get()) - location;
+ testRange->setEnd(range->endContainer(), range->endOffset());
+ ASSERT(&testRange->startContainer() == scope);
+ length = TextIterator::rangeLength(testRange.ptr()) - location;
return true;
}
@@ -2570,7 +2630,7 @@ String plainText(const Range* r, TextIteratorBehavior defaultBehavior, bool isDi
for (TextIterator it(r, behavior); !it.atEnd(); it.advance()) {
it.appendTextToStringBuilder(builder);
- bufferLength += it.length();
+ bufferLength += it.text().length();
}
if (!bufferLength)
@@ -2584,71 +2644,101 @@ String plainText(const Range* r, TextIteratorBehavior defaultBehavior, bool isDi
return result;
}
-static PassRefPtr<Range> collapsedToBoundary(const Range* range, bool forward)
+String plainTextReplacingNoBreakSpace(const Range* range, TextIteratorBehavior defaultBehavior, bool isDisplayString)
{
- RefPtr<Range> result = range->cloneRange(ASSERT_NO_EXCEPTION);
- result->collapse(!forward, ASSERT_NO_EXCEPTION);
- return result.release();
+ return plainText(range, defaultBehavior, isDisplayString).replace(noBreakSpace, ' ');
}
-static size_t findPlainText(CharacterIterator& it, const String& target, FindOptions options, size_t& matchStart)
+static Ref<Range> collapsedToBoundary(const Range& range, bool forward)
{
- matchStart = 0;
- size_t matchLength = 0;
+ Ref<Range> result = range.cloneRange();
+ result->collapse(!forward);
+ return result;
+}
- SearchBuffer buffer(target, options);
+static TextIteratorBehavior findIteratorOptions(FindOptions options)
+{
+ TextIteratorBehavior iteratorOptions = TextIteratorEntersTextControls | TextIteratorClipsToFrameAncestors;
+ if (!(options & DoNotTraverseFlatTree))
+ iteratorOptions |= TextIteratorTraversesFlatTree;
+ return iteratorOptions;
+}
+static void findPlainTextMatches(const Range& range, const String& target, FindOptions options, const std::function<bool(size_t, size_t)>& match)
+{
+ SearchBuffer buffer(target, options);
if (buffer.needsMoreContext()) {
- RefPtr<Range> startRange = it.range();
- RefPtr<Range> beforeStartRange = startRange->ownerDocument().createRange();
- beforeStartRange->setEnd(startRange->startContainer(), startRange->startOffset(), IGNORE_EXCEPTION);
+ Ref<Range> beforeStartRange = range.ownerDocument().createRange();
+ beforeStartRange->setEnd(range.startContainer(), range.startOffset());
for (SimplifiedBackwardsTextIterator backwardsIterator(beforeStartRange.get()); !backwardsIterator.atEnd(); backwardsIterator.advance()) {
- buffer.prependContext(backwardsIterator.characters(), backwardsIterator.length());
+ buffer.prependContext(backwardsIterator.text());
if (!buffer.needsMoreContext())
break;
}
}
- while (!it.atEnd()) {
- it.advance(buffer.append(it.characters(), it.length()));
-tryAgain:
- size_t matchStartOffset;
- if (size_t newMatchLength = buffer.search(matchStartOffset)) {
- // Note that we found a match, and where we found it.
- size_t lastCharacterInBufferOffset = it.characterOffset();
- ASSERT(lastCharacterInBufferOffset >= matchStartOffset);
- matchStart = lastCharacterInBufferOffset - matchStartOffset;
- matchLength = newMatchLength;
- // If searching forward, stop on the first match.
- // If searching backward, don't stop, so we end up with the last match.
- if (!(options & Backwards))
+ CharacterIterator findIterator(range, findIteratorOptions(options));
+ while (!findIterator.atEnd()) {
+ findIterator.advance(buffer.append(findIterator.text()));
+ while (1) {
+ size_t matchStartOffset;
+ size_t newMatchLength = buffer.search(matchStartOffset);
+ if (!newMatchLength) {
+ if (findIterator.atBreak() && !buffer.atBreak()) {
+ buffer.reachedBreak();
+ continue;
+ }
break;
- goto tryAgain;
- }
- if (it.atBreak() && !buffer.atBreak()) {
- buffer.reachedBreak();
- goto tryAgain;
+ }
+ size_t lastCharacterInBufferOffset = findIterator.characterOffset();
+ ASSERT(lastCharacterInBufferOffset >= matchStartOffset);
+ if (match(lastCharacterInBufferOffset - matchStartOffset, newMatchLength))
+ return;
}
}
+}
- return matchLength;
+static Ref<Range> rangeForMatch(const Range& range, FindOptions options, size_t matchStart, size_t matchLength, bool searchForward)
+{
+ if (!matchLength)
+ return collapsedToBoundary(range, searchForward);
+ CharacterIterator rangeComputeIterator(range, findIteratorOptions(options));
+ return characterSubrange(range.ownerDocument(), rangeComputeIterator, matchStart, matchLength);
}
-PassRefPtr<Range> findPlainText(const Range* range, const String& target, FindOptions options)
+Ref<Range> findClosestPlainText(const Range& range, const String& target, FindOptions options, unsigned targetOffset)
{
- // First, find the text.
- size_t matchStart;
- size_t matchLength;
- {
- CharacterIterator findIterator(range, TextIteratorEntersTextControls);
- matchLength = findPlainText(findIterator, target, options, matchStart);
- if (!matchLength)
- return collapsedToBoundary(range, !(options & Backwards));
- }
+ size_t matchStart = 0;
+ size_t matchLength = 0;
+ size_t distance = std::numeric_limits<size_t>::max();
+ auto match = [targetOffset, &distance, &matchStart, &matchLength] (size_t start, size_t length) {
+ size_t newDistance = std::min(abs(static_cast<signed>(start - targetOffset)), abs(static_cast<signed>(start + length - targetOffset)));
+ if (newDistance < distance) {
+ matchStart = start;
+ matchLength = length;
+ distance = newDistance;
+ }
+ return false;
+ };
+
+ findPlainTextMatches(range, target, options, match);
+ return rangeForMatch(range, options, matchStart, matchLength, !(options & Backwards));
+}
+
+Ref<Range> findPlainText(const Range& range, const String& target, FindOptions options)
+{
+ bool searchForward = !(options & Backwards);
+ size_t matchStart = 0;
+ size_t matchLength = 0;
+ auto match = [searchForward, &matchStart, &matchLength] (size_t start, size_t length) {
+ matchStart = start;
+ matchLength = length;
+ // Look for the last match when searching backwards instead.
+ return searchForward;
+ };
- // Then, find the document position of the start and the end of the text.
- CharacterIterator computeRangeIterator(range, TextIteratorEntersTextControls);
- return characterSubrange(computeRangeIterator, matchStart, matchLength);
+ findPlainTextMatches(range, target, options, match);
+ return rangeForMatch(range, options, matchStart, matchLength, searchForward);
}
}
diff --git a/Source/WebCore/editing/TextIterator.h b/Source/WebCore/editing/TextIterator.h
index 8b8919f41..c0d0851f4 100644
--- a/Source/WebCore/editing/TextIterator.h
+++ b/Source/WebCore/editing/TextIterator.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2006, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2006, 2009, 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,46 +23,31 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TextIterator_h
-#define TextIterator_h
+#pragma once
+
+// FIXME: Move each iterator class into a separate header file.
#include "FindOptions.h"
#include "Range.h"
+#include "TextIteratorBehavior.h"
#include <wtf/Vector.h>
+#include <wtf/text/StringView.h>
namespace WebCore {
class InlineTextBox;
class RenderText;
class RenderTextFragment;
-
-enum TextIteratorBehavior {
- TextIteratorDefaultBehavior = 0,
- TextIteratorEmitsCharactersBetweenAllVisiblePositions = 1 << 0,
- TextIteratorEntersTextControls = 1 << 1,
- TextIteratorEmitsTextsWithoutTranscoding = 1 << 2,
- TextIteratorIgnoresStyleVisibility = 1 << 3,
- TextIteratorEmitsObjectReplacementCharacters = 1 << 4,
- TextIteratorEmitsOriginalText = 1 << 5,
- TextIteratorStopsOnFormControls = 1 << 6,
- TextIteratorEmitsImageAltText = 1 << 7,
-};
-
-// FIXME: Can't really answer this question correctly without knowing the white-space mode.
-// FIXME: Move this somewhere else in the editing directory. It doesn't belong here.
-inline bool isCollapsibleWhitespace(UChar c)
-{
- switch (c) {
- case ' ':
- case '\n':
- return true;
- default:
- return false;
- }
+namespace SimpleLineLayout {
+class RunResolver;
}
-String plainText(const Range*, TextIteratorBehavior defaultBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
-PassRefPtr<Range> findPlainText(const Range*, const String&, FindOptions);
+WEBCORE_EXPORT String plainText(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
+WEBCORE_EXPORT String plainTextReplacingNoBreakSpace(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior, bool isDisplayString = false);
+Ref<Range> findPlainText(const Range&, const String&, FindOptions);
+WEBCORE_EXPORT Ref<Range> findClosestPlainText(const Range&, const String&, FindOptions, unsigned);
+
+// FIXME: Move this somewhere else in the editing directory. It doesn't belong here.
bool isRendererReplacedElement(RenderObject*);
class BitStack {
@@ -81,121 +66,123 @@ private:
Vector<unsigned, 1> m_words;
};
+class TextIteratorCopyableText {
+public:
+ TextIteratorCopyableText()
+ : m_singleCharacter(0)
+ , m_offset(0)
+ , m_length(0)
+ {
+ }
+
+ StringView text() const { return m_singleCharacter ? StringView(&m_singleCharacter, 1) : StringView(m_string).substring(m_offset, m_length); }
+ void appendToStringBuilder(StringBuilder&) const;
+
+ void reset();
+ void set(String&&);
+ void set(String&&, unsigned offset, unsigned length);
+ void set(UChar);
+
+private:
+ UChar m_singleCharacter;
+ String m_string;
+ unsigned m_offset;
+ unsigned m_length;
+};
+
// Iterates through the DOM range, returning all the text, and 0-length boundaries
-// at points where replaced elements break up the text flow. The text comes back in
-// chunks so as to optimize for performance of the iteration.
+// at points where replaced elements break up the text flow. The text is delivered in
+// the chunks it's already stored in, to avoid copying any text.
class TextIterator {
public:
- explicit TextIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
- ~TextIterator();
+ WEBCORE_EXPORT explicit TextIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
+ WEBCORE_EXPORT ~TextIterator();
+
+ bool atEnd() const { return !m_positionNode; }
+ WEBCORE_EXPORT void advance();
+
+ StringView text() const { ASSERT(!atEnd()); return m_text; }
+ WEBCORE_EXPORT Ref<Range> range() const;
+ WEBCORE_EXPORT Node* node() const;
+
+ const TextIteratorCopyableText& copyableText() const { ASSERT(!atEnd()); return m_copyableText; }
+ void appendTextToStringBuilder(StringBuilder& builder) const { copyableText().appendToStringBuilder(builder); }
+
+ WEBCORE_EXPORT static int rangeLength(const Range*, bool spacesForReplacedElements = false);
+ WEBCORE_EXPORT static RefPtr<Range> rangeFromLocationAndLength(ContainerNode* scope, int rangeLocation, int rangeLength, bool spacesForReplacedElements = false);
+ WEBCORE_EXPORT static bool getLocationAndLengthFromRange(Node* scope, const Range*, size_t& location, size_t& length);
+ WEBCORE_EXPORT static Ref<Range> subrange(Range* entireRange, int characterOffset, int characterCount);
- bool atEnd() const { return !m_positionNode || m_shouldStop; }
- void advance();
-
- int length() const { return m_textLength; }
- const UChar* characters() const { return m_textCharacters ? m_textCharacters : m_text.deprecatedCharacters() + startOffset(); }
- UChar characterAt(unsigned index) const;
- void appendTextToStringBuilder(StringBuilder&) const;
-
- PassRefPtr<Range> range() const;
- Node* node() const;
-
- static int rangeLength(const Range*, bool spacesForReplacedElements = false);
- static PassRefPtr<Range> rangeFromLocationAndLength(ContainerNode* scope, int rangeLocation, int rangeLength, bool spacesForReplacedElements = false);
- static bool getLocationAndLengthFromRange(Node* scope, const Range*, size_t& location, size_t& length);
- static PassRefPtr<Range> subrange(Range* entireRange, int characterOffset, int characterCount);
-
private:
- int startOffset() const { return m_positionStartOffset; }
- const String& string() const { return m_text; }
- void exitNode();
+ void exitNode(Node*);
bool shouldRepresentNodeOffsetZero();
- bool shouldEmitSpaceBeforeAndAfterNode(Node*);
+ bool shouldEmitSpaceBeforeAndAfterNode(Node&);
void representNodeOffsetZero();
bool handleTextNode();
bool handleReplacedElement();
bool handleNonTextNode();
void handleTextBox();
- void handleTextNodeFirstLetter(RenderTextFragment*);
- bool hasVisibleTextNode(RenderText*);
- void emitCharacter(UChar, Node* textNode, Node* offsetBaseNode, int textStartOffset, int textEndOffset);
- void emitText(Node* textNode, RenderObject* renderObject, int textStartOffset, int textEndOffset);
- void emitText(Node* textNode, int textStartOffset, int textEndOffset);
-
- // Current position, not necessarily of the text being returned, but position
- // as we walk through the DOM tree.
- Node* m_node;
- int m_offset;
- bool m_handledNode;
- bool m_handledChildren;
+ void handleTextNodeFirstLetter(RenderTextFragment&);
+ void emitCharacter(UChar, Node& characterNode, Node* offsetBaseNode, int textStartOffset, int textEndOffset);
+ void emitText(Text& textNode, RenderText&, int textStartOffset, int textEndOffset);
+
+ Node* baseNodeForEmittingNewLine() const;
+
+ const TextIteratorBehavior m_behavior { TextIteratorDefaultBehavior };
+
+ // Current position, not necessarily of the text being returned, but position as we walk through the DOM tree.
+ Node* m_node { nullptr };
+ int m_offset { 0 };
+ bool m_handledNode { false };
+ bool m_handledChildren { false };
BitStack m_fullyClippedStack;
-
+
// The range.
- Node* m_startContainer;
- int m_startOffset;
- Node* m_endContainer;
- int m_endOffset;
- Node* m_pastEndNode;
-
+ Node* m_startContainer { nullptr };
+ int m_startOffset { 0 };
+ Node* m_endContainer { nullptr };
+ int m_endOffset { 0 };
+ Node* m_pastEndNode { nullptr };
+
// The current text and its position, in the form to be returned from the iterator.
- Node* m_positionNode;
- mutable Node* m_positionOffsetBaseNode;
- mutable int m_positionStartOffset;
- mutable int m_positionEndOffset;
- const UChar* m_textCharacters; // If null, then use m_text for character data.
- int m_textLength;
- // Hold string m_textCharacters points to so we ensure it won't be deleted.
- String m_text;
-
- // Used when there is still some pending text from the current node; when these
- // are false and 0, we go back to normal iterating.
- bool m_needsAnotherNewline;
- InlineTextBox* m_textBox;
- // Used when iteration over :first-letter text to save pointer to
- // remaining text box.
- InlineTextBox* m_remainingTextBox;
+ Node* m_positionNode { nullptr };
+ mutable Node* m_positionOffsetBaseNode { nullptr };
+ mutable int m_positionStartOffset { 0 };
+ mutable int m_positionEndOffset { 0 };
+ TextIteratorCopyableText m_copyableText;
+ StringView m_text;
+
+ // Used when there is still some pending text from the current node; when these are false and null, we go back to normal iterating.
+ Node* m_nodeForAdditionalNewline { nullptr };
+ InlineTextBox* m_textBox { nullptr };
+
+ // Used when iterating over :first-letter text to save pointer to remaining text box.
+ InlineTextBox* m_remainingTextBox { nullptr };
+
// Used to point to RenderText object for :first-letter.
- RenderText *m_firstLetterText;
-
+ RenderText* m_firstLetterText { nullptr };
+
// Used to do the whitespace collapsing logic.
- Node* m_lastTextNode;
- bool m_lastTextNodeEndedWithCollapsedSpace;
- UChar m_lastCharacter;
-
- // Used for whitespace characters that aren't in the DOM, so we can point at them.
- UChar m_singleCharacterBuffer;
-
- // Used when text boxes are out of order (Hebrew/Arabic w/ embeded LTR text)
+ Text* m_lastTextNode { nullptr };
+ bool m_lastTextNodeEndedWithCollapsedSpace { false };
+ UChar m_lastCharacter { 0 };
+
+ // Used to do simple line layout run logic.
+ bool m_nextRunNeedsWhitespace { false };
+ unsigned m_accumulatedSimpleTextLengthInFlow { 0 };
+ Text* m_previousSimpleTextNodeInFlow { nullptr };
+ std::unique_ptr<SimpleLineLayout::RunResolver> m_flowRunResolverCache;
+
+ // Used when text boxes are out of order (Hebrew/Arabic with embedded LTR text)
Vector<InlineTextBox*> m_sortedTextBoxes;
- size_t m_sortedTextBoxesPosition;
-
+ size_t m_sortedTextBoxesPosition { 0 };
+
// Used when deciding whether to emit a "positioning" (e.g. newline) before any other content
- bool m_hasEmitted;
-
- // Used by selection preservation code. There should be one character emitted between every VisiblePosition
- // in the Range used to create the TextIterator.
- // FIXME <rdar://problem/6028818>: This functionality should eventually be phased out when we rewrite
- // moveParagraphs to not clone/destroy moved content.
- bool m_emitsCharactersBetweenAllVisiblePositions;
- bool m_entersTextControls;
-
- // Used when we want texts for copying, pasting, and transposing.
- bool m_emitsTextWithoutTranscoding;
- // Used in pasting inside password field.
- bool m_emitsOriginalText;
+ bool m_hasEmitted { false };
+
// Used when deciding text fragment created by :first-letter should be looked into.
- bool m_handledFirstLetter;
- // Used when the visibility of the style should not affect text gathering.
- bool m_ignoresStyleVisibility;
- // Used when emitting the special 0xFFFC character is required. Children for replaced objects will be ignored.
- 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;
-
- bool m_emitsImageAltText;
+ bool m_handledFirstLetter { false };
};
// Iterates through the DOM range, returning all the text, and 0-length boundaries
@@ -203,147 +190,119 @@ private:
// chunks so as to optimize for performance of the iteration.
class SimplifiedBackwardsTextIterator {
public:
- explicit SimplifiedBackwardsTextIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
-
- bool atEnd() const { return !m_positionNode || m_shouldStop; }
+ explicit SimplifiedBackwardsTextIterator(const Range&);
+
+ bool atEnd() const { return !m_positionNode; }
void advance();
- Node* node() const { return m_node; }
- int length() const { return m_textLength; }
- const UChar* characters() const { return m_textCharacters; }
-
- PassRefPtr<Range> range() const;
-
+ StringView text() const { ASSERT(!atEnd()); return m_text; }
+ WEBCORE_EXPORT Ref<Range> range() const;
+ Node* node() const { ASSERT(!atEnd()); return m_node; }
+
private:
void exitNode();
bool handleTextNode();
RenderText* handleFirstLetter(int& startOffset, int& offsetInNode);
bool handleReplacedElement();
bool handleNonTextNode();
- void emitCharacter(UChar, Node*, int startOffset, int endOffset);
+ void emitCharacter(UChar, Node&, int startOffset, int endOffset);
bool advanceRespectingRange(Node*);
- // Current position, not necessarily of the text being returned, but position
- // as we walk through the DOM tree.
- Node* m_node;
- int m_offset;
- bool m_handledNode;
- bool m_handledChildren;
+ const TextIteratorBehavior m_behavior { TextIteratorDefaultBehavior };
+
+ // Current position, not necessarily of the text being returned, but position as we walk through the DOM tree.
+ Node* m_node { nullptr };
+ int m_offset { 0 };
+ bool m_handledNode { false };
+ bool m_handledChildren { false };
BitStack m_fullyClippedStack;
- // End of the range.
- Node* m_startNode;
- int m_startOffset;
- // Start of the range.
- Node* m_endNode;
- int m_endOffset;
+ // The range.
+ Node* m_startContainer { nullptr };
+ int m_startOffset { 0 };
+ Node* m_endContainer { nullptr };
+ int m_endOffset { 0 };
// The current text and its position, in the form to be returned from the iterator.
- Node* m_positionNode;
- int m_positionStartOffset;
- int m_positionEndOffset;
- const UChar* m_textCharacters;
- int m_textLength;
+ Node* m_positionNode { nullptr };
+ int m_positionStartOffset { 0 };
+ int m_positionEndOffset { 0 };
+ TextIteratorCopyableText m_copyableText;
+ StringView m_text;
// Used to do the whitespace logic.
- Node* m_lastTextNode;
- UChar m_lastCharacter;
-
- // Used for whitespace characters that aren't in the DOM, so we can point at them.
- UChar m_singleCharacterBuffer;
+ Text* m_lastTextNode { nullptr };
+ UChar m_lastCharacter { 0 };
- // Whether m_node has advanced beyond the iteration range (i.e. m_startNode).
- bool m_havePassedStartNode;
+ // Whether m_node has advanced beyond the iteration range (i.e. m_startContainer).
+ bool m_havePassedStartContainer { false };
// 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;
-
- // Used in pasting inside password field.
- bool m_emitsOriginalText;
+ bool m_shouldHandleFirstLetter { false };
};
// Builds on the text iterator, adding a character position so we can walk one
// character at a time, or faster, as needed. Useful for searching.
class CharacterIterator {
public:
- explicit CharacterIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
+ explicit CharacterIterator(const Range&, TextIteratorBehavior = TextIteratorDefaultBehavior);
+ bool atEnd() const { return m_underlyingIterator.atEnd(); }
void advance(int numCharacters);
+ StringView text() const { return m_underlyingIterator.text().substring(m_runOffset); }
+ Ref<Range> range() const;
+
bool atBreak() const { return m_atBreak; }
- bool atEnd() const { return m_textIterator.atEnd(); }
-
- int length() const { return m_textIterator.length() - m_runOffset; }
- const UChar* characters() const { return m_textIterator.characters() + m_runOffset; }
- String string(int numChars);
-
int characterOffset() const { return m_offset; }
- PassRefPtr<Range> range() const;
-
+
private:
+ TextIterator m_underlyingIterator;
+
int m_offset;
int m_runOffset;
bool m_atBreak;
-
- TextIterator m_textIterator;
};
class BackwardsCharacterIterator {
public:
- explicit BackwardsCharacterIterator(const Range*, TextIteratorBehavior = TextIteratorDefaultBehavior);
-
- void advance(int);
+ explicit BackwardsCharacterIterator(const Range&);
- bool atEnd() const { return m_textIterator.atEnd(); }
+ bool atEnd() const { return m_underlyingIterator.atEnd(); }
+ void advance(int numCharacters);
- PassRefPtr<Range> range() const;
+ Ref<Range> range() const;
private:
+ SimplifiedBackwardsTextIterator m_underlyingIterator;
+
int m_offset;
int m_runOffset;
bool m_atBreak;
-
- SimplifiedBackwardsTextIterator m_textIterator;
};
-// Very similar to the TextIterator, except that the chunks of text returned are "well behaved",
-// meaning they never end split up a word. This is useful for spellcheck or (perhaps one day) searching.
+// Similar to the TextIterator, except that the chunks of text returned are "well behaved", meaning
+// they never split up a word. This is useful for spell checking and perhaps one day for searching as well.
class WordAwareIterator {
public:
- explicit WordAwareIterator(const Range*);
- ~WordAwareIterator();
+ explicit WordAwareIterator(const Range&);
- bool atEnd() const { return !m_didLookAhead && m_textIterator.atEnd(); }
+ bool atEnd() const { return !m_didLookAhead && m_underlyingIterator.atEnd(); }
void advance();
-
- int length() const;
- const UChar* characters() const;
-
- // Range of the text we're currently returning
- PassRefPtr<Range> range() const { return m_range; }
+
+ StringView text() const;
private:
- // text from the previous chunk from the textIterator
- const UChar* m_previousText;
- int m_previousLength;
+ TextIterator m_underlyingIterator;
+
+ // Text from the previous chunk from the text iterator.
+ TextIteratorCopyableText m_previousText;
- // many chunks from textIterator concatenated
+ // Many chunks from text iterator concatenated.
Vector<UChar> m_buffer;
- // Did we have to look ahead in the textIterator to confirm the current chunk?
+ // Did we have to look ahead in the text iterator to confirm the current chunk?
bool m_didLookAhead;
-
- RefPtr<Range> m_range;
-
- TextIterator m_textIterator;
};
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/TextIteratorBehavior.h b/Source/WebCore/editing/TextIteratorBehavior.h
new file mode 100644
index 000000000..c01f97f82
--- /dev/null
+++ b/Source/WebCore/editing/TextIteratorBehavior.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2004, 2006, 2009, 2014 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.
+ */
+
+#pragma once
+
+namespace WebCore {
+
+enum TextIteratorBehaviorFlag {
+ TextIteratorDefaultBehavior = 0,
+
+ // Used by selection preservation code. There should be one character emitted between every VisiblePosition
+ // in the Range used to create the TextIterator.
+ // FIXME <rdar://problem/6028818>: This functionality should eventually be phased out when we rewrite
+ // moveParagraphs to not clone/destroy moved content.
+ TextIteratorEmitsCharactersBetweenAllVisiblePositions = 1 << 0,
+
+ TextIteratorEntersTextControls = 1 << 1,
+
+ // Used when we want text for copying, pasting, and transposing.
+ TextIteratorEmitsTextsWithoutTranscoding = 1 << 2,
+
+ // Used when the visibility of the style should not affect text gathering.
+ TextIteratorIgnoresStyleVisibility = 1 << 3,
+
+ // Used when emitting the special 0xFFFC character is required. Children for replaced objects will be ignored.
+ TextIteratorEmitsObjectReplacementCharacters = 1 << 4,
+
+ // Used when pasting inside password field.
+ TextIteratorEmitsOriginalText = 1 << 5,
+
+ TextIteratorEmitsImageAltText = 1 << 6,
+
+ TextIteratorBehavesAsIfNodesFollowing = 1 << 7,
+
+ // Makes visiblity test take into account the visibility of the frame.
+ // FIXME: This should probably be always on unless TextIteratorIgnoresStyleVisibility is set.
+ TextIteratorClipsToFrameAncestors = 1 << 8,
+
+ TextIteratorTraversesFlatTree = 1 << 9,
+};
+
+typedef unsigned short TextIteratorBehavior;
+
+} // namespace WebCore
diff --git a/Source/WebCore/editing/TypingCommand.cpp b/Source/WebCore/editing/TypingCommand.cpp
index 1f8768787..b61304a7f 100644
--- a/Source/WebCore/editing/TypingCommand.cpp
+++ b/Source/WebCore/editing/TypingCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2005-2008, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -26,17 +26,25 @@
#include "config.h"
#include "TypingCommand.h"
+#include "AXObjectCache.h"
#include "BreakBlockquoteCommand.h"
+#include "DataTransfer.h"
#include "DeleteSelectionCommand.h"
#include "Document.h"
#include "Editor.h"
#include "Element.h"
#include "Frame.h"
+#include "HTMLElement.h"
#include "HTMLNames.h"
#include "InsertLineBreakCommand.h"
#include "InsertParagraphSeparatorCommand.h"
#include "InsertTextCommand.h"
+#include "Logging.h"
+#include "MarkupAccumulator.h"
+#include "MathMLElement.h"
#include "RenderElement.h"
+#include "StaticRange.h"
+#include "TextIterator.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
@@ -71,24 +79,89 @@ private:
const String& m_text;
};
+static inline EditAction editActionForTypingCommand(TypingCommand::ETypingCommand command, TextGranularity granularity, TypingCommand::TextCompositionType compositionType, bool isAutocompletion)
+{
+ if (compositionType == TypingCommand::TextCompositionPending) {
+ if (command == TypingCommand::InsertText)
+ return EditActionTypingInsertPendingComposition;
+ if (command == TypingCommand::DeleteSelection)
+ return EditActionTypingDeletePendingComposition;
+ ASSERT_NOT_REACHED();
+ }
+
+ if (compositionType == TypingCommand::TextCompositionFinal) {
+ if (command == TypingCommand::InsertText)
+ return EditActionTypingInsertFinalComposition;
+ if (command == TypingCommand::DeleteSelection)
+ return EditActionTypingDeleteFinalComposition;
+ ASSERT_NOT_REACHED();
+ }
+
+ switch (command) {
+ case TypingCommand::DeleteSelection:
+ return EditActionTypingDeleteSelection;
+ case TypingCommand::DeleteKey: {
+ if (granularity == WordGranularity)
+ return EditActionTypingDeleteWordBackward;
+ if (granularity == LineBoundary)
+ return EditActionTypingDeleteLineBackward;
+ return EditActionTypingDeleteBackward;
+ }
+ case TypingCommand::ForwardDeleteKey:
+ if (granularity == WordGranularity)
+ return EditActionTypingDeleteWordForward;
+ if (granularity == LineBoundary)
+ return EditActionTypingDeleteLineForward;
+ return EditActionTypingDeleteForward;
+ case TypingCommand::InsertText:
+ return isAutocompletion ? EditActionInsertReplacement : EditActionTypingInsertText;
+ case TypingCommand::InsertLineBreak:
+ return EditActionTypingInsertLineBreak;
+ case TypingCommand::InsertParagraphSeparator:
+ case TypingCommand::InsertParagraphSeparatorInQuotedContent:
+ return EditActionTypingInsertParagraph;
+ default:
+ return EditActionUnspecified;
+ }
+}
+
+static inline bool editActionIsDeleteByTyping(EditAction action)
+{
+ switch (action) {
+ case EditActionTypingDeleteSelection:
+ case EditActionTypingDeleteBackward:
+ case EditActionTypingDeleteWordBackward:
+ case EditActionTypingDeleteLineBackward:
+ case EditActionTypingDeleteForward:
+ case EditActionTypingDeleteWordForward:
+ case EditActionTypingDeleteLineForward:
+ return true;
+ default:
+ return false;
+ }
+}
+
TypingCommand::TypingCommand(Document& document, ETypingCommand commandType, const String &textToInsert, Options options, TextGranularity granularity, TextCompositionType compositionType)
- : TextInsertionBaseCommand(document)
+ : TextInsertionBaseCommand(document, editActionForTypingCommand(commandType, granularity, compositionType, options & IsAutocompletion))
, m_commandType(commandType)
, m_textToInsert(textToInsert)
+ , m_currentTextToInsert(textToInsert)
, m_openForMoreTyping(true)
, m_selectInsertedText(options & SelectInsertedText)
, m_smartDelete(options & SmartDelete)
, m_granularity(granularity)
, m_compositionType(compositionType)
- , m_killRing(options & KillRing)
+ , m_shouldAddToKillRing(options & AddsToKillRing)
+ , m_isAutocompletion(options & IsAutocompletion)
, m_openedByBackwardDelete(false)
, m_shouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator)
, m_shouldPreventSpellChecking(options & PreventSpellChecking)
{
+ m_currentTypingEditAction = editingAction();
updatePreservesTypingStyle(m_commandType);
}
-void TypingCommand::deleteSelection(Document& document, Options options)
+void TypingCommand::deleteSelection(Document& document, Options options, TextCompositionType compositionType)
{
Frame* frame = document.frame();
ASSERT(frame);
@@ -96,27 +169,31 @@ void TypingCommand::deleteSelection(Document& document, Options options)
if (!frame->selection().isRange())
return;
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame)) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
+ lastTypingCommand->setCompositionType(compositionType);
lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
lastTypingCommand->deleteSelection(options & SmartDelete);
return;
}
- TypingCommand::create(document, DeleteSelection, "", options)->apply();
+ TypingCommand::create(document, DeleteSelection, emptyString(), options, compositionType)->apply();
}
void TypingCommand::deleteKeyPressed(Document& document, Options options, TextGranularity granularity)
{
if (granularity == CharacterGranularity) {
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), document.frame());
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
+ lastTypingCommand->setCompositionType(TextCompositionNone);
lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
- lastTypingCommand->deleteKeyPressed(granularity, options & KillRing);
+ lastTypingCommand->deleteKeyPressed(granularity, options & AddsToKillRing);
return;
}
}
- TypingCommand::create(document, DeleteKey, "", options, granularity)->apply();
+ TypingCommand::create(document, DeleteKey, emptyString(), options, granularity)->apply();
}
void TypingCommand::forwardDeleteKeyPressed(Document& document, Options options, TextGranularity granularity)
@@ -124,15 +201,17 @@ void TypingCommand::forwardDeleteKeyPressed(Document& document, Options options,
// FIXME: Forward delete in TextEdit appears to open and close a new typing command.
Frame* frame = document.frame();
if (granularity == CharacterGranularity) {
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame)) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
updateSelectionIfDifferentFromCurrentSelection(lastTypingCommand.get(), frame);
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
+ lastTypingCommand->setCompositionType(TextCompositionNone);
lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
- lastTypingCommand->forwardDeleteKeyPressed(granularity, options & KillRing);
+ lastTypingCommand->forwardDeleteKeyPressed(granularity, options & AddsToKillRing);
return;
}
}
- TypingCommand::create(document, ForwardDeleteKey, "", options, granularity)->apply();
+ TypingCommand::create(document, ForwardDeleteKey, emptyString(), options, granularity)->apply();
}
void TypingCommand::updateSelectionIfDifferentFromCurrentSelection(TypingCommand* typingCommand, Frame* frame)
@@ -152,7 +231,7 @@ void TypingCommand::insertText(Document& document, const String& text, Options o
ASSERT(frame);
if (!text.isEmpty())
- frame->editor().updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text.deprecatedCharacters()[0]));
+ frame->editor().updateMarkersForWordsAffectedByEditing(isSpaceOrNewline(text[0]));
insertText(document, text, frame->selection().selection(), options, composition);
}
@@ -163,45 +242,52 @@ void TypingCommand::insertText(Document& document, const String& text, const Vis
RefPtr<Frame> frame = document.frame();
ASSERT(frame);
+ LOG(Editing, "TypingCommand::insertText (text %s)", text.utf8().data());
+
VisibleSelection currentSelection = frame->selection().selection();
- String newText = dispatchBeforeTextInsertedEvent(text, selectionForInsertion, compositionType == TextCompositionUpdate);
+ String newText = dispatchBeforeTextInsertedEvent(text, selectionForInsertion, compositionType == TextCompositionPending);
// 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
// to deal with custom selections in a general way that can be used by all of the commands.
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame.get())) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
if (lastTypingCommand->endingSelection() != selectionForInsertion) {
lastTypingCommand->setStartingSelection(selectionForInsertion);
lastTypingCommand->setEndingSelection(selectionForInsertion);
}
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
lastTypingCommand->setCompositionType(compositionType);
lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
lastTypingCommand->setShouldPreventSpellChecking(options & PreventSpellChecking);
- lastTypingCommand->insertText(newText, options & SelectInsertedText);
+ lastTypingCommand->insertTextAndNotifyAccessibility(newText, options & SelectInsertedText);
return;
}
RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, options, compositionType);
- applyTextInsertionCommand(frame.get(), cmd, selectionForInsertion, currentSelection);
+ applyTextInsertionCommand(frame.get(), *cmd, selectionForInsertion, currentSelection);
}
void TypingCommand::insertLineBreak(Document& document, Options options)
{
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
+ lastTypingCommand->setCompositionType(TextCompositionNone);
lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
- lastTypingCommand->insertLineBreak();
+ lastTypingCommand->insertLineBreakAndNotifyAccessibility();
return;
}
- applyCommand(TypingCommand::create(document, InsertLineBreak, "", options));
+ applyCommand(TypingCommand::create(document, InsertLineBreak, emptyString(), options));
}
void TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document)
{
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
- lastTypingCommand->insertParagraphSeparatorInQuotedContent();
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
+ lastTypingCommand->setIsAutocompletion(false);
+ lastTypingCommand->setCompositionType(TextCompositionNone);
+ lastTypingCommand->insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
return;
}
@@ -210,47 +296,73 @@ void TypingCommand::insertParagraphSeparatorInQuotedContent(Document& document)
void TypingCommand::insertParagraphSeparator(Document& document, Options options)
{
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(document.frame())) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*document.frame())) {
+ lastTypingCommand->setIsAutocompletion(options & IsAutocompletion);
+ lastTypingCommand->setCompositionType(TextCompositionNone);
lastTypingCommand->setShouldRetainAutocorrectionIndicator(options & RetainAutocorrectionIndicator);
- lastTypingCommand->insertParagraphSeparator();
+ lastTypingCommand->insertParagraphSeparatorAndNotifyAccessibility();
return;
}
- applyCommand(TypingCommand::create(document, InsertParagraphSeparator, "", options));
+ applyCommand(TypingCommand::create(document, InsertParagraphSeparator, emptyString(), options));
}
-PassRefPtr<TypingCommand> TypingCommand::lastTypingCommandIfStillOpenForTyping(Frame* frame)
+RefPtr<TypingCommand> TypingCommand::lastTypingCommandIfStillOpenForTyping(Frame& frame)
{
- ASSERT(frame);
-
- RefPtr<CompositeEditCommand> lastEditCommand = frame->editor().lastEditCommand();
+ RefPtr<CompositeEditCommand> lastEditCommand = frame.editor().lastEditCommand();
if (!lastEditCommand || !lastEditCommand->isTypingCommand() || !static_cast<TypingCommand*>(lastEditCommand.get())->isOpenForMoreTyping())
- return 0;
+ return nullptr;
return static_cast<TypingCommand*>(lastEditCommand.get());
}
+bool TypingCommand::shouldDeferWillApplyCommandUntilAddingTypingCommand() const
+{
+ return !m_isHandlingInitialTypingCommand || editActionIsDeleteByTyping(editingAction());
+}
+
void TypingCommand::closeTyping(Frame* frame)
{
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame))
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame))
lastTypingCommand->closeTyping();
}
#if PLATFORM(IOS)
void TypingCommand::ensureLastEditCommandHasCurrentSelectionIfOpenForMoreTyping(Frame* frame, const VisibleSelection& newSelection)
{
- if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(frame)) {
+ if (RefPtr<TypingCommand> lastTypingCommand = lastTypingCommandIfStillOpenForTyping(*frame)) {
lastTypingCommand->setEndingSelection(newSelection);
lastTypingCommand->setEndingSelectionOnLastInsertCommand(newSelection);
}
}
#endif
+void TypingCommand::postTextStateChangeNotificationForDeletion(const VisibleSelection& selection)
+{
+ if (!AXObjectCache::accessibilityEnabled())
+ return;
+ postTextStateChangeNotification(AXTextEditTypeDelete, AccessibilityObject::stringForVisiblePositionRange(selection), selection.start());
+ VisiblePositionIndexRange range;
+ range.startIndex.value = indexForVisiblePosition(selection.start(), range.startIndex.scope);
+ range.endIndex.value = indexForVisiblePosition(selection.end(), range.endIndex.scope);
+ composition()->setRangeDeletedByUnapply(range);
+}
+
+bool TypingCommand::willApplyCommand()
+{
+ if (shouldDeferWillApplyCommandUntilAddingTypingCommand()) {
+ // The TypingCommand will handle the willApplyCommand logic separately in TypingCommand::willAddTypingToOpenCommand.
+ return true;
+ }
+
+ return CompositeEditCommand::willApplyCommand();
+}
+
void TypingCommand::doApply()
{
- if (!endingSelection().isNonOrphanedCaretOrRange())
+ if (endingSelection().isNoneOrOrphaned())
return;
-
+
if (m_commandType == DeleteKey)
if (m_commands.isEmpty())
m_openedByBackwardDelete = true;
@@ -260,44 +372,81 @@ void TypingCommand::doApply()
deleteSelection(m_smartDelete);
return;
case DeleteKey:
- deleteKeyPressed(m_granularity, m_killRing);
+ deleteKeyPressed(m_granularity, m_shouldAddToKillRing);
return;
case ForwardDeleteKey:
- forwardDeleteKeyPressed(m_granularity, m_killRing);
+ forwardDeleteKeyPressed(m_granularity, m_shouldAddToKillRing);
return;
case InsertLineBreak:
- insertLineBreak();
+ insertLineBreakAndNotifyAccessibility();
return;
case InsertParagraphSeparator:
- insertParagraphSeparator();
+ insertParagraphSeparatorAndNotifyAccessibility();
return;
case InsertParagraphSeparatorInQuotedContent:
- insertParagraphSeparatorInQuotedContent();
+ insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
return;
case InsertText:
- insertText(m_textToInsert, m_selectInsertedText);
+ insertTextAndNotifyAccessibility(m_textToInsert, m_selectInsertedText);
return;
}
ASSERT_NOT_REACHED();
}
-EditAction TypingCommand::editingAction() const
+String TypingCommand::inputEventTypeName() const
{
- return EditActionTyping;
+ return inputTypeNameForEditingAction(m_currentTypingEditAction);
+}
+
+bool TypingCommand::isBeforeInputEventCancelable() const
+{
+ return m_currentTypingEditAction != EditActionTypingInsertPendingComposition && m_currentTypingEditAction != EditActionTypingDeletePendingComposition;
+}
+
+String TypingCommand::inputEventData() const
+{
+ switch (m_currentTypingEditAction) {
+ case EditActionTypingInsertText:
+ case EditActionTypingInsertPendingComposition:
+ case EditActionTypingInsertFinalComposition:
+ return m_currentTextToInsert;
+ case EditActionInsertReplacement:
+ return isEditingTextAreaOrTextInput() ? m_currentTextToInsert : String();
+ default:
+ return CompositeEditCommand::inputEventData();
+ }
+}
+
+RefPtr<DataTransfer> TypingCommand::inputEventDataTransfer() const
+{
+ if (m_currentTypingEditAction != EditActionInsertReplacement || isEditingTextAreaOrTextInput())
+ return nullptr;
+
+ StringBuilder htmlText;
+ MarkupAccumulator::appendCharactersReplacingEntities(htmlText, m_currentTextToInsert, 0, m_currentTextToInsert.length(), EntityMaskInHTMLPCDATA);
+ return DataTransfer::createForInputEvent(m_currentTextToInsert, htmlText.toString());
+}
+
+void TypingCommand::didApplyCommand()
+{
+ // TypingCommands handle applied editing separately (see TypingCommand::typingAddedToOpenCommand).
+ m_isHandlingInitialTypingCommand = false;
}
void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
{
Frame& frame = this->frame();
-#if PLATFORM(MAC) && !PLATFORM(IOS)
+#if PLATFORM(MAC)
if (!frame.editor().isContinuousSpellCheckingEnabled()
&& !frame.editor().isAutomaticQuoteSubstitutionEnabled()
&& !frame.editor().isAutomaticLinkDetectionEnabled()
&& !frame.editor().isAutomaticDashSubstitutionEnabled()
&& !frame.editor().isAutomaticTextReplacementEnabled())
return;
+ if (frame.editor().isHandlingAcceptedCandidate())
+ return;
#else
if (!frame.editor().isContinuousSpellCheckingEnabled())
return;
@@ -339,13 +488,28 @@ void TypingCommand::markMisspellingsAfterTyping(ETypingCommand commandType)
}
}
+bool TypingCommand::willAddTypingToOpenCommand(ETypingCommand commandType, TextGranularity granularity, const String& text, RefPtr<Range>&& range)
+{
+ m_currentTextToInsert = text;
+ m_currentTypingEditAction = editActionForTypingCommand(commandType, granularity, m_compositionType, m_isAutocompletion);
+
+ if (!shouldDeferWillApplyCommandUntilAddingTypingCommand())
+ return true;
+
+ if (!range || isEditingTextAreaOrTextInput())
+ return frame().editor().willApplyEditing(*this, CompositeEditCommand::targetRangesForBindings());
+
+ RefPtr<StaticRange> staticRange = StaticRange::createFromRange(*range);
+ return frame().editor().willApplyEditing(*this, { 1, staticRange });
+}
+
void TypingCommand::typingAddedToOpenCommand(ETypingCommand commandTypeForAddedTyping)
{
Frame& frame = this->frame();
updatePreservesTypingStyle(commandTypeForAddedTyping);
-#if PLATFORM(MAC)
+#if PLATFORM(COCOA)
frame.editor().appliedEditing(this);
// Since the spellchecking code may also perform corrections and other replacements, it should happen after the typing changes.
if (!m_shouldPreventSpellChecking)
@@ -368,10 +532,23 @@ void TypingCommand::insertText(const String &text, bool selectInsertedText)
forEachLineInString(text, operation);
}
+void TypingCommand::insertTextAndNotifyAccessibility(const String &text, bool selectInsertedText)
+{
+ LOG(Editing, "TypingCommand %p insertTextAndNotifyAccessibility (text %s, selectInsertedText %d)", this, text.utf8().data(), selectInsertedText);
+
+ AccessibilityReplacedText replacedText(frame().selection().selection());
+ insertText(text, selectInsertedText);
+ replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, text, frame().selection().selection());
+ composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
+}
+
void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText)
{
+ if (!willAddTypingToOpenCommand(InsertText, CharacterGranularity, text))
+ return;
+
RefPtr<InsertTextCommand> command = InsertTextCommand::create(document(), text, selectInsertedText,
- m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces);
+ m_compositionType == TextCompositionNone ? InsertTextCommand::RebalanceLeadingAndTrailingWhitespaces : InsertTextCommand::RebalanceAllWhitespaces, EditActionTypingInsertText);
applyCommandToComposite(command, endingSelection());
@@ -383,21 +560,46 @@ void TypingCommand::insertLineBreak()
if (!canAppendNewLineFeedToSelection(endingSelection()))
return;
+ if (!willAddTypingToOpenCommand(InsertLineBreak, LineGranularity))
+ return;
+
applyCommandToComposite(InsertLineBreakCommand::create(document()));
typingAddedToOpenCommand(InsertLineBreak);
}
+void TypingCommand::insertLineBreakAndNotifyAccessibility()
+{
+ AccessibilityReplacedText replacedText(frame().selection().selection());
+ insertLineBreak();
+ replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
+ composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
+}
+
void TypingCommand::insertParagraphSeparator()
{
if (!canAppendNewLineFeedToSelection(endingSelection()))
return;
- applyCommandToComposite(InsertParagraphSeparatorCommand::create(document()));
+ if (!willAddTypingToOpenCommand(InsertParagraphSeparator, ParagraphGranularity))
+ return;
+
+ applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), false, false, EditActionTypingInsertParagraph));
typingAddedToOpenCommand(InsertParagraphSeparator);
}
+void TypingCommand::insertParagraphSeparatorAndNotifyAccessibility()
+{
+ AccessibilityReplacedText replacedText(frame().selection().selection());
+ insertParagraphSeparator();
+ replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
+ composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
+}
+
void TypingCommand::insertParagraphSeparatorInQuotedContent()
{
+ if (!willAddTypingToOpenCommand(InsertParagraphSeparatorInQuotedContent, ParagraphGranularity))
+ return;
+
// If the selection starts inside a table, just insert the paragraph separator normally
// Breaking the blockquote would also break apart the table, which is unecessary when inserting a newline
if (enclosingNodeOfType(endingSelection().start(), &isTableStructureNode)) {
@@ -409,6 +611,14 @@ void TypingCommand::insertParagraphSeparatorInQuotedContent()
typingAddedToOpenCommand(InsertParagraphSeparatorInQuotedContent);
}
+void TypingCommand::insertParagraphSeparatorInQuotedContentAndNotifyAccessibility()
+{
+ AccessibilityReplacedText replacedText(frame().selection().selection());
+ insertParagraphSeparatorInQuotedContent();
+ replacedText.postTextStateChangeNotification(document().existingAXObjectCache(), AXTextEditTypeTyping, "\n", frame().selection().selection());
+ composition()->setRangeDeletedByUnapply(replacedText.replacedRange());
+}
+
bool TypingCommand::makeEditableRootEmpty()
{
Element* root = endingSelection().rootEditableElement();
@@ -430,9 +640,10 @@ bool TypingCommand::makeEditableRootEmpty()
return true;
}
-void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
+void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool shouldAddToKillRing)
{
Frame& frame = this->frame();
+ Ref<Frame> protector(frame);
frame.editor().updateMarkersForWordsAffectedByEditing(false);
@@ -455,16 +666,20 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
FrameSelection selection;
selection.setSelection(endingSelection());
selection.modify(FrameSelection::AlterationExtend, DirectionBackward, granularity);
- if (killRing && selection.isCaret() && granularity != CharacterGranularity)
+ if (shouldAddToKillRing && selection.isCaret() && granularity != CharacterGranularity)
selection.modify(FrameSelection::AlterationExtend, DirectionBackward, CharacterGranularity);
if (endingSelection().visibleStart().previous(CannotCrossEditingBoundary).isNull()) {
// When the caret is at the start of the editable area in an empty list item, break out of the list item.
- if (breakOutOfEmptyListItem()) {
- typingAddedToOpenCommand(DeleteKey);
+ if (auto deleteListSelection = shouldBreakOutOfEmptyListItem()) {
+ if (willAddTypingToOpenCommand(DeleteKey, granularity, { }, Range::create(document(), deleteListSelection.value().start(), deleteListSelection.value().end()))) {
+ breakOutOfEmptyListItem();
+ typingAddedToOpenCommand(DeleteKey);
+ }
return;
}
// When there are no visible positions in the editing root, delete its entire contents.
+ // FIXME: Dispatch a `beforeinput` event here and bail if preventDefault() was invoked.
if (endingSelection().visibleStart().next(CannotCrossEditingBoundary).isNull() && makeEditableRootEmpty()) {
typingAddedToOpenCommand(DeleteKey);
return;
@@ -527,8 +742,15 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
if (selectionToDelete.isCaret() || !frame.selection().shouldDeleteSelection(selectionToDelete))
return;
- if (killRing)
- frame.editor().addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
+ if (!willAddTypingToOpenCommand(DeleteKey, granularity, { }, selectionToDelete.firstRange()))
+ return;
+
+ if (shouldAddToKillRing)
+ frame.editor().addRangeToKillRing(*selectionToDelete.toNormalizedRange().get(), Editor::KillRingInsertionMode::PrependText);
+
+ // Post the accessibility notification before actually deleting the content while selectionToDelete is still valid
+ postTextStateChangeNotificationForDeletion(selectionToDelete);
+
// Make undo select everything that has been deleted, unless an undo will undo more than just this deletion.
// FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete
// more text than you insert. In that case all of the text that was around originally should be selected.
@@ -539,9 +761,10 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
typingAddedToOpenCommand(DeleteKey);
}
-void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool killRing)
+void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool shouldAddToKillRing)
{
Frame& frame = this->frame();
+ Ref<Frame> protector(frame);
frame.editor().updateMarkersForWordsAffectedByEditing(false);
@@ -562,7 +785,7 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
FrameSelection selection;
selection.setSelection(endingSelection());
selection.modify(FrameSelection::AlterationExtend, DirectionForward, granularity);
- if (killRing && selection.isCaret() && granularity != CharacterGranularity)
+ if (shouldAddToKillRing && selection.isCaret() && granularity != CharacterGranularity)
selection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
Position downstreamEnd = endingSelection().end().downstream();
@@ -574,7 +797,7 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
downstreamEnd = visibleEnd.next(CannotCrossEditingBoundary).deepEquivalent().downstream();
// When deleting tables: Select the table first, then perform the deletion
if (downstreamEnd.containerNode() && downstreamEnd.containerNode()->renderer() && downstreamEnd.containerNode()->renderer()->isTable()
- && downstreamEnd.computeOffsetInContainerNode() <= caretMinOffset(downstreamEnd.containerNode())) {
+ && downstreamEnd.computeOffsetInContainerNode() <= caretMinOffset(*downstreamEnd.containerNode())) {
setEndingSelection(VisibleSelection(endingSelection().end(), positionAfterNode(downstreamEnd.containerNode()), DOWNSTREAM, endingSelection().isDirectional()));
typingAddedToOpenCommand(ForwardDeleteKey);
return;
@@ -624,9 +847,15 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
if (selectionToDelete.isCaret() || !frame.selection().shouldDeleteSelection(selectionToDelete))
return;
-
- if (killRing)
- frame.editor().addToKillRing(selectionToDelete.toNormalizedRange().get(), false);
+
+ if (!willAddTypingToOpenCommand(ForwardDeleteKey, granularity, { }, selectionToDelete.firstRange()))
+ return;
+
+ // Post the accessibility notification before actually deleting the content while selectionToDelete is still valid
+ postTextStateChangeNotificationForDeletion(selectionToDelete);
+
+ if (shouldAddToKillRing)
+ frame.editor().addRangeToKillRing(*selectionToDelete.toNormalizedRange().get(), Editor::KillRingInsertionMode::AppendText);
// make undo select what was deleted
setStartingSelection(selectionAfterUndo);
CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete);
@@ -636,6 +865,9 @@ void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity, bool ki
void TypingCommand::deleteSelection(bool smartDelete)
{
+ if (!willAddTypingToOpenCommand(DeleteSelection, CharacterGranularity))
+ return;
+
CompositeEditCommand::deleteSelection(smartDelete);
typingAddedToOpenCommand(DeleteSelection);
}
diff --git a/Source/WebCore/editing/TypingCommand.h b/Source/WebCore/editing/TypingCommand.h
index e249a34b1..44666884b 100644
--- a/Source/WebCore/editing/TypingCommand.h
+++ b/Source/WebCore/editing/TypingCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,14 +23,13 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef TypingCommand_h
-#define TypingCommand_h
+#pragma once
#include "TextInsertionBaseCommand.h"
namespace WebCore {
-class TypingCommand : public TextInsertionBaseCommand {
+class TypingCommand final : public TextInsertionBaseCommand {
public:
enum ETypingCommand {
DeleteSelection,
@@ -44,20 +43,21 @@ public:
enum TextCompositionType {
TextCompositionNone,
- TextCompositionUpdate,
- TextCompositionConfirm
+ TextCompositionPending,
+ TextCompositionFinal,
};
enum Option {
SelectInsertedText = 1 << 0,
- KillRing = 1 << 1,
+ AddsToKillRing = 1 << 1,
RetainAutocorrectionIndicator = 1 << 2,
PreventSpellChecking = 1 << 3,
- SmartDelete = 1 << 4
+ SmartDelete = 1 << 4,
+ IsAutocompletion = 1 << 5,
};
typedef unsigned Options;
- static void deleteSelection(Document&, Options = 0);
+ static void deleteSelection(Document&, Options = 0, TextCompositionType = TextCompositionNone);
static void deleteKeyPressed(Document&, Options = 0, TextGranularity = CharacterGranularity);
static void forwardDeleteKeyPressed(Document&, Options = 0, TextGranularity = CharacterGranularity);
static void insertText(Document&, const String&, Options, TextCompositionType = TextCompositionNone);
@@ -75,24 +75,25 @@ public:
void insertLineBreak();
void insertParagraphSeparatorInQuotedContent();
void insertParagraphSeparator();
- void deleteKeyPressed(TextGranularity, bool killRing);
- void forwardDeleteKeyPressed(TextGranularity, bool killRing);
+ void deleteKeyPressed(TextGranularity, bool shouldAddToKillRing);
+ void forwardDeleteKeyPressed(TextGranularity, bool shouldAddToKillRing);
void deleteSelection(bool smartDelete);
void setCompositionType(TextCompositionType type) { m_compositionType = type; }
+ void setIsAutocompletion(bool isAutocompletion) { m_isAutocompletion = isAutocompletion; }
#if PLATFORM(IOS)
void setEndingSelectionOnLastInsertCommand(const VisibleSelection& selection);
#endif
private:
- static PassRefPtr<TypingCommand> create(Document& document, ETypingCommand command, const String& text = "", Options options = 0, TextGranularity granularity = CharacterGranularity)
+ static Ref<TypingCommand> create(Document& document, ETypingCommand command, const String& text = emptyString(), Options options = 0, TextGranularity granularity = CharacterGranularity, TextCompositionType compositionType = TextCompositionNone)
{
- return adoptRef(new TypingCommand(document, command, text, options, granularity, TextCompositionNone));
+ return adoptRef(*new TypingCommand(document, command, text, options, granularity, compositionType));
}
- static PassRefPtr<TypingCommand> create(Document& document, ETypingCommand command, const String& text, Options options, TextCompositionType compositionType)
+ static Ref<TypingCommand> create(Document& document, ETypingCommand command, const String& text, Options options, TextCompositionType compositionType)
{
- return adoptRef(new TypingCommand(document, command, text, options, CharacterGranularity, compositionType));
+ return adoptRef(*new TypingCommand(document, command, text, options, CharacterGranularity, compositionType));
}
TypingCommand(Document&, ETypingCommand, const String& text, Options, TextGranularity, TextCompositionType);
@@ -102,37 +103,57 @@ private:
bool isOpenForMoreTyping() const { return m_openForMoreTyping; }
void closeTyping() { m_openForMoreTyping = false; }
- static PassRefPtr<TypingCommand> lastTypingCommandIfStillOpenForTyping(Frame*);
+ static RefPtr<TypingCommand> lastTypingCommandIfStillOpenForTyping(Frame&);
- virtual void doApply();
- virtual EditAction editingAction() const;
- virtual bool isTypingCommand() const;
- virtual bool preservesTypingStyle() const { return m_preservesTypingStyle; }
- virtual bool shouldRetainAutocorrectionIndicator() const
+ void doApply() final;
+ bool isTypingCommand() const final;
+ bool preservesTypingStyle() const final { return m_preservesTypingStyle; }
+ bool shouldRetainAutocorrectionIndicator() const final
{
ASSERT(isTopLevelCommand());
return m_shouldRetainAutocorrectionIndicator;
}
- virtual void setShouldRetainAutocorrectionIndicator(bool retain) { m_shouldRetainAutocorrectionIndicator = retain; }
- virtual bool shouldStopCaretBlinking() const { return true; }
+ void setShouldRetainAutocorrectionIndicator(bool retain) final { m_shouldRetainAutocorrectionIndicator = retain; }
+ bool shouldStopCaretBlinking() const final { return true; }
void setShouldPreventSpellChecking(bool prevent) { m_shouldPreventSpellChecking = prevent; }
+ String inputEventTypeName() const final;
+ String inputEventData() const final;
+ RefPtr<DataTransfer> inputEventDataTransfer() const final;
+ bool isBeforeInputEventCancelable() const final;
+
static void updateSelectionIfDifferentFromCurrentSelection(TypingCommand*, Frame*);
void updatePreservesTypingStyle(ETypingCommand);
+ bool willAddTypingToOpenCommand(ETypingCommand, TextGranularity, const String& = emptyString(), RefPtr<Range>&& = nullptr);
void markMisspellingsAfterTyping(ETypingCommand);
void typingAddedToOpenCommand(ETypingCommand);
bool makeEditableRootEmpty();
+ void postTextStateChangeNotificationForDeletion(const VisibleSelection&);
+ void insertTextAndNotifyAccessibility(const String &text, bool selectInsertedText);
+ void insertLineBreakAndNotifyAccessibility();
+ void insertParagraphSeparatorInQuotedContentAndNotifyAccessibility();
+ void insertParagraphSeparatorAndNotifyAccessibility();
+
+ bool willApplyCommand() final;
+ void didApplyCommand() final;
+
+ bool shouldDeferWillApplyCommandUntilAddingTypingCommand() const;
+
ETypingCommand m_commandType;
+ EditAction m_currentTypingEditAction;
String m_textToInsert;
+ String m_currentTextToInsert;
bool m_openForMoreTyping;
bool m_selectInsertedText;
bool m_smartDelete;
+ bool m_isHandlingInitialTypingCommand { true };
TextGranularity m_granularity;
TextCompositionType m_compositionType;
- bool m_killRing;
+ bool m_shouldAddToKillRing;
bool m_preservesTypingStyle;
+ bool m_isAutocompletion;
// Undoing a series of backward deletes will restore a selection around all of the
// characters that were deleted, but only if the typing command being undone
@@ -144,5 +165,3 @@ private:
};
} // namespace WebCore
-
-#endif // TypingCommand_h
diff --git a/Source/WebCore/editing/UndoStep.h b/Source/WebCore/editing/UndoStep.h
index ed202b427..ff10f2799 100644
--- a/Source/WebCore/editing/UndoStep.h
+++ b/Source/WebCore/editing/UndoStep.h
@@ -28,8 +28,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef UndoStep_h
-#define UndoStep_h
+#pragma once
#include "EditAction.h"
#include <wtf/RefCounted.h>
@@ -45,6 +44,4 @@ public:
virtual EditAction editingAction() const = 0;
};
-}
-
-#endif
+} // namespace WebCore
diff --git a/Source/WebCore/editing/UnlinkCommand.cpp b/Source/WebCore/editing/UnlinkCommand.cpp
index a82f2a0e0..443e9a667 100644
--- a/Source/WebCore/editing/UnlinkCommand.cpp
+++ b/Source/WebCore/editing/UnlinkCommand.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2006 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
diff --git a/Source/WebCore/editing/UnlinkCommand.h b/Source/WebCore/editing/UnlinkCommand.h
index 47c62c137..17c50a9a3 100644
--- a/Source/WebCore/editing/UnlinkCommand.h
+++ b/Source/WebCore/editing/UnlinkCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef UnlinkCommand_h
-#define UnlinkCommand_h
+#pragma once
#include "CompositeEditCommand.h"
@@ -32,18 +31,16 @@ namespace WebCore {
class UnlinkCommand : public CompositeEditCommand {
public:
- static PassRefPtr<UnlinkCommand> create(Document& document)
+ static Ref<UnlinkCommand> create(Document& document)
{
- return adoptRef(new UnlinkCommand(document));
+ return adoptRef(*new UnlinkCommand(document));
}
private:
explicit UnlinkCommand(Document&);
- virtual void doApply();
- virtual EditAction editingAction() const { return EditActionUnlink; }
+ void doApply() override;
+ EditAction editingAction() const override { return EditActionUnlink; }
};
} // namespace WebCore
-
-#endif // UnlinkCommand_h
diff --git a/Source/WebCore/editing/VisiblePosition.cpp b/Source/WebCore/editing/VisiblePosition.cpp
index 9155aaef3..bda4a1476 100644
--- a/Source/WebCore/editing/VisiblePosition.cpp
+++ b/Source/WebCore/editing/VisiblePosition.cpp
@@ -11,10 +11,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -30,6 +30,7 @@
#include "Document.h"
#include "FloatQuad.h"
#include "HTMLElement.h"
+#include "HTMLHtmlElement.h"
#include "HTMLNames.h"
#include "InlineTextBox.h"
#include "Logging.h"
@@ -37,6 +38,7 @@
#include "RenderBlock.h"
#include "RootInlineBox.h"
#include "Text.h"
+#include "TextStream.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
#include <stdio.h>
@@ -62,8 +64,10 @@ void VisiblePosition::init(const Position& position, EAffinity affinity)
m_affinity = DOWNSTREAM;
}
-VisiblePosition VisiblePosition::next(EditingBoundaryCrossingRule rule) const
+VisiblePosition VisiblePosition::next(EditingBoundaryCrossingRule rule, bool* reachedBoundary) const
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
// FIXME: Support CanSkipEditingBoundary
ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
VisiblePosition next(nextVisuallyDistinctCandidate(m_deepPosition), m_affinity);
@@ -71,19 +75,24 @@ VisiblePosition VisiblePosition::next(EditingBoundaryCrossingRule rule) const
if (rule == CanCrossEditingBoundary)
return next;
- return honorEditingBoundaryAtOrAfter(next);
+ return honorEditingBoundaryAtOrAfter(next, reachedBoundary);
}
-VisiblePosition VisiblePosition::previous(EditingBoundaryCrossingRule rule) const
+VisiblePosition VisiblePosition::previous(EditingBoundaryCrossingRule rule, bool* reachedBoundary) const
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
// FIXME: Support CanSkipEditingBoundary
ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
// find first previous DOM position that is visible
Position pos = previousVisuallyDistinctCandidate(m_deepPosition);
// return null visible position if there is no previous visible position
- if (pos.atStartOfTree())
+ if (pos.atStartOfTree()) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM);
ASSERT(prev != *this);
@@ -101,7 +110,7 @@ VisiblePosition VisiblePosition::previous(EditingBoundaryCrossingRule rule) cons
if (rule == CanCrossEditingBoundary)
return prev;
- return honorEditingBoundaryAtOrBefore(prev);
+ return honorEditingBoundaryAtOrBefore(prev, reachedBoundary);
}
Position VisiblePosition::leftVisuallyDistinctCandidate() const
@@ -173,7 +182,7 @@ Position VisiblePosition::leftVisuallyDistinctCandidate() const
if (box->direction() == primaryDirection) {
if (!prevBox) {
- InlineBox* logicalStart = 0;
+ InlineBox* logicalStart = nullptr;
if (primaryDirection == LTR ? box->root().getLogicalStartBoxWithNode(logicalStart) : box->root().getLogicalEndBoxWithNode(logicalStart)) {
box = logicalStart;
renderer = &box->renderer();
@@ -252,12 +261,17 @@ Position VisiblePosition::leftVisuallyDistinctCandidate() const
}
}
-VisiblePosition VisiblePosition::left(bool stayInEditableContent) const
+VisiblePosition VisiblePosition::left(bool stayInEditableContent, bool* reachedBoundary) const
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
Position pos = leftVisuallyDistinctCandidate();
// FIXME: Why can't we move left from the last position in a tree?
- if (pos.atStartOfTree() || pos.atEndOfTree())
+ if (pos.atStartOfTree() || pos.atEndOfTree()) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
VisiblePosition left = VisiblePosition(pos, DOWNSTREAM);
ASSERT(left != *this);
@@ -266,7 +280,7 @@ VisiblePosition VisiblePosition::left(bool stayInEditableContent) const
return left;
// FIXME: This may need to do something different from "before".
- return honorEditingBoundaryAtOrBefore(left);
+ return honorEditingBoundaryAtOrBefore(left, reachedBoundary);
}
Position VisiblePosition::rightVisuallyDistinctCandidate() const
@@ -338,7 +352,7 @@ Position VisiblePosition::rightVisuallyDistinctCandidate() const
if (box->direction() == primaryDirection) {
if (!nextBox) {
- InlineBox* logicalEnd = 0;
+ InlineBox* logicalEnd = nullptr;
if (primaryDirection == LTR ? box->root().getLogicalEndBoxWithNode(logicalEnd) : box->root().getLogicalStartBoxWithNode(logicalEnd)) {
box = logicalEnd;
renderer = &box->renderer();
@@ -420,12 +434,17 @@ Position VisiblePosition::rightVisuallyDistinctCandidate() const
}
}
-VisiblePosition VisiblePosition::right(bool stayInEditableContent) const
+VisiblePosition VisiblePosition::right(bool stayInEditableContent, bool* reachedBoundary) const
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
Position pos = rightVisuallyDistinctCandidate();
// FIXME: Why can't we move left from the last position in a tree?
- if (pos.atStartOfTree() || pos.atEndOfTree())
+ if (pos.atStartOfTree() || pos.atEndOfTree()) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
VisiblePosition right = VisiblePosition(pos, DOWNSTREAM);
ASSERT(right != *this);
@@ -434,56 +453,78 @@ VisiblePosition VisiblePosition::right(bool stayInEditableContent) const
return right;
// FIXME: This may need to do something different from "after".
- return honorEditingBoundaryAtOrAfter(right);
+ return honorEditingBoundaryAtOrAfter(right, reachedBoundary);
}
-VisiblePosition VisiblePosition::honorEditingBoundaryAtOrBefore(const VisiblePosition &pos) const
+VisiblePosition VisiblePosition::honorEditingBoundaryAtOrBefore(const VisiblePosition& position, bool* reachedBoundary) const
{
- if (pos.isNull())
- return pos;
+ if (reachedBoundary)
+ *reachedBoundary = false;
+ if (position.isNull())
+ return position;
- Node* highestRoot = highestEditableRoot(deepEquivalent());
+ auto* highestRoot = highestEditableRoot(deepEquivalent());
// Return empty position if pos is not somewhere inside the editable region containing this position
- if (highestRoot && !pos.deepEquivalent().deprecatedNode()->isDescendantOf(highestRoot))
+ if (highestRoot && !position.deepEquivalent().deprecatedNode()->isDescendantOf(*highestRoot)) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
-
- // Return pos itself if the two are from the very same editable region, or both are non-editable
+ }
+
+ // Return position itself if the two are from the very same editable region, or both are non-editable
// FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement
// to it is allowed. VisibleSelection::adjustForEditableContent has this problem too.
- if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
- return pos;
+ if (highestEditableRoot(position.deepEquivalent()) == highestRoot) {
+ if (reachedBoundary)
+ *reachedBoundary = *this == position;
+ return position;
+ }
// Return empty position if this position is non-editable, but pos is editable
// FIXME: Move to the previous non-editable region.
- if (!highestRoot)
+ if (!highestRoot) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
// Return the last position before pos that is in the same editable region as this position
- return lastEditablePositionBeforePositionInRoot(pos.deepEquivalent(), highestRoot);
+ return lastEditablePositionBeforePositionInRoot(position.deepEquivalent(), highestRoot);
}
-VisiblePosition VisiblePosition::honorEditingBoundaryAtOrAfter(const VisiblePosition &pos) const
+VisiblePosition VisiblePosition::honorEditingBoundaryAtOrAfter(const VisiblePosition &pos, bool* reachedBoundary) const
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
if (pos.isNull())
return pos;
- Node* highestRoot = highestEditableRoot(deepEquivalent());
+ auto* highestRoot = highestEditableRoot(deepEquivalent());
// Return empty position if pos is not somewhere inside the editable region containing this position
- if (highestRoot && !pos.deepEquivalent().deprecatedNode()->isDescendantOf(highestRoot))
+ if (highestRoot && !pos.deepEquivalent().deprecatedNode()->isDescendantOf(*highestRoot)) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
// Return pos itself if the two are from the very same editable region, or both are non-editable
// FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement
// to it is allowed. VisibleSelection::adjustForEditableContent has this problem too.
- if (highestEditableRoot(pos.deepEquivalent()) == highestRoot)
+ if (highestEditableRoot(pos.deepEquivalent()) == highestRoot) {
+ if (reachedBoundary)
+ *reachedBoundary = *this == pos;
return pos;
+ }
// Return empty position if this position is non-editable, but pos is editable
// FIXME: Move to the next non-editable region.
- if (!highestRoot)
+ if (!highestRoot) {
+ if (reachedBoundary)
+ *reachedBoundary = true;
return VisiblePosition();
+ }
// Return the next position after pos that is in the same editable region as this position
return firstEditablePositionAfterPositionInRoot(pos.deepEquivalent(), highestRoot);
@@ -536,14 +577,17 @@ Position VisiblePosition::canonicalPosition(const Position& passedPosition)
// The new position must be in the same editable element. Enforce that first.
// Unless the descent is from a non-editable html element to an editable body.
- if (node && node->hasTagName(htmlTag) && !node->hasEditableStyle() && node->document().body() && node->document().body()->hasEditableStyle())
- return next.isNotNull() ? next : prev;
+ if (is<HTMLHtmlElement>(node) && !node->hasEditableStyle()) {
+ auto* body = node->document().bodyOrFrameset();
+ if (body && body->hasEditableStyle())
+ return next.isNotNull() ? next : prev;
+ }
Node* editingRoot = editableRootForPosition(position);
// If the html element is editable, descending into its body will look like a descent
// from non-editable to editable content since rootEditableElement() always stops at the body.
- if ((editingRoot && editingRoot->hasTagName(htmlTag)) || position.deprecatedNode()->isDocumentNode())
+ if ((editingRoot && editingRoot->hasTagName(htmlTag)) || (node && (node->isDocumentNode() || node->isShadowRoot())))
return next.isNotNull() ? next : prev;
bool prevIsInSameEditableElement = prevNode && editableRootForPosition(prev) == editingRoot;
@@ -590,15 +634,14 @@ UChar32 VisiblePosition::characterAfter() const
return 0;
UChar32 ch;
- const UChar* characters = textNode->data().deprecatedCharacters();
- U16_NEXT(characters, offset, length, ch);
+ U16_NEXT(textNode->data(), offset, length, ch);
return ch;
}
LayoutRect VisiblePosition::localCaretRect(RenderObject*& renderer) const
{
if (m_deepPosition.isNull()) {
- renderer = 0;
+ renderer = nullptr;
return IntRect();
}
Node* node = m_deepPosition.anchorNode();
@@ -617,14 +660,11 @@ LayoutRect VisiblePosition::localCaretRect(RenderObject*& renderer) const
return renderer->localCaretRect(inlineBox, caretOffset);
}
-IntRect VisiblePosition::absoluteCaretBounds() const
+IntRect VisiblePosition::absoluteCaretBounds(bool* insideFixed) const
{
- RenderObject* renderer;
- LayoutRect localRect = localCaretRect(renderer);
- if (localRect.isEmpty() || !renderer)
- return IntRect();
-
- return renderer->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
+ RenderBlock* renderer = nullptr;
+ LayoutRect localRect = localCaretRectInRendererForCaretPainting(*this, renderer);
+ return absoluteBoundsForLocalCaretRect(renderer, localRect, insideFixed);
}
int VisiblePosition::lineDirectionPointForBlockDirectionNavigation() const
@@ -644,7 +684,7 @@ int VisiblePosition::lineDirectionPointForBlockDirectionNavigation() const
return containingBlock->isHorizontalWritingMode() ? caretPoint.x() : caretPoint.y();
}
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void VisiblePosition::debugPosition(const char* msg) const
{
@@ -668,15 +708,15 @@ void VisiblePosition::showTreeForThis() const
#endif
-PassRefPtr<Range> makeRange(const VisiblePosition &start, const VisiblePosition &end)
+RefPtr<Range> makeRange(const VisiblePosition& start, const VisiblePosition& end)
{
if (start.isNull() || end.isNull())
- return 0;
+ return nullptr;
Position s = start.deepEquivalent().parentAnchoredEquivalent();
Position e = end.deepEquivalent().parentAnchoredEquivalent();
if (s.isNull() || e.isNull())
- return 0;
+ return nullptr;
return Range::create(s.containerNode()->document(), s.containerNode(), s.offsetInContainerNode(), e.containerNode(), e.offsetInContainerNode());
}
@@ -691,31 +731,35 @@ VisiblePosition endVisiblePosition(const Range *r, EAffinity affinity)
return VisiblePosition(r->endPosition(), affinity);
}
-bool setStart(Range *r, const VisiblePosition &visiblePosition)
+bool setStart(Range* range, const VisiblePosition& visiblePosition)
{
- if (!r)
+ if (!range)
return false;
+
Position p = visiblePosition.deepEquivalent().parentAnchoredEquivalent();
- int code = 0;
- r->setStart(p.containerNode(), p.offsetInContainerNode(), code);
- return code == 0;
+ if (!p.containerNode())
+ return false;
+
+ return !range->setStart(*p.containerNode(), p.offsetInContainerNode()).hasException();
}
-bool setEnd(Range *r, const VisiblePosition &visiblePosition)
+bool setEnd(Range* range, const VisiblePosition& visiblePosition)
{
- if (!r)
+ if (!range)
return false;
+
Position p = visiblePosition.deepEquivalent().parentAnchoredEquivalent();
- int code = 0;
- r->setEnd(p.containerNode(), p.offsetInContainerNode(), code);
- return code == 0;
+ if (!p.containerNode())
+ return false;
+
+ return !range->setEnd(*p.containerNode(), p.offsetInContainerNode()).hasException();
}
// FIXME: Maybe this should be deprecated too, like the underlying function?
Element* enclosingBlockFlowElement(const VisiblePosition& visiblePosition)
{
if (visiblePosition.isNull())
- return NULL;
+ return nullptr;
return deprecatedEnclosingBlockFlowElement(visiblePosition.deepEquivalent().deprecatedNode());
}
@@ -744,9 +788,38 @@ bool isLastVisiblePositionInNode(const VisiblePosition &visiblePosition, const N
return next.isNull() || !next.deepEquivalent().deprecatedNode()->isDescendantOf(node);
}
+bool VisiblePosition::equals(const VisiblePosition& other) const
+{
+ return m_affinity == other.m_affinity && m_deepPosition.equals(other.m_deepPosition);
+}
+
+TextStream& operator<<(TextStream& stream, EAffinity affinity)
+{
+ switch (affinity) {
+ case UPSTREAM:
+ stream << "upstream";
+ break;
+ case DOWNSTREAM:
+ stream << "downstream";
+ break;
+ }
+ return stream;
+}
+
+TextStream& operator<<(TextStream& stream, const VisiblePosition& visiblePosition)
+{
+ TextStream::GroupScope scope(stream);
+ stream << "VisiblePosition " << &visiblePosition;
+
+ stream.dumpProperty("position", visiblePosition.deepEquivalent());
+ stream.dumpProperty("affinity", visiblePosition.affinity());
+
+ return stream;
+}
+
} // namespace WebCore
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void showTree(const WebCore::VisiblePosition* vpos)
{
diff --git a/Source/WebCore/editing/VisiblePosition.h b/Source/WebCore/editing/VisiblePosition.h
index 0ce78f444..3b9273538 100644
--- a/Source/WebCore/editing/VisiblePosition.h
+++ b/Source/WebCore/editing/VisiblePosition.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,12 +23,11 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef VisiblePosition_h
-#define VisiblePosition_h
+#pragma once
#include "EditingBoundary.h"
#include "Position.h"
-#include "TextDirection.h"
+#include "TextFlags.h"
namespace WebCore {
@@ -47,13 +46,14 @@ namespace WebCore {
class InlineBox;
class Node;
+class TextStream;
class VisiblePosition {
public:
// NOTE: UPSTREAM affinity will be used only if pos is at end of a wrapped line,
// otherwise it will be converted to DOWNSTREAM
VisiblePosition() : m_affinity(VP_DEFAULT_AFFINITY) { }
- VisiblePosition(const Position&, EAffinity = VP_DEFAULT_AFFINITY);
+ WEBCORE_EXPORT VisiblePosition(const Position&, EAffinity = VP_DEFAULT_AFFINITY);
void clear() { m_deepPosition.clear(); }
@@ -68,15 +68,15 @@ public:
// FIXME: Change the following functions' parameter from a boolean to StayInEditableContent.
// next() and previous() will increment/decrement by a character cluster.
- VisiblePosition next(EditingBoundaryCrossingRule = CanCrossEditingBoundary) const;
- VisiblePosition previous(EditingBoundaryCrossingRule = CanCrossEditingBoundary) const;
- VisiblePosition honorEditingBoundaryAtOrBefore(const VisiblePosition&) const;
- VisiblePosition honorEditingBoundaryAtOrAfter(const VisiblePosition&) const;
+ WEBCORE_EXPORT VisiblePosition next(EditingBoundaryCrossingRule = CanCrossEditingBoundary, bool* reachedBoundary = nullptr) const;
+ WEBCORE_EXPORT VisiblePosition previous(EditingBoundaryCrossingRule = CanCrossEditingBoundary, bool* reachedBoundary = nullptr) const;
+ VisiblePosition honorEditingBoundaryAtOrBefore(const VisiblePosition&, bool* reachedBoundary = nullptr) const;
+ VisiblePosition honorEditingBoundaryAtOrAfter(const VisiblePosition&, bool* reachedBoundary = nullptr) const;
- VisiblePosition left(bool stayInEditableContent = false) const;
- VisiblePosition right(bool stayInEditableContent = false) const;
+ WEBCORE_EXPORT VisiblePosition left(bool stayInEditableContent = false, bool* reachedBoundary = nullptr) const;
+ WEBCORE_EXPORT VisiblePosition right(bool stayInEditableContent = false, bool* reachedBoundary = nullptr) const;
- UChar32 characterAfter() const;
+ WEBCORE_EXPORT UChar32 characterAfter() const;
UChar32 characterBefore() const { return previous().characterAfter(); }
// FIXME: This does not handle [table, 0] correctly.
@@ -93,14 +93,18 @@ public:
}
// Rect is local to the returned renderer
- LayoutRect localCaretRect(RenderObject*&) const;
+ WEBCORE_EXPORT LayoutRect localCaretRect(RenderObject*&) const;
// Bounds of (possibly transformed) caret in absolute coords
- IntRect absoluteCaretBounds() const;
+ WEBCORE_EXPORT IntRect absoluteCaretBounds(bool* insideFixed = nullptr) const;
// Abs x/y position of the caret ignoring transforms.
// FIXME: navigation with transforms should be smarter.
- int lineDirectionPointForBlockDirectionNavigation() const;
-
-#ifndef NDEBUG
+ WEBCORE_EXPORT int lineDirectionPointForBlockDirectionNavigation() const;
+
+ // This is a tentative enhancement of operator== to account for affinity.
+ // FIXME: Combine this function with operator==
+ bool equals(const VisiblePosition&) const;
+
+#if ENABLE(TREE_DEBUGGING)
void debugPosition(const char* msg = "") const;
void formatForDebugger(char* buffer, unsigned length) const;
void showTreeForThis() const;
@@ -128,7 +132,6 @@ inline bool operator!=(const VisiblePosition& a, const VisiblePosition& b)
return !(a == b);
}
-#if PLATFORM(IOS)
inline bool operator<(const VisiblePosition& a, const VisiblePosition& b)
{
return a.deepEquivalent() < b.deepEquivalent();
@@ -148,25 +151,25 @@ inline bool operator>=(const VisiblePosition& a, const VisiblePosition& b)
{
return a.deepEquivalent() >= b.deepEquivalent();
}
-#endif
-PassRefPtr<Range> makeRange(const VisiblePosition&, const VisiblePosition&);
+WEBCORE_EXPORT RefPtr<Range> makeRange(const VisiblePosition&, const VisiblePosition&);
bool setStart(Range*, const VisiblePosition&);
bool setEnd(Range*, const VisiblePosition&);
VisiblePosition startVisiblePosition(const Range*, EAffinity);
VisiblePosition endVisiblePosition(const Range*, EAffinity);
-Element* enclosingBlockFlowElement(const VisiblePosition&);
+WEBCORE_EXPORT Element* enclosingBlockFlowElement(const VisiblePosition&);
bool isFirstVisiblePositionInNode(const VisiblePosition&, const Node*);
bool isLastVisiblePositionInNode(const VisiblePosition&, const Node*);
+TextStream& operator<<(TextStream&, EAffinity);
+TextStream& operator<<(TextStream&, const VisiblePosition&);
+
} // namespace WebCore
-#ifndef NDEBUG
-// Outside the WebCore namespace for ease of invocation from gdb.
+#if ENABLE(TREE_DEBUGGING)
+// Outside the WebCore namespace for ease of invocation from the debugger.
void showTree(const WebCore::VisiblePosition*);
void showTree(const WebCore::VisiblePosition&);
#endif
-
-#endif // VisiblePosition_h
diff --git a/Source/WebCore/editing/VisibleSelection.cpp b/Source/WebCore/editing/VisibleSelection.cpp
index 79f61199d..60f51b64c 100644
--- a/Source/WebCore/editing/VisibleSelection.cpp
+++ b/Source/WebCore/editing/VisibleSelection.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -28,6 +28,7 @@
#include "Document.h"
#include "Element.h"
+#include "HTMLInputElement.h"
#include "TextIterator.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
@@ -83,9 +84,9 @@ VisibleSelection::VisibleSelection(const VisiblePosition& base, const VisiblePos
validate();
}
-VisibleSelection::VisibleSelection(const Range* range, EAffinity affinity, bool isDirectional)
- : m_base(range->startPosition())
- , m_extent(range->endPosition())
+VisibleSelection::VisibleSelection(const Range& range, EAffinity affinity, bool isDirectional)
+ : m_base(range.startPosition())
+ , m_extent(range.endPosition())
, m_affinity(affinity)
, m_isDirectional(isDirectional)
{
@@ -94,7 +95,7 @@ VisibleSelection::VisibleSelection(const Range* range, EAffinity affinity, bool
VisibleSelection VisibleSelection::selectionFromContentsOfNode(Node* node)
{
- ASSERT(!editingIgnoresContent(node));
+ ASSERT(!editingIgnoresContent(*node));
return VisibleSelection(firstPositionInNode(node), lastPositionInNode(node), DOWNSTREAM);
}
@@ -122,19 +123,21 @@ void VisibleSelection::setExtent(const VisiblePosition& visiblePosition)
validate();
}
-PassRefPtr<Range> VisibleSelection::firstRange() const
+RefPtr<Range> VisibleSelection::firstRange() const
{
- if (isNone())
- return 0;
+ if (isNoneOrOrphaned())
+ return nullptr;
Position start = m_start.parentAnchoredEquivalent();
Position end = m_end.parentAnchoredEquivalent();
+ if (start.isNull() || start.isOrphan() || end.isNull() || end.isOrphan())
+ return nullptr;
return Range::create(start.anchorNode()->document(), start, end);
}
-PassRefPtr<Range> VisibleSelection::toNormalizedRange() const
+RefPtr<Range> VisibleSelection::toNormalizedRange() const
{
- if (isNone())
- return 0;
+ if (isNoneOrOrphaned())
+ return nullptr;
// Make sure we have an updated layout since this function is called
// in the course of running edit commands which modify the DOM.
@@ -143,8 +146,8 @@ PassRefPtr<Range> VisibleSelection::toNormalizedRange() const
m_start.anchorNode()->document().updateLayout();
// Check again, because updating layout can clear the selection.
- if (isNone())
- return 0;
+ if (isNoneOrOrphaned())
+ return nullptr;
Position s, e;
if (isCaret()) {
@@ -180,7 +183,7 @@ PassRefPtr<Range> VisibleSelection::toNormalizedRange() const
}
if (!s.containerNode() || !e.containerNode())
- return 0;
+ return nullptr;
// VisibleSelections are supposed to always be valid. This constructor will ASSERT
// if a valid range could not be created, which is fine for this callsite.
@@ -196,30 +199,26 @@ bool VisibleSelection::expandUsingGranularity(TextGranularity granularity)
return true;
}
-static PassRefPtr<Range> makeSearchRange(const Position& pos)
+static RefPtr<Range> makeSearchRange(const Position& position)
{
- Node* n = pos.deprecatedNode();
- if (!n)
- return 0;
- Node* de = n->document().documentElement();
- if (!de)
- return 0;
- Element* boundary = deprecatedEnclosingBlockFlowElement(n);
+ auto* node = position.deprecatedNode();
+ if (!node)
+ return nullptr;
+ auto* boundary = deprecatedEnclosingBlockFlowElement(node);
if (!boundary)
- return 0;
+ return nullptr;
- RefPtr<Range> searchRange(Range::create(n->document()));
- ExceptionCode ec = 0;
+ auto searchRange = Range::create(node->document());
- Position start(pos.parentAnchoredEquivalent());
- searchRange->selectNodeContents(boundary, ec);
- searchRange->setStart(start.containerNode(), start.offsetInContainerNode(), ec);
+ auto result = searchRange->selectNodeContents(*boundary);
+ if (result.hasException())
+ return nullptr;
+ Position start { position.parentAnchoredEquivalent() };
+ result = searchRange->setStart(*start.containerNode(), start.offsetInContainerNode());
+ if (result.hasException())
+ return nullptr;
- ASSERT(!ec);
- if (ec)
- return 0;
-
- return searchRange.release();
+ return WTFMove(searchRange);
}
bool VisibleSelection::isAll(EditingBoundaryCrossingRule rule) const
@@ -233,10 +232,10 @@ void VisibleSelection::appendTrailingWhitespace()
if (!searchRange)
return;
- CharacterIterator charIt(searchRange.get(), TextIteratorEmitsCharactersBetweenAllVisiblePositions);
+ CharacterIterator charIt(*searchRange, TextIteratorEmitsCharactersBetweenAllVisiblePositions);
- for (; charIt.length(); charIt.advance(1)) {
- UChar c = charIt.characters()[0];
+ for (; !charIt.atEnd() && charIt.text().length(); charIt.advance(1)) {
+ UChar c = charIt.text()[0];
if ((!isSpaceOrNewline(c) && c != noBreakSpace) || c == '\n')
break;
m_end = charIt.range()->endPosition();
@@ -306,7 +305,7 @@ void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity(Text
// the next one) to match TextEdit.
end = wordEnd.next();
- if (Node* table = isFirstPositionAfterTable(end)) {
+ if (auto* table = isFirstPositionAfterTable(end)) {
// The paragraph break after the last paragraph in the last cell of a block table ends
// at the start of the paragraph after the table.
if (isBlock(table))
@@ -321,14 +320,12 @@ void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity(Text
}
m_end = end.deepEquivalent();
-#if PLATFORM(IOS)
// End must not be before start.
if (m_start.deprecatedNode() == m_end.deprecatedNode() && m_start.deprecatedEditingOffset() > m_end.deprecatedEditingOffset()) {
Position swap(m_start);
m_start = m_end;
m_end = swap;
}
-#endif
break;
}
case SentenceGranularity: {
@@ -392,11 +389,9 @@ void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity(Text
m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent();
m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent();
break;
-#if PLATFORM(IOS)
case DocumentGranularity:
ASSERT_NOT_REACHED();
break;
-#endif
}
// Make sure we do not have a dangling start or end.
@@ -478,13 +473,13 @@ static Position adjustPositionForEnd(const Position& currentPosition, Node* star
ASSERT(&currentPosition.containerNode()->treeScope() != &treeScope);
- if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.containerNode())) {
+ if (Node* ancestor = treeScope.ancestorNodeInThisScope(currentPosition.containerNode())) {
if (ancestor->contains(startContainerNode))
return positionAfterNode(ancestor);
return positionBeforeNode(ancestor);
}
- if (Node* lastChild = treeScope.rootNode()->lastChild())
+ if (Node* lastChild = treeScope.rootNode().lastChild())
return positionAfterNode(lastChild);
return Position();
@@ -496,13 +491,13 @@ static Position adjustPositionForStart(const Position& currentPosition, Node* en
ASSERT(&currentPosition.containerNode()->treeScope() != &treeScope);
- if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.containerNode())) {
+ if (Node* ancestor = treeScope.ancestorNodeInThisScope(currentPosition.containerNode())) {
if (ancestor->contains(endContainerNode))
return positionBeforeNode(ancestor);
return positionAfterNode(ancestor);
}
- if (Node* firstChild = treeScope.rootNode()->firstChild())
+ if (Node* firstChild = treeScope.rootNode().firstChild())
return positionBeforeNode(firstChild);
return Position();
@@ -532,18 +527,16 @@ void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries()
if (m_base.isNull() || m_start.isNull() || m_end.isNull())
return;
-#if PLATFORM(IOS)
- // Early return in the caret case (the state hasn't actually been set yet, so we can't use isCaret()) to avoid the
+ // Early return in the caret case (the state hasn't actually been set yet, so we can't use isCaret()) to avoid the
// expense of computing highestEditableRoot.
if (m_base == m_start && m_base == m_end)
return;
-#endif
- Node* baseRoot = highestEditableRoot(m_base);
- Node* startRoot = highestEditableRoot(m_start);
- Node* endRoot = highestEditableRoot(m_end);
+ auto* baseRoot = highestEditableRoot(m_base);
+ auto* startRoot = highestEditableRoot(m_start);
+ auto* endRoot = highestEditableRoot(m_end);
- Node* baseEditableAncestor = lowestEditableAncestor(m_base.containerNode());
+ auto* baseEditableAncestor = lowestEditableAncestor(m_base.containerNode());
// The base, start and end are all in the same region. No adjustment necessary.
if (baseRoot == startRoot && baseRoot == endRoot)
@@ -578,7 +571,7 @@ void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries()
// The selection ends in editable content or non-editable content inside a different editable ancestor,
// move backward until non-editable content inside the same lowest editable ancestor is reached.
- Node* endEditableAncestor = lowestEditableAncestor(m_end.containerNode());
+ auto* endEditableAncestor = lowestEditableAncestor(m_end.containerNode());
if (endRoot || endEditableAncestor != baseEditableAncestor) {
Position p = previousVisuallyDistinctCandidate(m_end);
@@ -608,7 +601,7 @@ void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries()
// The selection starts in editable content or non-editable content inside a different editable ancestor,
// move forward until non-editable content inside the same lowest editable ancestor is reached.
- Node* startEditableAncestor = lowestEditableAncestor(m_start.containerNode());
+ auto* startEditableAncestor = lowestEditableAncestor(m_start.containerNode());
if (startRoot || startEditableAncestor != baseEditableAncestor) {
Position p = nextVisuallyDistinctCandidate(m_start);
Node* shadowAncestor = startRoot ? startRoot->shadowHost() : 0;
@@ -648,7 +641,9 @@ bool VisibleSelection::isContentEditable() const
bool VisibleSelection::hasEditableStyle() const
{
- return isEditablePosition(start(), ContentIsEditable, DoNotUpdateStyle);
+ if (Node* containerNode = start().containerNode())
+ return containerNode->hasEditableStyle();
+ return false;
}
bool VisibleSelection::isContentRichlyEditable() const
@@ -663,10 +658,16 @@ Element* VisibleSelection::rootEditableElement() const
Node* VisibleSelection::nonBoundaryShadowTreeRootNode() const
{
- return start().deprecatedNode() ? start().deprecatedNode()->nonBoundaryShadowTreeRootNode() : 0;
+ return start().deprecatedNode() ? start().deprecatedNode()->nonBoundaryShadowTreeRootNode() : nullptr;
+}
+
+bool VisibleSelection::isInPasswordField() const
+{
+ HTMLTextFormControlElement* textControl = enclosingTextFormControl(start());
+ return is<HTMLInputElement>(textControl) && downcast<HTMLInputElement>(*textControl).isPasswordField();
}
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void VisibleSelection::debugPosition() const
{
@@ -723,7 +724,7 @@ void VisibleSelection::showTreeForThis() const
} // namespace WebCore
-#ifndef NDEBUG
+#if ENABLE(TREE_DEBUGGING)
void showTree(const WebCore::VisibleSelection& sel)
{
diff --git a/Source/WebCore/editing/VisibleSelection.h b/Source/WebCore/editing/VisibleSelection.h
index 345f82deb..045f572d0 100644
--- a/Source/WebCore/editing/VisibleSelection.h
+++ b/Source/WebCore/editing/VisibleSelection.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004, 2015 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef VisibleSelection_h
-#define VisibleSelection_h
+#pragma once
#include "TextGranularity.h"
#include "VisiblePosition.h"
@@ -40,17 +39,17 @@ class VisibleSelection {
public:
enum SelectionType { NoSelection, CaretSelection, RangeSelection };
- VisibleSelection();
+ WEBCORE_EXPORT VisibleSelection();
VisibleSelection(const Position&, EAffinity, bool isDirectional = false);
VisibleSelection(const Position&, const Position&, EAffinity = SEL_DEFAULT_AFFINITY, bool isDirectional = false);
- VisibleSelection(const Range*, EAffinity = SEL_DEFAULT_AFFINITY, bool isDirectional = false);
+ WEBCORE_EXPORT VisibleSelection(const Range&, EAffinity = SEL_DEFAULT_AFFINITY, bool isDirectional = false);
- VisibleSelection(const VisiblePosition&, bool isDirectional = false);
- VisibleSelection(const VisiblePosition&, const VisiblePosition&, bool isDirectional = false);
+ WEBCORE_EXPORT VisibleSelection(const VisiblePosition&, bool isDirectional = false);
+ WEBCORE_EXPORT VisibleSelection(const VisiblePosition&, const VisiblePosition&, bool isDirectional = false);
- static VisibleSelection selectionFromContentsOfNode(Node*);
+ WEBCORE_EXPORT static VisibleSelection selectionFromContentsOfNode(Node*);
SelectionType selectionType() const { return m_selectionType; }
@@ -77,35 +76,37 @@ public:
bool isRange() const { return selectionType() == RangeSelection; }
bool isCaretOrRange() const { return selectionType() != NoSelection; }
bool isNonOrphanedRange() const { return isRange() && !start().isOrphan() && !end().isOrphan(); }
- bool isNonOrphanedCaretOrRange() const { return isCaretOrRange() && !start().isOrphan() && !end().isOrphan(); }
+ bool isNoneOrOrphaned() const { return isNone() || start().isOrphan() || end().isOrphan(); }
bool isBaseFirst() const { return m_baseIsFirst; }
bool isDirectional() const { return m_isDirectional; }
void setIsDirectional(bool isDirectional) { m_isDirectional = isDirectional; }
- bool isAll(EditingBoundaryCrossingRule) const;
+ WEBCORE_EXPORT bool isAll(EditingBoundaryCrossingRule) const;
void appendTrailingWhitespace();
- bool expandUsingGranularity(TextGranularity granularity);
+ WEBCORE_EXPORT bool expandUsingGranularity(TextGranularity granularity);
// We don't yet support multi-range selections, so we only ever have one range to return.
- PassRefPtr<Range> firstRange() const;
+ WEBCORE_EXPORT RefPtr<Range> firstRange() const;
// FIXME: Most callers probably don't want this function, but are using it
// for historical reasons. toNormalizedRange contracts the range around
// text, and moves the caret upstream before returning the range.
- PassRefPtr<Range> toNormalizedRange() const;
+ WEBCORE_EXPORT RefPtr<Range> toNormalizedRange() const;
- Element* rootEditableElement() const;
- bool isContentEditable() const;
+ WEBCORE_EXPORT Element* rootEditableElement() const;
+ WEBCORE_EXPORT bool isContentEditable() const;
bool hasEditableStyle() const;
- bool isContentRichlyEditable() const;
+ WEBCORE_EXPORT bool isContentRichlyEditable() const;
// Returns a shadow tree node for legacy shadow trees, a child of the
// ShadowRoot node for new shadow trees, or 0 for non-shadow trees.
Node* nonBoundaryShadowTreeRootNode() const;
-#ifndef NDEBUG
+ WEBCORE_EXPORT bool isInPasswordField() const;
+
+#if ENABLE(TREE_DEBUGGING)
void debugPosition() const;
void formatForDebugger(char* buffer, unsigned length) const;
void showTreeForThis() const;
@@ -154,10 +155,8 @@ inline bool operator!=(const VisibleSelection& a, const VisibleSelection& b)
} // namespace WebCore
-#ifndef NDEBUG
-// Outside the WebCore namespace for ease of invocation from gdb.
+#if ENABLE(TREE_DEBUGGING)
+// Outside the WebCore namespace for ease of invocation from the debugger.
void showTree(const WebCore::VisibleSelection&);
void showTree(const WebCore::VisibleSelection*);
#endif
-
-#endif // VisibleSelection_h
diff --git a/Source/WebCore/editing/VisibleUnits.cpp b/Source/WebCore/editing/VisibleUnits.cpp
index b082b3dac..4363c1be7 100644
--- a/Source/WebCore/editing/VisibleUnits.cpp
+++ b/Source/WebCore/editing/VisibleUnits.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -27,7 +27,8 @@
#include "VisibleUnits.h"
#include "Document.h"
-#include "Element.h"
+#include "HTMLBRElement.h"
+#include "HTMLElement.h"
#include "HTMLNames.h"
#include "InlineTextBox.h"
#include "NodeTraversal.h"
@@ -36,9 +37,10 @@
#include "RenderedPosition.h"
#include "Text.h"
#include "TextBoundaries.h"
-#include "TextBreakIterator.h"
#include "TextIterator.h"
#include "VisibleSelection.h"
+#include <unicode/ubrk.h>
+#include <wtf/text/TextBreakIterator.h>
#include "htmlediting.h"
namespace WebCore {
@@ -48,35 +50,35 @@ using namespace WTF::Unicode;
static Node* previousLeafWithSameEditability(Node* node, EditableType editableType)
{
- bool editable = node->hasEditableStyle(editableType);
+ bool editable = hasEditableStyle(*node, editableType);
node = previousLeafNode(node);
while (node) {
- if (editable == node->hasEditableStyle(editableType))
+ if (editable == hasEditableStyle(*node, editableType))
return node;
node = previousLeafNode(node);
}
- return 0;
+ return nullptr;
}
-static Node* nextLeafWithSameEditability(Node* node, EditableType editableType = ContentIsEditable)
+static Node* nextLeafWithSameEditability(Node* node, EditableType editableType)
{
if (!node)
- return 0;
+ return nullptr;
- bool editable = node->hasEditableStyle(editableType);
+ bool editable = hasEditableStyle(*node, editableType);
node = nextLeafNode(node);
while (node) {
- if (editable == node->hasEditableStyle(editableType))
+ if (editable == hasEditableStyle(*node, editableType))
return node;
node = nextLeafNode(node);
}
- return 0;
+ return nullptr;
}
// FIXME: consolidate with code in previousLinePosition.
static Position previousRootInlineBoxCandidatePosition(Node* node, const VisiblePosition& visiblePosition, EditableType editableType)
{
- Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), editableType);
+ auto* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), editableType);
Node* previousNode = previousLeafWithSameEditability(node, editableType);
while (previousNode && (!previousNode->renderer() || inSameLine(firstPositionInOrBeforeNode(previousNode), visiblePosition)))
@@ -87,7 +89,7 @@ static Position previousRootInlineBoxCandidatePosition(Node* node, const Visible
break;
Position pos = previousNode->hasTagName(brTag) ? positionBeforeNode(previousNode) :
- createLegacyEditingPosition(previousNode, caretMaxOffset(previousNode));
+ createLegacyEditingPosition(previousNode, caretMaxOffset(*previousNode));
if (pos.isCandidate())
return pos;
@@ -99,7 +101,7 @@ static Position previousRootInlineBoxCandidatePosition(Node* node, const Visible
static Position nextRootInlineBoxCandidatePosition(Node* node, const VisiblePosition& visiblePosition, EditableType editableType)
{
- Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), editableType);
+ auto* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent(), editableType);
Node* nextNode = nextLeafWithSameEditability(node, editableType);
while (nextNode && (!nextNode->renderer() || inSameLine(firstPositionInOrBeforeNode(nextNode), visiblePosition)))
nextNode = nextLeafWithSameEditability(nextNode, ContentIsEditable);
@@ -109,7 +111,7 @@ static Position nextRootInlineBoxCandidatePosition(Node* node, const VisiblePosi
break;
Position pos;
- pos = createLegacyEditingPosition(nextNode, caretMinOffset(nextNode));
+ pos = createLegacyEditingPosition(nextNode, caretMinOffset(*nextNode));
if (pos.isCandidate())
return pos;
@@ -133,16 +135,18 @@ private:
const Vector<InlineBox*>& collectBoxes(const RootInlineBox*);
int boxIndexInLeaves(const InlineTextBox*) const;
- const RootInlineBox* m_rootInlineBox;
+ const RootInlineBox* m_rootInlineBox { nullptr };
Vector<InlineBox*> m_leafBoxes;
};
-CachedLogicallyOrderedLeafBoxes::CachedLogicallyOrderedLeafBoxes() : m_rootInlineBox(0) { };
+CachedLogicallyOrderedLeafBoxes::CachedLogicallyOrderedLeafBoxes()
+{
+}
const InlineBox* CachedLogicallyOrderedLeafBoxes::previousTextOrLineBreakBox(const RootInlineBox* root, const InlineTextBox* box)
{
if (!root)
- return 0;
+ return nullptr;
collectBoxes(root);
@@ -157,13 +161,13 @@ const InlineBox* CachedLogicallyOrderedLeafBoxes::previousTextOrLineBreakBox(con
return box;
}
- return 0;
+ return nullptr;
}
const InlineBox* CachedLogicallyOrderedLeafBoxes::nextTextOrLineBreakBox(const RootInlineBox* root, const InlineTextBox* box)
{
if (!root)
- return 0;
+ return nullptr;
collectBoxes(root);
@@ -179,7 +183,7 @@ const InlineBox* CachedLogicallyOrderedLeafBoxes::nextTextOrLineBreakBox(const R
return box;
}
- return 0;
+ return nullptr;
}
const Vector<InlineBox*>& CachedLogicallyOrderedLeafBoxes::collectBoxes(const RootInlineBox* root)
@@ -282,7 +286,7 @@ static const InlineBox* logicallyNextBox(const VisiblePosition& visiblePosition,
return 0;
}
-static TextBreakIterator* wordBreakIteratorForMinOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
+static UBreakIterator* wordBreakIteratorForMinOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
int& previousBoxLength, bool& previousBoxInDifferentBlock, Vector<UChar, 1024>& string, CachedLogicallyOrderedLeafBoxes& leafBoxes)
{
previousBoxInDifferentBlock = false;
@@ -290,21 +294,19 @@ static TextBreakIterator* wordBreakIteratorForMinOffsetBoundary(const VisiblePos
// FIXME: Handle the case when we don't have an inline text box.
const InlineBox* previousBox = logicallyPreviousBox(visiblePosition, textBox, previousBoxInDifferentBlock, leafBoxes);
- int len = 0;
string.clear();
- if (previousBox && previousBox->isInlineTextBox()) {
- const InlineTextBox* previousTextBox = toInlineTextBox(previousBox);
- previousBoxLength = previousTextBox->len();
- string.append(previousTextBox->renderer().text()->deprecatedCharacters() + previousTextBox->start(), previousBoxLength);
- len += previousBoxLength;
+
+ if (is<InlineTextBox>(previousBox)) {
+ const auto& previousTextBox = downcast<InlineTextBox>(*previousBox);
+ previousBoxLength = previousTextBox.len();
+ append(string, StringView(previousTextBox.renderer().text()).substring(previousTextBox.start(), previousBoxLength));
}
- string.append(textBox->renderer().text()->deprecatedCharacters() + textBox->start(), textBox->len());
- len += textBox->len();
+ append(string, StringView(textBox->renderer().text()).substring(textBox->start(), textBox->len()));
- return wordBreakIterator(StringView(string.data(), len));
+ return wordBreakIterator(StringView(string.data(), string.size()));
}
-static TextBreakIterator* wordBreakIteratorForMaxOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
+static UBreakIterator* wordBreakIteratorForMaxOffsetBoundary(const VisiblePosition& visiblePosition, const InlineTextBox* textBox,
bool& nextBoxInDifferentBlock, Vector<UChar, 1024>& string, CachedLogicallyOrderedLeafBoxes& leafBoxes)
{
nextBoxInDifferentBlock = false;
@@ -312,33 +314,30 @@ static TextBreakIterator* wordBreakIteratorForMaxOffsetBoundary(const VisiblePos
// FIXME: Handle the case when we don't have an inline text box.
const InlineBox* nextBox = logicallyNextBox(visiblePosition, textBox, nextBoxInDifferentBlock, leafBoxes);
- int len = 0;
string.clear();
- string.append(textBox->renderer().text()->deprecatedCharacters() + textBox->start(), textBox->len());
- len += textBox->len();
- if (nextBox && nextBox->isInlineTextBox()) {
- const InlineTextBox* nextTextBox = toInlineTextBox(nextBox);
- string.append(nextTextBox->renderer().text()->deprecatedCharacters() + nextTextBox->start(), nextTextBox->len());
- len += nextTextBox->len();
+ append(string, StringView(textBox->renderer().text()).substring(textBox->start(), textBox->len()));
+ if (is<InlineTextBox>(nextBox)) {
+ const auto& nextTextBox = downcast<InlineTextBox>(*nextBox);
+ append(string, StringView(nextTextBox.renderer().text()).substring(nextTextBox.start(), nextTextBox.len()));
}
- return wordBreakIterator(StringView(string.data(), len));
+ return wordBreakIterator(StringView(string.data(), string.size()));
}
-static bool isLogicalStartOfWord(TextBreakIterator* iter, int position, bool hardLineBreak)
+static bool isLogicalStartOfWord(UBreakIterator* iter, int position, bool hardLineBreak)
{
- bool boundary = hardLineBreak ? true : isTextBreak(iter, position);
+ bool boundary = hardLineBreak ? true : ubrk_isBoundary(iter, position);
if (!boundary)
return false;
- textBreakFollowing(iter, position);
+ ubrk_following(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)
+static bool islogicalEndOfWord(UBreakIterator* iter, int position, bool hardLineBreak)
{
- bool boundary = isTextBreak(iter, position);
+ bool boundary = ubrk_isBoundary(iter, position);
return (hardLineBreak || boundary) && isWordTextBreak(iter);
}
@@ -351,9 +350,10 @@ static VisiblePosition visualWordPosition(const VisiblePosition& visiblePosition
return VisiblePosition();
TextDirection blockDirection = directionOfEnclosingBlock(visiblePosition.deepEquivalent());
- InlineBox* previouslyVisitedBox = 0;
+ InlineBox* previouslyVisitedBox = nullptr;
VisiblePosition current = visiblePosition;
- TextBreakIterator* iter = 0;
+ std::optional<VisiblePosition> previousPosition;
+ UBreakIterator* iter = nullptr;
CachedLogicallyOrderedLeafBoxes leafBoxes;
Vector<UChar, 1024> string;
@@ -362,6 +362,9 @@ static VisiblePosition visualWordPosition(const VisiblePosition& visiblePosition
VisiblePosition adjacentCharacterPosition = direction == MoveRight ? current.right(true) : current.left(true);
if (adjacentCharacterPosition == current || adjacentCharacterPosition.isNull())
return VisiblePosition();
+ // FIXME: This is a workaround for webkit.org/b/167138.
+ if (previousPosition && adjacentCharacterPosition == previousPosition.value())
+ return VisiblePosition();
InlineBox* box;
int offsetInBox;
@@ -369,47 +372,48 @@ static VisiblePosition visualWordPosition(const VisiblePosition& visiblePosition
if (!box)
break;
- if (!box->isInlineTextBox()) {
+ if (!is<InlineTextBox>(*box)) {
current = adjacentCharacterPosition;
continue;
}
- InlineTextBox* textBox = toInlineTextBox(box);
+ InlineTextBox& textBox = downcast<InlineTextBox>(*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);
+ iter = wordBreakIteratorForMinOffsetBoundary(visiblePosition, &textBox, previousBoxLength, previousBoxInDifferentBlock, string, leafBoxes);
else if (offsetInBox == box->caretMaxOffset())
- iter = wordBreakIteratorForMaxOffsetBoundary(visiblePosition, textBox, nextBoxInDifferentBlock, string, leafBoxes);
+ iter = wordBreakIteratorForMaxOffsetBoundary(visiblePosition, &textBox, nextBoxInDifferentBlock, string, leafBoxes);
else if (movingIntoNewBox) {
- iter = wordBreakIterator(StringView(textBox->renderer().text()).substring(textBox->start(), textBox->len()));
+ iter = wordBreakIterator(StringView(textBox.renderer().text()).substring(textBox.start(), textBox.len()));
previouslyVisitedBox = box;
}
if (!iter)
break;
- textBreakFirst(iter);
- int offsetInIterator = offsetInBox - textBox->start() + previousBoxLength;
+ ubrk_first(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;
+ 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;
+ bool logicalEndInRenderer = offsetInBox == static_cast<int>(textBox.start() + textBox.len()) && nextBoxInDifferentBlock;
isWordBreak = islogicalEndOfWord(iter, offsetInIterator, logicalEndInRenderer);
}
if (isWordBreak)
return adjacentCharacterPosition;
+ previousPosition = current;
current = adjacentCharacterPosition;
}
return VisiblePosition();
@@ -442,90 +446,177 @@ VisiblePosition rightWordPosition(const VisiblePosition& visiblePosition, bool s
}
-enum BoundarySearchContextAvailability { DontHaveMoreContext, MayHaveMoreContext };
+static void prepend(Vector<UChar, 1024>& buffer, StringView string)
+{
+ unsigned oldSize = buffer.size();
+ unsigned length = string.length();
+ buffer.grow(oldSize + length);
+ memmove(buffer.data() + length, buffer.data(), oldSize * sizeof(UChar));
+ for (unsigned i = 0; i < length; ++i)
+ buffer[i] = string[i];
+}
-typedef unsigned (*BoundarySearchFunction)(const UChar*, unsigned length, unsigned offset, BoundarySearchContextAvailability, bool& needMoreContext);
+static void prependRepeatedCharacter(Vector<UChar, 1024>& buffer, UChar character, unsigned count)
+{
+ unsigned oldSize = buffer.size();
+ buffer.grow(oldSize + count);
+ memmove(buffer.data() + count, buffer.data(), oldSize * sizeof(UChar));
+ for (unsigned i = 0; i < count; ++i)
+ buffer[i] = character;
+}
-static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
+static void appendRepeatedCharacter(Vector<UChar, 1024>& buffer, UChar character, unsigned count)
{
- Position pos = c.deepEquivalent();
- Node* boundary = pos.parentEditingBoundary();
- if (!boundary)
- return VisiblePosition();
+ unsigned oldSize = buffer.size();
+ buffer.grow(oldSize + count);
+ for (unsigned i = 0; i < count; ++i)
+ buffer[oldSize + i] = character;
+}
- Document& boundaryDocument = boundary->document();
- Position start = createLegacyEditingPosition(boundary, 0).parentAnchoredEquivalent();
- Position end = pos.parentAnchoredEquivalent();
- RefPtr<Range> searchRange = Range::create(boundaryDocument);
-
- Vector<UChar, 1024> string;
+unsigned suffixLengthForRange(const Range& forwardsScanRange, Vector<UChar, 1024>& string)
+{
unsigned suffixLength = 0;
-
- ExceptionCode ec = 0;
- if (requiresContextForWordBoundary(c.characterBefore())) {
- RefPtr<Range> forwardsScanRange(boundaryDocument.createRange());
- forwardsScanRange->setEndAfter(boundary, ec);
- forwardsScanRange->setStart(end.deprecatedNode(), end.deprecatedEditingOffset(), ec);
- TextIterator forwardsIterator(forwardsScanRange.get());
- while (!forwardsIterator.atEnd()) {
- const UChar* characters = forwardsIterator.characters();
- int length = forwardsIterator.length();
- int i = endOfFirstWordBoundaryContext(characters, length);
- string.append(characters, i);
- suffixLength += i;
- if (i < length)
- break;
- forwardsIterator.advance();
- }
+ TextIterator forwardsIterator(&forwardsScanRange);
+ while (!forwardsIterator.atEnd()) {
+ StringView text = forwardsIterator.text();
+ unsigned i = endOfFirstWordBoundaryContext(text);
+ append(string, text.substring(0, i));
+ suffixLength += i;
+ if (i < text.length())
+ break;
+ forwardsIterator.advance();
}
+ return suffixLength;
+}
- searchRange->setStart(start.deprecatedNode(), start.deprecatedEditingOffset(), ec);
- searchRange->setEnd(end.deprecatedNode(), end.deprecatedEditingOffset(), ec);
-
- ASSERT(!ec);
- if (ec)
- return VisiblePosition();
+unsigned prefixLengthForRange(const Range& backwardsScanRange, Vector<UChar, 1024>& string)
+{
+ unsigned prefixLength = 0;
+ SimplifiedBackwardsTextIterator backwardsIterator(backwardsScanRange);
+ while (!backwardsIterator.atEnd()) {
+ StringView text = backwardsIterator.text();
+ int i = startOfLastWordBoundaryContext(text);
+ prepend(string, text.substring(i));
+ prefixLength += text.length() - i;
+ if (i > 0)
+ break;
+ backwardsIterator.advance();
+ }
+ return prefixLength;
+}
- SimplifiedBackwardsTextIterator it(searchRange.get());
+unsigned backwardSearchForBoundaryWithTextIterator(SimplifiedBackwardsTextIterator& it, Vector<UChar, 1024>& string, unsigned suffixLength, BoundarySearchFunction searchFunction)
+{
unsigned next = 0;
bool needMoreContext = false;
while (!it.atEnd()) {
bool inTextSecurityMode = it.node() && it.node()->renderer() && it.node()->renderer()->style().textSecurity() != TSNONE;
// iterate to get chunks until the searchFunction returns a non-zero value.
- if (!inTextSecurityMode)
- string.insert(0, it.characters(), it.length());
+ if (!inTextSecurityMode)
+ prepend(string, it.text());
else {
// Treat bullets used in the text security mode as regular characters when looking for boundaries
- String iteratorString(it.characters(), it.length());
-#if PLATFORM(IOS)
- iteratorString = iteratorString.impl()->fill('x');
-#else
- iteratorString.fill('x');
-#endif
- string.insert(0, iteratorString.deprecatedCharacters(), iteratorString.length());
+ prependRepeatedCharacter(string, 'x', it.text().length());
+ }
+ if (string.size() > suffixLength) {
+ next = searchFunction(StringView(string.data(), string.size()), string.size() - suffixLength, MayHaveMoreContext, needMoreContext);
+ if (next > 1) // FIXME: This is a work around for https://webkit.org/b/115070. We need to provide more contexts in general case.
+ break;
}
- next = searchFunction(string.data(), string.size(), string.size() - suffixLength, MayHaveMoreContext, needMoreContext);
- if (next > 1) // FIXME: This is a work around for https://webkit.org/b/115070. We need to provide more contexts in general case.
- break;
it.advance();
}
- if (needMoreContext) {
+ if (needMoreContext && string.size() > suffixLength) {
// The last search returned the beginning of the buffer and asked for more context,
// but there is no earlier text. Force a search with what's available.
- next = searchFunction(string.data(), string.size(), string.size() - suffixLength, DontHaveMoreContext, needMoreContext);
+ next = searchFunction(StringView(string.data(), string.size()), string.size() - suffixLength, DontHaveMoreContext, needMoreContext);
ASSERT(!needMoreContext);
}
+
+ return next;
+}
+
+unsigned forwardSearchForBoundaryWithTextIterator(TextIterator& it, Vector<UChar, 1024>& string, unsigned prefixLength, BoundarySearchFunction searchFunction)
+{
+ unsigned next = 0;
+ bool needMoreContext = false;
+ while (!it.atEnd()) {
+ bool inTextSecurityMode = it.node() && it.node()->renderer() && it.node()->renderer()->style().textSecurity() != TSNONE;
+ // Keep asking the iterator for chunks until the search function
+ // returns an end value not equal to the length of the string passed to it.
+ if (!inTextSecurityMode)
+ append(string, it.text());
+ else {
+ // Treat bullets used in the text security mode as regular characters when looking for boundaries
+ appendRepeatedCharacter(string, 'x', it.text().length());
+ }
+ if (string.size() > prefixLength) {
+ next = searchFunction(StringView(string.data(), string.size()), prefixLength, MayHaveMoreContext, needMoreContext);
+ if (next != string.size())
+ break;
+ }
+ it.advance();
+ }
+ if (needMoreContext && string.size() > prefixLength) {
+ // The last search returned the end of the buffer and asked for more context,
+ // but there is no further text. Force a search with what's available.
+ next = searchFunction(StringView(string.data(), string.size()), prefixLength, DontHaveMoreContext, needMoreContext);
+ ASSERT(!needMoreContext);
+ }
+
+ return next;
+}
+
+static VisiblePosition previousBoundary(const VisiblePosition& c, BoundarySearchFunction searchFunction)
+{
+ Position pos = c.deepEquivalent();
+ Node* boundary = pos.parentEditingBoundary();
+ if (!boundary)
+ return VisiblePosition();
+
+ Document& boundaryDocument = boundary->document();
+ Position start = createLegacyEditingPosition(boundary, 0).parentAnchoredEquivalent();
+ Position end = pos.parentAnchoredEquivalent();
+
+ if (start.isNull() || end.isNull())
+ return VisiblePosition();
+
+ Ref<Range> searchRange = Range::create(boundaryDocument);
+
+ Vector<UChar, 1024> string;
+ unsigned suffixLength = 0;
+
+ if (requiresContextForWordBoundary(c.characterBefore())) {
+ auto forwardsScanRange = boundaryDocument.createRange();
+ auto result = forwardsScanRange->setEndAfter(*boundary);
+ if (result.hasException())
+ return { };
+ result = forwardsScanRange->setStart(*end.deprecatedNode(), end.deprecatedEditingOffset());
+ if (result.hasException())
+ return { };
+ suffixLength = suffixLengthForRange(forwardsScanRange, string);
+ }
+
+ auto result = searchRange->setStart(*start.deprecatedNode(), start.deprecatedEditingOffset());
+ if (result.hasException())
+ return { };
+ result = searchRange->setEnd(*end.deprecatedNode(), end.deprecatedEditingOffset());
+ if (result.hasException())
+ return { };
+
+ SimplifiedBackwardsTextIterator it(searchRange);
+ unsigned next = backwardSearchForBoundaryWithTextIterator(it, string, suffixLength, searchFunction);
if (!next)
- return VisiblePosition(it.atEnd() ? it.range()->startPosition() : pos, DOWNSTREAM);
+ return VisiblePosition(it.atEnd() ? searchRange->startPosition() : pos, DOWNSTREAM);
- Node* node = it.range()->startContainer();
- if ((node->isTextNode() && static_cast<int>(next) <= node->maxCharacterOffset()) || (node->renderer() && node->renderer()->isBR() && !next))
+ Node& node = it.atEnd() ? searchRange->startContainer() : it.range()->startContainer();
+ if ((node.isTextNode() && static_cast<int>(next) <= node.maxCharacterOffset()) || (node.renderer() && node.renderer()->isBR() && !next)) {
// The next variable contains a usable index into a text node
- return VisiblePosition(createLegacyEditingPosition(node, next), DOWNSTREAM);
+ return VisiblePosition(createLegacyEditingPosition(&node, next), DOWNSTREAM);
+ }
// Use the character iterator to translate the next value into a DOM position.
- BackwardsCharacterIterator charIt(searchRange.get());
+ BackwardsCharacterIterator charIt(searchRange);
charIt.advance(string.size() - suffixLength - next);
// FIXME: charIt can get out of shadow host.
return VisiblePosition(charIt.range()->endPosition(), DOWNSTREAM);
@@ -539,71 +630,35 @@ static VisiblePosition nextBoundary(const VisiblePosition& c, BoundarySearchFunc
return VisiblePosition();
Document& boundaryDocument = boundary->document();
- RefPtr<Range> searchRange(boundaryDocument.createRange());
+ Ref<Range> searchRange = boundaryDocument.createRange();
Position start(pos.parentAnchoredEquivalent());
Vector<UChar, 1024> string;
unsigned prefixLength = 0;
if (requiresContextForWordBoundary(c.characterAfter())) {
- RefPtr<Range> backwardsScanRange(boundaryDocument.createRange());
- backwardsScanRange->setEnd(start.deprecatedNode(), start.deprecatedEditingOffset(), IGNORE_EXCEPTION);
- SimplifiedBackwardsTextIterator backwardsIterator(backwardsScanRange.get());
- while (!backwardsIterator.atEnd()) {
- const UChar* characters = backwardsIterator.characters();
- int length = backwardsIterator.length();
- int i = startOfLastWordBoundaryContext(characters, length);
- string.insert(0, characters + i, length - i);
- prefixLength += length - i;
- if (i > 0)
- break;
- backwardsIterator.advance();
- }
+ auto backwardsScanRange = boundaryDocument.createRange();
+ if (start.deprecatedNode())
+ backwardsScanRange->setEnd(*start.deprecatedNode(), start.deprecatedEditingOffset());
+ prefixLength = prefixLengthForRange(backwardsScanRange, string);
}
- searchRange->selectNodeContents(boundary, IGNORE_EXCEPTION);
- searchRange->setStart(start.deprecatedNode(), start.deprecatedEditingOffset(), IGNORE_EXCEPTION);
- TextIterator it(searchRange.get(), TextIteratorEmitsCharactersBetweenAllVisiblePositions);
- unsigned next = 0;
- bool needMoreContext = false;
- while (!it.atEnd()) {
- bool inTextSecurityMode = it.node() && it.node()->renderer() && it.node()->renderer()->style().textSecurity() != TSNONE;
- // Keep asking the iterator for chunks until the search function
- // returns an end value not equal to the length of the string passed to it.
- if (!inTextSecurityMode)
- string.append(it.characters(), it.length());
- else {
- // Treat bullets used in the text security mode as regular characters when looking for boundaries
- String iteratorString(it.characters(), it.length());
-#if PLATFORM(IOS)
- iteratorString = iteratorString.impl()->fill('x');
-#else
- iteratorString.fill('x');
-#endif
- string.append(iteratorString.deprecatedCharacters(), iteratorString.length());
- }
- next = searchFunction(string.data(), string.size(), prefixLength, MayHaveMoreContext, needMoreContext);
- if (next != string.size())
- break;
- it.advance();
- }
- if (needMoreContext) {
- // The last search returned the end of the buffer and asked for more context,
- // but there is no further text. Force a search with what's available.
- next = searchFunction(string.data(), string.size(), prefixLength, DontHaveMoreContext, needMoreContext);
- ASSERT(!needMoreContext);
- }
+ searchRange->selectNodeContents(*boundary);
+ if (start.deprecatedNode())
+ searchRange->setStart(*start.deprecatedNode(), start.deprecatedEditingOffset());
+ TextIterator it(searchRange.ptr(), TextIteratorEmitsCharactersBetweenAllVisiblePositions);
+ unsigned next = forwardSearchForBoundaryWithTextIterator(it, string, prefixLength, searchFunction);
- if (it.atEnd() && next == string.size()) {
- pos = it.range()->startPosition();
- } else if (next != prefixLength) {
+ if (it.atEnd() && next == string.size())
+ pos = searchRange->endPosition();
+ else if (next > prefixLength) {
// Use the character iterator to translate the next value into a DOM position.
- CharacterIterator charIt(searchRange.get(), TextIteratorEmitsCharactersBetweenAllVisiblePositions);
+ CharacterIterator charIt(searchRange, TextIteratorEmitsCharactersBetweenAllVisiblePositions);
charIt.advance(next - prefixLength - 1);
RefPtr<Range> characterRange = charIt.range();
pos = characterRange->endPosition();
- if (*charIt.characters() == '\n') {
+ if (charIt.text()[0] == '\n') {
// FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593)
VisiblePosition visPos = VisiblePosition(pos);
if (visPos == VisiblePosition(characterRange->startPosition())) {
@@ -619,21 +674,21 @@ static VisiblePosition nextBoundary(const VisiblePosition& c, BoundarySearchFunc
// ---------
-static unsigned startWordBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
+unsigned startWordBoundary(StringView text, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
{
ASSERT(offset);
- if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset)) {
+ if (mayHaveMoreContext && !startOfLastWordBoundaryContext(text.substring(0, offset))) {
needMoreContext = true;
return 0;
}
needMoreContext = false;
int start, end;
- U16_BACK_1(characters, 0, offset);
- findWordBoundary(characters, length, offset, &start, &end);
+ U16_BACK_1(text, 0, offset);
+ findWordBoundary(text, offset, &start, &end);
return start;
}
-VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
+VisiblePosition startOfWord(const VisiblePosition& c, EWordSide side)
{
// FIXME: This returns a null VP for c at the start of the document
// and side == LeftWordIfOnBoundary
@@ -650,26 +705,20 @@ VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side)
return previousBoundary(p, startWordBoundary);
}
-static unsigned endWordBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
+unsigned endWordBoundary(StringView text, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
{
- ASSERT(offset <= length);
- if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset, length - offset) == static_cast<int>(length - offset)) {
+ ASSERT(offset <= text.length());
+ if (mayHaveMoreContext && endOfFirstWordBoundaryContext(text.substring(offset)) == text.length() - offset) {
needMoreContext = true;
- return length;
+ return text.length();
}
needMoreContext = false;
-#if PLATFORM(IOS)
- // FIXME: Bug 126830: [iOS] Implement WebCore::findEndWordBoundary()
- int start, end;
- findWordBoundary(characters, length, offset, &start, &end);
-#else
int end;
- findEndWordBoundary(characters, length, offset, &end);
-#endif
+ findEndWordBoundary(text, offset, &end);
return end;
}
-VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side)
+VisiblePosition endOfWord(const VisiblePosition& c, EWordSide side)
{
VisiblePosition p = c;
if (side == LeftWordIfOnBoundary) {
@@ -685,36 +734,34 @@ VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side)
return nextBoundary(p, endWordBoundary);
}
-static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
+static unsigned previousWordPositionBoundary(StringView text, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
{
- if (mayHaveMoreContext && !startOfLastWordBoundaryContext(characters, offset)) {
+ if (mayHaveMoreContext && !startOfLastWordBoundaryContext(text.substring(0, offset))) {
needMoreContext = true;
return 0;
}
needMoreContext = false;
- return findNextWordFromIndex(characters, length, offset, false);
+ return findNextWordFromIndex(text, offset, false);
}
-VisiblePosition previousWordPosition(const VisiblePosition &c)
+VisiblePosition previousWordPosition(const VisiblePosition& position)
{
- VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary);
- return c.honorEditingBoundaryAtOrBefore(prev);
+ return position.honorEditingBoundaryAtOrBefore(previousBoundary(position, previousWordPositionBoundary));
}
-static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
+static unsigned nextWordPositionBoundary(StringView text, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
{
- if (mayHaveMoreContext && endOfFirstWordBoundaryContext(characters + offset, length - offset) == static_cast<int>(length - offset)) {
+ if (mayHaveMoreContext && endOfFirstWordBoundaryContext(text.substring(offset)) == text.length() - offset) {
needMoreContext = true;
- return length;
+ return text.length();
}
needMoreContext = false;
- return findNextWordFromIndex(characters, length, offset, true);
+ return findNextWordFromIndex(text, offset, true);
}
-VisiblePosition nextWordPosition(const VisiblePosition &c)
+VisiblePosition nextWordPosition(const VisiblePosition& position)
{
- VisiblePosition next = nextBoundary(c, nextWordPositionBoundary);
- return c.honorEditingBoundaryAtOrAfter(next);
+ return position.honorEditingBoundaryAtOrAfter(nextBoundary(position, nextWordPositionBoundary));
}
bool isStartOfWord(const VisiblePosition& p)
@@ -763,35 +810,41 @@ static VisiblePosition startPositionForLine(const VisiblePosition& c, LineEndpoi
}
}
- return startNode->isTextNode() ? Position(toText(startNode), toInlineTextBox(startBox)->start())
+ return is<Text>(*startNode) ? Position(downcast<Text>(startNode), downcast<InlineTextBox>(*startBox).start())
: positionBeforeNode(startNode);
}
-static VisiblePosition startOfLine(const VisiblePosition& c, LineEndpointComputationMode mode)
+static VisiblePosition startOfLine(const VisiblePosition& c, LineEndpointComputationMode mode, bool* reachedBoundary)
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
// TODO: this is the current behavior that might need to be fixed.
// Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
VisiblePosition visPos = startPositionForLine(c, mode);
if (mode == UseLogicalOrdering) {
if (Node* editableRoot = highestEditableRoot(c.deepEquivalent())) {
- if (!editableRoot->contains(visPos.deepEquivalent().containerNode()))
- return firstPositionInNode(editableRoot);
+ if (!editableRoot->contains(visPos.deepEquivalent().containerNode())) {
+ VisiblePosition newPosition = firstPositionInNode(editableRoot);
+ if (reachedBoundary)
+ *reachedBoundary = c == newPosition;
+ return newPosition;
+ }
}
}
- return c.honorEditingBoundaryAtOrBefore(visPos);
+ return c.honorEditingBoundaryAtOrBefore(visPos, reachedBoundary);
}
// FIXME: Rename this function to reflect the fact it ignores bidi levels.
VisiblePosition startOfLine(const VisiblePosition& currentPosition)
{
- return startOfLine(currentPosition, UseInlineBoxOrdering);
+ return startOfLine(currentPosition, UseInlineBoxOrdering, nullptr);
}
-VisiblePosition logicalStartOfLine(const VisiblePosition& currentPosition)
+VisiblePosition logicalStartOfLine(const VisiblePosition& currentPosition, bool* reachedBoundary)
{
- return startOfLine(currentPosition, UseLogicalOrdering);
+ return startOfLine(currentPosition, UseLogicalOrdering, reachedBoundary);
}
static VisiblePosition endPositionForLine(const VisiblePosition& c, LineEndpointComputationMode mode)
@@ -832,14 +885,14 @@ static VisiblePosition endPositionForLine(const VisiblePosition& c, LineEndpoint
}
Position pos;
- if (endNode->hasTagName(brTag))
+ if (is<HTMLBRElement>(*endNode))
pos = positionBeforeNode(endNode);
- else if (endBox->isInlineTextBox() && endNode->isTextNode()) {
- InlineTextBox* endTextBox = toInlineTextBox(endBox);
- int endOffset = endTextBox->start();
- if (!endTextBox->isLineBreak())
- endOffset += endTextBox->len();
- pos = Position(toText(endNode), endOffset);
+ else if (is<InlineTextBox>(*endBox) && is<Text>(*endNode)) {
+ auto& endTextBox = downcast<InlineTextBox>(*endBox);
+ int endOffset = endTextBox.start();
+ if (!endTextBox.isLineBreak())
+ endOffset += endTextBox.len();
+ pos = Position(downcast<Text>(endNode), endOffset);
} else
pos = positionAfterNode(endNode);
@@ -851,8 +904,10 @@ static bool inSameLogicalLine(const VisiblePosition& a, const VisiblePosition& b
return a.isNotNull() && logicalStartOfLine(a) == logicalStartOfLine(b);
}
-static VisiblePosition endOfLine(const VisiblePosition& c, LineEndpointComputationMode mode)
+static VisiblePosition endOfLine(const VisiblePosition& c, LineEndpointComputationMode mode, bool* reachedBoundary)
{
+ if (reachedBoundary)
+ *reachedBoundary = false;
// TODO: this is the current behavior that might need to be fixed.
// Please refer to https://bugs.webkit.org/show_bug.cgi?id=49107 for detail.
VisiblePosition visPos = endPositionForLine(c, mode);
@@ -867,11 +922,15 @@ static VisiblePosition endOfLine(const VisiblePosition& c, LineEndpointComputati
visPos = visPos.previous();
if (Node* editableRoot = highestEditableRoot(c.deepEquivalent())) {
- if (!editableRoot->contains(visPos.deepEquivalent().containerNode()))
- return lastPositionInNode(editableRoot);
+ if (!editableRoot->contains(visPos.deepEquivalent().containerNode())) {
+ VisiblePosition newPosition = lastPositionInNode(editableRoot);
+ if (reachedBoundary)
+ *reachedBoundary = c == newPosition;
+ return newPosition;
+ }
}
- return c.honorEditingBoundaryAtOrAfter(visPos);
+ return c.honorEditingBoundaryAtOrAfter(visPos, reachedBoundary);
}
// Make sure the end of line is at the same line as the given input position. Else use the previous position to
@@ -886,50 +945,59 @@ static VisiblePosition endOfLine(const VisiblePosition& c, LineEndpointComputati
visPos = endPositionForLine(visPos, UseInlineBoxOrdering);
}
- return c.honorEditingBoundaryAtOrAfter(visPos);
+ return c.honorEditingBoundaryAtOrAfter(visPos, reachedBoundary);
}
// FIXME: Rename this function to reflect the fact it ignores bidi levels.
VisiblePosition endOfLine(const VisiblePosition& currentPosition)
{
- return endOfLine(currentPosition, UseInlineBoxOrdering);
+ return endOfLine(currentPosition, UseInlineBoxOrdering, nullptr);
}
-VisiblePosition logicalEndOfLine(const VisiblePosition& currentPosition)
+VisiblePosition logicalEndOfLine(const VisiblePosition& currentPosition, bool* reachedBoundary)
{
- return endOfLine(currentPosition, UseLogicalOrdering);
+ return endOfLine(currentPosition, UseLogicalOrdering, reachedBoundary);
}
-bool inSameLine(const VisiblePosition &a, const VisiblePosition &b)
+bool inSameLine(const VisiblePosition& a, const VisiblePosition& b)
{
return a.isNotNull() && startOfLine(a) == startOfLine(b);
}
-bool isStartOfLine(const VisiblePosition &p)
+bool isStartOfLine(const VisiblePosition& p)
{
return p.isNotNull() && p == startOfLine(p);
}
-bool isEndOfLine(const VisiblePosition &p)
+bool isEndOfLine(const VisiblePosition& p)
{
return p.isNotNull() && p == endOfLine(p);
}
-static inline IntPoint absoluteLineDirectionPointToLocalPointInBlock(RootInlineBox* root, int lineDirectionPoint)
+bool isLogicalEndOfLine(const VisiblePosition &p)
+{
+ return p.isNotNull() && p == logicalEndOfLine(p);
+}
+
+static inline IntPoint absoluteLineDirectionPointToLocalPointInBlock(RootInlineBox& root, int lineDirectionPoint)
{
- ASSERT(root);
- RenderBlockFlow& containingBlock = root->blockFlow();
- FloatPoint absoluteBlockPoint = containingBlock.localToAbsolute(FloatPoint());
- if (containingBlock.hasOverflowClip())
- absoluteBlockPoint -= containingBlock.scrolledContentOffset();
+ RenderBlockFlow& containingBlock = root.blockFlow();
+ FloatPoint absoluteBlockPoint = containingBlock.localToAbsolute(FloatPoint()) - toFloatSize(containingBlock.scrollPosition());
if (containingBlock.isHorizontalWritingMode())
- return IntPoint(lineDirectionPoint - absoluteBlockPoint.x(), root->blockDirectionPointInLine());
+ return IntPoint(lineDirectionPoint - absoluteBlockPoint.x(), root.blockDirectionPointInLine());
- return IntPoint(root->blockDirectionPointInLine(), lineDirectionPoint - absoluteBlockPoint.y());
+ return IntPoint(root.blockDirectionPointInLine(), lineDirectionPoint - absoluteBlockPoint.y());
}
-VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint, EditableType editableType)
+static Element* rootEditableOrDocumentElement(Node& node, EditableType editableType)
+{
+ if (hasEditableStyle(node, editableType))
+ return editableRootForPosition(firstPositionInOrBeforeNode(&node), editableType);
+ return node.document().documentElement();
+}
+
+VisiblePosition previousLinePosition(const VisiblePosition& visiblePosition, int lineDirectionPoint, EditableType editableType)
{
Position p = visiblePosition.deepEquivalent();
Node* node = p.deprecatedNode();
@@ -943,7 +1011,7 @@ VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int
if (!renderer)
return VisiblePosition();
- RootInlineBox* root = 0;
+ RootInlineBox* root = nullptr;
InlineBox* box;
int ignoredCaretOffset;
visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
@@ -952,7 +1020,7 @@ VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int
// We want to skip zero height boxes.
// This could happen in case it is a TrailingFloatsRootInlineBox.
if (!root || !root->logicalHeight() || !root->firstLeafChild())
- root = 0;
+ root = nullptr;
}
if (!root) {
@@ -967,24 +1035,24 @@ VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int
if (root) {
// FIXME: Can be wrong for multi-column layout and with transforms.
- IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(root, lineDirectionPoint);
+ IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(*root, lineDirectionPoint);
RenderObject& renderer = root->closestLeafChildForPoint(pointInLine, isEditablePosition(p))->renderer();
Node* node = renderer.node();
- if (node && editingIgnoresContent(node))
+ if (node && editingIgnoresContent(*node))
return positionInParentBeforeNode(node);
- return renderer.positionForPoint(pointInLine);
+ return renderer.positionForPoint(pointInLine, nullptr);
}
// Could not find a previous line. This means we must already be on the first line.
// Move to the start of the content in this block, which effectively moves us
// to the start of the line we're on.
- Element* rootElement = node->hasEditableStyle(editableType) ? node->rootEditableElement(editableType) : node->document().documentElement();
+ Element* rootElement = rootEditableOrDocumentElement(*node, editableType);
if (!rootElement)
return VisiblePosition();
return VisiblePosition(firstPositionInNode(rootElement), DOWNSTREAM);
}
-VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lineDirectionPoint, EditableType editableType)
+VisiblePosition nextLinePosition(const VisiblePosition& visiblePosition, int lineDirectionPoint, EditableType editableType)
{
Position p = visiblePosition.deepEquivalent();
Node* node = p.deprecatedNode();
@@ -998,7 +1066,7 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lin
if (!renderer)
return VisiblePosition();
- RootInlineBox* root = 0;
+ RootInlineBox* root = nullptr;
InlineBox* box;
int ignoredCaretOffset;
visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
@@ -1007,12 +1075,12 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lin
// We want to skip zero height boxes.
// This could happen in case it is a TrailingFloatsRootInlineBox.
if (!root || !root->logicalHeight() || !root->firstLeafChild())
- root = 0;
+ root = nullptr;
}
if (!root) {
// FIXME: We need do the same in previousLinePosition.
- Node* child = node->childNode(p.deprecatedEditingOffset());
+ Node* child = node->traverseToChildAt(p.deprecatedEditingOffset());
node = child ? child : node->lastDescendant();
Position position = nextRootInlineBoxCandidatePosition(node, visiblePosition, editableType);
if (position.isNotNull()) {
@@ -1025,18 +1093,18 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lin
if (root) {
// FIXME: Can be wrong for multi-column layout and with transforms.
- IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(root, lineDirectionPoint);
+ IntPoint pointInLine = absoluteLineDirectionPointToLocalPointInBlock(*root, lineDirectionPoint);
RenderObject& renderer = root->closestLeafChildForPoint(pointInLine, isEditablePosition(p))->renderer();
Node* node = renderer.node();
- if (node && editingIgnoresContent(node))
+ if (node && editingIgnoresContent(*node))
return positionInParentBeforeNode(node);
- return renderer.positionForPoint(pointInLine);
+ return renderer.positionForPoint(pointInLine, nullptr);
}
// Could not find a next line. This means we must already be on the last line.
// Move to the end of the content in this block, which effectively moves us
// to the end of the line we're on.
- Element* rootElement = node->hasEditableStyle(editableType) ? node->rootEditableElement(editableType) : node->document().documentElement();
+ Element* rootElement = rootEditableOrDocumentElement(*node, editableType);
if (!rootElement)
return VisiblePosition();
return VisiblePosition(lastPositionInNode(rootElement), DOWNSTREAM);
@@ -1044,76 +1112,55 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int lin
// ---------
-static unsigned startSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
+unsigned startSentenceBoundary(StringView text, unsigned, BoundarySearchContextAvailability, bool&)
{
- TextBreakIterator* iterator = sentenceBreakIterator(StringView(characters, length));
// FIXME: The following function can return -1; we don't handle that.
- return textBreakPreceding(iterator, length);
+ return ubrk_preceding(sentenceBreakIterator(text), text.length());
}
-VisiblePosition startOfSentence(const VisiblePosition &c)
+VisiblePosition startOfSentence(const VisiblePosition& position)
{
- return previousBoundary(c, startSentenceBoundary);
+ return previousBoundary(position, startSentenceBoundary);
}
-static unsigned endSentenceBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
+unsigned endSentenceBoundary(StringView text, unsigned, BoundarySearchContextAvailability, bool&)
{
- TextBreakIterator* iterator = sentenceBreakIterator(StringView(characters, length));
- return textBreakNext(iterator);
+ return ubrk_next(sentenceBreakIterator(text));
}
-// FIXME: This includes the space after the punctuation that marks the end of the sentence.
-VisiblePosition endOfSentence(const VisiblePosition &c)
+VisiblePosition endOfSentence(const VisiblePosition& position)
{
- return nextBoundary(c, endSentenceBoundary);
+ // FIXME: This includes the space after the punctuation that marks the end of the sentence.
+ return nextBoundary(position, endSentenceBoundary);
}
-static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
+static unsigned previousSentencePositionBoundary(StringView text, unsigned, BoundarySearchContextAvailability, bool&)
{
// FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right.
- TextBreakIterator* iterator = sentenceBreakIterator(StringView(characters, length));
// FIXME: The following function can return -1; we don't handle that.
- return textBreakPreceding(iterator, length);
+ return ubrk_preceding(sentenceBreakIterator(text), text.length());
}
-VisiblePosition previousSentencePosition(const VisiblePosition &c)
+VisiblePosition previousSentencePosition(const VisiblePosition& position)
{
- VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary);
- return c.honorEditingBoundaryAtOrBefore(prev);
+ return position.honorEditingBoundaryAtOrBefore(previousBoundary(position, previousSentencePositionBoundary));
}
-static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length, unsigned, BoundarySearchContextAvailability, bool&)
+static unsigned nextSentencePositionBoundary(StringView text, unsigned, BoundarySearchContextAvailability, bool&)
{
- // FIXME: This is identical to endSentenceBoundary. This isn't right, it needs to
- // move to the equivlant position in the following sentence.
- TextBreakIterator* iterator = sentenceBreakIterator(StringView(characters, length));
- return textBreakFollowing(iterator, 0);
+ // FIXME: This is identical to endSentenceBoundary.
+ // That isn't right. This function needs to move to the equivalent position in the following sentence.
+ return ubrk_following(sentenceBreakIterator(text), 0);
}
-VisiblePosition nextSentencePosition(const VisiblePosition &c)
+VisiblePosition nextSentencePosition(const VisiblePosition& position)
{
- VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary);
- return c.honorEditingBoundaryAtOrAfter(next);
+ return position.honorEditingBoundaryAtOrAfter(nextBoundary(position, nextSentencePositionBoundary));
}
-VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
+Node* findStartOfParagraph(Node* startNode, Node* highestRoot, Node* startBlock, int& offset, Position::AnchorType& type, EditingBoundaryCrossingRule boundaryCrossingRule)
{
- Position p = c.deepEquivalent();
- Node* startNode = p.deprecatedNode();
-
- if (!startNode)
- return VisiblePosition();
-
- if (isRenderedAsNonInlineTableImageOrHR(startNode))
- return positionBeforeNode(startNode);
-
- Node* startBlock = enclosingBlock(startNode);
-
Node* node = startNode;
- Node* highestRoot = highestEditableRoot(p);
- int offset = p.deprecatedEditingOffset();
- Position::AnchorType type = p.anchorType();
-
Node* n = startNode;
while (n) {
#if ENABLE(USERSELECT_ALL)
@@ -1124,76 +1171,57 @@ VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossi
break;
if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
while (n && n->hasEditableStyle() != startNode->hasEditableStyle())
- n = NodeTraversal::previousPostOrder(n, startBlock);
+ n = NodeTraversal::previousPostOrder(*n, startBlock);
if (!n || !n->isDescendantOf(highestRoot))
break;
}
RenderObject* r = n->renderer();
if (!r) {
- n = NodeTraversal::previousPostOrder(n, startBlock);
+ n = NodeTraversal::previousPostOrder(*n, startBlock);
continue;
}
const RenderStyle& style = r->style();
if (style.visibility() != VISIBLE) {
- n = NodeTraversal::previousPostOrder(n, startBlock);
+ n = NodeTraversal::previousPostOrder(*n, startBlock);
continue;
}
if (r->isBR() || isBlock(n))
break;
- if (r->isText() && toRenderText(r)->hasRenderedText()) {
- ASSERT_WITH_SECURITY_IMPLICATION(n->isTextNode());
+ if (is<RenderText>(*r) && downcast<RenderText>(*r).hasRenderedText()) {
+ ASSERT_WITH_SECURITY_IMPLICATION(is<Text>(*n));
type = Position::PositionIsOffsetInAnchor;
if (style.preserveNewline()) {
- const UChar* chars = toRenderText(r)->deprecatedCharacters();
- int i = toRenderText(r)->textLength();
+ StringImpl& text = *downcast<RenderText>(*r).text();
+ int i = text.length();
int o = offset;
if (n == startNode && o < i)
i = std::max(0, o);
while (--i >= 0) {
- if (chars[i] == '\n')
- return VisiblePosition(Position(toText(n), i + 1), DOWNSTREAM);
+ if (text[i] == '\n') {
+ offset = i + 1;
+ return n;
+ }
}
}
node = n;
offset = 0;
- n = NodeTraversal::previousPostOrder(n, startBlock);
- } else if (editingIgnoresContent(n) || isTableElement(n)) {
+ n = NodeTraversal::previousPostOrder(*n, startBlock);
+ } else if (editingIgnoresContent(*n) || isRenderedTable(n)) {
node = n;
type = Position::PositionIsBeforeAnchor;
- n = n->previousSibling() ? n->previousSibling() : NodeTraversal::previousPostOrder(n, startBlock);
+ n = n->previousSibling() ? n->previousSibling() : NodeTraversal::previousPostOrder(*n, startBlock);
} else
- n = NodeTraversal::previousPostOrder(n, startBlock);
+ n = NodeTraversal::previousPostOrder(*n, startBlock);
}
- if (type == Position::PositionIsOffsetInAnchor) {
- ASSERT(type == Position::PositionIsOffsetInAnchor || !offset);
- return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
- }
-
- return VisiblePosition(Position(node, type), DOWNSTREAM);
+ return node;
}
-VisiblePosition endOfParagraph(const VisiblePosition &c, EditingBoundaryCrossingRule boundaryCrossingRule)
-{
- if (c.isNull())
- return VisiblePosition();
-
- Position p = c.deepEquivalent();
- Node* startNode = p.deprecatedNode();
-
- if (isRenderedAsNonInlineTableImageOrHR(startNode))
- return positionAfterNode(startNode);
-
- Node* startBlock = enclosingBlock(startNode);
- Node* stayInsideBlock = startBlock;
-
+Node* findEndOfParagraph(Node* startNode, Node* highestRoot, Node* stayInsideBlock, int& offset, Position::AnchorType& type, EditingBoundaryCrossingRule boundaryCrossingRule)
+{
Node* node = startNode;
- Node* highestRoot = highestEditableRoot(p);
- int offset = p.deprecatedEditingOffset();
- Position::AnchorType type = p.anchorType();
-
Node* n = startNode;
while (n) {
#if ENABLE(USERSELECT_ALL)
@@ -1204,49 +1232,107 @@ VisiblePosition endOfParagraph(const VisiblePosition &c, EditingBoundaryCrossing
break;
if (boundaryCrossingRule == CanSkipOverEditingBoundary) {
while (n && n->hasEditableStyle() != startNode->hasEditableStyle())
- n = NodeTraversal::next(n, stayInsideBlock);
+ n = NodeTraversal::next(*n, stayInsideBlock);
if (!n || !n->isDescendantOf(highestRoot))
break;
}
RenderObject* r = n->renderer();
if (!r) {
- n = NodeTraversal::next(n, stayInsideBlock);
+ n = NodeTraversal::next(*n, stayInsideBlock);
continue;
}
const RenderStyle& style = r->style();
if (style.visibility() != VISIBLE) {
- n = NodeTraversal::next(n, stayInsideBlock);
+ n = NodeTraversal::next(*n, stayInsideBlock);
continue;
}
+ // FIXME: This is wrong when startNode is a block. We should return a position after the block.
if (r->isBR() || isBlock(n))
break;
// FIXME: We avoid returning a position where the renderer can't accept the caret.
- if (r->isText() && toRenderText(r)->hasRenderedText()) {
- ASSERT_WITH_SECURITY_IMPLICATION(n->isTextNode());
- int length = toRenderText(r)->textLength();
+ if (is<RenderText>(*r) && downcast<RenderText>(*r).hasRenderedText()) {
+ ASSERT_WITH_SECURITY_IMPLICATION(is<Text>(*n));
type = Position::PositionIsOffsetInAnchor;
if (style.preserveNewline()) {
- const UChar* chars = toRenderText(r)->deprecatedCharacters();
+ StringImpl& text = *downcast<RenderText>(*r).text();
int o = n == startNode ? offset : 0;
+ int length = text.length();
for (int i = o; i < length; ++i) {
- if (chars[i] == '\n')
- return VisiblePosition(Position(toText(n), i), DOWNSTREAM);
+ if (text[i] == '\n') {
+ offset = i;
+ return n;
+ }
}
}
node = n;
offset = r->caretMaxOffset();
- n = NodeTraversal::next(n, stayInsideBlock);
- } else if (editingIgnoresContent(n) || isTableElement(n)) {
+ n = NodeTraversal::next(*n, stayInsideBlock);
+ } else if (editingIgnoresContent(*n) || isRenderedTable(n)) {
node = n;
type = Position::PositionIsAfterAnchor;
- n = NodeTraversal::nextSkippingChildren(n, stayInsideBlock);
+ n = NodeTraversal::nextSkippingChildren(*n, stayInsideBlock);
} else
- n = NodeTraversal::next(n, stayInsideBlock);
+ n = NodeTraversal::next(*n, stayInsideBlock);
+ }
+ return node;
+}
+
+VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
+{
+ Position p = c.deepEquivalent();
+ auto* startNode = p.deprecatedNode();
+
+ if (!startNode)
+ return VisiblePosition();
+
+ if (isRenderedAsNonInlineTableImageOrHR(startNode))
+ return positionBeforeNode(startNode);
+
+ Node* startBlock = enclosingBlock(startNode);
+
+ auto* highestRoot = highestEditableRoot(p);
+ int offset = p.deprecatedEditingOffset();
+ Position::AnchorType type = p.anchorType();
+
+ auto* node = findStartOfParagraph(startNode, highestRoot, startBlock, offset, type, boundaryCrossingRule);
+
+ if (is<Text>(node))
+ return VisiblePosition(Position(downcast<Text>(node), offset), DOWNSTREAM);
+
+ if (type == Position::PositionIsOffsetInAnchor) {
+ ASSERT(type == Position::PositionIsOffsetInAnchor || !offset);
+ return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
}
+
+ return VisiblePosition(Position(node, type), DOWNSTREAM);
+}
+VisiblePosition endOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
+{
+ if (c.isNull())
+ return VisiblePosition();
+
+ Position p = c.deepEquivalent();
+ auto* startNode = p.deprecatedNode();
+
+ if (isRenderedAsNonInlineTableImageOrHR(startNode))
+ return positionAfterNode(startNode);
+
+ auto* startBlock = enclosingBlock(startNode);
+ auto* stayInsideBlock = startBlock;
+
+ auto* highestRoot = highestEditableRoot(p);
+ int offset = p.deprecatedEditingOffset();
+ Position::AnchorType type = p.anchorType();
+
+ auto* node = findEndOfParagraph(startNode, highestRoot, stayInsideBlock, offset, type, boundaryCrossingRule);
+
+ if (is<Text>(node))
+ return VisiblePosition(Position(downcast<Text>(node), offset), DOWNSTREAM);
+
if (type == Position::PositionIsOffsetInAnchor)
return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
@@ -1265,17 +1351,17 @@ VisiblePosition startOfNextParagraph(const VisiblePosition& visiblePosition)
return afterParagraphEnd;
}
-bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b, EditingBoundaryCrossingRule boundaryCrossingRule)
+bool inSameParagraph(const VisiblePosition& a, const VisiblePosition& b, EditingBoundaryCrossingRule boundaryCrossingRule)
{
return a.isNotNull() && startOfParagraph(a, boundaryCrossingRule) == startOfParagraph(b, boundaryCrossingRule);
}
-bool isStartOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule)
+bool isStartOfParagraph(const VisiblePosition& pos, EditingBoundaryCrossingRule boundaryCrossingRule)
{
return pos.isNotNull() && pos == startOfParagraph(pos, boundaryCrossingRule);
}
-bool isEndOfParagraph(const VisiblePosition &pos, EditingBoundaryCrossingRule boundaryCrossingRule)
+bool isEndOfParagraph(const VisiblePosition& pos, EditingBoundaryCrossingRule boundaryCrossingRule)
{
return pos.isNotNull() && pos == endOfParagraph(pos, boundaryCrossingRule);
}
@@ -1324,17 +1410,17 @@ VisiblePosition endOfBlock(const VisiblePosition& visiblePosition, EditingBounda
return lastPositionInNode(endBlock);
}
-bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b)
+bool inSameBlock(const VisiblePosition& a, const VisiblePosition& b)
{
return !a.isNull() && enclosingBlock(a.deepEquivalent().containerNode()) == enclosingBlock(b.deepEquivalent().containerNode());
}
-bool isStartOfBlock(const VisiblePosition &pos)
+bool isStartOfBlock(const VisiblePosition& pos)
{
return pos.isNotNull() && pos == startOfBlock(pos, CanCrossEditingBoundary);
}
-bool isEndOfBlock(const VisiblePosition &pos)
+bool isEndOfBlock(const VisiblePosition& pos)
{
return pos.isNotNull() && pos == endOfBlock(pos, CanCrossEditingBoundary);
}
@@ -1346,21 +1432,16 @@ VisiblePosition startOfDocument(const Node* node)
if (!node || !node->document().documentElement())
return VisiblePosition();
-#if PLATFORM(IOS)
// The canonicalization of the position at (documentElement, 0) can turn the visible
// position to null, even when there's a valid candidate to be had, because the root HTML element
// is not content editable. So we construct directly from the valid candidate.
- // FIXME: Merge this to Open Source. https://bugs.webkit.org/show_bug.cgi?id=56437
Position firstCandidate = nextCandidate(createLegacyEditingPosition(node->document().documentElement(), 0));
if (firstCandidate.isNull())
return VisiblePosition();
return VisiblePosition(firstCandidate);
-#else
- return VisiblePosition(firstPositionInNode(node->document().documentElement()), DOWNSTREAM);
-#endif
}
-VisiblePosition startOfDocument(const VisiblePosition &c)
+VisiblePosition startOfDocument(const VisiblePosition& c)
{
return startOfDocument(c.deepEquivalent().deprecatedNode());
}
@@ -1370,29 +1451,22 @@ VisiblePosition endOfDocument(const Node* node)
if (!node || !node->document().documentElement())
return VisiblePosition();
-#if PLATFORM(IOS)
// (As above, in startOfDocument.) The canonicalization can reject valid visible positions
// when descending from the root element, so we construct the visible position directly from a
// valid candidate.
- // FIXME: Merge this to Open Source. https://bugs.webkit.org/show_bug.cgi?id=56437
-#endif
- Element* doc = node->document().documentElement();
-#if PLATFORM(IOS)
- Position lastPosition = createLegacyEditingPosition(node->document().documentElement(), doc->childNodeCount());
+ Position lastPosition = createLegacyEditingPosition(node->document().documentElement(), node->document().documentElement()->countChildNodes());
Position lastCandidate = previousCandidate(lastPosition);
if (lastCandidate.isNull())
return VisiblePosition();
return VisiblePosition(lastCandidate);
-#endif
- return VisiblePosition(lastPositionInNode(doc), DOWNSTREAM);
}
-VisiblePosition endOfDocument(const VisiblePosition &c)
+VisiblePosition endOfDocument(const VisiblePosition& c)
{
return endOfDocument(c.deepEquivalent().deprecatedNode());
}
-bool inSameDocument(const VisiblePosition &a, const VisiblePosition &b)
+bool inSameDocument(const VisiblePosition& a, const VisiblePosition& b)
{
Position ap = a.deepEquivalent();
Node* an = ap.deprecatedNode();
@@ -1406,12 +1480,12 @@ bool inSameDocument(const VisiblePosition &a, const VisiblePosition &b)
return &an->document() == &bn->document();
}
-bool isStartOfDocument(const VisiblePosition &p)
+bool isStartOfDocument(const VisiblePosition& p)
{
return p.isNotNull() && p.previous(CanCrossEditingBoundary).isNull();
}
-bool isEndOfDocument(const VisiblePosition &p)
+bool isEndOfDocument(const VisiblePosition& p)
{
return p.isNotNull() && p.next(CanCrossEditingBoundary).isNull();
}
@@ -1420,38 +1494,37 @@ bool isEndOfDocument(const VisiblePosition &p)
VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition)
{
- Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
+ auto* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
if (!highestRoot)
- return VisiblePosition();
+ return { };
return firstPositionInNode(highestRoot);
}
VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition)
{
- Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
+ auto* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent());
if (!highestRoot)
- return VisiblePosition();
+ return { };
return lastPositionInNode(highestRoot);
}
-bool isEndOfEditableOrNonEditableContent(const VisiblePosition &p)
+bool isEndOfEditableOrNonEditableContent(const VisiblePosition& p)
{
return p.isNotNull() && p.next().isNull();
}
-VisiblePosition leftBoundaryOfLine(const VisiblePosition& c, TextDirection direction)
+VisiblePosition leftBoundaryOfLine(const VisiblePosition& c, TextDirection direction, bool* reachedBoundary)
{
- return direction == LTR ? logicalStartOfLine(c) : logicalEndOfLine(c);
+ return direction == LTR ? logicalStartOfLine(c, reachedBoundary) : logicalEndOfLine(c, reachedBoundary);
}
-VisiblePosition rightBoundaryOfLine(const VisiblePosition& c, TextDirection direction)
+VisiblePosition rightBoundaryOfLine(const VisiblePosition& c, TextDirection direction, bool* reachedBoundary)
{
- return direction == LTR ? logicalEndOfLine(c) : logicalStartOfLine(c);
+ return direction == LTR ? logicalEndOfLine(c, reachedBoundary) : logicalStartOfLine(c, reachedBoundary);
}
-#if PLATFORM(IOS)
static bool directionIsDownstream(SelectionDirection direction)
{
if (direction == DirectionBackward)
@@ -1573,9 +1646,9 @@ bool withinTextUnitOfGranularity(const VisiblePosition& vp, TextGranularity gran
return (prevBoundary < vp && vp < nextBoundary);
}
-static VisiblePosition nextCharacterBoundaryInDirection(const VisiblePosition& vp, SelectionDirection direction)
+static VisiblePosition nextCharacterBoundaryInDirection(const VisiblePosition& vp, SelectionDirection direction, EditingBoundaryCrossingRule rule)
{
- return directionIsDownstream(direction) ? vp.next() : vp.previous();
+ return directionIsDownstream(direction) ? vp.next(rule) : vp.previous(rule);
}
static VisiblePosition nextWordBoundaryInDirection(const VisiblePosition& vp, SelectionDirection direction)
@@ -1715,7 +1788,7 @@ VisiblePosition positionOfNextBoundaryOfGranularity(const VisiblePosition& vp, T
{
switch (granularity) {
case CharacterGranularity:
- return nextCharacterBoundaryInDirection(vp, direction);
+ return nextCharacterBoundaryInDirection(vp, direction, CanCrossEditingBoundary);
case WordGranularity:
return nextWordBoundaryInDirection(vp, direction);
case SentenceGranularity:
@@ -1732,11 +1805,11 @@ VisiblePosition positionOfNextBoundaryOfGranularity(const VisiblePosition& vp, T
}
}
-PassRefPtr<Range> enclosingTextUnitOfGranularity(const VisiblePosition& vp, TextGranularity granularity, SelectionDirection direction)
+RefPtr<Range> enclosingTextUnitOfGranularity(const VisiblePosition& vp, TextGranularity granularity, SelectionDirection direction)
{
// This is particularly inefficient. We could easily obtain the answer with the boundaries computed below.
if (!withinTextUnitOfGranularity(vp, granularity, direction))
- return 0;
+ return nullptr;
VisiblePosition prevBoundary;
VisiblePosition nextBoundary;
@@ -1788,18 +1861,16 @@ PassRefPtr<Range> enclosingTextUnitOfGranularity(const VisiblePosition& vp, Text
default:
ASSERT_NOT_REACHED();
- return 0;
+ return nullptr;
}
if (prevBoundary.isNull() || nextBoundary.isNull())
- return 0;
+ return nullptr;
if (vp < prevBoundary || vp > nextBoundary)
- return 0;
-
- RefPtr<Range> range = Range::create(prevBoundary.deepEquivalent().deprecatedNode()->document(), prevBoundary, nextBoundary);
+ return nullptr;
- return range;
+ return Range::create(prevBoundary.deepEquivalent().deprecatedNode()->document(), prevBoundary, nextBoundary);
}
int distanceBetweenPositions(const VisiblePosition& vp, const VisiblePosition& other)
@@ -1818,9 +1889,47 @@ int distanceBetweenPositions(const VisiblePosition& vp, const VisiblePosition& o
return (thisIsStart ? -distance : distance);
}
-PassRefPtr<Range> wordRangeFromPosition(const VisiblePosition& position)
+void charactersAroundPosition(const VisiblePosition& position, UChar32& oneAfter, UChar32& oneBefore, UChar32& twoBefore)
{
- ASSERT(position.isNotNull());
+ const int maxCharacters = 3;
+ Vector<UChar32> characters(maxCharacters);
+
+ if (position.isNull() || isStartOfDocument(position))
+ return;
+
+ VisiblePosition startPosition = position;
+ VisiblePosition endPosition = position;
+
+ VisiblePosition nextPosition = nextCharacterBoundaryInDirection(position, DirectionForward, CannotCrossEditingBoundary);
+ if (nextPosition.isNotNull())
+ endPosition = nextPosition;
+
+ VisiblePosition previousPosition = nextCharacterBoundaryInDirection(position, DirectionBackward, CannotCrossEditingBoundary);
+ if (previousPosition.isNotNull()) {
+ startPosition = previousPosition;
+ previousPosition = nextCharacterBoundaryInDirection(previousPosition, DirectionBackward, CannotCrossEditingBoundary);
+ if (previousPosition.isNotNull())
+ startPosition = previousPosition;
+ }
+
+ if (startPosition != endPosition) {
+ String characterString = plainText(Range::create(position.deepEquivalent().anchorNode()->document(), startPosition, endPosition).ptr()).replace(noBreakSpace, ' ');
+ for (int i = characterString.length() - 1, index = 0; i >= 0 && index < maxCharacters; --i) {
+ if (!index && nextPosition.isNull())
+ index++;
+ characters[index++] = characterString[i];
+ }
+ }
+ oneAfter = characters[0];
+ oneBefore = characters[1];
+ twoBefore = characters[2];
+}
+
+RefPtr<Range> wordRangeFromPosition(const VisiblePosition& position)
+{
+ // The selection could be in a non visible element and we don't have a VisiblePosition.
+ if (position.isNull())
+ return nullptr;
RefPtr<Range> range = enclosingTextUnitOfGranularity(position, WordGranularity, DirectionBackward);
@@ -1888,6 +1997,35 @@ VisiblePosition closestWordBoundaryForPosition(const VisiblePosition& position)
return result;
}
-#endif
+RefPtr<Range> rangeExpandedByCharactersInDirectionAtWordBoundary(const VisiblePosition& position, int numberOfCharactersToExpand, SelectionDirection direction)
+{
+ Position start = position.deepEquivalent();
+ Position end = position.deepEquivalent();
+ for (int i = 0; i < numberOfCharactersToExpand; ++i) {
+ if (direction == DirectionBackward)
+ start = start.previous(Character);
+ else
+ end = end.next(Character);
+ }
+
+ if (direction == DirectionBackward && !atBoundaryOfGranularity(start, WordGranularity, DirectionBackward))
+ start = startOfWord(start).deepEquivalent();
+ if (direction == DirectionForward && !atBoundaryOfGranularity(end, WordGranularity, DirectionForward))
+ end = endOfWord(end).deepEquivalent();
+
+ return makeRange(start, end);
+}
+
+RefPtr<Range> rangeExpandedAroundPositionByCharacters(const VisiblePosition& position, int numberOfCharactersToExpand)
+{
+ Position start = position.deepEquivalent();
+ Position end = position.deepEquivalent();
+ for (int i = 0; i < numberOfCharactersToExpand; ++i) {
+ start = start.previous(Character);
+ end = end.next(Character);
+ }
+
+ return makeRange(start, end);
+}
}
diff --git a/Source/WebCore/editing/VisibleUnits.h b/Source/WebCore/editing/VisibleUnits.h
index 0b1e436d9..4ec8bf16f 100644
--- a/Source/WebCore/editing/VisibleUnits.h
+++ b/Source/WebCore/editing/VisibleUnits.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,56 +23,58 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef VisibleUnits_h
-#define VisibleUnits_h
+#pragma once
#include "EditingBoundary.h"
-#include "TextDirection.h"
+#include "TextFlags.h"
#include "VisibleSelection.h"
namespace WebCore {
class Node;
class VisiblePosition;
+class SimplifiedBackwardsTextIterator;
+class TextIterator;
enum EWordSide { RightWordIfOnBoundary = false, LeftWordIfOnBoundary = true };
// words
-VisiblePosition startOfWord(const VisiblePosition &, EWordSide = RightWordIfOnBoundary);
-VisiblePosition endOfWord(const VisiblePosition &, EWordSide = RightWordIfOnBoundary);
-VisiblePosition previousWordPosition(const VisiblePosition &);
-VisiblePosition nextWordPosition(const VisiblePosition &);
+WEBCORE_EXPORT VisiblePosition startOfWord(const VisiblePosition &, EWordSide = RightWordIfOnBoundary);
+WEBCORE_EXPORT VisiblePosition endOfWord(const VisiblePosition &, EWordSide = RightWordIfOnBoundary);
+WEBCORE_EXPORT VisiblePosition previousWordPosition(const VisiblePosition &);
+WEBCORE_EXPORT VisiblePosition nextWordPosition(const VisiblePosition &);
VisiblePosition rightWordPosition(const VisiblePosition&, bool skipsSpaceWhenMovingRight);
VisiblePosition leftWordPosition(const VisiblePosition&, bool skipsSpaceWhenMovingRight);
bool isStartOfWord(const VisiblePosition&);
// sentences
-VisiblePosition startOfSentence(const VisiblePosition &);
-VisiblePosition endOfSentence(const VisiblePosition &);
-VisiblePosition previousSentencePosition(const VisiblePosition &);
-VisiblePosition nextSentencePosition(const VisiblePosition &);
+WEBCORE_EXPORT VisiblePosition startOfSentence(const VisiblePosition &);
+WEBCORE_EXPORT VisiblePosition endOfSentence(const VisiblePosition &);
+WEBCORE_EXPORT VisiblePosition previousSentencePosition(const VisiblePosition &);
+WEBCORE_EXPORT VisiblePosition nextSentencePosition(const VisiblePosition &);
// lines
-VisiblePosition startOfLine(const VisiblePosition &);
-VisiblePosition endOfLine(const VisiblePosition &);
-VisiblePosition previousLinePosition(const VisiblePosition&, int lineDirectionPoint, EditableType = ContentIsEditable);
-VisiblePosition nextLinePosition(const VisiblePosition&, int lineDirectionPoint, EditableType = ContentIsEditable);
-bool inSameLine(const VisiblePosition &, const VisiblePosition &);
-bool isStartOfLine(const VisiblePosition &);
-bool isEndOfLine(const VisiblePosition &);
-VisiblePosition logicalStartOfLine(const VisiblePosition &);
-VisiblePosition logicalEndOfLine(const VisiblePosition &);
-VisiblePosition leftBoundaryOfLine(const VisiblePosition&, TextDirection);
-VisiblePosition rightBoundaryOfLine(const VisiblePosition&, TextDirection);
+WEBCORE_EXPORT VisiblePosition startOfLine(const VisiblePosition &);
+WEBCORE_EXPORT VisiblePosition endOfLine(const VisiblePosition &);
+WEBCORE_EXPORT VisiblePosition previousLinePosition(const VisiblePosition&, int lineDirectionPoint, EditableType = ContentIsEditable);
+WEBCORE_EXPORT VisiblePosition nextLinePosition(const VisiblePosition&, int lineDirectionPoint, EditableType = ContentIsEditable);
+WEBCORE_EXPORT bool inSameLine(const VisiblePosition &, const VisiblePosition &);
+WEBCORE_EXPORT bool isStartOfLine(const VisiblePosition &);
+WEBCORE_EXPORT bool isEndOfLine(const VisiblePosition &);
+VisiblePosition logicalStartOfLine(const VisiblePosition &, bool* reachedBoundary = nullptr);
+VisiblePosition logicalEndOfLine(const VisiblePosition &, bool* reachedBoundary = nullptr);
+bool isLogicalEndOfLine(const VisiblePosition &);
+VisiblePosition leftBoundaryOfLine(const VisiblePosition&, TextDirection, bool* reachedBoundary);
+VisiblePosition rightBoundaryOfLine(const VisiblePosition&, TextDirection, bool* reachedBoundary);
// paragraphs (perhaps a misnomer, can be divided by line break elements)
-VisiblePosition startOfParagraph(const VisiblePosition&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
-VisiblePosition endOfParagraph(const VisiblePosition&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
+WEBCORE_EXPORT VisiblePosition startOfParagraph(const VisiblePosition&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
+WEBCORE_EXPORT VisiblePosition endOfParagraph(const VisiblePosition&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
VisiblePosition startOfNextParagraph(const VisiblePosition&);
-VisiblePosition previousParagraphPosition(const VisiblePosition &, int x);
-VisiblePosition nextParagraphPosition(const VisiblePosition &, int x);
-bool isStartOfParagraph(const VisiblePosition &, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
-bool isEndOfParagraph(const VisiblePosition &, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
+WEBCORE_EXPORT VisiblePosition previousParagraphPosition(const VisiblePosition &, int x);
+WEBCORE_EXPORT VisiblePosition nextParagraphPosition(const VisiblePosition &, int x);
+WEBCORE_EXPORT bool isStartOfParagraph(const VisiblePosition &, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
+WEBCORE_EXPORT bool isEndOfParagraph(const VisiblePosition &, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
bool inSameParagraph(const VisiblePosition &, const VisiblePosition &, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
// blocks (true paragraphs; line break elements don't break blocks)
@@ -83,28 +85,42 @@ bool isStartOfBlock(const VisiblePosition &);
bool isEndOfBlock(const VisiblePosition &);
// document
-VisiblePosition startOfDocument(const Node*);
-VisiblePosition endOfDocument(const Node*);
-VisiblePosition startOfDocument(const VisiblePosition &);
-VisiblePosition endOfDocument(const VisiblePosition &);
+WEBCORE_EXPORT VisiblePosition startOfDocument(const Node*);
+WEBCORE_EXPORT VisiblePosition endOfDocument(const Node*);
+WEBCORE_EXPORT VisiblePosition startOfDocument(const VisiblePosition &);
+WEBCORE_EXPORT VisiblePosition endOfDocument(const VisiblePosition &);
bool inSameDocument(const VisiblePosition &, const VisiblePosition &);
-bool isStartOfDocument(const VisiblePosition &);
-bool isEndOfDocument(const VisiblePosition &);
+WEBCORE_EXPORT bool isStartOfDocument(const VisiblePosition &);
+WEBCORE_EXPORT bool isEndOfDocument(const VisiblePosition &);
// editable content
-VisiblePosition startOfEditableContent(const VisiblePosition&);
-VisiblePosition endOfEditableContent(const VisiblePosition&);
-bool isEndOfEditableOrNonEditableContent(const VisiblePosition&);
+WEBCORE_EXPORT VisiblePosition startOfEditableContent(const VisiblePosition&);
+WEBCORE_EXPORT VisiblePosition endOfEditableContent(const VisiblePosition&);
+WEBCORE_EXPORT bool isEndOfEditableOrNonEditableContent(const VisiblePosition&);
-#if PLATFORM(IOS)
-bool atBoundaryOfGranularity(const VisiblePosition&, TextGranularity, SelectionDirection);
-bool withinTextUnitOfGranularity(const VisiblePosition&, TextGranularity, SelectionDirection);
-VisiblePosition positionOfNextBoundaryOfGranularity(const VisiblePosition&, TextGranularity, SelectionDirection);
-PassRefPtr<Range> enclosingTextUnitOfGranularity(const VisiblePosition&, TextGranularity, SelectionDirection);
-int distanceBetweenPositions(const VisiblePosition&, const VisiblePosition&);
-PassRefPtr<Range> wordRangeFromPosition(const VisiblePosition& position);
-VisiblePosition closestWordBoundaryForPosition(const VisiblePosition& position);
-#endif
-} // namespace WebCore
+WEBCORE_EXPORT bool atBoundaryOfGranularity(const VisiblePosition&, TextGranularity, SelectionDirection);
+WEBCORE_EXPORT bool withinTextUnitOfGranularity(const VisiblePosition&, TextGranularity, SelectionDirection);
+WEBCORE_EXPORT VisiblePosition positionOfNextBoundaryOfGranularity(const VisiblePosition&, TextGranularity, SelectionDirection);
+WEBCORE_EXPORT RefPtr<Range> enclosingTextUnitOfGranularity(const VisiblePosition&, TextGranularity, SelectionDirection);
+WEBCORE_EXPORT int distanceBetweenPositions(const VisiblePosition&, const VisiblePosition&);
+WEBCORE_EXPORT RefPtr<Range> wordRangeFromPosition(const VisiblePosition&);
+WEBCORE_EXPORT VisiblePosition closestWordBoundaryForPosition(const VisiblePosition& position);
+WEBCORE_EXPORT void charactersAroundPosition(const VisiblePosition&, UChar32& oneAfter, UChar32& oneBefore, UChar32& twoBefore);
+WEBCORE_EXPORT RefPtr<Range> rangeExpandedAroundPositionByCharacters(const VisiblePosition&, int numberOfCharactersToExpand);
+WEBCORE_EXPORT RefPtr<Range> rangeExpandedByCharactersInDirectionAtWordBoundary(const VisiblePosition&, int numberOfCharactersToExpand, SelectionDirection);
+
+// helper function
+enum BoundarySearchContextAvailability { DontHaveMoreContext, MayHaveMoreContext };
+typedef unsigned (*BoundarySearchFunction)(StringView, unsigned offset, BoundarySearchContextAvailability, bool& needMoreContext);
+unsigned startWordBoundary(StringView, unsigned, BoundarySearchContextAvailability, bool&);
+unsigned endWordBoundary(StringView, unsigned, BoundarySearchContextAvailability, bool&);
+unsigned startSentenceBoundary(StringView, unsigned, BoundarySearchContextAvailability, bool&);
+unsigned endSentenceBoundary(StringView, unsigned, BoundarySearchContextAvailability, bool&);
+unsigned suffixLengthForRange(const Range&, Vector<UChar, 1024>&);
+unsigned prefixLengthForRange(const Range&, Vector<UChar, 1024>&);
+unsigned backwardSearchForBoundaryWithTextIterator(SimplifiedBackwardsTextIterator&, Vector<UChar, 1024>&, unsigned, BoundarySearchFunction);
+unsigned forwardSearchForBoundaryWithTextIterator(TextIterator&, Vector<UChar, 1024>&, unsigned, BoundarySearchFunction);
+Node* findStartOfParagraph(Node*, Node*, Node*, int&, Position::AnchorType&, EditingBoundaryCrossingRule);
+Node* findEndOfParagraph(Node*, Node*, Node*, int&, Position::AnchorType&, EditingBoundaryCrossingRule);
-#endif // VisibleUnits_h
+} // namespace WebCore
diff --git a/Source/WebCore/editing/WrapContentsInDummySpanCommand.cpp b/Source/WebCore/editing/WrapContentsInDummySpanCommand.cpp
index f42d0778e..4006fa8ed 100644
--- a/Source/WebCore/editing/WrapContentsInDummySpanCommand.cpp
+++ b/Source/WebCore/editing/WrapContentsInDummySpanCommand.cpp
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -27,28 +27,25 @@
#include "WrapContentsInDummySpanCommand.h"
#include "ApplyStyleCommand.h"
-#include "ExceptionCodePlaceholder.h"
namespace WebCore {
-WrapContentsInDummySpanCommand::WrapContentsInDummySpanCommand(PassRefPtr<Element> element)
- : SimpleEditCommand(element->document())
+WrapContentsInDummySpanCommand::WrapContentsInDummySpanCommand(Element& element)
+ : SimpleEditCommand(element.document())
, m_element(element)
{
- ASSERT(m_element);
}
void WrapContentsInDummySpanCommand::executeApply()
{
- Vector<RefPtr<Node>> children;
+ Vector<Ref<Node>> children;
for (Node* child = m_element->firstChild(); child; child = child->nextSibling())
- children.append(child);
+ children.append(*child);
- size_t size = children.size();
- for (size_t i = 0; i < size; ++i)
- m_dummySpan->appendChild(children[i].release(), IGNORE_EXCEPTION);
+ for (auto& child : children)
+ m_dummySpan->appendChild(child);
- m_element->appendChild(m_dummySpan.get(), IGNORE_EXCEPTION);
+ m_element->appendChild(*m_dummySpan);
}
void WrapContentsInDummySpanCommand::doApply()
@@ -60,26 +57,21 @@ void WrapContentsInDummySpanCommand::doApply()
void WrapContentsInDummySpanCommand::doUnapply()
{
- ASSERT(m_element);
-
if (!m_dummySpan || !m_element->hasEditableStyle())
return;
- Vector<RefPtr<Node>> children;
+ Vector<Ref<Node>> children;
for (Node* child = m_dummySpan->firstChild(); child; child = child->nextSibling())
- children.append(child);
+ children.append(*child);
- size_t size = children.size();
- for (size_t i = 0; i < size; ++i)
- m_element->appendChild(children[i].release(), IGNORE_EXCEPTION);
+ for (auto& child : children)
+ m_element->appendChild(child);
- m_dummySpan->remove(IGNORE_EXCEPTION);
+ m_dummySpan->remove();
}
void WrapContentsInDummySpanCommand::doReapply()
-{
- ASSERT(m_element);
-
+{
if (!m_dummySpan || !m_element->hasEditableStyle())
return;
@@ -89,7 +81,7 @@ void WrapContentsInDummySpanCommand::doReapply()
#ifndef NDEBUG
void WrapContentsInDummySpanCommand::getNodesInCommand(HashSet<Node*>& nodes)
{
- addNodeAndDescendants(m_element.get(), nodes);
+ addNodeAndDescendants(m_element.ptr(), nodes);
addNodeAndDescendants(m_dummySpan.get(), nodes);
}
#endif
diff --git a/Source/WebCore/editing/WrapContentsInDummySpanCommand.h b/Source/WebCore/editing/WrapContentsInDummySpanCommand.h
index 5a9f930fb..fbd0a1d6f 100644
--- a/Source/WebCore/editing/WrapContentsInDummySpanCommand.h
+++ b/Source/WebCore/editing/WrapContentsInDummySpanCommand.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,8 +23,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef WrapContentsInDummySpanCommand_h
-#define WrapContentsInDummySpanCommand_h
+#pragma once
#include "EditCommand.h"
@@ -34,27 +33,25 @@ class HTMLElement;
class WrapContentsInDummySpanCommand : public SimpleEditCommand {
public:
- static PassRefPtr<WrapContentsInDummySpanCommand> create(PassRefPtr<Element> element)
+ static Ref<WrapContentsInDummySpanCommand> create(Element& element)
{
- return adoptRef(new WrapContentsInDummySpanCommand(element));
+ return adoptRef(*new WrapContentsInDummySpanCommand(element));
}
private:
- explicit WrapContentsInDummySpanCommand(PassRefPtr<Element>);
+ explicit WrapContentsInDummySpanCommand(Element&);
- virtual void doApply() override;
- virtual void doUnapply() override;
- virtual void doReapply() override;
+ void doApply() override;
+ void doUnapply() override;
+ void doReapply() override;
void executeApply();
#ifndef NDEBUG
- virtual void getNodesInCommand(HashSet<Node*>&) override;
+ void getNodesInCommand(HashSet<Node*>&) override;
#endif
- RefPtr<Element> m_element;
+ Ref<Element> m_element;
RefPtr<HTMLElement> m_dummySpan;
};
} // namespace WebCore
-
-#endif // WrapContentsInDummySpanCommand_h
diff --git a/Source/WebCore/editing/WritingDirection.h b/Source/WebCore/editing/WritingDirection.h
index b3cff6668..768b3c25d 100644
--- a/Source/WebCore/editing/WritingDirection.h
+++ b/Source/WebCore/editing/WritingDirection.h
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
diff --git a/Source/WebCore/editing/atk/FrameSelectionAtk.cpp b/Source/WebCore/editing/atk/FrameSelectionAtk.cpp
index 03a23a7e4..51839a990 100644
--- a/Source/WebCore/editing/atk/FrameSelectionAtk.cpp
+++ b/Source/WebCore/editing/atk/FrameSelectionAtk.cpp
@@ -25,14 +25,10 @@
#include "AXObjectCache.h"
#include "Document.h"
#include "Frame.h"
+#include "RenderListItem.h"
#include "WebKitAccessibleWrapperAtk.h"
-
-#if PLATFORM(EFL)
#include <glib.h>
-#else
-#include <gtk/gtk.h>
-#endif
-
+#include <wtf/NeverDestroyed.h>
#include <wtf/RefPtr.h>
namespace WebCore {
@@ -43,29 +39,34 @@ static void emitTextSelectionChange(AccessibilityObject* object, VisibleSelectio
if (!axObject || !ATK_IS_TEXT(axObject))
return;
+ // We need to adjust the offset for the list item marker in Left-To-Right text because
+ // the list item marker is exposed through the text of the accessible list item rather
+ // than through a separate accessible object.
+ RenderObject* renderer = object->renderer();
+ if (is<RenderListItem>(renderer) && renderer->style().direction() == LTR)
+ offset += downcast<RenderListItem>(*renderer).markerTextWithSuffix().length();
+
g_signal_emit_by_name(axObject, "text-caret-moved", offset);
if (selection.isRange())
g_signal_emit_by_name(axObject, "text-selection-changed");
}
-static void maybeEmitTextFocusChange(PassRefPtr<AccessibilityObject> prpObject)
+static void maybeEmitTextFocusChange(RefPtr<AccessibilityObject>&& object)
{
// This static variable is needed to keep track of the old object
// as per previous calls to this function, in order to properly
// decide whether to emit some signals or not.
- DEFINE_STATIC_LOCAL(RefPtr<AccessibilityObject>, oldObject, ());
-
- RefPtr<AccessibilityObject> object = prpObject;
+ static NeverDestroyed<RefPtr<AccessibilityObject>> oldObject;
// Ensure the oldObject belongs to the same document that the
// current object so further comparisons make sense. Otherwise,
// just reset oldObject to 0 so it won't be taken into account in
// the immediately following call to this function.
- if (object && oldObject && oldObject->document() != object->document())
- oldObject = 0;
+ if (object && oldObject.get() && oldObject.get()->document() != object->document())
+ oldObject.get() = nullptr;
AtkObject* axObject = object ? object->wrapper() : 0;
- AtkObject* oldAxObject = oldObject ? oldObject->wrapper() : 0;
+ AtkObject* oldAxObject = oldObject.get() ? oldObject.get()->wrapper() : nullptr;
if (axObject != oldAxObject) {
if (oldAxObject && ATK_IS_TEXT(oldAxObject)) {
@@ -79,11 +80,11 @@ static void maybeEmitTextFocusChange(PassRefPtr<AccessibilityObject> prpObject)
}
// Update pointer to last focused object.
- oldObject = object;
+ oldObject.get() = WTFMove(object);
}
-void FrameSelection::notifyAccessibilityForSelectionChange()
+void FrameSelection::notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&)
{
if (!AXObjectCache::accessibilityEnabled())
return;
@@ -91,12 +92,15 @@ void FrameSelection::notifyAccessibilityForSelectionChange()
if (!m_selection.start().isNotNull() || !m_selection.end().isNotNull())
return;
- RenderObject* focusedNode = m_selection.end().containerNode()->renderer();
+ Node* focusedNode = m_selection.end().containerNode();
+ if (!focusedNode)
+ return;
+
AXObjectCache* cache = m_frame->document()->existingAXObjectCache();
if (!cache)
return;
- AccessibilityObject* accessibilityObject = cache->getOrCreate(focusedNode);
+ AccessibilityObject* accessibilityObject = cache->getOrCreate(focusedNode->renderer());
if (!accessibilityObject)
return;
@@ -106,7 +110,7 @@ void FrameSelection::notifyAccessibilityForSelectionChange()
return;
emitTextSelectionChange(object.get(), m_selection, offset);
- maybeEmitTextFocusChange(object.release());
+ maybeEmitTextFocusChange(WTFMove(object));
}
} // namespace WebCore
diff --git a/Source/WebCore/editing/gtk/EditorGtk.cpp b/Source/WebCore/editing/gtk/EditorGtk.cpp
new file mode 100644
index 000000000..d16a3fec3
--- /dev/null
+++ b/Source/WebCore/editing/gtk/EditorGtk.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014 Igalia S.L.
+ *
+ * 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 "Editor.h"
+
+#include "Blob.h"
+#include "CachedImage.h"
+#include "DOMURL.h"
+#include "DocumentFragment.h"
+#include "Frame.h"
+#include "HTMLEmbedElement.h"
+#include "HTMLImageElement.h"
+#include "HTMLInputElement.h"
+#include "HTMLNames.h"
+#include "HTMLObjectElement.h"
+#include "HTMLParserIdioms.h"
+#include "Pasteboard.h"
+#include "RenderImage.h"
+#include "SVGElement.h"
+#include "SVGImageElement.h"
+#include "SelectionData.h"
+#include "XLinkNames.h"
+#include "markup.h"
+
+namespace WebCore {
+
+static RefPtr<DocumentFragment> createFragmentFromPasteboardData(Pasteboard& pasteboard, Frame& frame, Range& range, bool allowPlainText, bool& chosePlainText)
+{
+ chosePlainText = false;
+
+ if (!pasteboard.hasData())
+ return nullptr;
+
+ const auto& selection = pasteboard.selectionData();
+ if (selection.hasImage()) {
+ Vector<uint8_t> buffer;
+ auto status = cairo_surface_write_to_png_stream(selection.image()->nativeImage().get(), [](void* output, const unsigned char* data, unsigned size) {
+ if (!reinterpret_cast<Vector<uint8_t>*>(output)->tryAppend(data, size))
+ return CAIRO_STATUS_WRITE_ERROR;
+ return CAIRO_STATUS_SUCCESS;
+ }, &buffer);
+ if (status == CAIRO_STATUS_SUCCESS) {
+ auto blob = Blob::create(WTFMove(buffer), "image/png");
+ return frame.editor().createFragmentForImageAndURL(DOMURL::createObjectURL(*frame.document(), blob));
+ }
+ }
+
+ if (selection.hasMarkup() && frame.document())
+ return createFragmentFromMarkup(*frame.document(), selection.markup(), emptyString(), DisallowScriptingAndPluginContent);
+
+ if (!allowPlainText)
+ return nullptr;
+
+ if (selection.hasText()) {
+ chosePlainText = true;
+ return createFragmentFromText(range, selection.text());
+ }
+
+ return nullptr;
+}
+
+void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText, MailBlockquoteHandling mailBlockquoteHandling)
+{
+ RefPtr<Range> range = selectedRange();
+ if (!range)
+ return;
+
+ bool chosePlainText;
+ RefPtr<DocumentFragment> fragment = createFragmentFromPasteboardData(*pasteboard, m_frame, *range, allowPlainText, chosePlainText);
+ if (fragment && shouldInsertFragment(fragment, range, EditorInsertAction::Pasted))
+ pasteAsFragment(fragment.releaseNonNull(), canSmartReplaceWithPasteboard(*pasteboard), chosePlainText, mailBlockquoteHandling);
+}
+
+static const AtomicString& elementURL(Element& element)
+{
+ if (is<HTMLImageElement>(element) || is<HTMLInputElement>(element))
+ return element.attributeWithoutSynchronization(HTMLNames::srcAttr);
+ if (is<SVGImageElement>(element))
+ return element.attributeWithoutSynchronization(XLinkNames::hrefAttr);
+ if (is<HTMLEmbedElement>(element) || is<HTMLObjectElement>(element))
+ return element.imageSourceURL();
+ return nullAtom;
+}
+
+static bool getImageForElement(Element& element, RefPtr<Image>& image)
+{
+ auto* renderer = element.renderer();
+ if (!is<RenderImage>(renderer))
+ return false;
+
+ CachedImage* cachedImage = downcast<RenderImage>(*renderer).cachedImage();
+ if (!cachedImage || cachedImage->errorOccurred())
+ return false;
+
+ image = cachedImage->imageForRenderer(renderer);
+ return image;
+}
+
+void Editor::writeImageToPasteboard(Pasteboard& pasteboard, Element& imageElement, const URL&, const String& title)
+{
+ PasteboardImage pasteboardImage;
+
+ if (!getImageForElement(imageElement, pasteboardImage.image))
+ return;
+ ASSERT(pasteboardImage.image);
+
+ pasteboardImage.url.url = imageElement.document().completeURL(stripLeadingAndTrailingHTMLSpaces(elementURL(imageElement)));
+ pasteboardImage.url.title = title;
+ pasteboardImage.url.markup = createMarkup(imageElement, IncludeNode, nullptr, ResolveAllURLs);
+ pasteboard.write(pasteboardImage);
+}
+
+void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
+{
+ PasteboardWebContent pasteboardContent;
+ pasteboardContent.canSmartCopyOrDelete = canSmartCopyOrDelete();
+ pasteboardContent.text = selectedTextForDataTransfer();
+ pasteboardContent.markup = createMarkup(*selectedRange(), nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs);
+ pasteboard.write(pasteboardContent);
+}
+
+RefPtr<DocumentFragment> Editor::webContentFromPasteboard(Pasteboard& pasteboard, Range& context, bool allowPlainText, bool& chosePlainText)
+{
+ return createFragmentFromPasteboardData(pasteboard, m_frame, context, allowPlainText, chosePlainText);
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/editing/htmlediting.cpp b/Source/WebCore/editing/htmlediting.cpp
index 9e721c6da..f40341c9e 100644
--- a/Source/WebCore/editing/htmlediting.cpp
+++ b/Source/WebCore/editing/htmlediting.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2007, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -29,39 +29,49 @@
#include "AXObjectCache.h"
#include "Document.h"
#include "Editor.h"
-#include "ExceptionCodePlaceholder.h"
#include "Frame.h"
-#include "HTMLBRElement.h"
+#include "HTMLBodyElement.h"
+#include "HTMLDListElement.h"
#include "HTMLDivElement.h"
#include "HTMLElementFactory.h"
#include "HTMLInterchange.h"
#include "HTMLLIElement.h"
#include "HTMLNames.h"
#include "HTMLOListElement.h"
-#include "HTMLObjectElement.h"
#include "HTMLParagraphElement.h"
+#include "HTMLSpanElement.h"
#include "HTMLTableElement.h"
#include "HTMLTextFormControlElement.h"
#include "HTMLUListElement.h"
#include "NodeTraversal.h"
#include "PositionIterator.h"
+#include "RenderBlock.h"
#include "RenderElement.h"
+#include "RenderTableCell.h"
#include "ShadowRoot.h"
#include "Text.h"
+#include "TextIterator.h"
#include "VisibleUnits.h"
#include <wtf/Assertions.h>
#include <wtf/StdLibExtras.h>
+#include <wtf/text/StringBuilder.h>
#include <wtf/unicode/CharacterNames.h>
namespace WebCore {
using namespace HTMLNames;
-// Atomic means that the node has no children, or has children which are ignored for the
-// purposes of editing.
-bool isAtomicNode(const Node *node)
+static bool isVisiblyAdjacent(const Position&, const Position&);
+
+bool canHaveChildrenForEditing(const Node& node)
{
- return node && (!node->hasChildNodes() || editingIgnoresContent(node));
+ return !is<Text>(node) && node.canContainRangeEndPoint();
+}
+
+// Atomic means that the node has no children, or has children which are ignored for the purposes of editing.
+bool isAtomicNode(const Node* node)
+{
+ return node && (!node->hasChildNodes() || editingIgnoresContent(*node));
}
// Compare two positions, taking into account the possibility that one or both
@@ -70,16 +80,15 @@ int comparePositions(const Position& a, const Position& b)
{
TreeScope* commonScope = commonTreeScope(a.containerNode(), b.containerNode());
- ASSERT(commonScope);
if (!commonScope)
return 0;
- Node* nodeA = commonScope->ancestorInThisScope(a.containerNode());
+ Node* nodeA = commonScope->ancestorNodeInThisScope(a.containerNode());
ASSERT(nodeA);
bool hasDescendentA = nodeA != a.containerNode();
int offsetA = hasDescendentA ? 0 : a.computeOffsetInContainerNode();
- Node* nodeB = commonScope->ancestorInThisScope(b.containerNode());
+ Node* nodeB = commonScope->ancestorNodeInThisScope(b.containerNode());
ASSERT(nodeB);
bool hasDescendentB = nodeB != b.containerNode();
int offsetB = hasDescendentB ? 0 : b.computeOffsetInContainerNode();
@@ -92,7 +101,10 @@ int comparePositions(const Position& a, const Position& b)
bias = 1;
}
- int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB, IGNORE_EXCEPTION);
+ auto comparisonResult = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB);
+ if (comparisonResult.hasException())
+ return bias;
+ auto result = comparisonResult.releaseReturnValue();
return result ? result : bias;
}
@@ -101,202 +113,230 @@ int comparePositions(const VisiblePosition& a, const VisiblePosition& b)
return comparePositions(a.deepEquivalent(), b.deepEquivalent());
}
-Node* highestEditableRoot(const Position& position, EditableType editableType)
+ContainerNode* highestEditableRoot(const Position& position, EditableType editableType)
{
- Node* node = position.deprecatedNode();
- if (!node)
- return 0;
-
- Node* highestEditableRoot = editableRootForPosition(position, editableType);
+ ContainerNode* highestEditableRoot = editableRootForPosition(position, editableType);
if (!highestEditableRoot)
- return 0;
+ return nullptr;
- node = highestEditableRoot;
- while (!node->hasTagName(bodyTag)) {
+ for (ContainerNode* node = highestEditableRoot; !is<HTMLBodyElement>(*node); ) {
node = node->parentNode();
if (!node)
break;
- if (node->hasEditableStyle(editableType))
+ // FIXME: Can this ever be a Document or DocumentFragment? If not, this should return Element* instead.
+ if (hasEditableStyle(*node, editableType))
highestEditableRoot = node;
}
return highestEditableRoot;
}
-Node* lowestEditableAncestor(Node* node)
+Element* lowestEditableAncestor(Node* node)
{
- if (!node)
- return 0;
-
- while (node) {
+ for (; node; node = node->parentNode()) {
if (node->hasEditableStyle())
return node->rootEditableElement();
- if (node->hasTagName(bodyTag))
+ if (is<HTMLBodyElement>(*node))
break;
- node = node->parentNode();
}
-
- return 0;
+ return nullptr;
}
-bool isEditablePosition(const Position& p, EditableType editableType, EUpdateStyle updateStyle)
+static bool isEditableToAccessibility(const Node& node)
{
- Node* node = p.deprecatedNode();
- if (!node)
+ ASSERT(AXObjectCache::accessibilityEnabled());
+ ASSERT(node.document().existingAXObjectCache());
+
+ if (auto* cache = node.document().existingAXObjectCache())
+ return cache->rootAXEditableElement(&node);
+
+ return false;
+}
+
+static bool computeEditability(const Node& node, EditableType editableType, Node::ShouldUpdateStyle shouldUpdateStyle)
+{
+ if (node.computeEditability(Node::UserSelectAllIsAlwaysNonEditable, shouldUpdateStyle) != Node::Editability::ReadOnly)
+ return true;
+
+ switch (editableType) {
+ case ContentIsEditable:
return false;
- if (updateStyle == UpdateStyle)
- node->document().updateLayoutIgnorePendingStylesheets();
- else
- ASSERT(updateStyle == DoNotUpdateStyle);
+ case HasEditableAXRole:
+ return isEditableToAccessibility(node);
+ }
+ ASSERT_NOT_REACHED();
+ return false;
+}
- if (node->renderer() && node->renderer()->isTable())
- node = node->parentNode();
-
- return node->hasEditableStyle(editableType);
+bool hasEditableStyle(const Node& node, EditableType editableType)
+{
+ return computeEditability(node, editableType, Node::ShouldUpdateStyle::DoNotUpdate);
}
-bool isAtUnsplittableElement(const Position& pos)
+bool isEditableNode(const Node& node)
{
- Node* node = pos.deprecatedNode();
- return (node == editableRootForPosition(pos) || node == enclosingNodeOfType(pos, &isTableCell));
+ return computeEditability(node, ContentIsEditable, Node::ShouldUpdateStyle::Update);
}
-
-
-bool isRichlyEditablePosition(const Position& p, EditableType editableType)
+
+bool isEditablePosition(const Position& position, EditableType editableType)
{
- Node* node = p.deprecatedNode();
- if (!node)
- return false;
-
- if (node->renderer() && node->renderer()->isTable())
- node = node->parentNode();
-
- return node->hasRichlyEditableStyle(editableType);
+ Node* node = position.containerNode();
+ return node && computeEditability(*node, editableType, Node::ShouldUpdateStyle::Update);
}
-Element* editableRootForPosition(const Position& p, EditableType editableType)
+bool isAtUnsplittableElement(const Position& position)
{
- Node* node = p.containerNode();
+ Node* node = position.containerNode();
+ return node == editableRootForPosition(position) || node == enclosingNodeOfType(position, isTableCell);
+}
+
+bool isRichlyEditablePosition(const Position& position)
+{
+ auto* node = position.containerNode();
+ return node && node->hasRichlyEditableStyle();
+}
+
+Element* editableRootForPosition(const Position& position, EditableType editableType)
+{
+ Node* node = position.containerNode();
if (!node)
- return 0;
-
- if (node->renderer() && node->renderer()->isTable())
- node = node->parentNode();
-
- return node->rootEditableElement(editableType);
+ return nullptr;
+
+ switch (editableType) {
+ case HasEditableAXRole:
+ if (auto* cache = node->document().existingAXObjectCache())
+ return const_cast<Element*>(cache->rootAXEditableElement(node));
+ FALLTHROUGH;
+ case ContentIsEditable:
+ return node->rootEditableElement();
+ }
+ return nullptr;
}
// Finds the enclosing element until which the tree can be split.
// When a user hits ENTER, he/she won't expect this element to be split into two.
// You may pass it as the second argument of splitTreeToNode.
-Element* unsplittableElementForPosition(const Position& p)
+Element* unsplittableElementForPosition(const Position& position)
{
// Since enclosingNodeOfType won't search beyond the highest root editable node,
// this code works even if the closest table cell was outside of the root editable node.
- Element* enclosingCell = toElement(enclosingNodeOfType(p, &isTableCell));
- if (enclosingCell)
+ if (auto* enclosingCell = downcast<Element>(enclosingNodeOfType(position, &isTableCell)))
return enclosingCell;
-
- return editableRootForPosition(p);
+ return editableRootForPosition(position);
}
Position nextCandidate(const Position& position)
{
- PositionIterator p = position;
- while (!p.atEnd()) {
- p.increment();
- if (p.isCandidate())
- return p;
+ for (PositionIterator nextPosition = position; !nextPosition.atEnd(); ) {
+ nextPosition.increment();
+ if (nextPosition.isCandidate())
+ return nextPosition;
}
- return Position();
+ return { };
}
Position nextVisuallyDistinctCandidate(const Position& position)
{
- Position p = position;
- Position downstreamStart = p.downstream();
- while (!p.atEndOfTree()) {
- p = p.next(Character);
- if (p.isCandidate() && p.downstream() != downstreamStart)
- return p;
+ // FIXME: Use PositionIterator instead.
+ Position nextPosition = position;
+ Position downstreamStart = nextPosition.downstream();
+ while (!nextPosition.atEndOfTree()) {
+ nextPosition = nextPosition.next(Character);
+ if (nextPosition.isCandidate() && nextPosition.downstream() != downstreamStart)
+ return nextPosition;
+ if (auto* node = nextPosition.containerNode()) {
+ if (!node->renderer())
+ nextPosition = lastPositionInOrAfterNode(node);
+ }
}
- return Position();
+ return { };
}
Position previousCandidate(const Position& position)
{
- PositionIterator p = position;
- while (!p.atStart()) {
- p.decrement();
- if (p.isCandidate())
- return p;
+ PositionIterator previousPosition = position;
+ while (!previousPosition.atStart()) {
+ previousPosition.decrement();
+ if (previousPosition.isCandidate())
+ return previousPosition;
}
- return Position();
+ return { };
}
Position previousVisuallyDistinctCandidate(const Position& position)
{
- Position p = position;
- Position downstreamStart = p.downstream();
- while (!p.atStartOfTree()) {
- p = p.previous(Character);
- if (p.isCandidate() && p.downstream() != downstreamStart)
- return p;
+ // FIXME: Use PositionIterator instead.
+ Position previousPosition = position;
+ Position downstreamStart = previousPosition.downstream();
+ while (!previousPosition.atStartOfTree()) {
+ previousPosition = previousPosition.previous(Character);
+ if (previousPosition.isCandidate() && previousPosition.downstream() != downstreamStart)
+ return previousPosition;
+ if (auto* node = previousPosition.containerNode()) {
+ if (!node->renderer())
+ previousPosition = firstPositionInOrBeforeNode(node);
+ }
}
- return Position();
+ return { };
}
-VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
+Position firstEditablePositionAfterPositionInRoot(const Position& position, ContainerNode* highestRoot)
{
+ if (!highestRoot)
+ return { };
+
// position falls before highestRoot.
if (comparePositions(position, firstPositionInNode(highestRoot)) == -1 && highestRoot->hasEditableStyle())
return firstPositionInNode(highestRoot);
- Position p = position;
+ Position candidate = position;
if (&position.deprecatedNode()->treeScope() != &highestRoot->treeScope()) {
- Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(p.deprecatedNode());
+ auto* shadowAncestor = highestRoot->treeScope().ancestorNodeInThisScope(position.deprecatedNode());
if (!shadowAncestor)
- return VisiblePosition();
+ return { };
- p = positionAfterNode(shadowAncestor);
+ candidate = positionAfterNode(shadowAncestor);
}
- while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot))
- p = isAtomicNode(p.deprecatedNode()) ? positionInParentAfterNode(p.deprecatedNode()) : nextVisuallyDistinctCandidate(p);
-
- if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot))
- return VisiblePosition();
-
- return VisiblePosition(p);
+ while (candidate.deprecatedNode() && !isEditablePosition(candidate) && candidate.deprecatedNode()->isDescendantOf(*highestRoot))
+ candidate = isAtomicNode(candidate.deprecatedNode()) ? positionInParentAfterNode(candidate.deprecatedNode()) : nextVisuallyDistinctCandidate(candidate);
+
+ if (candidate.deprecatedNode() && candidate.deprecatedNode() != highestRoot && !candidate.deprecatedNode()->isDescendantOf(*highestRoot))
+ return { };
+
+ return candidate;
}
-VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
+Position lastEditablePositionBeforePositionInRoot(const Position& position, ContainerNode* highestRoot)
{
+ if (!highestRoot)
+ return { };
+
// When position falls after highestRoot, the result is easy to compute.
if (comparePositions(position, lastPositionInNode(highestRoot)) == 1)
return lastPositionInNode(highestRoot);
- Position p = position;
+ Position candidate = position;
if (&position.deprecatedNode()->treeScope() != &highestRoot->treeScope()) {
- Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(p.deprecatedNode());
+ auto* shadowAncestor = highestRoot->treeScope().ancestorNodeInThisScope(position.deprecatedNode());
if (!shadowAncestor)
- return VisiblePosition();
+ return { };
- p = firstPositionInOrBeforeNode(shadowAncestor);
+ candidate = firstPositionInOrBeforeNode(shadowAncestor);
}
+
+ while (candidate.deprecatedNode() && !isEditablePosition(candidate) && candidate.deprecatedNode()->isDescendantOf(*highestRoot))
+ candidate = isAtomicNode(candidate.deprecatedNode()) ? positionInParentBeforeNode(candidate.deprecatedNode()) : previousVisuallyDistinctCandidate(candidate);
- while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot))
- p = isAtomicNode(p.deprecatedNode()) ? positionInParentBeforeNode(p.deprecatedNode()) : previousVisuallyDistinctCandidate(p);
-
- if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot))
- return VisiblePosition();
+ if (candidate.deprecatedNode() && candidate.deprecatedNode() != highestRoot && !candidate.deprecatedNode()->isDescendantOf(*highestRoot))
+ return { };
- return VisiblePosition(p);
+ return candidate;
}
-// FIXME: The method name, comment, and code say three different things here!
+// FIXME: The function name, comment, and code say three different things here!
// Whether or not content before and after this node will collapse onto the same line as it.
bool isBlock(const Node* node)
{
@@ -315,7 +355,7 @@ bool isInline(const Node* node)
Element* enclosingBlock(Node* node, EditingBoundaryCrossingRule rule)
{
Node* enclosingNode = enclosingNodeOfType(firstPositionInOrBeforeNode(node), isBlock, rule);
- return enclosingNode && enclosingNode->isElementNode() ? toElement(enclosingNode) : 0;
+ return is<Element>(enclosingNode) ? downcast<Element>(enclosingNode) : nullptr;
}
TextDirection directionOfEnclosingBlock(const Position& position)
@@ -333,77 +373,89 @@ TextDirection directionOfEnclosingBlock(const Position& position)
// in a node. It returns 1 for some elements even though they do not have children, which
// creates technically invalid DOM Positions. Be sure to call parentAnchoredEquivalent
// on a Position before using it to create a DOM Range, or an exception will be thrown.
-int lastOffsetForEditing(const Node* node)
+int lastOffsetForEditing(const Node& node)
{
- ASSERT(node);
- if (!node)
- return 0;
- if (node->offsetInCharacters())
- return node->maxCharacterOffset();
+ if (node.offsetInCharacters())
+ return node.maxCharacterOffset();
- if (node->hasChildNodes())
- return node->childNodeCount();
+ if (node.hasChildNodes())
+ return node.countChildNodes();
- // NOTE: This should preempt the childNodeCount for, e.g., select nodes
+ // NOTE: This should preempt the countChildNodes() for, e.g., select nodes.
+ // FIXME: What does the comment above mean?
if (editingIgnoresContent(node))
return 1;
return 0;
}
+bool isAmbiguousBoundaryCharacter(UChar character)
+{
+ // These are characters that can behave as word boundaries, but can appear within words.
+ // If they are just typed, i.e. if they are immediately followed by a caret, we want to delay text checking until the next character has been typed.
+ // FIXME: this is required until <rdar://problem/6853027> is fixed and text checking can do this for us.
+ return character == '\'' || character == '@' || character == rightSingleQuotationMark || character == hebrewPunctuationGershayim;
+}
+
String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph)
{
- Vector<UChar> rebalancedString;
- append(rebalancedString, string);
+ StringBuilder rebalancedString;
bool previousCharacterWasSpace = false;
- for (size_t i = 0; i < rebalancedString.size(); i++) {
- if (!isWhitespace(rebalancedString[i])) {
+ unsigned length = string.length();
+ for (unsigned i = 0; i < length; ++i) {
+ auto character = string[i];
+ if (!deprecatedIsEditingWhitespace(character)) {
previousCharacterWasSpace = false;
continue;
}
-
- if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i + 1 == rebalancedString.size() && endIsEndOfParagraph)) {
- rebalancedString[i] = noBreakSpace;
+ LChar selectedWhitespaceCharacter;
+ if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i == length - 1 && endIsEndOfParagraph)) {
+ selectedWhitespaceCharacter = noBreakSpace;
previousCharacterWasSpace = false;
} else {
- rebalancedString[i] = ' ';
+ selectedWhitespaceCharacter = ' ';
previousCharacterWasSpace = true;
}
-
+ if (character == selectedWhitespaceCharacter)
+ continue;
+ rebalancedString.reserveCapacity(length);
+ rebalancedString.append(string, rebalancedString.length(), i - rebalancedString.length());
+ rebalancedString.append(selectedWhitespaceCharacter);
}
- return String::adopt(rebalancedString);
+ if (rebalancedString.isEmpty())
+ return string;
+
+ rebalancedString.reserveCapacity(length);
+ rebalancedString.append(string, rebalancedString.length(), length - rebalancedString.length());
+ return rebalancedString.toString();
}
-bool isTableStructureNode(const Node *node)
+bool isTableStructureNode(const Node* node)
{
- RenderObject* renderer = node->renderer();
- return (renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol()));
+ auto* renderer = node->renderer();
+ return renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol());
}
const String& nonBreakingSpaceString()
{
- DEFINE_STATIC_LOCAL(String, nonBreakingSpaceString, (&noBreakSpace, 1));
+ static NeverDestroyed<String> nonBreakingSpaceString(&noBreakSpace, 1);
return nonBreakingSpaceString;
}
-// FIXME: need to dump this
-bool isSpecialElement(const Node *n)
+static bool isSpecialHTMLElement(const Node* node)
{
- if (!n)
- return false;
-
- if (!n->isHTMLElement())
+ if (!is<HTMLElement>(node))
return false;
- if (n->isLink())
+ if (downcast<HTMLElement>(*node).isLink())
return true;
- RenderObject* renderer = n->renderer();
+ auto* renderer = downcast<HTMLElement>(*node).renderer();
if (!renderer)
return false;
-
+
if (renderer->style().display() == TABLE || renderer->style().display() == INLINE_TABLE)
return true;
@@ -412,162 +464,140 @@ bool isSpecialElement(const Node *n)
if (renderer->style().position() != StaticPosition)
return true;
-
- return false;
-}
-static Node* firstInSpecialElement(const Position& pos)
-{
- Node* rootEditableElement = pos.containerNode()->rootEditableElement();
- for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
- if (isSpecialElement(n)) {
- VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
- VisiblePosition firstInElement = VisiblePosition(firstPositionInOrBeforeNode(n), DOWNSTREAM);
- if (isTableElement(n) && vPos == firstInElement.next())
- return n;
- if (vPos == firstInElement)
- return n;
- }
- return 0;
+ return false;
}
-static Node* lastInSpecialElement(const Position& pos)
+static HTMLElement* firstInSpecialElement(const Position& position)
{
- Node* rootEditableElement = pos.containerNode()->rootEditableElement();
- for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
- if (isSpecialElement(n)) {
- VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
- VisiblePosition lastInElement = VisiblePosition(lastPositionInOrAfterNode(n), DOWNSTREAM);
- if (isTableElement(n) && vPos == lastInElement.previous())
- return n;
- if (vPos == lastInElement)
- return n;
- }
- return 0;
+ auto* rootEditableElement = position.containerNode()->rootEditableElement();
+ for (Node* node = position.deprecatedNode(); node && node->rootEditableElement() == rootEditableElement; node = node->parentNode()) {
+ if (!isSpecialHTMLElement(node))
+ continue;
+ VisiblePosition vPos(position, DOWNSTREAM);
+ VisiblePosition firstInElement(firstPositionInOrBeforeNode(node), DOWNSTREAM);
+ if ((isRenderedTable(node) && vPos == firstInElement.next()) || vPos == firstInElement)
+ return &downcast<HTMLElement>(*node);
+ }
+ return nullptr;
}
-bool isFirstVisiblePositionInSpecialElement(const Position& pos)
+static HTMLElement* lastInSpecialElement(const Position& position)
{
- return firstInSpecialElement(pos);
+ auto* rootEditableElement = position.containerNode()->rootEditableElement();
+ for (Node* node = position.deprecatedNode(); node && node->rootEditableElement() == rootEditableElement; node = node->parentNode()) {
+ if (!isSpecialHTMLElement(node))
+ continue;
+ VisiblePosition vPos(position, DOWNSTREAM);
+ VisiblePosition lastInElement(lastPositionInOrAfterNode(node), DOWNSTREAM);
+ if ((isRenderedTable(node) && vPos == lastInElement.previous()) || vPos == lastInElement)
+ return &downcast<HTMLElement>(*node);
+ }
+ return nullptr;
}
-Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement)
+Position positionBeforeContainingSpecialElement(const Position& position, HTMLElement** containingSpecialElement)
{
- Node* n = firstInSpecialElement(pos);
- if (!n)
- return pos;
- Position result = positionInParentBeforeNode(n);
- if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
- return pos;
+ auto* element = firstInSpecialElement(position);
+ if (!element)
+ return position;
+ Position result = positionInParentBeforeNode(element);
+ if (result.isNull() || result.deprecatedNode()->rootEditableElement() != position.deprecatedNode()->rootEditableElement())
+ return position;
if (containingSpecialElement)
- *containingSpecialElement = n;
+ *containingSpecialElement = element;
return result;
}
-bool isLastVisiblePositionInSpecialElement(const Position& pos)
-{
- return lastInSpecialElement(pos);
-}
-
-Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement)
+Position positionAfterContainingSpecialElement(const Position& position, HTMLElement** containingSpecialElement)
{
- Node* n = lastInSpecialElement(pos);
- if (!n)
- return pos;
- Position result = positionInParentAfterNode(n);
- if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
- return pos;
+ auto* element = lastInSpecialElement(position);
+ if (!element)
+ return position;
+ Position result = positionInParentAfterNode(element);
+ if (result.isNull() || result.deprecatedNode()->rootEditableElement() != position.deprecatedNode()->rootEditableElement())
+ return position;
if (containingSpecialElement)
- *containingSpecialElement = n;
+ *containingSpecialElement = element;
return result;
}
-Position positionOutsideContainingSpecialElement(const Position &pos, Node **containingSpecialElement)
-{
- if (isFirstVisiblePositionInSpecialElement(pos))
- return positionBeforeContainingSpecialElement(pos, containingSpecialElement);
- if (isLastVisiblePositionInSpecialElement(pos))
- return positionAfterContainingSpecialElement(pos, containingSpecialElement);
- return pos;
-}
-
-Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
+Element* isFirstPositionAfterTable(const VisiblePosition& position)
{
- Position upstream(visiblePosition.deepEquivalent().upstream());
- if (upstream.deprecatedNode() && upstream.deprecatedNode()->renderer() && upstream.deprecatedNode()->renderer()->isTable() && upstream.atLastEditingPositionForNode())
- return upstream.deprecatedNode();
-
- return 0;
+ Position upstream(position.deepEquivalent().upstream());
+ auto* node = upstream.deprecatedNode();
+ if (!node)
+ return nullptr;
+ auto* renderer = node->renderer();
+ if (!renderer || !renderer->isTable() || !upstream.atLastEditingPositionForNode())
+ return nullptr;
+ return &downcast<Element>(*node);
}
-Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
+Element* isLastPositionBeforeTable(const VisiblePosition& position)
{
- Position downstream(visiblePosition.deepEquivalent().downstream());
- if (downstream.deprecatedNode() && downstream.deprecatedNode()->renderer() && downstream.deprecatedNode()->renderer()->isTable() && downstream.atFirstEditingPositionForNode())
- return downstream.deprecatedNode();
-
- return 0;
+ Position downstream(position.deepEquivalent().downstream());
+ auto* node = downstream.deprecatedNode();
+ if (!node)
+ return nullptr;
+ auto* renderer = node->renderer();
+ if (!renderer || !renderer->isTable() || !downstream.atFirstEditingPositionForNode())
+ return nullptr;
+ return &downcast<Element>(*node);
}
// Returns the visible position at the beginning of a node
-VisiblePosition visiblePositionBeforeNode(Node* node)
+VisiblePosition visiblePositionBeforeNode(Node& node)
{
- ASSERT(node);
- if (node->childNodeCount())
- return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
- ASSERT(node->parentNode());
- ASSERT(!node->parentNode()->isShadowRoot());
- return positionInParentBeforeNode(node);
+ if (node.hasChildNodes())
+ return VisiblePosition(firstPositionInOrBeforeNode(&node), DOWNSTREAM);
+ ASSERT(node.parentNode());
+ ASSERT(!node.parentNode()->isShadowRoot());
+ return positionInParentBeforeNode(&node);
}
// Returns the visible position at the ending of a node
-VisiblePosition visiblePositionAfterNode(Node* node)
+VisiblePosition visiblePositionAfterNode(Node& node)
{
- ASSERT(node);
- if (node->childNodeCount())
- return VisiblePosition(lastPositionInOrAfterNode(node), DOWNSTREAM);
- ASSERT(node->parentNode());
- ASSERT(!node->parentNode()->isShadowRoot());
- return positionInParentAfterNode(node);
+ if (node.hasChildNodes())
+ return VisiblePosition(lastPositionInOrAfterNode(&node), DOWNSTREAM);
+ ASSERT(node.parentNode());
+ ASSERT(!node.parentNode()->isShadowRoot());
+ return positionInParentAfterNode(&node);
}
-bool isListElement(Node *n)
+bool isListHTMLElement(Node* node)
{
- return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag)));
+ return node && (is<HTMLUListElement>(*node) || is<HTMLOListElement>(*node) || is<HTMLDListElement>(*node));
}
-bool isListItem(const Node *n)
+bool isListItem(const Node* node)
{
- return n && (isListElement(n->parentNode()) || (n->renderer() && n->renderer()->isListItem()));
+ return node && (isListHTMLElement(node->parentNode()) || (node->renderer() && node->renderer()->isListItem()));
}
-Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName)
+Element* enclosingElementWithTag(const Position& position, const QualifiedName& tagName)
{
- if (p.isNull())
- return 0;
-
- Node* root = highestEditableRoot(p);
- for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
- if (root && !n->hasEditableStyle())
+ auto* root = highestEditableRoot(position);
+ for (Node* node = position.deprecatedNode(); node; node = node->parentNode()) {
+ if (root && !node->hasEditableStyle())
continue;
- if (n->hasTagName(tagName))
- return n;
- if (n == root)
- return 0;
+ if (!is<Element>(*node))
+ continue;
+ if (downcast<Element>(*node).hasTagName(tagName))
+ return &downcast<Element>(*node);
+ if (node == root)
+ return nullptr;
}
-
- return 0;
+ return nullptr;
}
-Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule)
+Node* enclosingNodeOfType(const Position& position, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule)
{
// FIXME: support CanSkipCrossEditingBoundary
ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
- if (p.isNull())
- return 0;
-
- Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
- for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
+ auto* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(position) : nullptr;
+ for (Node* n = position.deprecatedNode(); n; n = n->parentNode()) {
// Don't return a non-editable node if the input position was editable, since
// the callers from editing will no doubt want to perform editing inside the returned node.
if (root && !n->hasEditableStyle())
@@ -575,17 +605,16 @@ Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*),
if (nodeIsOfType(n))
return n;
if (n == root)
- return 0;
+ return nullptr;
}
-
- return 0;
+ return nullptr;
}
-Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin)
+Node* highestEnclosingNodeOfType(const Position& position, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin)
{
- Node* highest = 0;
- Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
- for (Node* n = p.containerNode(); n && n != stayWithin; n = n->parentNode()) {
+ Node* highest = nullptr;
+ auto* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(position) : nullptr;
+ for (Node* n = position.containerNode(); n && n != stayWithin; n = n->parentNode()) {
if (root && !n->hasEditableStyle())
continue;
if (nodeIsOfType(n))
@@ -593,7 +622,6 @@ Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const N
if (n == root)
break;
}
-
return highest;
}
@@ -601,43 +629,40 @@ static bool hasARenderedDescendant(Node* node, Node* excludedNode)
{
for (Node* n = node->firstChild(); n;) {
if (n == excludedNode) {
- n = NodeTraversal::nextSkippingChildren(n, node);
+ n = NodeTraversal::nextSkippingChildren(*n, node);
continue;
}
if (n->renderer())
return true;
- n = NodeTraversal::next(n, node);
+ n = NodeTraversal::next(*n, node);
}
return false;
}
Node* highestNodeToRemoveInPruning(Node* node)
{
- Node* previousNode = 0;
- Node* rootEditableElement = node ? node->rootEditableElement() : 0;
+ Node* previousNode = nullptr;
+ auto* rootEditableElement = node ? node->rootEditableElement() : nullptr;
for (; node; node = node->parentNode()) {
- if (RenderObject* renderer = node->renderer()) {
+ if (auto* renderer = node->renderer()) {
if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node)
return previousNode;
}
previousNode = node;
}
- return 0;
+ return nullptr;
}
-Node* enclosingTableCell(const Position& p)
+Element* enclosingTableCell(const Position& position)
{
- return toElement(enclosingNodeOfType(p, isTableCell));
+ return downcast<Element>(enclosingNodeOfType(position, isTableCell));
}
Element* enclosingAnchorElement(const Position& p)
{
- if (p.isNull())
- return nullptr;
-
for (Node* node = p.deprecatedNode(); node; node = node->parentNode()) {
- if (node->isElementNode() && node->isLink())
- return toElement(node);
+ if (is<Element>(*node) && node->isLink())
+ return downcast<Element>(node);
}
return nullptr;
}
@@ -645,90 +670,90 @@ Element* enclosingAnchorElement(const Position& p)
HTMLElement* enclosingList(Node* node)
{
if (!node)
- return 0;
+ return nullptr;
- Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
+ auto* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
- for (ContainerNode* n = node->parentNode(); n; n = n->parentNode()) {
- if (n->hasTagName(ulTag) || n->hasTagName(olTag))
- return toHTMLElement(n);
- if (n == root)
- return 0;
+ for (ContainerNode* ancestor = node->parentNode(); ancestor; ancestor = ancestor->parentNode()) {
+ if (is<HTMLUListElement>(*ancestor) || is<HTMLOListElement>(*ancestor))
+ return downcast<HTMLElement>(ancestor);
+ if (ancestor == root)
+ return nullptr;
}
- return 0;
+ return nullptr;
}
Node* enclosingListChild(Node *node)
{
if (!node)
- return 0;
+ return nullptr;
+
// Check for a list item element, or for a node whose parent is a list element. Such a node
// will appear visually as a list item (but without a list marker)
- Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
+ auto* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
- // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode()
+ // FIXME: This function is inappropriately named since it starts with node instead of node->parentNode()
for (Node* n = node; n && n->parentNode(); n = n->parentNode()) {
- if (n->hasTagName(liTag) || (isListElement(n->parentNode()) && n != root))
+ if (is<HTMLLIElement>(*n) || (isListHTMLElement(n->parentNode()) && n != root))
return n;
if (n == root || isTableCell(n))
- return 0;
+ return nullptr;
}
-
- return 0;
+
+ return nullptr;
}
static HTMLElement* embeddedSublist(Node* listItem)
{
// Check the DOM so that we'll find collapsed sublists without renderers.
for (Node* n = listItem->firstChild(); n; n = n->nextSibling()) {
- if (isListElement(n))
- return toHTMLElement(n);
+ if (isListHTMLElement(n))
+ return downcast<HTMLElement>(n);
}
-
- return 0;
+ return nullptr;
}
static Node* appendedSublist(Node* listItem)
{
// Check the DOM so that we'll find collapsed sublists without renderers.
for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) {
- if (isListElement(n))
- return toHTMLElement(n);
+ if (isListHTMLElement(n))
+ return downcast<HTMLElement>(n);
if (isListItem(listItem))
- return 0;
+ return nullptr;
}
- return 0;
+ return nullptr;
}
-// FIXME: This method should not need to call isStartOfParagraph/isEndOfParagraph
-Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
+// FIXME: This function should not need to call isStartOfParagraph/isEndOfParagraph.
+Node* enclosingEmptyListItem(const VisiblePosition& position)
{
// Check that position is on a line by itself inside a list item
- Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().deprecatedNode());
- if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
- return 0;
+ auto* listChildNode = enclosingListChild(position.deepEquivalent().deprecatedNode());
+ if (!listChildNode || !isStartOfParagraph(position) || !isEndOfParagraph(position))
+ return nullptr;
VisiblePosition firstInListChild(firstPositionInOrBeforeNode(listChildNode));
VisiblePosition lastInListChild(lastPositionInOrAfterNode(listChildNode));
- if (firstInListChild != visiblePos || lastInListChild != visiblePos)
- return 0;
-
+ if (firstInListChild != position || lastInListChild != position)
+ return nullptr;
+
if (embeddedSublist(listChildNode) || appendedSublist(listChildNode))
- return 0;
-
+ return nullptr;
+
return listChildNode;
}
HTMLElement* outermostEnclosingList(Node* node, Node* rootList)
{
- HTMLElement* list = enclosingList(node);
+ auto* list = enclosingList(node);
if (!list)
- return 0;
+ return nullptr;
- while (HTMLElement* nextList = enclosingList(list)) {
+ while (auto* nextList = enclosingList(list)) {
if (nextList == rootList)
break;
list = nextList;
@@ -739,23 +764,17 @@ HTMLElement* outermostEnclosingList(Node* node, Node* rootList)
bool canMergeLists(Element* firstList, Element* secondList)
{
- if (!firstList || !secondList || !firstList->isHTMLElement() || !secondList->isHTMLElement())
+ if (!is<HTMLElement>(firstList) || !is<HTMLElement>(secondList))
return false;
- return firstList->hasTagName(secondList->tagQName()) // make sure the list types match (ol vs. ul)
- && firstList->hasEditableStyle() && secondList->hasEditableStyle() // both lists are editable
- && firstList->rootEditableElement() == secondList->rootEditableElement() // don't cross editing boundaries
- && isVisiblyAdjacent(positionInParentAfterNode(firstList), positionInParentBeforeNode(secondList));
- // Make sure there is no visible content between this li and the previous list
-}
+ auto& first = downcast<HTMLElement>(*firstList);
+ auto& second = downcast<HTMLElement>(*secondList);
-Node* highestAncestor(Node* node)
-{
- ASSERT(node);
- Node* parent = node;
- while ((node = node->parentNode()))
- parent = node;
- return parent;
+ return first.localName() == second.localName() // make sure the list types match (ol vs. ul)
+ && first.hasEditableStyle() && second.hasEditableStyle() // both lists are editable
+ && first.rootEditableElement() == second.rootEditableElement() // don't cross editing boundaries
+ // Make sure there is no visible content between this li and the previous list.
+ && isVisiblyAdjacent(positionInParentAfterNode(&first), positionInParentBeforeNode(&second));
}
static Node* previousNodeConsideringAtomicNodes(const Node* node)
@@ -768,7 +787,7 @@ static Node* previousNodeConsideringAtomicNodes(const Node* node)
}
if (node->parentNode())
return node->parentNode();
- return 0;
+ return nullptr;
}
static Node* nextNodeConsideringAtomicNodes(const Node* node)
@@ -782,48 +801,42 @@ static Node* nextNodeConsideringAtomicNodes(const Node* node)
n = n->parentNode();
if (n)
return n->nextSibling();
- return 0;
+ return nullptr;
}
Node* previousLeafNode(const Node* node)
{
- Node* n = previousNodeConsideringAtomicNodes(node);
- while (n) {
- if (isAtomicNode(n))
- return n;
- n = previousNodeConsideringAtomicNodes(n);
+ while ((node = previousNodeConsideringAtomicNodes(node))) {
+ if (isAtomicNode(node))
+ return const_cast<Node*>(node);
}
- return 0;
+ return nullptr;
}
Node* nextLeafNode(const Node* node)
{
- Node* n = nextNodeConsideringAtomicNodes(node);
- while (n) {
- if (isAtomicNode(n))
- return n;
- n = nextNodeConsideringAtomicNodes(n);
+ while ((node = nextNodeConsideringAtomicNodes(node))) {
+ if (isAtomicNode(node))
+ return const_cast<Node*>(node);
}
- return 0;
+ return nullptr;
}
-// FIXME: do not require renderer, so that this can be used within fragments, or rename to isRenderedTable()
-bool isTableElement(Node* n)
+// FIXME: Do not require renderer, so that this can be used within fragments.
+bool isRenderedTable(const Node* node)
{
- if (!n || !n->isElementNode())
+ if (!is<Element>(node))
return false;
-
- RenderObject* renderer = n->renderer();
- return (renderer && (renderer->style().display() == TABLE || renderer->style().display() == INLINE_TABLE));
+ auto* renderer = downcast<Element>(*node).renderer();
+ return renderer && renderer->isTable();
}
bool isTableCell(const Node* node)
{
- RenderObject* r = node->renderer();
- if (!r)
+ auto* renderer = node->renderer();
+ if (!renderer)
return node->hasTagName(tdTag) || node->hasTagName(thTag);
-
- return r->isTableCell();
+ return renderer->isTableCell();
}
bool isEmptyTableCell(const Node* node)
@@ -841,17 +854,17 @@ bool isEmptyTableCell(const Node* node)
// Make sure the rendered node is a table cell or <br>.
// If it's a <br>, then the parent node has to be a table cell.
- RenderObject* renderer = node->renderer();
+ auto* renderer = node->renderer();
if (renderer->isBR()) {
renderer = renderer->parent();
if (!renderer)
return false;
}
- if (!renderer->isTableCell())
+ if (!is<RenderTableCell>(*renderer))
return false;
// Check that the table cell contains no child renderers except for perhaps a single <br>.
- RenderObject* childRenderer = toRenderElement(renderer)->firstChild();
+ auto* childRenderer = downcast<RenderTableCell>(*renderer).firstChild();
if (!childRenderer)
return true;
if (!childRenderer->isBR())
@@ -859,206 +872,159 @@ bool isEmptyTableCell(const Node* node)
return !childRenderer->nextSibling();
}
-PassRefPtr<HTMLElement> createDefaultParagraphElement(Document& document)
+Ref<HTMLElement> createDefaultParagraphElement(Document& document)
{
switch (document.frame()->editor().defaultParagraphSeparator()) {
case EditorParagraphSeparatorIsDiv:
return HTMLDivElement::create(document);
case EditorParagraphSeparatorIsP:
- return HTMLParagraphElement::create(document);
+ break;
}
-
- ASSERT_NOT_REACHED();
- return 0;
+ return HTMLParagraphElement::create(document);
}
-PassRefPtr<HTMLElement> createBreakElement(Document& document)
-{
- return HTMLBRElement::create(document);
-}
-
-PassRefPtr<HTMLElement> createOrderedListElement(Document& document)
-{
- return HTMLOListElement::create(document);
-}
-
-PassRefPtr<HTMLElement> createUnorderedListElement(Document& document)
-{
- return HTMLUListElement::create(document);
-}
-
-PassRefPtr<HTMLElement> createListItemElement(Document& document)
-{
- return HTMLLIElement::create(document);
-}
-
-PassRefPtr<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name)
+Ref<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name)
{
return HTMLElementFactory::createElement(name, document);
}
-PassRefPtr<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName)
+Ref<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName)
{
return createHTMLElement(document, QualifiedName(nullAtom, tagName, xhtmlNamespaceURI));
}
-bool isTabSpanNode(const Node *node)
+bool isTabSpanNode(const Node* node)
{
- return node && node->hasTagName(spanTag) && node->isElementNode() && static_cast<const Element *>(node)->getAttribute(classAttr) == AppleTabSpanClass;
+ return is<HTMLSpanElement>(node) && downcast<HTMLSpanElement>(*node).attributeWithoutSynchronization(classAttr) == AppleTabSpanClass;
}
-bool isTabSpanTextNode(const Node *node)
+bool isTabSpanTextNode(const Node* node)
{
- return node && node->isTextNode() && node->parentNode() && isTabSpanNode(node->parentNode());
+ return is<Text>(node) && isTabSpanNode(node->parentNode());
}
-Node* tabSpanNode(const Node *node)
-{
- return isTabSpanTextNode(node) ? node->parentNode() : 0;
-}
-
-Position positionOutsideTabSpan(const Position& pos)
+HTMLSpanElement* tabSpanNode(const Node* node)
{
- Node* node = pos.containerNode();
- if (isTabSpanTextNode(node))
- node = tabSpanNode(node);
- else if (!isTabSpanNode(node))
- return pos;
-
- if (node && VisiblePosition(pos) == lastPositionInNode(node))
- return positionInParentAfterNode(node);
-
- return positionInParentBeforeNode(node);
+ return isTabSpanTextNode(node) ? downcast<HTMLSpanElement>(node->parentNode()) : nullptr;
}
-PassRefPtr<Element> createTabSpanElement(Document& document, PassRefPtr<Node> prpTabTextNode)
+static Ref<Element> createTabSpanElement(Document& document, Text& tabTextNode)
{
- RefPtr<Node> tabTextNode = prpTabTextNode;
+ auto spanElement = HTMLSpanElement::create(document);
- // Make the span to hold the tab.
- RefPtr<Element> spanElement = document.createElement(spanTag, false);
- spanElement->setAttribute(classAttr, AppleTabSpanClass);
+ spanElement->setAttributeWithoutSynchronization(classAttr, AppleTabSpanClass);
spanElement->setAttribute(styleAttr, "white-space:pre");
- // Add tab text to that span.
- if (!tabTextNode)
- tabTextNode = document.createEditingTextNode("\t");
+ spanElement->appendChild(tabTextNode);
- spanElement->appendChild(tabTextNode.release(), ASSERT_NO_EXCEPTION);
-
- return spanElement.release();
+ return WTFMove(spanElement);
}
-PassRefPtr<Element> createTabSpanElement(Document& document, const String& tabText)
+Ref<Element> createTabSpanElement(Document& document, const String& tabText)
{
return createTabSpanElement(document, document.createTextNode(tabText));
}
-PassRefPtr<Element> createTabSpanElement(Document& document)
+Ref<Element> createTabSpanElement(Document& document)
{
- return createTabSpanElement(document, PassRefPtr<Node>());
+ return createTabSpanElement(document, document.createEditingTextNode(ASCIILiteral("\t")));
}
-bool isNodeRendered(const Node* node)
+bool isNodeRendered(const Node& node)
{
- if (!node)
- return false;
-
- RenderObject* renderer = node->renderer();
- if (!renderer)
- return false;
-
- return renderer->style().visibility() == VISIBLE;
+ auto* renderer = node.renderer();
+ return renderer && renderer->style().visibility() == VISIBLE;
}
-unsigned numEnclosingMailBlockquotes(const Position& p)
+unsigned numEnclosingMailBlockquotes(const Position& position)
{
- unsigned num = 0;
- for (Node* n = p.deprecatedNode(); n; n = n->parentNode())
- if (isMailBlockquote(n))
- num++;
-
- return num;
+ unsigned count = 0;
+ for (Node* node = position.deprecatedNode(); node; node = node->parentNode()) {
+ if (isMailBlockquote(node))
+ ++count;
+ }
+ return count;
}
-void updatePositionForNodeRemoval(Position& position, Node* node)
+void updatePositionForNodeRemoval(Position& position, Node& node)
{
if (position.isNull())
return;
switch (position.anchorType()) {
case Position::PositionIsBeforeChildren:
- if (position.containerNode() == node)
- position = positionInParentBeforeNode(node);
+ if (node.containsIncludingShadowDOM(position.containerNode()))
+ position = positionInParentBeforeNode(&node);
break;
case Position::PositionIsAfterChildren:
- if (position.containerNode() == node)
- position = positionInParentAfterNode(node);
+ if (node.containsIncludingShadowDOM(position.containerNode()))
+ position = positionInParentBeforeNode(&node);
break;
case Position::PositionIsOffsetInAnchor:
- if (position.containerNode() == node->parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node->nodeIndex())
+ if (position.containerNode() == node.parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node.computeNodeIndex())
position.moveToOffset(position.offsetInContainerNode() - 1);
- else if (node->containsIncludingShadowDOM(position.containerNode()))
- position = positionInParentBeforeNode(node);
+ else if (node.containsIncludingShadowDOM(position.containerNode()))
+ position = positionInParentBeforeNode(&node);
break;
case Position::PositionIsAfterAnchor:
- if (node->containsIncludingShadowDOM(position.anchorNode()))
- position = positionInParentAfterNode(node);
+ if (node.containsIncludingShadowDOM(position.anchorNode()))
+ position = positionInParentAfterNode(&node);
break;
case Position::PositionIsBeforeAnchor:
- if (node->containsIncludingShadowDOM(position.anchorNode()))
- position = positionInParentBeforeNode(node);
+ if (node.containsIncludingShadowDOM(position.anchorNode()))
+ position = positionInParentBeforeNode(&node);
break;
}
}
-bool isMailBlockquote(const Node *node)
+bool isMailBlockquote(const Node* node)
{
- if (!node || !node->hasTagName(blockquoteTag))
+ ASSERT(node);
+ if (!node->hasTagName(blockquoteTag))
return false;
-
- return static_cast<const Element *>(node)->getAttribute("type") == "cite";
+ return downcast<HTMLElement>(*node).attributeWithoutSynchronization(typeAttr) == "cite";
}
-int caretMinOffset(const Node* n)
+int caretMinOffset(const Node& node)
{
- RenderObject* r = n->renderer();
- ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now.
- return r ? r->caretMinOffset() : 0;
+ auto* renderer = node.renderer();
+ ASSERT(!node.isCharacterDataNode() || !renderer || renderer->isText());
+ return renderer ? renderer->caretMinOffset() : 0;
}
// If a node can contain candidates for VisiblePositions, return the offset of the last candidate, otherwise
// return the number of children for container nodes and the length for unrendered text nodes.
-int caretMaxOffset(const Node* n)
+int caretMaxOffset(const Node& node)
{
// For rendered text nodes, return the last position that a caret could occupy.
- if (n->isTextNode() && n->renderer())
- return n->renderer()->caretMaxOffset();
- // For containers return the number of children. For others do the same as above.
- return lastOffsetForEditing(n);
+ if (is<Text>(node)) {
+ if (auto* renderer = downcast<Text>(node).renderer())
+ return renderer->caretMaxOffset();
+ }
+ return lastOffsetForEditing(node);
}
-bool lineBreakExistsAtVisiblePosition(const VisiblePosition& visiblePosition)
+bool lineBreakExistsAtVisiblePosition(const VisiblePosition& position)
{
- return lineBreakExistsAtPosition(visiblePosition.deepEquivalent().downstream());
+ return lineBreakExistsAtPosition(position.deepEquivalent().downstream());
}
bool lineBreakExistsAtPosition(const Position& position)
{
if (position.isNull())
return false;
-
+
if (position.anchorNode()->hasTagName(brTag) && position.atFirstEditingPositionForNode())
return true;
-
+
if (!position.anchorNode()->renderer())
return false;
-
- if (!position.anchorNode()->isTextNode() || !position.anchorNode()->renderer()->style().preserveNewline())
+
+ if (!is<Text>(*position.anchorNode()) || !position.anchorNode()->renderer()->style().preserveNewline())
return false;
- Text* textNode = toText(position.anchorNode());
+ Text& textNode = downcast<Text>(*position.anchorNode());
unsigned offset = position.offsetInContainerNode();
- return offset < textNode->length() && textNode->data()[offset] == '\n';
+ return offset < textNode.length() && textNode.data()[offset] == '\n';
}
// Modifies selections that have an end point at the edge of a table
@@ -1074,17 +1040,19 @@ VisibleSelection selectionForParagraphIteration(const VisibleSelection& original
// if the start of the selection is inside that table, then the last paragraph
// that we'll want modify is the last one inside the table, not the table itself
// (a table is itself a paragraph).
- if (Node* table = isFirstPositionAfterTable(endOfSelection))
- if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
+ if (auto* table = isFirstPositionAfterTable(endOfSelection)) {
+ if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(*table))
newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(CannotCrossEditingBoundary));
+ }
// If the start of the selection to modify is just before a table,
// and if the end of the selection is inside that table, then the first paragraph
// we'll want to modify is the first one inside the table, not the paragraph
// containing the table itself.
- if (Node* table = isLastPositionBeforeTable(startOfSelection))
- if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
+ if (auto* table = isLastPositionBeforeTable(startOfSelection)) {
+ if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(*table))
newSelection = VisibleSelection(startOfSelection.next(CannotCrossEditingBoundary), endOfSelection);
+ }
return newSelection;
}
@@ -1100,74 +1068,74 @@ int indexForVisiblePosition(const VisiblePosition& visiblePosition, RefPtr<Conta
if (visiblePosition.isNull())
return 0;
- Position p(visiblePosition.deepEquivalent());
- Document& document = p.anchorNode()->document();
- ShadowRoot* shadowRoot = p.anchorNode()->containingShadowRoot();
-
- if (shadowRoot)
- scope = shadowRoot;
- else
- scope = document.documentElement();
+ auto position = visiblePosition.deepEquivalent();
+ auto& document = *position.document();
+
+ auto* editableRoot = highestEditableRoot(position, AXObjectCache::accessibilityEnabled() ? HasEditableAXRole : ContentIsEditable);
+ if (editableRoot && !document.inDesignMode())
+ scope = editableRoot;
+ else {
+ if (position.containerNode()->isInShadowTree())
+ scope = position.containerNode()->containingShadowRoot();
+ else
+ scope = &document;
+ }
- RefPtr<Range> range = Range::create(document, firstPositionInNode(scope.get()), p.parentAnchoredEquivalent());
- return TextIterator::rangeLength(range.get(), true);
+ auto range = Range::create(document, firstPositionInNode(scope.get()), position.parentAnchoredEquivalent());
+ return TextIterator::rangeLength(range.ptr(), true);
}
-// FIXME: Merge these two functions.
-int indexForVisiblePosition(Node* node, const VisiblePosition& visiblePosition, bool forSelectionPreservation)
+// FIXME: Merge this function with the one above.
+int indexForVisiblePosition(Node& node, const VisiblePosition& visiblePosition, bool forSelectionPreservation)
{
- ASSERT(node);
- RefPtr<Range> range = Range::create(node->document(), firstPositionInNode(node), visiblePosition.deepEquivalent().parentAnchoredEquivalent());
- return TextIterator::rangeLength(range.get(), forSelectionPreservation);
+ auto range = Range::create(node.document(), firstPositionInNode(&node), visiblePosition.deepEquivalent().parentAnchoredEquivalent());
+ return TextIterator::rangeLength(range.ptr(), forSelectionPreservation);
}
VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope)
{
- RefPtr<Range> range = TextIterator::rangeFromLocationAndLength(scope, index, 0, true);
+ auto range = TextIterator::rangeFromLocationAndLength(scope, index, 0, true);
// Check for an invalid index. Certain editing operations invalidate indices because
// of problems with TextIteratorEmitsCharactersBetweenAllVisiblePositions.
if (!range)
- return VisiblePosition();
- return VisiblePosition(range->startPosition());
+ return { };
+ return { range->startPosition() };
}
-VisiblePosition visiblePositionForIndexUsingCharacterIterator(Node* node, int index)
+VisiblePosition visiblePositionForIndexUsingCharacterIterator(Node& node, int index)
{
- ASSERT(node);
if (index <= 0)
- return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
+ return { firstPositionInOrBeforeNode(&node), DOWNSTREAM };
- RefPtr<Range> range = Range::create(node->document());
- range->selectNodeContents(node, IGNORE_EXCEPTION);
- CharacterIterator it(range.get());
+ RefPtr<Range> range = Range::create(node.document());
+ range->selectNodeContents(node);
+ CharacterIterator it(*range);
it.advance(index - 1);
-
- return VisiblePosition(Position(it.range()->endContainer(), it.range()->endOffset(), Position::PositionIsOffsetInAnchor), UPSTREAM);
+ return { it.atEnd() ? range->endPosition() : it.range()->endPosition(), UPSTREAM };
}
// Determines whether two positions are visibly next to each other (first then second)
// while ignoring whitespaces and unrendered nodes
-bool isVisiblyAdjacent(const Position& first, const Position& second)
+static bool isVisiblyAdjacent(const Position& first, const Position& second)
{
return VisiblePosition(first) == VisiblePosition(second.upstream());
}
// Determines whether a node is inside a range or visibly starts and ends at the boundaries of the range.
// Call this function to determine whether a node is visibly fit inside selectedRange
-bool isNodeVisiblyContainedWithin(Node* node, const Range* selectedRange)
+bool isNodeVisiblyContainedWithin(Node& node, const Range& range)
{
- ASSERT(node);
- ASSERT(selectedRange);
- // If the node is inside the range, then it surely is contained within
- if (selectedRange->compareNode(node, IGNORE_EXCEPTION) == Range::NODE_INSIDE)
+ // If the node is inside the range, then it surely is contained within.
+ auto comparisonResult = range.compareNode(node);
+ if (!comparisonResult.hasException() && comparisonResult.releaseReturnValue() == Range::NODE_INSIDE)
return true;
- bool startIsVisuallySame = visiblePositionBeforeNode(node) == selectedRange->startPosition();
- if (startIsVisuallySame && comparePositions(positionInParentAfterNode(node), selectedRange->endPosition()) < 0)
+ bool startIsVisuallySame = visiblePositionBeforeNode(node) == range.startPosition();
+ if (startIsVisuallySame && comparePositions(positionInParentAfterNode(&node), range.endPosition()) < 0)
return true;
- bool endIsVisuallySame = visiblePositionAfterNode(node) == selectedRange->endPosition();
- if (endIsVisuallySame && comparePositions(selectedRange->startPosition(), positionInParentBeforeNode(node)) < 0)
+ bool endIsVisuallySame = visiblePositionAfterNode(node) == range.endPosition();
+ if (endIsVisuallySame && comparePositions(range.startPosition(), positionInParentBeforeNode(&node)) < 0)
return true;
return startIsVisuallySame && endIsVisuallySame;
@@ -1181,36 +1149,28 @@ bool isRenderedAsNonInlineTableImageOrHR(const Node* node)
return renderer && ((renderer->isTable() && !renderer->isInline()) || (renderer->isImage() && !renderer->isInline()) || renderer->isHR());
}
-bool areIdenticalElements(const Node* first, const Node* second)
+bool areIdenticalElements(const Node& first, const Node& second)
{
- if (!first->isElementNode() || !second->isElementNode())
- return false;
-
- const Element* firstElement = toElement(first);
- const Element* secondElement = toElement(second);
- if (!firstElement->hasTagName(secondElement->tagQName()))
+ if (!is<Element>(first) || !is<Element>(second))
return false;
-
- return firstElement->hasEquivalentAttributes(secondElement);
+ auto& firstElement = downcast<Element>(first);
+ auto& secondElement = downcast<Element>(second);
+ return firstElement.hasTagName(secondElement.tagQName()) && firstElement.hasEquivalentAttributes(&secondElement);
}
bool isNonTableCellHTMLBlockElement(const Node* node)
{
- if (!node->isElementNode())
- return false;
-
- const Element* element = toElement(node);
- return element->hasTagName(listingTag)
- || element->hasTagName(olTag)
- || element->hasTagName(preTag)
- || isHTMLTableElement(element)
- || element->hasTagName(ulTag)
- || element->hasTagName(xmpTag)
- || element->hasTagName(h1Tag)
- || element->hasTagName(h2Tag)
- || element->hasTagName(h3Tag)
- || element->hasTagName(h4Tag)
- || element->hasTagName(h5Tag);
+ return node->hasTagName(listingTag)
+ || node->hasTagName(olTag)
+ || node->hasTagName(preTag)
+ || is<HTMLTableElement>(*node)
+ || node->hasTagName(ulTag)
+ || node->hasTagName(xmpTag)
+ || node->hasTagName(h1Tag)
+ || node->hasTagName(h2Tag)
+ || node->hasTagName(h3Tag)
+ || node->hasTagName(h4Tag)
+ || node->hasTagName(h5Tag);
}
Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selection)
@@ -1220,9 +1180,9 @@ Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selec
// It is important to skip certain irrelevant content at the start of the selection, so we do not wind up
// with a spurious "mixed" style.
- VisiblePosition visiblePosition = selection.start();
+ auto visiblePosition = selection.visibleStart();
if (visiblePosition.isNull())
- return Position();
+ return { };
// if the selection is a caret, just return the position, since the style
// behind us is relevant
@@ -1239,25 +1199,89 @@ Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selec
}
// FIXME: Should this be deprecated like deprecatedEnclosingBlockFlowElement is?
-bool isBlockFlowElement(const Node* node)
+bool isBlockFlowElement(const Node& node)
{
- if (!node->isElementNode())
+ if (!node.isElementNode())
return false;
- RenderObject* renderer = node->renderer();
+ auto* renderer = downcast<Element>(node).renderer();
return renderer && renderer->isRenderBlockFlow();
}
Element* deprecatedEnclosingBlockFlowElement(Node* node)
{
if (!node)
- return 0;
- if (isBlockFlowElement(node))
- return toElement(node);
+ return nullptr;
+ if (isBlockFlowElement(*node))
+ return downcast<Element>(node);
while ((node = node->parentNode())) {
- if (isBlockFlowElement(node) || node->hasTagName(bodyTag))
- return toElement(node);
+ if (isBlockFlowElement(*node) || is<HTMLBodyElement>(*node))
+ return downcast<Element>(node);
}
- return 0;
+ return nullptr;
+}
+
+static inline bool caretRendersInsideNode(Node& node)
+{
+ return !isRenderedTable(&node) && !editingIgnoresContent(node);
+}
+
+RenderBlock* rendererForCaretPainting(Node* node)
+{
+ if (!node)
+ return nullptr;
+
+ auto* renderer = node->renderer();
+ if (!renderer)
+ return nullptr;
+
+ // If caretNode is a block and caret is inside it, then caret should be painted by that block.
+ bool paintedByBlock = is<RenderBlockFlow>(*renderer) && caretRendersInsideNode(*node);
+ return paintedByBlock ? downcast<RenderBlock>(renderer) : renderer->containingBlock();
+}
+
+LayoutRect localCaretRectInRendererForCaretPainting(const VisiblePosition& caretPosition, RenderBlock*& caretPainter)
+{
+ if (caretPosition.isNull())
+ return LayoutRect();
+
+ ASSERT(caretPosition.deepEquivalent().deprecatedNode()->renderer());
+
+ // First compute a rect local to the renderer at the selection start.
+ RenderObject* renderer;
+ LayoutRect localRect = caretPosition.localCaretRect(renderer);
+
+ return localCaretRectInRendererForRect(localRect, caretPosition.deepEquivalent().deprecatedNode(), renderer, caretPainter);
+}
+
+LayoutRect localCaretRectInRendererForRect(LayoutRect& localRect, Node* node, RenderObject* renderer, RenderBlock*& caretPainter)
+{
+ // Get the renderer that will be responsible for painting the caret
+ // (which is either the renderer we just found, or one of its containers).
+ caretPainter = rendererForCaretPainting(node);
+
+ // Compute an offset between the renderer and the caretPainter.
+ while (renderer != caretPainter) {
+ RenderElement* containerObject = renderer->container();
+ if (!containerObject)
+ return LayoutRect();
+ localRect.move(renderer->offsetFromContainer(*containerObject, localRect.location()));
+ renderer = containerObject;
+ }
+
+ return localRect;
+}
+
+IntRect absoluteBoundsForLocalCaretRect(RenderBlock* rendererForCaretPainting, const LayoutRect& rect, bool* insideFixed)
+{
+ if (insideFixed)
+ *insideFixed = false;
+
+ if (!rendererForCaretPainting || rect.isEmpty())
+ return IntRect();
+
+ LayoutRect localRect(rect);
+ rendererForCaretPainting->flipForWritingMode(localRect);
+ return rendererForCaretPainting->localToAbsoluteQuad(FloatRect(localRect), UseTransforms, insideFixed).enclosingBoundingBox();
}
} // namespace WebCore
diff --git a/Source/WebCore/editing/htmlediting.h b/Source/WebCore/editing/htmlediting.h
index 94e74e352..eae48c0bb 100644
--- a/Source/WebCore/editing/htmlediting.h
+++ b/Source/WebCore/editing/htmlediting.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2006, 2008, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,239 +23,221 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef htmlediting_h
-#define htmlediting_h
+#pragma once
-#include "EditingBoundary.h"
#include "Position.h"
-#include "TextDirection.h"
#include <wtf/Forward.h>
#include <wtf/unicode/CharacterNames.h>
namespace WebCore {
class Document;
-class Element;
class HTMLElement;
+class HTMLSpanElement;
class HTMLTextFormControlElement;
-class Node;
-class Position;
-class Range;
+class RenderBlock;
class VisiblePosition;
class VisibleSelection;
-
-// This file contains a set of helper functions used by the editing commands
-
// -------------------------------------------------------------------------
// Node
// -------------------------------------------------------------------------
-// Functions returning Node
+ContainerNode* highestEditableRoot(const Position&, EditableType = ContentIsEditable);
-Node* highestAncestor(Node*);
-Node* highestEditableRoot(const Position&, EditableType = ContentIsEditable);
-
-Node* highestEnclosingNodeOfType(const Position&, bool (*nodeIsOfType)(const Node*),
- EditingBoundaryCrossingRule = CannotCrossEditingBoundary, Node* stayWithin = 0);
+Node* highestEnclosingNodeOfType(const Position&, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule = CannotCrossEditingBoundary, Node* stayWithin = nullptr);
Node* highestNodeToRemoveInPruning(Node*);
-Node* lowestEditableAncestor(Node*);
+Element* lowestEditableAncestor(Node*);
Element* deprecatedEnclosingBlockFlowElement(Node*); // Use enclosingBlock instead.
Element* enclosingBlock(Node*, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
-Node* enclosingTableCell(const Position&);
+Element* enclosingTableCell(const Position&);
Node* enclosingEmptyListItem(const VisiblePosition&);
Element* enclosingAnchorElement(const Position&);
-Node* enclosingNodeWithTag(const Position&, const QualifiedName&);
+Element* enclosingElementWithTag(const Position&, const QualifiedName&);
Node* enclosingNodeOfType(const Position&, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
-
-Node* tabSpanNode(const Node*);
-Node* isLastPositionBeforeTable(const VisiblePosition&);
-Node* isFirstPositionAfterTable(const VisiblePosition&);
+HTMLSpanElement* tabSpanNode(const Node*);
+Element* isLastPositionBeforeTable(const VisiblePosition&); // FIXME: Strange to name this isXXX, but return an element.
+Element* isFirstPositionAfterTable(const VisiblePosition&); // FIXME: Strange to name this isXXX, but return an element.
// These two deliver leaf nodes as if the whole DOM tree were a linear chain of its leaf nodes.
Node* nextLeafNode(const Node*);
Node* previousLeafNode(const Node*);
-// offset functions on Node
+WEBCORE_EXPORT int lastOffsetForEditing(const Node&);
+int caretMinOffset(const Node&);
+int caretMaxOffset(const Node&);
-int lastOffsetForEditing(const Node*);
-int caretMinOffset(const Node*);
-int caretMaxOffset(const Node*);
+bool hasEditableStyle(const Node&, EditableType);
+bool isEditableNode(const Node&);
-// boolean functions on Node
-
-// FIXME: editingIgnoresContent, canHaveChildrenForEditing, and isAtomicNode
-// should be renamed to reflect its usage.
+// FIXME: editingIgnoresContent, canHaveChildrenForEditing, and isAtomicNode should be named to clarify how they differ.
// Returns true for nodes that either have no content, or have content that is ignored (skipped over) while editing.
// There are no VisiblePositions inside these nodes.
-inline bool editingIgnoresContent(const Node* node)
-{
- return !node->canContainRangeEndPoint();
-}
-
-inline bool canHaveChildrenForEditing(const Node* node)
-{
- return !node->isTextNode() && node->canContainRangeEndPoint();
-}
+bool editingIgnoresContent(const Node&);
+bool canHaveChildrenForEditing(const Node&);
bool isAtomicNode(const Node*);
+
bool isBlock(const Node*);
-bool isBlockFlowElement(const Node*);
+bool isBlockFlowElement(const Node&);
bool isInline(const Node*);
-bool isSpecialElement(const Node*);
bool isTabSpanNode(const Node*);
bool isTabSpanTextNode(const Node*);
bool isMailBlockquote(const Node*);
-bool isTableElement(Node*);
+bool isRenderedTable(const Node*);
bool isTableCell(const Node*);
bool isEmptyTableCell(const Node*);
bool isTableStructureNode(const Node*);
-bool isListElement(Node*);
+bool isListHTMLElement(Node*);
bool isListItem(const Node*);
-bool isNodeRendered(const Node*);
-bool isNodeVisiblyContainedWithin(Node*, const Range*);
+bool isNodeRendered(const Node&);
bool isRenderedAsNonInlineTableImageOrHR(const Node*);
-bool areIdenticalElements(const Node*, const Node*);
bool isNonTableCellHTMLBlockElement(const Node*);
-TextDirection directionOfEnclosingBlock(const Position&);
+bool isNodeVisiblyContainedWithin(Node&, const Range&);
+
+bool areIdenticalElements(const Node&, const Node&);
+
+bool positionBeforeOrAfterNodeIsCandidate(Node&);
// -------------------------------------------------------------------------
// Position
// -------------------------------------------------------------------------
-
-// Functions returning Position
-
+
Position nextCandidate(const Position&);
Position previousCandidate(const Position&);
-
+
Position nextVisuallyDistinctCandidate(const Position&);
Position previousVisuallyDistinctCandidate(const Position&);
-Position positionOutsideTabSpan(const Position&);
-Position positionBeforeContainingSpecialElement(const Position&, Node** containingSpecialElement = 0);
-Position positionAfterContainingSpecialElement(const Position&, Node** containingSpecialElement = 0);
-Position positionOutsideContainingSpecialElement(const Position&, Node** containingSpecialElement = 0);
+Position positionBeforeContainingSpecialElement(const Position&, HTMLElement** containingSpecialElement = nullptr);
+Position positionAfterContainingSpecialElement(const Position&, HTMLElement** containingSpecialElement = nullptr);
-inline Position firstPositionInOrBeforeNode(Node* node)
-{
- if (!node)
- return Position();
- return editingIgnoresContent(node) ? positionBeforeNode(node) : firstPositionInNode(node);
-}
+Position firstPositionInOrBeforeNode(Node*);
+Position lastPositionInOrAfterNode(Node*);
-inline Position lastPositionInOrAfterNode(Node* node)
-{
- if (!node)
- return Position();
- return editingIgnoresContent(node) ? positionAfterNode(node) : lastPositionInNode(node);
-}
+Position firstEditablePositionAfterPositionInRoot(const Position&, ContainerNode* root);
+Position lastEditablePositionBeforePositionInRoot(const Position&, ContainerNode* root);
-// comparision functions on Position
-
int comparePositions(const Position&, const Position&);
-// boolean functions on Position
-
-enum EUpdateStyle { UpdateStyle, DoNotUpdateStyle };
-bool isEditablePosition(const Position&, EditableType = ContentIsEditable, EUpdateStyle = UpdateStyle);
-bool isRichlyEditablePosition(const Position&, EditableType = ContentIsEditable);
-bool isFirstVisiblePositionInSpecialElement(const Position&);
-bool isLastVisiblePositionInSpecialElement(const Position&);
+WEBCORE_EXPORT bool isEditablePosition(const Position&, EditableType = ContentIsEditable);
+bool isRichlyEditablePosition(const Position&);
bool lineBreakExistsAtPosition(const Position&);
-bool isVisiblyAdjacent(const Position& first, const Position& second);
bool isAtUnsplittableElement(const Position&);
-// miscellaneous functions on Position
-
unsigned numEnclosingMailBlockquotes(const Position&);
-void updatePositionForNodeRemoval(Position&, Node*);
+void updatePositionForNodeRemoval(Position&, Node&);
+
+WEBCORE_EXPORT TextDirection directionOfEnclosingBlock(const Position&);
// -------------------------------------------------------------------------
// VisiblePosition
// -------------------------------------------------------------------------
-
-// Functions returning VisiblePosition
-
-VisiblePosition firstEditablePositionAfterPositionInRoot(const Position&, Node*);
-VisiblePosition lastEditablePositionBeforePositionInRoot(const Position&, Node*);
-VisiblePosition visiblePositionBeforeNode(Node*);
-VisiblePosition visiblePositionAfterNode(Node*);
+
+VisiblePosition visiblePositionBeforeNode(Node&);
+VisiblePosition visiblePositionAfterNode(Node&);
bool lineBreakExistsAtVisiblePosition(const VisiblePosition&);
-
+
int comparePositions(const VisiblePosition&, const VisiblePosition&);
-int indexForVisiblePosition(const VisiblePosition&, RefPtr<ContainerNode>& scope);
-int indexForVisiblePosition(Node*, const VisiblePosition&, bool forSelectionPreservation);
-VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope);
-VisiblePosition visiblePositionForIndexUsingCharacterIterator(Node*, int index); // FIXME: Why do we need this version?
+WEBCORE_EXPORT int indexForVisiblePosition(const VisiblePosition&, RefPtr<ContainerNode>& scope);
+int indexForVisiblePosition(Node&, const VisiblePosition&, bool forSelectionPreservation);
+WEBCORE_EXPORT VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope);
+VisiblePosition visiblePositionForIndexUsingCharacterIterator(Node&, int index); // FIXME: Why do we need this version?
// -------------------------------------------------------------------------
// HTMLElement
// -------------------------------------------------------------------------
-
-// Functions returning HTMLElement
-
-PassRefPtr<HTMLElement> createDefaultParagraphElement(Document&);
-PassRefPtr<HTMLElement> createBreakElement(Document&);
-PassRefPtr<HTMLElement> createOrderedListElement(Document&);
-PassRefPtr<HTMLElement> createUnorderedListElement(Document&);
-PassRefPtr<HTMLElement> createListItemElement(Document&);
-PassRefPtr<HTMLElement> createHTMLElement(Document&, const QualifiedName&);
-PassRefPtr<HTMLElement> createHTMLElement(Document&, const AtomicString&);
-
-HTMLElement* enclosingList(Node*);
-HTMLElement* outermostEnclosingList(Node*, Node* rootList = 0);
+
+WEBCORE_EXPORT Ref<HTMLElement> createDefaultParagraphElement(Document&);
+Ref<HTMLElement> createHTMLElement(Document&, const QualifiedName&);
+Ref<HTMLElement> createHTMLElement(Document&, const AtomicString&);
+
+WEBCORE_EXPORT HTMLElement* enclosingList(Node*);
+HTMLElement* outermostEnclosingList(Node*, Node* rootList = nullptr);
Node* enclosingListChild(Node*);
// -------------------------------------------------------------------------
// Element
// -------------------------------------------------------------------------
-
-// Functions returning Element
-
-PassRefPtr<Element> createTabSpanElement(Document&);
-PassRefPtr<Element> createTabSpanElement(Document&, PassRefPtr<Node> tabTextNode);
-PassRefPtr<Element> createTabSpanElement(Document&, const String& tabText);
-PassRefPtr<Element> createBlockPlaceholderElement(Document&);
+
+Ref<Element> createTabSpanElement(Document&);
+Ref<Element> createTabSpanElement(Document&, const String& tabText);
+Ref<Element> createBlockPlaceholderElement(Document&);
Element* editableRootForPosition(const Position&, EditableType = ContentIsEditable);
Element* unsplittableElementForPosition(const Position&);
-// Boolean functions on Element
-
bool canMergeLists(Element* firstList, Element* secondList);
-
+
// -------------------------------------------------------------------------
// VisibleSelection
// -------------------------------------------------------------------------
-// Functions returning VisibleSelection
VisibleSelection selectionForParagraphIteration(const VisibleSelection&);
-
Position adjustedSelectionStartForStyleComputation(const VisibleSelection&);
-
-// Miscellaneous functions on Text
-inline bool isWhitespace(UChar c)
+// -------------------------------------------------------------------------
+
+// FIXME: This is only one of many definitions of whitespace. Possibly never the right one to use.
+bool deprecatedIsEditingWhitespace(UChar);
+
+// FIXME: Can't answer this question correctly without being passed the white-space mode.
+bool deprecatedIsCollapsibleWhitespace(UChar);
+
+bool isAmbiguousBoundaryCharacter(UChar);
+
+String stringWithRebalancedWhitespace(const String&, bool startIsStartOfParagraph, bool endIsEndOfParagraph);
+const String& nonBreakingSpaceString();
+
+// Miscellaneous functions for caret rendering.
+
+RenderBlock* rendererForCaretPainting(Node*);
+LayoutRect localCaretRectInRendererForCaretPainting(const VisiblePosition&, RenderBlock*&);
+LayoutRect localCaretRectInRendererForRect(LayoutRect&, Node*, RenderObject*, RenderBlock*&);
+IntRect absoluteBoundsForLocalCaretRect(RenderBlock* rendererForCaretPainting, const LayoutRect&, bool* insideFixed = nullptr);
+
+// -------------------------------------------------------------------------
+
+inline bool deprecatedIsEditingWhitespace(UChar c)
{
return c == noBreakSpace || c == ' ' || c == '\n' || c == '\t';
}
-inline bool isAmbiguousBoundaryCharacter(UChar character)
+// FIXME: Can't really answer this question correctly without knowing the white-space mode.
+inline bool deprecatedIsCollapsibleWhitespace(UChar c)
{
- // These are characters that can behave as word boundaries, but can appear within words.
- // If they are just typed, i.e. if they are immediately followed by a caret, we want to delay text checking until the next character has been typed.
- // FIXME: this is required until 6853027 is fixed and text checking can do this for us.
- return character == '\'' || character == rightSingleQuotationMark || character == hebrewPunctuationGershayim;
+ return c == ' ' || c == '\n';
}
-String stringWithRebalancedWhitespace(const String&, bool startIsStartOfParagraph, bool endIsEndOfParagraph);
-const String& nonBreakingSpaceString();
+bool isAmbiguousBoundaryCharacter(UChar);
+inline bool editingIgnoresContent(const Node& node)
+{
+ return !node.canContainRangeEndPoint();
+}
+
+inline bool positionBeforeOrAfterNodeIsCandidate(Node& node)
+{
+ return isRenderedTable(&node) || editingIgnoresContent(node);
}
-#endif
+inline Position firstPositionInOrBeforeNode(Node* node)
+{
+ if (!node)
+ return { };
+ return editingIgnoresContent(*node) ? positionBeforeNode(node) : firstPositionInNode(node);
+}
+
+inline Position lastPositionInOrAfterNode(Node* node)
+{
+ if (!node)
+ return { };
+ return editingIgnoresContent(*node) ? positionAfterNode(node) : lastPositionInNode(node);
+}
+
+}
diff --git a/Source/WebCore/editing/markup.cpp b/Source/WebCore/editing/markup.cpp
index 5b6173b15..db57589af 100644
--- a/Source/WebCore/editing/markup.cpp
+++ b/Source/WebCore/editing/markup.cpp
@@ -13,10 +13,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -29,7 +29,6 @@
#include "config.h"
#include "markup.h"
-#include "CDATASection.h"
#include "CSSPrimitiveValue.h"
#include "CSSPropertyNames.h"
#include "CSSValue.h"
@@ -40,30 +39,33 @@
#include "Editor.h"
#include "ElementIterator.h"
#include "ExceptionCode.h"
-#include "ExceptionCodePlaceholder.h"
+#include "File.h"
#include "Frame.h"
+#include "HTMLAttachmentElement.h"
+#include "HTMLBRElement.h"
#include "HTMLBodyElement.h"
-#include "HTMLElement.h"
+#include "HTMLDivElement.h"
+#include "HTMLHeadElement.h"
+#include "HTMLHtmlElement.h"
#include "HTMLNames.h"
#include "HTMLTableElement.h"
#include "HTMLTextAreaElement.h"
#include "HTMLTextFormControlElement.h"
#include "URL.h"
#include "MarkupAccumulator.h"
+#include "NodeList.h"
#include "Range.h"
#include "RenderBlock.h"
+#include "Settings.h"
#include "StyleProperties.h"
#include "TextIterator.h"
+#include "TypedElementDescendantIterator.h"
#include "VisibleSelection.h"
#include "VisibleUnits.h"
#include "htmlediting.h"
#include <wtf/StdLibExtras.h>
#include <wtf/text/StringBuilder.h>
-#if ENABLE(DELETION_UI)
-#include "DeleteButtonController.h"
-#endif
-
namespace WebCore {
using namespace HTMLNames;
@@ -77,7 +79,7 @@ public:
{
}
- AttributeChange(PassRefPtr<Element> element, const QualifiedName& name, const String& value)
+ AttributeChange(Element* element, const QualifiedName& name, const String& value)
: m_element(element), m_name(name), m_value(value)
{
}
@@ -103,26 +105,28 @@ static void completeURLs(DocumentFragment* fragment, const String& baseURL)
if (!element.hasAttributes())
continue;
for (const Attribute& attribute : element.attributesIterator()) {
- if (element.isURLAttribute(attribute) && !attribute.value().isEmpty())
- changes.append(AttributeChange(&element, attribute.name(), URL(parsedBaseURL, attribute.value()).string()));
+ if (element.attributeContainsURL(attribute) && !attribute.value().isEmpty())
+ changes.append(AttributeChange(&element, attribute.name(), element.completeURLsInAttributeValue(parsedBaseURL, attribute)));
}
}
- size_t numChanges = changes.size();
- for (size_t i = 0; i < numChanges; ++i)
- changes[i].apply();
+ for (auto& change : changes)
+ change.apply();
}
class StyledMarkupAccumulator final : public MarkupAccumulator {
public:
enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode };
- StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs, EAnnotateForInterchange, const Range*, Node* highestNodeToBeSerialized = 0);
+ StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs, EAnnotateForInterchange, const Range*, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized = nullptr);
Node* serializeNodes(Node* startNode, Node* pastEnd);
void wrapWithNode(Node&, bool convertBlocksToInlines = false, RangeFullySelectsNode = DoesFullySelectNode);
void wrapWithStyleNode(StyleProperties*, Document&, bool isBlock = false);
String takeResults();
+
+ bool needRelativeStyleWrapper() const { return m_needRelativeStyleWrapper; }
+ bool needClearingDiv() const { return m_needClearingDiv; }
using MarkupAccumulator::appendString;
@@ -134,9 +138,10 @@ private:
String stringValueForRange(const Node&, const Range*);
void appendElement(StringBuilder& out, const Element&, bool addDisplayInline, RangeFullySelectsNode);
+ void appendCustomAttributes(StringBuilder&, const Element&, Namespaces*) override;
- virtual void appendText(StringBuilder& out, const Text&) override;
- virtual void appendElement(StringBuilder& out, const Element& element, Namespaces*) override
+ void appendText(StringBuilder& out, const Text&) override;
+ void appendElement(StringBuilder& out, const Element& element, Namespaces*) override
{
appendElement(out, element, false, DoesFullySelectNode);
}
@@ -158,23 +163,28 @@ private:
const EAnnotateForInterchange m_shouldAnnotate;
Node* m_highestNodeToBeSerialized;
RefPtr<EditingStyle> m_wrappingStyle;
+ bool m_needRelativeStyleWrapper;
+ bool m_needsPositionStyleConversion;
+ bool m_needClearingDiv;
};
-inline StyledMarkupAccumulator::StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, EAnnotateForInterchange shouldAnnotate,
- const Range* range, Node* highestNodeToBeSerialized)
+inline StyledMarkupAccumulator::StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, EAnnotateForInterchange shouldAnnotate, const Range* range, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized)
: MarkupAccumulator(nodes, shouldResolveURLs, range)
, m_shouldAnnotate(shouldAnnotate)
, m_highestNodeToBeSerialized(highestNodeToBeSerialized)
+ , m_needRelativeStyleWrapper(false)
+ , m_needsPositionStyleConversion(needsPositionStyleConversion)
+ , m_needClearingDiv(false)
{
}
void StyledMarkupAccumulator::wrapWithNode(Node& node, bool convertBlocksToInlines, RangeFullySelectsNode rangeFullySelectsNode)
{
StringBuilder markup;
- if (node.isElementNode())
- appendElement(markup, toElement(node), convertBlocksToInlines && isBlock(&node), rangeFullySelectsNode);
+ if (is<Element>(node))
+ appendElement(markup, downcast<Element>(node), convertBlocksToInlines && isBlock(&node), rangeFullySelectsNode);
else
- appendStartMarkup(markup, node, 0);
+ appendStartMarkup(markup, node, nullptr);
m_reversedPrecedingMarkup.append(markup.toString());
appendEndTag(node);
if (m_nodes)
@@ -203,8 +213,8 @@ void StyledMarkupAccumulator::appendStyleNodeOpenTag(StringBuilder& out, StylePr
const String& StyledMarkupAccumulator::styleNodeCloseTag(bool isBlock)
{
- DEFINE_STATIC_LOCAL(const String, divClose, (ASCIILiteral("</div>")));
- DEFINE_STATIC_LOCAL(const String, styleSpanClose, (ASCIILiteral("</span>")));
+ static NeverDestroyed<const String> divClose(ASCIILiteral("</div>"));
+ static NeverDestroyed<const String> styleSpanClose(ASCIILiteral("</span>"));
return isBlock ? divClose : styleSpanClose;
}
@@ -224,7 +234,7 @@ String StyledMarkupAccumulator::takeResults()
void StyledMarkupAccumulator::appendText(StringBuilder& out, const Text& text)
{
- const bool parentIsTextarea = text.parentElement() && isHTMLTextAreaElement(text.parentElement());
+ const bool parentIsTextarea = is<HTMLTextAreaElement>(text.parentElement());
const bool wrappingSpan = shouldApplyWrappingStyle(text) && !parentIsTextarea;
if (wrappingSpan) {
RefPtr<EditingStyle> wrappingStyle = m_wrappingStyle->copy();
@@ -240,7 +250,7 @@ void StyledMarkupAccumulator::appendText(StringBuilder& out, const Text& text)
if (!shouldAnnotate() || parentIsTextarea)
MarkupAccumulator::appendText(out, text);
else {
- const bool useRenderedText = !enclosingNodeWithTag(firstPositionInNode(const_cast<Text*>(&text)), selectTag);
+ const bool useRenderedText = !enclosingElementWithTag(firstPositionInNode(const_cast<Text*>(&text)), selectTag);
String content = useRenderedText ? renderedText(text, m_range) : stringValueForRange(text, m_range);
StringBuilder buffer;
appendCharactersReplacingEntities(buffer, content, 0, content.length(), EntityMaskInPCDATA);
@@ -253,21 +263,24 @@ void StyledMarkupAccumulator::appendText(StringBuilder& out, const Text& text)
String StyledMarkupAccumulator::renderedText(const Node& node, const Range* range)
{
- if (!node.isTextNode())
+ if (!is<Text>(node))
return String();
- const Text& textNode = toText(node);
+ const Text& textNode = downcast<Text>(node);
unsigned startOffset = 0;
unsigned endOffset = textNode.length();
- if (range && &node == range->startContainer())
+ TextIteratorBehavior behavior = TextIteratorDefaultBehavior;
+ if (range && &node == &range->startContainer())
startOffset = range->startOffset();
- if (range && &node == range->endContainer())
+ if (range && &node == &range->endContainer())
endOffset = range->endOffset();
+ else if (range)
+ behavior = TextIteratorBehavesAsIfNodesFollowing;
Position start = createLegacyEditingPosition(const_cast<Node*>(&node), startOffset);
Position end = createLegacyEditingPosition(const_cast<Node*>(&node), endOffset);
- return plainText(Range::create(node.document(), start, end).get());
+ return plainText(Range::create(node.document(), start, end).ptr(), behavior);
}
String StyledMarkupAccumulator::stringValueForRange(const Node& node, const Range* range)
@@ -276,18 +289,36 @@ String StyledMarkupAccumulator::stringValueForRange(const Node& node, const Rang
return node.nodeValue();
String nodeValue = node.nodeValue();
- if (&node == range->endContainer())
+ if (&node == &range->endContainer())
nodeValue.truncate(range->endOffset());
- if (&node == range->startContainer())
+ if (&node == &range->startContainer())
nodeValue.remove(0, range->startOffset());
return nodeValue;
}
+void StyledMarkupAccumulator::appendCustomAttributes(StringBuilder& out, const Element&element, Namespaces* namespaces)
+{
+#if ENABLE(ATTACHMENT_ELEMENT)
+ if (!is<HTMLAttachmentElement>(element))
+ return;
+
+ const HTMLAttachmentElement& attachment = downcast<HTMLAttachmentElement>(element);
+ if (attachment.file())
+ appendAttribute(out, element, Attribute(webkitattachmentpathAttr, attachment.file()->path()), namespaces);
+#else
+ UNUSED_PARAM(out);
+ UNUSED_PARAM(element);
+ UNUSED_PARAM(namespaces);
+#endif
+}
+
void StyledMarkupAccumulator::appendElement(StringBuilder& out, const Element& element, bool addDisplayInline, RangeFullySelectsNode rangeFullySelectsNode)
{
const bool documentIsHTML = element.document().isHTMLDocument();
appendOpenTag(out, element, 0);
+ appendCustomAttributes(out, element, nullptr);
+
const bool shouldAnnotateOrForceInline = element.isHTMLElement() && (shouldAnnotate() || addDisplayInline);
const bool shouldOverrideStyleAttr = shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element);
if (element.hasAttributes()) {
@@ -309,15 +340,20 @@ void StyledMarkupAccumulator::appendElement(StringBuilder& out, const Element& e
} else
newInlineStyle = EditingStyle::create();
- if (element.isStyledElement() && toStyledElement(element).inlineStyle())
- newInlineStyle->overrideWithStyle(toStyledElement(element).inlineStyle());
+ if (is<StyledElement>(element) && downcast<StyledElement>(element).inlineStyle())
+ newInlineStyle->overrideWithStyle(downcast<StyledElement>(element).inlineStyle());
if (shouldAnnotateOrForceInline) {
if (shouldAnnotate())
- newInlineStyle->mergeStyleFromRulesForSerialization(toHTMLElement(const_cast<Element*>(&element)));
+ newInlineStyle->mergeStyleFromRulesForSerialization(downcast<HTMLElement>(const_cast<Element*>(&element)));
if (addDisplayInline)
newInlineStyle->forceInline();
+
+ if (m_needsPositionStyleConversion) {
+ m_needRelativeStyleWrapper |= newInlineStyle->convertPositionStyle();
+ m_needClearingDiv |= newInlineStyle->isFloating();
+ }
// If the node is not fully selected by the range, then we don't want to keep styles that affect its relationship to the nodes around it
// only the ones that affect it and the nodes within it.
@@ -353,7 +389,7 @@ Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, No
const bool shouldEmit = traversalMode == EmitString;
Vector<Node*> ancestorsToClose;
Node* next;
- Node* lastClosed = 0;
+ Node* lastClosed = nullptr;
for (Node* n = startNode; n != pastEnd; n = next) {
// According to <rdar://problem/5730668>, it is possible for n to blow
// past pastEnd and become null here. This shouldn't be possible.
@@ -363,17 +399,18 @@ Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, No
if (!n)
break;
- next = NodeTraversal::next(n);
+ next = NodeTraversal::next(*n);
bool openedTag = false;
- if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd)
+ if (isBlock(n) && canHaveChildrenForEditing(*n) && next == pastEnd) {
// Don't write out empty block containers that aren't fully selected.
continue;
+ }
- if (!n->renderer() && !enclosingNodeWithTag(firstPositionInOrBeforeNode(n), selectTag)) {
- next = NodeTraversal::nextSkippingChildren(n);
+ if (!n->renderer() && !enclosingElementWithTag(firstPositionInOrBeforeNode(n), selectTag)) {
+ next = NodeTraversal::nextSkippingChildren(*n);
// Don't skip over pastEnd.
- if (pastEnd && pastEnd->isDescendantOf(n))
+ if (pastEnd && pastEnd->isDescendantOf(*n))
next = pastEnd;
} else {
// Add the node to the markup if we're not skipping the descendants
@@ -381,7 +418,7 @@ Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, No
appendStartTag(*n);
// If node has no children, close the tag now.
- if (!n->childNodeCount()) {
+ if (!n->hasChildNodes()) {
if (shouldEmit)
appendEndTag(*n);
lastClosed = n;
@@ -415,7 +452,7 @@ Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, No
if (!parent->renderer())
continue;
// or b) ancestors that we never encountered during a pre-order traversal starting at startNode:
- ASSERT(startNode->isDescendantOf(parent));
+ ASSERT(startNode->isDescendantOf(*parent));
if (shouldEmit)
wrapWithNode(*parent);
lastClosed = parent;
@@ -430,11 +467,11 @@ Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, No
static Node* ancestorToRetainStructureAndAppearanceForBlock(Node* commonAncestorBlock)
{
if (!commonAncestorBlock)
- return 0;
+ return nullptr;
if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) {
ContainerNode* table = commonAncestorBlock->parentNode();
- while (table && !isHTMLTableElement(table))
+ while (table && !is<HTMLTableElement>(*table))
table = table->parentNode();
return table;
@@ -443,7 +480,7 @@ static Node* ancestorToRetainStructureAndAppearanceForBlock(Node* commonAncestor
if (isNonTableCellHTMLBlockElement(commonAncestorBlock))
return commonAncestorBlock;
- return 0;
+ return nullptr;
}
static inline Node* ancestorToRetainStructureAndAppearance(Node* commonAncestor)
@@ -458,9 +495,9 @@ static bool propertyMissingOrEqualToNone(StyleProperties* style, CSSPropertyID p
RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
if (!value)
return true;
- if (!value->isPrimitiveValue())
+ if (!is<CSSPrimitiveValue>(*value))
return false;
- return toCSSPrimitiveValue(value.get())->getValueID() == CSSValueNone;
+ return downcast<CSSPrimitiveValue>(*value).valueID() == CSSValueNone;
}
static bool needInterchangeNewlineAfter(const VisiblePosition& v)
@@ -472,17 +509,17 @@ static bool needInterchangeNewlineAfter(const VisiblePosition& v)
return isEndOfParagraph(v) && isStartOfParagraph(next) && !(upstreamNode->hasTagName(brTag) && upstreamNode == downstreamNode);
}
-static PassRefPtr<EditingStyle> styleFromMatchedRulesAndInlineDecl(const Node* node)
+static RefPtr<EditingStyle> styleFromMatchedRulesAndInlineDecl(const Node* node)
{
if (!node->isHTMLElement())
- return 0;
+ return nullptr;
// FIXME: Having to const_cast here is ugly, but it is quite a bit of work to untangle
// the non-const-ness of styleFromMatchedRulesForElement.
HTMLElement* element = const_cast<HTMLElement*>(static_cast<const HTMLElement*>(node));
RefPtr<EditingStyle> style = EditingStyle::create(element->inlineStyle());
style->mergeStyleFromRules(element);
- return style.release();
+ return style;
}
static bool isElementPresentational(const Node* node)
@@ -493,18 +530,18 @@ static bool isElementPresentational(const Node* node)
static Node* highestAncestorToWrapMarkup(const Range* range, EAnnotateForInterchange shouldAnnotate)
{
- Node* commonAncestor = range->commonAncestorContainer(IGNORE_EXCEPTION);
+ auto* commonAncestor = range->commonAncestorContainer();
ASSERT(commonAncestor);
- Node* specialCommonAncestor = 0;
+ Node* specialCommonAncestor = nullptr;
if (shouldAnnotate == AnnotateForInterchange) {
// Include ancestors that aren't completely inside the range but are required to retain
// the structure and appearance of the copied markup.
specialCommonAncestor = ancestorToRetainStructureAndAppearance(commonAncestor);
- if (Node* parentListNode = enclosingNodeOfType(firstPositionInOrBeforeNode(range->firstNode()), isListItem)) {
- if (WebCore::areRangesEqual(VisibleSelection::selectionFromContentsOfNode(parentListNode).toNormalizedRange().get(), range)) {
+ if (auto* parentListNode = enclosingNodeOfType(firstPositionInOrBeforeNode(range->firstNode()), isListItem)) {
+ if (!editingIgnoresContent(*parentListNode) && WebCore::areRangesEqual(VisibleSelection::selectionFromContentsOfNode(parentListNode).toNormalizedRange().get(), range)) {
specialCommonAncestor = parentListNode->parentNode();
- while (specialCommonAncestor && !isListElement(specialCommonAncestor))
+ while (specialCommonAncestor && !isListHTMLElement(specialCommonAncestor))
specialCommonAncestor = specialCommonAncestor->parentNode();
}
}
@@ -514,7 +551,7 @@ static Node* highestAncestorToWrapMarkup(const Range* range, EAnnotateForInterch
specialCommonAncestor = highestMailBlockquote;
}
- Node* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor;
+ auto* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor;
if (checkAncestor->renderer() && checkAncestor->renderer()->containingBlock()) {
Node* newSpecialCommonAncestor = highestEnclosingNodeOfType(firstPositionInNode(checkAncestor), &isElementPresentational, CanCrossEditingBoundary, checkAncestor->renderer()->containingBlock()->element());
if (newSpecialCommonAncestor)
@@ -530,7 +567,7 @@ static Node* highestAncestorToWrapMarkup(const Range* range, EAnnotateForInterch
if (!specialCommonAncestor && isTabSpanNode(commonAncestor))
specialCommonAncestor = commonAncestor;
- if (Node *enclosingAnchor = enclosingNodeWithTag(firstPositionInNode(specialCommonAncestor ? specialCommonAncestor : commonAncestor), aTag))
+ if (auto* enclosingAnchor = enclosingElementWithTag(firstPositionInNode(specialCommonAncestor ? specialCommonAncestor : commonAncestor), aTag))
specialCommonAncestor = enclosingAnchor;
return specialCommonAncestor;
@@ -538,33 +575,36 @@ static Node* highestAncestorToWrapMarkup(const Range* range, EAnnotateForInterch
// FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange?
// FIXME: At least, annotation and style info should probably not be included in range.markupString()
-static String createMarkupInternal(Document& document, const Range& range, const Range& updatedRange, Vector<Node*>* nodes,
+static String createMarkupInternal(Document& document, const Range& range, Vector<Node*>* nodes,
EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs)
{
- DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, (ASCIILiteral("<br class=\"" AppleInterchangeNewline "\">")));
+ static NeverDestroyed<const String> interchangeNewlineString(ASCIILiteral("<br class=\"" AppleInterchangeNewline "\">"));
- bool collapsed = updatedRange.collapsed(ASSERT_NO_EXCEPTION);
+ bool collapsed = range.collapsed();
if (collapsed)
return emptyString();
- Node* commonAncestor = updatedRange.commonAncestorContainer(ASSERT_NO_EXCEPTION);
+ Node* commonAncestor = range.commonAncestorContainer();
if (!commonAncestor)
return emptyString();
document.updateLayoutIgnorePendingStylesheets();
- Node* body = enclosingNodeWithTag(firstPositionInNode(commonAncestor), bodyTag);
- Node* fullySelectedRoot = 0;
+ auto* body = enclosingElementWithTag(firstPositionInNode(commonAncestor), bodyTag);
+ Element* fullySelectedRoot = nullptr;
// FIXME: Do this for all fully selected blocks, not just the body.
- if (body && areRangesEqual(VisibleSelection::selectionFromContentsOfNode(body).toNormalizedRange().get(), &range))
+ if (body && VisiblePosition(firstPositionInNode(body)) == VisiblePosition(range.startPosition())
+ && VisiblePosition(lastPositionInNode(body)) == VisiblePosition(range.endPosition()))
fullySelectedRoot = body;
- Node* specialCommonAncestor = highestAncestorToWrapMarkup(&updatedRange, shouldAnnotate);
+ Node* specialCommonAncestor = highestAncestorToWrapMarkup(&range, shouldAnnotate);
- StyledMarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, &updatedRange, specialCommonAncestor);
- Node* pastEnd = updatedRange.pastLastNode();
+ bool needsPositionStyleConversion = body && fullySelectedRoot == body
+ && document.settings().shouldConvertPositionStyleOnCopy();
+ StyledMarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, &range, needsPositionStyleConversion, specialCommonAncestor);
+ Node* pastEnd = range.pastLastNode();
- Node* startNode = updatedRange.firstNode();
- VisiblePosition visibleStart(updatedRange.startPosition(), VP_DEFAULT_AFFINITY);
- VisiblePosition visibleEnd(updatedRange.endPosition(), VP_DEFAULT_AFFINITY);
+ Node* startNode = range.firstNode();
+ VisiblePosition visibleStart(range.startPosition(), VP_DEFAULT_AFFINITY);
+ VisiblePosition visibleEnd(range.endPosition(), VP_DEFAULT_AFFINITY);
if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleStart)) {
if (visibleStart == visibleEnd.previous())
return interchangeNewlineString;
@@ -572,7 +612,7 @@ static String createMarkupInternal(Document& document, const Range& range, const
accumulator.appendString(interchangeNewlineString);
startNode = visibleStart.next().deepEquivalent().deprecatedNode();
- if (pastEnd && Range::compareBoundaryPoints(startNode, 0, pastEnd, 0, ASSERT_NO_EXCEPTION) >= 0)
+ if (pastEnd && Range::compareBoundaryPoints(startNode, 0, pastEnd, 0).releaseReturnValue() >= 0)
return interchangeNewlineString;
}
@@ -587,8 +627,8 @@ static String createMarkupInternal(Document& document, const Range& range, const
// Bring the background attribute over, but not as an attribute because a background attribute on a div
// appears to have no effect.
if ((!fullySelectedRootStyle || !fullySelectedRootStyle->style() || !fullySelectedRootStyle->style()->getPropertyCSSValue(CSSPropertyBackgroundImage))
- && toElement(fullySelectedRoot)->hasAttribute(backgroundAttr))
- fullySelectedRootStyle->style()->setProperty(CSSPropertyBackgroundImage, "url('" + toElement(fullySelectedRoot)->getAttribute(backgroundAttr) + "')");
+ && fullySelectedRoot->hasAttributeWithoutSynchronization(backgroundAttr))
+ fullySelectedRootStyle->style()->setProperty(CSSPropertyBackgroundImage, "url('" + fullySelectedRoot->getAttribute(backgroundAttr) + "')");
if (fullySelectedRootStyle->style()) {
// Reset the CSS properties to avoid an assertion error in addStyleMarkup().
@@ -608,12 +648,18 @@ static String createMarkupInternal(Document& document, const Range& range, const
if (nodes)
nodes->append(ancestor);
- lastClosed = ancestor;
-
if (ancestor == specialCommonAncestor)
break;
}
}
+
+ if (accumulator.needRelativeStyleWrapper() && needsPositionStyleConversion) {
+ if (accumulator.needClearingDiv())
+ accumulator.appendString("<div style=\"clear: both;\"></div>");
+ RefPtr<EditingStyle> positionRelativeStyle = styleFromMatchedRulesAndInlineDecl(body);
+ positionRelativeStyle->style()->setProperty(CSSPropertyPosition, CSSValueRelative);
+ accumulator.wrapWithStyleNode(positionRelativeStyle->style(), document, true);
+ }
// FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally.
if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleEnd.previous()))
@@ -624,62 +670,46 @@ static String createMarkupInternal(Document& document, const Range& range, const
String createMarkup(const Range& range, Vector<Node*>* nodes, EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs)
{
- Document& document = range.ownerDocument();
- const Range* updatedRange = &range;
-
-#if ENABLE(DELETION_UI)
- // Disable the delete button so it's elements are not serialized into the markup,
- // but make sure neither endpoint is inside the delete user interface.
- Frame* frame = document.frame();
- DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(frame);
-
- RefPtr<Range> updatedRangeRef;
- if (frame) {
- updatedRangeRef = frame->editor().avoidIntersectionWithDeleteButtonController(&range);
- updatedRange = updatedRangeRef.get();
- if (!updatedRange)
- return emptyString();
- }
-#endif
-
- return createMarkupInternal(document, range, *updatedRange, nodes, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs);
+ return createMarkupInternal(range.ownerDocument(), range, nodes, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs);
}
-PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document& document, const String& markup, const String& baseURL, ParserContentPolicy parserContentPolicy)
+Ref<DocumentFragment> createFragmentFromMarkup(Document& document, const String& markup, const String& baseURL, ParserContentPolicy parserContentPolicy)
{
// We use a fake body element here to trick the HTML parser to using the InBody insertion mode.
- RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(document);
- RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
+ auto fakeBody = HTMLBodyElement::create(document);
+ auto fragment = DocumentFragment::create(document);
+
+ fragment->parseHTML(markup, fakeBody.ptr(), parserContentPolicy);
- fragment->parseHTML(markup, fakeBody.get(), parserContentPolicy);
+#if ENABLE(ATTACHMENT_ELEMENT)
+ // When creating a fragment we must strip the webkit-attachment-path attribute after restoring the File object.
+ Vector<Ref<HTMLAttachmentElement>> attachments;
+ for (auto& attachment : descendantsOfType<HTMLAttachmentElement>(fragment))
+ attachments.append(attachment);
+ for (auto& attachment : attachments) {
+ attachment->setFile(File::create(attachment->attributeWithoutSynchronization(webkitattachmentpathAttr)).ptr());
+ attachment->removeAttribute(webkitattachmentpathAttr);
+ }
+#endif
if (!baseURL.isEmpty() && baseURL != blankURL() && baseURL != document.baseURL())
- completeURLs(fragment.get(), baseURL);
+ completeURLs(fragment.ptr(), baseURL);
- return fragment.release();
+ return fragment;
}
String createMarkup(const Node& node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, Vector<QualifiedName>* tagNamesToSkip, EFragmentSerialization fragmentSerialization)
{
- HTMLElement* deleteButtonContainerElement = 0;
-#if ENABLE(DELETION_UI)
- if (Frame* frame = node.document().frame()) {
- deleteButtonContainerElement = frame->editor().deleteButtonController().containerElement();
- if (node.isDescendantOf(deleteButtonContainerElement))
- return emptyString();
- }
-#endif
-
MarkupAccumulator accumulator(nodes, shouldResolveURLs, 0, fragmentSerialization);
- return accumulator.serializeNodes(const_cast<Node&>(node), deleteButtonContainerElement, childrenOnly, tagNamesToSkip);
+ return accumulator.serializeNodes(const_cast<Node&>(node), childrenOnly, tagNamesToSkip);
}
-static void fillContainerFromString(ContainerNode* paragraph, const String& string)
+static void fillContainerFromString(ContainerNode& paragraph, const String& string)
{
- Document& document = paragraph->document();
+ Document& document = paragraph.document();
if (string.isEmpty()) {
- paragraph->appendChild(createBlockPlaceholderElement(document), ASSERT_NO_EXCEPTION);
+ paragraph.appendChild(createBlockPlaceholderElement(document));
return;
}
@@ -696,11 +726,11 @@ static void fillContainerFromString(ContainerNode* paragraph, const String& stri
// append the non-tab textual part
if (!s.isEmpty()) {
if (!tabText.isEmpty()) {
- paragraph->appendChild(createTabSpanElement(document, tabText), ASSERT_NO_EXCEPTION);
+ paragraph.appendChild(createTabSpanElement(document, tabText));
tabText = emptyString();
}
- RefPtr<Node> textNode = document.createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries));
- paragraph->appendChild(textNode.release(), ASSERT_NO_EXCEPTION);
+ Ref<Node> textNode = document.createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries));
+ paragraph.appendChild(textNode);
}
// there is a tab after every entry, except the last entry
@@ -708,21 +738,34 @@ static void fillContainerFromString(ContainerNode* paragraph, const String& stri
if (i + 1 != numEntries)
tabText.append('\t');
else if (!tabText.isEmpty())
- paragraph->appendChild(createTabSpanElement(document, tabText), ASSERT_NO_EXCEPTION);
+ paragraph.appendChild(createTabSpanElement(document, tabText));
first = false;
}
}
-bool isPlainTextMarkup(Node *node)
+bool isPlainTextMarkup(Node* node)
{
- if (!node->isElementNode() || !node->hasTagName(divTag) || toElement(node)->hasAttributes())
+ ASSERT(node);
+ if (!is<HTMLDivElement>(*node))
+ return false;
+
+ HTMLDivElement& element = downcast<HTMLDivElement>(*node);
+ if (element.hasAttributes())
+ return false;
+
+ Node* firstChild = element.firstChild();
+ if (!firstChild)
return false;
+
+ Node* secondChild = firstChild->nextSibling();
+ if (!secondChild)
+ return firstChild->isTextNode() || firstChild->firstChild();
- if (node->childNodeCount() == 1 && (node->firstChild()->isTextNode() || (node->firstChild()->firstChild())))
- return true;
+ if (secondChild->nextSibling())
+ return false;
- return (node->childNodeCount() == 2 && isTabSpanTextNode(node->firstChild()->firstChild()) && node->firstChild()->nextSibling()->isTextNode());
+ return isTabSpanTextNode(firstChild->firstChild()) && secondChild->isTextNode();
}
static bool contextPreservesNewline(const Range& context)
@@ -735,37 +778,37 @@ static bool contextPreservesNewline(const Range& context)
return container->renderer()->style().preserveNewline();
}
-PassRefPtr<DocumentFragment> createFragmentFromText(Range& context, const String& text)
+Ref<DocumentFragment> createFragmentFromText(Range& context, const String& text)
{
Document& document = context.ownerDocument();
- RefPtr<DocumentFragment> fragment = document.createDocumentFragment();
+ Ref<DocumentFragment> fragment = document.createDocumentFragment();
if (text.isEmpty())
- return fragment.release();
+ return fragment;
String string = text;
string.replace("\r\n", "\n");
string.replace('\r', '\n');
if (contextPreservesNewline(context)) {
- fragment->appendChild(document.createTextNode(string), ASSERT_NO_EXCEPTION);
+ fragment->appendChild(document.createTextNode(string));
if (string.endsWith('\n')) {
- RefPtr<Element> element = createBreakElement(document);
- element->setAttribute(classAttr, AppleInterchangeNewline);
- fragment->appendChild(element.release(), ASSERT_NO_EXCEPTION);
+ auto element = HTMLBRElement::create(document);
+ element->setAttributeWithoutSynchronization(classAttr, AppleInterchangeNewline);
+ fragment->appendChild(element);
}
- return fragment.release();
+ return fragment;
}
// A string with no newlines gets added inline, rather than being put into a paragraph.
if (string.find('\n') == notFound) {
- fillContainerFromString(fragment.get(), string);
- return fragment.release();
+ fillContainerFromString(fragment, string);
+ return fragment;
}
// Break string into paragraphs. Extra line breaks turn into empty paragraphs.
Node* blockNode = enclosingBlock(context.firstNode());
- Element* block = toElement(blockNode);
+ Element* block = downcast<Element>(blockNode);
bool useClonesOfEnclosingBlock = blockNode
&& blockNode->isElementNode()
&& !block->hasTagName(bodyTag)
@@ -782,21 +825,21 @@ PassRefPtr<DocumentFragment> createFragmentFromText(Range& context, const String
RefPtr<Element> element;
if (s.isEmpty() && i + 1 == numLines) {
// For last line, use the "magic BR" rather than a P.
- element = createBreakElement(document);
- element->setAttribute(classAttr, AppleInterchangeNewline);
+ element = HTMLBRElement::create(document);
+ element->setAttributeWithoutSynchronization(classAttr, AppleInterchangeNewline);
} else if (useLineBreak) {
- element = createBreakElement(document);
- fillContainerFromString(fragment.get(), s);
+ element = HTMLBRElement::create(document);
+ fillContainerFromString(fragment, s);
} else {
if (useClonesOfEnclosingBlock)
- element = block->cloneElementWithoutChildren();
+ element = block->cloneElementWithoutChildren(document);
else
element = createDefaultParagraphElement(document);
- fillContainerFromString(element.get(), s);
+ fillContainerFromString(*element, s);
}
- fragment->appendChild(element.release(), ASSERT_NO_EXCEPTION);
+ fragment->appendChild(*element);
}
- return fragment.release();
+ return fragment;
}
String documentTypeString(const Document& document)
@@ -821,81 +864,73 @@ String createFullMarkup(const Node& node)
String createFullMarkup(const Range& range)
{
- Node* node = range.startContainer();
- if (!node)
- return String();
-
// FIXME: This is always "for interchange". Is that right?
- return documentTypeString(node->document()) + createMarkup(range, 0, AnnotateForInterchange);
+ return documentTypeString(range.startContainer().document()) + createMarkup(range, 0, AnnotateForInterchange);
}
String urlToMarkup(const URL& url, const String& title)
{
StringBuilder markup;
- markup.append("<a href=\"");
+ markup.appendLiteral("<a href=\"");
markup.append(url.string());
- markup.append("\">");
+ markup.appendLiteral("\">");
MarkupAccumulator::appendCharactersReplacingEntities(markup, title, 0, title.length(), EntityMaskInPCDATA);
- markup.append("</a>");
+ markup.appendLiteral("</a>");
return markup.toString();
}
-PassRefPtr<DocumentFragment> createFragmentForInnerOuterHTML(const String& markup, Element* contextElement, ParserContentPolicy parserContentPolicy, ExceptionCode& ec)
+ExceptionOr<Ref<DocumentFragment>> createFragmentForInnerOuterHTML(Element& contextElement, const String& markup, ParserContentPolicy parserContentPolicy)
{
- Document* document = &contextElement->document();
-#if ENABLE(TEMPLATE_ELEMENT)
- if (contextElement->hasTagName(templateTag))
- document = document->ensureTemplateDocument();
-#endif
- RefPtr<DocumentFragment> fragment = DocumentFragment::create(*document);
+ auto* document = &contextElement.document();
+ if (contextElement.hasTagName(templateTag))
+ document = &document->ensureTemplateDocument();
+ auto fragment = DocumentFragment::create(*document);
if (document->isHTMLDocument()) {
- fragment->parseHTML(markup, contextElement, parserContentPolicy);
- return fragment;
+ fragment->parseHTML(markup, &contextElement, parserContentPolicy);
+ return WTFMove(fragment);
}
- bool wasValid = fragment->parseXML(markup, contextElement, parserContentPolicy);
- if (!wasValid) {
- ec = SYNTAX_ERR;
- return 0;
- }
- return fragment.release();
+ bool wasValid = fragment->parseXML(markup, &contextElement, parserContentPolicy);
+ if (!wasValid)
+ return Exception { SYNTAX_ERR };
+ return WTFMove(fragment);
}
-PassRefPtr<DocumentFragment> createFragmentForTransformToFragment(const String& sourceString, const String& sourceMIMEType, Document* outputDoc)
+RefPtr<DocumentFragment> createFragmentForTransformToFragment(Document& outputDoc, const String& sourceString, const String& sourceMIMEType)
{
- RefPtr<DocumentFragment> fragment = outputDoc->createDocumentFragment();
+ RefPtr<DocumentFragment> fragment = outputDoc.createDocumentFragment();
if (sourceMIMEType == "text/html") {
// As far as I can tell, there isn't a spec for how transformToFragment is supposed to work.
// Based on the documentation I can find, it looks like we want to start parsing the fragment in the InBody insertion mode.
// Unfortunately, that's an implementation detail of the parser.
// We achieve that effect here by passing in a fake body element as context for the fragment.
- RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(*outputDoc);
+ RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(outputDoc);
fragment->parseHTML(sourceString, fakeBody.get());
} else if (sourceMIMEType == "text/plain")
- fragment->parserAppendChild(Text::create(*outputDoc, sourceString));
+ fragment->parserAppendChild(Text::create(outputDoc, sourceString));
else {
bool successfulParse = fragment->parseXML(sourceString, 0);
if (!successfulParse)
- return 0;
+ return nullptr;
}
// FIXME: Do we need to mess with URLs here?
- return fragment.release();
+ return fragment;
}
static Vector<Ref<HTMLElement>> collectElementsToRemoveFromFragment(ContainerNode& container)
{
Vector<Ref<HTMLElement>> toRemove;
for (auto& element : childrenOfType<HTMLElement>(container)) {
- if (isHTMLHtmlElement(element)) {
+ if (is<HTMLHtmlElement>(element)) {
toRemove.append(element);
collectElementsToRemoveFromFragment(element);
continue;
}
- if (isHTMLHeadElement(element) || isHTMLBodyElement(element))
+ if (is<HTMLHeadElement>(element) || is<HTMLBodyElement>(element))
toRemove.append(element);
}
return toRemove;
@@ -906,94 +941,80 @@ static void removeElementFromFragmentPreservingChildren(DocumentFragment& fragme
RefPtr<Node> nextChild;
for (RefPtr<Node> child = element.firstChild(); child; child = nextChild) {
nextChild = child->nextSibling();
- element.removeChild(child.get(), ASSERT_NO_EXCEPTION);
- fragment.insertBefore(child, &element, ASSERT_NO_EXCEPTION);
+ element.removeChild(*child);
+ fragment.insertBefore(*child, &element);
}
- fragment.removeChild(&element, ASSERT_NO_EXCEPTION);
+ fragment.removeChild(element);
}
-PassRefPtr<DocumentFragment> createContextualFragment(const String& markup, HTMLElement* element, ParserContentPolicy parserContentPolicy, ExceptionCode& ec)
+ExceptionOr<Ref<DocumentFragment>> createContextualFragment(Element& element, const String& markup, ParserContentPolicy parserContentPolicy)
{
- ASSERT(element);
- if (element->ieForbidsInsertHTML()) {
- ec = NOT_SUPPORTED_ERR;
- return 0;
- }
-
- if (element->hasLocalName(colTag) || element->hasLocalName(colgroupTag) || element->hasLocalName(framesetTag)
- || element->hasLocalName(headTag) || element->hasLocalName(styleTag) || element->hasLocalName(titleTag)) {
- ec = NOT_SUPPORTED_ERR;
- return 0;
- }
+ auto result = createFragmentForInnerOuterHTML(element, markup, parserContentPolicy);
+ if (result.hasException())
+ return result.releaseException();
- RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, element, parserContentPolicy, ec);
- if (!fragment)
- return 0;
+ auto fragment = result.releaseReturnValue();
// We need to pop <html> and <body> elements and remove <head> to
// accommodate folks passing complete HTML documents to make the
// child of an element.
- auto toRemove = collectElementsToRemoveFromFragment(*fragment);
- for (unsigned i = 0; i < toRemove.size(); ++i)
- removeElementFromFragmentPreservingChildren(*fragment, toRemove[i].get());
+ auto toRemove = collectElementsToRemoveFromFragment(fragment);
+ for (auto& element : toRemove)
+ removeElementFromFragmentPreservingChildren(fragment, element);
- return fragment.release();
+ return WTFMove(fragment);
}
-static inline bool hasOneChild(ContainerNode* node)
+static inline bool hasOneChild(ContainerNode& node)
{
- Node* firstChild = node->firstChild();
+ Node* firstChild = node.firstChild();
return firstChild && !firstChild->nextSibling();
}
-static inline bool hasOneTextChild(ContainerNode* node)
+static inline bool hasOneTextChild(ContainerNode& node)
{
- return hasOneChild(node) && node->firstChild()->isTextNode();
+ return hasOneChild(node) && node.firstChild()->isTextNode();
}
-void replaceChildrenWithFragment(ContainerNode& container, PassRefPtr<DocumentFragment> fragment, ExceptionCode& ec)
+static inline bool hasMutationEventListeners(const Document& document)
{
- Ref<ContainerNode> containerNode(container);
- ChildListMutationScope mutation(containerNode.get());
-
- if (!fragment->firstChild()) {
- containerNode->removeChildren();
- return;
- }
-
- if (hasOneTextChild(&containerNode.get()) && hasOneTextChild(fragment.get())) {
- toText(containerNode->firstChild())->setData(toText(fragment->firstChild())->data(), ec);
- return;
- }
-
- if (hasOneChild(&containerNode.get())) {
- containerNode->replaceChild(fragment, containerNode->firstChild(), ec);
- return;
- }
+ return document.hasListenerType(Document::DOMSUBTREEMODIFIED_LISTENER)
+ || document.hasListenerType(Document::DOMNODEINSERTED_LISTENER)
+ || document.hasListenerType(Document::DOMNODEREMOVED_LISTENER)
+ || document.hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)
+ || document.hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER);
+}
- containerNode->removeChildren();
- containerNode->appendChild(fragment, ec);
+// We can use setData instead of replacing Text node as long as script can't observe the difference.
+static inline bool canUseSetDataOptimization(const Text& containerChild, const ChildListMutationScope& mutationScope)
+{
+ bool authorScriptMayHaveReference = containerChild.refCount();
+ return !authorScriptMayHaveReference && !mutationScope.canObserve() && !hasMutationEventListeners(containerChild.document());
}
-void replaceChildrenWithText(ContainerNode& container, const String& text, ExceptionCode& ec)
+ExceptionOr<void> replaceChildrenWithFragment(ContainerNode& container, Ref<DocumentFragment>&& fragment)
{
Ref<ContainerNode> containerNode(container);
- ChildListMutationScope mutation(containerNode.get());
+ ChildListMutationScope mutation(containerNode);
- if (hasOneTextChild(&containerNode.get())) {
- toText(containerNode->firstChild())->setData(text, ec);
- return;
+ if (!fragment->firstChild()) {
+ containerNode->removeChildren();
+ return { };
}
- RefPtr<Text> textNode = Text::create(containerNode->document(), text);
+ auto* containerChild = containerNode->firstChild();
+ if (containerChild && !containerChild->nextSibling()) {
+ if (is<Text>(*containerChild) && hasOneTextChild(fragment) && canUseSetDataOptimization(downcast<Text>(*containerChild), mutation)) {
+ ASSERT(!fragment->firstChild()->refCount());
+ downcast<Text>(*containerChild).setData(downcast<Text>(*fragment->firstChild()).data());
+ return { };
+ }
- if (hasOneChild(&containerNode.get())) {
- containerNode->replaceChild(textNode.release(), containerNode->firstChild(), ec);
- return;
+ return containerNode->replaceChild(fragment, *containerChild);
}
containerNode->removeChildren();
- containerNode->appendChild(textNode.release(), ec);
+ return containerNode->appendChild(fragment);
}
}
diff --git a/Source/WebCore/editing/markup.h b/Source/WebCore/editing/markup.h
index 65b72adc3..8d4fe389d 100644
--- a/Source/WebCore/editing/markup.h
+++ b/Source/WebCore/editing/markup.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
+ * Copyright (C) 2004 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -10,10 +10,10 @@
* 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
+ * 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 COMPUTER, INC. OR
+ * 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
@@ -23,9 +23,9 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef markup_h
-#define markup_h
+#pragma once
+#include "ExceptionOr.h"
#include "FragmentScriptingPermission.h"
#include "HTMLInterchange.h"
#include <wtf/Forward.h>
@@ -43,35 +43,29 @@ class Node;
class QualifiedName;
class Range;
-typedef int ExceptionCode;
-
enum EChildrenOnly { IncludeNode, ChildrenOnly };
enum EAbsoluteURLs { DoNotResolveURLs, ResolveAllURLs, ResolveNonLocalURLs };
enum EFragmentSerialization { HTMLFragmentSerialization, XMLFragmentSerialization };
-PassRefPtr<DocumentFragment> createFragmentFromText(Range& context, const String& text);
-PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document&, const String& markup, const String& baseURL, ParserContentPolicy = AllowScriptingContent);
-PassRefPtr<DocumentFragment> createFragmentForInnerOuterHTML(const String&, Element*, ParserContentPolicy, ExceptionCode&);
-PassRefPtr<DocumentFragment> createFragmentForTransformToFragment(const String&, const String& sourceMIMEType, Document* outputDoc);
-PassRefPtr<DocumentFragment> createContextualFragment(const String&, HTMLElement*, ParserContentPolicy, ExceptionCode&);
+WEBCORE_EXPORT Ref<DocumentFragment> createFragmentFromText(Range& context, const String& text);
+WEBCORE_EXPORT Ref<DocumentFragment> createFragmentFromMarkup(Document&, const String& markup, const String& baseURL, ParserContentPolicy = AllowScriptingContent);
+ExceptionOr<Ref<DocumentFragment>> createFragmentForInnerOuterHTML(Element&, const String& markup, ParserContentPolicy);
+RefPtr<DocumentFragment> createFragmentForTransformToFragment(Document&, const String& sourceString, const String& sourceMIMEType);
+ExceptionOr<Ref<DocumentFragment>> createContextualFragment(Element&, const String& markup, ParserContentPolicy);
bool isPlainTextMarkup(Node*);
-// These methods are used by HTMLElement & ShadowRoot to replace the
-// children with respected fragment/text.
-void replaceChildrenWithFragment(ContainerNode&, PassRefPtr<DocumentFragment>, ExceptionCode&);
-void replaceChildrenWithText(ContainerNode&, const String&, ExceptionCode&);
+// These methods are used by HTMLElement & ShadowRoot to replace the children with respected fragment/text.
+ExceptionOr<void> replaceChildrenWithFragment(ContainerNode&, Ref<DocumentFragment>&&);
-String createMarkup(const Range&, Vector<Node*>* = 0, EAnnotateForInterchange = DoNotAnnotateForInterchange, bool convertBlocksToInlines = false, EAbsoluteURLs = DoNotResolveURLs);
-String createMarkup(const Node&, EChildrenOnly = IncludeNode, Vector<Node*>* = 0, EAbsoluteURLs = DoNotResolveURLs, Vector<QualifiedName>* tagNamesToSkip = 0, EFragmentSerialization = HTMLFragmentSerialization);
+String createMarkup(const Range&, Vector<Node*>* = nullptr, EAnnotateForInterchange = DoNotAnnotateForInterchange, bool convertBlocksToInlines = false, EAbsoluteURLs = DoNotResolveURLs);
+String createMarkup(const Node&, EChildrenOnly = IncludeNode, Vector<Node*>* = nullptr, EAbsoluteURLs = DoNotResolveURLs, Vector<QualifiedName>* tagNamesToSkip = nullptr, EFragmentSerialization = HTMLFragmentSerialization);
-String createFullMarkup(const Node&);
-String createFullMarkup(const Range&);
+WEBCORE_EXPORT String createFullMarkup(const Node&);
+WEBCORE_EXPORT String createFullMarkup(const Range&);
String urlToMarkup(const URL&, const String& title);
String documentTypeString(const Document&);
}
-
-#endif // markup_h