Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/editor/libeditor/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 278 kB image not shown  

Quelle  EditorBase.cpp   Sprache: C

 
/* -*- 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 <stdio.h>   // for nullptr, stdout
#include <string.h>  // for strcmp

#include "AutoClonedRangeArray.h"  // for AutoClonedRangeArray and AutoClonedSelectionRangeArray
#include "AutoSelectionRestorer.h"
#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 "EditorForwards.h"
#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/AsyncEventDispatcher.h"
#include "mozilla/EditorDOMPoint.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/ScopeExit.h"           // for MakeScopeExit
#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/BorrowedAttrInfo.h"  // for BorrowedAttrInfo
#include "mozilla/dom/BrowsingContext.h"   // for BrowsingContext
#include "mozilla/dom/CharacterData.h"     // for CharacterData
#include "mozilla/dom/ContentParent.h"     // for ContentParent
#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<CreateContentResult, nsresult>
EditorBase::InsertNodeWithTransaction(nsIContent& aContentToInsert,
                                      const EditorDOMPoint& aPointToInsert);
template Result<CreateElementResult, nsresult>
EditorBase::InsertNodeWithTransaction(Element& aContentToInsert,
                                      const EditorDOMPoint& aPointToInsert);
template Result<CreateTextResult, nsresult>
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 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),
      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 <input> or <textarea>, the text node which
  // has composition string is always recreated with same content. Therefore,
  // we need to nodify mComposition of text node destruction and replacing
  // composing string when this receives eCompositionChange event next time.
  if (mComposition && mComposition->GetContainerTextNode() &&
      !mComposition->GetContainerTextNode()->IsInComposedDoc()) {
    mComposition->OnTextNodeRemoved();
  }

  // Show the caret.
  DebugOnly<nsresult> rvIgnored = aSelectionController.SetCaretReadOnly(false);
  NS_WARNING_ASSERTION(
      NS_SUCCEEDED(rvIgnored),
      "nsISelectionController::SetCaretReadOnly(false) failed, but ignored");
  // Show all the selection reflected to user.
  rvIgnored =
      aSelectionController.SetSelectionFlags(nsISelectionDisplay::DISPLAY_ALL);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                       "nsISelectionController::SetSelectionFlags("
                       "nsISelectionDisplay::DISPLAY_ALL) failed, but ignored");

  // Make sure that the editor will be destroyed properly
  mDidPreDestroy = false;
  // Make sure that the editor will be created properly
  mDidPostCreate = false;

  MOZ_ASSERT(IsBeingInitialized());

  AutoEditActionDataSetter editActionData(*this, EditAction::eInitializing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_FAILURE;
  }

  SelectionRef().AddSelectionListener(this);

  return NS_OK;
}

nsresult EditorBase::EnsureEmptyTextFirstChild() {
  MOZ_ASSERT(IsTextEditor());
  RefPtr<Element> root = GetRoot();
  nsIContent* firstChild = root->GetFirstChild();

  if (!firstChild || !firstChild->IsText()) {
    RefPtr<nsTextNode> newTextNode = CreateTextNode(u""_ns);
    if (!newTextNode) {
      NS_WARNING("EditorBase::CreateTextNode() failed");
      return NS_ERROR_UNEXPECTED;
    }
    IgnoredErrorResult ignoredError;
    root->InsertChildBefore(newTextNode, root->GetFirstChild(), true,
                            ignoredError);
    MOZ_ASSERT(!ignoredError.Failed());
  }

  return NS_OK;
}

nsresult EditorBase::PostCreateInternal() {
  MOZ_ASSERT(IsEditActionDataAvailable());

  // Synchronize some stuff for the flags.  SetFlags() will initialize
  // something by the flag difference.  This is first time of that, so, all
  // initializations must be run.  For such reason, we need to invert mFlags
  // value first.
  mFlags = ~mFlags;
  nsresult rv = SetFlags(~mFlags);
  if (NS_FAILED(rv)) {
    NS_WARNING("EditorBase::SetFlags() failed");
    return EditorBase::ToGenericNSResult(rv);
  }

  // These operations only need to happen on the first PostCreate call
  if (!mDidPostCreate) {
    mDidPostCreate = true;

    // Set up listeners
    CreateEventListeners();
    nsresult rv = InstallEventListeners();
    if (NS_FAILED(rv)) {
      NS_WARNING("EditorBase::InstallEventListeners() failed");
      return EditorBase::ToGenericNSResult(rv);
    }

    // nuke the modification count, so the doc appears unmodified
    // do this before we notify listeners
    DebugOnly<nsresult> rvIgnored = ResetModificationCount();
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rvIgnored),
        "EditorBase::ResetModificationCount() failed, but ignored");

    // update the UI with our state
    rvIgnored = NotifyDocumentListeners(eDocumentCreated);
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                         "EditorBase::NotifyDocumentListeners(eDocumentCreated)"
                         " failed, but ignored");
    rvIgnored = NotifyDocumentListeners(eDocumentStateChanged);
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                         "EditorBase::NotifyDocumentListeners("
                         "eDocumentStateChanged) failed, but ignored");
  }

  // update nsTextStateManager and caret if we have focus
  if (RefPtr<Element> focusedElement = GetFocusedElement()) {
    DebugOnly<nsresult> rvIgnored = InitializeSelection(*focusedElement);
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rvIgnored),
        "EditorBase::InitializeSelection() failed, but ignored");

    // If the text control gets reframed during focus, Focus() would not be
    // called, so take a chance here to see if we need to spell check the text
    // control.
    nsresult rv = FlushPendingSpellCheck();
    if (MOZ_UNLIKELY(rv == NS_ERROR_EDITOR_DESTROYED)) {
      NS_WARNING(
          "EditorBase::FlushPendingSpellCheck() caused destroying the editor");
      return EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
    }
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rv),
        "EditorBase::FlushPendingSpellCheck() failed, but ignored");

    IMEState newState;
    rv = GetPreferredIMEState(&newState);
    if (NS_FAILED(rv)) {
      NS_WARNING("EditorBase::GetPreferredIMEState() failed");
      return NS_OK;
    }
    IMEStateManager::UpdateIMEState(newState, focusedElement, *this);
  }

  // FYI: This call might cause destroying this editor.
  IMEStateManager::OnEditorInitialized(*this);

  return NS_OK;
}

void EditorBase::SetTextInputListener(TextInputListener* aTextInputListener) {
  MOZ_ASSERT(!mTextInputListener || !aTextInputListener ||
             mTextInputListener == aTextInputListener);
  mTextInputListener = aTextInputListener;
}

void EditorBase::SetIMEContentObserver(
    IMEContentObserver* aIMEContentObserver) {
  MOZ_ASSERT(!mIMEContentObserver || !aIMEContentObserver ||
             mIMEContentObserver == aIMEContentObserver);
  mIMEContentObserver = aIMEContentObserver;
}

void EditorBase::CreateEventListeners() {
  // Don't create the handler twice
  if (!mEventListener) {
    mEventListener = new EditorEventListener();
  }
}

nsresult EditorBase::InstallEventListeners() {
  // FIXME InstallEventListeners() should not be called if we failed to set
  // document or create an event listener.  So, these checks should be
  // MOZ_DIAGNOSTIC_ASSERT instead.
  MOZ_ASSERT(GetDocument());
  if (MOZ_UNLIKELY(!GetDocument()) || NS_WARN_IF(!mEventListener)) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  // Initialize the event target.
  mEventTarget = GetExposedRoot();
  if (NS_WARN_IF(!mEventTarget)) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  nsresult rv = mEventListener->Connect(this);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorEventListener::Connect() failed");
  if (mComposition) {
    // If mComposition has already been destroyed, we should forget it.
    // This may happen if it ended while we don't listen to composition
    // events.
    if (mComposition->Destroyed()) {
      // XXX We may need to fix existing composition transaction here.
      //     However, this may be called when it's not safe.
      //     Perhaps, we should stop handling composition with events.
      mComposition = nullptr;
    }
    // Otherwise, Restart to handle composition with new editor contents.
    else {
      mComposition->StartHandlingComposition(this);
    }
  }
  return rv;
}

void EditorBase::RemoveEventListeners() {
  if (!mEventListener) {
    return;
  }
  mEventListener->Disconnect();
  if (mComposition) {
    // Even if this is called, don't release mComposition because this is
    // may be reused after reframing.
    mComposition->EndHandlingComposition(this);
  }
  mEventTarget = nullptr;
}

bool EditorBase::IsListeningToEvents() const {
  return mEventListener && !mEventListener->DetachedFromEditor();
}

bool EditorBase::GetDesiredSpellCheckState() {
  // Check user override on this element
  if (mSpellcheckCheckboxState != eTriUnset) {
    return (mSpellcheckCheckboxState == eTriTrue);
  }

  // Check user preferences
  int32_t spellcheckLevel = StaticPrefs::layout_spellcheckDefault();

  if (!spellcheckLevel) {
    return false;  // Spellchecking forced off globally
  }

  if (!CanEnableSpellCheck()) {
    return false;
  }

  PresShell* presShell = GetPresShell();
  if (presShell) {
    nsPresContext* context = presShell->GetPresContext();
    if (context && !context->IsDynamic()) {
      return false;
    }
  }

  // Check DOM state
  nsCOMPtr<nsIContent> content = GetExposedRoot();
  if (!content) {
    return false;
  }

  auto element = nsGenericHTMLElement::FromNode(content);
  if (!element) {
    return false;
  }

  // XXX I'm not sure whether we don't use this path when we're a plaintext mail
  // composer.
  if (IsHTMLEditor() && !AsHTMLEditor()->IsPlaintextMailComposer()) {
    // Some of the page content might be editable and some not, if spellcheck=
    // is explicitly set anywhere, so if there's anything editable on the page,
    // return true and let the spellchecker figure it out.
    Document* doc = content->GetComposedDoc();
    return doc && doc->IsEditingOn();
  }

  return element->Spellcheck();
}

void EditorBase::PreDestroyInternal() {
  MOZ_ASSERT(!mDidPreDestroy);

  mInitSucceeded = false;

  Selection* selection = GetSelection();
  if (selection) {
    selection->RemoveSelectionListener(this);
  }

  IMEStateManager::OnEditorDestroying(*this);

  // Let spellchecker clean up its observers etc. It is important not to
  // actually free the spellchecker here, since the spellchecker could have
  // caused flush notifications, which could have gotten here if a textbox
  // is being removed. Setting the spellchecker to nullptr could free the
  // object that is still in use! It will be freed when the editor is
  // destroyed.
  if (mInlineSpellChecker) {
    DebugOnly<nsresult> rvIgnored =
        mInlineSpellChecker->Cleanup(IsTextEditor());
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rvIgnored),
        "mozInlineSpellChecker::Cleanup() failed, but ignored");
  }

  // tell our listeners that the doc is going away
  DebugOnly<nsresult> rvIgnored =
      NotifyDocumentListeners(eDocumentToBeDestroyed);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                       "EditorBase::NotifyDocumentListeners("
                       "eDocumentToBeDestroyed) failed, but ignored");

  // Unregister event listeners
  RemoveEventListeners();
  // If this editor is still hiding the caret, we need to restore it.
  HideCaret(false);
  mActionListeners.Clear();
  mDocStateListeners.Clear();
  mInlineSpellChecker = nullptr;
  mTextServicesDocument = nullptr;
  mTextInputListener = nullptr;
  mSpellcheckCheckboxState = eTriUnset;
  mRootElement = nullptr;

  // Transaction may grab this instance.  Therefore, they should be released
  // here for stopping the circular reference with this instance.
  if (mTransactionManager) {
    DebugOnly<bool> disabledUndoRedo = DisableUndoRedo();
    NS_WARNING_ASSERTION(disabledUndoRedo,
                         "EditorBase::DisableUndoRedo() failed, but ignored");
    mTransactionManager = nullptr;
  }

  if (mEditActionData) {
    mEditActionData->OnEditorDestroy();
  }

  mDidPreDestroy = true;
}

NS_IMETHODIMP EditorBase::GetFlags(uint32_t* aFlags) {
  // NOTE: If you need to override this method, you need to make Flags()
  //       virtual.
  *aFlags = Flags();
  return NS_OK;
}

NS_IMETHODIMP EditorBase::SetFlags(uint32_t aFlags) {
  if (mFlags == aFlags) {
    return NS_OK;
  }

  // If we're a `TextEditor` instance, it's always a plaintext editor.
  // Therefore, `eEditorPlaintextMask` is not necessary and should not be set
  // for the performance reason.
  MOZ_ASSERT_IF(IsTextEditor(), !(aFlags & nsIEditor::eEditorPlaintextMask));
  // If we're an `HTMLEditor` instance, we cannot treat it as a single line
  // editor.  So, eEditorSingleLineMask is available only when we're a
  // `TextEditor` instance.
  MOZ_ASSERT_IF(IsHTMLEditor(), !(aFlags & nsIEditor::eEditorSingleLineMask));
  // If we're an `HTMLEditor` instance, we cannot treat it as a password editor.
  // So, eEditorPasswordMask is available only when we're a `TextEditor`
  // instance.
  MOZ_ASSERT_IF(IsHTMLEditor(), !(aFlags & nsIEditor::eEditorPasswordMask));
  // eEditorAllowInteraction changes the behavior of `HTMLEditor`.  So, it's
  // not available with `TextEditor` instance.
  MOZ_ASSERT_IF(IsTextEditor(), !(aFlags & nsIEditor::eEditorAllowInteraction));

  const bool isCalledByPostCreate = (mFlags == ~aFlags);
  // We don't support dynamic password flag change.
  MOZ_ASSERT_IF(!isCalledByPostCreate,
                !((mFlags ^ aFlags) & nsIEditor::eEditorPasswordMask));
  bool spellcheckerWasEnabled = !isCalledByPostCreate && CanEnableSpellCheck();
  mFlags = aFlags;

  if (!IsInitialized()) {
    // If we're initializing, we shouldn't do anything now.
    // SetFlags() will be called by PostCreate(),
    // we should synchronize some stuff for the flags at that time.
    return NS_OK;
  }

  // The flag change may cause the spellchecker state change
  if (CanEnableSpellCheck() != spellcheckerWasEnabled) {
    SyncRealTimeSpell();
  }

  // If this is called from PostCreate(), it will update the IME state if it's
  // necessary.
  if (!mDidPostCreate) {
    return NS_OK;
  }

  // Might be changing editable state, so, we need to reset current IME state
  // if we're focused and the flag change causes IME state change.
  if (RefPtr<Element> focusedElement = GetFocusedElement()) {
    IMEState newState;
    nsresult rv = GetPreferredIMEState(&newState);
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rv),
        "EditorBase::GetPreferredIMEState() failed, but ignored");
    if (NS_SUCCEEDED(rv)) {
      // NOTE: When the enabled state isn't going to be modified, this method
      // is going to do nothing.
      IMEStateManager::UpdateIMEState(newState, focusedElement, *this);
    }
  }

  return NS_OK;
}

NS_IMETHODIMP EditorBase::GetIsSelectionEditable(bool* aIsSelectionEditable) {
  if (NS_WARN_IF(!aIsSelectionEditable)) {
    return NS_ERROR_INVALID_ARG;
  }
  *aIsSelectionEditable = IsSelectionEditable();
  return NS_OK;
}

bool EditorBase::IsSelectionEditable() {
  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return false;
  }

  if (IsTextEditor()) {
    // XXX we just check that the anchor node is editable at the moment
    //     we should check that all nodes in the selection are editable
    const nsINode* anchorNode = SelectionRef().GetAnchorNode();
    return anchorNode && anchorNode->IsContent() && anchorNode->IsEditable();
  }

  const nsINode* anchorNode = SelectionRef().GetAnchorNode();
  const nsINode* focusNode = SelectionRef().GetFocusNode();
  if (!anchorNode || !focusNode) {
    return false;
  }

  // if anchorNode or focusNode is in a native anonymous subtree, HTMLEditor
  // shouldn't edit content in it.
  // XXX This must be a bug of Selection API.
  if (MOZ_UNLIKELY(anchorNode->IsInNativeAnonymousSubtree() ||
                   focusNode->IsInNativeAnonymousSubtree())) {
    return false;
  }

  // Per the editing spec as of June 2012: we have to have a selection whose
  // start and end nodes are editable, and which share an ancestor editing
  // host.  (Bug 766387.)
  bool isSelectionEditable = SelectionRef().RangeCount() &&
                             anchorNode->IsEditable() &&
                             focusNode->IsEditable();
  if (!isSelectionEditable) {
    return false;
  }

  const nsINode* commonAncestor =
      SelectionRef().GetAnchorFocusRange()->GetClosestCommonInclusiveAncestor();
  while (commonAncestor && !commonAncestor->IsEditable()) {
    commonAncestor = commonAncestor->GetParentNode();
  }
  // If there is no editable common ancestor, return false.
  return !!commonAncestor;
}

NS_IMETHODIMP EditorBase::GetIsDocumentEditable(bool* aIsDocumentEditable) {
  if (NS_WARN_IF(!aIsDocumentEditable)) {
    return NS_ERROR_INVALID_ARG;
  }
  RefPtr<Document> document = GetDocument();
  *aIsDocumentEditable = document && IsModifiable();
  return NS_OK;
}

NS_IMETHODIMP EditorBase::GetDocument(Document** aDocument) {
  if (NS_WARN_IF(!aDocument)) {
    return NS_ERROR_INVALID_ARG;
  }
  *aDocument = do_AddRef(mDocument).take();
  return NS_WARN_IF(!*aDocument) ? NS_ERROR_NOT_INITIALIZED : NS_OK;
}

already_AddRefed<nsIWidget> EditorBase::GetWidget() const {
  nsPresContext* presContext = GetPresContext();
  if (NS_WARN_IF(!presContext)) {
    return nullptr;
  }
  nsCOMPtr<nsIWidget> widget = presContext->GetRootWidget();
  return NS_WARN_IF(!widget) ? nullptr : widget.forget();
}

NS_IMETHODIMP EditorBase::GetContentsMIMEType(nsAString& aContentsMIMEType) {
  aContentsMIMEType = mContentMIMEType;
  return NS_OK;
}

NS_IMETHODIMP EditorBase::SetContentsMIMEType(
    const nsAString& aContentsMIMEType) {
  mContentMIMEType.Assign(aContentsMIMEType);
  return NS_OK;
}

NS_IMETHODIMP EditorBase::GetSelectionController(
    nsISelectionController** aSelectionController) {
  if (NS_WARN_IF(!aSelectionController)) {
    return NS_ERROR_INVALID_ARG;
  }
  *aSelectionController = do_AddRef(GetSelectionController()).take();
  return NS_WARN_IF(!*aSelectionController) ? NS_ERROR_FAILURE : NS_OK;
}

NS_IMETHODIMP EditorBase::DeleteSelection(EDirection aAction,
                                          EStripWrappers aStripWrappers) {
  nsresult rv = DeleteSelectionAsAction(aAction, aStripWrappers);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorBase::DeleteSelectionAsAction() failed");
  return rv;
}

NS_IMETHODIMP EditorBase::GetSelection(Selection** aSelection) {
  nsresult rv = GetSelection(SelectionType::eNormal, aSelection);
  NS_WARNING_ASSERTION(
      NS_SUCCEEDED(rv),
      "EditorBase::GetSelection(SelectionType::eNormal) failed");
  return rv;
}

nsresult EditorBase::GetSelection(SelectionType aSelectionType,
                                  Selection** aSelection) const {
  if (NS_WARN_IF(!aSelection)) {
    return NS_ERROR_INVALID_ARG;
  }
  if (IsEditActionDataAvailable()) {
    *aSelection = do_AddRef(&SelectionRef()).take();
    return NS_OK;
  }
  nsISelectionController* selectionController = GetSelectionController();
  if (NS_WARN_IF(!selectionController)) {
    *aSelection = nullptr;
    return NS_ERROR_NOT_INITIALIZED;
  }
  *aSelection = do_AddRef(selectionController->GetSelection(
                              ToRawSelectionType(aSelectionType)))
                    .take();
  return NS_WARN_IF(!*aSelection) ? NS_ERROR_FAILURE : NS_OK;
}

nsresult EditorBase::DoTransactionInternal(nsITransaction* aTransaction) {
  MOZ_ASSERT(IsEditActionDataAvailable());
  MOZ_ASSERT_IF(
      // If the DOM is modified by a clipboard event handler,
      // HTMLEditor::OnModifyDocument() may need to do some transactions before
      // dispatching `beforeinput`.
      // FIXME: It shouldn't happen, and I think that it should be done once
      // before dispatching `input` event to hide the our editor hack from
      // the event listeners.
      GetEditAction() != EditAction::ePaste &&
          GetEditAction() != EditAction::eCut,
      !ShouldAlreadyHaveHandledBeforeInputEventDispatching());

  if (mPlaceholderBatch && !mPlaceholderTransaction) {
    MOZ_DIAGNOSTIC_ASSERT(mPlaceholderName);
    mPlaceholderTransaction = PlaceholderTransaction::Create(
        *this, *mPlaceholderName, std::move(mSelState));
    MOZ_ASSERT(mSelState.isNothing());

    // We will recurse, but will not hit this case in the nested call
    RefPtr<PlaceholderTransaction> placeholderTransaction =
        mPlaceholderTransaction;
    DebugOnly<nsresult> rvIgnored =
        DoTransactionInternal(placeholderTransaction);
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rvIgnored),
        "EditorBase::DoTransactionInternal() failed, but ignored");

    if (mTransactionManager) {
      if (nsCOMPtr<nsITransaction> topTransaction =
              mTransactionManager->PeekUndoStack()) {
        if (RefPtr<EditTransactionBase> topTransactionBase =
                topTransaction->GetAsEditTransactionBase()) {
          if (PlaceholderTransaction* topPlaceholderTransaction =
                  topTransactionBase->GetAsPlaceholderTransaction()) {
            // there is a placeholder transaction on top of the undo stack.  It
            // is either the one we just created, or an earlier one that we are
            // now merging into.  From here on out remember this placeholder
            // instead of the one we just created.
            mPlaceholderTransaction = topPlaceholderTransaction;
          }
        }
      }
    }
  }

  if (aTransaction) {
    // XXX: Why are we doing selection specific batching stuff here?
    // XXX: Most entry points into the editor have auto variables that
    // XXX: should trigger Begin/EndUpdateViewBatch() calls that will make
    // XXX: these selection batch calls no-ops.
    // XXX:
    // XXX: I suspect that this was placed here to avoid multiple
    // XXX: selection changed notifications from happening until after
    // XXX: the transaction was done. I suppose that can still happen
    // XXX: if an embedding application called DoTransaction() directly
    // XXX: to pump its own transactions through the system, but in that
    // XXX: case, wouldn't we want to use Begin/EndUpdateViewBatch() or
    // XXX: its auto equivalent AutoUpdateViewBatch to ensure that
    // XXX: selection listeners have access to accurate frame data?
    // XXX:
    // XXX: Note that if we did add Begin/EndUpdateViewBatch() calls
    // XXX: we will need to make sure that they are disabled during
    // XXX: the init of the editor for text widgets to avoid layout
    // XXX: re-entry during initial reflow. - kin

    // get the selection and start a batch change
    SelectionBatcher selectionBatcher(SelectionRef(), __FUNCTION__);

    if (mTransactionManager) {
      RefPtr<TransactionManager> transactionManager(mTransactionManager);
      nsresult rv = transactionManager->DoTransaction(aTransaction);
      if (NS_FAILED(rv)) {
        NS_WARNING("TransactionManager::DoTransaction() failed");
        return rv;
      }
    } else {
      nsresult rv = aTransaction->DoTransaction();
      if (NS_FAILED(rv)) {
        NS_WARNING("nsITransaction::DoTransaction() failed");
        return rv;
      }
    }

    DoAfterDoTransaction(aTransaction);
  }

  return NS_OK;
}

NS_IMETHODIMP EditorBase::EnableUndo(bool aEnable) {
  // XXX Should we return NS_ERROR_FAILURE if EdnableUndoRedo() or
  //     DisableUndoRedo() returns false?
  if (aEnable) {
    DebugOnly<bool> enabledUndoRedo = EnableUndoRedo();
    NS_WARNING_ASSERTION(enabledUndoRedo,
                         "EditorBase::EnableUndoRedo() failed, but ignored");
    return NS_OK;
  }
  DebugOnly<bool> disabledUndoRedo = DisableUndoRedo();
  NS_WARNING_ASSERTION(disabledUndoRedo,
                       "EditorBase::DisableUndoRedo() failed, but ignored");
  return NS_OK;
}

NS_IMETHODIMP EditorBase::ClearUndoRedoXPCOM() {
  if (MOZ_UNLIKELY(!ClearUndoRedo())) {
    return NS_ERROR_FAILURE;  // We're handling a transaction
  }
  return NS_OK;
}

NS_IMETHODIMP EditorBase::Undo() {
  nsresult rv = UndoAsAction(1u);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::UndoAsAction() failed");
  return rv;
}

NS_IMETHODIMP EditorBase::UndoAll() {
  if (!mTransactionManager) {
    return NS_OK;
  }
  size_t numberOfUndoItems = mTransactionManager->NumberOfUndoItems();
  if (!numberOfUndoItems) {
    return NS_OK;  // no transactions
  }
  nsresult rv = UndoAsAction(numberOfUndoItems);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::UndoAsAction() failed");
  return rv;
}

NS_IMETHODIMP EditorBase::GetUndoRedoEnabled(bool* aIsEnabled) {
  MOZ_ASSERT(aIsEnabled);
  *aIsEnabled = IsUndoRedoEnabled();
  return NS_OK;
}

NS_IMETHODIMP EditorBase::GetCanUndo(bool* aCanUndo) {
  MOZ_ASSERT(aCanUndo);
  *aCanUndo = CanUndo();
  return NS_OK;
}

NS_IMETHODIMP EditorBase::Redo() {
  nsresult rv = RedoAsAction(1u);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::RedoAsAction() failed");
  return rv;
}

NS_IMETHODIMP EditorBase::GetCanRedo(bool* aCanRedo) {
  MOZ_ASSERT(aCanRedo);
  *aCanRedo = CanRedo();
  return NS_OK;
}

nsresult EditorBase::UndoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
  if (aCount == 0 || IsReadonly()) {
    return NS_OK;
  }

  // If we don't have transaction in the undo stack, we shouldn't notify
  // anybody of trying to undo since it's not useful notification but we
  // need to pay some runtime cost.
  if (!CanUndo()) {
    return NS_OK;
  }

  // If there is composition, we shouldn't allow to undo with committing
  // composition since Chrome doesn't allow it and it doesn't make sense
  // because committing composition causes one transaction and Undo(1)
  // undoes the committing composition.
  if (GetComposition()) {
    return NS_OK;
  }

  AutoEditActionDataSetter editActionData(*this, EditAction::eUndo, aPrincipal);
  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
  if (NS_FAILED(rv)) {
    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
    return EditorBase::ToGenericNSResult(rv);
  }

  AutoUpdateViewBatch preventSelectionChangeEvent(*this, __FUNCTION__);

  NotifyEditorObservers(eNotifyEditorObserversOfBefore);
  if (NS_WARN_IF(!CanUndo()) || NS_WARN_IF(Destroyed())) {
    return NS_ERROR_FAILURE;
  }

  rv = NS_OK;
  {
    IgnoredErrorResult ignoredError;
    AutoEditSubActionNotifier startToHandleEditSubAction(
        *this, EditSubAction::eUndo, nsIEditor::eNone, ignoredError);
    if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
      return EditorBase::ToGenericNSResult(ignoredError.StealNSResult());
    }
    NS_WARNING_ASSERTION(!ignoredError.Failed(),
                         "TextEditor::OnStartToHandleTopLevelEditSubAction() "
                         "failed, but ignored");

    RefPtr<TransactionManager> transactionManager(mTransactionManager);
    for (uint32_t i = 0; i < aCount; ++i) {
      if (NS_FAILED(transactionManager->Undo())) {
        NS_WARNING("TransactionManager::Undo() failed");
        break;
      }
      DoAfterUndoTransaction();
    }

    if (IsHTMLEditor()) {
      rv = AsHTMLEditor()->ReflectPaddingBRElementForEmptyEditor();
    }
  }

  NotifyEditorObservers(eNotifyEditorObserversOfEnd);
  return EditorBase::ToGenericNSResult(rv);
}

nsresult EditorBase::RedoAsAction(uint32_t aCount, nsIPrincipal* aPrincipal) {
  if (aCount == 0 || IsReadonly()) {
    return NS_OK;
  }

  // If we don't have transaction in the redo stack, we shouldn't notify
  // anybody of trying to redo since it's not useful notification but we
  // need to pay some runtime cost.
  if (!CanRedo()) {
    return NS_OK;
  }

  // If there is composition, we shouldn't allow to redo with committing
  // composition since Chrome doesn't allow it and it doesn't make sense
  // because committing composition causes removing all transactions from
  // the redo queue.  So, it becomes impossible to redo anything.
  if (GetComposition()) {
    return NS_OK;
  }

  AutoEditActionDataSetter editActionData(*this, EditAction::eRedo, aPrincipal);
  nsresult rv = editActionData.CanHandleAndMaybeDispatchBeforeInputEvent();
  if (NS_FAILED(rv)) {
    NS_WARNING_ASSERTION(rv == NS_ERROR_EDITOR_ACTION_CANCELED,
                         "CanHandleAndMaybeDispatchBeforeInputEvent() failed");
    return EditorBase::ToGenericNSResult(rv);
  }

  AutoUpdateViewBatch preventSelectionChangeEvent(*this, __FUNCTION__);

  NotifyEditorObservers(eNotifyEditorObserversOfBefore);
  if (NS_WARN_IF(!CanRedo()) || NS_WARN_IF(Destroyed())) {
    return NS_ERROR_FAILURE;
  }

  rv = NS_OK;
  {
    IgnoredErrorResult ignoredError;
    AutoEditSubActionNotifier startToHandleEditSubAction(
        *this, EditSubAction::eRedo, nsIEditor::eNone, ignoredError);
    if (NS_WARN_IF(ignoredError.ErrorCodeIs(NS_ERROR_EDITOR_DESTROYED))) {
      return ignoredError.StealNSResult();
    }
    NS_WARNING_ASSERTION(!ignoredError.Failed(),
                         "TextEditor::OnStartToHandleTopLevelEditSubAction() "
                         "failed, but ignored");

    RefPtr<TransactionManager> transactionManager(mTransactionManager);
    for (uint32_t i = 0; i < aCount; ++i) {
      if (NS_FAILED(transactionManager->Redo())) {
        NS_WARNING("TransactionManager::Redo() failed");
        break;
      }
      DoAfterRedoTransaction();
    }

    if (IsHTMLEditor()) {
      rv = AsHTMLEditor()->ReflectPaddingBRElementForEmptyEditor();
    }
  }

  NotifyEditorObservers(eNotifyEditorObserversOfEnd);
  return EditorBase::ToGenericNSResult(rv);
}

NS_IMETHODIMP EditorBase::BeginTransaction() {
  AutoEditActionDataSetter editActionData(*this, EditAction::eUnknown);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_FAILURE;
  }

  BeginTransactionInternal(__FUNCTION__);
  return NS_OK;
}

void EditorBase::BeginTransactionInternal(const char* aRequesterFuncName) {
  BeginUpdateViewBatch(aRequesterFuncName);

  if (NS_WARN_IF(!mTransactionManager)) {
    return;
  }

  RefPtr<TransactionManager> transactionManager(mTransactionManager);
  DebugOnly<nsresult> rvIgnored = transactionManager->BeginBatch(nullptr);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                       "TransactionManager::BeginBatch() failed, but ignored");
}

NS_IMETHODIMP EditorBase::EndTransaction() {
  AutoEditActionDataSetter editActionData(*this, EditAction::eUnknown);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_FAILURE;
  }

  EndTransactionInternal(__FUNCTION__);
  return NS_OK;
}

void EditorBase::EndTransactionInternal(const char* aRequesterFuncName) {
  if (mTransactionManager) {
    RefPtr<TransactionManager> transactionManager(mTransactionManager);
    DebugOnly<nsresult> rvIgnored = transactionManager->EndBatch(false);
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                         "TransactionManager::EndBatch() failed, but ignored");
  }

  EndUpdateViewBatch(aRequesterFuncName);
}

void EditorBase::BeginPlaceholderTransaction(nsStaticAtom& aTransactionName,
                                             const char* aRequesterFuncName) {
  MOZ_ASSERT(IsEditActionDataAvailable());
  MOZ_ASSERT(mPlaceholderBatch >= 0, "negative placeholder batch count!");

  if (!mPlaceholderBatch) {
    NotifyEditorObservers(eNotifyEditorObserversOfBefore);
    // time to turn on the batch
    BeginUpdateViewBatch(aRequesterFuncName);
    mPlaceholderTransaction = nullptr;
    mPlaceholderName = &aTransactionName;
    mSelState.emplace();
    mSelState->SaveSelection(SelectionRef());
    // Composition transaction can modify multiple nodes and it merges text
    // node for ime into single text node.
    // So if current selection is into IME text node, it might be failed
    // to restore selection by UndoTransaction.
    // So we need update selection by range updater.
    if (mPlaceholderName == nsGkAtoms::IMETxnName) {
      RangeUpdaterRef().RegisterSelectionState(*mSelState);
    }
  }
  mPlaceholderBatch++;
}

void EditorBase::EndPlaceholderTransaction(
    ScrollSelectionIntoView aScrollSelectionIntoView,
    const char* aRequesterFuncName) {
  MOZ_ASSERT(IsEditActionDataAvailable());
  MOZ_ASSERT(mPlaceholderBatch > 0,
             "zero or negative placeholder batch count when ending batch!");

  if (!(--mPlaceholderBatch)) {
    // By making the assumption that no reflow happens during the calls
    // to EndUpdateViewBatch and ScrollSelectionFocusIntoView, we are able to
    // allow the selection to cache a frame offset which is used by the
    // caret drawing code. We only enable this cache here; at other times,
    // we have no way to know whether reflow invalidates it
    // See bugs 35296 and 199412.
    SelectionRef().SetCanCacheFrameOffset(true);

    // time to turn off the batch
    EndUpdateViewBatch(aRequesterFuncName);
    // make sure selection is in view

    // After ScrollSelectionFocusIntoView(), the pending notifications might be
    // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
    // XXX Even if we're destroyed, we need to keep handling below because
    //     this method changes a lot of status.  We should rewrite this safer.
    if (aScrollSelectionIntoView == ScrollSelectionIntoView::Yes) {
      DebugOnly<nsresult> rvIgnored = ScrollSelectionFocusIntoView();
      NS_WARNING_ASSERTION(
          NS_SUCCEEDED(rvIgnored),
          "EditorBase::ScrollSelectionFocusIntoView() failed, but Ignored");
    }

    // cached for frame offset are Not available now
    SelectionRef().SetCanCacheFrameOffset(false);

    if (mSelState) {
      // we saved the selection state, but never got to hand it to placeholder
      // (else we ould have nulled out this pointer), so destroy it to prevent
      // leaks.
      if (mPlaceholderName == nsGkAtoms::IMETxnName) {
        RangeUpdaterRef().DropSelectionState(*mSelState);
      }
      mSelState.reset();
    }
    // We might have never made a placeholder if no action took place.
    if (mPlaceholderTransaction) {
      // FYI: Disconnect placeholder transaction before dispatching "input"
      //      event because an input event listener may start other things.
      // TODO: We should forget EditActionDataSetter too.
      RefPtr<PlaceholderTransaction> placeholderTransaction =
          std::move(mPlaceholderTransaction);
      DebugOnly<nsresult> rvIgnored =
          placeholderTransaction->EndPlaceHolderBatch();
      NS_WARNING_ASSERTION(
          NS_SUCCEEDED(rvIgnored),
          "PlaceholderTransaction::EndPlaceHolderBatch() failed, but ignored");
      // notify editor observers of action but if composing, it's done by
      // compositionchange event handler.
      if (!mComposition) {
        NotifyEditorObservers(eNotifyEditorObserversOfEnd);
      }
    } else {
      NotifyEditorObservers(eNotifyEditorObserversOfCancel);
    }
  }
}

NS_IMETHODIMP EditorBase::GetDocumentIsEmpty(bool* aDocumentIsEmpty) {
  MOZ_ASSERT(aDocumentIsEmpty);
  *aDocumentIsEmpty = IsEmpty();
  return NS_OK;
}

// XXX: The rule system should tell us which node to select all on (ie, the
//      root, or the body)
NS_IMETHODIMP EditorBase::SelectAll() {
  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  nsresult rv = SelectAllInternal();
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "SelectAllInternal() failed");
  // This is low level API for XUL applcation.  So, we should return raw
  // error code here.
  return rv;
}

nsresult EditorBase::SelectAllInternal() {
  MOZ_ASSERT(IsInitialized());

  DebugOnly<nsresult> rvIgnored = CommitComposition();
  if (NS_WARN_IF(Destroyed())) {
    return NS_ERROR_EDITOR_DESTROYED;
  }
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
                       "EditorBase::CommitComposition() failed, but ignored");

  // XXX Do we need to keep handling after committing composition causes moving
  //     focus to different element?  Although TextEditor has independent
  //     selection, so, we may not see any odd behavior even in such case.

  nsresult rv = SelectEntireDocument();
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorBase::SelectEntireDocument() failed");
  return rv;
}

MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP EditorBase::BeginningOfDocument() {
  MOZ_ASSERT(IsTextEditor());

  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  // get the root element
  RefPtr<Element> rootElement = GetRoot();
  if (NS_WARN_IF(!rootElement)) {
    return NS_ERROR_NULL_POINTER;
  }

  // find first editable thingy
  nsCOMPtr<nsIContent> firstEditableLeaf;
  // If we're `TextEditor`, the first editable leaf node is a text node or
  // padding `<br>` element.  In the first case, we need to collapse selection
  // into it.
  if (rootElement->GetFirstChild() && rootElement->GetFirstChild()->IsText()) {
    firstEditableLeaf = rootElement->GetFirstChild();
  }
  if (!firstEditableLeaf) {
    // just the root node, set selection to inside the root
    nsresult rv = CollapseSelectionToStartOf(*rootElement);
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                         "EditorBase::CollapseSelectionToStartOf() failed");
    return rv;
  }

  if (firstEditableLeaf->IsText()) {
    // If firstEditableLeaf is text, set selection to beginning of the text
    // node.
    nsresult rv = CollapseSelectionToStartOf(*firstEditableLeaf);
    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                         "EditorBase::CollapseSelectionToStartOf() failed");
    return rv;
  }

  // Otherwise, it's a leaf node and we set the selection just in front of it.
  nsCOMPtr<nsIContent> parent = firstEditableLeaf->GetParent();
  if (NS_WARN_IF(!parent)) {
    return NS_ERROR_NULL_POINTER;
  }

  MOZ_ASSERT(
      parent->ComputeIndexOf(firstEditableLeaf).valueOr(UINT32_MAX) == 0,
      "How come the first node isn't the left most child in its parent?");
  nsresult rv = CollapseSelectionToStartOf(*parent);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorBase::CollapseSelectionToStartOf() failed");
  return rv;
}

NS_IMETHODIMP EditorBase::EndOfDocument() { return NS_ERROR_NOT_IMPLEMENTED; }

NS_IMETHODIMP EditorBase::GetDocumentModified(bool* aOutDocModified) {
  if (NS_WARN_IF(!aOutDocModified)) {
    return NS_ERROR_INVALID_ARG;
  }

  int32_t modCount = 0;
  DebugOnly<nsresult> rvIgnored = GetModificationCount(&modCount);
  NS_WARNING_ASSERTION(
      NS_SUCCEEDED(rvIgnored),
      "EditorBase::GetModificationCount() failed, but ignored");

  *aOutDocModified = (modCount != 0);
  return NS_OK;
}

NS_IMETHODIMP EditorBase::GetDocumentCharacterSet(nsACString& aCharacterSet) {
  return NS_ERROR_NOT_AVAILABLE;
}

nsresult EditorBase::GetDocumentCharsetInternal(nsACString& aCharset) const {
  Document* document = GetDocument();
  if (NS_WARN_IF(!document)) {
    return NS_ERROR_NOT_INITIALIZED;
  }
  document->GetDocumentCharacterSet()->Name(aCharset);
  return NS_OK;
}

NS_IMETHODIMP EditorBase::SetDocumentCharacterSet(
    const nsACString& aCharacterSet) {
  return NS_ERROR_NOT_AVAILABLE;
}

NS_IMETHODIMP EditorBase::OutputToString(const nsAString& aFormatType,
                                         uint32_t aDocumentEncoderFlags,
                                         nsAString& aOutputString) {
  AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  nsresult rv =
      ComputeValueInternal(aFormatType, aDocumentEncoderFlags, aOutputString);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "EditorBase::ComputeValueInternal() failed");
  // This is low level API for XUL application.  So, we should return raw
  // error code here.
  return rv;
}

nsresult EditorBase::ComputeValueInternal(const nsAString& aFormatType,
                                          uint32_t aDocumentEncoderFlags,
                                          nsAString& aOutputString) const {
  MOZ_ASSERT(IsEditActionDataAvailable());

  // First, let's try to get the value simply only from text node if the
  // caller wants plaintext value.
  if (aFormatType.LowerCaseEqualsLiteral("text/plain") &&
      !(aDocumentEncoderFlags & (nsIDocumentEncoder::OutputSelectionOnly |
                                 nsIDocumentEncoder::OutputWrap))) {
    // Shortcut for empty editor case.
    if (IsEmpty()) {
      aOutputString.Truncate();
      return NS_OK;
    }
    // NOTE: 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 (IsTextEditor()) {
      // 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?
      Result<EditActionResult, nsresult> result =
          AsTextEditor()->ComputeValueFromTextNodeAndBRElement(aOutputString);
      if (MOZ_UNLIKELY(result.isErr())) {
        NS_WARNING("TextEditor::ComputeValueFromTextNodeAndBRElement() failed");
        return result.unwrapErr();
      }
      if (!result.inspect().Ignored()) {
        return NS_OK;
      }
    }
  }

  nsAutoCString charset;
  nsresult rv = GetDocumentCharsetInternal(charset);
  if (NS_FAILED(rv) || charset.IsEmpty()) {
    charset.AssignLiteral("windows-1252");  // XXX Why don't we use "UTF-8"?
  }

  nsCOMPtr<nsIDocumentEncoder> encoder =
      GetAndInitDocEncoder(aFormatType, aDocumentEncoderFlags, charset);
  if (!encoder) {
    NS_WARNING("EditorBase::GetAndInitDocEncoder() failed");
    return NS_ERROR_FAILURE;
  }

  rv = encoder->EncodeToString(aOutputString);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "nsIDocumentEncoder::EncodeToString() failed");
  return rv;
}

already_AddRefed<nsIDocumentEncoder> EditorBase::GetAndInitDocEncoder(
    const nsAString& aFormatType, uint32_t aDocumentEncoderFlags,
    const nsACString& aCharset) const {
  MOZ_ASSERT(IsEditActionDataAvailable());

  nsCOMPtr<nsIDocumentEncoder> 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<Document> 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<nsresult> rvIgnored = docEncoder->SetCharset(aCharset);
    NS_WARNING_ASSERTION(
        NS_SUCCEEDED(rvIgnored),
        "nsIDocumentEncoder::SetCharset() failed, but ignored");
  }

  const int32_t wrapWidth = std::max(WrapWidth(), 0);
  DebugOnly<nsresult> 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();
}

bool EditorBase::CheckForClipboardCommandListener(
    nsAtom* aCommand, EventMessage aEventMessage) const {
  RefPtr<Document> document = GetDocument();
  if (!document) {
    return false;
  }

  // We exclude XUL and chrome docs here to maintain current behavior where
  // in these cases the editor element alone is expected to handle clipboard
  // command availability.
  if (!document->AreClipboardCommandsUnconditionallyEnabled()) {
    return false;
  }

  // So in web content documents, "unconditionally" enabled Cut/Copy are not
  // really unconditional; they're enabled if there is a listener that wants
  // to handle them. What they're not conditional on here is whether there is
  // currently a selection in the editor.
  RefPtr<PresShell> presShell = document->GetObservingPresShell();
  if (!presShell) {
    return false;
  }
  RefPtr<nsPresContext> presContext = presShell->GetPresContext();
  if (!presContext) {
    return false;
  }

  RefPtr<EventTarget> et = IsHTMLEditor()
                               ? AsHTMLEditor()->ComputeEditingHost(
                                     HTMLEditor::LimitInBodyElement::No)
                               : GetDOMEventTarget();

  while (et) {
    EventListenerManager* elm = et->GetExistingListenerManager();
    if (elm && elm->HasListenersFor(aCommand)) {
      return true;
    }
    InternalClipboardEvent event(true, aEventMessage);
    EventChainPreVisitor visitor(presContext, &event, nullptr,
                                 nsEventStatus_eIgnore, false, et);
    et->GetEventTargetParent(visitor);
    et = visitor.GetParentTarget();
  }

  return false;
}

already_AddRefed<DataTransfer> EditorBase::CreateDataTransferForPaste(
    EventMessage aEventMessage,
    nsIClipboard::ClipboardType aClipboardType) const {
  nsIGlobalObject* scopeObject = nullptr;
  if (PresShell* presShell = GetPresShell()) {
    if (Document* doc = presShell->GetDocument()) {
      scopeObject = doc->GetScopeObject();
    }
  }

  auto dataTransfer = MakeRefPtr<DataTransfer>(scopeObject, aEventMessage, true,
                                               Some(aClipboardType));
  return dataTransfer.forget();
}

Result<EditorBase::ClipboardEventResult, nsresult>
EditorBase::DispatchClipboardEventAndUpdateClipboard(
    EventMessage aEventMessage,
    Maybe<nsIClipboard::ClipboardType> aClipboardType,
    DataTransfer* aDataTransfer /* = nullptr */) {
  MOZ_ASSERT(IsEditActionDataAvailable());

  // Clipboard events are fired before `beforeinput` event.  Therefore, we
  // need to forget mLastCollapsibleWhiteSpaceAppendedTextNode here to avoid
  // infinite loop caused by the hack.
  if (IsHTMLEditor()) {
    AsHTMLEditor()->mLastCollapsibleWhiteSpaceAppendedTextNode = nullptr;
  }

  const bool isPasting =
      aEventMessage == ePaste || aEventMessage == ePasteNoFormatting;
  if (isPasting) {
    CommitComposition();
    if (NS_WARN_IF(Destroyed())) {
      return Err(NS_ERROR_EDITOR_DESTROYED);
    }
  }

  RefPtr<PresShell> presShell = GetPresShell();
  if (NS_WARN_IF(!presShell)) {
    return Err(NS_ERROR_NOT_AVAILABLE);
  }

  const RefPtr<Selection> sel = [&]() {
    if (IsHTMLEditor() && aEventMessage == eCopy &&
        SelectionRef().IsCollapsed()) {
      // If we don't have a usable selection for copy and we're an HTML
      // editor (which is global for the document) try to use the last
      // focused selection instead.
      return nsCopySupport::GetSelectionForCopy(GetDocument());
    }
    return do_AddRef(&SelectionRef());
  }();

  bool actionTaken = false;
  const bool doDefault = nsCopySupport::FireClipboardEvent(
      aEventMessage, aClipboardType, presShell, sel, aDataTransfer,
      &actionTaken);
  NotifyOfDispatchingClipboardEvent();

  if (NS_WARN_IF(Destroyed())) {
    return Err(NS_ERROR_EDITOR_DESTROYED);
  }

  if (doDefault) {
    MOZ_ASSERT(actionTaken);
    return ClipboardEventResult::DoDefault;
  }
  // If we handle a "paste" and nsCopySupport::FireClipboardEvent sets
  // actionTaken to "false" means that it's an error.  Otherwise, the "paste"
  // event is just canceled.
  if (isPasting) {
    return actionTaken ? ClipboardEventResult::DefaultPreventedOfPaste
                       : ClipboardEventResult::IgnoredOrError;
  }
  // If we handle a "copy", actionTaken is set to true only when
  // nsCopySupport::FireClipboardEvent does not meet an error.
  // If we handle a "cut", actionTaken is set to true only when
  // nsCopySupport::FireClipboardEvent does not meet an error and
  // - the selection is collapsed in editable elements when the event is not
  //   canceled.
  // - the event is canceled but update the clipboard with the dataTransfer
  //   of the event.
  return actionTaken ? ClipboardEventResult::CopyOrCutHandled
                     : ClipboardEventResult::IgnoredOrError;
}

