1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #ifndef mozilla_TextEditRules_h
7 #define mozilla_TextEditRules_h
8 
9 #include "mozilla/EditAction.h"
10 #include "mozilla/EditorDOMPoint.h"
11 #include "nsCOMPtr.h"
12 #include "nsCycleCollectionParticipant.h"
13 #include "nsIEditor.h"
14 #include "nsINamed.h"
15 #include "nsISupportsImpl.h"
16 #include "nsITimer.h"
17 #include "nsString.h"
18 #include "nscore.h"
19 
20 class nsIDOMNode;
21 
22 namespace mozilla {
23 
24 class AutoLockRulesSniffing;
25 class HTMLEditRules;
26 class RulesInfo;
27 class TextEditor;
28 namespace dom {
29 class Selection;
30 }  // namespace dom
31 
32 /**
33  * Object that encapsulates HTML text-specific editing rules.
34  *
35  * To be a good citizen, edit rules must live by these restrictions:
36  * 1. All data manipulation is through the editor.
37  *    Content nodes in the document tree must <B>not</B> be manipulated
38  *    directly.  Content nodes in document fragments that are not part of the
39  *    document itself may be manipulated at will.  Operations on document
40  *    fragments must <B>not</B> go through the editor.
41  * 2. Selection must not be explicitly set by the rule method.
42  *    Any manipulation of Selection must be done by the editor.
43  */
44 class TextEditRules : public nsITimerCallback, public nsINamed {
45  public:
46   typedef dom::Element Element;
47   typedef dom::Selection Selection;
48   typedef dom::Text Text;
49   template <typename T>
50   using OwningNonNull = OwningNonNull<T>;
51 
52   NS_DECL_NSITIMERCALLBACK
53   NS_DECL_NSINAMED
54   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
55   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(TextEditRules, nsITimerCallback)
56 
57   TextEditRules();
58 
59   HTMLEditRules* AsHTMLEditRules();
60   const HTMLEditRules* AsHTMLEditRules() const;
61 
62   virtual nsresult Init(TextEditor* aTextEditor);
63   virtual nsresult SetInitialValue(const nsAString& aValue);
64   virtual nsresult DetachEditor();
65   virtual nsresult BeforeEdit(EditAction aAction,
66                               nsIEditor::EDirection aDirection);
67   virtual nsresult AfterEdit(EditAction aAction,
68                              nsIEditor::EDirection aDirection);
69   virtual nsresult WillDoAction(Selection* aSelection, RulesInfo* aInfo,
70                                 bool* aCancel, bool* aHandled);
71   virtual nsresult DidDoAction(Selection* aSelection, RulesInfo* aInfo,
72                                nsresult aResult);
73   virtual bool DocumentIsEmpty();
74   virtual nsresult DocumentModified();
75 
76  protected:
77   virtual ~TextEditRules();
78 
79  public:
80   void ResetIMETextPWBuf();
81 
82   /**
83    * Handles the newline characters either according to aNewLineHandling
84    * or to the default system prefs if aNewLineHandling is negative.
85    *
86    * @param aString the string to be modified in place.
87    * @param aNewLineHandling determine the desired type of newline handling:
88    *        * negative values:
89    *          handle newlines according to platform defaults.
90    *        * nsIPlaintextEditor::eNewlinesReplaceWithSpaces:
91    *          replace newlines with spaces.
92    *        * nsIPlaintextEditor::eNewlinesStrip:
93    *          remove newlines from the string.
94    *        * nsIPlaintextEditor::eNewlinesReplaceWithCommas:
95    *          replace newlines with commas.
96    *        * nsIPlaintextEditor::eNewlinesStripSurroundingWhitespace:
97    *          collapse newlines and surrounding whitespace characters and
98    *          remove them from the string.
99    *        * nsIPlaintextEditor::eNewlinesPasteIntact:
100    *          only remove the leading and trailing newlines.
101    *        * nsIPlaintextEditor::eNewlinesPasteToFirst or any other value:
102    *          remove the first newline and all characters following it.
103    */
104   static void HandleNewLines(nsString& aString, int32_t aNewLineHandling);
105 
106   /**
107    * Prepare a string buffer for being displayed as the contents of a password
108    * field.  This function uses the platform-specific character for representing
109    * characters entered into password fields.
110    *
111    * @param aOutString the output string.  When this function returns,
112    *        aOutString will contain aLength password characters.
113    * @param aLength the number of password characters that aOutString should
114    *        contain.
115    */
116   static void FillBufWithPWChars(nsAString* aOutString, int32_t aLength);
117 
HasBogusNode()118   bool HasBogusNode() { return !!mBogusNode; }
119 
120  protected:
121   void InitFields();
122 
123   // TextEditRules implementation methods
124   nsresult WillInsertText(EditAction aAction, Selection* aSelection,
125                           bool* aCancel, bool* aHandled,
126                           const nsAString* inString, nsAString* outString,
127                           int32_t aMaxLength);
128   nsresult DidInsertText(Selection* aSelection, nsresult aResult);
129 
130   nsresult WillInsertBreak(Selection* aSelection, bool* aCancel, bool* aHandled,
131                            int32_t aMaxLength);
132   nsresult DidInsertBreak(Selection* aSelection, nsresult aResult);
133 
134   nsresult WillSetText(Selection& aSelection, bool* aCancel, bool* aHandled,
135                        const nsAString* inString, int32_t aMaxLength);
136   nsresult DidSetText(Selection& aSelection, nsresult aResult);
137 
138   void WillInsert(Selection& aSelection, bool* aCancel);
139   nsresult DidInsert(Selection* aSelection, nsresult aResult);
140 
141   nsresult WillDeleteSelection(Selection* aSelection,
142                                nsIEditor::EDirection aCollapsedAction,
143                                bool* aCancel, bool* aHandled);
144   nsresult DidDeleteSelection(Selection* aSelection,
145                               nsIEditor::EDirection aCollapsedAction,
146                               nsresult aResult);
147 
148   nsresult WillSetTextProperty(Selection* aSelection, bool* aCancel,
149                                bool* aHandled);
150   nsresult DidSetTextProperty(Selection* aSelection, nsresult aResult);
151 
152   nsresult WillRemoveTextProperty(Selection* aSelection, bool* aCancel,
153                                   bool* aHandled);
154   nsresult DidRemoveTextProperty(Selection* aSelection, nsresult aResult);
155 
156   nsresult WillUndo(Selection* aSelection, bool* aCancel, bool* aHandled);
157   nsresult DidUndo(Selection* aSelection, nsresult aResult);
158 
159   nsresult WillRedo(Selection* aSelection, bool* aCancel, bool* aHandled);
160   nsresult DidRedo(Selection* aSelection, nsresult aResult);
161 
162   /**
163    * Called prior to nsIEditor::OutputToString.
164    * @param aSelection
165    * @param aInFormat  The format requested for the output, a MIME type.
166    * @param aOutText   The string to use for output, if aCancel is set to true.
167    * @param aOutCancel If set to true, the caller should cancel the operation
168    *                   and use aOutText as the result.
169    */
170   nsresult WillOutputText(Selection* aSelection, const nsAString* aInFormat,
171                           nsAString* aOutText, uint32_t aFlags,
172                           bool* aOutCancel, bool* aHandled);
173 
174   nsresult DidOutputText(Selection* aSelection, nsresult aResult);
175 
176   /**
177    * Check for and replace a redundant trailing break.
178    */
179   nsresult RemoveRedundantTrailingBR();
180 
181   /**
182    * Creates a trailing break in the text doc if there is not one already.
183    */
184   nsresult CreateTrailingBRIfNeeded();
185 
186   /**
187    * Creates a bogus text node if the document has no editable content.
188    */
189   nsresult CreateBogusNodeIfNeeded(Selection* aSelection);
190 
191   /**
192    * Returns a truncated insertion string if insertion would place us over
193    * aMaxLength
194    */
195   nsresult TruncateInsertionIfNeeded(Selection* aSelection,
196                                      const nsAString* aInString,
197                                      nsAString* aOutString, int32_t aMaxLength,
198                                      bool* aTruncated);
199 
200   /**
201    * Remove IME composition text from password buffer.
202    */
203   void RemoveIMETextFromPWBuf(uint32_t& aStart, nsAString* aIMEString);
204 
205   /**
206    * Create a normal <br> element and insert it to aPointToInsert.
207    *
208    * @param aPointToInsert  The point where the new <br> element will be
209    *                        inserted.
210    * @return                Returns created <br> element.
211    */
CreateBR(const EditorRawDOMPoint & aPointToInsert)212   already_AddRefed<Element> CreateBR(const EditorRawDOMPoint& aPointToInsert) {
213     return CreateBRInternal(aPointToInsert, false);
214   }
215 
216   /**
217    * Create a moz-<br> element and insert it to aPointToInsert.
218    *
219    * @param aPointToInsert  The point where the new moz-<br> element will be
220    *                        inserted.
221    * @return                Returns created moz-<br> element.
222    */
CreateMozBR(const EditorRawDOMPoint & aPointToInsert)223   already_AddRefed<Element> CreateMozBR(
224       const EditorRawDOMPoint& aPointToInsert) {
225     return CreateBRInternal(aPointToInsert, true);
226   }
227 
228   /**
229    * Create a normal <br> element or a moz-<br> element and insert it to
230    * aPointToInsert.
231    *
232    * @param aParentToInsert     The point where the new <br> element will be
233    *                            inserted.
234    * @param aCreateMozBR        true if the caller wants to create a moz-<br>
235    *                            element.  Otherwise, false.
236    * @return                    Returns created <br> element.
237    */
238   already_AddRefed<Element> CreateBRInternal(
239       const EditorRawDOMPoint& aPointToInsert, bool aCreateMozBR);
240 
241   void UndefineCaretBidiLevel(Selection* aSelection);
242 
243   nsresult CheckBidiLevelForDeletion(Selection* aSelection, nsINode* aSelNode,
244                                      int32_t aSelOffset,
245                                      nsIEditor::EDirection aAction,
246                                      bool* aCancel);
247 
248   nsresult HideLastPWInput();
249 
250   nsresult CollapseSelectionToTrailingBRIfNeeded(Selection* aSelection);
251 
252   bool IsPasswordEditor() const;
253   bool IsSingleLineEditor() const;
254   bool IsPlaintextEditor() const;
255   bool IsReadonly() const;
256   bool IsDisabled() const;
257   bool IsMailEditor() const;
258   bool DontEchoPassword() const;
259 
260  private:
261   // Note that we do not refcount the editor.
262   TextEditor* mTextEditor;
263 
264  protected:
265   // A buffer we use to store the real value of password editors.
266   nsString mPasswordText;
267   // A buffer we use to track the IME composition string.
268   nsString mPasswordIMEText;
269   uint32_t mPasswordIMEIndex;
270   // Magic node acts as placeholder in empty doc.
271   nsCOMPtr<nsIContent> mBogusNode;
272   // Cached selected node.
273   nsCOMPtr<nsINode> mCachedSelectionNode;
274   // Cached selected offset.
275   uint32_t mCachedSelectionOffset;
276   uint32_t mActionNesting;
277   bool mLockRulesSniffing;
278   bool mDidExplicitlySetInterline;
279   // In bidirectional text, delete characters not visually adjacent to the
280   // caret without moving the caret first.
281   bool mDeleteBidiImmediately;
282   bool mIsHTMLEditRules;
283   // The top level editor action.
284   EditAction mTheAction;
285   nsCOMPtr<nsITimer> mTimer;
286   uint32_t mLastStart;
287   uint32_t mLastLength;
288 
289   // friends
290   friend class AutoLockRulesSniffing;
291 };
292 
293 /**
294  * An object to encapsulate any additional info needed to be passed
295  * to rules system by the editor.
296  * TODO: This class (almost struct, though) is ugly and its size isn't
297  *       optimized.  Should be refined later.
298  */
299 class RulesInfo final {
300  public:
RulesInfo(EditAction aAction)301   explicit RulesInfo(EditAction aAction)
302       : action(aAction),
303         inString(nullptr),
304         outString(nullptr),
305         outputFormat(nullptr),
306         maxLength(-1),
307         flags(0),
308         collapsedAction(nsIEditor::eNext),
309         stripWrappers(nsIEditor::eStrip),
310         bOrdered(false),
311         entireList(false),
312         bulletType(nullptr),
313         alignType(nullptr),
314         blockType(nullptr) {}
315 
316   EditAction action;
317 
318   // EditAction::insertText / EditAction::insertIMEText
319   const nsAString* inString;
320   nsAString* outString;
321   const nsAString* outputFormat;
322   int32_t maxLength;
323 
324   // EditAction::outputText
325   uint32_t flags;
326 
327   // EditAction::deleteSelection
328   nsIEditor::EDirection collapsedAction;
329   nsIEditor::EStripWrappers stripWrappers;
330 
331   // EditAction::removeList
332   bool bOrdered;
333 
334   // EditAction::makeList
335   bool entireList;
336   const nsAString* bulletType;
337 
338   // EditAction::align
339   const nsAString* alignType;
340 
341   // EditAction::makeBasicBlock
342   const nsAString* blockType;
343 };
344 
345 /**
346  * Stack based helper class for StartOperation()/EndOperation() sandwich.
347  * This class sets a bool letting us know to ignore any rules sniffing
348  * that tries to occur reentrantly.
349  */
350 class MOZ_STACK_CLASS AutoLockRulesSniffing final {
351  public:
AutoLockRulesSniffing(TextEditRules * aRules)352   explicit AutoLockRulesSniffing(TextEditRules* aRules) : mRules(aRules) {
353     if (mRules) {
354       mRules->mLockRulesSniffing = true;
355     }
356   }
357 
~AutoLockRulesSniffing()358   ~AutoLockRulesSniffing() {
359     if (mRules) {
360       mRules->mLockRulesSniffing = false;
361     }
362   }
363 
364  protected:
365   TextEditRules* mRules;
366 };
367 
368 /**
369  * Stack based helper class for turning on/off the edit listener.
370  */
371 class MOZ_STACK_CLASS AutoLockListener final {
372  public:
AutoLockListener(bool * aEnabled)373   explicit AutoLockListener(bool* aEnabled)
374       : mEnabled(aEnabled), mOldState(false) {
375     if (mEnabled) {
376       mOldState = *mEnabled;
377       *mEnabled = false;
378     }
379   }
380 
~AutoLockListener()381   ~AutoLockListener() {
382     if (mEnabled) {
383       *mEnabled = mOldState;
384     }
385   }
386 
387  protected:
388   bool* mEnabled;
389   bool mOldState;
390 };
391 
392 }  // namespace mozilla
393 
394 #endif  // #ifndef mozilla_TextEditRules_h
395