Bug 1781994 - part 6: Make HTMLEditor::SetInlinePropertyInternal use AutoRangeArray r=m_kato

Depends on D154347

Differential Revision: https://phabricator.services.mozilla.com/D154348
This commit is contained in:
Masayuki Nakano 2022-08-16 00:38:30 +00:00
parent 9d26dd1005
commit da11385641
3 changed files with 81 additions and 72 deletions

View File

@ -3392,9 +3392,9 @@ nsresult HTMLEditor::InsertLinkAroundSelectionAsAction(
nsAutoString value;
for (uint32_t i = 0; i < count; ++i) {
// XXX nsDOMAttributeMap::Item() accesses current attribute at the index.
// Therefore, if `SetInlinePropertyInternal()` changed the attributes,
// this may fail to scan some attributes. Perhaps, we need to cache
// all attributes first.
// Therefore, if `SetInlinePropertyAsSubAction()` changed the
// attributes, this may fail to scan some attributes. Perhaps, we need
// to cache all attributes first.
RefPtr<Attr> attribute = attributeMap->Item(i);
if (!attribute) {
continue;
@ -3408,10 +3408,10 @@ nsresult HTMLEditor::InsertLinkAroundSelectionAsAction(
attribute->GetValue(value);
nsresult rv = SetInlinePropertyInternal(
nsresult rv = SetInlinePropertyAsSubAction(
*nsGkAtoms::a, MOZ_KnownLive(attributeName), value);
if (NS_FAILED(rv)) {
NS_WARNING("SetInlinePropertyInternal(nsGkAtoms::a) failed");
NS_WARNING("SetInlinePropertyAsSubAction(nsGkAtoms::a) failed");
return rv;
}
}
@ -5666,8 +5666,8 @@ nsresult HTMLEditor::SetCSSBackgroundColorWithTransaction(
AutoTransactionsConserveSelection dontChangeMySelection(*this);
// Loop through the ranges in the selection
// XXX This is different from `SetInlinePropertyInternal()`. It uses
// AutoSelectionRangeArray to store all ranges first. The result may be
// XXX This is different from `SetInlinePropertyAsSubAction()`. It uses
// AutoRangeArray to store all ranges first. The result may be
// different if mutation event listener changes the `Selection`.
// TODO: Store all selection ranges first since this updates the style.
for (uint32_t i = 0; i < SelectionRef().RangeCount(); i++) {

View File

@ -3298,18 +3298,19 @@ class HTMLEditor final : public EditorBase,
Document& aDocument, const nsACString& aCharacterSet);
/**
* SetInlinePropertyInternal() stores new style with `mTypeInState` if
* SetInlinePropertyAsSubAction() stores new style with `mTypeInState` if
* `Selection` is collapsed. Otherwise, applying the style at all selection
* ranges.
*
* @param aProperty One of the presentation tag names which we
* @param aHTMLProperty One of the presentation tag names which we
* support in style editor.
* @param aAttribute For some aProperty values, needs to be set to
* its attribute name. Otherwise, nullptr.
* @param aAttributeValue The value of aAttribute.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertyInternal(
nsAtom& aProperty, nsAtom* aAttribute, const nsAString& aValue);
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
SetInlinePropertyAsSubAction(nsAtom& aHTMLProperty, nsAtom* aAttribute,
const nsAString& aAttributeValue);
/**
* RemoveInlinePropertyAsSubAction() removes specified style from

View File

@ -158,10 +158,10 @@ nsresult HTMLEditor::SetInlinePropertyAsAction(nsAtom& aProperty,
}
}
}
rv = SetInlinePropertyInternal(MOZ_KnownLive(*property),
MOZ_KnownLive(attribute), value);
rv = SetInlinePropertyAsSubAction(MOZ_KnownLive(*property),
MOZ_KnownLive(attribute), value);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::SetInlinePropertyInternal() failed");
"HTMLEditor::SetInlinePropertyAsSubAction() failed");
return EditorBase::ToGenericNSResult(rv);
}
@ -195,14 +195,16 @@ NS_IMETHODIMP HTMLEditor::SetInlineProperty(const nsAString& aProperty,
"CanHandleAndMaybeDispatchBeforeInputEvent(), failed");
return EditorBase::ToGenericNSResult(rv);
}
rv = SetInlinePropertyInternal(*property, MOZ_KnownLive(attribute), aValue);
rv =
SetInlinePropertyAsSubAction(*property, MOZ_KnownLive(attribute), aValue);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::SetInlinePropertyInternal() failed");
"HTMLEditor::SetInlinePropertyAsSubAction() failed");
return EditorBase::ToGenericNSResult(rv);
}
nsresult HTMLEditor::SetInlinePropertyInternal(
nsAtom& aProperty, nsAtom* aAttribute, const nsAString& aAttributeValue) {
nsresult HTMLEditor::SetInlinePropertyAsSubAction(
nsAtom& aHTMLProperty, nsAtom* aAttribute,
const nsAString& aAttributeValue) {
MOZ_ASSERT(IsEditActionDataAvailable());
if (NS_WARN_IF(!mInitSucceeded)) {
@ -216,7 +218,7 @@ nsresult HTMLEditor::SetInlinePropertyInternal(
if (SelectionRef().IsCollapsed()) {
// Manipulating text attributes on a collapsed selection only sets state
// for the next text insertion
mTypeInState->SetProp(&aProperty, aAttribute, aAttributeValue);
mTypeInState->SetProp(&aHTMLProperty, aAttribute, aAttributeValue);
return NS_OK;
}
@ -244,45 +246,36 @@ nsresult HTMLEditor::SetInlinePropertyInternal(
!ignoredError.Failed(),
"HTMLEditor::OnStartToHandleTopLevelEditSubAction() failed, but ignored");
{
AutoSelectionRestorer restoreSelectionLater(*this);
// TODO: We don't need AutoTransactionsConserveSelection here in the normal
// cases, but removing this may cause the behavior with the legacy
// mutation event listeners. We should try to delete this in a bug.
AutoTransactionsConserveSelection dontChangeMySelection(*this);
// TODO: We don't need AutoTransactionsConserveSelection here in the normal
// cases, but removing this may cause the behavior with the legacy
// mutation event listeners. We should try to delete this in a bug.
AutoTransactionsConserveSelection dontChangeMySelection(*this);
// Loop through the ranges in the selection
// XXX This is different from `SetCSSBackgroundColorWithTransaction()`.
// It refers `Selection::GetRangeAt()` in each time. The result may
// be different if mutation event listener changes the `Selection`.
AutoSelectionRangeArray arrayOfRanges(SelectionRef());
for (auto& range : arrayOfRanges.mRanges) {
AutoRangeArray selectionRanges(SelectionRef());
{ // TODO: This block keeps the following line history after part.8
MOZ_ALWAYS_TRUE(selectionRanges.SaveAndTrackRanges(*this));
for (const OwningNonNull<nsRange>& selectionRange :
selectionRanges.Ranges()) {
// Adjust range to include any ancestors whose children are entirely
// selected
nsresult rv = PromoteInlineRange(*range);
nsresult rv = PromoteInlineRange(*selectionRange);
if (NS_FAILED(rv)) {
NS_WARNING("HTMLEditor::PromoteInlineRange() failed");
return rv;
}
// XXX Shouldn't we skip the range if it's been collapsed by mutation
// event listener?
EditorDOMPoint startOfRange(range->StartRef());
EditorDOMPoint endOfRange(range->EndRef());
if (NS_WARN_IF(!startOfRange.IsSet()) ||
NS_WARN_IF(!endOfRange.IsSet())) {
EditorDOMRange range(selectionRange);
if (NS_WARN_IF(!range.IsPositioned())) {
continue;
}
// If range is in a text node, apply new style simply.
if (startOfRange.GetContainer() == endOfRange.GetContainer() &&
startOfRange.IsInTextNode()) {
if (range.InSameContainer() && range.StartRef().IsInTextNode()) {
SplitRangeOffFromNodeResult wrapTextInStyledElementResult =
SetInlinePropertyOnTextNode(
MOZ_KnownLive(*startOfRange.ContainerAs<Text>()),
startOfRange.Offset(), endOfRange.Offset(), aProperty,
aAttribute, aAttributeValue);
MOZ_KnownLive(*range.StartRef().ContainerAs<Text>()),
range.StartRef().Offset(), range.EndRef().Offset(),
aHTMLProperty, aAttribute, aAttributeValue);
if (wrapTextInStyledElementResult.isErr()) {
NS_WARNING("HTMLEditor::SetInlinePropertyOnTextNode() failed");
return wrapTextInStyledElementResult.unwrapErr();
@ -294,33 +287,39 @@ nsresult HTMLEditor::SetInlinePropertyInternal(
}
// Collect editable nodes which are entirely contained in the range.
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
ContentSubtreeIterator subtreeIter;
// If there is no node which is entirely in the range,
// `ContentSubtreeIterator::Init()` fails, but this is possible case,
// don't warn it.
if (NS_SUCCEEDED(subtreeIter.Init(range))) {
for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
nsINode* node = subtreeIter.GetCurrentNode();
if (NS_WARN_IF(!node)) {
return NS_ERROR_FAILURE;
}
if (node->IsContent() && EditorUtils::IsEditableContent(
*node->AsContent(), EditorType::HTML)) {
arrayOfContents.AppendElement(*node->AsContent());
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContentsAroundRange;
{
ContentSubtreeIterator subtreeIter;
// If there is no node which is entirely in the range,
// `ContentSubtreeIterator::Init()` fails, but this is possible case,
// don't warn it.
if (NS_SUCCEEDED(
subtreeIter.Init(range.StartRef().ToRawRangeBoundary(),
range.EndRef().ToRawRangeBoundary()))) {
for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
nsINode* node = subtreeIter.GetCurrentNode();
if (NS_WARN_IF(!node)) {
return NS_ERROR_FAILURE;
}
if (node->IsContent() &&
EditorUtils::IsEditableContent(*node->AsContent(),
EditorType::HTML)) {
arrayOfContentsAroundRange.AppendElement(*node->AsContent());
}
}
}
}
// If start node is a text node, apply new style to a part of it.
if (startOfRange.IsInTextNode() &&
EditorUtils::IsEditableContent(*startOfRange.ContainerAs<Text>(),
if (range.StartRef().IsInTextNode() &&
EditorUtils::IsEditableContent(*range.StartRef().ContainerAs<Text>(),
EditorType::HTML)) {
SplitRangeOffFromNodeResult wrapTextInStyledElementResult =
SetInlinePropertyOnTextNode(
MOZ_KnownLive(*startOfRange.ContainerAs<Text>()),
startOfRange.Offset(), startOfRange.GetContainer()->Length(),
aProperty, aAttribute, aAttributeValue);
MOZ_KnownLive(*range.StartRef().ContainerAs<Text>()),
range.StartRef().Offset(),
range.StartRef().ContainerAs<Text>()->TextDataLength(),
aHTMLProperty, aAttribute, aAttributeValue);
if (wrapTextInStyledElementResult.isErr()) {
NS_WARNING("HTMLEditor::SetInlinePropertyOnTextNode() failed");
return wrapTextInStyledElementResult.unwrapErr();
@ -331,11 +330,11 @@ nsresult HTMLEditor::SetInlinePropertyInternal(
}
// Then, apply new style to all nodes in the range entirely.
for (auto& content : arrayOfContents) {
// MOZ_KnownLive because 'arrayOfContents' is guaranteed to
for (auto& content : arrayOfContentsAroundRange) {
// MOZ_KnownLive because 'arrayOfContentsAroundRange' guarantees to
// keep it alive.
Result<EditorDOMPoint, nsresult> setStyleResult =
SetInlinePropertyOnNode(MOZ_KnownLive(*content), aProperty,
SetInlinePropertyOnNode(MOZ_KnownLive(*content), aHTMLProperty,
aAttribute, aAttributeValue);
if (MOZ_UNLIKELY(setStyleResult.isErr())) {
NS_WARNING("HTMLEditor::SetInlinePropertyOnNode() failed");
@ -346,13 +345,14 @@ nsresult HTMLEditor::SetInlinePropertyInternal(
}
// Finally, if end node is a text node, apply new style to a part of it.
if (endOfRange.IsInTextNode() &&
EditorUtils::IsEditableContent(*endOfRange.ContainerAs<Text>(),
if (range.EndRef().IsInTextNode() &&
EditorUtils::IsEditableContent(*range.EndRef().ContainerAs<Text>(),
EditorType::HTML)) {
SplitRangeOffFromNodeResult wrapTextInStyledElementResult =
SetInlinePropertyOnTextNode(
MOZ_KnownLive(*endOfRange.ContainerAs<Text>()), 0,
endOfRange.Offset(), aProperty, aAttribute, aAttributeValue);
MOZ_KnownLive(*range.EndRef().ContainerAs<Text>()), 0,
range.EndRef().Offset(), aHTMLProperty, aAttribute,
aAttributeValue);
if (wrapTextInStyledElementResult.isErr()) {
NS_WARNING("HTMLEditor::SetInlinePropertyOnTextNode() failed");
return wrapTextInStyledElementResult.unwrapErr();
@ -362,9 +362,17 @@ nsresult HTMLEditor::SetInlinePropertyInternal(
wrapTextInStyledElementResult.IgnoreCaretPointSuggestion();
}
}
MOZ_ASSERT(selectionRanges.HasSavedRanges());
selectionRanges.RestoreFromSavedRanges();
}
// Restoring `Selection` may have destroyed us.
return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK;
nsresult rv = selectionRanges.ApplyTo(SelectionRef());
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "AutoRangeArray::ApplyTo() failed");
return rv;
}
Result<bool, nsresult> HTMLEditor::ElementIsGoodContainerForTheStyle(