NS_IMETHODIMP EditorBase::Cut() {
  nsresult rv = CutAsAction();
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "EditorBase::CutAsAction() failed");
  return rv;
}

nsresult EditorBase::CutAsAction(nsIPrincipal* aPrincipal) {
  AutoEditActionDataSetter editActionData(*this, EditAction::eCut, aPrincipal);
  if (NS_WARN_IF(!editActionData.CanHandle())) {
    return NS_ERROR_NOT_INITIALIZED;
  }

  {
    RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
    if (NS_WARN_IF(!focusManager)) {
      return NS_ERROR_UNEXPECTED;
    }
    const RefPtr<Element> focusedElement = focusManager->GetFocusedElement();

    Result<ClipboardEventResult, nsresult> ret =
        DispatchClipboardEventAndUpdateClipboard(
            eCut, Some(nsIClipboard::kGlobalClipboard));
    if (MOZ_UNLIKELY(ret.isErr())) {
      NS_WARNING(
          "EditorBase::DispatchClipboardEventAndUpdateClipboard(eCut, "
          "nsIClipboard::kGlobalClipboard) failed");
      return EditorBase::ToGenericNSResult(ret.unwrapErr());
    }
    switch (ret.unwrap()) {
      case ClipboardEventResult::DoDefault:
        break;
      case ClipboardEventResult::CopyOrCutHandled:
        return NS_OK;
      case ClipboardEventResult::IgnoredOrError:
--> --------------------

--> maximum size reached

--> --------------------

99%


¤ Dauer der Verarbeitung: 0.33 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.