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__