Modified: trunk/Source/WebCore/editing/CompositeEditCommand.cpp (276130 => 276131)
--- trunk/Source/WebCore/editing/CompositeEditCommand.cpp 2021-04-16 10:13:22 UTC (rev 276130)
+++ trunk/Source/WebCore/editing/CompositeEditCommand.cpp 2021-04-16 10:21:45 UTC (rev 276131)
@@ -64,6 +64,7 @@
#include "ReplaceNodeWithSpanCommand.h"
#include "ReplaceSelectionCommand.h"
#include "ScopedEventQueue.h"
+#include "ScriptDisallowedScope.h"
#include "SetNodeAttributeCommand.h"
#include "SplitElementCommand.h"
#include "SplitTextNodeCommand.h"
@@ -151,15 +152,15 @@
static void postTextStateChangeNotification(AXObjectCache* cache, const VisiblePosition& position, const String& deletedText, const String& insertedText)
{
ASSERT(cache);
- auto* node = highestEditableRoot(position.deepEquivalent(), HasEditableAXRole);
+ auto node = makeRefPtr(highestEditableRoot(position.deepEquivalent(), HasEditableAXRole));
if (!node)
return;
if (insertedText.length() && deletedText.length())
- cache->postTextReplacementNotification(node, AXTextEditTypeDelete, insertedText, AXTextEditTypeInsert, deletedText, position);
+ cache->postTextReplacementNotification(node.get(), AXTextEditTypeDelete, insertedText, AXTextEditTypeInsert, deletedText, position);
else if (deletedText.length())
- cache->postTextStateChangeNotification(node, AXTextEditTypeInsert, deletedText, position);
+ cache->postTextStateChangeNotification(node.get(), AXTextEditTypeInsert, deletedText, position);
else if (insertedText.length())
- cache->postTextStateChangeNotification(node, AXTextEditTypeDelete, insertedText, position);
+ cache->postTextStateChangeNotification(node.get(), AXTextEditTypeDelete, insertedText, position);
}
void AccessibilityUndoReplacedText::postTextStateChangeNotificationForUnapply(AXObjectCache* cache)
@@ -441,7 +442,7 @@
EditCommandComposition& CompositeEditCommand::ensureComposition()
{
- auto* command = this;
+ auto command = makeRefPtr(this);
while (auto* parent = command->parent())
command = parent;
if (!command->m_composition)
@@ -536,11 +537,12 @@
bool CompositeEditCommand::isRemovableBlock(const Node* node)
{
ASSERT(node);
+ // FIXME: We should support other elements that can be removed.
if (!is<HTMLDivElement>(*node))
return false;
- Node* parentNode = node->parentNode();
- if (parentNode && parentNode->firstChild() != parentNode->lastChild())
+ auto* parentNode = node->parentNode();
+ if (!parentNode || !parentNode->hasOneChild())
return false;
if (!downcast<HTMLDivElement>(*node).hasAttributes())
@@ -551,7 +553,7 @@
void CompositeEditCommand::insertNodeBefore(Ref<Node>&& insertChild, Node& refChild, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
{
- auto* parent = refChild.parentNode();
+ auto parent = makeRefPtr(refChild.parentNode());
if (!parent || (!parent->hasEditableStyle() && parent->renderer()))
return;
applyCommandToComposite(InsertNodeBeforeCommand::create(WTFMove(insertChild), refChild, shouldAssumeContentIsAlwaysEditable, editingAction()));
@@ -559,7 +561,7 @@
void CompositeEditCommand::insertNodeAfter(Ref<Node>&& insertChild, Node& refChild)
{
- ContainerNode* parent = refChild.parentNode();
+ auto parent = makeRefPtr(refChild.parentNode());
if (!parent)
return;
@@ -578,11 +580,11 @@
// For editing positions like [table, 0], insert before the table,
// likewise for replaced elements, brs, etc.
Position p = editingPosition.parentAnchoredEquivalent();
- Node* refChild = p.deprecatedNode();
+ auto refChild = makeRefPtr(p.deprecatedNode());
int offset = p.deprecatedEditingOffset();
if (canHaveChildrenForEditing(*refChild)) {
- Node* child = refChild->firstChild();
+ auto child = makeRefPtr(refChild->firstChild());
for (int i = 0; child && i < offset; i++)
child = child->nextSibling();
if (child)
@@ -611,7 +613,7 @@
void CompositeEditCommand::removeChildrenInRange(Node& node, unsigned from, unsigned to)
{
Vector<Ref<Node>> children;
- Node* child = node.traverseToChildAt(from);
+ auto child = makeRefPtr(node.traverseToChildAt(from));
for (unsigned i = from; child && i < to; i++, child = child->nextSibling())
children.append(*child);
@@ -666,13 +668,12 @@
// as a series of existing smaller edit commands. Someone who wanted to
// reduce the number of edit commands could do so here.
auto command = ReplaceNodeWithSpanCommand::create(element);
- auto* commandPtr = command.ptr();
- applyCommandToComposite(WTFMove(command));
+ applyCommandToComposite(command);
// 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(commandPtr->spanElement()->isConnected());
- return commandPtr->spanElement();
+ ASSERT(command->spanElement()->isConnected());
+ return command->spanElement();
}
void CompositeEditCommand::prune(Node* node)
@@ -822,16 +823,16 @@
return positionInParentAfterNode(position.anchorNode());
}
- auto* tabSpan = tabSpanNode(position.containerNode());
+ auto tabSpan = makeRefPtr(tabSpanNode(position.containerNode()));
if (position.offsetInContainerNode() <= caretMinOffset(*position.containerNode()))
- return positionInParentBeforeNode(tabSpan);
+ return positionInParentBeforeNode(tabSpan.get());
if (position.offsetInContainerNode() >= caretMaxOffset(*position.containerNode()))
- return positionInParentAfterNode(tabSpan);
+ return positionInParentAfterNode(tabSpan.get());
splitTextNodeContainingElement(downcast<Text>(*position.containerNode()), position.offsetInContainerNode());
- return positionInParentBeforeNode(tabSpan);
+ return positionInParentBeforeNode(tabSpan.get());
}
void CompositeEditCommand::insertNodeAtTabSpanPosition(Ref<Node>&& node, const Position& pos)
@@ -886,35 +887,37 @@
return containsOnlyDeprecatedEditingWhitespace(text);
}
-bool CompositeEditCommand::canRebalance(const Position& position) const
+RefPtr<Text> CompositeEditCommand::textNodeForRebalance(const Position& position) const
{
- Node* node = position.containerNode();
+ auto node = makeRefPtr(position.containerNode());
if (position.anchorType() != Position::PositionIsOffsetInAnchor || !is<Text>(node))
- return false;
+ return nullptr;
- Text& textNode = downcast<Text>(*node);
- if (!textNode.length())
- return false;
+ auto textNode = static_pointer_cast<Text>(std::exchange(node, nullptr));
+ if (!textNode->length())
+ return nullptr;
- node->document().updateStyleIfNeeded();
+ textNode->document().updateStyleIfNeeded();
- RenderObject* renderer = textNode.renderer();
+ ScriptDisallowedScope::InMainThread scriptDisallowedScope;
+
+ RenderObject* renderer = textNode->renderer();
if (renderer && !renderer->style().collapseWhiteSpace())
- return false;
+ return nullptr;
- return true;
+ return textNode;
}
// FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, cousins, etc).
void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
{
- Node* node = position.containerNode();
- if (!canRebalance(position))
+ auto textNode = textNodeForRebalance(position);
+ if (!textNode)
return;
// 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 = downcast<Text>(*node).data();
+ String text = textNode->data();
if (!deprecatedIsEditingWhitespace(text[offset])) {
offset--;
if (offset < 0 || !deprecatedIsEditingWhitespace(text[offset]))
@@ -921,7 +924,7 @@
return;
}
- rebalanceWhitespaceOnTextSubstring(downcast<Text>(*node), position.offsetInContainerNode(), position.offsetInContainerNode());
+ rebalanceWhitespaceOnTextSubstring(*textNode, position.offsetInContainerNode(), position.offsetInContainerNode());
}
void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(Text& textNode, int startOffset, int endOffset)
@@ -930,12 +933,12 @@
ASSERT(!text.isEmpty());
// Set upstream and downstream to define the extent of the whitespace surrounding text[offset].
- int upstream = startOffset;
+ unsigned upstream = std::max(0, startOffset);
while (upstream > 0 && deprecatedIsEditingWhitespace(text[upstream - 1]))
upstream--;
- int downstream = endOffset;
- while ((unsigned)downstream < text.length() && deprecatedIsEditingWhitespace(text[downstream]))
+ unsigned downstream = std::max(0, endOffset);
+ while (downstream < text.length() && deprecatedIsEditingWhitespace(text[downstream]))
downstream++;
int length = downstream - upstream;
@@ -946,12 +949,11 @@
VisiblePosition visibleDownstreamPos(Position(&textNode, downstream));
String string = text.substring(upstream, length);
- String rebalancedString = stringWithRebalancedWhitespace(string,
// FIXME: Because of the problem mentioned at the top of this function, we must also use nbsps at the start/end of the string because
// this function doesn't get all surrounding whitespace, just the whitespace in the current text node.
- isStartOfParagraph(visibleUpstreamPos) || upstream == 0,
- isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length());
-
+ String rebalancedString = stringWithRebalancedWhitespace(string, isStartOfParagraph(visibleUpstreamPos) || !upstream,
+ isEndOfParagraph(visibleDownstreamPos) || downstream == text.length());
+
if (string != rebalancedString)
replaceTextInNodePreservingMarkers(textNode, upstream, length, rebalancedString);
}
@@ -958,7 +960,7 @@
void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& position)
{
- Node* node = position.deprecatedNode();
+ auto node = makeRefPtr(position.deprecatedNode());
if (!is<Text>(node))
return;
Text& textNode = downcast<Text>(*node);
@@ -965,9 +967,13 @@
if (!textNode.length())
return;
- RenderObject* renderer = textNode.renderer();
- if (renderer && !renderer->style().collapseWhiteSpace())
- return;
+
+ {
+ ScriptDisallowedScope::InMainThread scriptDisallowedScope;
+ RenderObject* renderer = textNode.renderer();
+ if (renderer && !renderer->style().collapseWhiteSpace())
+ return;
+ }
// Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it.
Position upstreamPos = position.upstream();
@@ -1002,49 +1008,57 @@
document().updateLayout();
- RenderText* textRenderer = textNode.renderer();
- if (!textRenderer)
- return;
+ bool wholeTextNodeIsEmpty = false;
+ String str;
+ auto determineRemovalMode = [&] {
+ ScriptDisallowedScope::InMainThread scriptDisallowedScope;
+ RenderText* textRenderer = textNode.renderer();
+ if (!textRenderer)
+ return;
- auto run = LayoutIntegration::firstTextRunInTextOrderFor(*textRenderer);
- if (!run) {
- // whole text node is empty
- removeNode(textNode);
- return;
- }
-
- unsigned length = textNode.length();
- if (start >= length || end > length)
- return;
+ auto run = LayoutIntegration::firstTextRunInTextOrderFor(*textRenderer);
+ if (!run) {
+ wholeTextNodeIsEmpty = true;
+ return;
+ }
- unsigned removed = 0;
- LayoutIntegration::TextRunIterator previousRun;
- String str;
+ unsigned length = textNode.length();
+ if (start >= length || end > length)
+ return;
- // This loop structure works to process all gaps preceding a box,
- // and also will look at the gap after the last box.
- while (previousRun || run) {
- unsigned gapStart = previousRun ? previousRun->end() : 0;
- if (end < gapStart)
- // No more chance for any intersections
- break;
+ unsigned removed = 0;
+ LayoutIntegration::TextRunIterator previousRun;
- unsigned gapEnd = run ? run->start() : length;
- bool indicesIntersect = start <= gapEnd && end >= gapStart;
- int gapLen = gapEnd - gapStart;
- if (indicesIntersect && gapLen > 0) {
- gapStart = std::max(gapStart, start);
- gapEnd = std::min(gapEnd, end);
- if (str.isNull())
- str = textNode.data().substring(start, end - start);
- // remove text in the gap
- str.remove(gapStart - start - removed, gapLen);
- removed += gapLen;
+ // This loop structure works to process all gaps preceding a box,
+ // and also will look at the gap after the last box.
+ while (previousRun || run) {
+ unsigned gapStart = previousRun ? previousRun->end() : 0;
+ if (end < gapStart)
+ break; // No more chance for any intersections
+
+ unsigned gapEnd = run ? run->start() : length;
+ bool indicesIntersect = start <= gapEnd && end >= gapStart;
+ int gapLen = gapEnd - gapStart;
+ if (indicesIntersect && gapLen > 0) {
+ gapStart = std::max(gapStart, start);
+ gapEnd = std::min(gapEnd, end);
+ if (str.isNull())
+ str = textNode.data().substring(start, end - start);
+ // remove text in the gap
+ str.remove(gapStart - start - removed, gapLen);
+ removed += gapLen;
+ }
+
+ previousRun = run;
+ if (run)
+ run.traverseNextTextRunInTextOrder();
}
-
- previousRun = run;
- if (run)
- run.traverseNextTextRunInTextOrder();
+ };
+ determineRemovalMode();
+
+ if (wholeTextNodeIsEmpty) {
+ removeNode(textNode);
+ return;
}
if (!str.isNull()) {
@@ -1119,16 +1133,20 @@
document().updateLayoutIgnorePendingStylesheets();
- auto* renderer = container->renderer();
- if (!is<RenderBlockFlow>(renderer))
- return nullptr;
-
- // 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);
+ {
+ ScriptDisallowedScope::InMainThread scriptDisallowedScope;
- return nullptr;
+ auto* renderer = container->renderer();
+ if (!is<RenderBlockFlow>(renderer))
+ return nullptr;
+
+ // Append the placeholder to make sure it follows any unrendered blocks.
+ auto& blockFlow = downcast<RenderBlockFlow>(*renderer);
+ if (blockFlow.height() && (!blockFlow.isListItem() || blockFlow.firstChild()))
+ return nullptr;
+ }
+
+ return appendBlockPlaceholder(*container);
}
// Assumes that the position is at a placeholder and does the removal without much checking.
@@ -1261,8 +1279,8 @@
// Clone every node between start.deprecatedNode() and outerBlock.
for (size_t i = ancestors.size(); i != 0; --i) {
- Node* item = ancestors[i - 1].get();
- auto child = item->cloneNode(isRenderedTable(item));
+ auto item = std::exchange(ancestors[i - 1], nullptr);
+ auto child = item->cloneNode(isRenderedTable(item.get()));
appendNode(child.copyRef(), downcast<Element>(*lastNode));
lastNode = WTFMove(child);
}
@@ -1552,7 +1570,7 @@
style->mergeTypingStyle(document());
RefPtr<Element> newBlock;
- if (ContainerNode* blockEnclosingList = listNode->parentNode()) {
+ if (auto blockEnclosingList = makeRefPtr(listNode->parentNode())) {
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
@@ -1607,7 +1625,7 @@
return false;
VisiblePosition caret(endingSelection().visibleStart());
- Node* highestBlockquote = highestEnclosingNodeOfType(caret.deepEquivalent(), &isMailBlockquote);
+ auto highestBlockquote = makeRefPtr(highestEnclosingNodeOfType(caret.deepEquivalent(), &isMailBlockquote));
if (!highestBlockquote)
return false;
@@ -1620,15 +1638,14 @@
return false;
auto br = HTMLBRElement::create(document());
- auto* brPtr = br.ptr();
// 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.copyRef(), *highestBlockquote);
- VisiblePosition atBR(positionBeforeNode(brPtr));
+ insertNodeBefore(br, *highestBlockquote);
+ VisiblePosition atBR = positionBeforeNode(br.ptr());
// If the br we inserted collapsed, for example foo<br><blockquote>...</blockquote>, insert
// a second one.
if (!isStartOfParagraph(atBR))
- insertNodeBefore(HTMLBRElement::create(document()), *brPtr);
+ insertNodeBefore(HTMLBRElement::create(document()), br.get());
setEndingSelection(VisibleSelection(atBR, endingSelection().isDirectional()));
// If this is an empty paragraph there must be a line break here.
@@ -1664,7 +1681,7 @@
return original;
VisiblePosition visiblePos(original);
- Element* enclosingAnchor = enclosingAnchorElement(original);
+ auto enclosingAnchor = makeRefPtr(enclosingAnchorElement(original));
Position result = original;
if (!enclosingAnchor)
@@ -1671,9 +1688,9 @@
return result;
// Don't avoid block level anchors, because that would insert content into the wrong paragraph.
- if (enclosingAnchor && !isBlock(enclosingAnchor)) {
- VisiblePosition firstInAnchor(firstPositionInNode(enclosingAnchor));
- VisiblePosition lastInAnchor(lastPositionInNode(enclosingAnchor));
+ if (enclosingAnchor && !isBlock(enclosingAnchor.get())) {
+ VisiblePosition firstInAnchor(firstPositionInNode(enclosingAnchor.get()));
+ VisiblePosition lastInAnchor(lastPositionInNode(enclosingAnchor.get()));
// If visually just after the anchor, insert *inside* the anchor unless it's the last
// VisiblePosition in the document, to match NSTextView.
if (visiblePos == lastInAnchor) {
@@ -1688,10 +1705,10 @@
// Don't insert outside an anchor if doing so would skip over a line break. It would
// probably be safe to move the line break so that we could still avoid the anchor here.
Position downstream(visiblePos.deepEquivalent().downstream());
- if (lineBreakExistsAtVisiblePosition(visiblePos) && downstream.deprecatedNode()->isDescendantOf(enclosingAnchor))
+ if (lineBreakExistsAtVisiblePosition(visiblePos) && downstream.deprecatedNode()->isDescendantOf(enclosingAnchor.get()))
return original;
- result = positionInParentAfterNode(enclosingAnchor);
+ result = positionInParentAfterNode(enclosingAnchor.get());
}
// If visually just before an anchor, insert *outside* the anchor unless it's the first
// VisiblePosition in a paragraph, to match NSTextView.
@@ -1705,7 +1722,7 @@
if (!enclosingAnchor)
return original;
- result = positionInParentBeforeNode(enclosingAnchor);
+ result = positionInParentBeforeNode(enclosingAnchor.get());
}
}
Modified: trunk/Source/WebCore/editing/EditCommand.cpp (276130 => 276131)
--- trunk/Source/WebCore/editing/EditCommand.cpp 2021-04-16 10:13:22 UTC (rev 276130)
+++ trunk/Source/WebCore/editing/EditCommand.cpp 2021-04-16 10:21:45 UTC (rev 276131)
@@ -32,8 +32,7 @@
#include "Editing.h"
#include "Editor.h"
#include "Element.h"
-#include "HTMLInputElement.h"
-#include "HTMLTextAreaElement.h"
+#include "HTMLTextFormControlElement.h"
#include "NodeTraversal.h"
namespace WebCore {
@@ -123,18 +122,18 @@
}
EditCommand::EditCommand(Document& document, EditAction editingAction)
- : m_document(document)
- , m_editingAction(editingAction)
+ : m_document { document }
+ , m_startingSelection { m_document->selection().selection() }
+ , m_endingSelection { m_startingSelection }
+ , m_editingAction { editingAction }
{
- setStartingSelection(m_document->selection().selection());
- setEndingSelection(m_startingSelection);
}
EditCommand::EditCommand(Document& document, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection)
- : m_document(document)
+ : m_document { document }
+ , m_startingSelection { startingSelection }
+ , m_endingSelection { endingSelection }
{
- setStartingSelection(startingSelection);
- setEndingSelection(endingSelection);
}
EditCommand::~EditCommand() = default;
@@ -144,43 +143,35 @@
return m_editingAction;
}
-static inline EditCommandComposition* compositionIfPossible(EditCommand* command)
+static RefPtr<EditCommandComposition> compositionIfPossible(EditCommand& command)
{
- if (!command->isCompositeEditCommand())
- return 0;
- return toCompositeEditCommand(command)->composition();
+ if (!command.isCompositeEditCommand())
+ return nullptr;
+ return static_cast<CompositeEditCommand&>(command).composition();
}
bool EditCommand::isEditingTextAreaOrTextInput() const
{
- auto* container = m_document->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());
+ return enclosingTextFormControl(m_document->selection().selection().start());
}
-void EditCommand::setStartingSelection(const VisibleSelection& s)
+void EditCommand::setStartingSelection(const VisibleSelection& selection)
{
- for (EditCommand* cmd = this; ; cmd = cmd->m_parent) {
- if (auto* composition = compositionIfPossible(cmd))
- composition->setStartingSelection(s);
- cmd->m_startingSelection = s;
- if (!cmd->m_parent || cmd->m_parent->isFirstCommand(cmd))
+ for (auto command = makeRefPtr(this); ; command = command->m_parent.get()) {
+ if (auto composition = compositionIfPossible(*command))
+ composition->setStartingSelection(selection);
+ command->m_startingSelection = selection;
+ if (!command->m_parent || command->m_parent->isFirstCommand(command.get()))
break;
}
}
-void EditCommand::setEndingSelection(const VisibleSelection &s)
+void EditCommand::setEndingSelection(const VisibleSelection& selection)
{
- for (EditCommand* cmd = this; cmd; cmd = cmd->m_parent) {
- if (auto* composition = compositionIfPossible(cmd))
- composition->setEndingSelection(s);
- cmd->m_endingSelection = s;
+ for (auto command = makeRefPtr(this); command; command = command->m_parent.get()) {
+ if (auto composition = compositionIfPossible(*command))
+ composition->setEndingSelection(selection);
+ command->m_endingSelection = selection;
}
}
@@ -187,7 +178,7 @@
void EditCommand::setParent(CompositeEditCommand* parent)
{
ASSERT((parent && !m_parent) || (!parent && m_parent));
- m_parent = parent;
+ m_parent = makeWeakPtr(parent);
if (parent) {
m_startingSelection = parent->m_endingSelection;
m_endingSelection = parent->m_endingSelection;
@@ -210,8 +201,8 @@
auto* cache = document().existingAXObjectCache();
if (!cache)
return;
- auto* node = highestEditableRoot(position.deepEquivalent(), HasEditableAXRole);
- cache->postTextStateChangeNotification(node, type, text, position);
+ auto node = makeRefPtr(highestEditableRoot(position.deepEquivalent(), HasEditableAXRole));
+ cache->postTextStateChangeNotification(node.get(), type, text, position);
}
SimpleEditCommand::SimpleEditCommand(Document& document, EditAction editingAction)