зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1574852 - part 108: Move `TextEditRules::WillOutputText()` to `TextEditor` r=m_kato
Differential Revision: https://phabricator.services.mozilla.com/D45491 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
af80a20e09
Коммит
75507785e6
|
@ -754,12 +754,6 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
|
|||
*aCancel = false;
|
||||
*aHandled = false;
|
||||
|
||||
// Deal with actions for which we don't need to check whether the selection is
|
||||
// editable.
|
||||
if (aInfo.mEditSubAction == EditSubAction::eComputeTextToOutput) {
|
||||
return TextEditRules::WillDoAction(aInfo, aCancel, aHandled);
|
||||
}
|
||||
|
||||
AutoSafeEditorData setData(*this, *mHTMLEditor);
|
||||
|
||||
EditActionResult result = HTMLEditorRef().CanHandleHTMLEditSubAction();
|
||||
|
@ -786,6 +780,7 @@ nsresult HTMLEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
|
|||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WillInsert() failed");
|
||||
return NS_OK;
|
||||
}
|
||||
case EditSubAction::eComputeTextToOutput:
|
||||
case EditSubAction::eCreateOrChangeDefinitionListItem:
|
||||
case EditSubAction::eCreateOrChangeList:
|
||||
case EditSubAction::eCreateOrRemoveBlock:
|
||||
|
@ -825,6 +820,7 @@ nsresult HTMLEditRules::DidDoAction(EditSubActionInfo& aInfo,
|
|||
case EditSubAction::eInsertElement:
|
||||
case EditSubAction::eInsertQuotedText:
|
||||
return NS_OK;
|
||||
case EditSubAction::eComputeTextToOutput:
|
||||
case EditSubAction::eCreateOrChangeDefinitionListItem:
|
||||
case EditSubAction::eCreateOrChangeList:
|
||||
case EditSubAction::eCreateOrRemoveBlock:
|
||||
|
|
|
@ -205,9 +205,6 @@ nsresult TextEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
|
|||
case EditSubAction::eSetText:
|
||||
TextEditorRef().UndefineCaretBidiLevel();
|
||||
return WillSetText(aCancel, aHandled, aInfo.inString, aInfo.maxLength);
|
||||
case EditSubAction::eComputeTextToOutput:
|
||||
return WillOutputText(aInfo.outputFormat, aInfo.outString, aInfo.flags,
|
||||
aCancel, aHandled);
|
||||
case EditSubAction::eInsertQuotedText: {
|
||||
CANCEL_OPERATION_IF_READONLY_OR_DISABLED
|
||||
|
||||
|
@ -221,6 +218,7 @@ nsresult TextEditRules::WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
|
|||
"Failed to remove padding <br> element");
|
||||
return rv;
|
||||
}
|
||||
case EditSubAction::eComputeTextToOutput:
|
||||
case EditSubAction::eDeleteSelectedContent:
|
||||
case EditSubAction::eInsertElement:
|
||||
case EditSubAction::eInsertLineBreak:
|
||||
|
@ -242,6 +240,7 @@ nsresult TextEditRules::DidDoAction(EditSubActionInfo& aInfo,
|
|||
}
|
||||
|
||||
switch (aInfo.mEditSubAction) {
|
||||
case EditSubAction::eComputeTextToOutput:
|
||||
case EditSubAction::eDeleteSelectedContent:
|
||||
case EditSubAction::eInsertElement:
|
||||
case EditSubAction::eInsertLineBreak:
|
||||
|
@ -900,61 +899,35 @@ EditActionResult TextEditor::HandleDeleteSelectionInternal(
|
|||
return EditActionHandled(rv);
|
||||
}
|
||||
|
||||
nsresult TextEditRules::WillOutputText(const nsAString* aOutputFormat,
|
||||
nsAString* aOutString, uint32_t aFlags,
|
||||
bool* aCancel, bool* aHandled) {
|
||||
MOZ_ASSERT(IsEditorDataAvailable());
|
||||
|
||||
// null selection ok
|
||||
if (NS_WARN_IF(!aOutString) || NS_WARN_IF(!aOutputFormat) ||
|
||||
NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
// initialize out param
|
||||
*aCancel = false;
|
||||
*aHandled = false;
|
||||
|
||||
if (!aOutputFormat->LowerCaseEqualsLiteral("text/plain")) {
|
||||
return NS_OK;
|
||||
}
|
||||
EditActionResult TextEditor::ComputeValueFromTextNodeAndPaddingBRElement(
|
||||
nsAString& aValue) const {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
// If there is a padding <br> element, there's no content. So output empty
|
||||
// string.
|
||||
if (TextEditorRef().HasPaddingBRElementForEmptyEditor()) {
|
||||
aOutString->Truncate();
|
||||
*aHandled = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If it's necessary to check selection range or the editor wraps hard,
|
||||
// we need some complicated handling. In such case, we need to use the
|
||||
// expensive path.
|
||||
// XXX Anything else what we cannot return plain text simply?
|
||||
if (aFlags & nsIDocumentEncoder::OutputSelectionOnly ||
|
||||
aFlags & nsIDocumentEncoder::OutputWrap) {
|
||||
return NS_OK;
|
||||
if (HasPaddingBRElementForEmptyEditor()) {
|
||||
aValue.Truncate();
|
||||
return EditActionHandled();
|
||||
}
|
||||
|
||||
// If it's neither <input type="text"> nor <textarea>, e.g., an HTML editor
|
||||
// which is in plaintext mode (e.g., plaintext email composer on Thunderbird),
|
||||
// it should be handled by the expensive path.
|
||||
if (TextEditorRef().AsHTMLEditor()) {
|
||||
return NS_OK;
|
||||
if (AsHTMLEditor()) {
|
||||
return EditActionIgnored();
|
||||
}
|
||||
|
||||
Element* root = TextEditorRef().GetRoot();
|
||||
if (!root) { // Don't warn it, this is possible, e.g., 997805.html
|
||||
aOutString->Truncate();
|
||||
*aHandled = true;
|
||||
return NS_OK;
|
||||
Element* anonymousDivElement = GetRoot();
|
||||
if (!anonymousDivElement) {
|
||||
// Don't warn this case, this is possible, e.g., 997805.html
|
||||
aValue.Truncate();
|
||||
return EditActionHandled();
|
||||
}
|
||||
|
||||
nsIContent* firstChild = root->GetFirstChild();
|
||||
if (!firstChild) {
|
||||
aOutString->Truncate();
|
||||
*aHandled = true;
|
||||
return NS_OK;
|
||||
nsIContent* textNodeOrPaddingBRElement = anonymousDivElement->GetFirstChild();
|
||||
if (!textNodeOrPaddingBRElement) {
|
||||
aValue.Truncate();
|
||||
return EditActionHandled();
|
||||
}
|
||||
|
||||
// If it's an <input type="text"> element, the DOM tree should be:
|
||||
|
@ -970,9 +943,17 @@ nsresult TextEditRules::WillOutputText(const nsAString* aOutputFormat,
|
|||
// ...
|
||||
// </div>
|
||||
|
||||
Text* text = firstChild->GetAsText();
|
||||
Text* textNode = textNodeOrPaddingBRElement->GetAsText();
|
||||
if (!textNode) {
|
||||
// If there is no text node in the expected DOM tree, we can say that it's
|
||||
// just empty.
|
||||
aValue.Truncate();
|
||||
return EditActionHandled();
|
||||
}
|
||||
|
||||
nsIContent* firstChildExceptText =
|
||||
text ? firstChild->GetNextSibling() : firstChild;
|
||||
textNode ? textNodeOrPaddingBRElement->GetNextSibling()
|
||||
: textNodeOrPaddingBRElement;
|
||||
// If the DOM tree is unexpected, fall back to the expensive path.
|
||||
bool isInput = IsSingleLineEditor();
|
||||
bool isTextarea = !isInput;
|
||||
|
@ -982,22 +963,12 @@ nsresult TextEditRules::WillOutputText(const nsAString* aOutputFormat,
|
|||
!EditorBase::IsPaddingBRElementForEmptyLastLine(
|
||||
*firstChildExceptText) &&
|
||||
!firstChildExceptText->IsXULElement(nsGkAtoms::scrollbar))) {
|
||||
return NS_OK;
|
||||
return EditActionIgnored();
|
||||
}
|
||||
|
||||
// If there is no text node in the expected DOM tree, we can say that it's
|
||||
// just empty.
|
||||
if (!text) {
|
||||
aOutString->Truncate();
|
||||
*aHandled = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Otherwise, the text is the value.
|
||||
text->GetData(*aOutString);
|
||||
|
||||
*aHandled = true;
|
||||
return NS_OK;
|
||||
// Otherwise, the text data is the value.
|
||||
textNode->GetData(aValue);
|
||||
return EditActionHandled();
|
||||
}
|
||||
|
||||
nsresult TextEditRules::CreateTrailingBRIfNeeded() {
|
||||
|
|
|
@ -122,17 +122,6 @@ class TextEditRules {
|
|||
const nsAString* inString,
|
||||
int32_t aMaxLength);
|
||||
|
||||
/**
|
||||
* Called prior to nsIEditor::OutputToString.
|
||||
*
|
||||
* @param aInFormat The format requested for the output, a MIME type.
|
||||
* @param aOutText The string to use for output, if aCancel is set to true.
|
||||
* @param aOutCancel If set to true, the caller should cancel the operation
|
||||
* and use aOutText as the result.
|
||||
*/
|
||||
nsresult WillOutputText(const nsAString* aInFormat, nsAString* aOutText,
|
||||
uint32_t aFlags, bool* aOutCancel, bool* aHandled);
|
||||
|
||||
/**
|
||||
* Creates a trailing break in the text doc if there is not one already.
|
||||
*/
|
||||
|
|
|
@ -1851,12 +1851,10 @@ TextEditor::OutputToString(const nsAString& aFormatType,
|
|||
|
||||
nsresult rv =
|
||||
ComputeValueInternal(aFormatType, aDocumentEncoderFlags, aOutputString);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// This is low level API for XUL applcation. So, we should return raw
|
||||
// error code here.
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "ComputeValueInternal() failed");
|
||||
// This is low level API for XUL application. So, we should return raw
|
||||
// error code here.
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult TextEditor::ComputeValueInternal(const nsAString& aFormatType,
|
||||
|
@ -1864,27 +1862,26 @@ nsresult TextEditor::ComputeValueInternal(const nsAString& aFormatType,
|
|||
nsAString& aOutputString) const {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
// Protect the edit rules object from dying
|
||||
RefPtr<TextEditRules> rules(mRules);
|
||||
|
||||
EditSubActionInfo subActionInfo(EditSubAction::eComputeTextToOutput);
|
||||
subActionInfo.outString = &aOutputString;
|
||||
subActionInfo.flags = aDocumentEncoderFlags;
|
||||
subActionInfo.outputFormat = &aFormatType;
|
||||
|
||||
bool cancel{false};
|
||||
bool handled{false};
|
||||
nsresult rv = rules->WillDoAction(subActionInfo, &cancel, &handled);
|
||||
if (NS_FAILED(rv) || cancel) {
|
||||
return rv;
|
||||
}
|
||||
if (handled) {
|
||||
// This case will get triggered by password fields or single text node only.
|
||||
return rv;
|
||||
// First, let's try to get the value simply only from text node if the
|
||||
// caller wants plaintext value.
|
||||
if (aFormatType.LowerCaseEqualsLiteral("text/plain")) {
|
||||
// If it's necessary to check selection range or the editor wraps hard,
|
||||
// we need some complicated handling. In such case, we need to use the
|
||||
// expensive path.
|
||||
// XXX Anything else what we cannot return the text node data simply?
|
||||
if (!(aDocumentEncoderFlags & (nsIDocumentEncoder::OutputSelectionOnly |
|
||||
nsIDocumentEncoder::OutputWrap))) {
|
||||
EditActionResult result =
|
||||
ComputeValueFromTextNodeAndPaddingBRElement(aOutputString);
|
||||
if (NS_WARN_IF(result.Failed()) || result.Canceled() ||
|
||||
result.Handled()) {
|
||||
return result.Rv();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoCString charset;
|
||||
rv = GetDocumentCharsetInternal(charset);
|
||||
nsresult rv = GetDocumentCharsetInternal(charset);
|
||||
if (NS_FAILED(rv) || charset.IsEmpty()) {
|
||||
charset.AssignLiteral("windows-1252");
|
||||
}
|
||||
|
@ -1895,12 +1892,10 @@ nsresult TextEditor::ComputeValueInternal(const nsAString& aFormatType,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// XXX Why don't we call TextEditRules::DidDoAction() here?
|
||||
rv = encoder->EncodeToString(aOutputString);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"nsIDocumentEncoder::EncodeToString() failed");
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult TextEditor::PasteAsQuotationAsAction(int32_t aClipboardType,
|
||||
|
@ -2010,7 +2005,7 @@ nsresult TextEditor::InsertWithQuotationsAsSubAction(
|
|||
}
|
||||
|
||||
nsresult TextEditor::SharedOutputString(uint32_t aFlags, bool* aIsCollapsed,
|
||||
nsAString& aResult) {
|
||||
nsAString& aResult) const {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
*aIsCollapsed = SelectionRefPtr()->IsCollapsed();
|
||||
|
@ -2019,7 +2014,10 @@ nsresult TextEditor::SharedOutputString(uint32_t aFlags, bool* aIsCollapsed,
|
|||
aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
|
||||
}
|
||||
// If the selection isn't collapsed, we'll use the whole document.
|
||||
return ComputeValueInternal(NS_LITERAL_STRING("text/plain"), aFlags, aResult);
|
||||
nsresult rv =
|
||||
ComputeValueInternal(NS_LITERAL_STRING("text/plain"), aFlags, aResult);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "ComputeValueInternal() failed");
|
||||
return rv;
|
||||
}
|
||||
|
||||
void TextEditor::OnStartToHandleTopLevelEditSubAction(
|
||||
|
|
|
@ -650,6 +650,16 @@ class TextEditor : public EditorBase,
|
|||
HandleDeleteSelection(nsIEditor::EDirection aDirectionAndAmount,
|
||||
nsIEditor::EStripWrappers aStripWrappers);
|
||||
|
||||
/**
|
||||
* ComputeValueFromTextNodeAndPaddingBRElement() tries to compute "value" of
|
||||
* this editor content only with text node and padding `<br>` element.
|
||||
* If this succeeds to compute the value, it's returned with aValue and
|
||||
* the result is marked as "handled". Otherwise, the caller needs to
|
||||
* compute it with another way.
|
||||
*/
|
||||
EditActionResult ComputeValueFromTextNodeAndPaddingBRElement(
|
||||
nsAString& aValue) const;
|
||||
|
||||
protected: // Called by helper classes.
|
||||
virtual void OnStartToHandleTopLevelEditSubAction(
|
||||
EditSubAction aEditSubAction, nsIEditor::EDirection aDirection) override;
|
||||
|
@ -821,7 +831,7 @@ class TextEditor : public EditorBase,
|
|||
* string.
|
||||
*/
|
||||
nsresult SharedOutputString(uint32_t aFlags, bool* aIsCollapsed,
|
||||
nsAString& aResult);
|
||||
nsAString& aResult) const;
|
||||
|
||||
/**
|
||||
* See comment of IsCopyToClipboardAllowed() for the detail.
|
||||
|
|
Загрузка…
Ссылка в новой задаче