michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0:
michael@0: #ifndef nsTextEditRules_h__
michael@0: #define nsTextEditRules_h__
michael@0:
michael@0: #include "nsCOMPtr.h"
michael@0: #include "nsCycleCollectionParticipant.h"
michael@0: #include "nsEditRules.h"
michael@0: #include "nsEditor.h"
michael@0: #include "nsIEditor.h"
michael@0: #include "nsISupportsImpl.h"
michael@0: #include "nsITimer.h"
michael@0: #include "nsPlaintextEditor.h"
michael@0: #include "nsString.h"
michael@0: #include "nscore.h"
michael@0:
michael@0: class nsIDOMElement;
michael@0: class nsIDOMNode;
michael@0: class nsISelection;
michael@0: namespace mozilla {
michael@0: namespace dom {
michael@0: class Selection;
michael@0: } // namespace dom
michael@0: } // namespace mozilla
michael@0:
michael@0: /** Object that encapsulates HTML text-specific editing rules.
michael@0: *
michael@0: * To be a good citizen, edit rules must live by these restrictions:
michael@0: * 1. All data manipulation is through the editor.
michael@0: * Content nodes in the document tree must not be manipulated directly.
michael@0: * Content nodes in document fragments that are not part of the document itself
michael@0: * may be manipulated at will. Operations on document fragments must not
michael@0: * go through the editor.
michael@0: * 2. Selection must not be explicitly set by the rule method.
michael@0: * Any manipulation of Selection must be done by the editor.
michael@0: */
michael@0: class nsTextEditRules : public nsIEditRules, public nsITimerCallback
michael@0: {
michael@0: public:
michael@0: NS_DECL_NSITIMERCALLBACK
michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS
michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTextEditRules, nsIEditRules)
michael@0:
michael@0: nsTextEditRules();
michael@0: virtual ~nsTextEditRules();
michael@0:
michael@0: // nsIEditRules methods
michael@0: NS_IMETHOD Init(nsPlaintextEditor *aEditor);
michael@0: NS_IMETHOD SetInitialValue(const nsAString& aValue);
michael@0: NS_IMETHOD DetachEditor();
michael@0: NS_IMETHOD BeforeEdit(EditAction action,
michael@0: nsIEditor::EDirection aDirection);
michael@0: NS_IMETHOD AfterEdit(EditAction action,
michael@0: nsIEditor::EDirection aDirection);
michael@0: NS_IMETHOD WillDoAction(mozilla::dom::Selection* aSelection,
michael@0: nsRulesInfo* aInfo, bool* aCancel, bool* aHandled);
michael@0: NS_IMETHOD DidDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
michael@0: NS_IMETHOD DocumentIsEmpty(bool *aDocumentIsEmpty);
michael@0: NS_IMETHOD DocumentModified();
michael@0:
michael@0: public:
michael@0: void ResetIMETextPWBuf();
michael@0:
michael@0: /**
michael@0: * Handles the newline characters either according to aNewLineHandling
michael@0: * or to the default system prefs if aNewLineHandling is negative.
michael@0: *
michael@0: * @param aString the string to be modified in place.
michael@0: * @param aNewLineHandling determine the desired type of newline handling:
michael@0: * * negative values:
michael@0: * handle newlines according to platform defaults.
michael@0: * * nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
michael@0: * replace newlines with spaces.
michael@0: * * nsIPlaintextEditor::eNewlinesStrip:
michael@0: * remove newlines from the string.
michael@0: * * nsIPlaintextEditor::eNewlinesReplaceWithCommas:
michael@0: * replace newlines with commas.
michael@0: * * nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace:
michael@0: * collapse newlines and surrounding whitespace characters and
michael@0: * remove them from the string.
michael@0: * * nsIPlaintextEditor::eNewlinesPasteIntact:
michael@0: * only remove the leading and trailing newlines.
michael@0: * * nsIPlaintextEditor::eNewlinesPasteToFirst or any other value:
michael@0: * remove the first newline and all characters following it.
michael@0: */
michael@0: static void HandleNewLines(nsString &aString, int32_t aNewLineHandling);
michael@0:
michael@0: /**
michael@0: * Prepare a string buffer for being displayed as the contents of a password
michael@0: * field. This function uses the platform-specific character for representing
michael@0: * characters entered into password fields.
michael@0: *
michael@0: * @param aOutString the output string. When this function returns,
michael@0: * aOutString will contain aLength password characters.
michael@0: * @param aLength the number of password characters that aOutString should
michael@0: * contain.
michael@0: */
michael@0: static void FillBufWithPWChars(nsAString *aOutString, int32_t aLength);
michael@0:
michael@0: protected:
michael@0:
michael@0: void InitFields();
michael@0:
michael@0: // nsTextEditRules implementation methods
michael@0: nsresult WillInsertText( EditAction aAction,
michael@0: mozilla::dom::Selection* aSelection,
michael@0: bool *aCancel,
michael@0: bool *aHandled,
michael@0: const nsAString *inString,
michael@0: nsAString *outString,
michael@0: int32_t aMaxLength);
michael@0: nsresult DidInsertText(nsISelection *aSelection, nsresult aResult);
michael@0: nsresult GetTopEnclosingPre(nsIDOMNode *aNode, nsIDOMNode** aOutPreNode);
michael@0:
michael@0: nsresult WillInsertBreak(mozilla::dom::Selection* aSelection, bool* aCancel,
michael@0: bool *aHandled, int32_t aMaxLength);
michael@0: nsresult DidInsertBreak(nsISelection *aSelection, nsresult aResult);
michael@0:
michael@0: nsresult WillInsert(nsISelection *aSelection, bool *aCancel);
michael@0: nsresult DidInsert(nsISelection *aSelection, nsresult aResult);
michael@0:
michael@0: nsresult WillDeleteSelection(mozilla::dom::Selection* aSelection,
michael@0: nsIEditor::EDirection aCollapsedAction,
michael@0: bool *aCancel,
michael@0: bool *aHandled);
michael@0: nsresult DidDeleteSelection(nsISelection *aSelection,
michael@0: nsIEditor::EDirection aCollapsedAction,
michael@0: nsresult aResult);
michael@0:
michael@0: nsresult WillSetTextProperty(nsISelection *aSelection, bool *aCancel, bool *aHandled);
michael@0: nsresult DidSetTextProperty(nsISelection *aSelection, nsresult aResult);
michael@0:
michael@0: nsresult WillRemoveTextProperty(nsISelection *aSelection, bool *aCancel, bool *aHandled);
michael@0: nsresult DidRemoveTextProperty(nsISelection *aSelection, nsresult aResult);
michael@0:
michael@0: nsresult WillUndo(nsISelection *aSelection, bool *aCancel, bool *aHandled);
michael@0: nsresult DidUndo(nsISelection *aSelection, nsresult aResult);
michael@0:
michael@0: nsresult WillRedo(nsISelection *aSelection, bool *aCancel, bool *aHandled);
michael@0: nsresult DidRedo(nsISelection *aSelection, nsresult aResult);
michael@0:
michael@0: /** called prior to nsIEditor::OutputToString
michael@0: * @param aSelection
michael@0: * @param aInFormat the format requested for the output, a MIME type
michael@0: * @param aOutText the string to use for output, if aCancel is set to true
michael@0: * @param aOutCancel if set to true, the caller should cancel the operation
michael@0: * and use aOutText as the result.
michael@0: */
michael@0: nsresult WillOutputText(nsISelection *aSelection,
michael@0: const nsAString *aInFormat,
michael@0: nsAString *aOutText,
michael@0: bool *aOutCancel,
michael@0: bool *aHandled);
michael@0:
michael@0: nsresult DidOutputText(nsISelection *aSelection, nsresult aResult);
michael@0:
michael@0:
michael@0: // helper functions
michael@0:
michael@0: /** check for and replace a redundant trailing break */
michael@0: nsresult RemoveRedundantTrailingBR();
michael@0:
michael@0: /** creates a trailing break in the text doc if there is not one already */
michael@0: nsresult CreateTrailingBRIfNeeded();
michael@0:
michael@0: /** creates a bogus text node if the document has no editable content */
michael@0: nsresult CreateBogusNodeIfNeeded(nsISelection *aSelection);
michael@0:
michael@0: /** returns a truncated insertion string if insertion would place us
michael@0: over aMaxLength */
michael@0: nsresult TruncateInsertionIfNeeded(mozilla::dom::Selection* aSelection,
michael@0: const nsAString *aInString,
michael@0: nsAString *aOutString,
michael@0: int32_t aMaxLength,
michael@0: bool *aTruncated);
michael@0:
michael@0: /** Remove IME composition text from password buffer */
michael@0: void RemoveIMETextFromPWBuf(int32_t &aStart, nsAString *aIMEString);
michael@0:
michael@0: nsresult CreateMozBR(nsIDOMNode* inParent, int32_t inOffset,
michael@0: nsIDOMNode** outBRNode = nullptr);
michael@0:
michael@0: nsresult CheckBidiLevelForDeletion(nsISelection *aSelection,
michael@0: nsIDOMNode *aSelNode,
michael@0: int32_t aSelOffset,
michael@0: nsIEditor::EDirection aAction,
michael@0: bool *aCancel);
michael@0:
michael@0: nsresult HideLastPWInput();
michael@0:
michael@0: nsresult CollapseSelectionToTrailingBRIfNeeded(nsISelection *aSelection);
michael@0:
michael@0: bool IsPasswordEditor() const
michael@0: {
michael@0: return mEditor ? mEditor->IsPasswordEditor() : false;
michael@0: }
michael@0: bool IsSingleLineEditor() const
michael@0: {
michael@0: return mEditor ? mEditor->IsSingleLineEditor() : false;
michael@0: }
michael@0: bool IsPlaintextEditor() const
michael@0: {
michael@0: return mEditor ? mEditor->IsPlaintextEditor() : false;
michael@0: }
michael@0: bool IsReadonly() const
michael@0: {
michael@0: return mEditor ? mEditor->IsReadonly() : false;
michael@0: }
michael@0: bool IsDisabled() const
michael@0: {
michael@0: return mEditor ? mEditor->IsDisabled() : false;
michael@0: }
michael@0: bool IsMailEditor() const
michael@0: {
michael@0: return mEditor ? mEditor->IsMailEditor() : false;
michael@0: }
michael@0: bool DontEchoPassword() const
michael@0: {
michael@0: return mEditor ? mEditor->DontEchoPassword() : false;
michael@0: }
michael@0:
michael@0: // data members
michael@0: nsPlaintextEditor *mEditor; // note that we do not refcount the editor
michael@0: nsString mPasswordText; // a buffer we use to store the real value of password editors
michael@0: nsString mPasswordIMEText; // a buffer we use to track the IME composition string
michael@0: uint32_t mPasswordIMEIndex;
michael@0: nsCOMPtr mBogusNode; // magic node acts as placeholder in empty doc
michael@0: nsCOMPtr mCachedSelectionNode; // cached selected node
michael@0: int32_t mCachedSelectionOffset; // cached selected offset
michael@0: uint32_t mActionNesting;
michael@0: bool mLockRulesSniffing;
michael@0: bool mDidExplicitlySetInterline;
michael@0: bool mDeleteBidiImmediately; // in bidirectional text, delete
michael@0: // characters not visually
michael@0: // adjacent to the caret without
michael@0: // moving the caret first.
michael@0: EditAction mTheAction; // the top level editor action
michael@0: nsCOMPtr mTimer;
michael@0: uint32_t mLastStart, mLastLength;
michael@0:
michael@0: // friends
michael@0: friend class nsAutoLockRulesSniffing;
michael@0:
michael@0: };
michael@0:
michael@0:
michael@0:
michael@0: class nsTextRulesInfo : public nsRulesInfo
michael@0: {
michael@0: public:
michael@0:
michael@0: nsTextRulesInfo(EditAction aAction) :
michael@0: nsRulesInfo(aAction),
michael@0: inString(0),
michael@0: outString(0),
michael@0: outputFormat(0),
michael@0: maxLength(-1),
michael@0: collapsedAction(nsIEditor::eNext),
michael@0: stripWrappers(nsIEditor::eStrip),
michael@0: bOrdered(false),
michael@0: entireList(false),
michael@0: bulletType(0),
michael@0: alignType(0),
michael@0: blockType(0),
michael@0: insertElement(0)
michael@0: {}
michael@0:
michael@0: virtual ~nsTextRulesInfo() {}
michael@0:
michael@0: // kInsertText
michael@0: const nsAString *inString;
michael@0: nsAString *outString;
michael@0: const nsAString *outputFormat;
michael@0: int32_t maxLength;
michael@0:
michael@0: // kDeleteSelection
michael@0: nsIEditor::EDirection collapsedAction;
michael@0: nsIEditor::EStripWrappers stripWrappers;
michael@0:
michael@0: // kMakeList
michael@0: bool bOrdered;
michael@0: bool entireList;
michael@0: const nsAString *bulletType;
michael@0:
michael@0: // kAlign
michael@0: const nsAString *alignType;
michael@0:
michael@0: // kMakeBasicBlock
michael@0: const nsAString *blockType;
michael@0:
michael@0: // kInsertElement
michael@0: const nsIDOMElement* insertElement;
michael@0: };
michael@0:
michael@0:
michael@0: /***************************************************************************
michael@0: * stack based helper class for StartOperation()/EndOperation() sandwich.
michael@0: * this class sets a bool letting us know to ignore any rules sniffing
michael@0: * that tries to occur reentrantly.
michael@0: */
michael@0: class nsAutoLockRulesSniffing
michael@0: {
michael@0: public:
michael@0:
michael@0: nsAutoLockRulesSniffing(nsTextEditRules *rules) : mRules(rules)
michael@0: {if (mRules) mRules->mLockRulesSniffing = true;}
michael@0: ~nsAutoLockRulesSniffing()
michael@0: {if (mRules) mRules->mLockRulesSniffing = false;}
michael@0:
michael@0: protected:
michael@0: nsTextEditRules *mRules;
michael@0: };
michael@0:
michael@0:
michael@0:
michael@0: /***************************************************************************
michael@0: * stack based helper class for turning on/off the edit listener.
michael@0: */
michael@0: class nsAutoLockListener
michael@0: {
michael@0: public:
michael@0:
michael@0: nsAutoLockListener(bool *enabled) : mEnabled(enabled)
michael@0: {if (mEnabled) { mOldState=*mEnabled; *mEnabled = false;}}
michael@0: ~nsAutoLockListener()
michael@0: {if (mEnabled) *mEnabled = mOldState;}
michael@0:
michael@0: protected:
michael@0: bool *mEnabled;
michael@0: bool mOldState;
michael@0: };
michael@0:
michael@0: #endif //nsTextEditRules_h__