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_TextEditor_h
7 #define mozilla_TextEditor_h
8
9 #include "mozilla/EditorBase.h"
10 #include "mozilla/TextControlState.h"
11 #include "mozilla/UniquePtr.h"
12
13 #include "nsCOMPtr.h"
14 #include "nsCycleCollectionParticipant.h"
15 #include "nsINamed.h"
16 #include "nsISupportsImpl.h"
17 #include "nsITimer.h"
18 #include "nscore.h"
19
20 class nsIContent;
21 class nsIDocumentEncoder;
22 class nsIOutputStream;
23 class nsIPrincipal;
24 class nsISelectionController;
25 class nsITransferable;
26
27 namespace mozilla {
28 class DeleteNodeTransaction;
29 class InsertNodeTransaction;
30 enum class EditSubAction : int32_t;
31
32 namespace dom {
33 class Selection;
34 } // namespace dom
35
36 /**
37 * The text editor implementation.
38 * Use to edit text document represented as a DOM tree.
39 */
40 class TextEditor final : public EditorBase,
41 public nsITimerCallback,
42 public nsINamed {
43 public:
44 /****************************************************************************
45 * NOTE: DO NOT MAKE YOUR NEW METHODS PUBLIC IF they are called by other
46 * classes under libeditor except EditorEventListener and
47 * HTMLEditorEventListener because each public method which may fire
48 * eEditorInput event will need to instantiate new stack class for
49 * managing input type value of eEditorInput and cache some objects
50 * for smarter handling. In other words, when you add new root
51 * method to edit the DOM tree, you can make your new method public.
52 ****************************************************************************/
53
54 NS_DECL_ISUPPORTS_INHERITED
55 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TextEditor, EditorBase)
56
57 TextEditor();
58
59 /**
60 * Note that TextEditor::Init() shouldn't cause running script synchronously.
61 * So, `MOZ_CAN_RUN_SCRIPT_BOUNDARY` is safe here.
62 *
63 * @param aDocument The document which aAnonymousDivElement belongs to.
64 * @param aAnonymousDivElement
65 * The root editable element for this editor.
66 * @param aSelectionController
67 * The selection controller for independent selections
68 * in the `<input>` or `<textarea>` element.
69 * @param aFlags Some of nsIEditor::eEditor*Mask flags.
70 * @param aPasswordMaskData
71 * Set to an instance only when aFlags includes
72 * `nsIEditor::eEditorPasswordMask`. Otherwise, must be
73 * `nullptr`.
74 */
75 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult
76 Init(Document& aDocument, Element& aAnonymousDivElement,
77 nsISelectionController& aSelectionController, uint32_t aFlags,
78 UniquePtr<PasswordMaskData>&& aPasswordMaskData);
79
80 /**
81 * PostCreate() should be called after Init, and is the time that the editor
82 * tells its documentStateObservers that the document has been created.
83 * Note that TextEditor::PostCreate() shouldn't cause running script
84 * synchronously. So, `MOZ_CAN_RUN_SCRIPT_BOUNDARY` is safe here.
85 */
86 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult PostCreate();
87
88 /**
89 * PreDestroy() is called before the editor goes away, and gives the editor a
90 * chance to tell its documentStateObservers that the document is going away.
91 * Note that TextEditor::PreDestroy() shouldn't cause running script
92 * synchronously. So, `MOZ_CAN_RUN_SCRIPT_BOUNDARY` is safe here.
93 */
94 [[nodiscard]] MOZ_CAN_RUN_SCRIPT_BOUNDARY UniquePtr<PasswordMaskData>
95 PreDestroy();
96
GetFrom(nsIEditor * aEditor)97 static TextEditor* GetFrom(nsIEditor* aEditor) {
98 return aEditor ? aEditor->GetAsTextEditor() : nullptr;
99 }
GetFrom(const nsIEditor * aEditor)100 static const TextEditor* GetFrom(const nsIEditor* aEditor) {
101 return aEditor ? aEditor->GetAsTextEditor() : nullptr;
102 }
103
104 NS_DECL_NSITIMERCALLBACK
105 NS_DECL_NSINAMED
106
107 // Overrides of nsIEditor
108 NS_IMETHOD GetTextLength(uint32_t* aCount) final;
Paste(int32_t aClipboardType)109 MOZ_CAN_RUN_SCRIPT NS_IMETHOD Paste(int32_t aClipboardType) final {
110 const nsresult rv = TextEditor::PasteAsAction(aClipboardType, true);
111 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
112 "HTMLEditor::PasteAsAction() failed");
113 return rv;
114 }
115
116 // Shouldn't be used internally, but we need these using declarations for
117 // avoiding warnings of clang.
118 using EditorBase::CanCopy;
119 using EditorBase::CanCut;
120 using EditorBase::CanPaste;
121
122 // Overrides of EditorBase
123 bool IsEmpty() const final;
124
125 bool CanPaste(int32_t aClipboardType) const final;
126
127 MOZ_CAN_RUN_SCRIPT nsresult PasteTransferableAsAction(
128 nsITransferable* aTransferable, nsIPrincipal* aPrincipal = nullptr) final;
129
130 bool CanPasteTransferable(nsITransferable* aTransferable) final;
131
132 MOZ_CAN_RUN_SCRIPT nsresult
133 HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) final;
134
135 dom::EventTarget* GetDOMEventTarget() const final;
136
137 MOZ_CAN_RUN_SCRIPT nsresult
138 PasteAsAction(int32_t aClipboardType, bool aDispatchPasteEvent,
139 nsIPrincipal* aPrincipal = nullptr) final;
140
141 MOZ_CAN_RUN_SCRIPT nsresult
142 PasteAsQuotationAsAction(int32_t aClipboardType, bool aDispatchPasteEvent,
143 nsIPrincipal* aPrincipal = nullptr) final;
144
145 /**
146 * The maximum number of characters allowed.
147 * default: -1 (unlimited).
148 */
MaxTextLength()149 int32_t MaxTextLength() const { return mMaxTextLength; }
SetMaxTextLength(int32_t aLength)150 void SetMaxTextLength(int32_t aLength) { mMaxTextLength = aLength; }
151
152 /**
153 * Replace existed string with a string.
154 * This is fast path to replace all string when using single line control.
155 *
156 * @param aString The string to be set
157 * @param aAllowBeforeInputEventCancelable
158 * Whether `beforeinput` event which will be
159 * dispatched for this can be cancelable or not.
160 * @param aPrincipal Set subject principal if it may be called by
161 * JS. If set to nullptr, will be treated as
162 * called by system.
163 */
164 MOZ_CAN_RUN_SCRIPT nsresult SetTextAsAction(
165 const nsAString& aString,
166 AllowBeforeInputEventCancelable aAllowBeforeInputEventCancelable,
167 nsIPrincipal* aPrincipal = nullptr);
168
169 MOZ_CAN_RUN_SCRIPT nsresult
170 InsertLineBreakAsAction(nsIPrincipal* aPrincipal = nullptr) final;
171
172 /**
173 * ComputeTextValue() computes plaintext value of this editor. This may be
174 * too expensive if it's in hot path.
175 *
176 * @param aDocumentEncoderFlags Flags of nsIDocumentEncoder.
177 * @param aCharset Encoding of the document.
178 */
ComputeTextValue(uint32_t aDocumentEncoderFlags,nsAString & aOutputString)179 nsresult ComputeTextValue(uint32_t aDocumentEncoderFlags,
180 nsAString& aOutputString) const {
181 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
182 if (NS_WARN_IF(!editActionData.CanHandle())) {
183 return NS_ERROR_NOT_INITIALIZED;
184 }
185 nsresult rv = ComputeValueInternal(u"text/plain"_ns, aDocumentEncoderFlags,
186 aOutputString);
187 if (NS_WARN_IF(NS_FAILED(rv))) {
188 return EditorBase::ToGenericNSResult(rv);
189 }
190 return NS_OK;
191 }
192
193 /**
194 * The following methods are available only when the instance is a password
195 * editor. They return whether there is unmasked range or not and range
196 * start and length.
197 */
IsAllMasked()198 MOZ_ALWAYS_INLINE bool IsAllMasked() const {
199 MOZ_ASSERT(IsPasswordEditor());
200 return !mPasswordMaskData || mPasswordMaskData->IsAllMasked();
201 }
UnmaskedStart()202 MOZ_ALWAYS_INLINE uint32_t UnmaskedStart() const {
203 MOZ_ASSERT(IsPasswordEditor());
204 return mPasswordMaskData ? mPasswordMaskData->mUnmaskedStart : UINT32_MAX;
205 }
UnmaskedLength()206 MOZ_ALWAYS_INLINE uint32_t UnmaskedLength() const {
207 MOZ_ASSERT(IsPasswordEditor());
208 return mPasswordMaskData ? mPasswordMaskData->mUnmaskedLength : 0;
209 }
UnmaskedEnd()210 MOZ_ALWAYS_INLINE uint32_t UnmaskedEnd() const {
211 MOZ_ASSERT(IsPasswordEditor());
212 return mPasswordMaskData ? mPasswordMaskData->UnmaskedEnd() : UINT32_MAX;
213 }
214
215 /**
216 * IsMaskingPassword() returns false when the last caller of `Unmask()`
217 * didn't want to mask again automatically. When this returns true, user
218 * input causes masking the password even before timed-out.
219 */
IsMaskingPassword()220 bool IsMaskingPassword() const {
221 MOZ_ASSERT(IsPasswordEditor());
222 return mPasswordMaskData && mPasswordMaskData->mIsMaskingPassword;
223 }
224
225 /**
226 * PasswordMask() returns a character which masks each character in password
227 * fields.
228 */
229 static char16_t PasswordMask();
230
231 /**
232 * If you want to prevent to echo password temporarily, use the following
233 * methods.
234 */
EchoingPasswordPrevented()235 bool EchoingPasswordPrevented() const {
236 return mPasswordMaskData && mPasswordMaskData->mEchoingPasswordPrevented;
237 }
PreventToEchoPassword()238 void PreventToEchoPassword() {
239 if (mPasswordMaskData) {
240 mPasswordMaskData->mEchoingPasswordPrevented = true;
241 }
242 }
AllowToEchoPassword()243 void AllowToEchoPassword() {
244 if (mPasswordMaskData) {
245 mPasswordMaskData->mEchoingPasswordPrevented = false;
246 }
247 }
248
249 protected: // May be called by friends.
250 /****************************************************************************
251 * Some friend classes are allowed to call the following protected methods.
252 * However, those methods won't prepare caches of some objects which are
253 * necessary for them. So, if you call them from friend classes, you need
254 * to make sure that AutoEditActionDataSetter is created.
255 ****************************************************************************/
256
257 // Overrides of EditorBase
258 MOZ_CAN_RUN_SCRIPT nsresult RemoveAttributeOrEquivalent(
259 Element* aElement, nsAtom* aAttribute, bool aSuppressTransaction) final;
260 MOZ_CAN_RUN_SCRIPT nsresult SetAttributeOrEquivalent(
261 Element* aElement, nsAtom* aAttribute, const nsAString& aValue,
262 bool aSuppressTransaction) final;
263 using EditorBase::RemoveAttributeOrEquivalent;
264 using EditorBase::SetAttributeOrEquivalent;
265
266 /**
267 * Replace existed string with aString. Caller must guarantee that there
268 * is a placeholder transaction which will have the transaction.
269 *
270 * @ param aString The string to be set.
271 */
272 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
273 SetTextAsSubAction(const nsAString& aString);
274
275 /**
276 * MaybeDoAutoPasswordMasking() may mask password if we're doing auto-masking.
277 */
MaybeDoAutoPasswordMasking()278 void MaybeDoAutoPasswordMasking() {
279 if (IsPasswordEditor() && IsMaskingPassword()) {
280 MaskAllCharacters();
281 }
282 }
283
284 /**
285 * SetUnmaskRange() is available only when the instance is a password
286 * editor. This just updates unmask range. I.e., caller needs to
287 * guarantee to update the layout.
288 *
289 * @param aStart First index to show the character.
290 * If aLength is 0, this value is ignored.
291 * @param aLength Optional, Length to show characters.
292 * If UINT32_MAX, it means unmasking all characters after
293 * aStart.
294 * If 0, it means that masking all characters.
295 * @param aTimeout Optional, specify milliseconds to hide the unmasked
296 * characters after this call.
297 * If 0, it means this won't mask the characters
298 * automatically.
299 * If aLength is 0, this value is ignored.
300 */
301 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult SetUnmaskRange(
302 uint32_t aStart, uint32_t aLength = UINT32_MAX, uint32_t aTimeout = 0) {
303 return SetUnmaskRangeInternal(aStart, aLength, aTimeout, false, false);
304 }
305
306 /**
307 * SetUnmaskRangeAndNotify() is available only when the instance is a
308 * password editor. This updates unmask range and notifying the text frame
309 * to update the visible characters.
310 *
311 * @param aStart First index to show the character.
312 * If UINT32_MAX, it means masking all.
313 * @param aLength Optional, Length to show characters.
314 * If UINT32_MAX, it means unmasking all characters after
315 * aStart.
316 * @param aTimeout Optional, specify milliseconds to hide the unmasked
317 * characters after this call.
318 * If 0, it means this won't mask the characters
319 * automatically.
320 * If aLength is 0, this value is ignored.
321 */
322 MOZ_CAN_RUN_SCRIPT nsresult SetUnmaskRangeAndNotify(
323 uint32_t aStart, uint32_t aLength = UINT32_MAX, uint32_t aTimeout = 0) {
324 return SetUnmaskRangeInternal(aStart, aLength, aTimeout, true, false);
325 }
326
327 /**
328 * MaskAllCharacters() is an alias of SetUnmaskRange() to mask all characters.
329 * In other words, this removes existing unmask range.
330 * After this is called, TextEditor starts masking password automatically.
331 */
MaskAllCharacters()332 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult MaskAllCharacters() {
333 if (!mPasswordMaskData) {
334 return NS_OK; // Already we don't have masked range data.
335 }
336 return SetUnmaskRangeInternal(UINT32_MAX, 0, 0, false, true);
337 }
338
339 /**
340 * MaskAllCharactersAndNotify() is an alias of SetUnmaskRangeAndNotify() to
341 * mask all characters and notifies the text frame. In other words, this
342 * removes existing unmask range.
343 * After this is called, TextEditor starts masking password automatically.
344 */
MaskAllCharactersAndNotify()345 MOZ_CAN_RUN_SCRIPT nsresult MaskAllCharactersAndNotify() {
346 return SetUnmaskRangeInternal(UINT32_MAX, 0, 0, true, true);
347 }
348
349 /**
350 * WillDeleteText() is called before `DeleteTextTransaction` or something
351 * removes text in a text node. Note that this won't be called if the
352 * instance is `HTMLEditor` since supporting it makes the code complicated
353 * due to mutation events.
354 *
355 * @param aCurrentLength Current text length of the node.
356 * @param aRemoveStartOffset Start offset of the range to be removed.
357 * @param aRemoveLength Length of the range to be removed.
358 */
359 void WillDeleteText(uint32_t aCurrentLength, uint32_t aRemoveStartOffset,
360 uint32_t aRemoveLength);
361
362 /**
363 * DidInsertText() is called after `InsertTextTransaction` or something
364 * inserts text into a text node. Note that this won't be called if the
365 * instance is `HTMLEditor` since supporting it makes the code complicated
366 * due to mutatione events.
367 *
368 * @param aNewLength New text length after the insertion.
369 * @param aInsertedOffset Start offset of the inserted text.
370 * @param aInsertedLength Length of the inserted text.
371 * @return NS_OK or NS_ERROR_EDITOR_DESTROYED.
372 */
373 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult DidInsertText(
374 uint32_t aNewLength, uint32_t aInsertedOffset, uint32_t aInsertedLength);
375
376 protected: // edit sub-action handler
377 /**
378 * MaybeTruncateInsertionStringForMaxLength() truncates aInsertionString to
379 * `maxlength` if it was not pasted in by the user.
380 *
381 * @param aInsertionString [in/out] New insertion string. This is
382 * truncated to `maxlength` if it was not pasted in
383 * by the user.
384 * @return If aInsertionString is truncated, it returns "as
385 * handled", else "as ignored."
386 */
387 EditActionResult MaybeTruncateInsertionStringForMaxLength(
388 nsAString& aInsertionString);
389
390 /**
391 * InsertLineFeedCharacterAtSelection() inserts a linefeed character at
392 * selection.
393 */
394 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
395 InsertLineFeedCharacterAtSelection();
396
397 /**
398 * Handles the newline characters according to the default system prefs
399 * (editor.singleLine.pasteNewlines).
400 * Each value means:
401 * nsIEditor::eNewlinesReplaceWithSpaces (2, Firefox default):
402 * replace newlines with spaces.
403 * nsIEditor::eNewlinesStrip (3):
404 * remove newlines from the string.
405 * nsIEditor::eNewlinesReplaceWithCommas (4, Thunderbird default):
406 * replace newlines with commas.
407 * nsIEditor::eNewlinesStripSurroundingWhitespace (5):
408 * collapse newlines and surrounding white-space characters and
409 * remove them from the string.
410 * nsIEditor::eNewlinesPasteIntact (0):
411 * only remove the leading and trailing newlines.
412 * nsIEditor::eNewlinesPasteToFirst (1) or any other value:
413 * remove the first newline and all characters following it.
414 *
415 * @param aString the string to be modified in place.
416 */
417 void HandleNewLinesInStringForSingleLineEditor(nsString& aString) const;
418
419 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult HandleInsertText(
420 EditSubAction aEditSubAction, const nsAString& aInsertionString) final;
421
422 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertDroppedDataTransferAsAction(
423 AutoEditActionDataSetter& aEditActionData,
424 dom::DataTransfer& aDataTransfer, const EditorDOMPoint& aDroppedAt,
425 dom::Document* aSrcDocument) final;
426
427 /**
428 * HandleDeleteSelectionInternal() is a helper method of
429 * HandleDeleteSelection(). Must be called only when the instance is
430 * TextEditor.
431 * NOTE: This method creates SelectionBatcher. Therefore, each caller
432 * needs to check if the editor is still available even if this returns
433 * NS_OK.
434 */
435 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
436 HandleDeleteSelectionInternal(nsIEditor::EDirection aDirectionAndAmount,
437 nsIEditor::EStripWrappers aStripWrappers);
438
439 /**
440 * This method handles "delete selection" commands.
441 *
442 * @param aDirectionAndAmount Direction of the deletion.
443 * @param aStripWrappers Must be nsIEditor::eNoStrip.
444 */
445 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
446 HandleDeleteSelection(nsIEditor::EDirection aDirectionAndAmount,
447 nsIEditor::EStripWrappers aStripWrappers) final;
448
449 /**
450 * ComputeValueFromTextNodeAndBRElement() tries to compute "value" of
451 * this editor content only with text nodes and `<br>` elements.
452 * If this succeeds to compute the value, it's returned with aValue and
453 * the result is marked as "handled". Otherwise, the caller needs to
454 * compute it with another way.
455 */
456 EditActionResult ComputeValueFromTextNodeAndBRElement(
457 nsAString& aValue) const;
458
459 /**
460 * SetTextWithoutTransaction() is optimized method to set `<input>.value`
461 * and `<textarea>.value` to aValue without transaction. This must be
462 * called only when it's not `HTMLEditor` and undo/redo is disabled.
463 */
464 [[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
465 SetTextWithoutTransaction(const nsAString& aValue);
466
467 /**
468 * EnsureCaretNotAtEndOfTextNode() collapses selection at the padding `<br>`
469 * element (i.e., container becomes the anonymous `<div>` element) if
470 * `Selection` is at end of the text node.
471 */
472 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult EnsureCaretNotAtEndOfTextNode();
473
474 protected: // Called by helper classes.
475 MOZ_CAN_RUN_SCRIPT void OnStartToHandleTopLevelEditSubAction(
476 EditSubAction aTopLevelEditSubAction,
477 nsIEditor::EDirection aDirectionOfTopLevelEditSubAction,
478 ErrorResult& aRv) final;
479 MOZ_CAN_RUN_SCRIPT nsresult OnEndHandlingTopLevelEditSubAction() final;
480
481 /**
482 * HandleInlineSpellCheckAfterEdit() does spell-check after handling top level
483 * edit subaction.
484 */
HandleInlineSpellCheckAfterEdit()485 nsresult HandleInlineSpellCheckAfterEdit() {
486 MOZ_ASSERT(IsEditActionDataAvailable());
487 if (!GetSpellCheckRestartPoint().IsSet()) {
488 return NS_OK; // Maybe being initialized.
489 }
490 nsresult rv = HandleInlineSpellCheck(GetSpellCheckRestartPoint());
491 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to spellcheck");
492 ClearSpellCheckRestartPoint();
493 return rv;
494 }
495
496 protected: // Shouldn't be used by friend classes
497 virtual ~TextEditor();
498
499 /**
500 * CanEchoPasswordNow() returns true if currently we can echo password.
501 * If it's direct user input such as pasting or dropping text, this
502 * returns false even if we may echo password.
503 */
504 bool CanEchoPasswordNow() const;
505
506 /**
507 * Make the given selection span the entire document.
508 */
509 MOZ_CAN_RUN_SCRIPT nsresult SelectEntireDocument() final;
510
511 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
512 InsertWithQuotationsAsSubAction(const nsAString& aQuotedText) final;
513
514 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
515 InsertTextFromTransferable(nsITransferable* transferable);
516
517 bool IsCopyToClipboardAllowedInternal() const final;
518
519 already_AddRefed<Element> GetInputEventTargetElement() const final;
520
521 /**
522 * See SetUnmaskRange() and SetUnmaskRangeAndNotify() for the detail.
523 *
524 * @param aForceStartMasking If true, forcibly starts masking. This should
525 * be used only when `nsIEditor::Mask()` is called.
526 */
527 MOZ_CAN_RUN_SCRIPT nsresult SetUnmaskRangeInternal(uint32_t aStart,
528 uint32_t aLength,
529 uint32_t aTimeout,
530 bool aNotify,
531 bool aForceStartMasking);
532
HasAutoMaskingTimer()533 MOZ_ALWAYS_INLINE bool HasAutoMaskingTimer() const {
534 return mPasswordMaskData && mPasswordMaskData->mTimer;
535 }
536
537 protected:
538 UniquePtr<PasswordMaskData> mPasswordMaskData;
539
540 int32_t mMaxTextLength = -1;
541
542 friend class DeleteNodeTransaction;
543 friend class EditorBase;
544 friend class InsertNodeTransaction;
545 };
546
547 } // namespace mozilla
548
AsTextEditor()549 mozilla::TextEditor* nsIEditor::AsTextEditor() {
550 MOZ_DIAGNOSTIC_ASSERT(IsTextEditor());
551 return static_cast<mozilla::TextEditor*>(this);
552 }
553
AsTextEditor()554 const mozilla::TextEditor* nsIEditor::AsTextEditor() const {
555 MOZ_DIAGNOSTIC_ASSERT(IsTextEditor());
556 return static_cast<const mozilla::TextEditor*>(this);
557 }
558
GetAsTextEditor()559 mozilla::TextEditor* nsIEditor::GetAsTextEditor() {
560 return AsEditorBase()->IsTextEditor() ? AsTextEditor() : nullptr;
561 }
562
GetAsTextEditor()563 const mozilla::TextEditor* nsIEditor::GetAsTextEditor() const {
564 return AsEditorBase()->IsTextEditor() ? AsTextEditor() : nullptr;
565 }
566
567 #endif // #ifndef mozilla_TextEditor_h
568