/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "EditorBase.h" #include // for nullptr, stdout #include // for strcmp #include "AutoRangeArray.h" // for AutoRangeArray #include "ChangeAttributeTransaction.h" #include "CompositionTransaction.h" #include "DeleteContentTransactionBase.h" #include "DeleteMultipleRangesTransaction.h" #include "DeleteNodeTransaction.h" #include "DeleteRangeTransaction.h" #include "DeleteTextTransaction.h" #include "EditAction.h" // for EditSubAction #include "EditorDOMPoint.h" // for EditorDOMPoint #include "EditorUtils.h" // for various helper classes. #include "EditTransactionBase.h" // for EditTransactionBase #include "EditorEventListener.h" // for EditorEventListener #include "HTMLEditor.h" // for HTMLEditor #include "HTMLEditorInlines.h" #include "HTMLEditUtils.h" // for HTMLEditUtils #include "InsertNodeTransaction.h" // for InsertNodeTransaction #include "InsertTextTransaction.h" // for InsertTextTransaction #include "JoinNodesTransaction.h" // for JoinNodesTransaction #include "PlaceholderTransaction.h" // for PlaceholderTransaction #include "SplitNodeTransaction.h" // for SplitNodeTransaction #include "TextEditor.h" // for TextEditor #include "ErrorList.h" #include "gfxFontUtils.h" // for gfxFontUtils #include "mozilla/Assertions.h" #include "mozilla/intl/BidiEmbeddingLevel.h" #include "mozilla/BasePrincipal.h" // for BasePrincipal #include "mozilla/CheckedInt.h" // for CheckedInt #include "mozilla/ComposerCommandsUpdater.h" // for ComposerCommandsUpdater #include "mozilla/ContentEvents.h" // for InternalClipboardEvent #include "mozilla/DebugOnly.h" // for DebugOnly #include "mozilla/EditorSpellCheck.h" // for EditorSpellCheck #include "mozilla/Encoding.h" // for Encoding (used in Document::GetDocumentCharacterSet) #include "mozilla/EventDispatcher.h" // for EventChainPreVisitor, etc. #include "mozilla/FlushType.h" // for FlushType::Frames #include "mozilla/IMEContentObserver.h" // for IMEContentObserver #include "mozilla/IMEStateManager.h" // for IMEStateManager #include "mozilla/InputEventOptions.h" // for InputEventOptions #include "mozilla/IntegerRange.h" // for IntegerRange #include "mozilla/InternalMutationEvent.h" // for NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED #include "mozilla/mozalloc.h" // for operator new, etc. #include "mozilla/mozInlineSpellChecker.h" // for mozInlineSpellChecker #include "mozilla/mozSpellChecker.h" // for mozSpellChecker #include "mozilla/Preferences.h" // for Preferences #include "mozilla/PresShell.h" // for PresShell #include "mozilla/RangeBoundary.h" // for RawRangeBoundary, RangeBoundary #include "mozilla/Services.h" // for GetObserverService #include "mozilla/StaticPrefs_bidi.h" // for StaticPrefs::bidi_* #include "mozilla/StaticPrefs_dom.h" // for StaticPrefs::dom_* #include "mozilla/StaticPrefs_editor.h" // for StaticPrefs::editor_* #include "mozilla/StaticPrefs_layout.h" // for StaticPrefs::layout_* #include "mozilla/TextComposition.h" // for TextComposition #include "mozilla/TextControlElement.h" // for TextControlElement #include "mozilla/TextInputListener.h" // for TextInputListener #include "mozilla/TextServicesDocument.h" // for TextServicesDocument #include "mozilla/TextEvents.h" #include "mozilla/TransactionManager.h" // for TransactionManager #include "mozilla/dom/AbstractRange.h" // for AbstractRange #include "mozilla/dom/Attr.h" // for Attr #include "mozilla/dom/BrowsingContext.h" // for BrowsingContext #include "mozilla/dom/CharacterData.h" // for CharacterData #include "mozilla/dom/DataTransfer.h" // for DataTransfer #include "mozilla/dom/Document.h" // for Document #include "mozilla/dom/DocumentInlines.h" // for GetObservingPresShell #include "mozilla/dom/DragEvent.h" // for DragEvent #include "mozilla/dom/Element.h" // for Element, nsINode::AsElement #include "mozilla/dom/EventTarget.h" // for EventTarget #include "mozilla/dom/HTMLBodyElement.h" #include "mozilla/dom/HTMLBRElement.h" #include "mozilla/dom/Selection.h" // for Selection, etc. #include "mozilla/dom/StaticRange.h" // for StaticRange #include "mozilla/dom/Text.h" #include "mozilla/dom/Event.h" #include "nsAString.h" // for nsAString::Length, etc. #include "nsCCUncollectableMarker.h" // for nsCCUncollectableMarker #include "nsCaret.h" // for nsCaret #include "nsCaseTreatment.h" #include "nsCharTraits.h" // for NS_IS_HIGH_SURROGATE, etc. #include "nsContentUtils.h" // for nsContentUtils #include "nsCopySupport.h" // for nsCopySupport #include "nsDOMString.h" // for DOMStringIsNull #include "nsDebug.h" // for NS_WARNING, etc. #include "nsError.h" // for NS_OK, etc. #include "nsFocusManager.h" // for nsFocusManager #include "nsFrameSelection.h" // for nsFrameSelection #include "nsGenericHTMLElement.h" // for nsGenericHTMLElement #include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::dir #include "nsIClipboard.h" // for nsIClipboard #include "nsIContent.h" // for nsIContent #include "nsIContentInlines.h" // for nsINode::IsInDesignMode() #include "nsIDocumentEncoder.h" // for nsIDocumentEncoder #include "nsIDocumentStateListener.h" // for nsIDocumentStateListener #include "nsIDocShell.h" // for nsIDocShell #include "nsIEditActionListener.h" // for nsIEditActionListener #include "nsIFrame.h" // for nsIFrame #include "nsIInlineSpellChecker.h" // for nsIInlineSpellChecker, etc. #include "nsNameSpaceManager.h" // for kNameSpaceID_None, etc. #include "nsINode.h" // for nsINode, etc. #include "nsISelectionController.h" // for nsISelectionController, etc. #include "nsISelectionDisplay.h" // for nsISelectionDisplay, etc. #include "nsISupports.h" // for nsISupports #include "nsISupportsUtils.h" // for NS_ADDREF, NS_IF_ADDREF #include "nsITransferable.h" // for nsITransferable #include "nsIWeakReference.h" // for nsISupportsWeakReference #include "nsIWidget.h" // for nsIWidget, IMEState, etc. #include "nsPIDOMWindow.h" // for nsPIDOMWindow #include "nsPresContext.h" // for nsPresContext #include "nsRange.h" // for nsRange #include "nsReadableUtils.h" // for EmptyString, ToNewCString #include "nsString.h" // for nsAutoString, nsString, etc. #include "nsStringFwd.h" // for nsString #include "nsStyleConsts.h" // for StyleDirection::Rtl, etc. #include "nsStyleStruct.h" // for nsStyleDisplay, nsStyleText, etc. #include "nsStyleStructFwd.h" // for nsIFrame::StyleUIReset, etc. #include "nsTextNode.h" // for nsTextNode #include "nsThreadUtils.h" // for nsRunnable #include "prtime.h" // for PR_Now class nsIOutputStream; class nsITransferable; namespace mozilla { using namespace dom; using namespace widget; using EmptyCheckOption = HTMLEditUtils::EmptyCheckOption; using LeafNodeType = HTMLEditUtils::LeafNodeType; using LeafNodeTypes = HTMLEditUtils::LeafNodeTypes; using WalkTreeOption = HTMLEditUtils::WalkTreeOption; /***************************************************************************** * mozilla::EditorBase *****************************************************************************/ template EditorDOMPoint EditorBase::GetFirstIMESelectionStartPoint() const; template EditorRawDOMPoint EditorBase::GetFirstIMESelectionStartPoint() const; template EditorDOMPoint EditorBase::GetLastIMESelectionEndPoint() const; template EditorRawDOMPoint EditorBase::GetLastIMESelectionEndPoint() const; template Result EditorBase::InsertNodeWithTransaction(nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert); template Result EditorBase::InsertNodeWithTransaction(Element& aContentToInsert, const EditorDOMPoint& aPointToInsert); template Result EditorBase::InsertNodeWithTransaction(Text& aContentToInsert, const EditorDOMPoint& aPointToInsert); template EditorDOMPoint EditorBase::GetFirstSelectionStartPoint() const; template EditorRawDOMPoint EditorBase::GetFirstSelectionStartPoint() const; template EditorDOMPoint EditorBase::GetFirstSelectionEndPoint() const; template EditorRawDOMPoint EditorBase::GetFirstSelectionEndPoint() const; template EditorDOMPoint EditorBase::FindBetterInsertionPoint( const EditorDOMPoint& aPoint) const; template EditorRawDOMPoint EditorBase::FindBetterInsertionPoint( const EditorRawDOMPoint& aPoint) const; template EditorBase::AutoCaretBidiLevelManager::AutoCaretBidiLevelManager( const EditorBase& aEditorBase, nsIEditor::EDirection aDirectionAndAmount, const EditorDOMPoint& aPointAtCaret); template EditorBase::AutoCaretBidiLevelManager::AutoCaretBidiLevelManager( const EditorBase& aEditorBase, nsIEditor::EDirection aDirectionAndAmount, const EditorRawDOMPoint& aPointAtCaret); EditorBase::EditorBase(EditorType aEditorType) : mEditActionData(nullptr), mPlaceholderName(nullptr), mModCount(0), mFlags(0), mUpdateCount(0), mPlaceholderBatch(0), mWrapColumn(0), mNewlineHandling(StaticPrefs::editor_singleLine_pasteNewlines()), mCaretStyle(StaticPrefs::layout_selection_caret_style()), mDocDirtyState(-1), mSpellcheckCheckboxState(eTriUnset), mInitSucceeded(false), mAllowsTransactionsToChangeSelection(true), mDidPreDestroy(false), mDidPostCreate(false), mDispatchInputEvent(true), mIsInEditSubAction(false), mHidingCaret(false), mSpellCheckerDictionaryUpdated(true), mIsHTMLEditorClass(aEditorType == EditorType::HTML) { #ifdef XP_WIN if (!mCaretStyle && !IsTextEditor()) { // Wordpad-like caret behavior. mCaretStyle = 1; } #endif // #ifdef XP_WIN if (mNewlineHandling < nsIEditor::eNewlinesPasteIntact || mNewlineHandling > nsIEditor::eNewlinesStripSurroundingWhitespace) { mNewlineHandling = nsIEditor::eNewlinesPasteToFirst; } } EditorBase::~EditorBase() { MOZ_ASSERT(!IsInitialized() || mDidPreDestroy, "Why PreDestroy hasn't been called?"); if (mComposition) { mComposition->OnEditorDestroyed(); mComposition = nullptr; } // If this editor is still hiding the caret, we need to restore it. HideCaret(false); mTransactionManager = nullptr; } NS_IMPL_CYCLE_COLLECTION_CLASS(EditorBase) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EditorBase) // Remove event listeners first since EditorEventListener may need // mDocument, mEventTarget, etc. if (tmp->mEventListener) { tmp->mEventListener->Disconnect(); tmp->mEventListener = nullptr; } NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootElement) NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectionController) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mIMEContentObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK(mInlineSpellChecker) NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextServicesDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextInputListener) NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransactionManager) NS_IMPL_CYCLE_COLLECTION_UNLINK(mActionListeners) 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 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EditorBase) Document* currentDoc = tmp->mRootElement ? tmp->mRootElement->GetUncomposedDoc() : nullptr; if (currentDoc && nsCCUncollectableMarker::InGeneration( cb, currentDoc->GetMarkedCCGeneration())) { return NS_SUCCESS_INTERRUPTED_TRAVERSE; } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectionController) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIMEContentObserver) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInlineSpellChecker) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextServicesDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextInputListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransactionManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActionListeners) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocStateListeners) 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) NS_INTERFACE_MAP_ENTRY(nsISelectionListener) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIEditor) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(EditorBase) NS_IMPL_CYCLE_COLLECTING_RELEASE(EditorBase) nsresult EditorBase::InitInternal(Document& aDocument, Element* aRootElement, nsISelectionController& aSelectionController, uint32_t aFlags) { MOZ_ASSERT_IF( !mEditActionData || !mEditActionData->HasEditorDestroyedDuringHandlingEditAction(), GetTopLevelEditSubAction() == EditSubAction::eNone); // First only set flags, but other stuff shouldn't be initialized now. // Note that SetFlags() will be called by PostCreate(). mFlags = aFlags; mDocument = &aDocument; // nsISelectionController should be stored only when we're a `TextEditor`. // Otherwise, in `HTMLEditor`, it's `PresShell`, and grabbing it causes // a circular reference and memory leak. // XXX Should we move `mSelectionController to `TextEditor`? MOZ_ASSERT_IF(!IsTextEditor(), &aSelectionController == GetPresShell()); if (IsTextEditor()) { MOZ_ASSERT(&aSelectionController != GetPresShell()); mSelectionController = &aSelectionController; } if (mEditActionData) { // During edit action, selection is cached. But this selection is invalid // now since selection controller is updated, so we have to update this // cache. Selection* selection = aSelectionController.GetSelection( nsISelectionController::SELECTION_NORMAL); NS_WARNING_ASSERTION(selection, "SelectionController::GetSelection() failed"); if (selection) { mEditActionData->UpdateSelectionCache(*selection); } } // set up root element if we are passed one. if (aRootElement) { mRootElement = aRootElement; } // If this is an editor for or