Bug 1574852 - part 105: Get rid of `TextEditRules::WillSetProperty()` and `TextEditRules::WillRemoveProperty()` r=m_kato

Oddly, they are used only by `HTMLEditor`, but implemented by `TextEditRules`.
They cancels when the editor is in plaintext mode.  So, actual things are
implemented by each caller.  This patch cleans them up too.

Differential Revision: https://phabricator.services.mozilla.com/D45299

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Masayuki Nakano 2019-09-12 09:13:00 +00:00
Родитель f521a40385
Коммит 72fa9d1512
6 изменённых файлов: 359 добавлений и 322 удалений

Просмотреть файл

@ -809,9 +809,11 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
case EditSubAction::eUndo:
case EditSubAction::eRedo:
case EditSubAction::eRemoveList:
case EditSubAction::eRemoveTextProperty:
case EditSubAction::eSetOrClearAlignment:
case EditSubAction::eSetPositionToAbsolute:
case EditSubAction::eSetPositionToStatic:
case EditSubAction::eSetTextProperty:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:
@ -847,9 +849,11 @@ nsresult HTMLEditRules::DidDoAction(EditSubActionInfo& aInfo,
case EditSubAction::eUndo:
case EditSubAction::eRedo:
case EditSubAction::eRemoveList:
case EditSubAction::eRemoveTextProperty:
case EditSubAction::eSetOrClearAlignment:
case EditSubAction::eSetPositionToAbsolute:
case EditSubAction::eSetPositionToStatic:
case EditSubAction::eSetTextProperty:
MOZ_ASSERT_UNREACHABLE("This path should've been dead code");
return NS_ERROR_UNEXPECTED;
default:

Просмотреть файл

@ -4150,142 +4150,161 @@ nsresult HTMLEditor::SetCSSBackgroundColorWithTransaction(
CommitComposition();
// Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules);
// XXX Shouldn't we do this before calling `CommitComposition()`?
if (IsPlaintextEditor()) {
return NS_OK;
}
bool isCollapsed = SelectionRefPtr()->IsCollapsed();
EditActionResult result = CanHandleHTMLEditSubAction();
if (NS_WARN_IF(result.Failed()) || result.Canceled()) {
return result.Rv();
}
bool selectionIsCollapsed = SelectionRefPtr()->IsCollapsed();
AutoPlaceholderBatch treatAsOneTransaction(*this);
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eInsertElement, nsIEditor::eNext);
AutoSelectionRestorer restoreSelectionLater(*this);
AutoTransactionsConserveSelection dontChangeMySelection(*this);
// XXX Although, this method may set background color of ancestor block
// element, using EditSubAction::eSetTextProperty.
bool cancel, handled;
EditSubActionInfo subActionInfo(EditSubAction::eSetTextProperty);
nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!cancel && !handled) {
{
AutoSelectionRestorer restoreSelectionLater(*this);
AutoTransactionsConserveSelection dontChangeMySelection(*this);
// Loop through the ranges in the selection
// XXX This is different from `SetInlinePropertyInternal()`. It uses
// AutoRangeArray to store all ranges first. The result may be
// different if mutation event listener changes the `Selection`.
for (uint32_t i = 0; i < SelectionRefPtr()->RangeCount(); i++) {
RefPtr<nsRange> range = SelectionRefPtr()->GetRangeAt(i);
if (NS_WARN_IF(!range)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<Element> cachedBlockParent;
EditorDOMPoint startOfRange(range->StartRef());
EditorDOMPoint endOfRange(range->EndRef());
if (NS_WARN_IF(!startOfRange.IsSet()) ||
NS_WARN_IF(!endOfRange.IsSet())) {
continue;
}
// Check for easy case: both range endpoints in same text node
nsCOMPtr<nsINode> startNode = range->GetStartContainer();
int32_t startOffset = range->StartOffset();
nsCOMPtr<nsINode> endNode = range->GetEndContainer();
int32_t endOffset = range->EndOffset();
if (startNode == endNode && IsTextNode(startNode)) {
// Let's find the block container of the text node
nsCOMPtr<Element> blockParent = GetBlockNodeParent(startNode);
// And apply the background color to that block container
if (blockParent && cachedBlockParent != blockParent) {
cachedBlockParent = blockParent;
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
blockParent, nullptr, nsGkAtoms::bgcolor, &aColor, false);
}
} else if (startNode == endNode &&
startNode->IsHTMLElement(nsGkAtoms::body) && isCollapsed) {
// No block in the document, let's apply the background to the body
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
MOZ_KnownLive(startNode->AsElement()), nullptr, nsGkAtoms::bgcolor,
&aColor, false);
} else if (startNode == endNode && (endOffset - startOffset == 1 ||
(!startOffset && !endOffset))) {
// A unique node is selected, let's also apply the background color to
// the containing block, possibly the node itself
nsCOMPtr<nsIContent> selectedNode = range->GetChildAtStartOffset();
nsCOMPtr<Element> blockParent = GetBlock(*selectedNode);
if (blockParent && cachedBlockParent != blockParent) {
cachedBlockParent = blockParent;
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
blockParent, nullptr, nsGkAtoms::bgcolor, &aColor, false);
}
} else {
// Not the easy case. Range not contained in single text node. There
// are up to three phases here. There are all the nodes reported by
// the subtree iterator to be processed. And there are potentially a
// starting textnode and an ending textnode which are only partially
// contained by the range.
// Let's handle the nodes reported by the iterator. These nodes are
// entirely contained in the selection range. We build up a list of
// them (since doing operations on the document during iteration would
// perturb the iterator).
nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
nsCOMPtr<nsINode> node;
// Iterate range and build up array
ContentSubtreeIterator subtreeIter;
rv = subtreeIter.Init(range);
// Init returns an error if no nodes in range. This can easily happen
// with the subtree iterator if the selection doesn't contain any
// *whole* nodes.
if (NS_SUCCEEDED(rv)) {
for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
node = subtreeIter.GetCurrentNode();
NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
if (IsEditable(node)) {
arrayOfNodes.AppendElement(*node);
if (startOfRange.GetContainer() == endOfRange.GetContainer()) {
// If the range is in a text node, set background color of its parent
// block.
if (startOfRange.IsInTextNode()) {
if (RefPtr<Element> blockParent =
GetBlockNodeParent(startOfRange.GetContainer())) {
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
blockParent, nullptr, nsGkAtoms::bgcolor, &aColor, false);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
}
}
// First check the start parent of the range to see if it needs to be
// separately handled (it does if it's a text node, due to how the
// subtree iterator works - it will not have reported it).
if (IsTextNode(startNode) && IsEditable(startNode)) {
nsCOMPtr<Element> blockParent = GetBlockNodeParent(startNode);
if (blockParent && cachedBlockParent != blockParent) {
cachedBlockParent = blockParent;
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
blockParent, nullptr, nsGkAtoms::bgcolor, &aColor, false);
}
continue;
}
// Then loop through the list, set the property on each node
for (auto& node : arrayOfNodes) {
nsCOMPtr<Element> blockParent = GetBlock(node);
if (blockParent && cachedBlockParent != blockParent) {
cachedBlockParent = blockParent;
// If `Selection` is collapsed in a `<body>` element, set background
// color of the `<body>` element.
// XXX Why do we refer whether the `Selection` is collapsed rather
// than the `nsRange` is collapsed?
if (startOfRange.GetContainer()->IsHTMLElement(nsGkAtoms::body) &&
selectionIsCollapsed) {
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
MOZ_KnownLive(startOfRange.GetContainerAsElement()), nullptr,
nsGkAtoms::bgcolor, &aColor, false);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
continue;
}
// If one node is selected, set background color of it if it's a
// block, or of its parent block otherwise.
if ((startOfRange.IsStartOfContainer() &&
endOfRange.IsStartOfContainer()) ||
startOfRange.Offset() + 1 == endOfRange.Offset()) {
if (NS_WARN_IF(startOfRange.IsInDataNode())) {
continue;
}
if (RefPtr<Element> blockParent =
GetBlock(*startOfRange.GetChild())) {
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
blockParent, nullptr, nsGkAtoms::bgcolor, &aColor, false);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
}
continue;
}
}
// 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() && IsEditable(node)) {
arrayOfContents.AppendElement(*node->AsContent());
}
}
arrayOfNodes.Clear();
}
// Last, check the end parent of the range to see if it needs to be
// separately handled (it does if it's a text node, due to how the
// subtree iterator works - it will not have reported it).
if (IsTextNode(endNode) && IsEditable(endNode)) {
nsCOMPtr<Element> blockParent = GetBlockNodeParent(endNode);
if (blockParent && cachedBlockParent != blockParent) {
cachedBlockParent = blockParent;
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
blockParent, nullptr, nsGkAtoms::bgcolor, &aColor, false);
// This caches block parent if we set its background color.
RefPtr<Element> handledBlockParent;
// If start node is a text node, set background color of its parent
// block.
if (startOfRange.IsInTextNode() &&
IsEditable(startOfRange.GetContainer())) {
RefPtr<Element> blockParent =
GetBlockNodeParent(startOfRange.GetContainer());
if (blockParent && handledBlockParent != blockParent) {
handledBlockParent = blockParent;
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
blockParent, nullptr, nsGkAtoms::bgcolor, &aColor, false);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
}
}
// Then, set background color of each block or block parent of all nodes
// in the range entirely.
for (auto& content : arrayOfContents) {
RefPtr<Element> blockParent = GetBlock(content);
if (blockParent && handledBlockParent != blockParent) {
handledBlockParent = blockParent;
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
blockParent, nullptr, nsGkAtoms::bgcolor, &aColor, false);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
}
}
// Finally, if end node is a text node, set background color of its
// parent block.
if (endOfRange.IsInTextNode() && IsEditable(endOfRange.GetContainer())) {
RefPtr<Element> blockParent =
GetBlockNodeParent(endOfRange.GetContainer());
if (blockParent && handledBlockParent != blockParent) {
mCSSEditUtils->SetCSSEquivalentToHTMLStyle(
blockParent, nullptr, nsGkAtoms::bgcolor, &aColor, false);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
}
}
}
}
if (!cancel) {
// Post-process
rv = rules->DidDoAction(subActionInfo, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
// Restoring `Selection` may cause destroying us.
return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK;
}
NS_IMETHODIMP
@ -4307,8 +4326,10 @@ nsresult HTMLEditor::SetBackgroundColorAsAction(const nsAString& aColor,
// if we are in CSS mode, we have to apply the background color to the
// containing block (or the body if we have no block-level element in
// the document)
return EditorBase::ToGenericNSResult(
SetCSSBackgroundColorWithTransaction(aColor));
nsresult rv = SetCSSBackgroundColorWithTransaction(aColor);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"SetCSSBackgroundColorWithTransaction() failed");
return EditorBase::ToGenericNSResult(rv);
}
// but in HTML mode, we can only set the document's background color

Просмотреть файл

@ -3243,11 +3243,33 @@ class HTMLEditor final : public TextEditor,
MOZ_CAN_RUN_SCRIPT
nsresult LoadHTML(const nsAString& aInputString);
MOZ_CAN_RUN_SCRIPT
nsresult SetInlinePropertyInternal(nsAtom& aProperty, nsAtom* aAttribute,
const nsAString& aValue);
MOZ_CAN_RUN_SCRIPT
nsresult RemoveInlinePropertyInternal(nsAtom* aProperty, nsAtom* aAttribute);
/**
* SetInlinePropertyInternal() 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
* 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.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult SetInlinePropertyInternal(
nsAtom& aProperty, nsAtom* aAttribute, const nsAString& aValue);
/**
* RemoveInlinePropertyInternal() removes specified style from `mTypeInState`
* if `Selection` is collapsed. Otherwise, removing the style.
* XXX Looks like that this has a lot of bugs in HTML mode.
*
* @param aProperty nullptr if you want to remove all inline styles.
* Otherwise, one of the presentation tag names
* which we support in style editor.
* @param aAttribute For some aProperty values, need to be set to
* its attribute name. Otherwise, nullptr.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
RemoveInlinePropertyInternal(nsAtom* aProperty, nsAtom* aAttribute);
/**
* ReplaceHeadContentsWithSourceWithTransaction() replaces all children of
@ -3272,10 +3294,10 @@ class HTMLEditor final : public TextEditor,
* This sets background on the appropriate container element (table, cell,)
* or calls into nsTextEditor to set the page background.
*/
MOZ_CAN_RUN_SCRIPT
nsresult SetCSSBackgroundColorWithTransaction(const nsAString& aColor);
MOZ_CAN_RUN_SCRIPT
nsresult SetHTMLBackgroundColorWithTransaction(const nsAString& aColor);
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
SetCSSBackgroundColorWithTransaction(const nsAString& aColor);
MOZ_CAN_RUN_SCRIPT nsresult
SetHTMLBackgroundColorWithTransaction(const nsAString& aColor);
virtual void InitializeSelectionAncestorLimit(
nsIContent& aAncestorLimit) override;

