EditorBase.h (124008B)
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_EditorBase_h 7 #define mozilla_EditorBase_h 8 9 #include "mozilla/intl/BidiEmbeddingLevel.h" 10 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc. 11 #include "mozilla/EditAction.h" // for EditAction and EditSubAction 12 #include "mozilla/EditorDOMPoint.h" // for EditorDOMPoint 13 #include "mozilla/EditorForwards.h" 14 #include "mozilla/EventForwards.h" // for InputEventTargetRanges 15 #include "mozilla/Likely.h" // for MOZ_UNLIKELY, MOZ_LIKELY 16 #include "mozilla/Maybe.h" // for Maybe 17 #include "mozilla/OwningNonNull.h" // for OwningNonNull 18 #include "mozilla/PendingStyles.h" // for PendingStyle, PendingStyleCache 19 #include "mozilla/RangeBoundary.h" // for RawRangeBoundary, RangeBoundary 20 #include "mozilla/SelectionState.h" // for RangeUpdater, etc. 21 #include "mozilla/StyleSheet.h" // for StyleSheet 22 #include "mozilla/TransactionManager.h" // for TransactionManager 23 #include "mozilla/WeakPtr.h" // for WeakPtr 24 #include "mozilla/dom/DataTransfer.h" // for dom::DataTransfer 25 #include "mozilla/dom/HTMLBRElement.h" // for dom::HTMLBRElement 26 #include "mozilla/dom/Selection.h" 27 #include "mozilla/dom/Text.h" 28 #include "nsAtom.h" // for nsAtom, nsStaticAtom 29 #include "nsCOMPtr.h" // for already_AddRefed, nsCOMPtr 30 #include "nsCycleCollectionParticipant.h" 31 #include "nsGkAtoms.h" 32 #include "nsIClipboard.h" // for nsIClipboard::ClipboardType 33 #include "nsIContentInlines.h" // for nsINode::IsEditable() 34 #include "nsIEditor.h" // for nsIEditor, etc. 35 #include "nsISelectionController.h" // for nsISelectionController constants 36 #include "nsISelectionListener.h" // for nsISelectionListener 37 #include "nsISupportsImpl.h" // for EditorBase::Release, etc. 38 #include "nsIWeakReferenceUtils.h" // for nsWeakPtr 39 #include "nsLiteralString.h" // for NS_LITERAL_STRING 40 #include "nsPIDOMWindow.h" // for nsPIDOMWindowInner, etc. 41 #include "nsString.h" // for nsCString 42 #include "nsTArray.h" // for nsTArray and AutoTArray 43 #include "nsWeakReference.h" // for nsSupportsWeakReference 44 #include "nscore.h" // for nsresult, nsAString, etc. 45 46 #include <tuple> // for std::tuple 47 48 class mozInlineSpellChecker; 49 class nsAtom; 50 class nsCaret; 51 class nsIContent; 52 class nsIDocumentEncoder; 53 class nsIDocumentStateListener; 54 class nsIEditActionListener; 55 class nsINode; 56 class nsIPrincipal; 57 class nsISupports; 58 class nsITransferable; 59 class nsITransaction; 60 class nsIWidget; 61 class nsRange; 62 63 namespace mozilla { 64 class AlignStateAtSelection; 65 class AutoTransactionsConserveSelection; 66 class AutoUpdateViewBatch; 67 class ErrorResult; 68 class IMEContentObserver; 69 class ListElementSelectionState; 70 class ListItemElementSelectionState; 71 class ParagraphStateAtSelection; 72 class PresShell; 73 class TextComposition; 74 class TextInputListener; 75 class TextServicesDocument; 76 namespace dom { 77 class AbstractRange; 78 class DataTransfer; 79 class Document; 80 class DragEvent; 81 class Element; 82 class EventTarget; 83 class HTMLBRElement; 84 } // namespace dom 85 86 namespace widget { 87 struct IMEState; 88 } // namespace widget 89 90 /** 91 * Implementation of an editor object. it will be the controller/focal point 92 * for the main editor services. i.e. the GUIManager, publishing, transaction 93 * manager, event interfaces. the idea for the event interfaces is to have them 94 * delegate the actual commands to the editor independent of the XPFE 95 * implementation. 96 */ 97 class EditorBase : public nsIEditor, 98 public nsISelectionListener, 99 public nsSupportsWeakReference { 100 public: 101 /**************************************************************************** 102 * NOTE: DO NOT MAKE YOUR NEW METHODS PUBLIC IF they are called by other 103 * classes under libeditor except EditorEventListener and 104 * HTMLEditorEventListener because each public method which may fire 105 * eEditorInput event will need to instantiate new stack class for 106 * managing input type value of eEditorInput and cache some objects 107 * for smarter handling. In other words, when you add new root 108 * method to edit the DOM tree, you can make your new method public. 109 ****************************************************************************/ 110 111 using DataTransfer = dom::DataTransfer; 112 using Document = dom::Document; 113 using Element = dom::Element; 114 using InterlinePosition = dom::Selection::InterlinePosition; 115 using Selection = dom::Selection; 116 using Text = dom::Text; 117 118 enum class EditorType { Text, HTML }; 119 120 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 121 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor) 122 123 // nsIEditor methods 124 NS_DECL_NSIEDITOR 125 126 // nsISelectionListener method 127 NS_DECL_NSISELECTIONLISTENER 128 129 /** 130 * The default constructor. This should suffice. the setting of the 131 * interfaces is done after the construction of the editor class. 132 */ 133 explicit EditorBase(EditorType aEditorType); 134 135 [[nodiscard]] bool IsInitialized() const { 136 return mDocument && mDidPostCreate; 137 } 138 [[nodiscard]] bool IsBeingInitialized() const { 139 return mDocument && !mDidPostCreate; 140 } 141 [[nodiscard]] bool Destroyed() const { return mDidPreDestroy; } 142 143 Document* GetDocument() const { return mDocument; } 144 nsPIDOMWindowOuter* GetWindow() const; 145 nsPIDOMWindowInner* GetInnerWindow() const; 146 147 /** 148 * MaybeNodeRemovalsObservedByDevTools() returns true when the mutations in 149 * the document is observed by DevTools. 150 * 151 * @return true if the editor is an HTMLEditor instance 152 * and the mutations in the document is observed by 153 * DevTools. 154 */ 155 [[nodiscard]] bool MaybeNodeRemovalsObservedByDevTools() const; 156 157 /** 158 * MayHaveBeforeInputEventListenersForTelemetry() returns true when the 159 * window may have or have had one or more `beforeinput` event listeners. 160 * Note that this may return false even if there is a `beforeinput`. 161 * See nsPIDOMWindowInner::HasBeforeInputEventListenersForTelemetry()'s 162 * comment for the detail. 163 */ 164 bool MayHaveBeforeInputEventListenersForTelemetry() const { 165 if (const nsPIDOMWindowInner* window = GetInnerWindow()) { 166 return window->HasBeforeInputEventListenersForTelemetry(); 167 } 168 return false; 169 } 170 171 /** 172 * MutationObserverHasObservedNodeForTelemetry() returns true when a node in 173 * the window may have been observed by the web apps with a mutation observer 174 * (i.e., `MutationObserver.observe()` called by chrome script and addon's 175 * script does not make this returns true). 176 * Note that this may return false even if there is a node observed by 177 * a MutationObserver. See 178 * nsPIDOMWindowInner::MutationObserverHasObservedNodeForTelemetry()'s comment 179 * for the detail. 180 */ 181 bool MutationObserverHasObservedNodeForTelemetry() const { 182 if (const nsPIDOMWindowInner* window = GetInnerWindow()) { 183 return window->MutationObserverHasObservedNodeForTelemetry(); 184 } 185 return false; 186 } 187 188 /** 189 * This checks whether the call with aPrincipal should or should not be 190 * treated as user input. 191 */ 192 [[nodiscard]] static bool TreatAsUserInput(nsIPrincipal* aPrincipal); 193 194 PresShell* GetPresShell() const; 195 nsPresContext* GetPresContext() const; 196 already_AddRefed<nsCaret> GetCaret() const; 197 198 already_AddRefed<nsIWidget> GetWidget() const; 199 200 nsISelectionController* GetSelectionController() const; 201 202 nsresult GetSelection(SelectionType aSelectionType, 203 Selection** aSelection) const; 204 205 Selection* GetSelection( 206 SelectionType aSelectionType = SelectionType::eNormal) const { 207 if (aSelectionType == SelectionType::eNormal && 208 IsEditActionDataAvailable()) { 209 return &SelectionRef(); 210 } 211 nsISelectionController* sc = GetSelectionController(); 212 if (!sc) { 213 return nullptr; 214 } 215 Selection* selection = sc->GetSelection(ToRawSelectionType(aSelectionType)); 216 return selection; 217 } 218 219 /** 220 * @return Ancestor limiter of normal selection 221 */ 222 [[nodiscard]] nsIContent* GetSelectionAncestorLimiter() const { 223 Selection* selection = GetSelection(SelectionType::eNormal); 224 return selection ? selection->GetAncestorLimiter() : nullptr; 225 } 226 227 /** 228 * Create a DataTransfer object that can be shared between the paste event 229 * and pasting into a DOM element. 230 */ 231 already_AddRefed<DataTransfer> CreateDataTransferForPaste( 232 EventMessage aEventMessage, 233 nsIClipboard::ClipboardType aClipboardType) const; 234 235 /** 236 * Fast non-refcounting editor root element accessor 237 */ 238 Element* GetRoot() const { return mRootElement; } 239 240 /** 241 * Likewise, but gets the text control element instead of the root for 242 * plaintext editors. 243 */ 244 Element* GetExposedRoot() const; 245 246 /** 247 * Set or unset TextInputListener. If setting non-nullptr when the editor 248 * already has a TextInputListener, this will crash in debug build. 249 */ 250 void SetTextInputListener(TextInputListener* aTextInputListener); 251 252 /** 253 * Set or unset IMEContentObserver. If setting non-nullptr when the editor 254 * already has an IMEContentObserver, this will crash in debug build. 255 */ 256 void SetIMEContentObserver(IMEContentObserver* aIMEContentObserver); 257 258 /** 259 * Returns current composition. 260 */ 261 TextComposition* GetComposition() const; 262 263 /** 264 * Get preferred IME status of current widget. 265 */ 266 [[nodiscard]] virtual Result<widget::IMEState, nsresult> 267 GetPreferredIMEState() const = 0; 268 269 /** 270 * Returns true if there is composition string and not fixed. 271 */ 272 bool IsIMEComposing() const; 273 274 /** 275 * Commit composition if there is. 276 * Note that when there is a composition, this requests to commit composition 277 * to native IME. Therefore, when there is composition, this can do anything. 278 * For example, the editor instance, the widget or the process itself may 279 * be destroyed. 280 */ 281 nsresult CommitComposition(); 282 283 /** 284 * ToggleTextDirection() toggles text-direction of the root element. 285 * 286 * @param aPrincipal Set subject principal if it may be called by 287 * JS. If set to nullptr, will be treated as 288 * called by system. 289 */ 290 MOZ_CAN_RUN_SCRIPT nsresult 291 ToggleTextDirectionAsAction(nsIPrincipal* aPrincipal = nullptr); 292 293 /** 294 * SwitchTextDirectionTo() sets the text-direction of the root element to 295 * LTR or RTL. 296 */ 297 enum class TextDirection { 298 eLTR, 299 eRTL, 300 }; 301 MOZ_CAN_RUN_SCRIPT void SwitchTextDirectionTo(TextDirection aTextDirection); 302 303 /** 304 * Finalizes selection and caret for the editor. 305 */ 306 MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult FinalizeSelection(); 307 308 /** 309 * Returns true if selection is in an editable element and both the range 310 * start and the range end are editable. E.g., even if the selection range 311 * includes non-editable elements, returns true when one of common ancestors 312 * of the range start and the range end is editable. Otherwise, false. 313 */ 314 bool IsSelectionEditable(); 315 316 /** 317 * Returns number of undo or redo items. 318 */ 319 size_t NumberOfUndoItems() const { 320 return mTransactionManager ? mTransactionManager->NumberOfUndoItems() : 0; 321 } 322 size_t NumberOfRedoItems() const { 323 return mTransactionManager ? mTransactionManager->NumberOfRedoItems() : 0; 324 } 325 326 /** 327 * Returns number of maximum undo/redo transactions. 328 */ 329 int32_t NumberOfMaximumTransactions() const { 330 return mTransactionManager 331 ? mTransactionManager->NumberOfMaximumTransactions() 332 : 0; 333 } 334 335 /** 336 * Returns true if this editor can store transactions for undo/redo. 337 */ 338 bool IsUndoRedoEnabled() const { 339 return mTransactionManager && 340 mTransactionManager->NumberOfMaximumTransactions(); 341 } 342 343 /** 344 * Return true if it's possible to undo/redo right now. 345 */ 346 bool CanUndo() const { 347 return IsUndoRedoEnabled() && NumberOfUndoItems() > 0; 348 } 349 bool CanRedo() const { 350 return IsUndoRedoEnabled() && NumberOfRedoItems() > 0; 351 } 352 353 /** 354 * Enables or disables undo/redo feature. Returns true if it succeeded, 355 * otherwise, e.g., we're undoing or redoing, returns false. 356 */ 357 bool EnableUndoRedo(int32_t aMaxTransactionCount = -1) { 358 if (!mTransactionManager) { 359 mTransactionManager = new TransactionManager(); 360 } 361 return mTransactionManager->EnableUndoRedo(aMaxTransactionCount); 362 } 363 bool DisableUndoRedo() { 364 if (!mTransactionManager) { 365 return true; 366 } 367 return mTransactionManager->DisableUndoRedo(); 368 } 369 bool ClearUndoRedo() { 370 if (!mTransactionManager) { 371 return true; 372 } 373 return mTransactionManager->ClearUndoRedo(); 374 } 375 376 /** 377 * See Document::AreClipboardCommandsUnconditionallyEnabled. 378 */ 379 bool AreClipboardCommandsUnconditionallyEnabled() const; 380 381 /** 382 * IsCutCommandEnabled() returns whether cut command can be enabled or 383 * disabled. This always returns true if we're in non-chrome HTML/XHTML 384 * document. Otherwise, same as the result of `IsCopyToClipboardAllowed()`. 385 */ 386 MOZ_CAN_RUN_SCRIPT bool IsCutCommandEnabled() const; 387 388 /** 389 * IsCopyCommandEnabled() returns copy command can be enabled or disabled. 390 * This always returns true if we're in non-chrome HTML/XHTML document. 391 * Otherwise, same as the result of `IsCopyToClipboardAllowed()`. 392 */ 393 MOZ_CAN_RUN_SCRIPT bool IsCopyCommandEnabled() const; 394 395 /** 396 * IsCopyToClipboardAllowed() returns true if the selected content can 397 * be copied into the clipboard. This returns true when: 398 * - `Selection` is not collapsed and we're not a password editor. 399 * - `Selection` is not collapsed and we're a password editor but selection 400 * range is in unmasked range. 401 */ 402 bool IsCopyToClipboardAllowed() const { 403 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing); 404 if (NS_WARN_IF(!editActionData.CanHandle())) { 405 return false; 406 } 407 return IsCopyToClipboardAllowedInternal(); 408 } 409 410 /** 411 * Called before starting to handle eMouseDown or eMouseUp in PresShell. 412 * 413 * @return true if IME consumed aMouseEvent. 414 */ 415 MOZ_CAN_RUN_SCRIPT bool WillHandleMouseButtonEvent( 416 WidgetMouseEvent& aMouseEvent); 417 418 /** 419 * HandleDropEvent() is called from EditorEventListener::Drop that is handler 420 * of drop event. 421 */ 422 MOZ_CAN_RUN_SCRIPT nsresult HandleDropEvent(dom::DragEvent* aDropEvent); 423 424 MOZ_CAN_RUN_SCRIPT virtual nsresult HandleKeyPressEvent( 425 WidgetKeyboardEvent* aKeyboardEvent); 426 427 virtual dom::EventTarget* GetDOMEventTarget() const = 0; 428 429 /** 430 * OnCompositionStart() is called when editor receives eCompositionStart 431 * event which should be handled in this editor. 432 */ 433 nsresult OnCompositionStart(WidgetCompositionEvent& aCompositionStartEvent); 434 435 /** 436 * OnCompositionChange() is called when editor receives an eCompositioChange 437 * event which should be handled in this editor. 438 * 439 * @param aCompositionChangeEvent eCompositionChange event which should 440 * be handled in this editor. 441 */ 442 MOZ_CAN_RUN_SCRIPT nsresult 443 OnCompositionChange(WidgetCompositionEvent& aCompositionChangeEvent); 444 445 /** 446 * OnCompositionEnd() is called when editor receives an eCompositionChange 447 * event and it's followed by eCompositionEnd event and after 448 * OnCompositionChange() is called. 449 */ 450 MOZ_CAN_RUN_SCRIPT void OnCompositionEnd( 451 WidgetCompositionEvent& aCompositionEndEvent); 452 453 /** 454 * Accessor methods to flags. 455 */ 456 uint32_t Flags() const { return mFlags; } 457 458 MOZ_CAN_RUN_SCRIPT nsresult AddFlags(uint32_t aFlags) { 459 const uint32_t kOldFlags = Flags(); 460 const uint32_t kNewFlags = (kOldFlags | aFlags); 461 if (kNewFlags == kOldFlags) { 462 return NS_OK; 463 } 464 return SetFlags(kNewFlags); // virtual call and may be expensive. 465 } 466 MOZ_CAN_RUN_SCRIPT nsresult RemoveFlags(uint32_t aFlags) { 467 const uint32_t kOldFlags = Flags(); 468 const uint32_t kNewFlags = (kOldFlags & ~aFlags); 469 if (kNewFlags == kOldFlags) { 470 return NS_OK; 471 } 472 return SetFlags(kNewFlags); // virtual call and may be expensive. 473 } 474 MOZ_CAN_RUN_SCRIPT nsresult AddAndRemoveFlags(uint32_t aAddingFlags, 475 uint32_t aRemovingFlags) { 476 MOZ_ASSERT(!(aAddingFlags & aRemovingFlags), 477 "Same flags are specified both adding and removing"); 478 const uint32_t kOldFlags = Flags(); 479 const uint32_t kNewFlags = ((kOldFlags | aAddingFlags) & ~aRemovingFlags); 480 if (kNewFlags == kOldFlags) { 481 return NS_OK; 482 } 483 return SetFlags(kNewFlags); // virtual call and may be expensive. 484 } 485 486 bool IsSingleLineEditor() const { 487 const bool isSingleLineEditor = 488 (mFlags & nsIEditor::eEditorSingleLineMask) != 0; 489 MOZ_ASSERT_IF(isSingleLineEditor, IsTextEditor()); 490 return isSingleLineEditor; 491 } 492 493 bool IsPasswordEditor() const { 494 const bool isPasswordEditor = 495 (mFlags & nsIEditor::eEditorPasswordMask) != 0; 496 MOZ_ASSERT_IF(isPasswordEditor, IsTextEditor()); 497 return isPasswordEditor; 498 } 499 500 // FYI: Both IsRightToLeft() and IsLeftToRight() may return false if 501 // the editor inherits the content node's direction. 502 bool IsRightToLeft() const { 503 return (mFlags & nsIEditor::eEditorRightToLeft) != 0; 504 } 505 bool IsLeftToRight() const { 506 return (mFlags & nsIEditor::eEditorLeftToRight) != 0; 507 } 508 509 bool IsReadonly() const { 510 return (mFlags & nsIEditor::eEditorReadonlyMask) != 0; 511 } 512 513 bool IsMailEditor() const { 514 return (mFlags & nsIEditor::eEditorMailMask) != 0; 515 } 516 517 bool IsInteractionAllowed() const { 518 const bool isInteractionAllowed = 519 (mFlags & nsIEditor::eEditorAllowInteraction) != 0; 520 MOZ_ASSERT_IF(isInteractionAllowed, IsHTMLEditor()); 521 return isInteractionAllowed; 522 } 523 524 bool ShouldSkipSpellCheck() const { 525 return (mFlags & nsIEditor::eEditorSkipSpellCheck) != 0; 526 } 527 528 bool HasIndependentSelection() const { 529 MOZ_ASSERT_IF(mSelectionController, IsTextEditor()); 530 return !!mSelectionController; 531 } 532 533 bool IsModifiable() const { return !IsReadonly(); } 534 535 /** 536 * IsInEditSubAction() return true while the instance is handling an edit 537 * sub-action. Otherwise, false. 538 */ 539 bool IsInEditSubAction() const { return mIsInEditSubAction; } 540 541 /** 542 * IsEmpty() checks whether the editor is empty. If editor has only padding 543 * <br> element for empty editor, returns true. If editor's root element has 544 * non-empty text nodes or other nodes like <br>, returns false. 545 */ 546 virtual bool IsEmpty() const = 0; 547 548 /** 549 * SuppressDispatchingInputEvent() suppresses or unsuppresses dispatching 550 * "input" event. 551 */ 552 void SuppressDispatchingInputEvent(bool aSuppress) { 553 mDispatchInputEvent = !aSuppress; 554 } 555 556 /** 557 * IsSuppressingDispatchingInputEvent() returns true if the editor stops 558 * dispatching input event. Otherwise, false. 559 */ 560 bool IsSuppressingDispatchingInputEvent() const { 561 return !mDispatchInputEvent; 562 } 563 564 /** 565 * Returns true if markNodeDirty() has any effect. Returns false if 566 * markNodeDirty() is a no-op. 567 */ 568 bool OutputsMozDirty() const { 569 // Return true for Composer (!IsInteractionAllowed()) or mail 570 // (IsMailEditor()), but false for webpages. 571 return !IsInteractionAllowed() || IsMailEditor(); 572 } 573 574 /** 575 * Get the focused element, if we're focused. Returns null otherwise. 576 */ 577 virtual Element* GetFocusedElement() const; 578 579 /** 580 * Whether the aGUIEvent should be handled by this editor or not. When this 581 * returns false, The aGUIEvent shouldn't be handled on this editor, 582 * i.e., The aGUIEvent should be handled by another inner editor or ancestor 583 * elements. 584 */ 585 virtual bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) const; 586 587 /** 588 * FindSelectionRoot() returns a selection root of this editor when aNode 589 * gets focus. aNode must be a content node or a document node. When the 590 * target isn't a part of this editor, returns nullptr. If this is for 591 * designMode, you should set the document node to aNode except that an 592 * element in the document has focus. 593 */ 594 [[nodiscard]] virtual Element* FindSelectionRoot(const nsINode& aNode) const; 595 596 /** 597 * OnFocus() is called when we get a focus event. 598 * 599 * @param aOriginalEventTargetNode The original event target node of the 600 * focus event. 601 */ 602 MOZ_CAN_RUN_SCRIPT virtual nsresult OnFocus( 603 const nsINode& aOriginalEventTargetNode); 604 605 /** 606 * OnBlur() is called when we're blurred. 607 * 608 * @param aEventTarget The event target of the blur event. 609 */ 610 virtual nsresult OnBlur(const dom::EventTarget* aEventTarget) = 0; 611 612 /** Resyncs spellchecking state (enabled/disabled). This should be called 613 * when anything that affects spellchecking state changes, such as the 614 * spellcheck attribute value. 615 */ 616 void SyncRealTimeSpell(); 617 618 /** 619 * Do "cut". 620 * 621 * @param aPrincipal If you know current context is subject 622 * principal or system principal, set it. 623 * When nullptr, this checks it automatically. 624 */ 625 MOZ_CAN_RUN_SCRIPT nsresult CutAsAction(nsIPrincipal* aPrincipal = nullptr); 626 627 /** 628 * CanPaste() returns true if user can paste something at current selection. 629 */ 630 virtual bool CanPaste(nsIClipboard::ClipboardType aClipboardType) const = 0; 631 632 /** 633 * Do "undo" or "redo". 634 * 635 * @param aCount How many count of transactions should be 636 * handled. 637 * @param aPrincipal Set subject principal if it may be called by 638 * JS. If set to nullptr, will be treated as 639 * called by system. 640 */ 641 MOZ_CAN_RUN_SCRIPT nsresult UndoAsAction(uint32_t aCount, 642 nsIPrincipal* aPrincipal = nullptr); 643 MOZ_CAN_RUN_SCRIPT nsresult RedoAsAction(uint32_t aCount, 644 nsIPrincipal* aPrincipal = nullptr); 645 646 /** 647 * InsertTextAsAction() inserts aStringToInsert at selection. 648 * Although this method is implementation of nsIEditor.insertText(), 649 * this treats the input is an edit action. If you'd like to insert text 650 * as part of edit action, you probably should use InsertTextAsSubAction(). 651 * 652 * @param aStringToInsert The string to insert. 653 * @param aPrincipal Set subject principal if it may be called by 654 * JS. If set to nullptr, will be treated as 655 * called by system. 656 */ 657 MOZ_CAN_RUN_SCRIPT nsresult InsertTextAsAction( 658 const nsAString& aStringToInsert, nsIPrincipal* aPrincipal = nullptr); 659 660 /** 661 * InsertLineBreakAsAction() is called when user inputs a line break with 662 * Enter or something. If the instance is `HTMLEditor`, this is called 663 * when Shift + Enter or "insertlinebreak" command. 664 * 665 * @param aPrincipal Set subject principal if it may be called by 666 * JS. If set to nullptr, will be treated as 667 * called by system. 668 */ 669 MOZ_CAN_RUN_SCRIPT virtual nsresult InsertLineBreakAsAction( 670 nsIPrincipal* aPrincipal = nullptr) = 0; 671 672 /** 673 * CanDeleteSelection() returns true if `Selection` is not collapsed and 674 * it's allowed to be removed. 675 */ 676 bool CanDeleteSelection() const { 677 AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing); 678 if (NS_WARN_IF(!editActionData.CanHandle())) { 679 return false; 680 } 681 return IsModifiable() && !SelectionRef().IsCollapsed(); 682 } 683 684 /** 685 * DeleteSelectionAsAction() removes selection content or content around 686 * caret with transactions. This should be used for handling it as an 687 * edit action. If you'd like to remove selection for preparing to insert 688 * something, you probably should use DeleteSelectionAsSubAction(). 689 * 690 * @param aDirectionAndAmount How much range should be removed. 691 * @param aStripWrappers Whether the parent blocks should be removed 692 * when they become empty. 693 * @param aPrincipal Set subject principal if it may be called by 694 * JS. If set to nullptr, will be treated as 695 * called by system. 696 */ 697 MOZ_CAN_RUN_SCRIPT nsresult 698 DeleteSelectionAsAction(nsIEditor::EDirection aDirectionAndAmount, 699 nsIEditor::EStripWrappers aStripWrappers, 700 nsIPrincipal* aPrincipal = nullptr); 701 702 enum class AllowBeforeInputEventCancelable { 703 No, 704 Yes, 705 }; 706 707 enum class PreventSetSelection { 708 No, 709 Yes, 710 }; 711 712 /** 713 * Replace text in aReplaceRange or all text in this editor with aString and 714 * treat the change as inserting the string. 715 * 716 * @param aString The string to set. 717 * @param aReplaceRange The range to be replaced. 718 * If nullptr, all contents will be replaced. 719 * NOTE: Currently, nullptr is not allowed if 720 * the editor is an HTMLEditor. 721 * @param aAllowBeforeInputEventCancelable 722 * Whether `beforeinput` event which will be 723 * dispatched for this can be cancelable or not. 724 * @param aPreventSetSelection 725 * Whether setting selection after replacing text. 726 * If No, selection is the tail of replaced text. 727 * If Yes, selection isn't changed. 728 * @param aPrincipal Set subject principal if it may be called by 729 * JS. If set to nullptr, will be treated as 730 * called by system. 731 */ 732 MOZ_CAN_RUN_SCRIPT nsresult ReplaceTextAsAction( 733 const nsAString& aString, nsRange* aReplaceRange, 734 AllowBeforeInputEventCancelable aAllowBeforeInputEventCancelable, 735 PreventSetSelection aPreventSetSelection = PreventSetSelection::No, 736 nsIPrincipal* aPrincipal = nullptr); 737 738 /** 739 * Can we paste |aTransferable| or, if |aTransferable| is null, will a call 740 * to pasteTransferable later possibly succeed if given an instance of 741 * nsITransferable then? True if the doc is modifiable, and, if 742 * |aTransfeable| is non-null, we have pasteable data in |aTransfeable|. 743 */ 744 virtual bool CanPasteTransferable(nsITransferable* aTransferable) = 0; 745 746 /** 747 * PasteAsAction() pastes clipboard content to Selection. This method 748 * may dispatch ePaste event first. If its defaultPrevent() is called, 749 * this does nothing but returns NS_OK. 750 * 751 * @param aClipboardType nsIClipboard::kGlobalClipboard or 752 * nsIClipboard::kSelectionClipboard. 753 * @param aDispatchPasteEvent Yes if this should dispatch ePaste event 754 * before pasting. Otherwise, No. 755 * @param aDataTransfer The object containing the data to use for the 756 * paste operation. May be nullptr, in which case 757 * this will just get the data from the clipboard. 758 * @param aPrincipal Set subject principal if it may be called by 759 * JS. If set to nullptr, will be treated as 760 * called by system. 761 */ 762 enum class DispatchPasteEvent { No, Yes }; 763 MOZ_CAN_RUN_SCRIPT nsresult 764 PasteAsAction(nsIClipboard::ClipboardType aClipboardType, 765 DispatchPasteEvent aDispatchPasteEvent, 766 DataTransfer* aDataTransfer = nullptr, 767 nsIPrincipal* aPrincipal = nullptr); 768 769 /** 770 * Paste aTransferable at Selection. 771 * 772 * @param aTransferable Must not be nullptr. 773 * @param aDispatchPasteEvent Yes if this should dispatch ePaste event 774 * before pasting. Otherwise, No. 775 * @param aPrincipal Set subject principal if it may be called by 776 * JS. If set to nullptr, will be treated as 777 * called by system. 778 */ 779 MOZ_CAN_RUN_SCRIPT nsresult PasteTransferableAsAction( 780 nsITransferable* aTransferable, DispatchPasteEvent aDispatchPasteEvent, 781 nsIPrincipal* aPrincipal = nullptr); 782 783 /** 784 * PasteAsQuotationAsAction() pastes content in clipboard as quotation. 785 * If the editor is TextEditor or in plaintext mode, will paste the content 786 * with appending ">" to start of each line. 787 * if the editor is HTMLEditor and is not in plaintext mode, will patste it 788 * into newly created blockquote element. 789 * 790 * @param aClipboardType nsIClipboard::kGlobalClipboard or 791 * nsIClipboard::kSelectionClipboard. 792 * @param aDispatchPasteEvent Yes if this should dispatch ePaste event 793 * before pasting. Otherwise, No. 794 * @param aDataTransfer The object containing the data to use for the 795 * paste operation. May be nullptr, in which case 796 * this will just get the data from the clipboard. 797 * @param aPrincipal Set subject principal if it may be called by 798 * JS. If set to nullptr, will be treated as 799 * called by system. 800 */ 801 MOZ_CAN_RUN_SCRIPT nsresult 802 PasteAsQuotationAsAction(nsIClipboard::ClipboardType aClipboardType, 803 DispatchPasteEvent aDispatchPasteEvent, 804 DataTransfer* aDataTransfer = nullptr, 805 nsIPrincipal* aPrincipal = nullptr); 806 807 /** 808 * Return true if `beforeinput` or `input` event is being dispatched. 809 */ 810 [[nodiscard]] bool IsDispatchingInputEvent() const { 811 return mEditActionData && mEditActionData->IsDispatchingInputEvent(); 812 } 813 814 protected: // May be used by friends. 815 class AutoEditActionDataSetter; 816 817 /** 818 * TopLevelEditSubActionData stores temporary data while we're handling 819 * top-level edit sub-action. 820 */ 821 struct MOZ_STACK_CLASS TopLevelEditSubActionData final { 822 friend class AutoEditActionDataSetter; 823 824 // Set selected range before edit. Then, RangeUpdater keep modifying 825 // the range while we're changing the DOM tree. 826 RefPtr<RangeItem> mSelectedRange; 827 828 // Computing changed range while we're handling sub actions. 829 RefPtr<nsRange> mChangedRange; 830 831 // XXX In strict speaking, mCachedPendingStyles isn't enough to cache 832 // inline styles because inline style can be specified with "style" 833 // attribute and/or CSS in <style> elements or CSS files. So, we need 834 // to look for better implementation about this. 835 // FYI: Initialization cost of AutoPendingStyleCacheArray is expensive and 836 // it is not used by TextEditor so that we should construct it only 837 // when we're an HTMLEditor. 838 Maybe<AutoPendingStyleCacheArray> mCachedPendingStyles; 839 840 // If we tried to delete selection, set to true. 841 bool mDidDeleteSelection; 842 843 // If we have explicitly set selection inter line, set to true. 844 // `AfterEdit()` or something shouldn't overwrite it in such case. 845 bool mDidExplicitlySetInterLine; 846 847 // If we have deleted non-collapsed range set to true, there are only 2 848 // cases for now: 849 // - non-collapsed range was selected. 850 // - selection was collapsed in a text node and a Unicode character 851 // was removed. 852 bool mDidDeleteNonCollapsedRange; 853 854 // If we have deleted parent empty blocks, set to true. 855 bool mDidDeleteEmptyParentBlocks; 856 857 // If we're a contenteditable editor, we temporarily increase edit count 858 // of the document between `BeforeEdit()` and `AfterEdit()`. I.e., if 859 // we increased the count in `BeforeEdit()`, we need to decrease it in 860 // `AfterEdit()`, however, the document may be changed to designMode or 861 // non-editable. Therefore, we need to store with this whether we need 862 // to restore it. 863 bool mRestoreContentEditableCount; 864 865 // If we explicitly normalized whitespaces around the changed range, 866 // set to true. 867 bool mDidNormalizeWhitespaces; 868 869 // Set to true by default. If somebody inserts an HTML fragment 870 // intentionally, any empty elements shouldn't be cleaned up later. In the 871 // case this is set to false. 872 // TODO: We should not do this by default. If it's necessary, each edit 873 // action handler do it by itself instead. Then, we can avoid such 874 // unnecessary DOM tree scan. 875 bool mNeedsToCleanUpEmptyElements; 876 877 /** 878 * The following methods modifies some data of this struct and 879 * `EditSubActionData` struct. Currently, these are required only 880 * by `HTMLEditor`. Therefore, for cutting the runtime cost of 881 * `TextEditor`, these methods should be called only by `HTMLEditor`. 882 * But it's fine to use these methods in `TextEditor` if necessary. 883 * If so, you need to call `DidDeleteText()` and `DidInsertText()` 884 * from `SetTextNodeWithoutTransaction()`. 885 */ 886 void DidCreateElement(EditorBase& aEditorBase, Element& aNewElement); 887 void DidInsertContent(EditorBase& aEditorBase, nsIContent& aNewContent); 888 void WillDeleteContent(EditorBase& aEditorBase, 889 nsIContent& aRemovingContent); 890 void DidSplitContent(EditorBase& aEditorBase, nsIContent& aSplitContent, 891 nsIContent& aNewContent); 892 void DidJoinContents(EditorBase& aEditorBase, 893 const EditorRawDOMPoint& aJoinedPoint); 894 void DidInsertText(EditorBase& aEditorBase, 895 const EditorRawDOMPoint& aInsertionBegin, 896 const EditorRawDOMPoint& aInsertionEnd); 897 void DidDeleteText(EditorBase& aEditorBase, 898 const EditorRawDOMPoint& aStartInTextNode); 899 void WillDeleteRange(EditorBase& aEditorBase, 900 const EditorRawDOMPoint& aStart, 901 const EditorRawDOMPoint& aEnd); 902 903 private: 904 void Clear() { 905 mDidExplicitlySetInterLine = false; 906 // We don't need to clear other members which are referred only when the 907 // editor is an HTML editor anymore. Note that if `mSelectedRange` is 908 // non-nullptr, that means that we're in `HTMLEditor`. 909 if (!mSelectedRange) { 910 return; 911 } 912 mSelectedRange->Clear(); 913 mChangedRange->Reset(); 914 if (mCachedPendingStyles.isSome()) { 915 mCachedPendingStyles->Clear(); 916 } 917 mDidDeleteSelection = false; 918 mDidDeleteNonCollapsedRange = false; 919 mDidDeleteEmptyParentBlocks = false; 920 mRestoreContentEditableCount = false; 921 mDidNormalizeWhitespaces = false; 922 mNeedsToCleanUpEmptyElements = true; 923 } 924 925 /** 926 * Extend mChangedRange to include `aNode`. 927 */ 928 nsresult AddNodeToChangedRange(const HTMLEditor& aHTMLEditor, 929 nsINode& aNode); 930 931 /** 932 * Extend mChangedRange to include `aPoint`. 933 */ 934 nsresult AddPointToChangedRange(const HTMLEditor& aHTMLEditor, 935 const EditorRawDOMPoint& aPoint); 936 937 /** 938 * Extend mChangedRange to include `aStart` and `aEnd`. 939 */ 940 nsresult AddRangeToChangedRange(const HTMLEditor& aHTMLEditor, 941 const EditorRawDOMPoint& aStart, 942 const EditorRawDOMPoint& aEnd); 943 944 TopLevelEditSubActionData() = default; 945 TopLevelEditSubActionData(const TopLevelEditSubActionData& aOther) = delete; 946 }; 947 948 struct MOZ_STACK_CLASS EditSubActionData final { 949 // While this is set to false, TopLevelEditSubActionData::mChangedRange 950 // shouldn't be modified since in some cases, modifying it in the setter 951 // itself may be faster. Note that we should affect this only for current 952 // edit sub action since mutation event listener may edit different range. 953 bool mAdjustChangedRangeFromListener; 954 955 private: 956 void Clear() { mAdjustChangedRangeFromListener = true; } 957 958 friend EditorBase; 959 }; 960 961 protected: // AutoEditActionDataSetter, this shouldn't be accessed by friends. 962 /** 963 * SettingDataTransfer enum class is used to specify whether DataTransfer 964 * should be initialized with or without format. For example, when user 965 * uses Accel + Shift + V to paste text without format, DataTransfer should 966 * have only plain/text data to make web apps treat it without format. 967 */ 968 enum class SettingDataTransfer { 969 eWithFormat, 970 eWithoutFormat, 971 }; 972 973 /** 974 * AutoEditActionDataSetter grabs some necessary objects for handling any 975 * edit actions and store the edit action what we're handling. When this is 976 * created, its pointer is set to the mEditActionData, and this guarantees 977 * the lifetime of grabbing objects until it's destroyed. 978 */ 979 class MOZ_STACK_CLASS AutoEditActionDataSetter final { 980 public: 981 // NOTE: aPrincipal will be used when we implement "beforeinput" event. 982 // It's set only when maybe we shouldn't dispatch it because of 983 // called by JS. I.e., if this is nullptr, we can always dispatch 984 // it. 985 AutoEditActionDataSetter(const EditorBase& aEditorBase, 986 EditAction aEditAction, 987 nsIPrincipal* aPrincipal = nullptr); 988 AutoEditActionDataSetter() = delete; 989 AutoEditActionDataSetter(const AutoEditActionDataSetter& aOther) = delete; 990 ~AutoEditActionDataSetter(); 991 992 void SetSelectionCreatedByDoubleclick(bool aSelectionCreatedByDoubleclick) { 993 mSelectionCreatedByDoubleclick = aSelectionCreatedByDoubleclick; 994 } 995 996 [[nodiscard]] bool SelectionCreatedByDoubleclick() const { 997 return mSelectionCreatedByDoubleclick; 998 } 999 1000 void UpdateEditAction(EditAction aEditAction) { 1001 MOZ_ASSERT(!mHasTriedToDispatchBeforeInputEvent, 1002 "It's too late to update EditAction since this may have " 1003 "already dispatched a beforeinput event"); 1004 mEditAction = aEditAction; 1005 } 1006 1007 /** 1008 * CanHandle() or CanHandleAndHandleBeforeInput() must be called 1009 * immediately after creating the instance. If caller does not need to 1010 * handle "beforeinput" event or caller needs to set additional information 1011 * the events later, use the former. Otherwise, use the latter. If caller 1012 * uses the former, it's required to call MaybeDispatchBeforeInputEvent() by 1013 * itself. 1014 * 1015 */ 1016 [[nodiscard]] bool CanHandle() const { 1017 #ifdef DEBUG 1018 mHasCanHandleChecked = true; 1019 #endif // #ifdef DEBUG 1020 // Don't allow to run new edit action when an edit action caused 1021 // destroying the editor while it's being handled. 1022 if (mEditAction != EditAction::eInitializing && 1023 HasEditorDestroyedDuringHandlingEditActionAndNotYetReinitialized()) { 1024 NS_WARNING("Editor was destroyed during an edit action being handled"); 1025 return false; 1026 } 1027 return IsDataAvailable(); 1028 } 1029 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1030 CanHandleAndMaybeDispatchBeforeInputEvent() { 1031 if (MOZ_UNLIKELY(NS_WARN_IF(!CanHandle()))) { 1032 return NS_ERROR_NOT_INITIALIZED; 1033 } 1034 nsresult rv = MaybeFlushPendingNotifications(); 1035 if (MOZ_UNLIKELY(NS_FAILED(rv))) { 1036 return rv; 1037 } 1038 return MaybeDispatchBeforeInputEvent(); 1039 } 1040 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1041 CanHandleAndFlushPendingNotifications() { 1042 if (MOZ_UNLIKELY(NS_WARN_IF(!CanHandle()))) { 1043 return NS_ERROR_NOT_INITIALIZED; 1044 } 1045 MOZ_ASSERT(MayEditActionRequireLayout(mRawEditAction)); 1046 return MaybeFlushPendingNotifications(); 1047 } 1048 1049 [[nodiscard]] bool IsDataAvailable() const { 1050 return mSelection && mEditorBase.mDocument; 1051 } 1052 1053 /** 1054 * MaybeDispatchBeforeInputEvent() considers whether this instance needs to 1055 * dispatch "beforeinput" event or not. Then, 1056 * mHasTriedToDispatchBeforeInputEvent is set to true. 1057 * 1058 * @param aDeleteDirectionAndAmount 1059 * If `MayEditActionDeleteAroundCollapsedSelection( 1060 * mEditAction)` returns true, this must be set. 1061 * Otherwise, don't set explicitly. 1062 * @return If this method actually dispatches "beforeinput" event 1063 * and it's canceled, returns 1064 * NS_ERROR_EDITOR_ACTION_CANCELED. 1065 */ 1066 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MaybeDispatchBeforeInputEvent( 1067 nsIEditor::EDirection aDeleteDirectionAndAmount = nsIEditor::eNone); 1068 1069 /** 1070 * MarkAsBeforeInputHasBeenDispatched() should be called only when updating 1071 * the DOM occurs asynchronously from user input (e.g., inserting blob 1072 * object which is loaded asynchronously) and `beforeinput` has already 1073 * been dispatched (always should be so). 1074 */ 1075 void MarkAsBeforeInputHasBeenDispatched() { 1076 MOZ_ASSERT(!HasTriedToDispatchBeforeInputEvent()); 1077 MOZ_ASSERT(mEditAction == EditAction::ePaste || 1078 mEditAction == EditAction::ePasteAsQuotation || 1079 mEditAction == EditAction::eDrop); 1080 mHasTriedToDispatchBeforeInputEvent = true; 1081 } 1082 1083 /** 1084 * MarkAsHandled() is called before dispatching `input` event and notifying 1085 * editor observers. After this is called, any nested edit action become 1086 * non illegal case. 1087 */ 1088 void MarkAsHandled() { 1089 MOZ_ASSERT(!mHandled); 1090 mHandled = true; 1091 } 1092 1093 /** 1094 * ShouldAlreadyHaveHandledBeforeInputEventDispatching() returns true if the 1095 * edit action requires to handle "beforeinput" event but not yet dispatched 1096 * it nor considered as not dispatched it and can dispatch it when this is 1097 * called. 1098 */ 1099 bool ShouldAlreadyHaveHandledBeforeInputEventDispatching() const { 1100 return !HasTriedToDispatchBeforeInputEvent() && 1101 NeedsBeforeInputEventHandling(mEditAction) && 1102 IsBeforeInputEventEnabled() /* && 1103 // If we still need to dispatch a clipboard event, we should 1104 // dispatch it first, then, we need to dispatch beforeinput 1105 // event later. 1106 !NeedsToDispatchClipboardEvent()*/ 1107 ; 1108 } 1109 1110 /** 1111 * HasTriedToDispatchBeforeInputEvent() returns true if the instance's 1112 * MaybeDispatchBeforeInputEvent() has already been called. 1113 */ 1114 bool HasTriedToDispatchBeforeInputEvent() const { 1115 return mHasTriedToDispatchBeforeInputEvent; 1116 } 1117 1118 bool IsCanceled() const { return mBeforeInputEventCanceled; } 1119 1120 /** 1121 * Returns a `Selection` for normal selection. The lifetime is guaranteed 1122 * during alive this instance in the stack. 1123 */ 1124 MOZ_KNOWN_LIVE Selection& SelectionRef() const { 1125 MOZ_ASSERT(!mSelection || 1126 (mSelection->GetType() == SelectionType::eNormal)); 1127 return *mSelection; 1128 } 1129 1130 Text* GetCachedTextNode() const { 1131 MOZ_ASSERT(mEditorBase.IsTextEditor()); 1132 return mTextNode; 1133 } 1134 1135 nsIPrincipal* GetPrincipal() const { return mPrincipal; } 1136 EditAction GetEditAction() const { return mEditAction; } 1137 1138 template <typename PT, typename CT> 1139 void SetSpellCheckRestartPoint(const EditorDOMPointBase<PT, CT>& aPoint) { 1140 MOZ_ASSERT(aPoint.IsSet()); 1141 // We should store only container and offset because new content may 1142 // be inserted before referring child. 1143 // XXX Shouldn't we compare whether aPoint is before 1144 // mSpellCheckRestartPoint if it's set. 1145 mSpellCheckRestartPoint = 1146 EditorDOMPoint(aPoint.GetContainer(), aPoint.Offset()); 1147 } 1148 void ClearSpellCheckRestartPoint() { mSpellCheckRestartPoint.Clear(); } 1149 const EditorDOMPoint& GetSpellCheckRestartPoint() const { 1150 return mSpellCheckRestartPoint; 1151 } 1152 1153 void SetData(const nsAString& aData) { 1154 MOZ_ASSERT(!mHasTriedToDispatchBeforeInputEvent, 1155 "It's too late to set data since this may have already " 1156 "dispatched a beforeinput event"); 1157 mData = aData; 1158 } 1159 const nsString& GetData() const { return mData; } 1160 1161 void SetColorData(const nsAString& aData); 1162 1163 /** 1164 * InitializeDataTransfer(DataTransfer*) sets mDataTransfer to 1165 * aDataTransfer. In this case, aDataTransfer should not be read/write 1166 * because it'll be set to InputEvent.dataTransfer and which should be 1167 * read-only. 1168 */ 1169 void InitializeDataTransfer(DataTransfer* aDataTransfer); 1170 /** 1171 * InitializeDataTransfer(nsITransferable*) creates new DataTransfer 1172 * instance, initializes it with aTransferable and sets mDataTransfer to 1173 * it. 1174 */ 1175 void InitializeDataTransfer(nsITransferable* aTransferable); 1176 /** 1177 * InitializeDataTransfer(const nsAString&) creates new DataTransfer 1178 * instance, initializes it with aString and sets mDataTransfer to it. 1179 */ 1180 void InitializeDataTransfer(const nsAString& aString); 1181 /** 1182 * InitializeDataTransferWithClipboard() creates new DataTransfer instance, 1183 * initializes it with clipboard and sets mDataTransfer to it. 1184 */ 1185 void InitializeDataTransferWithClipboard( 1186 SettingDataTransfer aSettingDataTransfer, DataTransfer* aDataTransfer, 1187 nsIClipboard::ClipboardType aClipboardType); 1188 DataTransfer* GetDataTransfer() const { return mDataTransfer; } 1189 1190 /** 1191 * AppendTargetRange() appends aTargetRange to target ranges. This should 1192 * be used only by edit action handlers which do not want to set target 1193 * ranges to selection ranges. 1194 */ 1195 void AppendTargetRange(dom::StaticRange& aTargetRange); 1196 void AppendTargetRange(RefPtr<dom::StaticRange>&& aTargetRange); 1197 1198 /** 1199 * Make dispatching `beforeinput` forcibly non-cancelable. 1200 */ 1201 void MakeBeforeInputEventNonCancelable() { 1202 mMakeBeforeInputEventNonCancelable = true; 1203 } 1204 1205 /** 1206 * NotifyOfDispatchingClipboardEvent() is called after dispatching 1207 * a clipboard event. 1208 */ 1209 void NotifyOfDispatchingClipboardEvent() { 1210 MOZ_ASSERT(NeedsToDispatchClipboardEvent()); 1211 MOZ_ASSERT(!mHasTriedToDispatchClipboardEvent); 1212 mHasTriedToDispatchClipboardEvent = true; 1213 } 1214 1215 void Abort() { mAborted = true; } 1216 bool IsAborted() const { return mAborted; } 1217 1218 void OnEditorDestroy() { 1219 if (!mHandled && mHasTriedToDispatchBeforeInputEvent) { 1220 // Remember the editor was destroyed only when this edit action is being 1221 // handled because they are caused by mutation event listeners or 1222 // something other unexpected event listeners. In the cases, new child 1223 // edit action shouldn't been aborted. 1224 mEditorWasDestroyedDuringHandlingEditAction = true; 1225 mEditorWasReinitialized = false; 1226 } 1227 if (mParentData) { 1228 mParentData->OnEditorDestroy(); 1229 } 1230 } 1231 void OnEditorInitialized(); 1232 /** 1233 * Return true if the editor was destroyed at least once while the 1234 * EditAction is being handled. Note that the editor may have already been 1235 * reinitialized even if this returns true. 1236 */ 1237 [[nodiscard]] bool HasEditorDestroyedDuringHandlingEditAction() const { 1238 return mEditorWasDestroyedDuringHandlingEditAction; 1239 } 1240 /** 1241 * Return true if the editor was destroyed while the EditAction is being 1242 * handled and has not been reinitialized. I.e., the editor is still under 1243 * the destroyed state. 1244 */ 1245 [[nodiscard]] bool 1246 HasEditorDestroyedDuringHandlingEditActionAndNotYetReinitialized() const { 1247 return mEditorWasDestroyedDuringHandlingEditAction && 1248 !mEditorWasReinitialized; 1249 } 1250 1251 void SetTopLevelEditSubAction(EditSubAction aEditSubAction, 1252 EDirection aDirection = eNone) { 1253 mTopLevelEditSubAction = aEditSubAction; 1254 TopLevelEditSubActionDataRef().Clear(); 1255 switch (mTopLevelEditSubAction) { 1256 case EditSubAction::eInsertNode: 1257 case EditSubAction::eMoveNode: 1258 case EditSubAction::eCreateNode: 1259 case EditSubAction::eSplitNode: 1260 case EditSubAction::eInsertText: 1261 case EditSubAction::eInsertTextComingFromIME: 1262 case EditSubAction::eSetTextProperty: 1263 case EditSubAction::eRemoveTextProperty: 1264 case EditSubAction::eRemoveAllTextProperties: 1265 case EditSubAction::eSetText: 1266 case EditSubAction::eInsertLineBreak: 1267 case EditSubAction::eInsertParagraphSeparator: 1268 case EditSubAction::eCreateOrChangeList: 1269 case EditSubAction::eIndent: 1270 case EditSubAction::eOutdent: 1271 case EditSubAction::eSetOrClearAlignment: 1272 case EditSubAction::eCreateOrRemoveBlock: 1273 case EditSubAction::eFormatBlockForHTMLCommand: 1274 case EditSubAction::eMergeBlockContents: 1275 case EditSubAction::eRemoveList: 1276 case EditSubAction::eCreateOrChangeDefinitionListItem: 1277 case EditSubAction::eInsertElement: 1278 case EditSubAction::eInsertQuotation: 1279 case EditSubAction::eInsertQuotedText: 1280 case EditSubAction::ePasteHTMLContent: 1281 case EditSubAction::eInsertHTMLSource: 1282 case EditSubAction::eSetPositionToAbsolute: 1283 case EditSubAction::eSetPositionToStatic: 1284 case EditSubAction::eDecreaseZIndex: 1285 case EditSubAction::eIncreaseZIndex: 1286 MOZ_ASSERT(aDirection == eNext); 1287 mDirectionOfTopLevelEditSubAction = eNext; 1288 break; 1289 case EditSubAction::eJoinNodes: 1290 case EditSubAction::eDeleteText: 1291 MOZ_ASSERT(aDirection == ePrevious); 1292 mDirectionOfTopLevelEditSubAction = ePrevious; 1293 break; 1294 case EditSubAction::eUndo: 1295 case EditSubAction::eRedo: 1296 case EditSubAction::eComputeTextToOutput: 1297 case EditSubAction::eCreatePaddingBRElementForEmptyEditor: 1298 case EditSubAction::eMaintainWhiteSpaceVisibility: 1299 case EditSubAction::eNone: 1300 MOZ_ASSERT(aDirection == eNone); 1301 mDirectionOfTopLevelEditSubAction = eNone; 1302 break; 1303 case EditSubAction::eDeleteNode: 1304 case EditSubAction::eDeleteSelectedContent: 1305 // Unfortunately, eDeleteNode and eDeleteSelectedContent is used with 1306 // any direction. We might have specific sub-action for each 1307 // direction, but there are some points referencing 1308 // eDeleteSelectedContent so that we should keep storing direction 1309 // as-is for now. 1310 mDirectionOfTopLevelEditSubAction = aDirection; 1311 break; 1312 } 1313 } 1314 EditSubAction GetTopLevelEditSubAction() const { 1315 MOZ_ASSERT(IsDataAvailable()); 1316 return mTopLevelEditSubAction; 1317 } 1318 EDirection GetDirectionOfTopLevelEditSubAction() const { 1319 return mDirectionOfTopLevelEditSubAction; 1320 } 1321 1322 const TopLevelEditSubActionData& TopLevelEditSubActionDataRef() const { 1323 return mParentData ? mParentData->TopLevelEditSubActionDataRef() 1324 : mTopLevelEditSubActionData; 1325 } 1326 TopLevelEditSubActionData& TopLevelEditSubActionDataRef() { 1327 return mParentData ? mParentData->TopLevelEditSubActionDataRef() 1328 : mTopLevelEditSubActionData; 1329 } 1330 1331 const EditSubActionData& EditSubActionDataRef() const { 1332 return mEditSubActionData; 1333 } 1334 EditSubActionData& EditSubActionDataRef() { return mEditSubActionData; } 1335 1336 SelectionState& SavedSelectionRef() { 1337 return mParentData ? mParentData->SavedSelectionRef() : mSavedSelection; 1338 } 1339 const SelectionState& SavedSelectionRef() const { 1340 return mParentData ? mParentData->SavedSelectionRef() : mSavedSelection; 1341 } 1342 1343 RangeUpdater& RangeUpdaterRef() { 1344 return mParentData ? mParentData->RangeUpdaterRef() : mRangeUpdater; 1345 } 1346 const RangeUpdater& RangeUpdaterRef() const { 1347 return mParentData ? mParentData->RangeUpdaterRef() : mRangeUpdater; 1348 } 1349 1350 MOZ_CAN_RUN_SCRIPT void UpdateSelectionCache(Selection& aSelection); 1351 1352 bool IsDispatchingInputEvent() const { 1353 return mDispatchingInputEvent || 1354 (mParentData && mParentData->IsDispatchingInputEvent()); 1355 } 1356 void WillDispatchInputEvent() { 1357 MOZ_ASSERT(!mDispatchingInputEvent); 1358 mDispatchingInputEvent = true; 1359 } 1360 void DidDispatchInputEvent() { 1361 MOZ_ASSERT(mDispatchingInputEvent); 1362 mDispatchingInputEvent = false; 1363 } 1364 1365 private: 1366 bool IsBeforeInputEventEnabled() const; 1367 1368 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1369 MaybeFlushPendingNotifications() const; 1370 1371 static bool NeedsBeforeInputEventHandling(EditAction aEditAction) { 1372 MOZ_ASSERT(aEditAction != EditAction::eNone); 1373 switch (aEditAction) { 1374 case EditAction::eNone: 1375 // If we're not handling edit action, we don't need to handle 1376 // "beforeinput" event. 1377 case EditAction::eNotEditing: 1378 // If we're being initialized, we may need to create a padding <br> 1379 // element, but it shouldn't cause `beforeinput` event. 1380 case EditAction::eInitializing: 1381 // If we're just selecting or getting table cells, we shouldn't 1382 // dispatch `beforeinput` event. 1383 case NS_EDIT_ACTION_CASES_ACCESSING_TABLE_DATA_WITHOUT_EDITING: 1384 // If raw level transaction API is used, the API user needs to handle 1385 // both "beforeinput" event and "input" event if it's necessary. 1386 case EditAction::eUnknown: 1387 // Hiding/showing password affects only layout so that we don't need 1388 // to handle beforeinput event for it. 1389 case EditAction::eHidePassword: 1390 // We don't need to dispatch "beforeinput" event before 1391 // "compositionstart". 1392 case EditAction::eStartComposition: 1393 // We don't need to let web apps know the mode change. 1394 case EditAction::eEnableOrDisableCSS: 1395 case EditAction::eEnableOrDisableAbsolutePositionEditor: 1396 case EditAction::eEnableOrDisableResizer: 1397 case EditAction::eEnableOrDisableInlineTableEditingUI: 1398 // We don't need to let contents in chrome's editor to know the size 1399 // change. 1400 case EditAction::eSetWrapWidth: 1401 // While resizing or moving element, we update only shadow, i.e., 1402 // don't touch to the DOM in content. Therefore, we don't need to 1403 // dispatch "beforeinput" event. 1404 case EditAction::eResizingElement: 1405 case EditAction::eMovingElement: 1406 // Perhaps, we don't need to dispatch "beforeinput" event for 1407 // padding `<br>` element for empty editor because it's internal 1408 // handling and it should be occurred by another change. 1409 case EditAction::eCreatePaddingBRElementForEmptyEditor: 1410 return false; 1411 default: 1412 return true; 1413 } 1414 } 1415 1416 bool NeedsToDispatchClipboardEvent() const { 1417 if (mHasTriedToDispatchClipboardEvent) { 1418 return false; 1419 } 1420 switch (mEditAction) { 1421 case EditAction::ePaste: 1422 case EditAction::ePasteAsQuotation: 1423 case EditAction::eCut: 1424 case EditAction::eCopy: 1425 return true; 1426 default: 1427 return false; 1428 } 1429 } 1430 1431 void MarkEditActionCanceled(); 1432 1433 EditorBase& mEditorBase; 1434 RefPtr<Selection> mSelection; 1435 nsTArray<OwningNonNull<Selection>> mRetiredSelections; 1436 1437 // mTextNode is the text node if and only if the instance is TextEditor. 1438 // This is set when the instance is created and updated when the TextEditor 1439 // is reinitialized with the new native anonymous subtree. 1440 RefPtr<Text> mTextNode; 1441 1442 // True if the selection was created by doubleclicking a word. 1443 bool mSelectionCreatedByDoubleclick{false}; 1444 1445 nsCOMPtr<nsIPrincipal> mPrincipal; 1446 // EditAction may be nested, for example, a command may be executed 1447 // from mutation event listener which is run while editor changes 1448 // the DOM tree. In such case, we need to handle edit action separately. 1449 AutoEditActionDataSetter* mParentData; 1450 1451 // Cached selection for AutoSelectionRestorer. 1452 SelectionState mSavedSelection; 1453 1454 // Utility class object for maintaining preserved ranges. 1455 RangeUpdater mRangeUpdater; 1456 1457 // The data should be set to InputEvent.data. 1458 nsString mData; 1459 1460 // The dataTransfer should be set to InputEvent.dataTransfer. 1461 RefPtr<DataTransfer> mDataTransfer; 1462 1463 // They are used for result of InputEvent.getTargetRanges() of beforeinput. 1464 OwningNonNullStaticRangeArray mTargetRanges; 1465 1466 // Start point where spell checker should check from. This is used only 1467 // by TextEditor. 1468 EditorDOMPoint mSpellCheckRestartPoint; 1469 1470 // Different from mTopLevelEditSubAction, its data should be stored only 1471 // in the most ancestor AutoEditActionDataSetter instance since we don't 1472 // want to pay the copying cost and sync cost. 1473 TopLevelEditSubActionData mTopLevelEditSubActionData; 1474 1475 // Different from mTopLevelEditSubActionData, this stores temporaly data 1476 // for current edit sub action. 1477 EditSubActionData mEditSubActionData; 1478 1479 // mEditAction and mRawEditActions stores edit action. The difference of 1480 // them is, if and only if edit actions are nested and parent edit action 1481 // is one of trying to edit something, but nested one is not so, it's 1482 // overwritten by the parent edit action. 1483 EditAction mEditAction; 1484 EditAction mRawEditAction; 1485 1486 // Different from its data, you can refer "current" AutoEditActionDataSetter 1487 // instance's mTopLevelEditSubAction member since it's copied from the 1488 // parent instance at construction and it's always cleared before this 1489 // won't be overwritten and cleared before destruction. 1490 EditSubAction mTopLevelEditSubAction = EditSubAction::eNone; 1491 1492 EDirection mDirectionOfTopLevelEditSubAction = nsIEditor::eNone; 1493 1494 bool mAborted = false; 1495 1496 // Set to true when this handles "beforeinput" event dispatching. Note 1497 // that even if "beforeinput" event shouldn't be dispatched for this, 1498 // instance, this is set to true when it's considered. 1499 bool mHasTriedToDispatchBeforeInputEvent = false; 1500 // Set to true if "beforeinput" event was dispatched and it's canceled. 1501 bool mBeforeInputEventCanceled = false; 1502 // Set to true if `beforeinput` event must not be cancelable even if 1503 // its inputType is defined as cancelable by the standards. 1504 bool mMakeBeforeInputEventNonCancelable = false; 1505 // Set to true when the edit action handler tries to dispatch a clipboard 1506 // event. 1507 bool mHasTriedToDispatchClipboardEvent = false; 1508 // The editor instance may be destroyed once temporarily if `document.write` 1509 // etc runs. In such case, we should mark this flag of being handled 1510 // edit action. 1511 bool mEditorWasDestroyedDuringHandlingEditAction; 1512 // This is set to `true` if the editor was destroyed but now, it's 1513 // initialized again. 1514 bool mEditorWasReinitialized; 1515 // This is set before dispatching `input` event and notifying editor 1516 // observers. 1517 bool mHandled = false; 1518 // Whether the editor is dispatching a `beforeinput` or `input` event. 1519 bool mDispatchingInputEvent = false; 1520 1521 #ifdef DEBUG 1522 mutable bool mHasCanHandleChecked = false; 1523 #endif // #ifdef DEBUG 1524 }; 1525 1526 void UpdateEditActionData(const nsAString& aData) { 1527 mEditActionData->SetData(aData); 1528 } 1529 1530 void NotifyOfDispatchingClipboardEvent() { 1531 MOZ_ASSERT(mEditActionData); 1532 mEditActionData->NotifyOfDispatchingClipboardEvent(); 1533 } 1534 1535 protected: // May be called by friends. 1536 /**************************************************************************** 1537 * Some friend classes are allowed to call the following protected methods. 1538 * However, those methods won't prepare caches of some objects which are 1539 * necessary for them. So, if you call them from friend classes, you need 1540 * to make sure that AutoEditActionDataSetter is created. 1541 ****************************************************************************/ 1542 1543 bool IsEditActionCanceled() const { 1544 MOZ_ASSERT(mEditActionData); 1545 return mEditActionData->IsCanceled(); 1546 } 1547 1548 bool ShouldAlreadyHaveHandledBeforeInputEventDispatching() const { 1549 MOZ_ASSERT(mEditActionData); 1550 return mEditActionData 1551 ->ShouldAlreadyHaveHandledBeforeInputEventDispatching(); 1552 } 1553 1554 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MaybeDispatchBeforeInputEvent() { 1555 MOZ_ASSERT(mEditActionData); 1556 return mEditActionData->MaybeDispatchBeforeInputEvent(); 1557 } 1558 1559 void MarkAsBeforeInputHasBeenDispatched() { 1560 MOZ_ASSERT(mEditActionData); 1561 return mEditActionData->MarkAsBeforeInputHasBeenDispatched(); 1562 } 1563 1564 bool HasTriedToDispatchBeforeInputEvent() const { 1565 return mEditActionData && 1566 mEditActionData->HasTriedToDispatchBeforeInputEvent(); 1567 } 1568 1569 bool IsEditActionDataAvailable() const { 1570 return mEditActionData && mEditActionData->IsDataAvailable(); 1571 } 1572 1573 bool IsTopLevelEditSubActionDataAvailable() const { 1574 return mEditActionData && !!GetTopLevelEditSubAction(); 1575 } 1576 1577 bool IsEditActionAborted() const { 1578 MOZ_ASSERT(mEditActionData); 1579 return mEditActionData->IsAborted(); 1580 } 1581 1582 nsresult GetDataFromDataTransferOrClipboard( 1583 DataTransfer* aDataTransfer, nsITransferable* aTransferable, 1584 nsIClipboard::ClipboardType aClipboardType) const; 1585 1586 /** 1587 * SelectionRef() returns cached normal Selection. This is pretty faster than 1588 * EditorBase::GetSelection() if available. 1589 * Note that this never crash unless public methods ignore the result of 1590 * AutoEditActionDataSetter::CanHandle() and keep handling edit action but any 1591 * methods should stop handling edit action if it returns false. 1592 */ 1593 MOZ_KNOWN_LIVE Selection& SelectionRef() const { 1594 MOZ_ASSERT(mEditActionData); 1595 MOZ_ASSERT(mEditActionData->SelectionRef().GetType() == 1596 SelectionType::eNormal); 1597 return mEditActionData->SelectionRef(); 1598 } 1599 1600 // Return the Text if and only if we're a TextEditor instance. It's cached 1601 // while we're handling an edit action, so, this stores the latest value even 1602 // after we have been destroyed. 1603 Text* GetCachedTextNode() { 1604 MOZ_ASSERT(IsTextEditor()); 1605 return mEditActionData ? mEditActionData->GetCachedTextNode() : nullptr; 1606 } 1607 1608 // Return the Text if and only if we're a TextEditor instance. It's cached 1609 // while we're handling an edit action, so, this stores the latest value even 1610 // after we have been destroyed. 1611 const Text* GetCachedTextNode() const { 1612 MOZ_ASSERT(IsTextEditor()); 1613 return const_cast<EditorBase*>(this)->GetCachedTextNode(); 1614 } 1615 1616 nsIPrincipal* GetEditActionPrincipal() const { 1617 MOZ_ASSERT(mEditActionData); 1618 return mEditActionData->GetPrincipal(); 1619 } 1620 1621 /** 1622 * GetEditAction() returns EditAction which is being handled. If some 1623 * edit actions are nested, this returns the innermost edit action. 1624 */ 1625 EditAction GetEditAction() const { 1626 return mEditActionData ? mEditActionData->GetEditAction() 1627 : EditAction::eNone; 1628 } 1629 1630 /** 1631 * GetInputEventData() returns inserting or inserted text value with 1632 * current edit action. The result is proper for InputEvent.data value. 1633 */ 1634 const nsString& GetInputEventData() const { 1635 return mEditActionData ? mEditActionData->GetData() : VoidString(); 1636 } 1637 1638 /** 1639 * GetInputEventDataTransfer() returns inserting or inserted transferable 1640 * content with current edit action. The result is proper for 1641 * InputEvent.dataTransfer value. 1642 */ 1643 DataTransfer* GetInputEventDataTransfer() const { 1644 return mEditActionData ? mEditActionData->GetDataTransfer() : nullptr; 1645 } 1646 1647 /** 1648 * GetTopLevelEditSubAction() returns the top level edit sub-action. 1649 * For example, if selected content is being replaced with inserted text, 1650 * while removing selected content, the top level edit sub-action may be 1651 * EditSubAction::eDeleteSelectedContent. However, while inserting new 1652 * text, the top level edit sub-action may be EditSubAction::eInsertText. 1653 * So, this result means what we are doing right now unless you're looking 1654 * for a case which the method is called via mutation event listener or 1655 * selectionchange event listener which are fired while handling the edit 1656 * sub-action. 1657 */ 1658 EditSubAction GetTopLevelEditSubAction() const { 1659 return mEditActionData ? mEditActionData->GetTopLevelEditSubAction() 1660 : EditSubAction::eNone; 1661 } 1662 1663 /** 1664 * GetDirectionOfTopLevelEditSubAction() returns direction which user 1665 * intended for doing the edit sub-action. 1666 */ 1667 EDirection GetDirectionOfTopLevelEditSubAction() const { 1668 return mEditActionData 1669 ? mEditActionData->GetDirectionOfTopLevelEditSubAction() 1670 : eNone; 1671 } 1672 1673 /** 1674 * SavedSelection() returns reference to saved selection which are 1675 * stored by AutoSelectionRestorer. 1676 */ 1677 SelectionState& SavedSelectionRef() { 1678 MOZ_ASSERT(IsEditActionDataAvailable()); 1679 return mEditActionData->SavedSelectionRef(); 1680 } 1681 const SelectionState& SavedSelectionRef() const { 1682 MOZ_ASSERT(IsEditActionDataAvailable()); 1683 return mEditActionData->SavedSelectionRef(); 1684 } 1685 1686 RangeUpdater& RangeUpdaterRef() { 1687 MOZ_ASSERT(IsEditActionDataAvailable()); 1688 return mEditActionData->RangeUpdaterRef(); 1689 } 1690 const RangeUpdater& RangeUpdaterRef() const { 1691 MOZ_ASSERT(IsEditActionDataAvailable()); 1692 return mEditActionData->RangeUpdaterRef(); 1693 } 1694 1695 template <typename PT, typename CT> 1696 void SetSpellCheckRestartPoint(const EditorDOMPointBase<PT, CT>& aPoint) { 1697 MOZ_ASSERT(IsEditActionDataAvailable()); 1698 return mEditActionData->SetSpellCheckRestartPoint(aPoint); 1699 } 1700 1701 void ClearSpellCheckRestartPoint() { 1702 MOZ_ASSERT(IsEditActionDataAvailable()); 1703 return mEditActionData->ClearSpellCheckRestartPoint(); 1704 } 1705 1706 const EditorDOMPoint& GetSpellCheckRestartPoint() const { 1707 MOZ_ASSERT(IsEditActionDataAvailable()); 1708 return mEditActionData->GetSpellCheckRestartPoint(); 1709 } 1710 1711 const TopLevelEditSubActionData& TopLevelEditSubActionDataRef() const { 1712 MOZ_ASSERT(IsEditActionDataAvailable()); 1713 return mEditActionData->TopLevelEditSubActionDataRef(); 1714 } 1715 TopLevelEditSubActionData& TopLevelEditSubActionDataRef() { 1716 MOZ_ASSERT(IsEditActionDataAvailable()); 1717 return mEditActionData->TopLevelEditSubActionDataRef(); 1718 } 1719 1720 const EditSubActionData& EditSubActionDataRef() const { 1721 MOZ_ASSERT(IsEditActionDataAvailable()); 1722 return mEditActionData->EditSubActionDataRef(); 1723 } 1724 EditSubActionData& EditSubActionDataRef() { 1725 MOZ_ASSERT(IsEditActionDataAvailable()); 1726 return mEditActionData->EditSubActionDataRef(); 1727 } 1728 1729 /** 1730 * GetFirstIMESelectionStartPoint() and GetLastIMESelectionEndPoint() returns 1731 * start of first IME selection range or end of last IME selection range if 1732 * there is. Otherwise, returns non-set DOM point. 1733 */ 1734 template <typename EditorDOMPointType> 1735 EditorDOMPointType GetFirstIMESelectionStartPoint() const; 1736 template <typename EditorDOMPointType> 1737 EditorDOMPointType GetLastIMESelectionEndPoint() const; 1738 1739 /** 1740 * IsSelectionRangeContainerNotContent() returns true if one of container 1741 * of selection ranges is not a content node, i.e., a Document node. 1742 */ 1743 bool IsSelectionRangeContainerNotContent() const; 1744 1745 /** 1746 * OnInputText() is called when user inputs text with keyboard or something. 1747 * 1748 * @param aStringToInsert The string to insert. 1749 */ 1750 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1751 OnInputText(const nsAString& aStringToInsert); 1752 1753 enum class InsertTextFor { 1754 NormalText, 1755 CompositionStart, 1756 CompositionUpdate, 1757 CompositionEnd, 1758 CompositionStartAndEnd, 1759 }; 1760 friend inline std::ostream& operator<<(std::ostream& aStream, 1761 const InsertTextFor& aPurpose) { 1762 switch (aPurpose) { 1763 case InsertTextFor::NormalText: 1764 return aStream << "InsertTextFor::NormalText"; 1765 case InsertTextFor::CompositionStart: 1766 return aStream << "InsertTextFor::CompositionStart"; 1767 case InsertTextFor::CompositionUpdate: 1768 return aStream << "InsertTextFor::CompositionUpdate"; 1769 case InsertTextFor::CompositionEnd: 1770 return aStream << "InsertTextFor::CompositionEnd"; 1771 case InsertTextFor::CompositionStartAndEnd: 1772 return aStream << "InsertTextFor::CompositionStartAndEnd"; 1773 } 1774 return aStream << "<illegal value>"; 1775 } 1776 [[nodiscard]] static bool InsertingTextForComposition( 1777 InsertTextFor aPurpose) { 1778 return aPurpose != InsertTextFor::NormalText; 1779 } 1780 [[nodiscard]] static bool InsertingTextForExtantComposition( 1781 InsertTextFor aPurpose) { 1782 return aPurpose == InsertTextFor::CompositionUpdate || 1783 aPurpose == InsertTextFor::CompositionEnd; 1784 } 1785 [[nodiscard]] static bool InsertingTextForStartingComposition( 1786 InsertTextFor aPurpose) { 1787 return aPurpose == InsertTextFor::CompositionStart || 1788 aPurpose == InsertTextFor::CompositionStartAndEnd; 1789 } 1790 [[nodiscard]] static bool InsertingTextForCommittingComposition( 1791 InsertTextFor aPurpose) { 1792 return aPurpose == InsertTextFor::CompositionEnd || 1793 aPurpose == InsertTextFor::CompositionStartAndEnd; 1794 } 1795 [[nodiscard]] static bool NothingToDoIfInsertingEmptyText( 1796 InsertTextFor aPurpose) { 1797 return aPurpose == InsertTextFor::NormalText || 1798 aPurpose == InsertTextFor::CompositionStartAndEnd; 1799 } 1800 1801 /** 1802 * InsertTextAsSubAction() inserts aStringToInsert at selection. This 1803 * should be used for handling it as an edit sub-action. 1804 * 1805 * @param aStringToInsert The string to insert. 1806 * @param aPurpose Specify the purpose to insert text. 1807 */ 1808 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertTextAsSubAction( 1809 const nsAString& aStringToInsert, InsertTextFor aPurpose); 1810 1811 /** 1812 * Insert aStringToInsert to aPointToInsert or better insertion point around 1813 * it. If aPointToInsert isn't in a text node, this method looks for the 1814 * nearest point in a text node with TextEditor::FindBetterInsertionPoint() 1815 * or EditorDOMPoint::GetPointInTextNodeIfPointingAroundTextNode(). 1816 * If there is no text node, this creates new text node and put 1817 * aStringToInsert to it. 1818 * 1819 * @param aStringToInsert The string to insert. 1820 * @param aPointToInsert The point to insert aStringToInsert. 1821 * Must be valid DOM point. 1822 * @param aInsertTextTo Whether forcibly creates a new `Text` node in 1823 * specific condition or use existing `Text` if 1824 * available. 1825 */ 1826 enum class InsertTextTo { 1827 SpecifiedPoint, 1828 ExistingTextNodeIfAvailable, 1829 ExistingTextNodeIfAvailableAndNotStart, 1830 AlwaysCreateNewTextNode 1831 }; 1832 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual Result<InsertTextResult, nsresult> 1833 InsertTextWithTransaction(const nsAString& aStringToInsert, 1834 const EditorDOMPoint& aPointToInsert, 1835 InsertTextTo aInsertTextTo); 1836 1837 /** 1838 * Compute insertion point from aPoint and aInsertTextTo. 1839 */ 1840 [[nodiscard]] EditorDOMPoint ComputePointToInsertText( 1841 const EditorDOMPoint& aPoint, InsertTextTo aInsertTextTo) const; 1842 1843 /** 1844 * Insert aStringToInsert to aPointToInsert. 1845 */ 1846 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<InsertTextResult, nsresult> 1847 InsertTextIntoTextNodeWithTransaction( 1848 const nsAString& aStringToInsert, 1849 const EditorDOMPointInText& aPointToInsert); 1850 1851 /** 1852 * SetTextNodeWithoutTransaction() is optimized path to set new value to 1853 * the text node directly and without transaction. This is used when 1854 * setting `<input>.value` and `<textarea>.value`. 1855 */ 1856 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1857 SetTextNodeWithoutTransaction(const nsAString& aString, Text& aTextNode); 1858 1859 /** 1860 * DeleteNodeWithTransaction() removes aContent from the DOM tree. 1861 * 1862 * @param aContent The node which will be removed form the DOM tree. 1863 */ 1864 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1865 DeleteNodeWithTransaction(nsIContent& aContent); 1866 1867 /** 1868 * InsertNodeWithTransaction() inserts aContentToInsert before the child 1869 * specified by aPointToInsert. 1870 * 1871 * @param aContentToInsert The node to be inserted. 1872 * @param aPointToInsert The insertion point of aContentToInsert. 1873 * If this refers end of the container, the 1874 * transaction will append the node to the 1875 * container. Otherwise, will insert the node 1876 * before child node referred by this. 1877 * @return If succeeded, returns the new content node and 1878 * point to put caret. 1879 */ 1880 template <typename ContentNodeType> 1881 [[nodiscard]] MOZ_CAN_RUN_SCRIPT 1882 Result<CreateNodeResultBase<ContentNodeType>, nsresult> 1883 InsertNodeWithTransaction(ContentNodeType& aContentToInsert, 1884 const EditorDOMPoint& aPointToInsert); 1885 1886 /** 1887 * InsertPaddingBRElementForEmptyLastLineWithTransaction() creates a padding 1888 * <br> element with setting flags to NS_PADDING_FOR_EMPTY_LAST_LINE and 1889 * inserts it around aPointToInsert. 1890 * 1891 * @param aPointToInsert The DOM point where should be <br> node inserted 1892 * before. 1893 * @return If succeeded, returns the new <br> element and 1894 * point to put caret around it. 1895 */ 1896 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1897 InsertPaddingBRElementForEmptyLastLineWithTransaction( 1898 const EditorDOMPoint& aPointToInsert); 1899 1900 enum class BRElementType { 1901 Normal, 1902 PaddingForEmptyEditor, 1903 PaddingForEmptyLastLine 1904 }; 1905 /** 1906 * Updates the type of aBRElement. If it will be hidden or shown from 1907 * IMEContentObserver and ContentEventHandler points of view, this temporarily 1908 * removes the node and reconnect to the same position. 1909 */ 1910 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1911 UpdateBRElementType(dom::HTMLBRElement& aBRElement, BRElementType aNewType); 1912 1913 /** 1914 * Create and insert a line break to aPointToInsert. 1915 */ 1916 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1917 InsertBRElement(WithTransaction aWithTransaction, 1918 BRElementType aBRElementType, 1919 const EditorDOMPoint& aPointToInsert); 1920 1921 /** 1922 * CloneAttributesWithTransaction() clones all attributes from 1923 * aSourceElement to aDestElement after removing all attributes in 1924 * aDestElement. 1925 */ 1926 MOZ_CAN_RUN_SCRIPT void CloneAttributesWithTransaction( 1927 Element& aDestElement, Element& aSourceElement); 1928 1929 /** 1930 * CloneAttributeWithTransaction() copies aAttribute of aSourceElement to 1931 * aDestElement. If aSourceElement doesn't have aAttribute, this removes 1932 * aAttribute from aDestElement. 1933 * 1934 * @param aAttribute Attribute name to be cloned. 1935 * @param aDestElement Element node which will be set aAttribute or 1936 * whose aAttribute will be removed. 1937 * @param aSourceElement Element node which provides the value of 1938 * aAttribute in aDestElement. 1939 */ 1940 MOZ_CAN_RUN_SCRIPT nsresult CloneAttributeWithTransaction( 1941 nsAtom& aAttribute, Element& aDestElement, Element& aSourceElement); 1942 1943 /** 1944 * RemoveAttributeWithTransaction() removes aAttribute from aElement. 1945 * 1946 * @param aElement Element node which will lose aAttribute. 1947 * @param aAttribute Attribute name to be removed from aElement. 1948 */ 1949 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1950 RemoveAttributeWithTransaction(Element& aElement, nsAtom& aAttribute); 1951 1952 MOZ_CAN_RUN_SCRIPT virtual nsresult RemoveAttributeOrEquivalent( 1953 Element* aElement, nsAtom* aAttribute, bool aSuppressTransaction) = 0; 1954 1955 /** 1956 * SetAttributeWithTransaction() sets aAttribute of aElement to aValue. 1957 * 1958 * @param aElement Element node which will have aAttribute. 1959 * @param aAttribute Attribute name to be set. 1960 * @param aValue Attribute value be set to aAttribute. 1961 */ 1962 MOZ_CAN_RUN_SCRIPT nsresult SetAttributeWithTransaction( 1963 Element& aElement, nsAtom& aAttribute, const nsAString& aValue); 1964 1965 MOZ_CAN_RUN_SCRIPT virtual nsresult SetAttributeOrEquivalent( 1966 Element* aElement, nsAtom* aAttribute, const nsAString& aValue, 1967 bool aSuppressTransaction) = 0; 1968 1969 /** 1970 * Method to replace certain CreateElementNS() calls. 1971 * 1972 * @param aTag Tag you want. 1973 */ 1974 already_AddRefed<Element> CreateHTMLContent(const nsAtom* aTag) const; 1975 1976 /** 1977 * Creates text node which is marked as "maybe modified frequently" and 1978 * "maybe masked" if this is a password editor. 1979 */ 1980 already_AddRefed<nsTextNode> CreateTextNode(const nsAString& aData) const; 1981 1982 /** 1983 * DoInsertText(), DoDeleteText(), DoReplaceText() and DoSetText() are 1984 * wrapper of `CharacterData::InsertData()`, `CharacterData::DeleteData()`, 1985 * `CharacterData::ReplaceData()` and `CharacterData::SetData()`. 1986 */ 1987 MOZ_CAN_RUN_SCRIPT void DoInsertText(dom::Text& aText, uint32_t aOffset, 1988 const nsAString& aStringToInsert, 1989 ErrorResult& aRv); 1990 MOZ_CAN_RUN_SCRIPT void DoDeleteText(dom::Text& aText, uint32_t aOffset, 1991 uint32_t aCount, ErrorResult& aRv); 1992 MOZ_CAN_RUN_SCRIPT void DoReplaceText(dom::Text& aText, uint32_t aOffset, 1993 uint32_t aCount, 1994 const nsAString& aStringToInsert, 1995 ErrorResult& aRv); 1996 MOZ_CAN_RUN_SCRIPT void DoSetText(dom::Text& aText, 1997 const nsAString& aStringToSet, 1998 ErrorResult& aRv); 1999 2000 /** 2001 * Delete text in the range in aTextNode. Use 2002 * `HTMLEditor::ReplaceTextWithTransaction` if you'll insert text there (and 2003 * if you want to use it in `TextEditor`, move it into `EditorBase`). 2004 * 2005 * @param aTextNode The text node which should be modified. 2006 * @param aOffset Start offset of removing text in aTextNode. 2007 * @param aLength Length of removing text. 2008 */ 2009 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CaretPoint, nsresult> 2010 DeleteTextWithTransaction(dom::Text& aTextNode, uint32_t aOffset, 2011 uint32_t aLength); 2012 2013 /** 2014 * MarkElementDirty() sets a special dirty attribute on the element. 2015 * Usually this will be called immediately after creating a new node. 2016 * 2017 * @param aElement The element for which to insert formatting. 2018 */ 2019 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MarkElementDirty(Element& aElement); 2020 2021 MOZ_CAN_RUN_SCRIPT nsresult 2022 DoTransactionInternal(nsITransaction* aTransaction); 2023 2024 /** 2025 * Returns true if aNode is our root node. The root is: 2026 * If TextEditor, the anonymous <div> element. 2027 * If HTMLEditor, a <body> element or the document element which may not be 2028 * editable if it's not in the design mode. 2029 */ 2030 bool IsRoot(const nsINode* inNode) const; 2031 2032 /** 2033 * Returns true if aNode is a descendant of our root node. 2034 * See the comment for IsRoot() for what the root node means. 2035 */ 2036 bool IsDescendantOfRoot(const nsINode* inNode) const; 2037 2038 /** 2039 * Returns true when inserting text should be a part of current composition. 2040 */ 2041 bool ShouldHandleIMEComposition() const; 2042 2043 template <typename EditorDOMPointType> 2044 EditorDOMPointType GetFirstSelectionStartPoint() const; 2045 template <typename EditorDOMPointType> 2046 EditorDOMPointType GetFirstSelectionEndPoint() const; 2047 2048 static nsresult GetEndChildNode(const Selection& aSelection, 2049 nsIContent** aEndNode); 2050 2051 template <typename PT, typename CT> 2052 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2053 CollapseSelectionTo(const EditorDOMPointBase<PT, CT>& aPoint) const { 2054 // We don't need to throw exception directly for a failure of updating 2055 // selection. Therefore, let's use IgnoredErrorResult for the performance. 2056 IgnoredErrorResult error; 2057 CollapseSelectionTo(aPoint, error); 2058 return error.StealNSResult(); 2059 } 2060 2061 template <typename PT, typename CT> 2062 MOZ_CAN_RUN_SCRIPT void CollapseSelectionTo( 2063 const EditorDOMPointBase<PT, CT>& aPoint, ErrorResult& aRv) const { 2064 MOZ_ASSERT(IsEditActionDataAvailable()); 2065 MOZ_ASSERT(!aRv.Failed()); 2066 2067 if (aPoint.GetInterlinePosition() != InterlinePosition::Undefined) { 2068 if (MOZ_UNLIKELY(NS_FAILED(SelectionRef().SetInterlinePosition( 2069 aPoint.GetInterlinePosition())))) { 2070 NS_WARNING("Selection::SetInterlinePosition() failed"); 2071 aRv.Throw(NS_ERROR_FAILURE); 2072 return; 2073 } 2074 } 2075 2076 SelectionRef().CollapseInLimiter(aPoint, aRv); 2077 if (MOZ_UNLIKELY(Destroyed())) { 2078 NS_WARNING("Selection::CollapseInLimiter() caused destroying the editor"); 2079 aRv.Throw(NS_ERROR_EDITOR_DESTROYED); 2080 return; 2081 } 2082 NS_WARNING_ASSERTION(!aRv.Failed(), 2083 "Selection::CollapseInLimiter() failed"); 2084 } 2085 2086 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2087 CollapseSelectionToStartOf(nsINode& aNode) const { 2088 return CollapseSelectionTo(EditorRawDOMPoint(&aNode, 0u)); 2089 } 2090 2091 MOZ_CAN_RUN_SCRIPT void CollapseSelectionToStartOf(nsINode& aNode, 2092 ErrorResult& aRv) const { 2093 CollapseSelectionTo(EditorRawDOMPoint(&aNode, 0u), aRv); 2094 } 2095 2096 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2097 CollapseSelectionToEndOf(nsINode& aNode) const { 2098 return CollapseSelectionTo(EditorRawDOMPoint::AtEndOf(aNode)); 2099 } 2100 2101 MOZ_CAN_RUN_SCRIPT void CollapseSelectionToEndOf(nsINode& aNode, 2102 ErrorResult& aRv) const { 2103 CollapseSelectionTo(EditorRawDOMPoint::AtEndOf(aNode), aRv); 2104 } 2105 2106 /** 2107 * AllowsTransactionsToChangeSelection() returns true if editor allows any 2108 * transactions to change Selection. Otherwise, transactions shouldn't 2109 * change Selection. 2110 */ 2111 inline bool AllowsTransactionsToChangeSelection() const { 2112 return mAllowsTransactionsToChangeSelection; 2113 } 2114 2115 /** 2116 * MakeThisAllowTransactionsToChangeSelection() with true makes this editor 2117 * allow transactions to change Selection. Otherwise, i.e., with false, 2118 * makes this editor not allow transactions to change Selection. 2119 */ 2120 inline void MakeThisAllowTransactionsToChangeSelection(bool aAllow) { 2121 mAllowsTransactionsToChangeSelection = aAllow; 2122 } 2123 2124 nsresult HandleInlineSpellCheck( 2125 const EditorDOMPoint& aPreviouslySelectedStart, 2126 const dom::AbstractRange* aRange = nullptr); 2127 2128 /** 2129 * Whether the editor is active on the DOM window. Note that when this 2130 * returns true but GetFocusedElement() returns null, it means that this 2131 * editor was focused when the DOM window was active. 2132 */ 2133 virtual bool IsActiveInDOMWindow() const; 2134 2135 /** 2136 * HideCaret() hides caret with nsCaret::AddForceHide() or may show carent 2137 * with nsCaret::RemoveForceHide(). This does NOT set visibility of 2138 * nsCaret. Therefore, this is stateless. 2139 */ 2140 void HideCaret(bool aHide); 2141 2142 protected: // Edit sub-action handler 2143 /** 2144 * AutoCaretBidiLevelManager() computes bidi level of caret, deleting 2145 * character(s) from aPointAtCaret at construction. Then, if you'll 2146 * need to extend the selection, you should calls `UpdateCaretBidiLevel()`, 2147 * then, this class may update caret bidi level for you if it's required. 2148 */ 2149 class MOZ_RAII AutoCaretBidiLevelManager final { 2150 public: 2151 /** 2152 * @param aEditorBase The editor. 2153 * @param aPointAtCaret Collapsed `Selection` point. 2154 * @param aDirectionAndAmount The direction and amount to delete. 2155 */ 2156 template <typename PT, typename CT> 2157 AutoCaretBidiLevelManager(const EditorBase& aEditorBase, 2158 nsIEditor::EDirection aDirectionAndAmount, 2159 const EditorDOMPointBase<PT, CT>& aPointAtCaret); 2160 2161 /** 2162 * Failed() returns true if the constructor failed to handle the bidi 2163 * information. 2164 */ 2165 bool Failed() const { return mFailed; } 2166 2167 /** 2168 * Canceled() returns true if when the caller should stop deleting 2169 * characters since caret position is not visually adjacent the deleting 2170 * characters and user does not wand to delete them in that case. 2171 */ 2172 bool Canceled() const { return mCanceled; } 2173 2174 /** 2175 * MaybeUpdateCaretBidiLevel() may update caret bidi level and schedule to 2176 * paint it if they are necessary. 2177 */ 2178 void MaybeUpdateCaretBidiLevel(const EditorBase& aEditorBase) const; 2179 2180 private: 2181 Maybe<mozilla::intl::BidiEmbeddingLevel> mNewCaretBidiLevel; 2182 bool mFailed = false; 2183 bool mCanceled = false; 2184 }; 2185 2186 /** 2187 * UndefineCaretBidiLevel() resets bidi level of the caret. 2188 */ 2189 void UndefineCaretBidiLevel() const; 2190 2191 /** 2192 * Flushing pending notifications if nsFrameSelection requires the latest 2193 * layout information to compute deletion range. This may destroy the 2194 * editor instance itself. When this returns false, don't keep doing 2195 * anything. 2196 */ 2197 [[nodiscard]] MOZ_CAN_RUN_SCRIPT bool 2198 FlushPendingNotificationsIfToHandleDeletionWithFrameSelection( 2199 nsIEditor::EDirection aDirectionAndAmount) const; 2200 2201 /** 2202 * DeleteSelectionAsSubAction() removes selection content or content around 2203 * caret with transactions. This should be used for handling it as an 2204 * edit sub-action. 2205 * 2206 * @param aDirectionAndAmount How much range should be removed. 2207 * @param aStripWrappers Whether the parent blocks should be removed 2208 * when they become empty. If this instance is 2209 * a TextEditor, Must be nsIEditor::eNoStrip. 2210 */ 2211 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2212 DeleteSelectionAsSubAction(nsIEditor::EDirection aDirectionAndAmount, 2213 nsIEditor::EStripWrappers aStripWrappers); 2214 2215 /** 2216 * This method handles "delete selection" commands. 2217 * 2218 * @param aDirectionAndAmount Direction of the deletion. 2219 * @param aStripWrappers Must be nsIEditor::eNoStrip if this is a 2220 * TextEditor instance. Otherwise, 2221 * nsIEditor::eStrip is also valid. 2222 */ 2223 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual Result<EditActionResult, nsresult> 2224 HandleDeleteSelection(nsIEditor::EDirection aDirectionAndAmount, 2225 nsIEditor::EStripWrappers aStripWrappers) = 0; 2226 2227 /** 2228 * ReplaceSelectionAsSubAction() replaces selection with aString. 2229 * 2230 * @param aString The string to replace. 2231 */ 2232 MOZ_CAN_RUN_SCRIPT nsresult 2233 ReplaceSelectionAsSubAction(const nsAString& aString); 2234 2235 /** 2236 * HandleInsertText() handles inserting text at selection. 2237 * 2238 * @param aInsertionString String to be inserted at selection. 2239 * @param aPurpose Specify the purpose of inserting text. 2240 */ 2241 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual Result<EditActionResult, nsresult> 2242 HandleInsertText(const nsAString& aInsertionString, 2243 InsertTextFor aPurpose) = 0; 2244 2245 /** 2246 * InsertWithQuotationsAsSubAction() inserts aQuotedText with appending ">" 2247 * to start of every line. 2248 * 2249 * @param aQuotedText String to insert. This will be quoted by ">" 2250 * automatically. 2251 */ 2252 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult 2253 InsertWithQuotationsAsSubAction(const nsAString& aQuotedText) = 0; 2254 2255 /** 2256 * PrepareInsertContent() is a helper method of InsertTextAt(), 2257 * HTMLEditor::HTMLWithContextInserter::Run(). They insert content coming 2258 * from clipboard or drag and drop. Before that, they may need to remove 2259 * selected contents and adjust selection. This does them instead. 2260 * 2261 * @param aPointToInsert Point to insert. Must be set. Callers 2262 * shouldn't use this instance after calling this 2263 * method because this method may cause changing 2264 * the DOM tree and Selection. 2265 */ 2266 enum class DeleteSelectedContent : bool { 2267 No, // Don't delete selection 2268 Yes, // Delete selected content 2269 }; 2270 MOZ_CAN_RUN_SCRIPT nsresult 2271 PrepareToInsertContent(const EditorDOMPoint& aPointToInsert, 2272 DeleteSelectedContent aDeleteSelectedContent); 2273 2274 /** 2275 * InsertTextAt() inserts aStringToInsert at aPointToInsert. 2276 * 2277 * @param aStringToInsert The string which you want to insert. 2278 * @param aPointToInsert The insertion point. 2279 */ 2280 MOZ_CAN_RUN_SCRIPT nsresult InsertTextAt( 2281 const nsAString& aStringToInsert, const EditorDOMPoint& aPointToInsert, 2282 DeleteSelectedContent aDeleteSelectedContent); 2283 2284 /** 2285 * Return whether the data is safe to insert as the source and destination 2286 * principals match, or we are in a editor context where this doesn't matter. 2287 * Otherwise, the data must be sanitized first. 2288 */ 2289 enum class SafeToInsertData : bool { No, Yes }; 2290 SafeToInsertData IsSafeToInsertData(nsIPrincipal* aSourcePrincipal) const; 2291 2292 /** 2293 * Routines for managing the preservation of selection across 2294 * various editor actions. 2295 */ 2296 bool ArePreservingSelection() const; 2297 void PreserveSelectionAcrossActions(); 2298 MOZ_CAN_RUN_SCRIPT nsresult RestorePreservedSelection(); 2299 void StopPreservingSelection(); 2300 2301 protected: // Called by helper classes. 2302 /** 2303 * OnStartToHandleTopLevelEditSubAction() is called when 2304 * GetTopLevelEditSubAction() is EditSubAction::eNone and somebody starts to 2305 * handle aEditSubAction. 2306 * 2307 * @param aTopLevelEditSubAction Top level edit sub action which 2308 * will be handled soon. 2309 * @param aDirectionOfTopLevelEditSubAction Direction of aEditSubAction. 2310 */ 2311 MOZ_CAN_RUN_SCRIPT virtual void OnStartToHandleTopLevelEditSubAction( 2312 EditSubAction aTopLevelEditSubAction, 2313 nsIEditor::EDirection aDirectionOfTopLevelEditSubAction, 2314 ErrorResult& aRv); 2315 2316 /** 2317 * OnEndHandlingTopLevelEditSubAction() is called after 2318 * SetTopLevelEditSubAction() is handled. 2319 */ 2320 MOZ_CAN_RUN_SCRIPT virtual nsresult OnEndHandlingTopLevelEditSubAction(); 2321 2322 /** 2323 * OnStartToHandleEditSubAction() and OnEndHandlingEditSubAction() are called 2324 * when starting to handle an edit sub action and ending handling an edit 2325 * sub action. 2326 */ 2327 void OnStartToHandleEditSubAction() { EditSubActionDataRef().Clear(); } 2328 void OnEndHandlingEditSubAction() { EditSubActionDataRef().Clear(); } 2329 2330 /** 2331 * (Begin|End)PlaceholderTransaction() are called by AutoPlaceholderBatch. 2332 * This set of methods are similar to the (Begin|End)Transaction(), but do 2333 * not use the transaction managers batching feature. Instead we use a 2334 * placeholder transaction to wrap up any further transaction while the 2335 * batch is open. The advantage of this is that placeholder transactions 2336 * can later merge, if needed. Merging is unavailable between transaction 2337 * manager batches. 2338 */ 2339 MOZ_CAN_RUN_SCRIPT_BOUNDARY void BeginPlaceholderTransaction( 2340 nsStaticAtom& aTransactionName, const char* aRequesterFuncName); 2341 enum class ScrollSelectionIntoView { No, Yes }; 2342 MOZ_CAN_RUN_SCRIPT_BOUNDARY void EndPlaceholderTransaction( 2343 ScrollSelectionIntoView aScrollSelectionIntoView, 2344 const char* aRequesterFuncName); 2345 2346 void BeginUpdateViewBatch(const char* aRequesterFuncName); 2347 MOZ_CAN_RUN_SCRIPT void EndUpdateViewBatch(const char* aRequesterFuncName); 2348 2349 /** 2350 * Used by HTMLEditor::AutoTransactionBatch, nsIEditor::BeginTransaction 2351 * and nsIEditor::EndTransation. After calling BeginTransactionInternal(), 2352 * all transactions will be treated as an atomic transaction. I.e., two or 2353 * more transactions are undid once. 2354 * XXX What's the difference with PlaceholderTransaction? Should we always 2355 * use it instead? 2356 */ 2357 MOZ_CAN_RUN_SCRIPT void BeginTransactionInternal( 2358 const char* aRequesterFuncName); 2359 MOZ_CAN_RUN_SCRIPT void EndTransactionInternal( 2360 const char* aRequesterFuncName); 2361 2362 protected: // Shouldn't be used by friend classes 2363 /** 2364 * The default destructor. This should suffice. Should this be pure virtual 2365 * for someone to derive from the EditorBase later? I don't believe so. 2366 */ 2367 virtual ~EditorBase(); 2368 2369 /** 2370 * @param aDocument The dom document interface being observed 2371 * @param aRootElement 2372 * This is the root of the editable section of this 2373 * document. If it is null then we get root from document 2374 * body. 2375 * @param aSelectionController 2376 * The selection controller of selections which will be 2377 * used in this editor. 2378 * @param aFlags Some of nsIEditor::eEditor*Mask flags. 2379 */ 2380 MOZ_CAN_RUN_SCRIPT nsresult 2381 InitInternal(Document& aDocument, Element* aRootElement, 2382 nsISelectionController& aSelectionController, uint32_t aFlags); 2383 2384 /** 2385 * PostCreateInternal() should be called after InitInternal(), and is the time 2386 * that the editor tells its documentStateObservers that the document has been 2387 * created. 2388 */ 2389 MOZ_CAN_RUN_SCRIPT nsresult PostCreateInternal(); 2390 2391 /** 2392 * PreDestroyInternal() is called before the editor goes away, and gives the 2393 * editor a chance to tell its documentStateObservers that the document is 2394 * going away. 2395 */ 2396 MOZ_CAN_RUN_SCRIPT virtual void PreDestroyInternal(); 2397 2398 MOZ_ALWAYS_INLINE EditorType GetEditorType() const { 2399 return mIsHTMLEditorClass ? EditorType::HTML : EditorType::Text; 2400 } 2401 2402 /** 2403 * Check whether the caller can keep handling focus event. 2404 * 2405 * @param aOriginalEventTargetNode The original event target of the focus 2406 * event. 2407 */ 2408 [[nodiscard]] bool CanKeepHandlingFocusEvent( 2409 const nsINode& aOriginalEventTargetNode) const; 2410 2411 /** 2412 * If this editor has skipped spell checking and not yet flushed, this runs 2413 * the spell checker. 2414 */ 2415 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult FlushPendingSpellCheck(); 2416 2417 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult EnsureEmptyTextFirstChild(); 2418 2419 int32_t WrapWidth() const { return mWrapColumn; } 2420 2421 /** 2422 * ToGenericNSResult() computes proper nsresult value for the editor users. 2423 * This should be used only when public methods return result of internal 2424 * methods. 2425 */ 2426 static inline nsresult ToGenericNSResult(nsresult aRv) { 2427 switch (aRv) { 2428 // If the editor is destroyed while handling an edit action, editor needs 2429 // to stop handling it. However, editor throw exception in this case 2430 // because Chrome does not throw exception even in this case. 2431 case NS_ERROR_EDITOR_DESTROYED: 2432 return NS_OK; 2433 // If editor meets unexpected DOM tree due to modified by mutation event 2434 // listener, editor needs to stop handling it. However, editor shouldn't 2435 // return error for the users because Chrome does not throw exception in 2436 // this case. 2437 case NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE: 2438 return NS_OK; 2439 // If the editing action is canceled by event listeners, editor needs 2440 // to stop handling it. However, editor shouldn't return error for 2441 // the callers but they should be able to distinguish whether it's 2442 // canceled or not. Although it's DOM specific code, let's return 2443 // DOM_SUCCESS_DOM_NO_OPERATION here. 2444 case NS_ERROR_EDITOR_ACTION_CANCELED: 2445 return NS_SUCCESS_DOM_NO_OPERATION; 2446 // If there is no selection range or editable selection ranges, editor 2447 // needs to stop handling it. However, editor shouldn't return error for 2448 // the callers to avoid throwing exception. However, they may want to 2449 // check whether it works or not. Therefore, we should return 2450 // NS_SUCCESS_DOM_NO_OPERATION instead. 2451 case NS_ERROR_EDITOR_NO_EDITABLE_RANGE: 2452 return NS_SUCCESS_DOM_NO_OPERATION; 2453 // If CreateNodeResultBase::SuggestCaretPointTo etc is called with 2454 // SuggestCaret::AndIgnoreTrivialErrors and CollapseSelectionTo returns 2455 // non-critical error e.g., not NS_ERROR_EDITOR_DESTROYED, it returns 2456 // this success code instead of actual error code for making the caller 2457 // handle the case easier. Therefore, this should be mapped to NS_OK 2458 // for the users of editor. 2459 case NS_SUCCESS_EDITOR_BUT_IGNORED_TRIVIAL_ERROR: 2460 return NS_OK; 2461 default: 2462 return aRv; 2463 } 2464 } 2465 2466 /** 2467 * GetDocumentCharsetInternal() returns charset of the document. 2468 */ 2469 nsresult GetDocumentCharsetInternal(nsACString& aCharset) const; 2470 2471 /** 2472 * ComputeValueInternal() computes string value of this editor for given 2473 * format. This may be too expensive if it's in hot path. 2474 * 2475 * @param aFormatType MIME type like "text/plain". 2476 * @param aDocumentEncoderFlags Flags of nsIDocumentEncoder. 2477 * @param aCharset Encoding of the document. 2478 */ 2479 nsresult ComputeValueInternal(const nsAString& aFormatType, 2480 uint32_t aDocumentEncoderFlags, 2481 nsAString& aOutputString) const; 2482 2483 /** 2484 * GetAndInitDocEncoder() returns a document encoder instance for aFormatType 2485 * after initializing it. The result may be cached for saving recreation 2486 * cost. 2487 * 2488 * @param aFormatType MIME type like "text/plain". 2489 * @param aDocumentEncoderFlags Flags of nsIDocumentEncoder. 2490 * @param aCharset Encoding of the document. 2491 */ 2492 already_AddRefed<nsIDocumentEncoder> GetAndInitDocEncoder( 2493 const nsAString& aFormatType, uint32_t aDocumentEncoderFlags, 2494 const nsACString& aCharset) const; 2495 2496 /** 2497 * EnsurePaddingBRElementInMultilineEditor() creates a padding `<br>` element 2498 * at end of multiline text editor. 2499 */ 2500 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2501 EnsurePaddingBRElementInMultilineEditor(); 2502 2503 /** 2504 * SelectAllInternal() should be used instead of SelectAll() in editor 2505 * because SelectAll() creates AutoEditActionSetter but we should avoid 2506 * to create it as far as possible. 2507 */ 2508 MOZ_CAN_RUN_SCRIPT virtual nsresult SelectAllInternal(); 2509 2510 nsresult DetermineCurrentDirection(); 2511 2512 /** 2513 * DispatchInputEvent() dispatches an "input" event synchronously or 2514 * asynchronously if it's not safe to dispatch. 2515 */ 2516 MOZ_CAN_RUN_SCRIPT void DispatchInputEvent(); 2517 2518 /** 2519 * Called after a transaction is done successfully. 2520 */ 2521 MOZ_CAN_RUN_SCRIPT void DoAfterDoTransaction(nsITransaction* aTransaction); 2522 2523 /** 2524 * Called after a transaction is undone successfully. 2525 */ 2526 2527 MOZ_CAN_RUN_SCRIPT void DoAfterUndoTransaction(); 2528 2529 /** 2530 * Called after a transaction is redone successfully. 2531 */ 2532 MOZ_CAN_RUN_SCRIPT void DoAfterRedoTransaction(); 2533 2534 /** 2535 * Tell the doc state listeners that the doc state has changed. 2536 */ 2537 enum TDocumentListenerNotification { 2538 eDocumentCreated, 2539 eDocumentToBeDestroyed, 2540 eDocumentStateChanged 2541 }; 2542 MOZ_CAN_RUN_SCRIPT nsresult 2543 NotifyDocumentListeners(TDocumentListenerNotification aNotificationType); 2544 2545 /** 2546 * Make the given selection span the entire document. 2547 */ 2548 MOZ_CAN_RUN_SCRIPT virtual nsresult SelectEntireDocument() = 0; 2549 2550 /** 2551 * Helper method for scrolling the selection into view after 2552 * an edit operation. 2553 * 2554 * Editor methods *should* call this method instead of the versions 2555 * in the various selection interfaces, since this makes sure that 2556 * the editor's sync/async settings for reflowing, painting, and scrolling 2557 * match. 2558 */ 2559 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2560 ScrollSelectionFocusIntoView() const; 2561 2562 virtual nsresult InstallEventListeners(); 2563 virtual void CreateEventListeners(); 2564 void RemoveEventListeners(); 2565 [[nodiscard]] bool IsListeningToEvents() const; 2566 2567 /** 2568 * Called if and only if this editor is in readonly mode. 2569 */ 2570 void HandleKeyPressEventInReadOnlyMode( 2571 WidgetKeyboardEvent& aKeyboardEvent) const; 2572 2573 /** 2574 * Get the input event target. This might return null. 2575 */ 2576 virtual already_AddRefed<Element> GetInputEventTargetElement() const = 0; 2577 2578 /** 2579 * Return true if spellchecking should be enabled for this editor. 2580 */ 2581 [[nodiscard]] bool GetDesiredSpellCheckState(); 2582 2583 [[nodiscard]] bool CanEnableSpellCheck() const { 2584 // Check for password/readonly/disabled, which are not spellchecked 2585 // regardless of DOM. Also, check to see if spell check should be skipped 2586 // or not. 2587 return !IsPasswordEditor() && !IsReadonly() && !ShouldSkipSpellCheck(); 2588 } 2589 2590 /** 2591 * InitializeSelectionAncestorLimit() is called by InitializeSelection(). 2592 * When this is called, each implementation has to call 2593 * Selection::SetAncestorLimiter() with aAnotherLimit. 2594 * 2595 * @param aAncestorLimit New ancestor limit of Selection. This always 2596 * has parent node. So, it's always safe to 2597 * call SetAncestorLimit() with this node. 2598 */ 2599 MOZ_CAN_RUN_SCRIPT virtual void InitializeSelectionAncestorLimit( 2600 Element& aAncestorLimit) const; 2601 2602 /** 2603 * Initializes selection and caret for the editor at getting focus. If 2604 * aOriginalEventTargetNode isn't a host of the editor, i.e., the editor 2605 * doesn't get focus, this does nothing. 2606 * 2607 * @param aOriginalEventTargetNode The original event target node of the 2608 * focus event. 2609 */ 2610 MOZ_CAN_RUN_SCRIPT nsresult 2611 InitializeSelection(const nsINode& aOriginalEventTargetNode); 2612 2613 enum NotificationForEditorObservers { 2614 eNotifyEditorObserversOfEnd, 2615 eNotifyEditorObserversOfBefore, 2616 eNotifyEditorObserversOfCancel 2617 }; 2618 MOZ_CAN_RUN_SCRIPT void NotifyEditorObservers( 2619 NotificationForEditorObservers aNotification); 2620 2621 /** 2622 * HowToHandleCollapsedRange indicates how collapsed range should be treated. 2623 */ 2624 enum class HowToHandleCollapsedRange { 2625 // Ignore collapsed range. 2626 Ignore, 2627 // Extend collapsed range for removing previous content. 2628 ExtendBackward, 2629 // Extend collapsed range for removing next content. 2630 ExtendForward, 2631 }; 2632 2633 static HowToHandleCollapsedRange HowToHandleCollapsedRangeFor( 2634 nsIEditor::EDirection aDirectionAndAmount) { 2635 switch (aDirectionAndAmount) { 2636 case nsIEditor::eNone: 2637 return HowToHandleCollapsedRange::Ignore; 2638 case nsIEditor::ePrevious: 2639 return HowToHandleCollapsedRange::ExtendBackward; 2640 case nsIEditor::eNext: 2641 return HowToHandleCollapsedRange::ExtendForward; 2642 case nsIEditor::ePreviousWord: 2643 case nsIEditor::eNextWord: 2644 case nsIEditor::eToBeginningOfLine: 2645 case nsIEditor::eToEndOfLine: 2646 // If the amount is word or 2647 // line,`AutoClonedSelectionRangeArray::ExtendAnchorFocusRangeFor()` 2648 // must have already been extended collapsed ranges before. 2649 return HowToHandleCollapsedRange::Ignore; 2650 } 2651 MOZ_ASSERT_UNREACHABLE("Invalid nsIEditor::EDirection value"); 2652 return HowToHandleCollapsedRange::Ignore; 2653 } 2654 2655 /** 2656 * InsertDroppedDataTransferAsAction() inserts all data items in aDataTransfer 2657 * at aDroppedAt unless the editor is destroyed. 2658 * 2659 * @param aEditActionData The edit action data whose edit action must be 2660 * EditAction::eDrop. 2661 * @param aDataTransfer The data transfer object which is dropped. 2662 * @param aDroppedAt The DOM tree position whether aDataTransfer 2663 * is dropped. 2664 * @param aSourcePrincipal Principal of the source of the drag. 2665 * May be nullptr if it comes from another app 2666 * or process. 2667 */ 2668 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult 2669 InsertDroppedDataTransferAsAction(AutoEditActionDataSetter& aEditActionData, 2670 DataTransfer& aDataTransfer, 2671 const EditorDOMPoint& aDroppedAt, 2672 nsIPrincipal* aSourcePrincipal) = 0; 2673 2674 /** 2675 * DeleteSelectionByDragAsAction() removes selection and dispatch "input" 2676 * event whose inputType is "deleteByDrag". 2677 */ 2678 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2679 DeleteSelectionByDragAsAction(bool aDispatchInputEvent); 2680 2681 /** 2682 * DeleteRangeWithTransaction() removes content in aRangeToDelete or content 2683 * around collapsed aRangeToDelete with transactions and remove empty 2684 * inclusive ancestor inline elements of the collapsed range after removing 2685 * the contents. 2686 * 2687 * @param aDirectionAndAmount How much range should be removed. 2688 * @param aStripWrappers Whether the parent blocks should be removed 2689 * when they become empty. 2690 * Note that this must be `nsIEditor::eNoStrip` 2691 * if this is a TextEditor because anyway it'll 2692 * be ignored. 2693 * @param aRangeToDelete The range to delete content. 2694 */ 2695 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CaretPoint, nsresult> 2696 DeleteRangeWithTransaction(nsIEditor::EDirection aDirectionAndAmount, 2697 nsIEditor::EStripWrappers aStripWrappers, 2698 nsRange& aRangeToDelete); 2699 2700 /** 2701 * DeleteRangesWithTransaction() removes content in aRangesToDelete or content 2702 * around collapsed ranges in aRangesToDelete with transactions and remove 2703 * empty inclusive ancestor inline elements of collapsed ranges after 2704 * removing the contents. 2705 * 2706 * @param aDirectionAndAmount How much range should be removed. 2707 * @param aStripWrappers Whether the parent blocks should be removed 2708 * when they become empty. 2709 * Note that this must be `nsIEditor::eNoStrip` 2710 * if this is a TextEditor because anyway it'll 2711 * be ignored. 2712 * @param aRangesToDelete The ranges to delete content. 2713 */ 2714 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual Result<CaretPoint, nsresult> 2715 DeleteRangesWithTransaction(nsIEditor::EDirection aDirectionAndAmount, 2716 nsIEditor::EStripWrappers aStripWrappers, 2717 AutoClonedRangeArray& aRangesToDelete); 2718 2719 /** 2720 * Create a transaction for delete the content in aRangesToDelete. 2721 * The result may include DeleteRangeTransaction (for deleting non-collapsed 2722 * range), DeleteNodeTransactions and DeleteTextTransactions (for deleting 2723 * collapsed range) as its children. 2724 * 2725 * @param aHowToHandleCollapsedRange 2726 * How to handle collapsed ranges. 2727 * @param aRangesToDelete The ranges to delete content. 2728 */ 2729 already_AddRefed<DeleteMultipleRangesTransaction> 2730 CreateTransactionForDeleteSelection( 2731 HowToHandleCollapsedRange aHowToHandleCollapsedRange, 2732 const AutoClonedRangeArray& aRangesToDelete); 2733 2734 /** 2735 * Create a DeleteNodeTransaction or DeleteTextTransaction for removing a 2736 * nodes or some text around aRangeToDelete. 2737 * 2738 * @param aCollapsedRange The range to be removed. This must be 2739 * collapsed. 2740 * @param aHowToHandleCollapsedRange 2741 * How to handle aCollapsedRange. Must 2742 * be HowToHandleCollapsedRange::ExtendBackward or 2743 * HowToHandleCollapsedRange::ExtendForward. 2744 */ 2745 already_AddRefed<DeleteContentTransactionBase> 2746 CreateTransactionForCollapsedRange( 2747 const nsRange& aCollapsedRange, 2748 HowToHandleCollapsedRange aHowToHandleCollapsedRange); 2749 2750 /** 2751 * ComputeInsertedRange() returns actual range modified by inserting string 2752 * in a text node. If mutation event listener changed the text data, this 2753 * returns a range which covers all over the text data. 2754 */ 2755 std::tuple<EditorDOMPointInText, EditorDOMPointInText> ComputeInsertedRange( 2756 const EditorDOMPointInText& aInsertedPoint, 2757 const nsAString& aInsertedString) const; 2758 2759 /** 2760 * EnsureComposition() should be called by composition event handlers. This 2761 * tries to get the composition for the event and set it to mComposition. 2762 * However, this may fail because the composition may be committed before 2763 * the event comes to the editor. 2764 * 2765 * @return true if there is a composition. Otherwise, for example, 2766 * a composition event handler in web contents moved focus 2767 * for committing the composition, returns false. 2768 */ 2769 bool EnsureComposition(WidgetCompositionEvent& aCompositionEvent); 2770 2771 /** 2772 * See comment of IsCopyToClipboardAllowed() for the detail. 2773 */ 2774 virtual bool IsCopyToClipboardAllowedInternal() const { 2775 MOZ_ASSERT(IsEditActionDataAvailable()); 2776 return !SelectionRef().IsCollapsed(); 2777 } 2778 2779 /** 2780 * Helper for Is{Cut|Copy}CommandEnabled. 2781 * Look for a listener for the given command, including up the target chain. 2782 */ 2783 MOZ_CAN_RUN_SCRIPT bool CheckForClipboardCommandListener( 2784 nsAtom* aCommand, EventMessage aEventMessage) const; 2785 2786 /** 2787 * DispatchClipboardEventAndUpdateClipboard() may dispatch a clipboard event 2788 * and update clipboard if aEventMessage is eCopy or eCut. 2789 * 2790 * @param aEventMessage The event message which may be set to the 2791 * dispatching event. 2792 * @param aClipboardType Working with global clipboard or selection. 2793 */ 2794 enum class ClipboardEventResult { 2795 // We have met an error in nsCopySupport::FireClipboardEvent, 2796 // or, default of dispatched event is NOT prevented, the event is "cut" 2797 // and the event target is not editable. 2798 IgnoredOrError, 2799 // A "paste" event is dispatched and prevented its default. 2800 DefaultPreventedOfPaste, 2801 // Default of a "copy" or "cut" event is prevented but the clipboard is 2802 // updated unless the dataTransfer of the event is cleared by the listener. 2803 // Or, default of the event is NOT prevented but selection is collapsed 2804 // when the event target is editable or the event is "copy". 2805 CopyOrCutHandled, 2806 // A clipboard event is maybe dispatched and not canceled by the web app. 2807 // In this case, the clipboard has been updated if aEventMessage is eCopy 2808 // or eCut. 2809 DoDefault, 2810 }; 2811 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<ClipboardEventResult, nsresult> 2812 DispatchClipboardEventAndUpdateClipboard( 2813 EventMessage aEventMessage, 2814 mozilla::Maybe<nsIClipboard::ClipboardType> aClipboardType, 2815 DataTransfer* aDataTransfer = nullptr); 2816 2817 /** 2818 * Called after PasteAsAction() dispatches "paste" event and it's not 2819 * canceled. 2820 */ 2821 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult HandlePaste( 2822 AutoEditActionDataSetter& aEditActionData, 2823 nsIClipboard::ClipboardType aClipboardType, 2824 DataTransfer* aDataTransfer) = 0; 2825 2826 /** 2827 * Called after PasteAsQuotationAsAction() dispatches "paste" event and it's 2828 * not canceled. 2829 */ 2830 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult HandlePasteAsQuotation( 2831 AutoEditActionDataSetter& aEditActionData, 2832 nsIClipboard::ClipboardType aClipboardType, 2833 DataTransfer* aDataTransfer) = 0; 2834 2835 /** 2836 * Called after PasteTransferableAsAction() dispatches "paste" event and 2837 * it's not canceled. 2838 */ 2839 [[nodiscard]] MOZ_CAN_RUN_SCRIPT virtual nsresult HandlePasteTransferable( 2840 AutoEditActionDataSetter& aEditActionData, 2841 nsITransferable& aTransferable) = 0; 2842 2843 private: 2844 nsCOMPtr<nsISelectionController> mSelectionController; 2845 RefPtr<Document> mDocument; 2846 2847 AutoEditActionDataSetter* mEditActionData; 2848 2849 /** 2850 * SetTextDirectionTo() sets text-direction of the root element. 2851 * Should use SwitchTextDirectionTo() or ToggleTextDirection() instead. 2852 * This is a helper class of them. 2853 */ 2854 MOZ_CAN_RUN_SCRIPT nsresult SetTextDirectionTo(TextDirection aTextDirection); 2855 2856 protected: // helper classes which may be used by friends 2857 /** 2858 * Stack based helper class for batching a collection of transactions 2859 * inside a placeholder transaction. Different from AutoTransactionBatch, 2860 * this notifies editor observers of before/end edit action handling, and 2861 * dispatches "input" event if it's necessary. 2862 */ 2863 class MOZ_RAII AutoPlaceholderBatch final { 2864 public: 2865 /** 2866 * @param aRequesterFuncName function name which wants to end the batch. 2867 * This won't be stored nor exposed to selection listeners etc, used 2868 * only for logging. This MUST be alive when the destructor runs. 2869 */ 2870 AutoPlaceholderBatch(EditorBase& aEditorBase, 2871 ScrollSelectionIntoView aScrollSelectionIntoView, 2872 const char* aRequesterFuncName) 2873 : mEditorBase(aEditorBase), 2874 mScrollSelectionIntoView(aScrollSelectionIntoView), 2875 mRequesterFuncName(aRequesterFuncName) { 2876 mEditorBase->BeginPlaceholderTransaction(*nsGkAtoms::_empty, 2877 mRequesterFuncName); 2878 } 2879 2880 AutoPlaceholderBatch(EditorBase& aEditorBase, 2881 nsStaticAtom& aTransactionName, 2882 ScrollSelectionIntoView aScrollSelectionIntoView, 2883 const char* aRequesterFuncName) 2884 : mEditorBase(aEditorBase), 2885 mScrollSelectionIntoView(aScrollSelectionIntoView), 2886 mRequesterFuncName(aRequesterFuncName) { 2887 mEditorBase->BeginPlaceholderTransaction(aTransactionName, 2888 mRequesterFuncName); 2889 } 2890 2891 ~AutoPlaceholderBatch() { 2892 mEditorBase->EndPlaceholderTransaction(mScrollSelectionIntoView, 2893 mRequesterFuncName); 2894 } 2895 2896 protected: 2897 const OwningNonNull<EditorBase> mEditorBase; 2898 const ScrollSelectionIntoView mScrollSelectionIntoView; 2899 const char* const mRequesterFuncName; 2900 }; 2901 2902 /** 2903 * AutoEditSubActionNotifier notifies editor of start to handle 2904 * top level edit sub-action and end handling top level edit sub-action. 2905 */ 2906 class MOZ_RAII AutoEditSubActionNotifier final { 2907 public: 2908 MOZ_CAN_RUN_SCRIPT AutoEditSubActionNotifier( 2909 EditorBase& aEditorBase, EditSubAction aEditSubAction, 2910 nsIEditor::EDirection aDirection, ErrorResult& aRv) 2911 : mEditorBase(aEditorBase), mIsTopLevel(true) { 2912 // The top level edit sub action has already be set if this is nested 2913 // call 2914 // XXX Looks like that this is not aware of unexpected nested edit 2915 // action 2916 // handling via selectionchange event listener or mutation event 2917 // listener. 2918 if (!mEditorBase.GetTopLevelEditSubAction()) { 2919 MOZ_KnownLive(mEditorBase) 2920 .OnStartToHandleTopLevelEditSubAction(aEditSubAction, aDirection, 2921 aRv); 2922 } else { 2923 mIsTopLevel = false; 2924 } 2925 mEditorBase.OnStartToHandleEditSubAction(); 2926 } 2927 2928 MOZ_CAN_RUN_SCRIPT ~AutoEditSubActionNotifier() { 2929 mEditorBase.OnEndHandlingEditSubAction(); 2930 if (mIsTopLevel) { 2931 MOZ_KnownLive(mEditorBase).OnEndHandlingTopLevelEditSubAction(); 2932 } 2933 } 2934 2935 protected: 2936 EditorBase& mEditorBase; 2937 bool mIsTopLevel; 2938 }; 2939 2940 /** 2941 * Stack based helper class for turning off active selection adjustment 2942 * by low level transactions 2943 */ 2944 class MOZ_RAII AutoTransactionsConserveSelection final { 2945 public: 2946 explicit AutoTransactionsConserveSelection(EditorBase& aEditorBase) 2947 : mEditorBase(aEditorBase), 2948 mAllowedTransactionsToChangeSelection( 2949 aEditorBase.AllowsTransactionsToChangeSelection()) { 2950 mEditorBase.MakeThisAllowTransactionsToChangeSelection(false); 2951 } 2952 2953 ~AutoTransactionsConserveSelection() { 2954 mEditorBase.MakeThisAllowTransactionsToChangeSelection( 2955 mAllowedTransactionsToChangeSelection); 2956 } 2957 2958 protected: 2959 EditorBase& mEditorBase; 2960 bool mAllowedTransactionsToChangeSelection; 2961 }; 2962 2963 /*************************************************************************** 2964 * stack based helper class for batching reflow and paint requests. 2965 */ 2966 class MOZ_RAII AutoUpdateViewBatch final { 2967 public: 2968 /** 2969 * @param aRequesterFuncName function name which wants to end the batch. 2970 * This won't be stored nor exposed to selection listeners etc, used 2971 * only for logging. This MUST be alive when the destructor runs. 2972 */ 2973 MOZ_CAN_RUN_SCRIPT explicit AutoUpdateViewBatch( 2974 EditorBase& aEditorBase, const char* aRequesterFuncName) 2975 : mEditorBase(aEditorBase), mRequesterFuncName(aRequesterFuncName) { 2976 mEditorBase.BeginUpdateViewBatch(mRequesterFuncName); 2977 } 2978 2979 MOZ_CAN_RUN_SCRIPT ~AutoUpdateViewBatch() { 2980 MOZ_KnownLive(mEditorBase).EndUpdateViewBatch(mRequesterFuncName); 2981 } 2982 2983 protected: 2984 EditorBase& mEditorBase; 2985 const char* const mRequesterFuncName; 2986 }; 2987 2988 protected: 2989 enum Tristate { eTriUnset, eTriFalse, eTriTrue }; 2990 2991 // MIME type of the doc we are editing. 2992 nsString mContentMIMEType; 2993 2994 RefPtr<mozInlineSpellChecker> mInlineSpellChecker; 2995 // Reference to text services document for mInlineSpellChecker. 2996 RefPtr<TextServicesDocument> mTextServicesDocument; 2997 2998 RefPtr<TransactionManager> mTransactionManager; 2999 // Cached root node. 3000 RefPtr<Element> mRootElement; 3001 3002 // The form field as an event receiver. 3003 nsCOMPtr<dom::EventTarget> mEventTarget; 3004 RefPtr<EditorEventListener> mEventListener; 3005 // Strong reference to placeholder for begin/end batch purposes. 3006 RefPtr<PlaceholderTransaction> mPlaceholderTransaction; 3007 // Name of placeholder transaction. 3008 nsStaticAtom* mPlaceholderName; 3009 // Saved selection state for placeholder transaction batching. 3010 mozilla::Maybe<SelectionState> mSelState; 3011 // IME composition this is not null between compositionstart and 3012 // compositionend. 3013 RefPtr<TextComposition> mComposition; 3014 3015 RefPtr<TextInputListener> mTextInputListener; 3016 3017 RefPtr<IMEContentObserver> mIMEContentObserver; 3018 3019 // These members cache last encoder and its type for the performance in 3020 // TextEditor::ComputeTextValue() which is the implementation of 3021 // `<input>.value` and `<textarea>.value`. See `GetAndInitDocEncoder()`. 3022 mutable nsCOMPtr<nsIDocumentEncoder> mCachedDocumentEncoder; 3023 mutable nsString mCachedDocumentEncoderType; 3024 3025 // Listens to all low level actions on the doc. 3026 // Edit action listener is currently used by highlighter of the findbar 3027 // and the spellchecker. So, we should reserve only 2 items. 3028 using AutoActionListenerArray = 3029 AutoTArray<OwningNonNull<nsIEditActionListener>, 2>; 3030 AutoActionListenerArray mActionListeners; 3031 // Listen to overall doc state (dirty or not, just created, etc.). 3032 // Document state listener is currently used by FinderHighlighter and 3033 // BlueGriffon so that reserving only one is enough. 3034 using AutoDocumentStateListenerArray = 3035 AutoTArray<OwningNonNull<nsIDocumentStateListener>, 1>; 3036 AutoDocumentStateListenerArray mDocStateListeners; 3037 3038 // Number of modifications (for undo/redo stack). 3039 uint32_t mModCount; 3040 // Behavior flags. See nsIEditor.idl for the flags we use. 3041 uint32_t mFlags; 3042 3043 int32_t mUpdateCount; 3044 3045 // Nesting count for batching. 3046 int32_t mPlaceholderBatch; 3047 3048 int32_t mWrapColumn = 0; 3049 int32_t mNewlineHandling; 3050 int32_t mCaretStyle; 3051 3052 // -1 = not initialized 3053 int8_t mDocDirtyState; 3054 // A Tristate value. 3055 uint8_t mSpellcheckCheckboxState; 3056 3057 // If true, initialization was succeeded. 3058 bool mInitSucceeded; 3059 // If false, transactions should not change Selection even after modifying 3060 // the DOM tree. 3061 bool mAllowsTransactionsToChangeSelection; 3062 // Whether PreDestroy has been called. 3063 bool mDidPreDestroy; 3064 // Whether PostCreate has been called. 3065 bool mDidPostCreate; 3066 bool mDispatchInputEvent; 3067 // True while the instance is handling an edit sub-action. 3068 bool mIsInEditSubAction; 3069 // Whether caret is hidden forcibly. 3070 bool mHidingCaret; 3071 // Whether spellchecker dictionary is initialized after focused. 3072 bool mSpellCheckerDictionaryUpdated; 3073 // Whether we are an HTML editor class. 3074 bool mIsHTMLEditorClass; 3075 3076 friend class AlignStateAtSelection; // AutoEditActionDataSetter, 3077 // ToGenericNSResult 3078 friend class AutoClonedRangeArray; // IsSEditActionDataAvailable, 3079 // RangeUpdaterRef 3080 friend class AutoClonedSelectionRangeArray; // RangeUpdaterRef, SelectionRef 3081 friend class AutoSelectionRestorer; // RangeUpdaterRef, SavedSelectionRef 3082 friend class CaretPoint; // AllowsTransactionsToChangeSelection, 3083 // CollapseSelectionTo 3084 friend class CompositionTransaction; // CollapseSelectionTo, 3085 // DoDeleteText, DoInsertText, 3086 // DoReplaceText, HideCaret, 3087 // RangeUpdaterRef 3088 friend class DeleteNodeTransaction; // RangeUpdaterRef 3089 friend class DeleteRangeTransaction; // AllowsTransactionsToChangeSelection, 3090 // CollapseSelectionTo 3091 friend class DeleteTextTransaction; // AllowsTransactionsToChangeSelection, 3092 // DoDeleteText, DoInsertText, 3093 // RangeUpdaterRef 3094 friend class InsertNodeTransaction; // AllowsTransactionsToChangeSelection, 3095 // CollapseSelectionTo, 3096 // MarkElementDirty, ToGenericNSResult 3097 friend class InsertTextTransaction; // AllowsTransactionsToChangeSelection, 3098 // CollapseSelectionTo, DoDeleteText, 3099 // DoInsertText, RangeUpdaterRef 3100 friend class ListElementSelectionState; // AutoEditActionDataSetter, 3101 // ToGenericNSResult 3102 friend class ListItemElementSelectionState; // AutoEditActionDataSetter, 3103 // ToGenericNSResult 3104 friend class MoveNodeTransaction; // MarkElementDirty, ToGenericNSResult 3105 friend class MoveSiblingsTransaction; // MarkElementDirty, ToGenericNSResult 3106 friend class ParagraphStateAtSelection; // AutoEditActionDataSetter, 3107 // ToGenericNSResult 3108 friend class PendingStyles; // GetEditAction, 3109 // GetFirstSelectionStartPoint, 3110 // SelectionRef 3111 friend class ReplaceTextTransaction; // AllowsTransactionsToChangeSelection, 3112 // CollapseSelectionTo, DoReplaceText, 3113 // RangeUpdaterRef 3114 friend class SplitNodeTransaction; // ToGenericNSResult 3115 friend class 3116 WhiteSpaceVisibilityKeeper; // AutoTransactionsConserveSelection, 3117 // ComputePointToInsertText 3118 friend class nsIEditor; // mIsHTMLEditorClass 3119 }; 3120 3121 } // namespace mozilla 3122 3123 bool nsIEditor::IsTextEditor() const { 3124 return !AsEditorBase()->mIsHTMLEditorClass; 3125 } 3126 3127 bool nsIEditor::IsHTMLEditor() const { 3128 return AsEditorBase()->mIsHTMLEditorClass; 3129 } 3130 3131 mozilla::EditorBase* nsIEditor::AsEditorBase() { 3132 return static_cast<mozilla::EditorBase*>(this); 3133 } 3134 3135 const mozilla::EditorBase* nsIEditor::AsEditorBase() const { 3136 return static_cast<const mozilla::EditorBase*>(this); 3137 } 3138 3139 #endif // #ifndef mozilla_EditorBase_h