diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index fb2f2f4c2d01..f4d84cb28e53 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -94,6 +94,7 @@ #include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::dir #include "nsIClipboard.h" // for nsIClipboard #include "nsIContent.h" // for nsIContent +#include "nsIDocumentEncoder.h" // for nsIDocumentEncoder #include "nsIDocumentStateListener.h" // for nsIDocumentStateListener #include "nsIEditActionListener.h" // for nsIEditActionListener #include "nsIEditorObserver.h" // for nsIEditorObserver @@ -214,6 +215,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EditorBase) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners) NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaceholderTransaction) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedDocumentEncoder) NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -239,6 +241,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EditorBase) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaceholderTransaction) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedDocumentEncoder) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EditorBase) @@ -1408,6 +1411,77 @@ NS_IMETHODIMP EditorBase::SetDocumentCharacterSet( return NS_ERROR_NOT_AVAILABLE; } +already_AddRefed EditorBase::GetAndInitDocEncoder( + const nsAString& aFormatType, uint32_t aDocumentEncoderFlags, + const nsACString& aCharset) const { + MOZ_ASSERT(IsEditActionDataAvailable()); + + nsCOMPtr docEncoder; + if (!mCachedDocumentEncoder || + !mCachedDocumentEncoderType.Equals(aFormatType)) { + nsAutoCString formatType; + LossyAppendUTF16toASCII(aFormatType, formatType); + docEncoder = do_createDocumentEncoder(PromiseFlatCString(formatType).get()); + if (NS_WARN_IF(!docEncoder)) { + return nullptr; + } + mCachedDocumentEncoder = docEncoder; + mCachedDocumentEncoderType = aFormatType; + } else { + docEncoder = mCachedDocumentEncoder; + } + + RefPtr doc = GetDocument(); + NS_ASSERTION(doc, "Need a document"); + + nsresult rv = docEncoder->NativeInit( + doc, aFormatType, + aDocumentEncoderFlags | nsIDocumentEncoder::RequiresReinitAfterOutput); + if (NS_FAILED(rv)) { + NS_WARNING("nsIDocumentEncoder::NativeInit() failed"); + return nullptr; + } + + if (!aCharset.IsEmpty() && !aCharset.EqualsLiteral("null")) { + DebugOnly rvIgnored = docEncoder->SetCharset(aCharset); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rvIgnored), + "nsIDocumentEncoder::SetCharset() failed, but ignored"); + } + + const int32_t wrapWidth = std::max(WrapWidth(), 0); + DebugOnly rvIgnored = docEncoder->SetWrapColumn(wrapWidth); + NS_WARNING_ASSERTION( + NS_SUCCEEDED(rvIgnored), + "nsIDocumentEncoder::SetWrapColumn() failed, but ignored"); + + // Set the selection, if appropriate. + // We do this either if the OutputSelectionOnly flag is set, + // in which case we use our existing selection ... + if (aDocumentEncoderFlags & nsIDocumentEncoder::OutputSelectionOnly) { + if (NS_FAILED(docEncoder->SetSelection(&SelectionRef()))) { + NS_WARNING("nsIDocumentEncoder::SetSelection() failed"); + return nullptr; + } + } + // ... or if the root element is not a body, + // in which case we set the selection to encompass the root. + else { + Element* rootElement = GetRoot(); + if (NS_WARN_IF(!rootElement)) { + return nullptr; + } + if (!rootElement->IsHTMLElement(nsGkAtoms::body)) { + if (NS_FAILED(docEncoder->SetContainerNode(rootElement))) { + NS_WARNING("nsIDocumentEncoder::SetContainerNode() failed"); + return nullptr; + } + } + } + + return docEncoder.forget(); +} + bool EditorBase::AreClipboardCommandsUnconditionallyEnabled() const { Document* document = GetDocument(); return document && document->AreClipboardCommandsUnconditionallyEnabled(); diff --git a/editor/libeditor/EditorBase.h b/editor/libeditor/EditorBase.h index 63fd260ca0ed..4afe22db01e5 100644 --- a/editor/libeditor/EditorBase.h +++ b/editor/libeditor/EditorBase.h @@ -44,6 +44,7 @@ class mozInlineSpellChecker; class nsAtom; class nsCaret; class nsIContent; +class nsIDocumentEncoder; class nsIDocumentStateListener; class nsIEditActionListener; class nsIEditorObserver; @@ -2240,6 +2241,19 @@ class EditorBase : public nsIEditor, */ nsresult GetDocumentCharsetInternal(nsACString& aCharset) const; + /** + * GetAndInitDocEncoder() returns a document encoder instance for aFormatType + * after initializing it. The result may be cached for saving recreation + * cost. + * + * @param aFormatType MIME type like "text/plain". + * @param aDocumentEncoderFlags Flags of nsIDocumentEncoder. + * @param aCharset Encoding of the document. + */ + already_AddRefed GetAndInitDocEncoder( + const nsAString& aFormatType, uint32_t aDocumentEncoderFlags, + const nsACString& aCharset) const; + /** * SelectAllInternal() should be used instead of SelectAll() in editor * because SelectAll() creates AutoEditActionSetter but we should avoid @@ -2726,6 +2740,12 @@ class EditorBase : public nsIEditor, RefPtr mIMEContentObserver; + // These members cache last encoder and its type for the performance in + // TextEditor::ComputeTextValue() which is the implementation of + // `.value` and `