Просмотреть файл

@ -100,10 +100,8 @@ nsresult HTMLEditor::SetInlinePropertyAsAction(nsAtom& aProperty,
}
}
nsresult rv = SetInlinePropertyInternal(aProperty, aAttribute, aValue);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditorBase::ToGenericNSResult(rv);
}
return NS_OK;
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetInlinePropertyInternal() failed");
return EditorBase::ToGenericNSResult(rv);
}
NS_IMETHODIMP
@ -135,138 +133,145 @@ HTMLEditor::SetInlineProperty(const nsAString& aProperty,
break;
}
nsresult rv = SetInlinePropertyInternal(*property, attribute, aValue);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditorBase::ToGenericNSResult(rv);
}
return NS_OK;
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SetInlinePropertyInternal() failed");
return EditorBase::ToGenericNSResult(rv);
}
nsresult HTMLEditor::SetInlinePropertyInternal(nsAtom& aProperty,
nsAtom* aAttribute,
const nsAString& aValue) {
nsresult HTMLEditor::SetInlinePropertyInternal(
nsAtom& aProperty, nsAtom* aAttribute, const nsAString& aAttributeValue) {
MOZ_ASSERT(IsEditActionDataAvailable());
if (NS_WARN_IF(!mRules)) {
return NS_ERROR_NOT_INITIALIZED;
}
RefPtr<TextEditRules> rules(mRules);
CommitComposition();
if (SelectionRefPtr()->IsCollapsed()) {
// Manipulating text attributes on a collapsed selection only sets state
// for the next text insertion
mTypeInState->SetProp(&aProperty, aAttribute, aValue);
mTypeInState->SetProp(&aProperty, aAttribute, aAttributeValue);
return NS_OK;
}
// XXX Shouldn't we return before calling `CommitComposition()`?
if (IsPlaintextEditor()) {
return NS_OK;
}
EditActionResult result = CanHandleHTMLEditSubAction();
if (NS_WARN_IF(result.Failed()) || result.Canceled()) {
return result.Rv();
}
AutoPlaceholderBatch treatAsOneTransaction(*this);
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eInsertElement, nsIEditor::eNext);
AutoSelectionRestorer restoreSelectionLater(*this);
AutoTransactionsConserveSelection dontChangeMySelection(*this);
bool cancel, handled;
EditSubActionInfo subActionInfo(EditSubAction::eSetTextProperty);
// Protect the edit rules object from dying
nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!cancel && !handled) {
{
AutoSelectionRestorer restoreSelectionLater(*this);
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`.
AutoRangeArray arrayOfRanges(SelectionRefPtr());
for (auto& range : arrayOfRanges.mRanges) {
// Adjust range to include any ancestors whose children are entirely
// selected
rv = PromoteInlineRange(*range);
nsresult rv = PromoteInlineRange(*range);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Check for easy case: both range endpoints in same text node
nsCOMPtr<nsINode> startNode = range->GetStartContainer();
nsCOMPtr<nsINode> endNode = range->GetEndContainer();
if (startNode && startNode == endNode && startNode->GetAsText()) {
rv = SetInlinePropertyOnTextNode(
MOZ_KnownLive(*startNode->GetAsText()), range->StartOffset(),
range->EndOffset(), aProperty, aAttribute, aValue);
// 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())) {
continue;
}
// If range is in a text node, apply new style simply.
if (startOfRange.GetContainer() == endOfRange.GetContainer() &&
startOfRange.IsInTextNode()) {
nsresult rv = SetInlinePropertyOnTextNode(
MOZ_KnownLive(*startOfRange.GetContainerAsText()),
startOfRange.Offset(), endOfRange.Offset(), aProperty, aAttribute,
aAttributeValue);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
continue;
}
// Not the easy case. Range not contained in single text node. There
// are up to three phases here. There are all the nodes reported by the
// subtree iterator to be processed. And there are potentially a
// starting textnode and an ending textnode which are only partially
// contained by the range.
// Let's handle the nodes reported by the iterator. These nodes are
// entirely contained in the selection range. We build up a list of them
// (since doing operations on the document during iteration would perturb
// the iterator).
nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
// Iterate range and build up array
// Collect editable nodes which are entirely contained in the range.
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
ContentSubtreeIterator subtreeIter;
rv = subtreeIter.Init(range);
// Init returns an error if there are no nodes in range. This can easily
// happen with the subtree iterator if the selection doesn't contain any
// *whole* nodes.
if (NS_SUCCEEDED(rv)) {
// 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()) {
OwningNonNull<nsINode> node = *subtreeIter.GetCurrentNode();
nsINode* node = subtreeIter.GetCurrentNode();
if (NS_WARN_IF(!node)) {
return NS_ERROR_FAILURE;
}
if (node->IsContent() && IsEditable(node)) {
arrayOfNodes.AppendElement(*node->AsContent());
arrayOfContents.AppendElement(*node->AsContent());
}
}
}
// First check the start parent of the range to see if it needs to be
// separately handled (it does if it's a text node, due to how the
// subtree iterator works - it will not have reported it).
if (startNode && startNode->GetAsText() && IsEditable(startNode)) {
rv = SetInlinePropertyOnTextNode(
MOZ_KnownLive(*startNode->GetAsText()), range->StartOffset(),
startNode->Length(), aProperty, aAttribute, aValue);
// If start node is a text node, apply new style to a part of it.
if (startOfRange.IsInTextNode() &&
IsEditable(startOfRange.GetContainer())) {
nsresult rv = SetInlinePropertyOnTextNode(
MOZ_KnownLive(*startOfRange.GetContainerAsText()),
startOfRange.Offset(), startOfRange.GetContainer()->Length(),
aProperty, aAttribute, aAttributeValue);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
// Then loop through the list, set the property on each node
for (auto& node : arrayOfNodes) {
rv = SetInlinePropertyOnNode(*node, aProperty, aAttribute, aValue);
// Then, apply new style to all nodes in the range entirely.
for (auto& content : arrayOfContents) {
nsresult rv = SetInlinePropertyOnNode(*content, aProperty, aAttribute,
aAttributeValue);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
// Last check the end parent of the range to see if it needs to be
// separately handled (it does if it's a text node, due to how the
// subtree iterator works - it will not have reported it).
if (endNode && endNode->GetAsText() && IsEditable(endNode)) {
rv = SetInlinePropertyOnTextNode(MOZ_KnownLive(*endNode->GetAsText()),
0, range->EndOffset(), aProperty,
aAttribute, aValue);
// Finally, if end node is a text node, apply new style to a part ot it.
if (endOfRange.IsInTextNode() && IsEditable(endOfRange.GetContainer())) {
nsresult rv = SetInlinePropertyOnTextNode(
MOZ_KnownLive(*endOfRange.GetContainerAsText()), 0,
endOfRange.Offset(), aProperty, aAttribute, aAttributeValue);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
}
}
if (cancel) {
return NS_OK;
}
rv = rules->DidDoAction(subActionInfo, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
// Restoring `Selection` may have destroyed us.
return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK;
}
// Helper function for SetInlinePropertyOn*: is aNode a simple old <b>, <font>,
@ -1259,10 +1264,9 @@ nsresult HTMLEditor::RemoveAllInlinePropertiesAsAction(
*this, EditSubAction::eRemoveAllTextProperties, nsIEditor::eNext);
nsresult rv = RemoveInlinePropertyInternal(nullptr, nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditorBase::ToGenericNSResult(rv);
}
return NS_OK;
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"RemoveInlinePropertyInternal() failed");
return EditorBase::ToGenericNSResult(rv);
}
nsresult HTMLEditor::RemoveInlinePropertyAsAction(nsAtom& aProperty,
@ -1288,10 +1292,9 @@ nsresult HTMLEditor::RemoveInlinePropertyAsAction(nsAtom& aProperty,
break;
}
nsresult rv = RemoveInlinePropertyInternal(&aProperty, aAttribute);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditorBase::ToGenericNSResult(rv);
}
return NS_OK;
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"RemoveInlinePropertyInternal() failed");
return EditorBase::ToGenericNSResult(rv);
}
NS_IMETHODIMP
@ -1319,10 +1322,9 @@ HTMLEditor::RemoveInlineProperty(const nsAString& aProperty,
break;
}
nsresult rv = RemoveInlinePropertyInternal(property, attribute);
if (NS_WARN_IF(NS_FAILED(rv))) {
return EditorBase::ToGenericNSResult(rv);
}
return NS_OK;
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"RemoveInlinePropertyInternal() failed");
return EditorBase::ToGenericNSResult(rv);
}
nsresult HTMLEditor::RemoveInlinePropertyInternal(nsAtom* aProperty,
@ -1353,21 +1355,24 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(nsAtom* aProperty,
return NS_OK;
}
// XXX Shouldn't we quit before calling `CommitComposition()`?
if (IsPlaintextEditor()) {
return NS_OK;
}
EditActionResult result = CanHandleHTMLEditSubAction();
if (NS_WARN_IF(result.Failed()) || result.Canceled()) {
return result.Rv();
}
AutoPlaceholderBatch treatAsOneTransaction(*this);
AutoEditSubActionNotifier startToHandleEditSubAction(
*this, EditSubAction::eRemoveTextProperty, nsIEditor::eNext);
AutoSelectionRestorer restoreSelectionLater(*this);
AutoTransactionsConserveSelection dontChangeMySelection(*this);
bool cancel, handled;
EditSubActionInfo subActionInfo(EditSubAction::eRemoveTextProperty);
// Protect the edit rules object from dying
RefPtr<TextEditRules> rules(mRules);
nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!cancel && !handled) {
{
AutoSelectionRestorer restoreSelectionLater(*this);
AutoTransactionsConserveSelection dontChangeMySelection(*this);
// Loop through the ranges in the selection
// Since ranges might be modified by SplitStyleAboveRange, we need hold
// current ranges
@ -1376,14 +1381,14 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(nsAtom* aProperty,
if (aProperty == nsGkAtoms::name) {
// Promote range if it starts or end in a named anchor and we want to
// remove named anchors
rv = PromoteRangeIfStartsOrEndsInNamedAnchor(*range);
nsresult rv = PromoteRangeIfStartsOrEndsInNamedAnchor(*range);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
// Adjust range to include any ancestors whose children are entirely
// selected
rv = PromoteInlineRange(*range);
nsresult rv = PromoteInlineRange(*range);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1391,90 +1396,109 @@ nsresult HTMLEditor::RemoveInlinePropertyInternal(nsAtom* aProperty,
// Remove this style from ancestors of our range endpoints, splitting
// them as appropriate
rv = SplitStyleAboveRange(range, aProperty, aAttribute);
// FYI: `range` may be modified to a range starting from first right
// node child at splitting at range start to first right node
// child at splitting at range end.
nsresult rv = SplitStyleAboveRange(range, aProperty, aAttribute);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Check for easy case: both range endpoints in same text node
nsCOMPtr<nsINode> startNode = range->GetStartContainer();
nsCOMPtr<nsINode> endNode = range->GetEndContainer();
if (startNode && startNode == endNode && startNode->GetAsText()) {
// We're done with this range!
if (IsCSSEnabled() && CSSEditUtils::IsCSSEditableProperty(
startNode, aProperty, aAttribute)) {
// The HTML style defined by aProperty/aAttribute has a CSS
// equivalence in this implementation for startNode
if (CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
startNode, aProperty, aAttribute, EmptyString(),
CSSEditUtils::eComputed)) {
// startNode's computed style indicates the CSS equivalence to the
// HTML style to remove is applied; but we found no element in the
// ancestors of startNode carrying specified styles; assume it
// comes from a rule and try to insert a span "inverting" the style
if (CSSEditUtils::IsCSSInvertible(*aProperty, aAttribute)) {
NS_NAMED_LITERAL_STRING(value, "-moz-editor-invert-value");
SetInlinePropertyOnTextNode(
MOZ_KnownLive(*startNode->GetAsText()), range->StartOffset(),
range->EndOffset(), *aProperty, aAttribute, value);
}
}
EditorDOMPoint startOfRange(range->StartRef());
EditorDOMPoint endOfRange(range->EndRef());
if (NS_WARN_IF(!startOfRange.IsSet()) ||
NS_WARN_IF(!endOfRange.IsSet())) {
continue;
}
if (startOfRange.GetContainer() == endOfRange.GetContainer() &&
startOfRange.IsInTextNode()) {
// TODO: If parent block has the removing style, we should create
// `<span>` element to remove the style even in HTML mode
// since Chrome does it. See bug 1566795.
if (!IsCSSEnabled()) {
continue;
}
} else {
// Not the easy case. Range not contained in single text node.
if (!CSSEditUtils::IsCSSEditableProperty(startOfRange.GetContainer(),
aProperty, aAttribute)) {
continue;
}
// The HTML style defined by aProperty/aAttribute has a CSS
// equivalence in this implementation for startOfRange.
if (!CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
startOfRange.GetContainer(), aProperty, aAttribute,
EmptyString(), CSSEditUtils::eComputed)) {
continue;
}
// startOfRange's computed style indicates the CSS equivalence to the
// HTML style to remove is applied; but we found no element in the
// ancestors of startOfRange carrying specified styles; assume it
// comes from a rule and try to insert a span "inverting" the style
if (!CSSEditUtils::IsCSSInvertible(*aProperty, aAttribute)) {
continue;
}
NS_NAMED_LITERAL_STRING(value, "-moz-editor-invert-value");
SetInlinePropertyOnTextNode(
MOZ_KnownLive(*startOfRange.GetContainerAsText()),
startOfRange.Offset(), endOfRange.Offset(), *aProperty, aAttribute,
value);
if (NS_WARN_IF(Destroyed())) {
return NS_ERROR_EDITOR_DESTROYED;
}
continue;
}
nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
// Iterate range and build up array
ContentSubtreeIterator subtreeIter;
DebugOnly<nsresult> rvIgnored = subtreeIter.Init(range);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
"Failed to initialize subtree iterator");
// Collect editable nodes which are entirely contained in the range.
AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
ContentSubtreeIterator subtreeIter;
if (NS_SUCCEEDED(subtreeIter.Init(range))) {
for (; !subtreeIter.IsDone(); subtreeIter.Next()) {
nsCOMPtr<nsINode> node = subtreeIter.GetCurrentNode();
if (NS_WARN_IF(!node)) {
return NS_ERROR_FAILURE;
}
if (IsEditable(node) && node->IsContent()) {
arrayOfNodes.AppendElement(*node->AsContent());
if (node->IsContent() && IsEditable(node)) {
arrayOfContents.AppendElement(*node->AsContent());
}
}
}
// Loop through the list, remove the property on each node
for (auto& node : arrayOfNodes) {
rv = RemoveStyleInside(node, aProperty, aAttribute);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (IsCSSEnabled() &&
CSSEditUtils::IsCSSEditableProperty(node, aProperty,
aAttribute) &&
CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
node, aProperty, aAttribute, EmptyString(),
CSSEditUtils::eComputed) &&
// startNode's computed style indicates the CSS equivalence to
// the HTML style to remove is applied; but we found no element
// in the ancestors of startNode carrying specified styles;
// assume it comes from a rule and let's try to insert a span
// "inverting" the style
CSSEditUtils::IsCSSInvertible(*aProperty, aAttribute)) {
NS_NAMED_LITERAL_STRING(value, "-moz-editor-invert-value");
SetInlinePropertyOnNode(node, *aProperty, aAttribute, value);
}
for (auto& content : arrayOfContents) {
nsresult rv = RemoveStyleInside(content, aProperty, aAttribute);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// TODO: If parent block has the removing style, we should create
// `<span>` element to remove the style even in HTML mode
// since Chrome does it. See bug 1566795.
if (!IsCSSEnabled()) {
continue;
}
if (!CSSEditUtils::IsCSSEditableProperty(content, aProperty,
aAttribute)) {
continue;
}
if (!CSSEditUtils::IsCSSEquivalentToHTMLInlineStyleSet(
content, aProperty, aAttribute, EmptyString(),
CSSEditUtils::eComputed)) {
continue;
}
// startOfRange's computed style indicates the CSS equivalence to
// the HTML style to remove is applied; but we found no element
// in the ancestors of startOfRange carrying specified styles;
// assume it comes from a rule and let's try to insert a span
// "inverting" the style
if (!CSSEditUtils::IsCSSInvertible(*aProperty, aAttribute)) {
continue;
}
NS_NAMED_LITERAL_STRING(value, "-moz-editor-invert-value");
SetInlinePropertyOnNode(content, *aProperty, aAttribute, value);
}
}
}
if (cancel) {
return NS_OK;
}
rv = rules->DidDoAction(subActionInfo, rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
// Restoring `Selection` may cause destroying us.
return NS_WARN_IF(Destroyed()) ? NS_ERROR_EDITOR_DESTROYED : NS_OK;
}
NS_IMETHODIMP

Просмотреть файл

@ -207,10 +207,6 @@ nsresult TextEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
return WillSetText(aCancel, aHandled, aInfo.inString, aInfo.maxLength);
case EditSubAction::eDeleteSelectedContent:
return WillDeleteSelection(aInfo.collapsedAction, aCancel, aHandled);
case EditSubAction::eSetTextProperty:
return WillSetTextProperty(aCancel, aHandled);
case EditSubAction::eRemoveTextProperty:
return WillRemoveTextProperty(aCancel, aHandled);
case EditSubAction::eComputeTextToOutput:
return WillOutputText(aInfo.outputFormat, aInfo.outString, aInfo.flags,
aCancel, aHandled);
@ -858,32 +854,6 @@ nsresult TextEditRules::WillSetText(bool* aCancel, bool* aHandled,
return NS_OK;
}
nsresult TextEditRules::WillSetTextProperty(bool* aCancel, bool* aHandled) {
if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
return NS_ERROR_INVALID_ARG;
}
// XXX: should probably return a success value other than NS_OK that means
// "not allowed"
if (IsPlaintextEditor()) {
*aCancel = true;
}
return NS_OK;
}
nsresult TextEditRules::WillRemoveTextProperty(bool* aCancel, bool* aHandled) {
if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
return NS_ERROR_INVALID_ARG;
}
// XXX: should probably return a success value other than NS_OK that means
// "not allowed"
if (IsPlaintextEditor()) {
*aCancel = true;
}
return NS_OK;
}
nsresult TextEditRules::WillDeleteSelection(
nsIEditor::EDirection aCollapsedAction, bool* aCancel, bool* aHandled) {
MOZ_ASSERT(IsEditorDataAvailable());

Просмотреть файл

@ -151,10 +151,6 @@ class TextEditRules {
MOZ_MUST_USE nsresult DeleteSelectionWithTransaction(
nsIEditor::EDirection aCollapsedAction, bool* aCancel, bool* aHandled);
nsresult WillSetTextProperty(bool* aCancel, bool* aHandled);
nsresult WillRemoveTextProperty(bool* aCancel, bool* aHandled);
/**
* Called prior to nsIEditor::OutputToString.
*