зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1616257 - part 3: Make `WSRunScanner::NextVisibleNode()` and `WSRunScanner::PriorVisibleNode()` return stack only class instance which storing the visible node as `nsIContent` r=m_kato
They are really messy because they take a lot of out parameters, and these out parameter meaning is really unclear. Therefore, they should return a stack only class instance which explain the meaning with getter methods. And also it should store the result node as `nsIContent`. And also this patch adds static methods for them for their users which don't need `WSRunScanner` instance. Differential Revision: https://phabricator.services.mozilla.com/D63613 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
6382dae45f
Коммит
f5407e4219
|
@ -373,6 +373,11 @@ class EditorDOMPointBase final {
|
|||
}
|
||||
SetToEndOf(parentNode);
|
||||
}
|
||||
static SelfType After(const nsINode& aContainer) {
|
||||
SelfType point;
|
||||
point.SetAfter(&aContainer);
|
||||
return point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear() makes the instance not point anywhere.
|
||||
|
|
|
@ -1920,8 +1920,7 @@ nsresult HTMLEditor::InsertBRElement(const EditorDOMPoint& aPointToBreak) {
|
|||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
bool brElementIsAfterBlock = false;
|
||||
bool brElementIsBeforeBlock = false;
|
||||
bool brElementIsAfterBlock = false, brElementIsBeforeBlock = false;
|
||||
|
||||
// First, insert a <br> element.
|
||||
RefPtr<Element> brElement;
|
||||
|
@ -1936,15 +1935,12 @@ nsresult HTMLEditor::InsertBRElement(const EditorDOMPoint& aPointToBreak) {
|
|||
} else {
|
||||
EditorDOMPoint pointToBreak(aPointToBreak);
|
||||
WSRunObject wsObj(this, pointToBreak);
|
||||
WSType wsType;
|
||||
wsObj.PriorVisibleNode(pointToBreak, &wsType);
|
||||
if (wsType & WSType::block) {
|
||||
brElementIsAfterBlock = true;
|
||||
}
|
||||
wsObj.NextVisibleNode(pointToBreak, &wsType);
|
||||
if (wsType & WSType::block) {
|
||||
brElementIsBeforeBlock = true;
|
||||
}
|
||||
brElementIsAfterBlock =
|
||||
wsObj.ScanPreviousVisibleNodeOrBlockBoundaryFrom(pointToBreak)
|
||||
.ReachedBlockBoundary();
|
||||
brElementIsBeforeBlock =
|
||||
wsObj.ScanNextVisibleNodeOrBlockBoundaryFrom(pointToBreak)
|
||||
.ReachedBlockBoundary();
|
||||
// If the container of the break is a link, we need to split it and
|
||||
// insert new <br> between the split links.
|
||||
RefPtr<Element> linkNode =
|
||||
|
@ -2003,22 +1999,21 @@ nsresult HTMLEditor::InsertBRElement(const EditorDOMPoint& aPointToBreak) {
|
|||
DebugOnly<bool> advanced = afterBRElement.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(advanced,
|
||||
"Failed to advance offset after the new <br> element");
|
||||
WSRunObject wsObj(this, afterBRElement);
|
||||
nsCOMPtr<nsINode> maybeSecondBRNode;
|
||||
WSType wsType;
|
||||
wsObj.NextVisibleNode(afterBRElement, address_of(maybeSecondBRNode), nullptr,
|
||||
&wsType);
|
||||
if (wsType == WSType::br) {
|
||||
WSScanResult forwardScanFromAfterBRElementResult =
|
||||
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(*this, afterBRElement);
|
||||
if (forwardScanFromAfterBRElementResult.ReachedBRElement()) {
|
||||
// The next thing after the break we inserted is another break. Move the
|
||||
// second break to be the first break's sibling. This will prevent them
|
||||
// from being in different inline nodes, which would break
|
||||
// SetInterlinePosition(). It will also assure that if the user clicks
|
||||
// away and then clicks back on their new blank line, they will still get
|
||||
// the style from the line above.
|
||||
EditorDOMPoint atSecondBRElement(maybeSecondBRNode);
|
||||
if (brElement->GetNextSibling() != maybeSecondBRNode) {
|
||||
if (brElement->GetNextSibling() !=
|
||||
forwardScanFromAfterBRElementResult.BRElementPtr()) {
|
||||
MOZ_ASSERT(forwardScanFromAfterBRElementResult.BRElementPtr());
|
||||
nsresult rv = MoveNodeWithTransaction(
|
||||
MOZ_KnownLive(*maybeSecondBRNode->AsContent()), afterBRElement);
|
||||
MOZ_KnownLive(*forwardScanFromAfterBRElementResult.BRElementPtr()),
|
||||
afterBRElement);
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -2077,18 +2072,14 @@ EditActionResult HTMLEditor::SplitMailCiteElements(
|
|||
// The latter can confuse a user if they click there and start typing,
|
||||
// because being in the mailquote may affect wrapping behavior, or font
|
||||
// color, etc.
|
||||
WSRunObject wsObj(this, pointToSplit);
|
||||
nsCOMPtr<nsINode> visNode;
|
||||
WSType wsType;
|
||||
wsObj.NextVisibleNode(pointToSplit, address_of(visNode), nullptr, &wsType);
|
||||
WSScanResult forwardScanFromPointToSplitResult =
|
||||
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(*this, pointToSplit);
|
||||
// If selection start point is before a break and it's inside the mailquote,
|
||||
// let's split it after the visible node.
|
||||
if (wsType == WSType::br && visNode != citeNode &&
|
||||
citeNode->Contains(visNode)) {
|
||||
pointToSplit.Set(visNode);
|
||||
DebugOnly<bool> advanced = pointToSplit.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(advanced,
|
||||
"Failed to advance offset to after the visible node");
|
||||
if (forwardScanFromPointToSplitResult.ReachedBRElement() &&
|
||||
forwardScanFromPointToSplitResult.BRElementPtr() != citeNode &&
|
||||
citeNode->Contains(forwardScanFromPointToSplitResult.BRElementPtr())) {
|
||||
pointToSplit = forwardScanFromPointToSplitResult.PointAfterContent();
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!pointToSplit.GetContainerAsContent())) {
|
||||
|
@ -2174,21 +2165,26 @@ EditActionResult HTMLEditor::SplitMailCiteElements(
|
|||
EditorDOMPoint pointToCreateNewBrNode(atBrNode.GetContainer(),
|
||||
atBrNode.Offset());
|
||||
|
||||
WSRunObject wsObj(this, pointToCreateNewBrNode);
|
||||
WSType wsType;
|
||||
wsObj.PriorVisibleNode(pointToCreateNewBrNode, nullptr, nullptr, &wsType);
|
||||
if (wsType == WSType::normalWS || wsType == WSType::text ||
|
||||
wsType == WSType::special) {
|
||||
WSScanResult backwardScanFromPointToCreateNewBRElementResult =
|
||||
WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundary(
|
||||
*this, pointToCreateNewBrNode);
|
||||
if (backwardScanFromPointToCreateNewBRElementResult
|
||||
.InNormalWhiteSpacesOrText() ||
|
||||
backwardScanFromPointToCreateNewBRElementResult
|
||||
.ReachedSpecialContent()) {
|
||||
EditorRawDOMPoint pointAfterNewBrNode(pointToCreateNewBrNode);
|
||||
DebugOnly<bool> advanced = pointAfterNewBrNode.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(advanced,
|
||||
"Failed to advance offset after the <br> node");
|
||||
WSRunObject wsObjAfterBR(this, pointAfterNewBrNode);
|
||||
wsObjAfterBR.NextVisibleNode(pointAfterNewBrNode, &wsType);
|
||||
if (wsType == WSType::normalWS || wsType == WSType::text ||
|
||||
wsType == WSType::special ||
|
||||
WSScanResult forwardScanFromPointAfterNewBRElementResult =
|
||||
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(*this,
|
||||
pointAfterNewBrNode);
|
||||
if (forwardScanFromPointAfterNewBRElementResult
|
||||
.InNormalWhiteSpacesOrText() ||
|
||||
forwardScanFromPointAfterNewBRElementResult.ReachedSpecialContent() ||
|
||||
// In case we're at the very end.
|
||||
wsType == WSType::thisBlock) {
|
||||
forwardScanFromPointAfterNewBRElementResult
|
||||
.ReachedCurrentBlockBoundary()) {
|
||||
brElement = InsertBRElementWithTransaction(pointToCreateNewBrNode);
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
|
||||
|
@ -2397,24 +2393,15 @@ EditActionResult HTMLEditor::HandleDeleteAroundCollapsedSelection(
|
|||
|
||||
// What's in the direction we are deleting?
|
||||
WSRunObject wsObj(this, startPoint);
|
||||
nsCOMPtr<nsINode> visibleNode;
|
||||
int32_t visibleNodeOffset;
|
||||
WSType wsType;
|
||||
|
||||
// Find next visible node
|
||||
if (aDirectionAndAmount == nsIEditor::eNext) {
|
||||
wsObj.NextVisibleNode(startPoint, address_of(visibleNode),
|
||||
&visibleNodeOffset, &wsType);
|
||||
} else {
|
||||
wsObj.PriorVisibleNode(startPoint, address_of(visibleNode),
|
||||
&visibleNodeOffset, &wsType);
|
||||
}
|
||||
|
||||
if (!visibleNode) {
|
||||
WSScanResult scanFromStartPointResult =
|
||||
aDirectionAndAmount == nsIEditor::eNext
|
||||
? wsObj.ScanNextVisibleNodeOrBlockBoundaryFrom(startPoint)
|
||||
: wsObj.ScanPreviousVisibleNodeOrBlockBoundaryFrom(startPoint);
|
||||
if (!scanFromStartPointResult.GetContent()) {
|
||||
return EditActionCanceled();
|
||||
}
|
||||
|
||||
if (wsType == WSType::normalWS) {
|
||||
if (scanFromStartPointResult.InNormalWhiteSpaces()) {
|
||||
EditActionResult result =
|
||||
HandleDeleteCollapsedSelectionAtWhiteSpaces(aDirectionAndAmount, wsObj);
|
||||
NS_WARNING_ASSERTION(
|
||||
|
@ -2423,53 +2410,53 @@ EditActionResult HTMLEditor::HandleDeleteAroundCollapsedSelection(
|
|||
return result;
|
||||
}
|
||||
|
||||
if (wsType == WSType::text) {
|
||||
if (NS_WARN_IF(!visibleNode->IsText())) {
|
||||
if (scanFromStartPointResult.InNormalText()) {
|
||||
if (NS_WARN_IF(!scanFromStartPointResult.GetContent()->IsText())) {
|
||||
return EditActionResult(NS_ERROR_FAILURE);
|
||||
}
|
||||
EditActionResult result = HandleDeleteCollapsedSelectionAtTextNode(
|
||||
aDirectionAndAmount, EditorDOMPoint(visibleNode, visibleNodeOffset));
|
||||
aDirectionAndAmount, scanFromStartPointResult.Point());
|
||||
NS_WARNING_ASSERTION(result.Succeeded(),
|
||||
"HandleDeleteCollapsedSelectionAtTextNode() failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (wsType == WSType::special || wsType == WSType::br ||
|
||||
visibleNode->IsHTMLElement(nsGkAtoms::hr)) {
|
||||
if (NS_WARN_IF(!visibleNode->IsContent())) {
|
||||
return EditActionResult(NS_ERROR_FAILURE);
|
||||
}
|
||||
if (scanFromStartPointResult.ReachedSpecialContent() ||
|
||||
scanFromStartPointResult.ReachedBRElement() ||
|
||||
scanFromStartPointResult.ReachedHRElement()) {
|
||||
EditActionResult result = HandleDeleteCollapsedSelectionAtAtomicContent(
|
||||
aDirectionAndAmount, aStripWrappers,
|
||||
MOZ_KnownLive(*visibleNode->AsContent()), startPoint, wsObj);
|
||||
MOZ_KnownLive(*scanFromStartPointResult.GetContent()), startPoint,
|
||||
wsObj);
|
||||
NS_WARNING_ASSERTION(
|
||||
result.Succeeded(),
|
||||
"HandleDeleteCollapsedSelectionAtAtomicContent() failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (wsType == WSType::otherBlock) {
|
||||
if (NS_WARN_IF(!visibleNode->IsElement())) {
|
||||
if (scanFromStartPointResult.ReachedOtherBlockElement()) {
|
||||
if (NS_WARN_IF(!scanFromStartPointResult.GetContent()->IsElement())) {
|
||||
return EditActionResult(NS_ERROR_FAILURE);
|
||||
}
|
||||
EditActionResult result =
|
||||
HandleDeleteCollapsedSelectionAtOtherBlockBoundary(
|
||||
aDirectionAndAmount, aStripWrappers,
|
||||
MOZ_KnownLive(*visibleNode->AsElement()), startPoint, wsObj);
|
||||
MOZ_KnownLive(*scanFromStartPointResult.ElementPtr()), startPoint,
|
||||
wsObj);
|
||||
NS_WARNING_ASSERTION(
|
||||
result.Succeeded(),
|
||||
"HandleDeleteCollapsedSelectionAtOtherBlockBoundary() failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (wsType == WSType::thisBlock) {
|
||||
if (NS_WARN_IF(!visibleNode->IsElement())) {
|
||||
if (scanFromStartPointResult.ReachedCurrentBlockBoundary()) {
|
||||
if (NS_WARN_IF(!scanFromStartPointResult.GetContent()->IsElement())) {
|
||||
return EditActionResult(NS_ERROR_FAILURE);
|
||||
}
|
||||
EditActionResult result =
|
||||
HandleDeleteCollapsedSelectionAtCurrentBlockBoundary(
|
||||
aDirectionAndAmount, MOZ_KnownLive(*visibleNode->AsElement()),
|
||||
startPoint);
|
||||
aDirectionAndAmount,
|
||||
MOZ_KnownLive(*scanFromStartPointResult.ElementPtr()), startPoint);
|
||||
NS_WARNING_ASSERTION(
|
||||
result.Succeeded(),
|
||||
"HandleDeleteCollapsedSelectionAtCurrentBlockBoundary() failed");
|
||||
|
@ -2608,12 +2595,12 @@ EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtTextNode(
|
|||
EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent(
|
||||
nsIEditor::EDirection aDirectionAndAmount,
|
||||
nsIEditor::EStripWrappers aStripWrappers, nsIContent& aAtomicContent,
|
||||
const EditorDOMPoint& aCaretPoint, WSRunObject& aWSRunObjectAtCaret) {
|
||||
const EditorDOMPoint& aCaretPoint, WSRunScanner& aWSRunScannerAtCaret) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
MOZ_ASSERT(aCaretPoint.IsSet());
|
||||
|
||||
// If the atomic element is editing host, we should do nothing.
|
||||
if (&aAtomicContent == aWSRunObjectAtCaret.GetEditingHost()) {
|
||||
if (&aAtomicContent == aWSRunScannerAtCaret.GetEditingHost()) {
|
||||
return EditActionHandled();
|
||||
}
|
||||
|
||||
|
@ -2700,30 +2687,24 @@ EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent(
|
|||
// There is one exception to the move only case. If the <hr> is
|
||||
// followed by a <br> we want to delete the <br>.
|
||||
|
||||
WSType otherWSType;
|
||||
nsCOMPtr<nsINode> otherNode;
|
||||
|
||||
aWSRunObjectAtCaret.NextVisibleNode(aCaretPoint, address_of(otherNode),
|
||||
nullptr, &otherWSType);
|
||||
|
||||
if (otherWSType != WSType::br) {
|
||||
WSScanResult forwardScanFromCaretResult =
|
||||
aWSRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom(
|
||||
aCaretPoint);
|
||||
if (!forwardScanFromCaretResult.ReachedBRElement()) {
|
||||
return EditActionHandled();
|
||||
}
|
||||
|
||||
// Delete the <br>
|
||||
if (NS_WARN_IF(!otherNode->IsContent())) {
|
||||
return EditActionHandled(NS_ERROR_FAILURE);
|
||||
}
|
||||
nsIContent* otherContent = otherNode->AsContent();
|
||||
nsresult rv =
|
||||
WSRunObject::PrepareToDeleteNode(this, MOZ_KnownLive(otherContent));
|
||||
nsresult rv = WSRunObject::PrepareToDeleteNode(
|
||||
this, MOZ_KnownLive(forwardScanFromCaretResult.BRElementPtr()));
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
|
||||
}
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return EditActionHandled(rv);
|
||||
}
|
||||
rv = DeleteNodeWithTransaction(MOZ_KnownLive(*otherContent));
|
||||
rv = DeleteNodeWithTransaction(
|
||||
MOZ_KnownLive(*forwardScanFromCaretResult.BRElementPtr()));
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return EditActionHandled(NS_ERROR_EDITOR_DESTROYED);
|
||||
}
|
||||
|
@ -2799,7 +2780,7 @@ EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtAtomicContent(
|
|||
EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtOtherBlockBoundary(
|
||||
nsIEditor::EDirection aDirectionAndAmount,
|
||||
nsIEditor::EStripWrappers aStripWrappers, Element& aOtherBlockElement,
|
||||
const EditorDOMPoint& aCaretPoint, WSRunObject& aWSRunObjectAtCaret) {
|
||||
const EditorDOMPoint& aCaretPoint, WSRunScanner& aWSRunScannerAtCaret) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
MOZ_ASSERT(aCaretPoint.IsSet());
|
||||
|
||||
|
@ -2812,17 +2793,12 @@ EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtOtherBlockBoundary(
|
|||
// Next to a block. See if we are between a block and a br. If so, we
|
||||
// really want to delete the br. Else join content at selection to the
|
||||
// block.
|
||||
WSType otherWSType;
|
||||
nsCOMPtr<nsINode> otherNode;
|
||||
|
||||
// Find node in other direction
|
||||
if (aDirectionAndAmount == nsIEditor::eNext) {
|
||||
aWSRunObjectAtCaret.PriorVisibleNode(aCaretPoint, address_of(otherNode),
|
||||
nullptr, &otherWSType);
|
||||
} else {
|
||||
aWSRunObjectAtCaret.NextVisibleNode(aCaretPoint, address_of(otherNode),
|
||||
nullptr, &otherWSType);
|
||||
}
|
||||
WSScanResult scanFromCaretResult =
|
||||
aDirectionAndAmount == nsIEditor::eNext
|
||||
? aWSRunScannerAtCaret.ScanPreviousVisibleNodeOrBlockBoundaryFrom(
|
||||
aCaretPoint)
|
||||
: aWSRunScannerAtCaret.ScanNextVisibleNodeOrBlockBoundaryFrom(
|
||||
aCaretPoint);
|
||||
|
||||
// First find the adjacent node in the block
|
||||
nsCOMPtr<nsIContent> leafNode;
|
||||
|
@ -2838,8 +2814,9 @@ EditActionResult HTMLEditor::HandleDeleteCollapsedSelectionAtOtherBlockBoundary(
|
|||
}
|
||||
|
||||
bool didBRElementDeleted = false;
|
||||
if (otherNode->IsHTMLElement(nsGkAtoms::br)) {
|
||||
nsresult rv = DeleteNodeWithTransaction(*otherNode);
|
||||
if (scanFromCaretResult.ReachedBRElement()) {
|
||||
nsresult rv = DeleteNodeWithTransaction(
|
||||
MOZ_KnownLive(*scanFromCaretResult.BRElementPtr()));
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
|
||||
}
|
||||
|
@ -6895,20 +6872,20 @@ HTMLEditor::GetExtendedRangeToIncludeInvisibleNodes(
|
|||
if (atStart.GetContainer() != commonAncestorBlock &&
|
||||
atStart.GetContainer() != editingHost) {
|
||||
for (;;) {
|
||||
WSRunObject wsObj(this, atStart);
|
||||
WSType wsType;
|
||||
wsObj.PriorVisibleNode(atStart, &wsType);
|
||||
if (wsType != WSType::thisBlock) {
|
||||
WSRunScanner wsScannerAtStart(this, atStart);
|
||||
if (!wsScannerAtStart.ScanPreviousVisibleNodeOrBlockBoundaryFrom(atStart)
|
||||
.ReachedCurrentBlockBoundary()) {
|
||||
break;
|
||||
}
|
||||
// We want to keep looking up. But stop if we are crossing table
|
||||
// element boundaries, or if we hit the root.
|
||||
if (HTMLEditUtils::IsTableElement(wsObj.GetStartReasonContent()) ||
|
||||
wsObj.GetStartReasonContent() == commonAncestorBlock ||
|
||||
wsObj.GetStartReasonContent() == editingHost) {
|
||||
if (HTMLEditUtils::IsTableElement(
|
||||
wsScannerAtStart.GetStartReasonContent()) ||
|
||||
wsScannerAtStart.GetStartReasonContent() == commonAncestorBlock ||
|
||||
wsScannerAtStart.GetStartReasonContent() == editingHost) {
|
||||
break;
|
||||
}
|
||||
atStart.Set(wsObj.GetStartReasonContent());
|
||||
atStart.Set(wsScannerAtStart.GetStartReasonContent());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6921,31 +6898,30 @@ HTMLEditor::GetExtendedRangeToIncludeInvisibleNodes(
|
|||
atEnd.GetContainer() != editingHost) {
|
||||
EditorDOMPoint atFirstInvisibleBRElement;
|
||||
for (;;) {
|
||||
WSRunObject wsObj(this, atEnd);
|
||||
WSType wsType;
|
||||
wsObj.NextVisibleNode(atEnd, &wsType);
|
||||
if (wsType == WSType::br) {
|
||||
if (IsVisibleBRElement(wsObj.GetEndReasonContent())) {
|
||||
WSRunScanner wsScannerAtEnd(this, atEnd);
|
||||
WSScanResult forwardScanFromEndResult =
|
||||
wsScannerAtEnd.ScanNextVisibleNodeOrBlockBoundaryFrom(atEnd);
|
||||
if (forwardScanFromEndResult.ReachedBRElement()) {
|
||||
if (IsVisibleBRElement(wsScannerAtEnd.GetEndReasonContent())) {
|
||||
break;
|
||||
}
|
||||
if (!atFirstInvisibleBRElement.IsSet()) {
|
||||
atFirstInvisibleBRElement = atEnd;
|
||||
}
|
||||
atEnd.Set(wsObj.GetEndReasonContent());
|
||||
atEnd.AdvanceOffset();
|
||||
atEnd.SetAfter(wsScannerAtEnd.GetEndReasonContent());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (wsType == WSType::thisBlock) {
|
||||
if (forwardScanFromEndResult.ReachedCurrentBlockBoundary()) {
|
||||
// We want to keep looking up. But stop if we are crossing table
|
||||
// element boundaries, or if we hit the root.
|
||||
if (HTMLEditUtils::IsTableElement(wsObj.GetEndReasonContent()) ||
|
||||
wsObj.GetEndReasonContent() == commonAncestorBlock ||
|
||||
wsObj.GetEndReasonContent() == editingHost) {
|
||||
if (HTMLEditUtils::IsTableElement(
|
||||
wsScannerAtEnd.GetEndReasonContent()) ||
|
||||
wsScannerAtEnd.GetEndReasonContent() == commonAncestorBlock ||
|
||||
wsScannerAtEnd.GetEndReasonContent() == editingHost) {
|
||||
break;
|
||||
}
|
||||
atEnd.Set(wsObj.GetEndReasonContent());
|
||||
atEnd.AdvanceOffset();
|
||||
atEnd.SetAfter(wsScannerAtEnd.GetEndReasonContent());
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -7022,63 +6998,59 @@ nsresult HTMLEditor::MaybeExtendSelectionToHardLineEdgesForBlockEditAction() {
|
|||
EditorDOMPoint newStartPoint(startPoint);
|
||||
EditorDOMPoint newEndPoint(endPoint);
|
||||
|
||||
// some locals we need for whitespace code
|
||||
WSType wsType;
|
||||
|
||||
// let the whitespace code do the heavy lifting
|
||||
WSRunObject wsEndObj(this, endPoint);
|
||||
// Is there any intervening visible whitespace? If so we can't push
|
||||
// selection past that, it would visibly change meaning of users selection.
|
||||
wsEndObj.PriorVisibleNode(endPoint, &wsType);
|
||||
if (wsType != WSType::text && wsType != WSType::normalWS) {
|
||||
WSRunScanner wsScannerAtEnd(this, endPoint);
|
||||
if (wsScannerAtEnd.ScanPreviousVisibleNodeOrBlockBoundaryFrom(endPoint)
|
||||
.ReachedSomething()) {
|
||||
// eThisBlock and eOtherBlock conveniently distinguish cases
|
||||
// of going "down" into a block and "up" out of a block.
|
||||
if (wsEndObj.mStartReason == WSType::otherBlock) {
|
||||
if (wsScannerAtEnd.StartReason() == WSType::otherBlock) {
|
||||
// endpoint is just after the close of a block.
|
||||
nsINode* child =
|
||||
GetRightmostChild(wsEndObj.GetStartReasonContent(), true);
|
||||
GetRightmostChild(wsScannerAtEnd.GetStartReasonContent(), true);
|
||||
if (child) {
|
||||
newEndPoint.SetAfter(child);
|
||||
}
|
||||
// else block is empty - we can leave selection alone here, i think.
|
||||
} else if (wsEndObj.mStartReason == WSType::thisBlock) {
|
||||
} else if (wsScannerAtEnd.StartReason() == WSType::thisBlock) {
|
||||
// endpoint is just after start of this block
|
||||
nsINode* child = GetPreviousEditableHTMLNode(endPoint);
|
||||
if (child) {
|
||||
newEndPoint.SetAfter(child);
|
||||
}
|
||||
// else block is empty - we can leave selection alone here, i think.
|
||||
} else if (wsEndObj.mStartReason == WSType::br) {
|
||||
} else if (wsScannerAtEnd.StartReason() == WSType::br) {
|
||||
// endpoint is just after break. lets adjust it to before it.
|
||||
newEndPoint.Set(wsEndObj.GetStartReasonContent());
|
||||
newEndPoint.Set(wsScannerAtEnd.GetStartReasonContent());
|
||||
}
|
||||
}
|
||||
|
||||
// similar dealio for start of range
|
||||
WSRunObject wsStartObj(this, startPoint);
|
||||
// Is there any intervening visible whitespace? If so we can't push
|
||||
// selection past that, it would visibly change meaning of users selection.
|
||||
wsStartObj.NextVisibleNode(startPoint, &wsType);
|
||||
if (wsType != WSType::text && wsType != WSType::normalWS) {
|
||||
WSRunScanner wsScannerAtStart(this, startPoint);
|
||||
if (wsScannerAtStart.ScanNextVisibleNodeOrBlockBoundaryFrom(startPoint)
|
||||
.ReachedSomething()) {
|
||||
// eThisBlock and eOtherBlock conveniently distinguish cases
|
||||
// of going "down" into a block and "up" out of a block.
|
||||
if (wsStartObj.mEndReason == WSType::otherBlock) {
|
||||
if (wsScannerAtStart.EndReason() == WSType::otherBlock) {
|
||||
// startpoint is just before the start of a block.
|
||||
nsINode* child = GetLeftmostChild(wsStartObj.GetEndReasonContent(), true);
|
||||
nsINode* child =
|
||||
GetLeftmostChild(wsScannerAtStart.GetEndReasonContent(), true);
|
||||
if (child) {
|
||||
newStartPoint.Set(child);
|
||||
}
|
||||
// else block is empty - we can leave selection alone here, i think.
|
||||
} else if (wsStartObj.mEndReason == WSType::thisBlock) {
|
||||
} else if (wsScannerAtStart.EndReason() == WSType::thisBlock) {
|
||||
// startpoint is just before end of this block
|
||||
nsINode* child = GetNextEditableHTMLNode(startPoint);
|
||||
if (child) {
|
||||
newStartPoint.Set(child);
|
||||
}
|
||||
// else block is empty - we can leave selection alone here, i think.
|
||||
} else if (wsStartObj.mEndReason == WSType::br) {
|
||||
} else if (wsScannerAtStart.EndReason() == WSType::br) {
|
||||
// startpoint is just before a break. lets adjust it to after it.
|
||||
newStartPoint.SetAfter(wsStartObj.GetEndReasonContent());
|
||||
newStartPoint.SetAfter(wsScannerAtStart.GetEndReasonContent());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8635,20 +8607,19 @@ nsresult HTMLEditor::HandleInsertParagraphInListItemElement(Element& aListItem,
|
|||
return NS_OK;
|
||||
}
|
||||
} else {
|
||||
WSRunObject wsObj(this, &aListItem, 0);
|
||||
nsCOMPtr<nsINode> visNode;
|
||||
int32_t visOffset = 0;
|
||||
WSType wsType;
|
||||
wsObj.NextVisibleNode(EditorRawDOMPoint(&aListItem, 0),
|
||||
address_of(visNode), &visOffset, &wsType);
|
||||
if (wsType == WSType::special || wsType == WSType::br ||
|
||||
visNode->IsHTMLElement(nsGkAtoms::hr)) {
|
||||
EditorRawDOMPoint atVisNode(visNode);
|
||||
if (NS_WARN_IF(!atVisNode.IsSetAndValid())) {
|
||||
WSScanResult forwardScanFromStartOfListItemResult =
|
||||
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
|
||||
*this, EditorRawDOMPoint(&aListItem, 0));
|
||||
if (forwardScanFromStartOfListItemResult.ReachedSpecialContent() ||
|
||||
forwardScanFromStartOfListItemResult.ReachedBRElement() ||
|
||||
forwardScanFromStartOfListItemResult.ReachedHRElement()) {
|
||||
EditorRawDOMPoint atFoundElement(
|
||||
forwardScanFromStartOfListItemResult.RawPointAtContent());
|
||||
if (NS_WARN_IF(!atFoundElement.IsSetAndValid())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
ErrorResult error;
|
||||
SelectionRefPtr()->Collapse(atVisNode, error);
|
||||
SelectionRefPtr()->Collapse(atFoundElement, error);
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
error.SuppressException();
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
|
@ -8659,7 +8630,11 @@ nsresult HTMLEditor::HandleInsertParagraphInListItemElement(Element& aListItem,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
rv = SelectionRefPtr()->Collapse(visNode, visOffset);
|
||||
// XXX This may be not meaningful position if it reached block element
|
||||
// in aListItem.
|
||||
rv = SelectionRefPtr()->Collapse(
|
||||
forwardScanFromStartOfListItemResult.RawPoint()
|
||||
.ToRawRangeBoundary());
|
||||
if (NS_WARN_IF(Destroyed())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
|
|
@ -541,49 +541,47 @@ nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
|
|||
// Find first editable and visible node.
|
||||
EditorRawDOMPoint pointToPutCaret(editingHost, 0);
|
||||
for (;;) {
|
||||
WSRunObject wsObj(this, pointToPutCaret.GetContainer(),
|
||||
pointToPutCaret.Offset());
|
||||
int32_t visOffset = 0;
|
||||
WSType visType;
|
||||
nsCOMPtr<nsINode> visNode;
|
||||
wsObj.NextVisibleNode(pointToPutCaret, address_of(visNode), &visOffset,
|
||||
&visType);
|
||||
|
||||
WSScanResult forwardScanFromPointToPutCaretResult =
|
||||
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(*this,
|
||||
pointToPutCaret);
|
||||
// If we meet a non-editable node first, we should move caret to start of
|
||||
// the editing host (perhaps, user may want to insert something before
|
||||
// the first non-editable node? Chromium behaves so).
|
||||
if (visNode && !visNode->IsEditable()) {
|
||||
if (forwardScanFromPointToPutCaretResult.GetContent() &&
|
||||
!forwardScanFromPointToPutCaretResult.IsContentEditable()) {
|
||||
pointToPutCaret.Set(editingHost, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
// WSRunObject::NextVisibleNode() returns WSType::special and the "special"
|
||||
// node when it meets empty inline element. In this case, we should go to
|
||||
// next sibling. For example, if current editor is:
|
||||
// <div contenteditable><span></span><b><br></b></div>
|
||||
// WSRunScanner::ScanNextVisibleNodeOrBlockBoundary() returns
|
||||
// WSType::special and the "special" node when it meets empty inline
|
||||
// element. In this case, we should go to next sibling. For example, if
|
||||
// current editor is: <div contenteditable><span></span><b><br></b></div>
|
||||
// then, we should put caret at the <br> element. So, let's check if
|
||||
// found node is an empty inline container element.
|
||||
if (visType == WSType::special && visNode &&
|
||||
TagCanContainTag(*visNode->NodeInfo()->NameAtom(),
|
||||
if (forwardScanFromPointToPutCaretResult.ReachedSpecialContent() &&
|
||||
forwardScanFromPointToPutCaretResult.GetContent() &&
|
||||
TagCanContainTag(*forwardScanFromPointToPutCaretResult.GetContent()
|
||||
->NodeInfo()
|
||||
->NameAtom(),
|
||||
*nsGkAtoms::textTagName)) {
|
||||
pointToPutCaret.Set(visNode);
|
||||
DebugOnly<bool> advanced = pointToPutCaret.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(
|
||||
advanced,
|
||||
"Failed to advance offset from found empty inline container element");
|
||||
pointToPutCaret =
|
||||
forwardScanFromPointToPutCaretResult.RawPointAfterContent();
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there is editable and visible text node, move caret at start of it.
|
||||
if (visType == WSType::normalWS || visType == WSType::text) {
|
||||
pointToPutCaret.Set(visNode, visOffset);
|
||||
if (forwardScanFromPointToPutCaretResult.InNormalWhiteSpacesOrText()) {
|
||||
pointToPutCaret = forwardScanFromPointToPutCaretResult.RawPoint();
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is editable <br> or something inline special element like
|
||||
// <img>, <input>, etc, move caret before it.
|
||||
if (visType == WSType::br || visType == WSType::special) {
|
||||
pointToPutCaret.Set(visNode);
|
||||
if (forwardScanFromPointToPutCaretResult.ReachedBRElement() ||
|
||||
forwardScanFromPointToPutCaretResult.ReachedSpecialContent()) {
|
||||
pointToPutCaret =
|
||||
forwardScanFromPointToPutCaretResult.RawPointAtContent();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -592,34 +590,34 @@ nsresult HTMLEditor::MaybeCollapseSelectionAtFirstEditableNode(
|
|||
// host.
|
||||
// XXX This may not make sense, but Chromium behaves so. Therefore, the
|
||||
// reason why we do this is just compatibility with Chromium.
|
||||
if (visType != WSType::otherBlock) {
|
||||
if (!forwardScanFromPointToPutCaretResult.ReachedOtherBlockElement()) {
|
||||
pointToPutCaret.Set(editingHost, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
// By definition of WSRunObject, a block element terminates a whitespace
|
||||
// By definition of WSRunScanner, a block element terminates a whitespace
|
||||
// run. That is, although we are calling a method that is named
|
||||
// "NextVisibleNode", the node returned might not be visible/editable!
|
||||
// "ScanNextVisibleNodeOrBlockBoundary", the node returned might not
|
||||
// be visible/editable!
|
||||
|
||||
// However, we were given a block that is not a container. Since the
|
||||
// block can not contain anything that's visible, such a block only
|
||||
// makes sense if it is visible by itself, like a <hr>. We want to
|
||||
// place the caret in front of that block.
|
||||
if (!IsContainer(visNode)) {
|
||||
pointToPutCaret.Set(visNode);
|
||||
if (!IsContainer(forwardScanFromPointToPutCaretResult.GetContent())) {
|
||||
pointToPutCaret =
|
||||
forwardScanFromPointToPutCaretResult.RawPointAtContent();
|
||||
break;
|
||||
}
|
||||
|
||||
// If the given block does not contain any visible/editable items, we want
|
||||
// to skip it and continue our search.
|
||||
if (IsEmptyNode(*visNode)) {
|
||||
if (IsEmptyNode(*forwardScanFromPointToPutCaretResult.GetContent())) {
|
||||
// Skip the empty block
|
||||
pointToPutCaret.Set(visNode);
|
||||
DebugOnly<bool> advanced = pointToPutCaret.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(
|
||||
advanced, "Failed to advance offset from the found empty block node");
|
||||
pointToPutCaret =
|
||||
forwardScanFromPointToPutCaretResult.RawPointAfterContent();
|
||||
} else {
|
||||
pointToPutCaret.Set(visNode, 0);
|
||||
pointToPutCaret.Set(forwardScanFromPointToPutCaretResult.GetContent(), 0);
|
||||
}
|
||||
}
|
||||
nsresult rv = SelectionRefPtr()->Collapse(pointToPutCaret);
|
||||
|
@ -934,18 +932,13 @@ bool HTMLEditor::IsVisibleBRElement(nsINode* aNode) {
|
|||
|
||||
// Sigh. We have to use expensive whitespace calculation code to
|
||||
// determine what is going on
|
||||
int32_t selOffset;
|
||||
nsCOMPtr<nsINode> selNode = GetNodeLocation(aNode, &selOffset);
|
||||
// Let's look after the break
|
||||
selOffset++;
|
||||
WSRunObject wsObj(this, selNode, selOffset);
|
||||
WSType visType;
|
||||
wsObj.NextVisibleNode(EditorRawDOMPoint(selNode, selOffset), &visType);
|
||||
if (visType & WSType::block) {
|
||||
EditorRawDOMPoint afterBRElement(EditorRawDOMPoint::After(*aNode));
|
||||
if (NS_WARN_IF(!afterBRElement.IsSet())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(*this,
|
||||
afterBRElement)
|
||||
.ReachedBlockBoundary();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1474,18 +1467,18 @@ EditorRawDOMPoint HTMLEditor::GetBetterInsertionPointFor(
|
|||
return pointToInsert;
|
||||
}
|
||||
|
||||
WSRunObject wsObj(this, pointToInsert.GetContainer(), pointToInsert.Offset());
|
||||
WSRunScanner wsScannerForPointToInsert(this, pointToInsert);
|
||||
|
||||
// If the insertion position is after the last visible item in a line,
|
||||
// i.e., the insertion position is just before a visible line break <br>,
|
||||
// we want to skip to the position just after the line break (see bug 68767).
|
||||
nsCOMPtr<nsINode> nextVisibleNode;
|
||||
WSType nextVisibleType;
|
||||
wsObj.NextVisibleNode(pointToInsert, address_of(nextVisibleNode), nullptr,
|
||||
&nextVisibleType);
|
||||
WSScanResult forwardScanFromPointToInsertResult =
|
||||
wsScannerForPointToInsert.ScanNextVisibleNodeOrBlockBoundaryFrom(
|
||||
pointToInsert);
|
||||
// So, if the next visible node isn't a <br> element, we can insert the block
|
||||
// level element to the point.
|
||||
if (!nextVisibleNode || !(nextVisibleType & WSType::br)) {
|
||||
if (!forwardScanFromPointToInsertResult.GetContent() ||
|
||||
!forwardScanFromPointToInsertResult.ReachedBRElement()) {
|
||||
return pointToInsert;
|
||||
}
|
||||
|
||||
|
@ -1493,25 +1486,21 @@ EditorRawDOMPoint HTMLEditor::GetBetterInsertionPointFor(
|
|||
// positioned at the beginning of a block, in that case skipping the <br>
|
||||
// would not insert the <br> at the caret position, but after the current
|
||||
// empty line.
|
||||
nsCOMPtr<nsINode> previousVisibleNode;
|
||||
WSType previousVisibleType;
|
||||
wsObj.PriorVisibleNode(pointToInsert, address_of(previousVisibleNode),
|
||||
nullptr, &previousVisibleType);
|
||||
WSScanResult backwardScanFromPointToInsertResult =
|
||||
wsScannerForPointToInsert.ScanPreviousVisibleNodeOrBlockBoundaryFrom(
|
||||
pointToInsert);
|
||||
// So, if there is no previous visible node,
|
||||
// or, if both nodes of the insertion point is <br> elements,
|
||||
// or, if the previous visible node is different block,
|
||||
// we need to skip the following <br>. So, otherwise, we can insert the
|
||||
// block at the insertion point.
|
||||
if (!previousVisibleNode || (previousVisibleType & WSType::br) ||
|
||||
(previousVisibleType & WSType::thisBlock)) {
|
||||
if (!backwardScanFromPointToInsertResult.GetContent() ||
|
||||
backwardScanFromPointToInsertResult.ReachedBRElement() ||
|
||||
backwardScanFromPointToInsertResult.ReachedCurrentBlockBoundary()) {
|
||||
return pointToInsert;
|
||||
}
|
||||
|
||||
EditorRawDOMPoint afterBRNode(nextVisibleNode);
|
||||
DebugOnly<bool> advanced = afterBRNode.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(advanced,
|
||||
"Failed to advance offset to after the <br> node");
|
||||
return afterBRNode;
|
||||
return forwardScanFromPointToInsertResult.RawPointAfterContent();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -3128,21 +3117,23 @@ nsresult HTMLEditor::DeleteParentBlocksWithTransactionIfEmpty(
|
|||
MOZ_ASSERT(mPlaceholderBatch);
|
||||
|
||||
// First, check there is visible contents before the point in current block.
|
||||
WSRunObject wsObj(this, aPoint);
|
||||
if (!(wsObj.mStartReason & WSType::thisBlock)) {
|
||||
WSRunScanner wsScannerForPoint(this, aPoint);
|
||||
if (wsScannerForPoint.StartReason() != WSType::thisBlock) {
|
||||
// If there is visible node before the point, we shouldn't remove the
|
||||
// parent block.
|
||||
return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
|
||||
}
|
||||
if (NS_WARN_IF(!wsObj.GetStartReasonContent()) ||
|
||||
NS_WARN_IF(!wsObj.GetStartReasonContent()->GetParentNode())) {
|
||||
if (NS_WARN_IF(!wsScannerForPoint.GetStartReasonContent()) ||
|
||||
NS_WARN_IF(!wsScannerForPoint.GetStartReasonContent()->GetParentNode())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (wsObj.GetEditingHost() == wsObj.GetStartReasonContent()) {
|
||||
if (wsScannerForPoint.GetEditingHost() ==
|
||||
wsScannerForPoint.GetStartReasonContent()) {
|
||||
// If we reach editing host, there is no parent blocks which can be removed.
|
||||
return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
|
||||
}
|
||||
if (HTMLEditUtils::IsTableCellOrCaption(*wsObj.GetStartReasonContent())) {
|
||||
if (HTMLEditUtils::IsTableCellOrCaption(
|
||||
*wsScannerForPoint.GetStartReasonContent())) {
|
||||
// If we reach a <td>, <th> or <caption>, we shouldn't remove it even
|
||||
// becomes empty because removing such element changes the structure of
|
||||
// the <table>.
|
||||
|
@ -3150,34 +3141,33 @@ nsresult HTMLEditor::DeleteParentBlocksWithTransactionIfEmpty(
|
|||
}
|
||||
|
||||
// Next, check there is visible contents after the point in current block.
|
||||
WSType wsType = WSType::none;
|
||||
wsObj.NextVisibleNode(aPoint, &wsType);
|
||||
if (wsType == WSType::br) {
|
||||
WSScanResult forwardScanFromPointResult =
|
||||
wsScannerForPoint.ScanNextVisibleNodeOrBlockBoundaryFrom(aPoint);
|
||||
if (forwardScanFromPointResult.ReachedBRElement()) {
|
||||
// If the <br> element is visible, we shouldn't remove the parent block.
|
||||
if (IsVisibleBRElement(wsObj.GetEndReasonContent())) {
|
||||
if (IsVisibleBRElement(wsScannerForPoint.GetEndReasonContent())) {
|
||||
return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
|
||||
}
|
||||
if (wsObj.GetEndReasonContent()->GetNextSibling()) {
|
||||
EditorRawDOMPoint afterBRElement;
|
||||
afterBRElement.SetAfter(wsObj.GetEndReasonContent());
|
||||
WSRunObject wsRunObjAfterBR(this, afterBRElement);
|
||||
WSType wsTypeAfterBR = WSType::none;
|
||||
wsRunObjAfterBR.NextVisibleNode(afterBRElement, &wsTypeAfterBR);
|
||||
if (wsTypeAfterBR != WSType::thisBlock) {
|
||||
if (wsScannerForPoint.GetEndReasonContent()->GetNextSibling()) {
|
||||
if (!WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
|
||||
*this, EditorRawDOMPoint::After(
|
||||
*wsScannerForPoint.GetEndReasonContent()))
|
||||
.ReachedCurrentBlockBoundary()) {
|
||||
// If we couldn't reach the block's end after the invisible <br>,
|
||||
// that means that there is visible content.
|
||||
return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
} else if (wsType != WSType::thisBlock) {
|
||||
} else if (!forwardScanFromPointResult.ReachedCurrentBlockBoundary()) {
|
||||
// If we couldn't reach the block's end, the block has visible content.
|
||||
return NS_SUCCESS_EDITOR_ELEMENT_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Delete the parent block.
|
||||
EditorDOMPoint nextPoint(wsObj.GetStartReasonContent()->GetParentNode(), 0);
|
||||
nsresult rv =
|
||||
DeleteNodeWithTransaction(MOZ_KnownLive(*wsObj.GetStartReasonContent()));
|
||||
EditorDOMPoint nextPoint(
|
||||
wsScannerForPoint.GetStartReasonContent()->GetParentNode(), 0);
|
||||
nsresult rv = DeleteNodeWithTransaction(
|
||||
MOZ_KnownLive(*wsScannerForPoint.GetStartReasonContent()));
|
||||
if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -3185,7 +3175,7 @@ nsresult HTMLEditor::DeleteParentBlocksWithTransactionIfEmpty(
|
|||
return rv;
|
||||
}
|
||||
// If we reach editing host, return NS_OK.
|
||||
if (nextPoint.GetContainer() == wsObj.GetEditingHost()) {
|
||||
if (nextPoint.GetContainer() == wsScannerForPoint.GetEditingHost()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -3199,7 +3189,7 @@ nsresult HTMLEditor::DeleteParentBlocksWithTransactionIfEmpty(
|
|||
NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
|
||||
Element* editingHost = GetActiveEditingHost();
|
||||
if (NS_WARN_IF(!editingHost) ||
|
||||
NS_WARN_IF(editingHost != wsObj.GetEditingHost())) {
|
||||
NS_WARN_IF(editingHost != wsScannerForPoint.GetEditingHost())) {
|
||||
return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
|
||||
}
|
||||
if (NS_WARN_IF(!EditorUtils::IsDescendantOf(*nextPoint.GetContainer(),
|
||||
|
@ -3970,15 +3960,11 @@ bool HTMLEditor::IsVisibleTextNode(Text& aText) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
WSRunScanner wsRunScanner(this, &aText, 0);
|
||||
nsCOMPtr<nsINode> nextVisibleNode;
|
||||
WSType visibleNodeType;
|
||||
wsRunScanner.NextVisibleNode(EditorRawDOMPoint(&aText, 0),
|
||||
address_of(nextVisibleNode), nullptr,
|
||||
&visibleNodeType);
|
||||
return (visibleNodeType == WSType::normalWS ||
|
||||
visibleNodeType == WSType::text) &&
|
||||
&aText == nextVisibleNode;
|
||||
WSScanResult nextWSScanResult =
|
||||
WSRunScanner::ScanNextVisibleNodeOrBlockBoundary(
|
||||
*this, EditorRawDOMPoint(&aText, 0));
|
||||
return nextWSScanResult.InNormalWhiteSpacesOrText() &&
|
||||
nextWSScanResult.TextPtr() == &aText;
|
||||
}
|
||||
|
||||
bool HTMLEditor::IsEmpty() const {
|
||||
|
|
|
@ -54,6 +54,8 @@ class ResizerSelectionListener;
|
|||
class SplitRangeOffFromNodeResult;
|
||||
class SplitRangeOffResult;
|
||||
class WSRunObject;
|
||||
class WSRunScanner;
|
||||
class WSScanResult;
|
||||
enum class EditSubAction : int32_t;
|
||||
struct PropItem;
|
||||
template <class T>
|
||||
|
@ -2246,14 +2248,14 @@ class HTMLEditor final : public TextEditor,
|
|||
* @param aAtomicContent The atomic content to be deleted.
|
||||
* @param aCaretPoint The caret point (i.e., selection start or
|
||||
* end).
|
||||
* @param aWSRunObjectAtCaret WSRunObject instance which was initialized with
|
||||
* the caret point.
|
||||
* @param aWSRunScannerAtCaret WSRunScanner instance which was initialized
|
||||
* with the caret point.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE EditActionResult
|
||||
HandleDeleteCollapsedSelectionAtAtomicContent(
|
||||
nsIEditor::EDirection aDirectionAndAmount,
|
||||
nsIEditor::EStripWrappers aStripWrappers, nsIContent& aAtomicContent,
|
||||
const EditorDOMPoint& aCaretPoint, WSRunObject& aWSRunObjectAtCaret);
|
||||
const EditorDOMPoint& aCaretPoint, WSRunScanner& aWSRunScannerAtCaret);
|
||||
|
||||
/**
|
||||
* HandleDeleteCollapsedSelectionAtOtherBlockBoundary() handles deletion at
|
||||
|
@ -2267,14 +2269,14 @@ class HTMLEditor final : public TextEditor,
|
|||
* is followed by caret.
|
||||
* @param aCaretPoint The caret point (i.e., selection start or
|
||||
* end).
|
||||
* @param aWSRunObjectAtCaret WSRunObject instance which was initialized with
|
||||
* the caret point.
|
||||
* @param aWSRunScannerAtCaret WSRunScanner instance which was initialized
|
||||
* with the caret point.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE EditActionResult
|
||||
HandleDeleteCollapsedSelectionAtOtherBlockBoundary(
|
||||
nsIEditor::EDirection aDirectionAndAmount,
|
||||
nsIEditor::EStripWrappers aStripWrappers, Element& aOtherBlockElement,
|
||||
const EditorDOMPoint& aCaretPoint, WSRunObject& aWSRunObjectAtCaret);
|
||||
const EditorDOMPoint& aCaretPoint, WSRunScanner& aWSRunScannerAtCaret);
|
||||
|
||||
/**
|
||||
* HandleDeleteCollapsedSelectionAtCurrentBlockBoundary() handles deletion
|
||||
|
@ -4577,6 +4579,7 @@ class HTMLEditor final : public TextEditor,
|
|||
friend class TextEditor;
|
||||
friend class WSRunObject;
|
||||
friend class WSRunScanner;
|
||||
friend class WSScanResult;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -632,24 +632,21 @@ nsresult HTMLEditor::DoInsertHTMLWithContext(
|
|||
|
||||
// Make sure we don't end up with selection collapsed after an invisible
|
||||
// `<br>` element.
|
||||
WSRunObject wsRunObj(this, pointToPutCaret);
|
||||
WSType visType;
|
||||
wsRunObj.PriorVisibleNode(pointToPutCaret, &visType);
|
||||
if (visType == WSType::br &&
|
||||
!IsVisibleBRElement(wsRunObj.GetStartReasonContent())) {
|
||||
WSRunObject wsRunObj2(this,
|
||||
EditorDOMPoint(wsRunObj.GetStartReasonContent()));
|
||||
nsCOMPtr<nsINode> visibleNode;
|
||||
int32_t visibleNodeOffset;
|
||||
wsRunObj2.PriorVisibleNode(pointToPutCaret, address_of(visibleNode),
|
||||
&visibleNodeOffset, &visType);
|
||||
if (visType == WSType::text || visType == WSType::normalWS) {
|
||||
pointToPutCaret.Set(visibleNode, visibleNodeOffset);
|
||||
} else if (visType == WSType::special) {
|
||||
pointToPutCaret.Set(wsRunObj2.GetStartReasonContent());
|
||||
DebugOnly<bool> advanced = pointToPutCaret.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(advanced,
|
||||
"Failed to advance offset from found object");
|
||||
WSRunScanner wsRunScannerAtCaret(this, pointToPutCaret);
|
||||
if (wsRunScannerAtCaret
|
||||
.ScanPreviousVisibleNodeOrBlockBoundaryFrom(pointToPutCaret)
|
||||
.ReachedBRElement() &&
|
||||
!IsVisibleBRElement(wsRunScannerAtCaret.GetStartReasonContent())) {
|
||||
WSRunScanner wsRunScannerAtStartReason(
|
||||
this, EditorDOMPoint(wsRunScannerAtCaret.GetStartReasonContent()));
|
||||
WSScanResult backwardScanFromPointToCaretResult =
|
||||
wsRunScannerAtStartReason.ScanPreviousVisibleNodeOrBlockBoundaryFrom(
|
||||
pointToPutCaret);
|
||||
if (backwardScanFromPointToCaretResult.InNormalWhiteSpacesOrText()) {
|
||||
pointToPutCaret = backwardScanFromPointToCaretResult.Point();
|
||||
} else if (backwardScanFromPointToCaretResult.ReachedSpecialContent()) {
|
||||
pointToPutCaret.SetAfter(
|
||||
wsRunScannerAtStartReason.GetStartReasonContent());
|
||||
}
|
||||
}
|
||||
DebugOnly<nsresult> rvIgnored =
|
||||
|
|
|
@ -44,22 +44,14 @@ template WSRunObject::WSRunObject(HTMLEditor* aHTMLEditor,
|
|||
template WSRunObject::WSRunObject(HTMLEditor* aHTMLEditor,
|
||||
const EditorRawDOMPoint& aScanStartPoint,
|
||||
const EditorRawDOMPoint& aScanEndPoint);
|
||||
template void WSRunScanner::PriorVisibleNode(const EditorDOMPoint& aPoint,
|
||||
nsCOMPtr<nsINode>* outVisNode,
|
||||
int32_t* outVisOffset,
|
||||
WSType* outType) const;
|
||||
template void WSRunScanner::PriorVisibleNode(const EditorRawDOMPoint& aPoint,
|
||||
nsCOMPtr<nsINode>* outVisNode,
|
||||
int32_t* outVisOffset,
|
||||
WSType* outType) const;
|
||||
template void WSRunScanner::NextVisibleNode(const EditorDOMPoint& aPoint,
|
||||
nsCOMPtr<nsINode>* outVisNode,
|
||||
int32_t* outVisOffset,
|
||||
WSType* outType) const;
|
||||
template void WSRunScanner::NextVisibleNode(const EditorRawDOMPoint& aPoint,
|
||||
nsCOMPtr<nsINode>* outVisNode,
|
||||
int32_t* outVisOffset,
|
||||
WSType* outType) const;
|
||||
template WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
|
||||
const EditorDOMPoint& aPoint) const;
|
||||
template WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
|
||||
const EditorRawDOMPoint& aPoint) const;
|
||||
template WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
|
||||
const EditorDOMPoint& aPoint) const;
|
||||
template WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
|
||||
const EditorRawDOMPoint& aPoint) const;
|
||||
template void WSRunObject::GetASCIIWhitespacesBounds(
|
||||
int16_t aDir, const EditorDOMPoint& aPoint, dom::Text** outStartNode,
|
||||
int32_t* outStartOffset, dom::Text** outEndNode,
|
||||
|
@ -537,14 +529,12 @@ nsresult WSRunObject::DeleteWSForward() {
|
|||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
void WSRunScanner::PriorVisibleNode(const EditorDOMPointBase<PT, CT>& aPoint,
|
||||
nsCOMPtr<nsINode>* outVisNode,
|
||||
int32_t* outVisOffset,
|
||||
WSType* outType) const {
|
||||
WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
|
||||
const EditorDOMPointBase<PT, CT>& aPoint) const {
|
||||
// Find first visible thing before the point. Position
|
||||
// outVisNode/outVisOffset just _after_ that thing. If we don't find
|
||||
// anything return start of ws.
|
||||
MOZ_ASSERT(aPoint.IsSet() && outType);
|
||||
MOZ_ASSERT(aPoint.IsSet());
|
||||
|
||||
WSFragment* run = FindNearestRun(aPoint, false);
|
||||
|
||||
|
@ -554,43 +544,30 @@ void WSRunScanner::PriorVisibleNode(const EditorDOMPointBase<PT, CT>& aPoint,
|
|||
WSPoint point = GetPreviousCharPoint(aPoint);
|
||||
// When it's a non-empty text node, return it.
|
||||
if (point.mTextNode && point.mTextNode->Length()) {
|
||||
if (outVisNode) {
|
||||
*outVisNode = point.mTextNode;
|
||||
}
|
||||
if (outVisOffset) {
|
||||
*outVisOffset = point.mOffset + 1;
|
||||
}
|
||||
if (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP) {
|
||||
*outType = WSType::normalWS;
|
||||
} else {
|
||||
*outType = WSType::text;
|
||||
}
|
||||
return;
|
||||
return WSScanResult(
|
||||
point.mTextNode, point.mOffset + 1,
|
||||
nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP
|
||||
? WSType::normalWS
|
||||
: WSType::text);
|
||||
}
|
||||
// If no text node, keep looking. We should eventually fall out of loop
|
||||
}
|
||||
}
|
||||
|
||||
if (outVisNode) {
|
||||
// If we get here, then nothing in ws data to find. Return start reason.
|
||||
*outVisNode = mStartReasonContent;
|
||||
if (mStartReasonContent != mStartNode) {
|
||||
// In this case, mStartOffset is not meaningful.
|
||||
return WSScanResult(mStartReasonContent, mStartReason);
|
||||
}
|
||||
if (outVisOffset) {
|
||||
// This really isn't meaningful if mStartReasonContent != mStartNode
|
||||
*outVisOffset = mStartOffset;
|
||||
}
|
||||
*outType = mStartReason;
|
||||
return WSScanResult(mStartReasonContent, mStartOffset, mStartReason);
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
void WSRunScanner::NextVisibleNode(const EditorDOMPointBase<PT, CT>& aPoint,
|
||||
nsCOMPtr<nsINode>* outVisNode,
|
||||
int32_t* outVisOffset,
|
||||
WSType* outType) const {
|
||||
WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
|
||||
const EditorDOMPointBase<PT, CT>& aPoint) const {
|
||||
// Find first visible thing after the point. Position
|
||||
// outVisNode/outVisOffset just _before_ that thing. If we don't find
|
||||
// anything return end of ws.
|
||||
MOZ_ASSERT(aPoint.IsSet() && outType);
|
||||
MOZ_ASSERT(aPoint.IsSet());
|
||||
|
||||
WSFragment* run = FindNearestRun(aPoint, true);
|
||||
|
||||
|
@ -600,32 +577,21 @@ void WSRunScanner::NextVisibleNode(const EditorDOMPointBase<PT, CT>& aPoint,
|
|||
WSPoint point = GetNextCharPoint(aPoint);
|
||||
// When it's a non-empty text node, return it.
|
||||
if (point.mTextNode && point.mTextNode->Length()) {
|
||||
if (outVisNode) {
|
||||
*outVisNode = point.mTextNode;
|
||||
}
|
||||
if (outVisOffset) {
|
||||
*outVisOffset = point.mOffset;
|
||||
}
|
||||
if (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP) {
|
||||
*outType = WSType::normalWS;
|
||||
} else {
|
||||
*outType = WSType::text;
|
||||
}
|
||||
return;
|
||||
return WSScanResult(
|
||||
point.mTextNode, point.mOffset,
|
||||
nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP
|
||||
? WSType::normalWS
|
||||
: WSType::text);
|
||||
}
|
||||
// If no text node, keep looking. We should eventually fall out of loop
|
||||
}
|
||||
}
|
||||
|
||||
if (outVisNode) {
|
||||
// If we get here, then nothing in ws data to find. Return end reason
|
||||
*outVisNode = mEndReasonContent;
|
||||
if (mEndReasonContent != mEndNode) {
|
||||
// In this case, mEndOffset is not meaningful.
|
||||
return WSScanResult(mEndReasonContent, mEndReason);
|
||||
}
|
||||
if (outVisOffset) {
|
||||
// This really isn't meaningful if mEndReasonContent != mEndNode
|
||||
*outVisOffset = mEndOffset;
|
||||
}
|
||||
*outType = mEndReason;
|
||||
return WSScanResult(mEndReasonContent, mEndOffset, mEndReason);
|
||||
}
|
||||
|
||||
nsresult WSRunObject::AdjustWhitespace() {
|
||||
|
@ -2079,8 +2045,4 @@ nsresult WSRunObject::Scrub() {
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool WSRunScanner::IsBlockNode(nsINode* aNode) {
|
||||
return aNode && aNode->IsElement() && HTMLEditor::NodeIsBlockStatic(*aNode);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -6,15 +6,20 @@
|
|||
#ifndef WSRunObject_h
|
||||
#define WSRunObject_h
|
||||
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/EditAction.h"
|
||||
#include "mozilla/EditorBase.h"
|
||||
#include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint
|
||||
#include "mozilla/HTMLEditor.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/HTMLBRElement.h"
|
||||
#include "mozilla/dom/Text.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIContent.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class HTMLEditor;
|
||||
|
||||
// class WSRunObject represents the entire whitespace situation
|
||||
// around a given point. It collects up a list of nodes that contain
|
||||
// whitespace and categorizes in up to 3 different WSFragments (detailed
|
||||
|
@ -131,6 +136,201 @@ inline const WSType operator|(const WSType::Enum& aLeft,
|
|||
return WSType(aLeft) | WSType(aRight);
|
||||
}
|
||||
|
||||
/**
|
||||
* WSScanResult is result of ScanNextVisibleNodeOrBlockBoundaryFrom(),
|
||||
* ScanPreviousVisibleNodeOrBlockBoundaryFrom(), and their static wrapper
|
||||
* methods. This will have information of found visible content (and its
|
||||
* position) or reached block element or topmost editable content at the
|
||||
* start of scanner.
|
||||
*/
|
||||
class MOZ_STACK_CLASS WSScanResult final {
|
||||
public:
|
||||
WSScanResult() = delete;
|
||||
MOZ_NEVER_INLINE_DEBUG WSScanResult(nsIContent* aContent, WSType aReason)
|
||||
: mContent(aContent), mReason(aReason) {
|
||||
AssertIfInvalidData();
|
||||
}
|
||||
MOZ_NEVER_INLINE_DEBUG WSScanResult(nsIContent* aContent, uint32_t aOffset,
|
||||
WSType aReason)
|
||||
: mContent(aContent), mOffset(Some(aOffset)), mReason(aReason) {
|
||||
AssertIfInvalidData();
|
||||
}
|
||||
|
||||
void AssertIfInvalidData() const {
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(mReason == WSType::text || mReason == WSType::normalWS ||
|
||||
mReason == WSType::br || mReason == WSType::special ||
|
||||
mReason == WSType::thisBlock || mReason == WSType::otherBlock);
|
||||
MOZ_ASSERT_IF(mReason == WSType::text || mReason == WSType::normalWS,
|
||||
mContent && mContent->IsText());
|
||||
MOZ_ASSERT_IF(mReason == WSType::br,
|
||||
mContent && mContent->IsHTMLElement(nsGkAtoms::br));
|
||||
MOZ_ASSERT_IF(
|
||||
mReason == WSType::special,
|
||||
mContent && ((mContent->IsText() && !mContent->IsEditable()) ||
|
||||
(!mContent->IsHTMLElement(nsGkAtoms::br) &&
|
||||
!HTMLEditor::NodeIsBlockStatic(*mContent))));
|
||||
MOZ_ASSERT_IF(mReason == WSType::otherBlock,
|
||||
mContent && HTMLEditor::NodeIsBlockStatic(*mContent));
|
||||
// If mReason is WSType::thisBlock, mContent can be any content. In most
|
||||
// cases, it's current block element which is editable. However, if there
|
||||
// is no editable block parent, this is topmost editable inline content.
|
||||
// Additionally, if there is no editable content, this is the container
|
||||
// start of scanner and is not editable.
|
||||
MOZ_ASSERT_IF(
|
||||
mReason == WSType::thisBlock,
|
||||
!mContent || !mContent->GetParentElement() ||
|
||||
HTMLEditor::NodeIsBlockStatic(*mContent) ||
|
||||
HTMLEditor::NodeIsBlockStatic(*mContent->GetParentElement()) ||
|
||||
!mContent->GetParentElement()->IsEditable());
|
||||
#endif // #ifdef DEBUG
|
||||
}
|
||||
|
||||
/**
|
||||
* GetContent() returns found visible and editable content/element.
|
||||
* See MOZ_ASSERT_IF()s in AssertIfInvalidData() for the detail.
|
||||
*/
|
||||
nsIContent* GetContent() const { return mContent; }
|
||||
|
||||
/**
|
||||
* The following accessors makes it easier to understand each callers.
|
||||
*/
|
||||
MOZ_NEVER_INLINE_DEBUG dom::Element* ElementPtr() const {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mContent->IsElement());
|
||||
return mContent->AsElement();
|
||||
}
|
||||
MOZ_NEVER_INLINE_DEBUG dom::HTMLBRElement* BRElementPtr() const {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mContent->IsHTMLElement(nsGkAtoms::br));
|
||||
return static_cast<dom::HTMLBRElement*>(mContent.get());
|
||||
}
|
||||
MOZ_NEVER_INLINE_DEBUG dom::Text* TextPtr() const {
|
||||
MOZ_DIAGNOSTIC_ASSERT(mContent->IsText());
|
||||
return mContent->AsText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if found or reached content is ediable.
|
||||
*/
|
||||
bool IsContentEditable() const { return mContent && mContent->IsEditable(); }
|
||||
|
||||
/**
|
||||
* Offset() returns meaningful value only when InNormalWhiteSpacesOrText()
|
||||
* returns true or the scanner reached to start or end of its scanning
|
||||
* range and that is same as start or end container which are specified
|
||||
* when the scanner is initialized. If it's result of scanning backward,
|
||||
* this offset means before the found point. Otherwise, i.e., scanning
|
||||
* forward, this offset means after the found point.
|
||||
*/
|
||||
MOZ_NEVER_INLINE_DEBUG uint32_t Offset() const {
|
||||
NS_ASSERTION(mOffset.isSome(), "Retrieved non-meaningful offset");
|
||||
return mOffset.valueOr(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Point() and RawPoint() return the position in found visible node or
|
||||
* reached block boundary. So, they return meaningful point only when
|
||||
* Offset() returns meaningful value.
|
||||
*/
|
||||
MOZ_NEVER_INLINE_DEBUG EditorDOMPoint Point() const {
|
||||
NS_ASSERTION(mOffset.isSome(), "Retrieved non-meaningful point");
|
||||
return EditorDOMPoint(mContent, mOffset.valueOr(0));
|
||||
}
|
||||
MOZ_NEVER_INLINE_DEBUG EditorRawDOMPoint RawPoint() const {
|
||||
NS_ASSERTION(mOffset.isSome(), "Retrieved non-meaningful raw point");
|
||||
return EditorRawDOMPoint(mContent, mOffset.valueOr(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* PointAtContent() and RawPointAtContent() return the position of found
|
||||
* visible content or reached block element.
|
||||
*/
|
||||
MOZ_NEVER_INLINE_DEBUG EditorDOMPoint PointAtContent() const {
|
||||
MOZ_ASSERT(mContent);
|
||||
return EditorDOMPoint(mContent);
|
||||
}
|
||||
MOZ_NEVER_INLINE_DEBUG EditorRawDOMPoint RawPointAtContent() const {
|
||||
MOZ_ASSERT(mContent);
|
||||
return EditorRawDOMPoint(mContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* PointAfterContent() and RawPointAfterContent() retrun the position after
|
||||
* found visible content or reached block element.
|
||||
*/
|
||||
MOZ_NEVER_INLINE_DEBUG EditorDOMPoint PointAfterContent() const {
|
||||
MOZ_ASSERT(mContent);
|
||||
return mContent ? EditorDOMPoint::After(*mContent) : EditorDOMPoint();
|
||||
}
|
||||
MOZ_NEVER_INLINE_DEBUG EditorRawDOMPoint RawPointAfterContent() const {
|
||||
MOZ_ASSERT(mContent);
|
||||
return mContent ? EditorRawDOMPoint::After(*mContent) : EditorRawDOMPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* The scanner reached <img> or something which is inline and is not a
|
||||
* container.
|
||||
*/
|
||||
bool ReachedSpecialContent() const { return mReason == WSType::special; }
|
||||
|
||||
/**
|
||||
* The point is in normal whitespaces or text.
|
||||
*/
|
||||
bool InNormalWhiteSpacesOrText() const {
|
||||
return mReason == WSType::normalWS || mReason == WSType::text;
|
||||
}
|
||||
|
||||
/**
|
||||
* The point is in normal whitespaces.
|
||||
*/
|
||||
bool InNormalWhiteSpaces() const { return mReason == WSType::normalWS; }
|
||||
|
||||
/**
|
||||
* The point is in normal text.
|
||||
*/
|
||||
bool InNormalText() const { return mReason == WSType::text; }
|
||||
|
||||
/**
|
||||
* The scanner reached a <br> element.
|
||||
*/
|
||||
bool ReachedBRElement() const { return mReason == WSType::br; }
|
||||
|
||||
/**
|
||||
* The scanner reached a <hr> element.
|
||||
*/
|
||||
bool ReachedHRElement() const {
|
||||
return mContent && mContent->IsHTMLElement(nsGkAtoms::hr);
|
||||
}
|
||||
|
||||
/**
|
||||
* The scanner reached current block boundary or other block element.
|
||||
*/
|
||||
bool ReachedBlockBoundary() const { return !!(mReason & WSType::block); }
|
||||
|
||||
/**
|
||||
* The scanner reached current block element boundary.
|
||||
*/
|
||||
bool ReachedCurrentBlockBoundary() const {
|
||||
return mReason == WSType::thisBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* The scanner reached other block element.
|
||||
*/
|
||||
bool ReachedOtherBlockElement() const {
|
||||
return mReason == WSType::otherBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* The scanner reached something non-text node.
|
||||
*/
|
||||
bool ReachedSomething() const { return !InNormalWhiteSpacesOrText(); }
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIContent> mContent;
|
||||
Maybe<uint32_t> mOffset;
|
||||
WSType mReason;
|
||||
};
|
||||
|
||||
class MOZ_STACK_CLASS WSRunScanner {
|
||||
public:
|
||||
/**
|
||||
|
@ -166,46 +366,62 @@ class MOZ_STACK_CLASS WSRunScanner {
|
|||
EditorRawDOMPoint(aScanStartNode, aScanStartOffset)) {}
|
||||
~WSRunScanner();
|
||||
|
||||
// NextVisibleNode() returns the first piece of visible thing after aPoint.
|
||||
// If there is no visible ws qualifying it returns what is after the ws run.
|
||||
// If outVisNode and/or outvisOffset is unused, callers can use nullptr.
|
||||
// Note that {outVisNode,outVisOffset} is set to just BEFORE the visible
|
||||
// object. Also outVisOffset might be invalid offset unless outVisNode is
|
||||
// end reason node.
|
||||
// ScanNextVisibleNodeOrBlockBoundaryForwardFrom() returns the first visible
|
||||
// node after aPoint. If there is no visible nodes after aPoint, returns
|
||||
// topmost editable inline ancestor at end of current block. See comments
|
||||
// around WSScanResult for the detail.
|
||||
template <typename PT, typename CT>
|
||||
void NextVisibleNode(const EditorDOMPointBase<PT, CT>& aPoint,
|
||||
nsCOMPtr<nsINode>* outVisNode, int32_t* outVisOffset,
|
||||
WSType* outType) const;
|
||||
|
||||
WSScanResult ScanNextVisibleNodeOrBlockBoundaryFrom(
|
||||
const EditorDOMPointBase<PT, CT>& aPoint) const;
|
||||
template <typename PT, typename CT>
|
||||
void NextVisibleNode(const EditorDOMPointBase<PT, CT>& aPoint,
|
||||
WSType* outType) const {
|
||||
NextVisibleNode(aPoint, nullptr, nullptr, outType);
|
||||
static WSScanResult ScanNextVisibleNodeOrBlockBoundary(
|
||||
const HTMLEditor& aHTMLEditor, const EditorDOMPointBase<PT, CT>& aPoint) {
|
||||
return WSRunScanner(&aHTMLEditor, aPoint)
|
||||
.ScanNextVisibleNodeOrBlockBoundaryFrom(aPoint);
|
||||
}
|
||||
|
||||
// PriorVisibleNode() returns the first piece of visible thing before aPoint.
|
||||
// If there is no visible ws qualifying it returns what is before the ws run.
|
||||
// If outVisNode and/or outvisOffset is unused, callers can use nullptr.
|
||||
// Note that {outVisNode,outVisOffset} is set to just AFTER the visible
|
||||
// object. Also outVisOffset might be invalid offset unless outVisNode is
|
||||
// start reason node.
|
||||
// ScanPreviousVisibleNodeOrBlockBoundaryFrom() returns the first visible node
|
||||
// before aPoint. If there is no visible nodes before aPoint, returns topmost
|
||||
// editable inline ancestor at start of current block. See comments around
|
||||
// WSScanResult for the detail.
|
||||
template <typename PT, typename CT>
|
||||
void PriorVisibleNode(const EditorDOMPointBase<PT, CT>& aPoint,
|
||||
nsCOMPtr<nsINode>* outVisNode, int32_t* outVisOffset,
|
||||
WSType* outType) const;
|
||||
|
||||
WSScanResult ScanPreviousVisibleNodeOrBlockBoundaryFrom(
|
||||
const EditorDOMPointBase<PT, CT>& aPoint) const;
|
||||
template <typename PT, typename CT>
|
||||
void PriorVisibleNode(const EditorDOMPointBase<PT, CT>& aPoint,
|
||||
WSType* outType) const {
|
||||
PriorVisibleNode(aPoint, nullptr, nullptr, outType);
|
||||
static WSScanResult ScanPreviousVisibleNodeOrBlockBoundary(
|
||||
const HTMLEditor& aHTMLEditor, const EditorDOMPointBase<PT, CT>& aPoint) {
|
||||
return WSRunScanner(&aHTMLEditor, aPoint)
|
||||
.ScanPreviousVisibleNodeOrBlockBoundaryFrom(aPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Document us!
|
||||
* GetStartReasonContent() and GetEndReasonContent() return a node which
|
||||
* was found by scanning from mScanStartPoint backward or mScanEndPoint
|
||||
* forward. If there was whitespaces or text from the point, returns the
|
||||
* text node. Otherwise, returns an element which is explained by
|
||||
* StartReason() or EndReason(). Note that when the reason is
|
||||
* WSType::thisBlock, In most cases, it's current block element which is
|
||||
* editable, but also may be non-element and/or non-editable. See
|
||||
* MOZ_ASSERT_IF()s in WSScanResult::AssertIfInvalidData() for the detail.
|
||||
*/
|
||||
nsIContent* GetStartReasonContent() const { return mStartReasonContent; }
|
||||
nsIContent* GetEndReasonContent() const { return mEndReasonContent; }
|
||||
|
||||
/**
|
||||
* StartReason() and EndReason() return one of WSType::normalWS, WSType::text,
|
||||
* WSType::br, WSType::special, WSType::thisBlock, WSType::otherBlock.
|
||||
* If WSType::normalWS or WSType::text, scanning from mScanStartPoint backward
|
||||
* or mScanEndPoint forward stopped in a text node.
|
||||
* Otherwise, they reached an element which is what the WSType indicates.
|
||||
*/
|
||||
WSType StartReason() const { return mStartReason; }
|
||||
WSType EndReason() const { return mEndReason; }
|
||||
|
||||
/**
|
||||
* Active editing host when this instance is created.
|
||||
*/
|
||||
Element* GetEditingHost() const { return mEditingHost; }
|
||||
|
||||
protected:
|
||||
// WSFragment represents a single run of ws (all leadingws, or all normalws,
|
||||
// or all trailingws, or all leading+trailingws). Note that this single run
|
||||
|
@ -304,7 +520,9 @@ class MOZ_STACK_CLASS WSRunScanner {
|
|||
nsIContent* GetEditableBlockParentOrTopmotEditableInlineContent(
|
||||
nsIContent* aContent) const;
|
||||
|
||||
static bool IsBlockNode(nsINode* aNode);
|
||||
static bool IsBlockNode(nsINode* aNode) {
|
||||
return aNode && aNode->IsElement() && HTMLEditor::NodeIsBlockStatic(*aNode);
|
||||
}
|
||||
|
||||
nsIContent* GetPreviousWSNodeInner(nsINode* aStartNode,
|
||||
nsINode* aBlockParent) const;
|
||||
|
@ -370,6 +588,7 @@ class MOZ_STACK_CLASS WSRunScanner {
|
|||
// The last WSFragment in the run, may be same as first.
|
||||
WSFragment* mEndRun;
|
||||
|
||||
// See above comment for GetStartReasonContent() and GetEndReasonContent().
|
||||
nsCOMPtr<nsIContent> mStartReasonContent;
|
||||
nsCOMPtr<nsIContent> mEndReasonContent;
|
||||
|
||||
|
@ -526,8 +745,6 @@ class MOZ_STACK_CLASS WSRunObject final : public WSRunScanner {
|
|||
// be safely converted to regular ascii space and converts them.
|
||||
MOZ_CAN_RUN_SCRIPT nsresult AdjustWhitespace();
|
||||
|
||||
Element* GetEditingHost() const { return mEditingHost; }
|
||||
|
||||
protected:
|
||||
using WSPoint = WSRunScanner::WSPoint;
|
||||
|
||||
|
|
|
@ -489,6 +489,7 @@
|
|||
|
||||
|
||||
[insertparagraph.html?6001-last]
|
||||
max-asserts: 2 # Only on W-fis on Linux1804-64-qr and on Android, there are 2 assertions of retrieving meaningless raw DOM point from WSScannerResult
|
||||
disabled:
|
||||
if (os == "android") and not e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=1499003
|
||||
[[["defaultparagraphseparator","div"\],["insertparagraph",""\]\] "<div class=a id=x><p class=b id=y>[\]foo</div>" queryCommandValue("defaultparagraphseparator") before]
|
||||
|
@ -502,6 +503,8 @@
|
|||
|
||||
|
||||
[insertparagraph.html?5001-6000]
|
||||
min-asserts: 14 # Retrieving meaningless raw DOM point from WSScannerResult
|
||||
max-asserts: 16 # Only on W-fis on Linux1804-64-qr and on Android, there are 2 additional assertions.
|
||||
[[["defaultparagraphseparator","p"\],["insertparagraph",""\]\] "<ul><li><div>foo[\]</ul>" compare innerHTML]
|
||||
expected: FAIL
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче