HTMLEditor.h (216363B)
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_HTMLEditor_h 7 #define mozilla_HTMLEditor_h 8 9 #include "mozilla/Attributes.h" 10 #include "mozilla/ComposerCommandsUpdater.h" 11 #include "mozilla/EditorBase.h" 12 #include "mozilla/EditorForwards.h" 13 #include "mozilla/ErrorResult.h" 14 #include "mozilla/ManualNAC.h" 15 #include "mozilla/Result.h" 16 #include "mozilla/dom/BlobImpl.h" 17 #include "mozilla/dom/Element.h" 18 #include "mozilla/dom/File.h" 19 20 #include "nsAttrName.h" 21 #include "nsCOMPtr.h" 22 #include "nsIDocumentObserver.h" 23 #include "nsIDOMEventListener.h" 24 #include "nsIEditorMailSupport.h" 25 #include "nsIHTMLAbsPosEditor.h" 26 #include "nsIHTMLEditor.h" 27 #include "nsIHTMLInlineTableEditor.h" 28 #include "nsIHTMLObjectResizer.h" 29 #include "nsIPrincipal.h" 30 #include "nsITableEditor.h" 31 #include "nsPoint.h" 32 #include "nsStubMutationObserver.h" 33 34 #include <functional> 35 36 class nsDocumentFragment; 37 class nsFrameSelection; 38 class nsHTMLDocument; 39 class nsITransferable; 40 class nsRange; 41 class nsStaticAtom; 42 class nsStyledElement; 43 class nsTableCellFrame; 44 class nsTableWrapperFrame; 45 template <class E> 46 class nsTArray; 47 48 namespace mozilla { 49 class AlignStateAtSelection; 50 class AutoSelectionSetterAfterTableEdit; 51 class EmptyEditableFunctor; 52 class ListElementSelectionState; 53 class ListItemElementSelectionState; 54 class ParagraphStateAtSelection; 55 class ResizerSelectionListener; 56 class Runnable; 57 template <class T> 58 class OwningNonNull; 59 enum class LogLevel; 60 namespace dom { 61 class AbstractRange; 62 class Blob; 63 class DocumentFragment; 64 class Event; 65 class HTMLBRElement; 66 class MouseEvent; 67 class StaticRange; 68 } // namespace dom 69 namespace widget { 70 struct IMEState; 71 } // namespace widget 72 73 enum class ParagraphSeparator { div, p, br }; 74 75 /** 76 * The HTML editor implementation.<br> 77 * Use to edit HTML document represented as a DOM tree. 78 */ 79 class HTMLEditor final : public EditorBase, 80 public nsIHTMLEditor, 81 public nsIHTMLObjectResizer, 82 public nsIHTMLAbsPosEditor, 83 public nsITableEditor, 84 public nsIHTMLInlineTableEditor, 85 public nsStubMutationObserver, 86 public nsIEditorMailSupport { 87 public: 88 /**************************************************************************** 89 * NOTE: DO NOT MAKE YOUR NEW METHODS PUBLIC IF they are called by other 90 * classes under libeditor except EditorEventListener and 91 * HTMLEditorEventListener because each public method which may fire 92 * eEditorInput event will need to instantiate new stack class for 93 * managing input type value of eEditorInput and cache some objects 94 * for smarter handling. In other words, when you add new root 95 * method to edit the DOM tree, you can make your new method public. 96 ****************************************************************************/ 97 98 NS_DECL_ISUPPORTS_INHERITED 99 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLEditor, EditorBase) 100 101 // nsStubMutationObserver overrides 102 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED 103 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED 104 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED 105 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED 106 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED 107 108 // nsIHTMLEditor methods 109 NS_DECL_NSIHTMLEDITOR 110 111 // nsIHTMLObjectResizer methods (implemented in HTMLObjectResizer.cpp) 112 NS_DECL_NSIHTMLOBJECTRESIZER 113 114 // nsIHTMLAbsPosEditor methods (implemented in HTMLAbsPositionEditor.cpp) 115 NS_DECL_NSIHTMLABSPOSEDITOR 116 117 // nsIHTMLInlineTableEditor methods (implemented in HTMLInlineTableEditor.cpp) 118 NS_DECL_NSIHTMLINLINETABLEEDITOR 119 120 // nsIEditorMailSupport methods 121 NS_DECL_NSIEDITORMAILSUPPORT 122 123 // nsITableEditor methods 124 NS_DECL_NSITABLEEDITOR 125 126 // nsISelectionListener overrides 127 NS_DECL_NSISELECTIONLISTENER 128 129 /** 130 * @param aDocument The document whose content will be editable. 131 */ 132 explicit HTMLEditor(const Document& aDocument); 133 134 /** 135 * @param aDocument The document whose content will be editable. 136 * @param aComposerCommandsUpdater The composer command updater. 137 * @param aFlags Some of nsIEditor::eEditor*Mask flags. 138 */ 139 MOZ_CAN_RUN_SCRIPT nsresult 140 Init(Document& aDocument, ComposerCommandsUpdater& aComposerCommandsUpdater, 141 uint32_t aFlags); 142 143 /** 144 * PostCreate() should be called after Init, and is the time that the editor 145 * tells its documentStateObservers that the document has been created. 146 */ 147 MOZ_CAN_RUN_SCRIPT nsresult PostCreate(); 148 149 /** 150 * PreDestroy() is called before the editor goes away, and gives the editor a 151 * chance to tell its documentStateObservers that the document is going away. 152 */ 153 MOZ_CAN_RUN_SCRIPT void PreDestroy(); 154 155 static HTMLEditor* GetFrom(nsIEditor* aEditor) { 156 return aEditor ? aEditor->GetAsHTMLEditor() : nullptr; 157 } 158 static const HTMLEditor* GetFrom(const nsIEditor* aEditor) { 159 return aEditor ? aEditor->GetAsHTMLEditor() : nullptr; 160 } 161 162 [[nodiscard]] bool GetReturnInParagraphCreatesNewParagraph() const; 163 164 // EditorBase overrides 165 MOZ_CAN_RUN_SCRIPT NS_IMETHOD BeginningOfDocument() final; 166 MOZ_CAN_RUN_SCRIPT NS_IMETHOD EndOfDocument() final; 167 168 NS_IMETHOD GetDocumentCharacterSet(nsACString& aCharacterSet) final; 169 MOZ_CAN_RUN_SCRIPT NS_IMETHOD 170 SetDocumentCharacterSet(const nsACString& aCharacterSet) final; 171 172 bool IsEmpty() const final; 173 174 bool CanPaste(nsIClipboard::ClipboardType aClipboardType) const final; 175 using EditorBase::CanPaste; 176 177 MOZ_CAN_RUN_SCRIPT NS_IMETHOD DeleteNode(nsINode* aNode, 178 bool aPreseveSelection, 179 uint8_t aOptionalArgCount) final; 180 181 MOZ_CAN_RUN_SCRIPT NS_IMETHOD InsertLineBreak() final; 182 183 /** 184 * PreHandleMouseDown() and PreHandleMouseUp() are called before 185 * HTMLEditorEventListener handles them. The coming event may be 186 * non-acceptable event. 187 */ 188 void PreHandleMouseDown(const dom::MouseEvent& aMouseDownEvent); 189 void PreHandleMouseUp(const dom::MouseEvent& aMouseUpEvent); 190 191 /** 192 * PreHandleSelectionChangeCommand() and PostHandleSelectionChangeCommand() 193 * are called before or after handling a command which may change selection 194 * and/or scroll position. 195 */ 196 void PreHandleSelectionChangeCommand(Command aCommand); 197 void PostHandleSelectionChangeCommand(Command aCommand); 198 199 MOZ_CAN_RUN_SCRIPT nsresult 200 HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) final; 201 Element* GetFocusedElement() const final; 202 bool IsActiveInDOMWindow() const final; 203 dom::EventTarget* GetDOMEventTarget() const final; 204 [[nodiscard]] Element* FindSelectionRoot(const nsINode& aNode) const final; 205 bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) const final; 206 [[nodiscard]] Result<widget::IMEState, nsresult> GetPreferredIMEState() 207 const final; 208 MOZ_CAN_RUN_SCRIPT nsresult 209 OnFocus(const nsINode& aOriginalEventTargetNode) final; 210 nsresult OnBlur(const dom::EventTarget* aEventTarget) final; 211 212 /** 213 * Called when aDocument or aElement becomes editable without focus change. 214 * E.g., when the design mode is enabled or the contenteditable attribute 215 * is set to the focused element. 216 */ 217 MOZ_CAN_RUN_SCRIPT nsresult FocusedElementOrDocumentBecomesEditable( 218 Document& aDocument, Element* aElement); 219 220 /** 221 * Called when aDocument or aElement becomes not editable without focus 222 * change. E.g., when the design mode ends or the contenteditable attribute is 223 * removed or set to "false". 224 */ 225 MOZ_CAN_RUN_SCRIPT static nsresult FocusedElementOrDocumentBecomesNotEditable( 226 HTMLEditor* aHTMLEditor, Document& aDocument, Element* aElement); 227 228 /** 229 * GetBackgroundColorState() returns what the background color of the 230 * selection. 231 * 232 * @param aMixed true if there is more than one font color 233 * @param aOutColor Color string. "" is returned for none. 234 */ 235 MOZ_CAN_RUN_SCRIPT nsresult GetBackgroundColorState(bool* aMixed, 236 nsAString& aOutColor); 237 238 /** 239 * PasteNoFormattingAsAction() pastes content in clipboard without any style 240 * information. 241 * 242 * @param aClipboardType nsIClipboard::kGlobalClipboard or 243 * nsIClipboard::kSelectionClipboard. 244 * @param aDispatchPasteEvent Yes if this should dispatch ePaste event 245 * before pasting. Otherwise, No. 246 * @param aDataTransfer The object containing the data to use for the 247 * paste operation. May be nullptr, in which case 248 * this will just get the data from the clipboard. 249 * @param aPrincipal Set subject principal if it may be called by 250 * JS. If set to nullptr, will be treated as 251 * called by system. 252 */ 253 MOZ_CAN_RUN_SCRIPT nsresult 254 PasteNoFormattingAsAction(nsIClipboard::ClipboardType aClipboardType, 255 DispatchPasteEvent aDispatchPasteEvent, 256 DataTransfer* aDataTransfer = nullptr, 257 nsIPrincipal* aPrincipal = nullptr); 258 259 bool CanPasteTransferable(nsITransferable* aTransferable) final; 260 261 MOZ_CAN_RUN_SCRIPT nsresult 262 InsertLineBreakAsAction(nsIPrincipal* aPrincipal = nullptr) final; 263 264 /** 265 * InsertParagraphSeparatorAsAction() is called when user tries to separate 266 * current paragraph with Enter key press in HTMLEditor or something. 267 * 268 * @param aPrincipal Set subject principal if it may be called by 269 * JS. If set to nullptr, will be treated as 270 * called by system. 271 */ 272 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 273 InsertParagraphSeparatorAsAction(nsIPrincipal* aPrincipal = nullptr); 274 275 enum class InsertElementOption { 276 // Delete selection if set, otherwise, insert aElement at start or end of 277 // selection. 278 DeleteSelection, 279 // Whether split all inline ancestors or not. 280 SplitAncestorInlineElements, 281 }; 282 using InsertElementOptions = EnumSet<InsertElementOption>; 283 MOZ_CAN_RUN_SCRIPT nsresult InsertElementAtSelectionAsAction( 284 Element* aElement, const InsertElementOptions aOptions, 285 nsIPrincipal* aPrincipal = nullptr); 286 287 MOZ_CAN_RUN_SCRIPT nsresult InsertLinkAroundSelectionAsAction( 288 Element* aAnchorElement, nsIPrincipal* aPrincipal = nullptr); 289 290 /** 291 * CreateElementWithDefaults() creates new element whose name is 292 * aTagName with some default attributes are set. Note that this is a 293 * public utility method. I.e., just creates element, not insert it 294 * into the DOM tree. 295 * NOTE: This is available for internal use too since this does not change 296 * the DOM tree nor undo transactions, and does not refer Selection, 297 * etc. 298 * 299 * @param aTagName The new element's tag name. If the name is 300 * one of "href", "anchor" or "namedanchor", 301 * this creates an <a> element. 302 * @return Newly created element. 303 */ 304 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> CreateElementWithDefaults( 305 const nsAtom& aTagName); 306 307 /** 308 * Indent or outdent content around Selection. 309 * 310 * @param aPrincipal Set subject principal if it may be called by 311 * JS. If set to nullptr, will be treated as 312 * called by system. 313 */ 314 MOZ_CAN_RUN_SCRIPT nsresult 315 IndentAsAction(nsIPrincipal* aPrincipal = nullptr); 316 MOZ_CAN_RUN_SCRIPT nsresult 317 OutdentAsAction(nsIPrincipal* aPrincipal = nullptr); 318 319 /** 320 * The Document.execCommand("formatBlock") handler. 321 * 322 * @param aParagraphFormat Must not be an empty string, and the value must 323 * be one of address, article, aside, blockquote, 324 * div, footer, h1, h2, h3, h4, h5, h6, header, 325 * hgroup, main, nav, p, pre, selection, dt or dd. 326 */ 327 MOZ_CAN_RUN_SCRIPT nsresult FormatBlockAsAction( 328 const nsAString& aParagraphFormat, nsIPrincipal* aPrincipal = nullptr); 329 330 /** 331 * The cmd_paragraphState command handler. 332 * 333 * @param aParagraphFormat Can be empty string. If this is empty string, 334 * this removes ancestor format elements. 335 * Otherwise, the value must be one of p, pre, 336 * h1, h2, h3, h4, h5, h6, address, dt or dl. 337 */ 338 MOZ_CAN_RUN_SCRIPT nsresult SetParagraphStateAsAction( 339 const nsAString& aParagraphFormat, nsIPrincipal* aPrincipal = nullptr); 340 341 MOZ_CAN_RUN_SCRIPT nsresult AlignAsAction(const nsAString& aAlignType, 342 nsIPrincipal* aPrincipal = nullptr); 343 344 MOZ_CAN_RUN_SCRIPT nsresult RemoveListAsAction( 345 const nsAString& aListType, nsIPrincipal* aPrincipal = nullptr); 346 347 /** 348 * MakeOrChangeListAsAction() makes selected hard lines list element(s). 349 * 350 * @param aListElementTagName The new list element tag name. Must be 351 * nsGkAtoms::ul, nsGkAtoms::ol or 352 * nsGkAtoms::dl. 353 * @param aBulletType If this is not empty string, it's set 354 * to `type` attribute of new list item 355 * elements. Otherwise, existing `type` 356 * attributes will be removed. 357 * @param aSelectAllOfCurrentList Yes if this should treat all of 358 * ancestor list element at selection. 359 */ 360 enum class SelectAllOfCurrentList { Yes, No }; 361 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MakeOrChangeListAsAction( 362 const nsStaticAtom& aListElementTagName, const nsAString& aBulletType, 363 SelectAllOfCurrentList aSelectAllOfCurrentList, 364 nsIPrincipal* aPrincipal = nullptr); 365 366 /** 367 * If aTargetElement is a resizer, start to drag the resizer. Otherwise, if 368 * aTargetElement is the grabber, start to handle drag gester on it. 369 * 370 * @param aMouseDownEvent A `mousedown` event fired on aTargetElement. 371 * @param aEventTargetElement The target element being pressed. This must 372 * be same as explicit original event target of 373 * aMouseDownEvent. 374 */ 375 MOZ_CAN_RUN_SCRIPT nsresult StartToDragResizerOrHandleDragGestureOnGrabber( 376 dom::MouseEvent& aMouseDownEvent, Element& aEventTargetElement); 377 378 /** 379 * If the editor is handling dragging a resizer, handling drag gesture on 380 * the grabber or dragging the grabber, this finalize it. Otherwise, 381 * does nothing. 382 * 383 * @param aClientPoint The final point of the drag. 384 */ 385 MOZ_CAN_RUN_SCRIPT nsresult 386 StopDraggingResizerOrGrabberAt(const CSSIntPoint& aClientPoint); 387 388 /** 389 * If the editor is handling dragging a resizer, handling drag gesture to 390 * start dragging the grabber or dragging the grabber, this method updates 391 * it's position. 392 * 393 * @param aClientPoint The new point of the drag. 394 */ 395 MOZ_CAN_RUN_SCRIPT nsresult 396 UpdateResizerOrGrabberPositionTo(const CSSIntPoint& aClientPoint); 397 398 /** 399 * IsCSSEnabled() returns true if this editor treats styles with style 400 * attribute of HTML elements. Otherwise, if this editor treats all styles 401 * with "font style elements" like <b>, <i>, etc, and <blockquote> to indent, 402 * align attribute to align contents, returns false. 403 */ 404 bool IsCSSEnabled() const { return mIsCSSPrefChecked; } 405 406 /** 407 * Return true when editing host is not plaintext-only. 408 */ 409 [[nodiscard]] bool IsStyleEditable() const; 410 411 /** 412 * Enable/disable object resizers for <img> elements, <table> elements, 413 * absolute positioned elements (required absolute position editor enabled). 414 */ 415 MOZ_CAN_RUN_SCRIPT void EnableObjectResizer(bool aEnable) { 416 if (mIsObjectResizingEnabled == aEnable) { 417 return; 418 } 419 420 AutoEditActionDataSetter editActionData( 421 *this, EditAction::eEnableOrDisableResizer); 422 if (NS_WARN_IF(!editActionData.CanHandle())) { 423 return; 424 } 425 426 mIsObjectResizingEnabled = aEnable; 427 RefreshEditingUI(); 428 } 429 bool IsObjectResizerEnabled() const { 430 return mIsObjectResizingEnabled && IsStyleEditable(); 431 } 432 433 Element* GetResizerTarget() const { return mResizedObject; } 434 435 /** 436 * Enable/disable inline table editor, e.g., adding new row or column, 437 * removing existing row or column. 438 */ 439 MOZ_CAN_RUN_SCRIPT void EnableInlineTableEditor(bool aEnable) { 440 if (mIsInlineTableEditingEnabled == aEnable) { 441 return; 442 } 443 444 AutoEditActionDataSetter editActionData( 445 *this, EditAction::eEnableOrDisableInlineTableEditingUI); 446 if (NS_WARN_IF(!editActionData.CanHandle())) { 447 return; 448 } 449 450 mIsInlineTableEditingEnabled = aEnable; 451 RefreshEditingUI(); 452 } 453 bool IsInlineTableEditorEnabled() const { 454 return mIsInlineTableEditingEnabled && IsStyleEditable(); 455 } 456 457 /** 458 * Enable/disable absolute position editor, resizing absolute positioned 459 * elements (required object resizers enabled) or positioning them with 460 * dragging grabber. 461 */ 462 MOZ_CAN_RUN_SCRIPT void EnableAbsolutePositionEditor(bool aEnable) { 463 if (mIsAbsolutelyPositioningEnabled == aEnable) { 464 return; 465 } 466 467 AutoEditActionDataSetter editActionData( 468 *this, EditAction::eEnableOrDisableAbsolutePositionEditor); 469 if (NS_WARN_IF(!editActionData.CanHandle())) { 470 return; 471 } 472 473 mIsAbsolutelyPositioningEnabled = aEnable; 474 RefreshEditingUI(); 475 } 476 bool IsAbsolutePositionEditorEnabled() const { 477 return mIsAbsolutelyPositioningEnabled && IsStyleEditable(); 478 } 479 480 /** 481 * returns the deepest absolutely positioned container of the selection 482 * if it exists or null. 483 */ 484 MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> 485 GetAbsolutelyPositionedSelectionContainer() const; 486 487 Element* GetPositionedElement() const { return mAbsolutelyPositionedObject; } 488 489 /** 490 * extracts the selection from the normal flow of the document and 491 * positions it. 492 * 493 * @param aEnabled [IN] true to absolutely position the selection, 494 * false to put it back in the normal flow 495 * @param aPrincipal Set subject principal if it may be called by 496 * JS. If set to nullptr, will be treated as 497 * called by system. 498 */ 499 MOZ_CAN_RUN_SCRIPT nsresult SetSelectionToAbsoluteOrStaticAsAction( 500 bool aEnabled, nsIPrincipal* aPrincipal = nullptr); 501 502 /** 503 * returns the absolute z-index of a positioned element. Never returns 'auto' 504 * @return the z-index of the element 505 * @param aElement [IN] the element. 506 */ 507 MOZ_CAN_RUN_SCRIPT int32_t GetZIndex(Element& aElement); 508 509 /** 510 * adds aChange to the z-index of the currently positioned element. 511 * 512 * @param aChange [IN] relative change to apply to current z-index 513 * @param aPrincipal Set subject principal if it may be called by 514 * JS. If set to nullptr, will be treated as 515 * called by system. 516 */ 517 MOZ_CAN_RUN_SCRIPT nsresult 518 AddZIndexAsAction(int32_t aChange, nsIPrincipal* aPrincipal = nullptr); 519 520 MOZ_CAN_RUN_SCRIPT nsresult SetBackgroundColorAsAction( 521 const nsAString& aColor, nsIPrincipal* aPrincipal = nullptr); 522 523 /** 524 * SetInlinePropertyAsAction() sets a property which changes inline style of 525 * text. E.g., bold, italic, super and sub. 526 * This automatically removes exclusive style, however, treats all changes 527 * as a transaction. 528 * 529 * @param aPrincipal Set subject principal if it may be called by 530 * JS. If set to nullptr, will be treated as 531 * called by system. 532 */ 533 MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertyAsAction( 534 nsStaticAtom& aProperty, nsStaticAtom* aAttribute, 535 const nsAString& aValue, nsIPrincipal* aPrincipal = nullptr); 536 537 /** 538 * GetInlineProperty() gets aggregate properties of the current selection. 539 * All object in the current selection are scanned and their attributes are 540 * represented in a list of Property object. 541 * TODO: Make this return Result<Something> instead of bool out arguments. 542 * 543 * @param aHTMLProperty the property to get on the selection 544 * @param aAttribute the attribute of the property, if applicable. 545 * May be null. 546 * Example: aHTMLProperty=nsGkAtoms::font, 547 * aAttribute=nsGkAtoms::color 548 * @param aValue if aAttribute is not null, the value of the 549 * attribute. May be null. 550 * Example: aHTMLProperty=nsGkAtoms::font, 551 * aAttribute=nsGkAtoms::color, 552 * aValue="0x00FFFF" 553 * @param aFirst [OUT] true if the first text node in the 554 * selection has the property 555 * @param aAny [OUT] true if any of the text nodes in the 556 * selection have the property 557 * @param aAll [OUT] true if all of the text nodes in the 558 * selection have the property 559 */ 560 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlineProperty( 561 nsStaticAtom& aHTMLProperty, nsAtom* aAttribute, const nsAString& aValue, 562 bool* aFirst, bool* aAny, bool* aAll) const; 563 564 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlinePropertyWithAttrValue( 565 nsStaticAtom& aHTMLProperty, nsAtom* aAttribute, const nsAString& aValue, 566 bool* aFirst, bool* aAny, bool* aAll, nsAString& outValue); 567 568 /** 569 * RemoveInlinePropertyAsAction() removes a property which changes inline 570 * style of text. E.g., bold, italic, super and sub. 571 * 572 * @param aHTMLProperty Tag name whcih represents the inline style you want 573 * to remove. E.g., nsGkAtoms::strong, nsGkAtoms::b, 574 * etc. If nsGkAtoms::href, <a> element which has 575 * href attribute will be removed. 576 * If nsGkAtoms::name, <a> element which has non-empty 577 * name attribute will be removed. 578 * @param aAttribute If aHTMLProperty is nsGkAtoms::font, aAttribute should 579 * be nsGkAtoms::fase, nsGkAtoms::size, nsGkAtoms::color 580 * or nsGkAtoms::bgcolor. Otherwise, set nullptr. 581 * Must not use nsGkAtoms::_empty here. 582 * @param aPrincipal Set subject principal if it may be called by JS. If 583 * set to nullptr, will be treated as called by system. 584 */ 585 MOZ_CAN_RUN_SCRIPT nsresult RemoveInlinePropertyAsAction( 586 nsStaticAtom& aHTMLProperty, nsStaticAtom* aAttribute, 587 nsIPrincipal* aPrincipal = nullptr); 588 589 MOZ_CAN_RUN_SCRIPT nsresult 590 RemoveAllInlinePropertiesAsAction(nsIPrincipal* aPrincipal = nullptr); 591 592 MOZ_CAN_RUN_SCRIPT nsresult 593 IncreaseFontSizeAsAction(nsIPrincipal* aPrincipal = nullptr); 594 595 MOZ_CAN_RUN_SCRIPT nsresult 596 DecreaseFontSizeAsAction(nsIPrincipal* aPrincipal = nullptr); 597 598 /** 599 * GetFontColorState() returns foreground color information in first 600 * range of Selection. 601 * If first range of Selection is collapsed and there is a cache of style for 602 * new text, aIsMixed is set to false and aColor is set to the cached color. 603 * If first range of Selection is collapsed and there is no cached color, 604 * this returns the color of the node, aIsMixed is set to false and aColor is 605 * set to the color. 606 * If first range of Selection is not collapsed, this collects colors of 607 * each node in the range. If there are two or more colors, aIsMixed is set 608 * to true and aColor is truncated. If only one color is set to all of the 609 * range, aIsMixed is set to false and aColor is set to the color. 610 * If there is no Selection ranges, aIsMixed is set to false and aColor is 611 * truncated. 612 * 613 * @param aIsMixed Must not be nullptr. This is set to true 614 * if there is two or more colors in first 615 * range of Selection. 616 * @param aColor Returns the color if only one color is set to 617 * all of first range in Selection. Otherwise, 618 * returns empty string. 619 * @return Returns error only when illegal cases, e.g., 620 * Selection instance has gone, first range 621 * Selection is broken. 622 */ 623 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 624 GetFontColorState(bool* aIsMixed, nsAString& aColor); 625 626 /** 627 * Detach aComposerCommandsUpdater from this. 628 */ 629 void Detach(const ComposerCommandsUpdater& aComposerCommandsUpdater); 630 631 nsStaticAtom& DefaultParagraphSeparatorTagName() const { 632 return HTMLEditor::ToParagraphSeparatorTagName(mDefaultParagraphSeparator); 633 } 634 ParagraphSeparator GetDefaultParagraphSeparator() const { 635 return mDefaultParagraphSeparator; 636 } 637 void SetDefaultParagraphSeparator(ParagraphSeparator aSep) { 638 mDefaultParagraphSeparator = aSep; 639 } 640 static nsStaticAtom& ToParagraphSeparatorTagName( 641 ParagraphSeparator aSeparator) { 642 switch (aSeparator) { 643 case ParagraphSeparator::div: 644 return *nsGkAtoms::div; 645 case ParagraphSeparator::p: 646 return *nsGkAtoms::p; 647 case ParagraphSeparator::br: 648 return *nsGkAtoms::br; 649 default: 650 MOZ_ASSERT_UNREACHABLE("New paragraph separator isn't handled here"); 651 return *nsGkAtoms::div; 652 } 653 } 654 655 /** 656 * Modifies the table containing the selection according to the 657 * activation of an inline table editing UI element 658 * @param aUIAnonymousElement [IN] the inline table editing UI element 659 */ 660 MOZ_CAN_RUN_SCRIPT nsresult 661 DoInlineTableEditingAction(const Element& aUIAnonymousElement); 662 663 /** 664 * GetInclusiveAncestorByTagName() looks for an element node whose name 665 * matches aTagName from aNode or anchor node of Selection to <body> element. 666 * 667 * @param aTagName The tag name which you want to look for. 668 * Must not be nsGkAtoms::_empty. 669 * If nsGkAtoms::list, the result may be <ul>, <ol> or 670 * <dl> element. 671 * If nsGkAtoms::td, the result may be <td> or <th>. 672 * If nsGkAtoms::href, the result may be <a> element 673 * which has "href" attribute with non-empty value. 674 * If nsGkAtoms::anchor, the result may be <a> which 675 * has "name" attribute with non-empty value. 676 * @param aContent Start node to look for the result. 677 * @return If an element which matches aTagName, returns 678 * an Element. Otherwise, nullptr. 679 */ 680 Element* GetInclusiveAncestorByTagName(const nsStaticAtom& aTagName, 681 nsIContent& aContent) const; 682 683 /** 684 * Compute editing host for aContent. If this editor isn't active in the DOM 685 * window, this returns nullptr. 686 */ 687 enum class LimitInBodyElement { No, Yes }; 688 [[nodiscard]] Element* ComputeEditingHost( 689 const nsIContent& aContent, 690 LimitInBodyElement aLimitInBodyElement = LimitInBodyElement::Yes) const { 691 return ComputeEditingHostInternal(&aContent, aLimitInBodyElement); 692 } 693 694 /** 695 * Compute editing host for the focus node of the Selection. If this editor 696 * isn't active in the DOM window, this returns nullptr. 697 */ 698 [[nodiscard]] Element* ComputeEditingHost( 699 LimitInBodyElement aLimitInBodyElement = LimitInBodyElement::Yes) const { 700 return ComputeEditingHostInternal(nullptr, aLimitInBodyElement); 701 } 702 703 /** 704 * Return true if this editor was notified of focus, but has not been notified 705 * of the blur. 706 */ 707 [[nodiscard]] bool HasFocus() const { return mHasFocus; } 708 709 /** 710 * Return true if this editor is in the designMode. 711 */ 712 [[nodiscard]] bool IsInDesignMode() const { return mIsInDesignMode; } 713 714 /** 715 * Return true if entire the document is editable (although the document 716 * may have non-editable nodes, e.g., 717 * <body contenteditable><div contenteditable="false"></div></body> 718 */ 719 bool EntireDocumentIsEditable() const; 720 721 /** 722 * Basically, this always returns true if we're for `contenteditable` or 723 * `designMode` editor in web apps. However, e.g., Composer of SeaMonkey 724 * can make the editor not tabbable. 725 */ 726 bool IsTabbable() const { return IsInteractionAllowed(); } 727 728 /** 729 * NotifyEditingHostMaybeChanged() is called when new element becomes 730 * contenteditable when the document already had contenteditable elements. 731 */ 732 MOZ_CAN_RUN_SCRIPT void NotifyEditingHostMaybeChanged(); 733 734 /** Insert a string as quoted text 735 * (whose representation is dependant on the editor type), 736 * replacing the selected text (if any). 737 * 738 * @param aQuotedText The actual text to be quoted 739 * @parem aNodeInserted Return the node which was inserted. 740 */ 741 MOZ_CAN_RUN_SCRIPT // USED_BY_COMM_CENTRAL 742 nsresult 743 InsertAsQuotation(const nsAString& aQuotedText, nsINode** aNodeInserted); 744 745 MOZ_CAN_RUN_SCRIPT nsresult InsertHTMLAsAction( 746 const nsAString& aInString, nsIPrincipal* aPrincipal = nullptr); 747 748 /** 749 * Refresh positions of resizers. If you change size of target of resizers, 750 * you need to refresh position of resizers with calling this. 751 */ 752 MOZ_CAN_RUN_SCRIPT nsresult RefreshResizers(); 753 754 bool IsWrapHackEnabled() const { 755 return (mFlags & nsIEditor::eEditorEnableWrapHackMask) != 0; 756 } 757 758 /** 759 * Return true if this is in the plaintext mail composer mode of 760 * Thunderbird or something. 761 * NOTE: This is different from contenteditable="plaintext-only" 762 */ 763 bool IsPlaintextMailComposer() const { 764 const bool isPlaintextMode = 765 (mFlags & nsIEditor::eEditorPlaintextMask) != 0; 766 MOZ_ASSERT_IF(IsTextEditor(), isPlaintextMode); 767 return isPlaintextMode; 768 } 769 770 protected: // May be called by friends. 771 /**************************************************************************** 772 * Some friend classes are allowed to call the following protected methods. 773 * However, those methods won't prepare caches of some objects which are 774 * necessary for them. So, if you call them from friend classes, you need 775 * to make sure that AutoEditActionDataSetter is created. 776 ****************************************************************************/ 777 778 enum class LineBreakType : bool { 779 BRElement, // <br> 780 Linefeed, // Preformatted linefeed 781 }; 782 friend std::ostream& operator<<(std::ostream& aStream, 783 const LineBreakType aLineBreakType) { 784 switch (aLineBreakType) { 785 case LineBreakType::BRElement: 786 return aStream << "LineBreakType::BRElement"; 787 case LineBreakType::Linefeed: 788 return aStream << "LineBreakType::BRElement"; 789 } 790 MOZ_ASSERT_UNREACHABLE("Invalid LineBreakType"); 791 return aStream; 792 } 793 794 /** 795 * Return preferred line break when you insert a line break in aNode (if 796 * aNode is a Text node, this assumes that line break will be inserted to 797 * its parent element). 798 * 799 * @param aNode The node where you want to insert a line break. 800 * This should be a inclusive descendant of 801 * aEditingHost because if it's not connected, we can 802 * not refer the proper style information. 803 * @param aEditingHost The editing host. 804 */ 805 Maybe<LineBreakType> GetPreferredLineBreakType( 806 const nsINode& aNode, const Element& aEditingHost) const; 807 808 /** 809 * InsertLineBreak() creates a <br> element or a Text node which has only 810 * preformatted linefeed and inserts it at aPointToInsert. 811 * 812 * @param aWithTransaction Whether the inserting is new element is undoable 813 * or not. WithTransaction::No is useful only when 814 * the new element is inserted into a new element 815 * which has not been connected yet. 816 * @param aLineBreakType Whether a <br> element or a linefeed should be 817 * used. 818 * @param aPointToInsert The DOM point where a <br> element or a Text 819 * node should be inserted. 820 * @param aSelect If eNone, returns a point to put caret which is 821 * suggested by InsertNodeTransaction. 822 * If eNext, returns a point after the new <br> 823 * element. 824 * If ePrevious, returns a point at the new <br> 825 * element. 826 * @return The new <br> or Text node and suggesting point 827 * to put caret with respecting aSelect. 828 */ 829 MOZ_CAN_RUN_SCRIPT Result<CreateLineBreakResult, nsresult> InsertLineBreak( 830 WithTransaction aWithTransaction, LineBreakType aLineBreakType, 831 const EditorDOMPoint& aPointToInsert, EDirection aSelect = eNone); 832 833 /** 834 * Delete text in the range in aTextNode. If aTextNode is not editable, this 835 * does nothing. 836 * 837 * @param aTextNode The text node which should be modified. 838 * @param aOffset Start offset of removing text in aTextNode. 839 * @param aLength Length of removing text. 840 */ 841 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CaretPoint, nsresult> 842 DeleteTextWithTransaction(dom::Text& aTextNode, uint32_t aOffset, 843 uint32_t aLength); 844 845 /** 846 * Replace text in the range with aStringToInsert. If there is a DOM range 847 * exactly same as the replacing range, it'll be collapsed to 848 * {aTextNode, aOffset} because of the order of deletion and insertion. 849 * Therefore, the callers may need to handle `Selection` even when callers 850 * do not want to update `Selection`. 851 */ 852 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<InsertTextResult, nsresult> 853 ReplaceTextWithTransaction(dom::Text& aTextNode, uint32_t aOffset, 854 uint32_t aLength, 855 const nsAString& aStringToInsert); 856 857 struct NormalizedStringToInsertText; 858 859 /** 860 * Insert text to aPointToInsert or replace text in the range stored by aData 861 * in the text node specified by aPointToInsert with the normalized string 862 * stored by aData. So, aPointToInsert must be in a `Text` node if 863 * aData.ReplaceLength() is not 0. 864 */ 865 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<InsertTextResult, nsresult> 866 InsertOrReplaceTextWithTransaction(const EditorDOMPoint& aPointToInsert, 867 const NormalizedStringToInsertText& aData); 868 869 struct ReplaceWhiteSpacesData; 870 871 /** 872 * Replace or insert white-spaces of aData to aTextNode. 873 */ 874 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<InsertTextResult, nsresult> 875 ReplaceTextWithTransaction(dom::Text& aTextNode, 876 const ReplaceWhiteSpacesData& aData); 877 878 /** 879 * Insert aStringToInsert to aPointToInsert. If the point is not editable, 880 * this returns error. 881 */ 882 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<InsertTextResult, nsresult> 883 InsertTextWithTransaction(const nsAString& aStringToInsert, 884 const EditorDOMPoint& aPointToInsert, 885 InsertTextTo aInsertTextTo) final; 886 887 /** 888 * CopyLastEditableChildStyles() clones inline container elements into 889 * aPreviousBlock to aNewBlock to keep using same style in it. 890 * 891 * @param aPreviousBlock The previous block element. All inline 892 * elements which are last sibling of each level 893 * are cloned to aNewBlock. 894 * @param aNewBlock New block container element. All children of 895 * this is deleted first. 896 * @param aEditingHost The editing host. 897 * @return If succeeded, returns a suggesting point to put 898 * caret. 899 */ 900 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 901 CopyLastEditableChildStylesWithTransaction(Element& aPreviousBlock, 902 Element& aNewBlock, 903 const Element& aEditingHost); 904 905 /** 906 * RemoveBlockContainerWithTransaction() removes aElement from the DOM tree 907 * but moves its all children to its parent node and if its parent needs <br> 908 * element to have at least one line-height, this inserts <br> element 909 * automatically. 910 * 911 * @param aElement Block element to be removed. 912 * @return If succeeded, returns a suggesting point to put 913 * caret. 914 */ 915 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 916 RemoveBlockContainerWithTransaction(Element& aElement); 917 918 MOZ_CAN_RUN_SCRIPT nsresult RemoveAttributeOrEquivalent( 919 Element* aElement, nsAtom* aAttribute, bool aSuppressTransaction) final; 920 MOZ_CAN_RUN_SCRIPT nsresult SetAttributeOrEquivalent( 921 Element* aElement, nsAtom* aAttribute, const nsAString& aValue, 922 bool aSuppressTransaction) final; 923 using EditorBase::RemoveAttributeOrEquivalent; 924 using EditorBase::SetAttributeOrEquivalent; 925 926 /** 927 * Returns container element of ranges in Selection. If Selection is 928 * collapsed, returns focus container node (or its parent element). 929 * If Selection selects only one element node, returns the element node. 930 * If Selection is only one range, returns common ancestor of the range. 931 * XXX If there are two or more Selection ranges, this returns parent node 932 * of start container of a range which starts with different node from 933 * start container of the first range. 934 */ 935 Element* GetSelectionContainerElement() const; 936 937 /** 938 * DeleteTableCellContentsWithTransaction() removes any contents in cell 939 * elements. If two or more cell elements are selected, this removes 940 * all selected cells' contents. Otherwise, this removes contents of 941 * a cell which contains first selection range. This does not return 942 * error even if selection is not in cell element, just does nothing. 943 */ 944 MOZ_CAN_RUN_SCRIPT nsresult DeleteTableCellContentsWithTransaction(); 945 946 /** 947 * extracts an element from the normal flow of the document and 948 * positions it, and puts it back in the normal flow. 949 * @param aElement [IN] the element 950 * @param aEnabled [IN] true to absolutely position the element, 951 * false to put it back in the normal flow 952 */ 953 MOZ_CAN_RUN_SCRIPT nsresult SetPositionToAbsoluteOrStatic(Element& aElement, 954 bool aEnabled); 955 956 /** 957 * adds aChange to the z-index of an arbitrary element. 958 * @param aElement [IN] the element 959 * @param aChange [IN] relative change to apply to current z-index of 960 * the element 961 * @return The new z-index of the element 962 */ 963 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<int32_t, nsresult> 964 AddZIndexWithTransaction(nsStyledElement& aStyledElement, int32_t aChange); 965 966 /** 967 * Join together adjacent editable text nodes in the range except preformatted 968 * linefeed only nodes. 969 */ 970 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 971 CollapseAdjacentTextNodes(nsRange& aRange); 972 973 static dom::Element* GetLinkElement(nsINode* aNode); 974 975 /** 976 * Helper routines for font size changing. 977 */ 978 enum class FontSize { incr, decr }; 979 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 980 SetFontSizeOnTextNode(Text& aTextNode, uint32_t aStartOffset, 981 uint32_t aEndOffset, FontSize aIncrementOrDecrement); 982 983 enum class SplitAtEdges { 984 // SplitNodeDeepWithTransaction() won't split container element 985 // nodes at their edges. I.e., when split point is start or end of 986 // container, it won't be split. 987 eDoNotCreateEmptyContainer, 988 // SplitNodeDeepWithTransaction() always splits containers even 989 // if the split point is at edge of a container. E.g., if split point is 990 // start of an inline element, empty inline element is created as a new left 991 // node. 992 eAllowToCreateEmptyContainer, 993 }; 994 995 /** 996 * SplitAncestorStyledInlineElementsAtRangeEdges() splits all ancestor inline 997 * elements in the block at aRange if given style matches with some of them. 998 * 999 * @param aRange Ancestor inline elements of the start and end 1000 * boundaries will be split. 1001 * @param aStyle The style which you want to split. 1002 * RemoveAllStyles instance is allowed to split any 1003 * inline elements. 1004 * @param aSplitAtEdges Whether this should split elements at start or 1005 * end of inline elements or not. 1006 */ 1007 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitRangeOffResult, nsresult> 1008 SplitAncestorStyledInlineElementsAtRangeEdges(const EditorDOMRange& aRange, 1009 const EditorInlineStyle& aStyle, 1010 SplitAtEdges aSplitAtEdges); 1011 1012 /** 1013 * SplitAncestorStyledInlineElementsAt() splits ancestor inline elements at 1014 * aPointToSplit if specified style matches with them. 1015 * 1016 * @param aPointToSplit The point to split style at. 1017 * @param aStyle The style which you want to split. 1018 * RemoveAllStyles instance is allowed to split any 1019 * inline elements. 1020 * @param aSplitAtEdges Whether this should split elements at start or 1021 * end of inline elements or not. 1022 * @return The result of SplitNodeDeepWithTransaction() 1023 * with topmost split element. If this didn't 1024 * find inline elements to be split, Handled() 1025 * returns false. 1026 */ 1027 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitNodeResult, nsresult> 1028 SplitAncestorStyledInlineElementsAt(const EditorDOMPoint& aPointToSplit, 1029 const EditorInlineStyle& aStyle, 1030 SplitAtEdges aSplitAtEdges); 1031 1032 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlinePropertyBase( 1033 const EditorInlineStyle& aStyle, const nsAString* aValue, bool* aFirst, 1034 bool* aAny, bool* aAll, nsAString* outValue) const; 1035 1036 /** 1037 * ClearStyleAt() splits parent elements to remove the specified style. 1038 * If this splits some parent elements at near their start or end, such 1039 * empty elements will be removed. Then, remove the specified style 1040 * from the point and returns DOM point to put caret. 1041 * 1042 * @param aPoint The point to clear style at. 1043 * @param aStyleToRemove The style which you want to clear. 1044 * @param aSpecifiedStyle Whether the class and style attributes should 1045 * be preserved or discarded. 1046 * @param aEditingHost The editing host. 1047 * @return A candidate position to put caret. If there is 1048 * AutoTransactionsConserveSelection instances, this stops 1049 * suggesting caret point only in some cases. 1050 */ 1051 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 1052 ClearStyleAt(const EditorDOMPoint& aPoint, 1053 const EditorInlineStyle& aStyleToRemove, 1054 SpecifiedStyle aSpecifiedStyle, const Element& aEditingHost); 1055 1056 MOZ_CAN_RUN_SCRIPT nsresult SetPositionToAbsolute(Element& aElement); 1057 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1058 SetPositionToStatic(Element& aElement); 1059 1060 /** 1061 * Called when aRunner starts calling a DOM API to modify the DOM. 1062 * 1063 * @return The previous runner if the DOM API calls are unfortunately nested. 1064 */ 1065 [[nodiscard]] const AutoDOMAPIWrapperBase* OnDOMAPICallStart( 1066 const AutoDOMAPIWrapperBase& aRunner); 1067 1068 /** 1069 * Called when aRunner ends calling a DOM API to modify the DOM. 1070 * 1071 * @param aPrevRunner Must be set to the result of the preceding 1072 * OnDOMMutationStart() call. 1073 */ 1074 void OnDOMAPICallEnd(const AutoDOMAPIWrapperBase* aPrevRunner); 1075 1076 class DocumentModifiedEvent; 1077 1078 /** 1079 * OnModifyDocument() is called when the editor is changed. This should 1080 * be called only by DocumentModifiedEvent when AutoEditActionDataSetter 1081 * instance is in the stack. 1082 */ 1083 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1084 OnModifyDocument(const DocumentModifiedEvent& aRunner); 1085 1086 /** 1087 * DoSplitNode() inserts aNewNode and moves all content before or after 1088 * aStartOfRightNode to aNewNode. 1089 * 1090 * @param aStartOfRightNode The point to split. The container will keep 1091 * having following or previous content of this. 1092 * @param aNewNode The new node called. The previous or following 1093 * content of aStartOfRightNode will be moved into 1094 * this node. 1095 */ 1096 MOZ_CAN_RUN_SCRIPT Result<SplitNodeResult, nsresult> DoSplitNode( 1097 const EditorDOMPoint& aStartOfRightNode, nsIContent& aNewNode); 1098 1099 /** 1100 * DoJoinNodes() merges contents in aContentToRemove to aContentToKeep and 1101 * remove aContentToRemove from the DOM tree. aContentToRemove and 1102 * aContentToKeep must have same parent. Additionally, if one of 1103 * aContentToRemove or aContentToKeep is a text node, the other must be a 1104 * text node. 1105 * 1106 * @param aContentToKeep The node that will remain after the join. 1107 * @param aContentToRemove The node that will be joined with aContentToKeep. 1108 * There is no requirement that the two nodes be of 1109 * the same type. 1110 */ 1111 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1112 DoJoinNodes(nsIContent& aContentToKeep, nsIContent& aContentToRemove); 1113 1114 /** 1115 * Called when JoinNodesTransaction::DoTransaction() did its transaction. 1116 * Note that this is not called when undoing nor redoing. 1117 * 1118 * @param aTransaction The transaction which did join nodes. 1119 * @param aDoJoinNodesResult Result of the doing join nodes. 1120 */ 1121 MOZ_CAN_RUN_SCRIPT void DidJoinNodesTransaction( 1122 const JoinNodesTransaction& aTransaction, nsresult aDoJoinNodesResult); 1123 1124 protected: // edit sub-action handler 1125 /** 1126 * CanHandleHTMLEditSubAction() checks whether there is at least one 1127 * selection range or not, and whether the first range is editable. 1128 * If it's not editable, `Canceled()` of the result returns true. 1129 * If `Selection` is in odd situation, returns an error. 1130 * 1131 * XXX I think that `IsSelectionEditable()` is better name, but it's already 1132 * in `EditorBase`... 1133 */ 1134 enum class CheckSelectionInReplacedElement { No, Yes, OnlyWhenNotInSameNode }; 1135 Result<EditActionResult, nsresult> CanHandleHTMLEditSubAction( 1136 CheckSelectionInReplacedElement aCheckSelectionInReplacedElement = 1137 CheckSelectionInReplacedElement::Yes) const; 1138 1139 /** 1140 * EnsureCaretNotAfterInvisibleBRElement() makes sure that caret is NOT after 1141 * padding `<br>` element for preventing insertion after padding `<br>` 1142 * element at empty last line. 1143 * NOTE: This method should be called only when `Selection` is collapsed 1144 * because `Selection` is a pain to work with when not collapsed. 1145 * (no good way to extend start or end of selection), so we need to 1146 * ignore those types of selections. 1147 */ 1148 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1149 EnsureCaretNotAfterInvisibleBRElement(const Element& aEditingHost); 1150 1151 /** 1152 * MaybeCreatePaddingBRElementForEmptyEditor() creates padding <br> element 1153 * for empty editor if there is no children. 1154 */ 1155 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1156 MaybeCreatePaddingBRElementForEmptyEditor(); 1157 1158 /** 1159 * EnsureNoPaddingBRElementForEmptyEditor() removes padding <br> element 1160 * for empty editor if there is. 1161 */ 1162 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1163 EnsureNoPaddingBRElementForEmptyEditor(); 1164 1165 /** 1166 * ReflectPaddingBRElementForEmptyEditor() scans the tree from the root 1167 * element and sets mPaddingBRElementForEmptyEditor if exists, or otherwise 1168 * nullptr. Can be used to manage undo/redo. 1169 */ 1170 [[nodiscard]] nsresult ReflectPaddingBRElementForEmptyEditor(); 1171 1172 /** 1173 * PrepareInlineStylesForCaret() consider inline styles from top level edit 1174 * sub-action and setting it to `mPendingStylesToApplyToNewContent` and clear 1175 * inline style cache if necessary. 1176 * NOTE: This method should be called only when `Selection` is collapsed. 1177 */ 1178 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult PrepareInlineStylesForCaret(); 1179 1180 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 1181 HandleInsertText(const nsAString& aInsertionString, 1182 InsertTextFor aPurpose) final; 1183 1184 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertDroppedDataTransferAsAction( 1185 AutoEditActionDataSetter& aEditActionData, DataTransfer& aDataTransfer, 1186 const EditorDOMPoint& aDroppedAt, nsIPrincipal* aSourcePrincipal) final; 1187 1188 /** 1189 * GetInlineStyles() retrieves the style of aElement and modifies each item of 1190 * aPendingStyleCacheArray. This might cause flushing layout at retrieving 1191 * computed values of CSS properties. 1192 */ 1193 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlineStyles( 1194 Element& aElement, AutoPendingStyleCacheArray& aPendingStyleCacheArray); 1195 1196 /** 1197 * CacheInlineStyles() caches style of aElement into mCachedPendingStyles of 1198 * TopLevelEditSubAction. This may cause flushing layout at retrieving 1199 * computed value of CSS properties. 1200 */ 1201 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1202 CacheInlineStyles(Element& aElement); 1203 1204 /** 1205 * ReapplyCachedStyles() restores some styles which are disappeared during 1206 * handling edit action and it should be restored. This may cause flushing 1207 * layout at retrieving computed value of CSS properties. 1208 */ 1209 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult ReapplyCachedStyles(); 1210 1211 /** 1212 * CreateStyleForInsertText() sets CSS properties which are stored in 1213 * PendingStyles to proper element node. 1214 * 1215 * @param aPointToInsertText The point to insert text. 1216 * @param aEditingHost The editing host. 1217 * @return A suggest point to put caret or unset point. 1218 */ 1219 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 1220 CreateStyleForInsertText(const EditorDOMPoint& aPointToInsertText, 1221 const Element& aEditingHost); 1222 1223 /** 1224 * GetMostDistantAncestorMailCiteElement() returns most-ancestor mail cite 1225 * element. "mail cite element" is <pre> element when it's in plaintext editor 1226 * mode or an element with which calling HTMLEditUtils::IsMailCite() returns 1227 * true. 1228 * 1229 * @param aNode The start node to look for parent mail cite elements. 1230 */ 1231 Element* GetMostDistantAncestorMailCiteElement(const nsINode& aNode) const; 1232 1233 /** 1234 * Splits inclusive inline ancestors at both start and end of aRangeItem. If 1235 * this splits at every point, this modifies aRangeItem to point each split 1236 * point (typically, at right node). 1237 * 1238 * @param aRangeItem [in/out] One or two DOM points where should be 1239 * split. Will be modified to split point if 1240 * they're split. 1241 * @param aBlockInlineCheck [in] Whether this method considers block vs. 1242 * inline with computed style or the default style. 1243 * @param aEditingHost [in] The editing host. 1244 * @param aAncestorLimiter [in/optional] If specified, this stops splitting 1245 * ancestors when meets this node. 1246 * @return A suggest point to put caret if succeeded, but 1247 * it may be unset. 1248 */ 1249 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 1250 SplitInlineAncestorsAtRangeBoundaries( 1251 RangeItem& aRangeItem, BlockInlineCheck aBlockInlineCheck, 1252 const Element& aEditingHost, 1253 const nsIContent* aAncestorLimiter = nullptr); 1254 1255 /** 1256 * SplitElementsAtEveryBRElement() splits before all <br> elements in 1257 * aMostAncestorToBeSplit. All <br> nodes will be moved before right node 1258 * at splitting its parent. Finally, this returns left node, first <br> 1259 * element, next left node, second <br> element... and right-most node. 1260 * 1261 * @param aMostAncestorToBeSplit Most-ancestor element which should 1262 * be split. 1263 * @param aOutArrayOfNodes First left node, first <br> element, 1264 * Second left node, second <br> element, 1265 * ...right-most node. So, all nodes 1266 * in this list should be siblings (may be 1267 * broken the relation by mutation event 1268 * listener though). If first <br> element 1269 * is first leaf node of 1270 * aMostAncestorToBeSplit, starting from 1271 * the first <br> element. 1272 * @return A suggest point to put caret if 1273 * succeeded, but it may unset. 1274 */ 1275 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 1276 SplitElementsAtEveryBRElement( 1277 nsIContent& aMostAncestorToBeSplit, 1278 nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents); 1279 1280 /** 1281 * MaybeSplitElementsAtEveryBRElement() calls SplitElementsAtEveryBRElement() 1282 * for each given node when this needs to do that for aEditSubAction. 1283 * If split a node, it in aArrayOfContents is replaced with split nodes and 1284 * <br> elements. 1285 * 1286 * @return A suggest point to put caret if 1287 * succeeded, but it may unset. 1288 */ 1289 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 1290 MaybeSplitElementsAtEveryBRElement( 1291 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents, 1292 EditSubAction aEditSubAction); 1293 1294 /** 1295 * CreateRangeIncludingAdjuscentWhiteSpaces() creates an nsRange instance 1296 * which may be expanded from the given range to include adjuscent 1297 * white-spaces. If this fails handling something, returns nullptr. 1298 */ 1299 template <typename EditorDOMRangeType> 1300 already_AddRefed<nsRange> CreateRangeIncludingAdjuscentWhiteSpaces( 1301 const EditorDOMRangeType& aRange); 1302 template <typename EditorDOMPointType1, typename EditorDOMPointType2> 1303 already_AddRefed<nsRange> CreateRangeIncludingAdjuscentWhiteSpaces( 1304 const EditorDOMPointType1& aStartPoint, 1305 const EditorDOMPointType2& aEndPoint); 1306 1307 /** 1308 * GetRangeExtendedToHardLineEdgesForBlockEditAction() returns an extended 1309 * range if aRange should be extended before handling a block level editing. 1310 * If aRange start and/or end point <br> or something non-editable point, they 1311 * should be moved to nearest text node or something where the other methods 1312 * easier to handle edit action. 1313 */ 1314 [[nodiscard]] Result<EditorRawDOMRange, nsresult> 1315 GetRangeExtendedToHardLineEdgesForBlockEditAction( 1316 const nsRange* aRange, const Element& aEditingHost) const; 1317 1318 /** 1319 * InitializeInsertingElement is a callback type of methods which inserts 1320 * an element into the DOM tree. This is called immediately before inserting 1321 * aNewElement into the DOM tree. 1322 * 1323 * @param aHTMLEditor The HTML editor which modifies the DOM tree. 1324 * @param aNewElement The new element which will be or was inserted into 1325 * the DOM tree. 1326 * @param aPointToInsert The position aNewElement will be or was inserted. 1327 */ 1328 using InitializeInsertingElement = 1329 std::function<nsresult(HTMLEditor& aHTMLEditor, Element& aNewElement, 1330 const EditorDOMPoint& aPointToInsert)>; 1331 static InitializeInsertingElement DoNothingForNewElement; 1332 static InitializeInsertingElement InsertNewBRElement; 1333 1334 /** 1335 * Helper methods to implement InitializeInsertingElement. 1336 */ 1337 MOZ_CAN_RUN_SCRIPT static Result<CreateElementResult, nsresult> 1338 AppendNewElementToInsertingElement( 1339 HTMLEditor& aHTMLEditor, const nsStaticAtom& aTagName, 1340 Element& aNewElement, 1341 const InitializeInsertingElement& aInitializer = DoNothingForNewElement); 1342 MOZ_CAN_RUN_SCRIPT static Result<CreateElementResult, nsresult> 1343 AppendNewElementWithBRToInsertingElement(HTMLEditor& aHTMLEditor, 1344 const nsStaticAtom& aTagName, 1345 Element& aNewElement); 1346 1347 /** 1348 * Create an element node whose name is aTag at before aPointToInsert. When 1349 * this succeed to create an element node, this inserts the element to 1350 * aPointToInsert. 1351 * 1352 * @param aWithTransaction Whether the inserting is new element is undoable 1353 * or not. WithTransaction::No is useful only when 1354 * the new element is inserted into a new element 1355 * which has not been connected yet. 1356 * @param aTagName The element name to create. 1357 * @param aPointToInsert The insertion point of new element. 1358 * If this refers end of the container or after, 1359 * the transaction will append the element to the 1360 * container. 1361 * Otherwise, will insert the element before the 1362 * child node referred by this. 1363 * Note that this point will be invalid once this 1364 * method inserts the new element. 1365 * @param aInitializer A function to initialize the new element before 1366 * connecting the element into the DOM tree. Note 1367 * that this should not touch outside given element 1368 * because doing it would break range updater's 1369 * result. 1370 * @return The created new element node and candidate caret 1371 * position. 1372 */ 1373 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1374 CreateAndInsertElement( 1375 WithTransaction aWithTransaction, const nsAtom& aTagName, 1376 const EditorDOMPoint& aPointToInsert, 1377 const InitializeInsertingElement& aInitializer = DoNothingForNewElement); 1378 1379 /** 1380 * Callback of CopyAttributes(). 1381 * 1382 * @param aHTMLEditor The HTML editor. 1383 * @param aSrcElement The element which have the attribute. 1384 * @param aDestElement The element which will have the attribute. 1385 * @param aNamespaceID [in] The namespace ID of aAttrName. 1386 * @param aAttrName [in] The attribute name which will be copied. 1387 * @param aValue [in/out] The attribute value which will be copied. 1388 * Once updated, the new value is used. 1389 * @return true if the attribute should be copied, otherwise, 1390 * false. 1391 */ 1392 using AttributeFilter = std::function<bool( 1393 HTMLEditor& aHTMLEditor, Element& aSrcElement, Element& aDestElement, 1394 int32_t aNamespaceID, const nsAtom& aAttrName, nsString& aValue)>; 1395 static AttributeFilter CopyAllAttributes; 1396 static AttributeFilter CopyAllAttributesExceptId; 1397 static AttributeFilter CopyAllAttributesExceptDir; 1398 static AttributeFilter CopyAllAttributesExceptIdAndDir; 1399 1400 /** 1401 * Copy all attributes of aSrcElement to aDestElement as-is. Different from 1402 * EditorBase::CloneAttributesWithTransaction(), this does not use 1403 * SetAttributeOrEquivalent() nor does not clear existing attributes of 1404 * aDestElement. 1405 * 1406 * @param aWithTransaction Whether recoding with transactions or not. 1407 * @param aDestElement The element will have attributes. 1408 * @param aSrcElement The element whose attributes will be copied. 1409 */ 1410 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult CopyAttributes( 1411 WithTransaction aWithTransaction, Element& aDestElement, 1412 Element& aSrcElement, const AttributeFilter& = CopyAllAttributes); 1413 1414 /** 1415 * MaybeSplitAncestorsForInsertWithTransaction() does nothing if container of 1416 * aStartOfDeepestRightNode can have an element whose tag name is aTag. 1417 * Otherwise, looks for an ancestor node which is or is in active editing 1418 * host and can have an element whose name is aTag. If there is such 1419 * ancestor, its descendants are split. 1420 * 1421 * Note that this may create empty elements while splitting ancestors. 1422 * 1423 * @param aTag The name of element to be inserted 1424 * after calling this method. 1425 * @param aStartOfDeepestRightNode The start point of deepest right node. 1426 * This point must be in aEditingHost. 1427 * @param aEditingHost The editing host. 1428 * @return When succeeded, SplitPoint() returns 1429 * the point to insert the element. 1430 */ 1431 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitNodeResult, nsresult> 1432 MaybeSplitAncestorsForInsertWithTransaction( 1433 const nsAtom& aTag, const EditorDOMPoint& aStartOfDeepestRightNode, 1434 const Element& aEditingHost); 1435 1436 /** 1437 * InsertElementWithSplittingAncestorsWithTransaction() is a wrapper of 1438 * MaybeSplitAncestorsForInsertWithTransaction() and CreateAndInsertElement(). 1439 * I.e., will create an element whose tag name is aTagName and split ancestors 1440 * if it's necessary, then, insert it. 1441 * 1442 * @param aTagName The tag name which you want to insert new 1443 * element at aPointToInsert. 1444 * @param aPointToInsert The insertion point. New element will be 1445 * inserted before here. 1446 * @param aBRElementNextToSplitPoint 1447 * Whether <br> element should be deleted or 1448 * kept if and only if a <br> element follows 1449 * split point. 1450 * @param aEditingHost The editing host with which we're handling it. 1451 * @param aInitializer A function to initialize the new element before 1452 * connecting the element into the DOM tree. Note 1453 * that this should not touch outside given element 1454 * because doing it would break range updater's 1455 * result. 1456 * @return If succeeded, returns the new element node and 1457 * suggesting point to put caret. 1458 */ 1459 enum class BRElementNextToSplitPoint { Keep, Delete }; 1460 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1461 InsertElementWithSplittingAncestorsWithTransaction( 1462 const nsAtom& aTagName, const EditorDOMPoint& aPointToInsert, 1463 BRElementNextToSplitPoint aBRElementNextToSplitPoint, 1464 const Element& aEditingHost, 1465 const InitializeInsertingElement& aInitializer = DoNothingForNewElement); 1466 1467 /** 1468 * Split aElementToSplit at two points, before aStartOfMiddleElement and after 1469 * aEndOfMiddleElement. If they are very start or very end of aBlockElement, 1470 * this won't create empty block. 1471 * 1472 * @param aElementToSplit An element which will be split. 1473 * @param aStartOfMiddleElement Start node of middle block element. 1474 * @param aEndOfMiddleElement End node of middle block element. 1475 */ 1476 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitRangeOffFromNodeResult, nsresult> 1477 SplitRangeOffFromElement(Element& aElementToSplit, 1478 nsIContent& aStartOfMiddleElement, 1479 nsIContent& aEndOfMiddleElement); 1480 1481 /** 1482 * RemoveBlockContainerElementWithTransactionBetween() splits the nodes 1483 * at aStartOfRange and aEndOfRange, then, removes the middle element which 1484 * was split off from aBlockContainerElement and moves the ex-children to 1485 * where the middle element was. I.e., all nodes between aStartOfRange and 1486 * aEndOfRange (including themselves) will be unwrapped from 1487 * aBlockContainerElement. 1488 * 1489 * @param aBlockContainerElement The node which will be split. 1490 * @param aStartOfRange The first node which will be unwrapped 1491 * from aBlockContainerElement. 1492 * @param aEndOfRange The last node which will be unwrapped from 1493 * aBlockContainerElement. 1494 * @param aBlockInlineCheck Whether this method considers block vs. 1495 * inline with computed style or the default 1496 * style. 1497 * @return The left content is new created left 1498 * element of aBlockContainerElement. 1499 * The right content is split element, 1500 * i.e., must be aBlockContainerElement. 1501 * The middle content is nullptr since 1502 * removing it is the job of this method. 1503 */ 1504 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitRangeOffFromNodeResult, nsresult> 1505 RemoveBlockContainerElementWithTransactionBetween( 1506 Element& aBlockContainerElement, nsIContent& aStartOfRange, 1507 nsIContent& aEndOfRange, BlockInlineCheck aBlockInlineCheck); 1508 1509 /** 1510 * WrapContentsInBlockquoteElementsWithTransaction() inserts at least one 1511 * <blockquote> element and moves nodes in aArrayOfContents into new 1512 * <blockquote> elements. If aArrayOfContents includes a table related element 1513 * except <table>, this calls itself recursively to insert <blockquote> into 1514 * the cell. 1515 * 1516 * @param aArrayOfContents Nodes which will be moved into created 1517 * <blockquote> elements. 1518 * @param aEditingHost The editing host. 1519 * @return A blockquote element which is created at last 1520 * and a suggest of caret position if succeeded. 1521 * The caret suggestion may be unset if there is 1522 * no suggestion. 1523 */ 1524 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1525 WrapContentsInBlockquoteElementsWithTransaction( 1526 const nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents, 1527 const Element& aEditingHost); 1528 1529 /** 1530 * Our traditional formatBlock was same as XUL cmd_paragraphState command. 1531 * However, the behavior is pretty different from the others and aligning 1532 * the XUL command behavior may break Thunderbird a lot because it handles 1533 * <blockquote> in a special path and <div> (generic block element) is not 1534 * treated as a format node and these things may be used for designing 1535 * current roles of the elements in the email composer of Thunderbird. 1536 * Therefore, we create a new mode for HTMLFormatBlockCommand to align 1537 * the behavior to the others but does not harm Thunderbird. 1538 */ 1539 enum class FormatBlockMode { 1540 // Document.execCommand("formatBlock"). Cannot set new format to "normal" 1541 // nor "". So, the paths to handle these ones are not used in this mode. 1542 HTMLFormatBlockCommand, 1543 // cmd_paragraphState. Can set new format to "normal" or "" to remove 1544 // ancestor format blocks. 1545 XULParagraphStateCommand, 1546 }; 1547 1548 /** 1549 * RemoveBlockContainerElementsWithTransaction() removes all format blocks, 1550 * table related element, etc in aArrayOfContents from the DOM tree. If 1551 * aArrayOfContents has a format node, it will be removed and its contents 1552 * will be moved to where it was. 1553 * If aArrayOfContents has a table related element, <li>, <blockquote> or 1554 * <div>, it will be removed and its contents will be moved to where it was. 1555 * 1556 * @param aFormatBlockMode Whether HTML formatBlock command or XUL 1557 * paragraphState command. 1558 * 1559 * @return A suggest point to put caret if succeeded, but it may be 1560 * unset if there is no suggestion. 1561 */ 1562 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 1563 RemoveBlockContainerElementsWithTransaction( 1564 const nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents, 1565 FormatBlockMode aFormatBlockMode, BlockInlineCheck aBlockInlineCheck); 1566 1567 /** 1568 * CreateOrChangeFormatContainerElement() formats all nodes in 1569 * aArrayOfContents with block elements whose name is aNewFormatTagName. 1570 * 1571 * If aArrayOfContents has an inline element, a block element is created and 1572 * the inline element and following inline elements are moved into the new 1573 * block element. 1574 * If aArrayOfContents has <br> elements, they'll be removed from the DOM tree 1575 * and new block element will be created when there are some remaining inline 1576 * elements. 1577 * If aArrayOfContents has a block element, this calls itself with children of 1578 * the block element. Then, new block element will be created when there are 1579 * some remaining inline elements. 1580 * 1581 * @param aArrayOfContents Must be descendants of a node. 1582 * @param aNewFormatTagName The element name of new block elements. 1583 * @param aFormatBlockMode The replacing block element target type is for 1584 * whether HTML formatBLock command or XUL 1585 * paragraphState command. 1586 * @param aEditingHost The editing host. 1587 * @return The latest created new block element and a 1588 * suggest point to put caret. 1589 */ 1590 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1591 CreateOrChangeFormatContainerElement( 1592 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents, 1593 const nsStaticAtom& aNewFormatTagName, FormatBlockMode aFormatBlockMode, 1594 const Element& aEditingHost); 1595 1596 /** 1597 * FormatBlockContainerWithTransaction() is implementation of "formatBlock" 1598 * command of `Document.execCommand()`. This applies block style or removes 1599 * it. 1600 * 1601 * @param aSelectionRanges The ranges which are cloned by selection or 1602 * updated from it with doing something before 1603 * calling this. 1604 * @param aNewFormatTagName New block tag name. 1605 * If nsGkAtoms::normal or nsGkAtoms::_empty, 1606 * RemoveBlockContainerElementsWithTransaction() 1607 * will be called. 1608 * If nsGkAtoms::blockquote, 1609 * WrapContentsInBlockquoteElementsWithTransaction() 1610 * will be called. 1611 * Otherwise, CreateOrChangeBlockContainerElement() 1612 * will be called. 1613 * @param aFormatBlockMode Whether HTML formatBlock command or XUL 1614 * paragraphState command. 1615 * @param aEditingHost The editing host. 1616 * @return If selection should be finally collapsed in a 1617 * created block element, this returns the element. 1618 */ 1619 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<RefPtr<Element>, nsresult> 1620 FormatBlockContainerWithTransaction( 1621 AutoClonedSelectionRangeArray& aSelectionRanges, 1622 const nsStaticAtom& aNewFormatTagName, FormatBlockMode aFormatBlockMode, 1623 const Element& aEditingHost); 1624 1625 /** 1626 * Retrun true if the specified line break can be inserted around aContent. 1627 * If aContent is an Element, this checks whether the element can have the 1628 * line break. 1629 * If aContent is a Text, this check whether its container element can have 1630 * the line break. 1631 */ 1632 [[nodiscard]] static bool CanInsertLineBreak(LineBreakType aLineBreakType, 1633 const nsIContent& aContent); 1634 1635 /** 1636 * If aPointToInsert is between line breaks or block boundaries, this 1637 * puts a <br> element to make an empty line between them. 1638 */ 1639 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateLineBreakResult, nsresult> 1640 InsertPaddingBRElementToMakeEmptyLineVisibleIfNeeded( 1641 const EditorDOMPoint& aPointToInsert); 1642 1643 /** 1644 * Insert a padding <br> if aPoint is in an empty block. 1645 * 1646 * @param aPoint The place where you want to put a padding line 1647 * break. 1648 * @param aDeleteEmptyInlines If nsIEditor::eStrip, this deletes empty inlines 1649 * before inserting a line break from the inserting 1650 * point. 1651 */ 1652 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateLineBreakResult, nsresult> 1653 InsertPaddingBRElementIfInEmptyBlock( 1654 const EditorDOMPoint& aPoint, 1655 nsIEditor::EStripWrappers aDeleteEmptyInlines); 1656 1657 /** 1658 * Insert a padding <br> element for making preceding collapsible white-spaces 1659 * visible or the point is empty between block boundaries. 1660 * 1661 * @param aPoint Where you want to check. A padding <br> may be 1662 * inserted different from this point. 1663 * @param aDeleteEmptyInlines If nsIEditor::eStrip, this deletes empty inlines 1664 * before inserting <br> from the inserting point. 1665 * @param aEditingHost The editing host. 1666 */ 1667 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateLineBreakResult, nsresult> 1668 InsertPaddingBRElementIfNeeded(const EditorDOMPoint& aPoint, 1669 nsIEditor::EStripWrappers aDeleteEmptyInlines, 1670 const Element& aEditingHost); 1671 1672 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CaretPoint, nsresult> 1673 DeleteRangesWithTransaction(nsIEditor::EDirection aDirectionAndAmount, 1674 nsIEditor::EStripWrappers aStripWrappers, 1675 AutoClonedRangeArray& aRangesToDelete) override; 1676 1677 class AutoInsertParagraphHandler; 1678 class AutoInsertLineBreakHandler; 1679 1680 /** 1681 * InsertParagraphSeparatorAsSubAction() handles insertParagraph command 1682 * (i.e., handling Enter key press) with the above helper methods. 1683 * 1684 * @param aEditingHost The editing host. 1685 */ 1686 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 1687 InsertParagraphSeparatorAsSubAction(const Element& aEditingHost); 1688 1689 /** 1690 * InsertLineBreakAsSubAction() inserts a new <br> element or a linefeed 1691 * character at selection. If there is non-collapsed selection ranges, the 1692 * selected ranges is deleted first. 1693 */ 1694 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertLineBreakAsSubAction(); 1695 1696 /** 1697 * ChangeListElementType() replaces child list items of aListElement with 1698 * new list item element whose tag name is aNewListItemTag. 1699 * Note that if there are other list elements as children of aListElement, 1700 * this calls itself recursively even though it's invalid structure. 1701 * 1702 * @param aListElement The list element whose list items will be 1703 * replaced. 1704 * @param aNewListTag New list tag name. 1705 * @param aNewListItemTag New list item tag name. 1706 * @return New list element or an error code if it fails. 1707 * New list element may be aListElement if its 1708 * tag name is same as aNewListTag. 1709 */ 1710 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1711 ChangeListElementType(Element& aListElement, nsAtom& aListType, 1712 nsAtom& aItemType); 1713 1714 class AutoListElementCreator; 1715 1716 [[nodiscard]] static bool IsFormatElement(FormatBlockMode aFormatBlockMode, 1717 const nsIContent& aContent); 1718 1719 /** 1720 * MakeOrChangeListAndListItemAsSubAction() handles create list commands with 1721 * current selection. If 1722 * 1723 * @param aListElementOrListItemElementTagName 1724 * The new list element tag name or 1725 * new list item tag name. 1726 * If the former, list item tag name will 1727 * be computed automatically. Otherwise, 1728 * list tag name will be computed. 1729 * @param aBulletType If this is not empty string, it's set 1730 * to `type` attribute of new list item 1731 * elements. Otherwise, existing `type` 1732 * attributes will be removed. 1733 * @param aSelectAllOfCurrentList Yes if this should treat all of 1734 * ancestor list element at selection. 1735 * @param aEditingHost The editing host. 1736 */ 1737 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 1738 MakeOrChangeListAndListItemAsSubAction( 1739 const nsStaticAtom& aListElementOrListItemElementTagName, 1740 const nsAString& aBulletType, 1741 SelectAllOfCurrentList aSelectAllOfCurrentList, 1742 const Element& aEditingHost); 1743 1744 /** 1745 * DeleteTextAndTextNodesWithTransaction() removes text or text nodes in 1746 * the given range. 1747 */ 1748 enum class TreatEmptyTextNodes { 1749 // KeepIfContainerOfRangeBoundaries: 1750 // Will remove empty text nodes middle of the range, but keep empty 1751 // text nodes which are containers of range boundaries. 1752 KeepIfContainerOfRangeBoundaries, 1753 // Remove: 1754 // Will remove all empty text nodes. 1755 Remove, 1756 // RemoveAllEmptyInlineAncestors: 1757 // Will remove all empty text nodes and its inline ancestors which 1758 // become empty due to removing empty text nodes. 1759 RemoveAllEmptyInlineAncestors, 1760 }; 1761 template <typename EditorDOMPointType> 1762 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CaretPoint, nsresult> 1763 DeleteTextAndTextNodesWithTransaction( 1764 const EditorDOMPointType& aStartPoint, 1765 const EditorDOMPointType& aEndPoint, 1766 TreatEmptyTextNodes aTreatEmptyTextNodes); 1767 1768 /** 1769 * Delete the line break with DeleteNodeTransaction or DeleteTextTransaction. 1770 * 1771 * @param aLineBreak The line break to be deleted. 1772 * @param aDeleteEmptyInlines If nsIEditor::eStrip, this deletes new empty 1773 * inline element if and only if this deletes the 1774 * line break node. 1775 * @param aEditingHost The editing host. 1776 * @return The point where the line break was. 1777 */ 1778 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 1779 DeleteLineBreakWithTransaction(const EditorLineBreak& aLineBreak, 1780 nsIEditor::EStripWrappers aDeleteEmptyInlines, 1781 const Element& aEditingHost); 1782 1783 /** 1784 * JoinNodesWithTransaction() joins aLeftContent and aRightContent. Content 1785 * of aLeftContent will be merged into aRightContent. Actual implemenation of 1786 * this method is JoinNodesImpl(). So, see its explanation for the detail. 1787 * 1788 * @param aLeftContent Will be removed from the DOM tree. 1789 * @param aRightContent The node which will be new container of the content 1790 * of aLeftContent. 1791 */ 1792 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<JoinNodesResult, nsresult> 1793 JoinNodesWithTransaction(nsIContent& aLeftContent, nsIContent& aRightContent); 1794 1795 /** 1796 * JoinNearestEditableNodesWithTransaction() joins two editable nodes which 1797 * are themselves or the nearest editable node of aLeftNode and aRightNode. 1798 * XXX This method's behavior is odd. For example, if user types Backspace 1799 * key at the second editable paragraph in this case: 1800 * <div contenteditable> 1801 * <p>first editable paragraph</p> 1802 * <p contenteditable="false">non-editable paragraph</p> 1803 * <p>second editable paragraph</p> 1804 * </div> 1805 * The first editable paragraph's content will be moved into the second 1806 * editable paragraph and the non-editable paragraph becomes the first 1807 * paragraph of the editor. I don't think that it's expected behavior of 1808 * any users... 1809 * 1810 * @param aLeftNode The node which will be removed. 1811 * @param aRightNode The node which will be inserted the content of 1812 * aLeftNode. 1813 * @param aNewFirstChildOfRightNode 1814 * [out] The point at the first child of aRightNode. 1815 */ 1816 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 1817 JoinNearestEditableNodesWithTransaction( 1818 nsIContent& aLeftNode, nsIContent& aRightNode, 1819 EditorDOMPoint* aNewFirstChildOfRightNode); 1820 1821 /** 1822 * ReplaceContainerAndCloneAttributesWithTransaction() creates new element 1823 * whose name is aTagName, copies all attributes from aOldContainer to the 1824 * new element, moves all children in aOldContainer to the new element, then, 1825 * removes aOldContainer from the DOM tree. 1826 * 1827 * @param aOldContainer The element node which should be replaced 1828 * with new element. 1829 * @param aTagName The name of new element node. 1830 */ 1831 [[nodiscard]] inline MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1832 ReplaceContainerAndCloneAttributesWithTransaction(Element& aOldContainer, 1833 const nsAtom& aTagName); 1834 1835 /** 1836 * ReplaceContainerWithTransaction() creates new element whose name is 1837 * aTagName, sets aAttributes of the new element to aAttributeValue, moves 1838 * all children in aOldContainer to the new element, then, removes 1839 * aOldContainer from the DOM tree. 1840 * 1841 * @param aOldContainer The element node which should be replaced 1842 * with new element. 1843 * @param aTagName The name of new element node. 1844 * @param aAttribute Attribute name to be set to the new element. 1845 * @param aAttributeValue Attribute value to be set to aAttribute. 1846 */ 1847 [[nodiscard]] inline MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1848 ReplaceContainerWithTransaction(Element& aOldContainer, 1849 const nsAtom& aTagName, 1850 const nsAtom& aAttribute, 1851 const nsAString& aAttributeValue); 1852 1853 /** 1854 * ReplaceContainerWithTransaction() creates new element whose name is 1855 * aTagName, moves all children in aOldContainer to the new element, then, 1856 * removes aOldContainer from the DOM tree. 1857 * 1858 * @param aOldContainer The element node which should be replaced 1859 * with new element. 1860 * @param aTagName The name of new element node. 1861 */ 1862 [[nodiscard]] inline MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1863 ReplaceContainerWithTransaction(Element& aOldContainer, 1864 const nsAtom& aTagName); 1865 1866 /** 1867 * RemoveContainerWithTransaction() removes aElement from the DOM tree and 1868 * moves all its children to the parent of aElement. 1869 * 1870 * @param aElement The element to be removed. 1871 * @return A suggestion point to put caret. 1872 */ 1873 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 1874 RemoveContainerWithTransaction(Element& aElement); 1875 1876 /** 1877 * InsertContainerWithTransaction() creates new element whose name is 1878 * aWrapperTagName, moves aContentToBeWrapped into the new element, then, 1879 * inserts the new element into where aContentToBeWrapped was. 1880 * NOTE: This method does not check if aContentToBeWrapped is valid child 1881 * of the new element. So, callers need to guarantee it. 1882 * 1883 * @param aContentToBeWrapped The content which will be wrapped with new 1884 * element. 1885 * @param aWrapperTagName Element name of new element which will wrap 1886 * aContent and be inserted into where aContent 1887 * was. 1888 * @param aInitializer A callback to initialize new element before 1889 * inserting to the DOM tree. 1890 */ 1891 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 1892 InsertContainerWithTransaction( 1893 nsIContent& aContentToBeWrapped, const nsAtom& aWrapperTagName, 1894 const InitializeInsertingElement& aInitializer = DoNothingForNewElement); 1895 1896 /** 1897 * MoveNodeWithTransaction() moves aContentToMove to aPointToInsert. 1898 * 1899 * @param aContentToMove The node to be moved. 1900 * @param aPointToInsert The point where aContentToMove will be inserted. 1901 */ 1902 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<MoveNodeResult, nsresult> 1903 MoveNodeWithTransaction(nsIContent& aContentToMove, 1904 const EditorDOMPoint& aPointToInsert); 1905 1906 /** 1907 * Moves all siblings from aFirstContentToMove to aLastContentToMove to 1908 * aPointToInsert with a transaction. 1909 */ 1910 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<MoveNodeResult, nsresult> 1911 MoveSiblingsWithTransaction(nsIContent& aFirstContentToMove, 1912 nsIContent& aLastContentToMove, 1913 const EditorDOMPoint& aPointToInsert); 1914 1915 /** 1916 * MoveNodeToEndWithTransaction() moves aContentToMove to end of 1917 * aNewContainer. 1918 * 1919 * @param aContentToMove The node to be moved. 1920 * @param aNewContainer The new container which will contain aContentToMove 1921 * as its last child. 1922 */ 1923 [[nodiscard]] inline MOZ_CAN_RUN_SCRIPT Result<MoveNodeResult, nsresult> 1924 MoveNodeToEndWithTransaction(nsIContent& aContentToMove, 1925 nsINode& aNewContainer); 1926 1927 /** 1928 * Moves all siblings from aFirstContentToMove to aLastContentToMove to the 1929 * end of aNewContainer with a transaction. 1930 */ 1931 [[nodiscard]] inline MOZ_CAN_RUN_SCRIPT Result<MoveNodeResult, nsresult> 1932 MoveSiblingsToEndWithTransaction(nsIContent& aFirstContentToMove, 1933 nsIContent& aLastContentToMove, 1934 nsINode& aNewContainer); 1935 1936 /** 1937 * MoveNodeOrChildrenWithTransaction() moves aContent to aPointToInsert. If 1938 * cannot insert aContent due to invalid relation, moves only its children 1939 * recursively and removes aContent from the DOM tree. 1940 * 1941 * @param aContent Content which should be moved. 1942 * @param aPointToInsert The point to be inserted aContent or its 1943 * descendants. 1944 * @param aPreserveWhiteSpaceStyle 1945 * If yes and if it's possible to keep white-space 1946 * style, this method will set `style` attribute to 1947 * moving node or creating new <span> element. 1948 * @param aRemoveIfCommentNode 1949 * If yes, this removes a comment node instead of 1950 * moving it to the destination. Note that this 1951 * does not remove comment nodes in moving nodes 1952 * because it requires additional scan. 1953 */ 1954 enum class PreserveWhiteSpaceStyle { No, Yes }; 1955 friend std::ostream& operator<<( 1956 std::ostream& aStream, 1957 const PreserveWhiteSpaceStyle aPreserveWhiteSpaceStyle); 1958 enum class RemoveIfCommentNode { No, Yes }; 1959 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<MoveNodeResult, nsresult> 1960 MoveNodeOrChildrenWithTransaction( 1961 nsIContent& aContentToMove, const EditorDOMPoint& aPointToInsert, 1962 PreserveWhiteSpaceStyle aPreserveWhiteSpaceStyle, 1963 RemoveIfCommentNode aRemoveIfCommentNode); 1964 1965 /** 1966 * CanMoveNodeOrChildren() returns true if 1967 * `MoveNodeOrChildrenWithTransaction()` can move or delete at least a 1968 * descendant of aElement into aNewContainer. I.e., when this returns true, 1969 * `MoveNodeOrChildrenWithTransaction()` must return "handled". 1970 */ 1971 Result<bool, nsresult> CanMoveNodeOrChildren( 1972 const nsIContent& aContent, const nsINode& aNewContainer) const; 1973 1974 /** 1975 * MoveChildrenWithTransaction() moves the children of aElement to 1976 * aPointToInsert. If cannot insert some children due to invalid relation, 1977 * calls MoveNodeOrChildrenWithTransaction() to remove the children but keep 1978 * moving its children. 1979 * 1980 * @param aElement Container element whose children should be 1981 * moved. 1982 * @param aPointToInsert The point to be inserted children of aElement 1983 * or its descendants. 1984 * @param aPreserveWhiteSpaceStyle 1985 * If yes and if it's possible to keep white-space 1986 * style, this method will set `style` attribute to 1987 * moving node or creating new <span> element. 1988 * @param aRemoveIfCommentNode 1989 * If yes, this removes a comment node instead of 1990 * moving it to the destination. Note that this 1991 * does not remove comment nodes in moving nodes 1992 * because it requires additional scan. 1993 */ 1994 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<MoveNodeResult, nsresult> 1995 MoveChildrenWithTransaction(Element& aElement, 1996 const EditorDOMPoint& aPointToInsert, 1997 PreserveWhiteSpaceStyle aPreserveWhiteSpaceStyle, 1998 RemoveIfCommentNode aRemoveIfCommentNode); 1999 2000 /** 2001 * CanMoveChildren() returns true if `MoveChildrenWithTransaction()` can move 2002 * at least a descendant of aElement into aNewContainer. I.e., when this 2003 * returns true, `MoveChildrenWithTransaction()` return "handled". 2004 */ 2005 Result<bool, nsresult> CanMoveChildren(const Element& aElement, 2006 const nsINode& aNewContainer) const; 2007 2008 /** 2009 * MoveAllChildren() moves all children of aContainer to before 2010 * aPointToInsert.GetChild(). 2011 * See explanation of MoveChildrenBetween() for the detail of the behavior. 2012 * 2013 * @param aContainer The container node whose all children should 2014 * be moved. 2015 * @param aPointToInsert The insertion point. The container must not 2016 * be a data node like a text node. 2017 */ 2018 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2019 MoveAllChildren(nsINode& aContainer, const EditorRawDOMPoint& aPointToInsert); 2020 2021 /** 2022 * MoveChildrenBetween() moves all children between aFirstChild and aLastChild 2023 * to before aPointToInsert.GetChild(). If some children are moved to 2024 * different container while this method moves other children, they are just 2025 * ignored. If the child node referred by aPointToInsert is moved to different 2026 * container while this method moves children, returns error. 2027 * 2028 * @param aFirstChild The first child which should be moved to 2029 * aPointToInsert. 2030 * @param aLastChild The last child which should be moved. This 2031 * must be a sibling of aFirstChild and it should 2032 * be positioned after aFirstChild in the DOM tree 2033 * order. 2034 * @param aPointToInsert The insertion point. The container must not 2035 * be a data node like a text node. 2036 */ 2037 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2038 MoveChildrenBetween(nsIContent& aFirstChild, nsIContent& aLastChild, 2039 const EditorRawDOMPoint& aPointToInsert); 2040 2041 /** 2042 * MovePreviousSiblings() moves all siblings before aChild (i.e., aChild 2043 * won't be moved) to before aPointToInsert.GetChild(). 2044 * See explanation of MoveChildrenBetween() for the detail of the behavior. 2045 * 2046 * @param aChild The node which is next sibling of the last 2047 * node to be moved. 2048 * @param aPointToInsert The insertion point. The container must not 2049 * be a data node like a text node. 2050 */ 2051 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MovePreviousSiblings( 2052 nsIContent& aChild, const EditorRawDOMPoint& aPointToInsert); 2053 2054 /** 2055 * MoveInclusiveNextSiblings() moves aChild and all siblings after it to 2056 * before aPointToInsert.GetChild(). 2057 * See explanation of MoveChildrenBetween() for the detail of the behavior. 2058 * 2059 * @param aChild The node which is first node to be moved. 2060 * @param aPointToInsert The insertion point. The container must not 2061 * be a data node like a text node. 2062 */ 2063 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MoveInclusiveNextSiblings( 2064 nsIContent& aChild, const EditorRawDOMPoint& aPointToInsert); 2065 2066 /** 2067 * SplitNodeWithTransaction() creates a transaction to create a new node 2068 * (left node) identical to an existing node (right node), and split the 2069 * contents between the same point in both nodes, then, execute the 2070 * transaction. 2071 * 2072 * @param aStartOfRightNode The point to split. Its container will be 2073 * the right node, i.e., become the new node's 2074 * next sibling. And the point will be start 2075 * of the right node. 2076 */ 2077 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitNodeResult, nsresult> 2078 SplitNodeWithTransaction(const EditorDOMPoint& aStartOfRightNode); 2079 2080 /** 2081 * SplitNodeDeepWithTransaction() splits aMostAncestorToSplit deeply. 2082 * 2083 * @param aMostAncestorToSplit The most ancestor node which should be 2084 * split. 2085 * @param aStartOfDeepestRightNode The start point of deepest right node. 2086 * This point must be descendant of 2087 * aMostAncestorToSplit. 2088 * @param aSplitAtEdges Whether the caller allows this to 2089 * create empty container element when 2090 * split point is start or end of an 2091 * element. 2092 * @return SplitPoint() returns split point in 2093 * aMostAncestorToSplit. The point must 2094 * be good to insert something if the 2095 * caller want to do it. 2096 */ 2097 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitNodeResult, nsresult> 2098 SplitNodeDeepWithTransaction(nsIContent& aMostAncestorToSplit, 2099 const EditorDOMPoint& aDeepestStartOfRightNode, 2100 SplitAtEdges aSplitAtEdges); 2101 2102 /** 2103 * DeleteEmptyInclusiveAncestorInlineElements() removes empty inclusive 2104 * ancestor inline elements in inclusive ancestor block element of aContent. 2105 * 2106 * @param aContent Must be an empty content. 2107 * @param aEditingHost The editing host. 2108 */ 2109 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CaretPoint, nsresult> 2110 DeleteEmptyInclusiveAncestorInlineElements(nsIContent& aContent, 2111 const Element& aEditingHost); 2112 2113 /** 2114 * DeleteTextAndNormalizeSurroundingWhiteSpaces() deletes text between 2115 * aStartToDelete and immediately before aEndToDelete and return new caret 2116 * position. If surrounding characters are white-spaces, this normalize them 2117 * too. Finally, inserts `<br>` element if it's required. 2118 * Note that if you wants only normalizing white-spaces, you can set same 2119 * point to both aStartToDelete and aEndToDelete. Then, this tries to 2120 * normalize white-space sequence containing previous character of 2121 * aStartToDelete. 2122 */ 2123 enum class DeleteDirection { 2124 Forward, 2125 Backward, 2126 }; 2127 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CaretPoint, nsresult> 2128 DeleteTextAndNormalizeSurroundingWhiteSpaces( 2129 const EditorDOMPointInText& aStartToDelete, 2130 const EditorDOMPointInText& aEndToDelete, 2131 TreatEmptyTextNodes aTreatEmptyTextNodes, 2132 DeleteDirection aDeleteDirection, const Element& aEditingHost); 2133 2134 enum class NormalizeSurroundingWhiteSpaces : bool { No, Yes }; 2135 friend constexpr bool operator!(NormalizeSurroundingWhiteSpaces aValue) { 2136 return !static_cast<bool>(aValue); 2137 } 2138 2139 /** 2140 * Return a normalized string. If aPointToInsert is in a `Text` node, 2141 * this returns a range in the `Text` to replace surrounding white-spaces at 2142 * aPointToInsert with the normalized string if white-spaces are collapsible 2143 * and aNormalizeSurroundingWhiteSpaces is "Yes". 2144 */ 2145 NormalizedStringToInsertText NormalizeWhiteSpacesToInsertText( 2146 const EditorDOMPoint& aPointToInsert, const nsAString& aStringToInsert, 2147 NormalizeSurroundingWhiteSpaces aNormalizeSurroundingWhiteSpaces) const; 2148 2149 /** 2150 * Return normalized white-spaces of a white-space sequence which contains 2151 * aPoint. This returns new offset of aPoint.Offset() after replacing the 2152 * white-space sequence with normalized white-spaces. 2153 */ 2154 ReplaceWhiteSpacesData GetNormalizedStringAt( 2155 const EditorDOMPointInText& aPoint) const; 2156 2157 /** 2158 * Return normalized white-spaces after aPointToSplit if there are some 2159 * collapsible white-spaces after the point. 2160 */ 2161 ReplaceWhiteSpacesData GetFollowingNormalizedStringToSplitAt( 2162 const EditorDOMPointInText& aPointToSplit) const; 2163 2164 /** 2165 * Return normalized white-spaces before aPointToSplit if there are some 2166 * collapsible white-spaces before the point. 2167 */ 2168 ReplaceWhiteSpacesData GetPrecedingNormalizedStringToSplitAt( 2169 const EditorDOMPointInText& aPointToSplit) const; 2170 2171 /** 2172 * Return normalized surrounding white-spaces of the given range in aTextNode 2173 * if there are some collapsible white-spaces. 2174 */ 2175 ReplaceWhiteSpacesData GetSurroundingNormalizedStringToDelete( 2176 const Text& aTextNode, uint32_t aOffset, uint32_t aLength) const; 2177 2178 /** 2179 * ExtendRangeToDeleteWithNormalizingWhiteSpaces() is a helper method of 2180 * DeleteTextAndNormalizeSurroundingWhiteSpaces(). This expands 2181 * aStartToDelete and/or aEndToDelete if there are white-spaces which need 2182 * normalizing. 2183 * 2184 * @param aStartToDelete [In/Out] Start to delete. If this point 2185 * follows white-spaces, this may be modified. 2186 * @param aEndToDelete [In/Out] Next point of last content to be 2187 * deleted. If this point is a white-space, 2188 * this may be modified. 2189 * @param aNormalizedWhiteSpacesInStartNode 2190 * [Out] If container text node of aStartToDelete 2191 * should be modified, this offers new string 2192 * in the range in the text node. 2193 * @param aNormalizedWhiteSpacesInEndNode 2194 * [Out] If container text node of aEndToDelete 2195 * is different from the container of 2196 * aStartToDelete and it should be modified, this 2197 * offers new string in the range in the text node. 2198 */ 2199 void ExtendRangeToDeleteWithNormalizingWhiteSpaces( 2200 EditorDOMPointInText& aStartToDelete, EditorDOMPointInText& aEndToDelete, 2201 nsString& aNormalizedWhiteSpacesInStartNode, 2202 nsString& aNormalizedWhiteSpacesInEndNode) const; 2203 2204 /** 2205 * CharPointType let the following helper methods of 2206 * ExtendRangeToDeleteWithNormalizingWhiteSpaces() know what type of 2207 * character will be previous or next char point after deletion. 2208 */ 2209 enum class CharPointType { 2210 TextEnd, // Start or end of the text (hardline break or replaced inline 2211 // element) 2212 ASCIIWhiteSpace, // One of ASCII white-spaces (collapsible white-space) 2213 NoBreakingSpace, // NBSP 2214 VisibleChar, // Non-white-space characters 2215 PreformattedChar, // Any character except a linefeed in a preformatted 2216 // node. 2217 PreformattedLineBreak, // Preformatted linebreak 2218 }; 2219 2220 /** 2221 * GetPreviousCharPointType() and GetCharPointType() get type of 2222 * previous/current char point from current DOM tree. In other words, if the 2223 * point will be deleted, you cannot use these methods. 2224 */ 2225 template <typename EditorDOMPointType> 2226 static CharPointType GetPreviousCharPointType( 2227 const EditorDOMPointType& aPoint); 2228 template <typename EditorDOMPointType> 2229 static CharPointType GetCharPointType(const EditorDOMPointType& aPoint); 2230 2231 /** 2232 * CharPointData let the following helper methods of 2233 * ExtendRangeToDeleteWithNormalizingWhiteSpaces() know what type of 2234 * character will be previous or next char point and the point is 2235 * in same or different text node after deletion. 2236 */ 2237 class MOZ_STACK_CLASS CharPointData final { 2238 public: 2239 CharPointData() = delete; 2240 2241 static CharPointData InDifferentTextNode(CharPointType aCharPointType) { 2242 return {aCharPointType, true}; 2243 } 2244 static CharPointData InSameTextNode(CharPointType aCharPointType) { 2245 // Let's mark this as in different text node if given one indicates 2246 // that there is end of text because it means that adjacent content 2247 // from point of text node view is another element. 2248 return {aCharPointType, aCharPointType == CharPointType::TextEnd}; 2249 } 2250 2251 bool AcrossTextNodeBoundary() const { return mIsInDifferentTextNode; } 2252 bool IsCollapsibleWhiteSpace() const { 2253 return mType == CharPointType::ASCIIWhiteSpace || 2254 mType == CharPointType::NoBreakingSpace; 2255 } 2256 CharPointType Type() const { return mType; } 2257 2258 private: 2259 CharPointData(CharPointType aType, bool aIsInDifferentTextNode) 2260 : mType(aType), mIsInDifferentTextNode(aIsInDifferentTextNode) {} 2261 2262 CharPointType mType; 2263 bool mIsInDifferentTextNode; 2264 }; 2265 2266 /** 2267 * GetPreviousCharPointDataForNormalizingWhiteSpaces() and 2268 * GetInclusiveNextCharPointDataForNormalizingWhiteSpaces() is helper methods 2269 * of ExtendRangeToDeleteWithNormalizingWhiteSpaces(). This retrieves 2270 * previous or inclusive next editable char point and returns its data. 2271 */ 2272 CharPointData GetPreviousCharPointDataForNormalizingWhiteSpaces( 2273 const EditorDOMPointInText& aPoint) const; 2274 CharPointData GetInclusiveNextCharPointDataForNormalizingWhiteSpaces( 2275 const EditorDOMPointInText& aPoint) const; 2276 2277 enum class Linefeed : bool { Collapsible, Preformatted }; 2278 2279 /** 2280 * Normalize all white-spaces in aResult. aPreviousCharPointData is used only 2281 * when the first character of aResult is an ASCII space or an NBSP. 2282 * aNextCharPointData is used only when the last character of aResult is an 2283 * ASCII space or an NBSP. 2284 */ 2285 static void NormalizeAllWhiteSpaceSequences( 2286 nsString& aResult, const CharPointData& aPreviousCharPointData, 2287 const CharPointData& aNextCharPointData, Linefeed aLinefeed); 2288 2289 /** 2290 * GenerateWhiteSpaceSequence() generates white-space sequence which won't 2291 * be collapsed. 2292 * 2293 * @param aResult [out] White space sequence which won't be 2294 * collapsed, but wrappable. 2295 * @param aLength Length of generating white-space sequence. 2296 * Must be 1 or larger. 2297 * @param aPreviousCharPointData 2298 * Specify the previous char point where it'll be 2299 * inserted. Currently, for keepin this method 2300 * simple, does not support to generate a part 2301 * of white-space sequence in a text node. So, 2302 * if the type is white-space, it must indicate 2303 * different text nodes white-space. 2304 * @param aNextCharPointData Specify the next char point where it'll be 2305 * inserted. Same as aPreviousCharPointData, 2306 * this must node indicate white-space in same 2307 * text node. 2308 */ 2309 static void GenerateWhiteSpaceSequence( 2310 nsString& aResult, uint32_t aLength, 2311 const CharPointData& aPreviousCharPointData, 2312 const CharPointData& aNextCharPointData); 2313 2314 /** 2315 * Replace characters starting from aOffset in aResult with normalized 2316 * white-space sequence. 2317 */ 2318 static void ReplaceStringWithNormalizedWhiteSpaceSequence( 2319 nsString& aResult, uint32_t aOffset, uint32_t aLength, 2320 const CharPointData& aPreviousCharPointData, 2321 const CharPointData& aNextCharPointData); 2322 2323 /** 2324 * ComputeTargetRanges() computes actual delete ranges which will be deleted 2325 * unless the following `beforeinput` event is canceled. 2326 * 2327 * @param aDirectionAndAmount The direction and amount of deletion. 2328 * @param aRangesToDelete [In/Out] The ranges to be deleted, 2329 * typically, initialized with the 2330 * selection ranges. This may be modified 2331 * if selection ranges should be extened. 2332 */ 2333 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2334 ComputeTargetRanges(nsIEditor::EDirection aDirectionAndAmount, 2335 AutoClonedSelectionRangeArray& aRangesToDelete) const; 2336 2337 /** 2338 * This method handles "delete selection" commands. 2339 * 2340 * @param aDirectionAndAmount Direction of the deletion. 2341 * @param aStripWrappers Must be eStrip or eNoStrip. 2342 */ 2343 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 2344 HandleDeleteSelection(nsIEditor::EDirection aDirectionAndAmount, 2345 nsIEditor::EStripWrappers aStripWrappers) final; 2346 2347 class AutoDeleteRangesHandler; 2348 class AutoMoveOneLineHandler; 2349 2350 /** 2351 * DeleteMostAncestorMailCiteElementIfEmpty() deletes most ancestor 2352 * mail cite element (`<blockquote type="cite">` or 2353 * `<span _moz_quote="true">`, the former can be created with middle click 2354 * paste with `Control` or `Command` even in the web) of aContent if it 2355 * becomes empty. 2356 */ 2357 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2358 DeleteMostAncestorMailCiteElementIfEmpty(nsIContent& aContent); 2359 2360 /** 2361 * LiftUpListItemElement() moves aListItemElement outside its parent. 2362 * If it's in a middle of a list element, the parent list element is split 2363 * before aListItemElement. Then, moves aListItemElement to before its 2364 * parent list element. I.e., moves aListItemElement between the 2 list 2365 * elements if original parent was split. Then, if new parent becomes not a 2366 * list element, the list item element is removed and its contents are moved 2367 * to where the list item element was. If aListItemElement becomse not a 2368 * child of list element, its contents are unwrapped from aListItemElement. 2369 * 2370 * @param aListItemElement Must be a <li>, <dt> or <dd> element. 2371 * @param aLiftUpFromAllParentListElements 2372 * If Yes, this method calls itself recursively 2373 * to unwrap the contents in aListItemElement 2374 * from any ancestor list elements. 2375 * XXX This checks only direct parent of list 2376 * elements. Therefore, if a parent list 2377 * element in a list item element, the 2378 * list item element and its list element 2379 * won't be unwrapped. 2380 */ 2381 enum class LiftUpFromAllParentListElements { Yes, No }; 2382 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult LiftUpListItemElement( 2383 dom::Element& aListItemElement, 2384 LiftUpFromAllParentListElements aLiftUpFromAllParentListElements); 2385 2386 /** 2387 * DestroyListStructureRecursively() destroys the list structure of 2388 * aListElement recursively. 2389 * If aListElement has <li>, <dl> or <dt> as a child, the element is removed 2390 * but its descendants are moved to where the list item element was. 2391 * If aListElement has another <ul>, <ol> or <dl> as a child, this method is 2392 * called recursively. 2393 * If aListElement has other nodes as its child, they are just removed. 2394 * Finally, aListElement is removed. and its all children are moved to 2395 * where the aListElement was. 2396 * 2397 * @param aListElement A <ul>, <ol> or <dl> element. 2398 */ 2399 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2400 DestroyListStructureRecursively(Element& aListElement); 2401 2402 /** 2403 * RemoveListAtSelectionAsSubAction() removes list elements and list item 2404 * elements at Selection. And move contents in them where the removed list 2405 * was. 2406 * 2407 * @param aEditingHost The editing host. 2408 */ 2409 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2410 RemoveListAtSelectionAsSubAction(const Element& aEditingHost); 2411 2412 /** 2413 * ChangeMarginStart() changes margin of aElement to indent or outdent. 2414 * If it's rtl text, margin-right will be changed. Otherwise, margin-left. 2415 * XXX This is not aware of vertical writing-mode. 2416 * 2417 * @param aElement The element whose start margin should be 2418 * changed. 2419 * @param aChangeMargin Whether increase or decrease the margin. 2420 * @param aEditingHost The editing host. 2421 * @return May suggest a suggest point to put caret. 2422 */ 2423 enum class ChangeMargin { Increase, Decrease }; 2424 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 2425 ChangeMarginStart(Element& aElement, ChangeMargin aChangeMargin, 2426 const Element& aEditingHost); 2427 2428 /** 2429 * HandleCSSIndentAroundRanges() indents around aRanges with CSS. 2430 * 2431 * @param aRanges The ranges where the content should be indented. 2432 * @param aEditingHost The editing host. 2433 */ 2434 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult HandleCSSIndentAroundRanges( 2435 AutoClonedSelectionRangeArray& aRanges, const Element& aEditingHost); 2436 2437 /** 2438 * HandleCSSIndentAroundRanges() indents around aRanges with HTML. 2439 * 2440 * @param aRanges The ranges where the content should be indented. 2441 * @param aEditingHost The editing host. 2442 */ 2443 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult HandleHTMLIndentAroundRanges( 2444 AutoClonedSelectionRangeArray& aRanges, const Element& aEditingHost); 2445 2446 /** 2447 * HandleIndentAtSelection() indents around Selection with HTML or CSS. 2448 * 2449 * @param aEditingHost The editing host. 2450 */ 2451 // TODO: Make this take AutoClonedSelectionRangeArray instead of retrieving 2452 // `Selection` 2453 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 2454 HandleIndentAtSelection(const Element& aEditingHost); 2455 2456 /** 2457 * OutdentPartOfBlock() outdents the nodes between aStartOfOutdent and 2458 * aEndOfOutdent. This splits the range off from aBlockElement first. 2459 * Then, removes the middle element if aIsBlockIndentedWithCSS is false. 2460 * Otherwise, decreases the margin of the middle element. 2461 * 2462 * @param aBlockElement A block element which includes both 2463 * aStartOfOutdent and aEndOfOutdent. 2464 * @param aStartOfOutdent First node which is descendant of 2465 * aBlockElement will be outdented. 2466 * @param aEndOfOutdent Last node which is descandant of 2467 * aBlockElement will be outdented. 2468 * @param aBlockIndentedWith `CSS` if aBlockElement is indented with 2469 * CSS margin property. 2470 * `HTML` if aBlockElement is `<blockquote>` 2471 * or something. 2472 * @param aEditingHost The editing host. 2473 * @return The left content is new created element 2474 * splitting before aStartOfOutdent. 2475 * The right content is existing element. 2476 * The middle content is outdented element 2477 * if aBlockIndentedWith is `CSS`. 2478 * Otherwise, nullptr. 2479 */ 2480 enum class BlockIndentedWith { CSS, HTML }; 2481 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitRangeOffFromNodeResult, nsresult> 2482 OutdentPartOfBlock(Element& aBlockElement, nsIContent& aStartOfOutdent, 2483 nsIContent& aEndOfOutdent, 2484 BlockIndentedWith aBlockIndentedWith, 2485 const Element& aEditingHost); 2486 2487 /** 2488 * HandleOutdentAtSelectionInternal() outdents contents around Selection. 2489 * This method creates AutoSelectionRestorer. Therefore, each caller 2490 * needs to check if the editor is still available even if this returns 2491 * NS_OK. 2492 * NOTE: Call `HandleOutdentAtSelection()` instead. 2493 * 2494 * @param aEditingHost The editing host. 2495 * @return The left content is left content of last 2496 * outdented element. 2497 * The right content is right content of last 2498 * outdented element. 2499 * The middle content is middle content of last 2500 * outdented element. 2501 */ 2502 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitRangeOffFromNodeResult, nsresult> 2503 HandleOutdentAtSelectionInternal(const Element& aEditingHost); 2504 2505 /** 2506 * HandleOutdentAtSelection() outdents contents around Selection. 2507 * 2508 * @param aEditingHost The editing host. 2509 */ 2510 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 2511 HandleOutdentAtSelection(const Element& aEditingHost); 2512 2513 /** 2514 * AlignBlockContentsWithDivElement() sets align attribute of <div> element 2515 * which is only child of aBlockElement to aAlignType. If aBlockElement 2516 * has 2 or more children or does not have a `<div>` element, inserts a 2517 * new `<div>` element into aBlockElement and move all children of 2518 * aBlockElement into the new `<div>` element. 2519 * 2520 * @param aBlockElement The element node whose contents should be 2521 * aligned to aAlignType. This should be 2522 * an element which can have `<div>` element 2523 * as its child. 2524 * @param aAlignType New value of align attribute of `<div>` 2525 * element. 2526 * @return A candidate position to put caret. 2527 */ 2528 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 2529 AlignBlockContentsWithDivElement(Element& aBlockElement, 2530 const nsAString& aAlignType); 2531 2532 /** 2533 * AlignContentsInAllTableCellsAndListItems() calls 2534 * AlignBlockContentsWithDivElement() for aligning contents in every list 2535 * item element and table cell element in aElement. 2536 * 2537 * @param aElement The node which is or whose descendants should 2538 * be aligned to aAlignType. 2539 * @param aAlignType New value of `align` attribute of `<div>`. 2540 * @return A candidate position to put caret. 2541 */ 2542 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 2543 AlignContentsInAllTableCellsAndListItems(dom::Element& aElement, 2544 const nsAString& aAlignType); 2545 2546 /** 2547 * MakeTransitionList() detects all the transitions in the array, where a 2548 * transition means that adjacent nodes in the array don't have the same 2549 * parent. 2550 */ 2551 static void MakeTransitionList( 2552 const nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents, 2553 nsTArray<bool>& aTransitionArray); 2554 2555 /** 2556 * EnsureHardLineBeginsWithFirstChildOf() inserts `<br>` element before 2557 * first child of aRemovingContainerElement if it will not be start of a 2558 * hard line after removing aRemovingContainerElement. 2559 */ 2560 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 2561 EnsureHardLineBeginsWithFirstChildOf(Element& aRemovingContainerElement); 2562 2563 /** 2564 * EnsureHardLineEndsWithLastChildOf() inserts `<br>` element after last 2565 * child of aRemovingContainerElement if it will not be end of a hard line 2566 * after removing aRemovingContainerElement. 2567 */ 2568 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 2569 EnsureHardLineEndsWithLastChildOf(Element& aRemovingContainerElement); 2570 2571 /** 2572 * RemoveAlignFromDescendants() removes align attributes, text-align 2573 * properties and <center> elements in aElement. 2574 * 2575 * @param aElement Alignment information of the node and/or its 2576 * descendants will be removed. 2577 * NOTE: aElement must not be a `<table>` element. 2578 * @param aAlignType New align value to be set only when it's in 2579 * CSS mode and this method meets <table> or <hr>. 2580 * XXX This is odd and not clear when you see caller of 2581 * this method. Do you have better idea? 2582 * @param aEditTarget If `OnlyDescendantsExceptTable`, modifies only 2583 * descendants of aElement. 2584 * If `NodeAndDescendantsExceptTable`, modifies `aElement` 2585 * and its descendants. 2586 * @return A candidate point to put caret. 2587 */ 2588 enum class EditTarget { 2589 OnlyDescendantsExceptTable, 2590 NodeAndDescendantsExceptTable 2591 }; 2592 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 2593 RemoveAlignFromDescendants(Element& aElement, const nsAString& aAlignType, 2594 EditTarget aEditTarget); 2595 2596 /** 2597 * SetBlockElementAlign() resets `align` attribute, `text-align` property 2598 * of descendants of aBlockOrHRElement except `<table>` element descendants. 2599 * Then, set `align` attribute or `text-align` property of aBlockOrHRElement. 2600 * 2601 * @param aBlockOrHRElement The element whose contents will be aligned. 2602 * This must be a block element or `<hr>` element. 2603 * If we're not in CSS mode, this element has 2604 * to support `align` attribute (i.e., 2605 * `HTMLEditUtils::SupportsAlignAttr()` must 2606 * return true). 2607 * @param aAlignType Boundary or "center" which contents should be 2608 * aligned on. 2609 * @param aEditTarget If `OnlyDescendantsExceptTable`, modifies only 2610 * descendants of aBlockOrHRElement. 2611 * If `NodeAndDescendantsExceptTable`, modifies 2612 * aBlockOrHRElement and its descendants. 2613 * @return A candidate point to put caret. 2614 */ 2615 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 2616 SetBlockElementAlign(Element& aBlockOrHRElement, const nsAString& aAlignType, 2617 EditTarget aEditTarget); 2618 2619 /** 2620 * InsertDivElementToAlignContents() inserts a new <div> element (which has 2621 * only a padding <br> element) to aPointToInsert for a placeholder whose 2622 * contents will be aligned. 2623 * 2624 * @param aPointToInsert A point to insert new empty <div>. 2625 * @param aAlignType New align attribute value where the contents 2626 * should be aligned to. 2627 * @param aEditingHost The editing host. 2628 * @return New <div> element which has only a padding <br> 2629 * element and is styled to align contents. 2630 */ 2631 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 2632 InsertDivElementToAlignContents(const EditorDOMPoint& aPointToInsert, 2633 const nsAString& aAlignType, 2634 const Element& aEditingHost); 2635 2636 /** 2637 * AlignNodesAndDescendants() make contents of nodes in aArrayOfContents and 2638 * their descendants aligned to aAlignType. 2639 * 2640 * @param aAlignType New align attribute value where the contents 2641 * should be aligned to. 2642 * @param aEditingHost The editing host. 2643 * @return Last created <div> element which should contain 2644 * caret and candidate position which may be 2645 * outside the <div> element. 2646 */ 2647 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 2648 AlignNodesAndDescendants( 2649 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents, 2650 const nsAString& aAlignType, const Element& aEditingHost); 2651 2652 /** 2653 * AlignContentsAtRanges() aligns contents around aRanges to aAlignType. 2654 * 2655 * @param aRanges The ranges where should be aligned. 2656 * @param aAlignType New align attribute value where the contents 2657 * should be aligned to. 2658 * @param aEditingHost The editing host. 2659 */ 2660 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult AlignContentsAtRanges( 2661 AutoClonedSelectionRangeArray& aRanges, const nsAString& aAlignType, 2662 const Element& aEditingHost); 2663 2664 /** 2665 * AlignAsSubAction() handles "align" command with `Selection`. 2666 * 2667 * @param aAlignType New align attribute value where the contents 2668 * should be aligned to. 2669 * @param aEditingHost The editing host. 2670 */ 2671 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 2672 AlignAsSubAction(const nsAString& aAlignType, const Element& aEditingHost); 2673 2674 /** 2675 * AdjustCaretPositionAndEnsurePaddingBRElement() may adjust caret 2676 * position to nearest editable content and if padding `<br>` element is 2677 * necessary at caret position, this creates it. 2678 * 2679 * @param aDirectionAndAmount Direction of the edit action. 2680 */ 2681 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2682 AdjustCaretPositionAndEnsurePaddingBRElement( 2683 nsIEditor::EDirection aDirectionAndAmount); 2684 2685 /** 2686 * EnsureSelectionInBodyOrDocumentElement() collapse `Selection` to the 2687 * primary `<body>` element or document element when `Selection` crosses 2688 * `<body>` element's boundary. 2689 */ 2690 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2691 EnsureSelectionInBodyOrDocumentElement(); 2692 2693 /** 2694 * InsertBRElementToEmptyListItemsAndTableCellsInRange() inserts 2695 * `<br>` element into empty list item or table cell elements between 2696 * aStartRef and aEndRef. 2697 */ 2698 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2699 InsertBRElementToEmptyListItemsAndTableCellsInRange( 2700 const RawRangeBoundary& aStartRef, const RawRangeBoundary& aEndRef); 2701 2702 /** 2703 * RemoveEmptyNodesIn() removes all empty nodes in aRange. However, if 2704 * mail-cite node has only a `<br>` element, the node will be removed 2705 * but <br> element is moved to where the mail-cite node was. 2706 * XXX This method is expensive if aRange is too wide and may remove 2707 * unexpected empty element, e.g., it was created by JS, but we haven't 2708 * touched it. Cannot we remove this method and make guarantee that 2709 * empty nodes won't be created? 2710 * 2711 * @param aRange Must be positioned. 2712 */ 2713 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2714 RemoveEmptyNodesIn(const EditorDOMRange& aRange); 2715 2716 /** 2717 * SetSelectionInterlinePosition() may set interline position if caret is 2718 * positioned around `<br>` or block boundary. Don't call this when 2719 * `Selection` is not collapsed. 2720 */ 2721 void SetSelectionInterlinePosition(); 2722 2723 /** 2724 * Called by `HTMLEditor::OnEndHandlingTopLevelEditSubAction()`. This may 2725 * adjust Selection, remove unnecessary empty nodes, create `<br>` elements 2726 * if needed, etc. 2727 */ 2728 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2729 OnEndHandlingTopLevelEditSubActionInternal(); 2730 2731 /** 2732 * MoveSelectedContentsToDivElementToMakeItAbsolutePosition() looks for 2733 * a `<div>` element in selection first. If not, creates new `<div>` 2734 * element. Then, move all selected contents into the target `<div>` 2735 * element. 2736 * Note that this creates AutoSelectionRestorer. Therefore, callers need 2737 * to check whether we have been destroyed even when this returns NS_OK. 2738 * 2739 * @param aTargetElement Returns target `<div>` element which should be 2740 * changed to absolute positioned. 2741 * @param aEditingHost The editing host. 2742 */ 2743 // TODO: Rewrite this with `Result<RefPtr<Element>, nsresult>`. 2744 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2745 MoveSelectedContentsToDivElementToMakeItAbsolutePosition( 2746 RefPtr<Element>* aTargetElement, const Element& aEditingHost); 2747 2748 /** 2749 * SetSelectionToAbsoluteAsSubAction() move selected contents to first 2750 * selected `<div>` element or newly created `<div>` element and make 2751 * the `<div>` element positioned absolutely. 2752 * 2753 * @param aEditingHost The editing host. 2754 */ 2755 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 2756 SetSelectionToAbsoluteAsSubAction(const Element& aEditingHost); 2757 2758 /** 2759 * SetSelectionToStaticAsSubAction() sets the `position` property of a 2760 * selection parent's block whose `position` is `absolute` to `static`. 2761 */ 2762 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 2763 SetSelectionToStaticAsSubAction(); 2764 2765 /** 2766 * AddZIndexAsSubAction() adds aChange to `z-index` of nearest parent 2767 * absolute-positioned element from current selection. 2768 * 2769 * @param aChange Amount to change `z-index`. 2770 */ 2771 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 2772 AddZIndexAsSubAction(int32_t aChange); 2773 2774 /** 2775 * RunOrScheduleOnModifyDocument() is called when editor content is changed. 2776 */ 2777 MOZ_CAN_RUN_SCRIPT nsresult RunOrScheduleOnModifyDocument( 2778 const nsIContent* aContentWillBeRemoved = nullptr); 2779 2780 protected: // Called by helper classes. 2781 MOZ_CAN_RUN_SCRIPT void OnStartToHandleTopLevelEditSubAction( 2782 EditSubAction aTopLevelEditSubAction, 2783 nsIEditor::EDirection aDirectionOfTopLevelEditSubAction, 2784 ErrorResult& aRv) final; 2785 MOZ_CAN_RUN_SCRIPT nsresult OnEndHandlingTopLevelEditSubAction() final; 2786 2787 protected: // Shouldn't be used by friend classes 2788 virtual ~HTMLEditor(); 2789 2790 enum class DOMMutationType { 2791 ContentAppended, 2792 ContentInserted, 2793 ContentWillBeRemoved, 2794 CharacterDataChanged, 2795 }; 2796 [[nodiscard]] LogLevel MutationLogLevelOf( 2797 nsIContent* aContent, 2798 const CharacterDataChangeInfo* aCharacterDataChangeInfo, 2799 DOMMutationType aDOMMutationType) const; 2800 [[nodiscard]] LogLevel AttrMutationLogLevelOf( 2801 Element* aElement, int32_t aNameSpaceID, nsAtom* aAttribute, 2802 AttrModType aModType, const nsAttrValue* aOldValue) const; 2803 2804 void MaybeLogContentAppended(nsIContent*) const; 2805 void MaybeLogContentInserted(nsIContent*) const; 2806 void MaybeLogContentWillBeRemoved(nsIContent*) const; 2807 void MaybeLogCharacterDataChanged(nsIContent*, 2808 const CharacterDataChangeInfo&) const; 2809 void MaybeLogAttributeChanged(Element*, int32_t, nsAtom*, AttrModType, 2810 const nsAttrValue*) const; 2811 2812 /** 2813 * InitEditorContentAndSelection() may insert `<br>` elements and padding 2814 * `<br>` elements if they are required for `<body>` or document element. 2815 * And collapse selection at the end if there is no selection ranges. 2816 */ 2817 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InitEditorContentAndSelection(); 2818 2819 /** 2820 * Collapse `Selection` to the last leaf content of the <body> or the document 2821 * element. 2822 */ 2823 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2824 CollapseSelectionToEndOfLastLeafNodeOfDocument() const; 2825 2826 MOZ_CAN_RUN_SCRIPT nsresult SelectAllInternal() final; 2827 2828 [[nodiscard]] Element* ComputeEditingHostInternal( 2829 const nsIContent* aContent, LimitInBodyElement aLimitInBodyElement) const; 2830 2831 /** 2832 * Creates a range with just the supplied node and appends that to the 2833 * selection. 2834 */ 2835 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 2836 AppendContentToSelectionAsRange(nsIContent& aContent); 2837 2838 /** 2839 * When you are using AppendContentToSelectionAsRange(), call this first to 2840 * start a new selection. 2841 */ 2842 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult ClearSelection(); 2843 2844 /** 2845 * SelectContentInternal() sets Selection to aContentToSelect to 2846 * aContentToSelect + 1 in parent of aContentToSelect. 2847 * 2848 * @param aContentToSelect The content which should be selected. 2849 */ 2850 MOZ_CAN_RUN_SCRIPT nsresult 2851 SelectContentInternal(nsIContent& aContentToSelect); 2852 2853 /** 2854 * GetInclusiveAncestorByTagNameAtSelection() looks for an element node whose 2855 * name matches aTagName from anchor node of Selection to <body> element. 2856 * 2857 * @param aTagName The tag name which you want to look for. 2858 * Must not be nsGkAtoms::_empty. 2859 * If nsGkAtoms::list, the result may be <ul>, <ol> or 2860 * <dl> element. 2861 * If nsGkAtoms::td, the result may be <td> or <th>. 2862 * If nsGkAtoms::href, the result may be <a> element 2863 * which has "href" attribute with non-empty value. 2864 * If nsGkAtoms::anchor, the result may be <a> which 2865 * has "name" attribute with non-empty value. 2866 * @return If an element which matches aTagName, returns 2867 * an Element. Otherwise, nullptr. 2868 */ 2869 Element* GetInclusiveAncestorByTagNameAtSelection( 2870 const nsStaticAtom& aTagName) const; 2871 2872 /** 2873 * GetInclusiveAncestorByTagNameInternal() looks for an element node whose 2874 * name matches aTagName from aNode to <body> element. 2875 * 2876 * @param aTagName The tag name which you want to look for. 2877 * Must not be nsGkAtoms::_empty. 2878 * If nsGkAtoms::list, the result may be <ul>, <ol> or 2879 * <dl> element. 2880 * If nsGkAtoms::td, the result may be <td> or <th>. 2881 * If nsGkAtoms::href, the result may be <a> element 2882 * which has "href" attribute with non-empty value. 2883 * If nsGkAtoms::anchor, the result may be <a> which 2884 * has "name" attribute with non-empty value. 2885 * @param aContent Start node to look for the element. This should 2886 * not be an orphan node. 2887 * @return If an element which matches aTagName, returns 2888 * an Element. Otherwise, nullptr. 2889 */ 2890 Element* GetInclusiveAncestorByTagNameInternal( 2891 const nsStaticAtom& aTagName, const nsIContent& aContent) const; 2892 2893 /** 2894 * GetSelectedElement() returns a "selected" element node. "selected" means: 2895 * - there is only one selection range 2896 * - the range starts from an element node or in an element 2897 * - the range ends at immediately after same element 2898 * - and the range does not include any other element nodes. 2899 * Additionally, only when aTagName is nsGkAtoms::href, this thinks that an 2900 * <a> element which has non-empty "href" attribute includes the range, the 2901 * <a> element is selected. 2902 * 2903 * NOTE: This method is implementation of nsIHTMLEditor.getSelectedElement() 2904 * and comm-central depends on this behavior. Therefore, if you need to use 2905 * this method internally but you need to change, perhaps, you should create 2906 * another method for avoiding breakage of comm-central apps. 2907 * 2908 * @param aTagName The atom of tag name in lower case. Set this to 2909 * result of EditorUtils::GetTagNameAtom() if you have a 2910 * tag name with nsString. 2911 * If nullptr, this returns any element node or nullptr. 2912 * If nsGkAtoms::href, this returns an <a> element which 2913 * has non-empty "href" attribute or nullptr. 2914 * If nsGkAtoms::anchor, this returns an <a> element which 2915 * has non-empty "name" attribute or nullptr. 2916 * Otherwise, returns an element node whose name is 2917 * same as aTagName or nullptr. 2918 * @param aRv Returns error code. 2919 * @return A "selected" element. 2920 */ 2921 already_AddRefed<Element> GetSelectedElement(const nsAtom* aTagName, 2922 ErrorResult& aRv); 2923 2924 /** 2925 * GetFirstTableRowElement() returns the first <tr> element in the most 2926 * nearest ancestor of aTableOrElementInTable or itself. 2927 * When aTableOrElementInTable is neither <table> nor in a <table> element, 2928 * returns NS_ERROR_FAILURE. However, if <table> does not have <tr> element, 2929 * returns nullptr. 2930 * 2931 * @param aTableOrElementInTable <table> element or another element. 2932 * If this is a <table> element, returns 2933 * first <tr> element in it. Otherwise, 2934 * returns first <tr> element in nearest 2935 * ancestor <table> element. 2936 */ 2937 Result<RefPtr<Element>, nsresult> GetFirstTableRowElement( 2938 const Element& aTableOrElementInTable) const; 2939 2940 /** 2941 * GetNextTableRowElement() returns next <tr> element of aTableRowElement. 2942 * This won't cross <table> element boundary but may cross table section 2943 * elements like <tbody>. 2944 * Note that if given element is <tr> but there is no next <tr> element, this 2945 * returns nullptr but does not return error. 2946 * 2947 * @param aTableRowElement A <tr> element. 2948 */ 2949 Result<RefPtr<Element>, nsresult> GetNextTableRowElement( 2950 const Element& aTableRowElement) const; 2951 2952 struct CellData; 2953 2954 /** 2955 * CellIndexes store both row index and column index of a table cell. 2956 */ 2957 struct MOZ_STACK_CLASS CellIndexes final { 2958 int32_t mRow; 2959 int32_t mColumn; 2960 2961 /** 2962 * This constructor initializes mRowIndex and mColumnIndex with indexes of 2963 * aCellElement. 2964 * 2965 * @param aCellElement An <td> or <th> element. 2966 */ 2967 MOZ_CAN_RUN_SCRIPT CellIndexes(Element& aCellElement, PresShell* aPresShell) 2968 : mRow(-1), mColumn(-1) { 2969 Update(aCellElement, aPresShell); 2970 } 2971 2972 /** 2973 * Update mRowIndex and mColumnIndex with indexes of aCellElement. 2974 * 2975 * @param See above. 2976 */ 2977 MOZ_CAN_RUN_SCRIPT void Update(Element& aCellElement, 2978 PresShell* aPresShell); 2979 2980 /** 2981 * This constructor initializes mRowIndex and mColumnIndex with indexes of 2982 * cell element which contains anchor of Selection. 2983 * 2984 * @param aHTMLEditor The editor which creates the instance. 2985 * @param aSelection The Selection for the editor. 2986 */ 2987 MOZ_CAN_RUN_SCRIPT CellIndexes(HTMLEditor& aHTMLEditor, 2988 Selection& aSelection) 2989 : mRow(-1), mColumn(-1) { 2990 Update(aHTMLEditor, aSelection); 2991 } 2992 2993 /** 2994 * Update mRowIndex and mColumnIndex with indexes of cell element which 2995 * contains anchor of Selection. 2996 * 2997 * @param See above. 2998 */ 2999 MOZ_CAN_RUN_SCRIPT void Update(HTMLEditor& aHTMLEditor, 3000 Selection& aSelection); 3001 3002 bool operator==(const CellIndexes& aOther) const { 3003 return mRow == aOther.mRow && mColumn == aOther.mColumn; 3004 } 3005 bool operator!=(const CellIndexes& aOther) const { 3006 return mRow != aOther.mRow || mColumn != aOther.mColumn; 3007 } 3008 3009 [[nodiscard]] bool isErr() const { return mRow < 0 || mColumn < 0; } 3010 3011 private: 3012 CellIndexes() : mRow(-1), mColumn(-1) {} 3013 CellIndexes(int32_t aRowIndex, int32_t aColumnIndex) 3014 : mRow(aRowIndex), mColumn(aColumnIndex) {} 3015 3016 friend struct CellData; 3017 }; 3018 3019 struct MOZ_STACK_CLASS CellData final { 3020 MOZ_KNOWN_LIVE RefPtr<Element> mElement; 3021 // Current indexes which this is initialized with. 3022 CellIndexes mCurrent; 3023 // First column/row indexes of the cell. When current position is spanned 3024 // from other column/row, this value becomes different from mCurrent. 3025 CellIndexes mFirst; 3026 // Computed rowspan/colspan values which are specified to the cell. 3027 // Note that if the cell has larger rowspan/colspan value than actual 3028 // table size, these values are the larger values. 3029 int32_t mRowSpan = -1; 3030 int32_t mColSpan = -1; 3031 // Effective rowspan/colspan value at the index. For example, if first 3032 // cell element in first row has rowspan="3", then, if this is initialized 3033 // with 0-0 indexes, effective rowspan is 3. However, if this is 3034 // initialized with 1-0 indexes, effective rowspan is 2. 3035 int32_t mEffectiveRowSpan = -1; 3036 int32_t mEffectiveColSpan = -1; 3037 // mIsSelected is set to true if mElement itself or its parent <tr> or 3038 // <table> is selected. Otherwise, e.g., the cell just contains selection 3039 // range, this is set to false. 3040 bool mIsSelected = false; 3041 3042 CellData() = delete; 3043 3044 /** 3045 * This returns an instance which is initialized with a <table> element and 3046 * both row and column index to specify a cell element. 3047 */ 3048 [[nodiscard]] static CellData AtIndexInTableElement( 3049 const HTMLEditor& aHTMLEditor, const Element& aTableElement, 3050 int32_t aRowIndex, int32_t aColumnIndex); 3051 [[nodiscard]] static CellData AtIndexInTableElement( 3052 const HTMLEditor& aHTMLEditor, const Element& aTableElement, 3053 const CellIndexes& aIndexes) { 3054 MOZ_ASSERT(!aIndexes.isErr()); 3055 return AtIndexInTableElement(aHTMLEditor, aTableElement, aIndexes.mRow, 3056 aIndexes.mColumn); 3057 } 3058 3059 /** 3060 * Treated as error if fails to compute current index or first index of the 3061 * cell. Note that even if the cell is not found due to no corresponding 3062 * frame at current index, it's not an error situation. 3063 */ 3064 [[nodiscard]] bool isOk() const { return !isErr(); } 3065 [[nodiscard]] bool isErr() const { return mFirst.isErr(); } 3066 3067 /** 3068 * FailedOrNotFound() returns true if this failed to initialize/update 3069 * or succeeded but found no cell element. 3070 */ 3071 [[nodiscard]] bool FailedOrNotFound() const { return isErr() || !mElement; } 3072 3073 /** 3074 * IsSpannedFromOtherRowOrColumn(), IsSpannedFromOtherColumn and 3075 * IsSpannedFromOtherRow() return true if there is no cell element 3076 * at the index because of spanning from other row and/or column. 3077 */ 3078 [[nodiscard]] bool IsSpannedFromOtherRowOrColumn() const { 3079 return mElement && mCurrent != mFirst; 3080 } 3081 [[nodiscard]] bool IsSpannedFromOtherColumn() const { 3082 return mElement && mCurrent.mColumn != mFirst.mColumn; 3083 } 3084 [[nodiscard]] bool IsSpannedFromOtherRow() const { 3085 return mElement && mCurrent.mRow != mFirst.mRow; 3086 } 3087 [[nodiscard]] bool IsNextColumnSpannedFromOtherColumn() const { 3088 return mElement && mCurrent.mColumn + 1 < NextColumnIndex(); 3089 } 3090 3091 /** 3092 * NextColumnIndex() and NextRowIndex() return column/row index of 3093 * next cell. Note that this does not check whether there is next 3094 * cell or not actually. 3095 */ 3096 [[nodiscard]] int32_t NextColumnIndex() const { 3097 if (NS_WARN_IF(FailedOrNotFound())) { 3098 return -1; 3099 } 3100 return mCurrent.mColumn + mEffectiveColSpan; 3101 } 3102 [[nodiscard]] int32_t NextRowIndex() const { 3103 if (NS_WARN_IF(FailedOrNotFound())) { 3104 return -1; 3105 } 3106 return mCurrent.mRow + mEffectiveRowSpan; 3107 } 3108 3109 /** 3110 * LastColumnIndex() and LastRowIndex() return column/row index of 3111 * column/row which is spanned by the cell. 3112 */ 3113 [[nodiscard]] int32_t LastColumnIndex() const { 3114 if (NS_WARN_IF(FailedOrNotFound())) { 3115 return -1; 3116 } 3117 return NextColumnIndex() - 1; 3118 } 3119 [[nodiscard]] int32_t LastRowIndex() const { 3120 if (NS_WARN_IF(FailedOrNotFound())) { 3121 return -1; 3122 } 3123 return NextRowIndex() - 1; 3124 } 3125 3126 /** 3127 * NumberOfPrecedingColmuns() and NumberOfPrecedingRows() return number of 3128 * preceding columns/rows if current index is spanned from other column/row. 3129 * Otherwise, i.e., current point is not spanned form other column/row, 3130 * returns 0. 3131 */ 3132 [[nodiscard]] int32_t NumberOfPrecedingColmuns() const { 3133 if (NS_WARN_IF(FailedOrNotFound())) { 3134 return -1; 3135 } 3136 return mCurrent.mColumn - mFirst.mColumn; 3137 } 3138 [[nodiscard]] int32_t NumberOfPrecedingRows() const { 3139 if (NS_WARN_IF(FailedOrNotFound())) { 3140 return -1; 3141 } 3142 return mCurrent.mRow - mFirst.mRow; 3143 } 3144 3145 /** 3146 * NumberOfFollowingColumns() and NumberOfFollowingRows() return 3147 * number of remaining columns/rows if the cell spans to other 3148 * column/row. 3149 */ 3150 [[nodiscard]] int32_t NumberOfFollowingColumns() const { 3151 if (NS_WARN_IF(FailedOrNotFound())) { 3152 return -1; 3153 } 3154 return mEffectiveColSpan - 1; 3155 } 3156 [[nodiscard]] int32_t NumberOfFollowingRows() const { 3157 if (NS_WARN_IF(FailedOrNotFound())) { 3158 return -1; 3159 } 3160 return mEffectiveRowSpan - 1; 3161 } 3162 3163 private: 3164 explicit CellData(int32_t aCurrentRowIndex, int32_t aCurrentColumnIndex, 3165 int32_t aFirstRowIndex, int32_t aFirstColumnIndex) 3166 : mCurrent(aCurrentRowIndex, aCurrentColumnIndex), 3167 mFirst(aFirstRowIndex, aFirstColumnIndex) {} 3168 explicit CellData(Element& aElement, int32_t aRowIndex, 3169 int32_t aColumnIndex, nsTableCellFrame& aTableCellFrame, 3170 nsTableWrapperFrame& aTableWrapperFrame); 3171 3172 [[nodiscard]] static CellData Error(int32_t aRowIndex, 3173 int32_t aColumnIndex) { 3174 return CellData(aRowIndex, aColumnIndex, -1, -1); 3175 } 3176 [[nodiscard]] static CellData NotFound(int32_t aRowIndex, 3177 int32_t aColumnIndex) { 3178 return CellData(aRowIndex, aColumnIndex, aRowIndex, aColumnIndex); 3179 } 3180 }; 3181 3182 /** 3183 * TableSize stores and computes number of rows and columns of a <table> 3184 * element. 3185 */ 3186 struct MOZ_STACK_CLASS TableSize final { 3187 int32_t mRowCount; 3188 int32_t mColumnCount; 3189 3190 TableSize() = delete; 3191 3192 /** 3193 * @param aHTMLEditor The editor which creates the instance. 3194 * @param aTableOrElementInTable If a <table> element, computes number 3195 * of rows and columns of it. 3196 * If another element in a <table> element, 3197 * computes number of rows and columns 3198 * of nearest ancestor <table> element. 3199 * Otherwise, i.e., non-<table> element 3200 * not in <table>, returns error. 3201 */ 3202 [[nodiscard]] static Result<TableSize, nsresult> Create( 3203 HTMLEditor& aHTMLEditor, Element& aTableOrElementInTable); 3204 3205 [[nodiscard]] bool IsEmpty() const { return !mRowCount || !mColumnCount; } 3206 3207 private: 3208 TableSize(int32_t aRowCount, int32_t aColumCount) 3209 : mRowCount(aRowCount), mColumnCount(aColumCount) {} 3210 }; 3211 3212 /** 3213 * GetTableCellElementAt() returns a <td> or <th> element of aTableElement 3214 * if there is a cell at the indexes. 3215 * 3216 * @param aTableElement Must be a <table> element. 3217 * @param aCellIndexes Indexes of cell which you want. 3218 * If rowspan and/or colspan is specified 2 or 3219 * larger, any indexes are allowed to retrieve 3220 * the cell in the area. 3221 * @return The cell element if there is in the <table>. 3222 * Returns nullptr without error if the indexes 3223 * are out of bounds. 3224 */ 3225 [[nodiscard]] inline Element* GetTableCellElementAt( 3226 Element& aTableElement, const CellIndexes& aCellIndexes) const; 3227 [[nodiscard]] Element* GetTableCellElementAt(Element& aTableElement, 3228 int32_t aRowIndex, 3229 int32_t aColumnIndex) const; 3230 3231 /** 3232 * GetSelectedOrParentTableElement() returns <td>, <th>, <tr> or <table> 3233 * element: 3234 * #1 if the first selection range selects a cell, returns it. 3235 * #2 if the first selection range does not select a cell and 3236 * the selection anchor refers a <table>, returns it. 3237 * #3 if the first selection range does not select a cell and 3238 * the selection anchor refers a <tr>, returns it. 3239 * #4 if the first selection range does not select a cell and 3240 * the selection anchor refers a <td>, returns it. 3241 * #5 otherwise, nearest ancestor <td> or <th> element of the 3242 * selection anchor if there is. 3243 * In #1 and #4, *aIsCellSelected will be set to true (i.e,, when 3244 * a selection range selects a cell element). 3245 */ 3246 Result<RefPtr<Element>, nsresult> GetSelectedOrParentTableElement( 3247 bool* aIsCellSelected = nullptr) const; 3248 3249 /** 3250 * GetFirstSelectedCellElementInTable() returns <td> or <th> element at 3251 * first selection (using GetSelectedOrParentTableElement). If found cell 3252 * element is not in <table> or <tr> element, this returns nullptr. 3253 */ 3254 Result<RefPtr<Element>, nsresult> GetFirstSelectedCellElementInTable() const; 3255 3256 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 3257 HandlePaste(AutoEditActionDataSetter& aEditActionData, 3258 nsIClipboard::ClipboardType aClipboardType, 3259 DataTransfer* aDataTransfer) final; 3260 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 3261 HandlePasteAsQuotation(AutoEditActionDataSetter& aEditActionData, 3262 nsIClipboard::ClipboardType aClipboardType, 3263 DataTransfer* aDataTransfer) final; 3264 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 3265 HandlePasteTransferable(AutoEditActionDataSetter& aEditActionData, 3266 nsITransferable& aTransferable) final; 3267 3268 /** 3269 * PasteInternal() pasts text with replacing selected content. 3270 * This tries to dispatch ePaste event first. If its defaultPrevent() is 3271 * called, this does nothing but returns NS_OK. 3272 * 3273 * @param aClipboardType nsIClipboard::kGlobalClipboard or 3274 * nsIClipboard::kSelectionClipboard. 3275 * @param aEditingHost The editing host. 3276 */ 3277 MOZ_CAN_RUN_SCRIPT nsresult 3278 PasteInternal(nsIClipboard::ClipboardType aClipboardType, 3279 DataTransfer* aDataTransfer, const Element& aEditingHost); 3280 3281 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 3282 InsertWithQuotationsAsSubAction(const nsAString& aQuotedText) final; 3283 3284 /** 3285 * InsertAsCitedQuotationInternal() inserts a <blockquote> element whose 3286 * cite attribute is aCitation and whose content is aQuotedText. 3287 * Note that this shouldn't be called when IsPlaintextMailComposer() is true. 3288 * 3289 * @param aQuotedText HTML source if aInsertHTML is true. Otherwise, 3290 * plain text. This is inserted into new <blockquote> 3291 * element. 3292 * @param aCitation cite attribute value of new <blockquote> element. 3293 * @param aInsertHTML true if aQuotedText should be treated as HTML 3294 * source. 3295 * false if aQuotedText should be treated as plain 3296 * text. 3297 * @param aEditingHost The editing host. 3298 * @param aNodeInserted [OUT] The new <blockquote> element. 3299 */ 3300 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertAsCitedQuotationInternal( 3301 const nsAString& aQuotedText, const nsAString& aCitation, 3302 bool aInsertHTML, const Element& aEditingHost, nsINode** aNodeInserted); 3303 3304 /** 3305 * InsertNodeIntoProperAncestorWithTransaction() attempts to insert aNode 3306 * into the document, at aPointToInsert. Checks with strict dtd to see if 3307 * containment is allowed. If not allowed, will attempt to find a parent 3308 * in the parent hierarchy of aPointToInsert.GetContainer() that will accept 3309 * aNode as a child. If such a parent is found, will split the document 3310 * tree from aPointToInsert up to parent, and then insert aNode. 3311 * aPointToInsert is then adjusted to point to the actual location that 3312 * aNode was inserted at. aSplitAtEdges specifies if the splitting process 3313 * is allowed to result in empty nodes. 3314 * 3315 * @param aContent The content node to insert. 3316 * @param aPointToInsert Insertion point. 3317 * @param aSplitAtEdges Splitting can result in empty nodes? 3318 */ 3319 template <typename NodeType> 3320 [[nodiscard]] MOZ_CAN_RUN_SCRIPT 3321 Result<CreateNodeResultBase<NodeType>, nsresult> 3322 InsertNodeIntoProperAncestorWithTransaction( 3323 NodeType& aContent, const EditorDOMPoint& aPointToInsert, 3324 SplitAtEdges aSplitAtEdges); 3325 3326 /** 3327 * InsertTextWithQuotationsInternal() replaces selection with new content. 3328 * First, this method splits aStringToInsert to multiple chunks which start 3329 * with non-linebreaker except first chunk and end with a linebreaker except 3330 * last chunk. Then, each chunk starting with ">" is inserted after wrapping 3331 * with <span _moz_quote="true">, and each chunk not starting with ">" is 3332 * inserted as normal text. 3333 */ 3334 MOZ_CAN_RUN_SCRIPT nsresult InsertTextWithQuotationsInternal( 3335 const nsAString& aStringToInsert, const Element& aEditingHost); 3336 3337 /** 3338 * ReplaceContainerWithTransactionInternal() is implementation of 3339 * ReplaceContainerWithTransaction() and 3340 * ReplaceContainerAndCloneAttributesWithTransaction(). 3341 * 3342 * @param aOldContainer The element which will be replaced with new 3343 * element. 3344 * @param aTagName The name of new element node. 3345 * @param aAttribute Attribute name which will be set to the new 3346 * element. This will be ignored if 3347 * aCloneAllAttributes is set to true. 3348 * @param aAttributeValue Attribute value which will be set to 3349 * aAttribute. 3350 * @param aCloneAllAttributes If true, all attributes of aOldContainer will 3351 * be copied to the new element. 3352 */ 3353 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 3354 ReplaceContainerWithTransactionInternal(Element& aOldContainer, 3355 const nsAtom& aTagName, 3356 const nsAtom& aAttribute, 3357 const nsAString& aAttributeValue, 3358 bool aCloneAllAttributes); 3359 3360 /** 3361 * DeleteSelectionAndCreateElement() creates a element whose name is aTag. 3362 * And insert it into the DOM tree after removing the selected content. 3363 * 3364 * @param aTag The element name to be created. 3365 * @param aInitializer A function to initialize the new element before 3366 * or after (depends on the pref) connecting the 3367 * element into the DOM tree. Note that this should 3368 * not touch outside given element because doing it 3369 * would break range updater's result. 3370 */ 3371 MOZ_CAN_RUN_SCRIPT Result<RefPtr<Element>, nsresult> 3372 DeleteSelectionAndCreateElement( 3373 nsAtom& aTag, 3374 const InitializeInsertingElement& aInitializer = DoNothingForNewElement); 3375 3376 /** 3377 * This method first deletes the selection, if it's not collapsed. Then if 3378 * the selection lies in a CharacterData node, it splits it. If the 3379 * selection is at this point collapsed in a CharacterData node, it's 3380 * adjusted to be collapsed right before or after the node instead (which is 3381 * always possible, since the node was split). 3382 */ 3383 MOZ_CAN_RUN_SCRIPT nsresult DeleteSelectionAndPrepareToCreateNode(); 3384 3385 /** 3386 * PrepareToInsertLineBreak() returns a point where a new line break node 3387 * should be inserted. If aPointToInsert points middle of a text node, this 3388 * method splits the text node and returns the point before right node. 3389 * 3390 * @param aLineBreakType Whether you will insert <br> or a preformatted 3391 * linefeed. 3392 * @param aPointToInsert Candidate point to insert new line break node. 3393 * @return Computed point to insert new line break node. 3394 * If something failed, this return error. 3395 */ 3396 MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> PrepareToInsertLineBreak( 3397 LineBreakType aLineBreakType, const EditorDOMPoint& aPointToInsert); 3398 3399 /** 3400 * If unnecessary line break is there immediately after aPoint, this deletes 3401 * the line break. Note that unnecessary line break means that the line break 3402 * is a padding line break for empty line immediately before a block boundary 3403 * and it's not a placeholder of ancestor inline elements. 3404 * 3405 * @param aNextOrAfterModifiedPoint If you inserted something, this should 3406 * be next point or after the inserted 3407 * content. 3408 * If you deleted something, this should be 3409 * end of the deleted range. 3410 */ 3411 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 3412 EnsureNoFollowingUnnecessaryLineBreak( 3413 const EditorDOMPoint& aNextOrAfterModifiedPoint); 3414 3415 /** 3416 * IndentAsSubAction() indents the content around Selection. 3417 * 3418 * @param aEditingHost The editing host. 3419 */ 3420 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 3421 IndentAsSubAction(const Element& aEditingHost); 3422 3423 /** 3424 * OutdentAsSubAction() outdents the content around Selection. 3425 * 3426 * @param aEditingHost The editing host. 3427 */ 3428 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 3429 OutdentAsSubAction(const Element& aEditingHost); 3430 3431 MOZ_CAN_RUN_SCRIPT nsresult LoadHTML(const nsAString& aInputString); 3432 3433 /** 3434 * UpdateMetaCharsetWithTransaction() scans all <meta> elements in the 3435 * document and if and only if there is a <meta> element having `httpEquiv` 3436 * attribute and whose value includes `content-type`, updates its `content` 3437 * attribute value to aCharacterSet. 3438 */ 3439 MOZ_CAN_RUN_SCRIPT bool UpdateMetaCharsetWithTransaction( 3440 Document& aDocument, const nsACString& aCharacterSet); 3441 3442 /** 3443 * SetInlinePropertiesAsSubAction() stores new styles with 3444 * mPendingStylesToApplyToNewContent if `Selection` is collapsed. Otherwise, 3445 * applying the styles to all selected contents. 3446 * 3447 * @param aStylesToSet The styles which should be applied to the 3448 * selected content. 3449 * @param aEditingHost The editing host. 3450 */ 3451 template <size_t N> 3452 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertiesAsSubAction( 3453 const AutoTArray<EditorInlineStyleAndValue, N>& aStylesToSet, 3454 const Element& aEditingHost); 3455 3456 /** 3457 * SetInlinePropertiesAroundRanges() applying the styles to the ranges even if 3458 * the ranges are collapsed. 3459 */ 3460 template <size_t N> 3461 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertiesAroundRanges( 3462 AutoClonedRangeArray& aRanges, 3463 const AutoTArray<EditorInlineStyleAndValue, N>& aStylesToSet); 3464 3465 /** 3466 * RemoveInlinePropertiesAsSubAction() removes specified styles from 3467 * mPendingStylesToApplyToNewContent if `Selection` is collapsed. Otherwise, 3468 * removing the style. 3469 * 3470 * @param aStylesToRemove Styles to remove from the selected contents. 3471 * @param aEditingHost The editing host. 3472 */ 3473 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveInlinePropertiesAsSubAction( 3474 const nsTArray<EditorInlineStyle>& aStylesToRemove, 3475 const Element& aEditingHost); 3476 3477 /** 3478 * Helper method to call RemoveInlinePropertiesAsSubAction(). If you want to 3479 * remove other elements to remove the style completely, this will append 3480 * related elements of aStyleToRemove and aStyleToRemove itself to the array. 3481 * E.g., nsGkAtoms::strong and nsGkAtoms::b will be appended if aStyleToRemove 3482 * is nsGkAtoms::b. 3483 */ 3484 void AppendInlineStyleAndRelatedStyle( 3485 const EditorInlineStyle& aStyleToRemove, 3486 nsTArray<EditorInlineStyle>& aStylesToRemove) const; 3487 3488 enum class RetrievingBackgroundColorOption { 3489 // Ignore inline styles, i.e., return only background color of ancestor 3490 // blocks. 3491 OnlyBlockBackgroundColor, 3492 // Stop at nearest inclusive ancestor block. 3493 StopAtInclusiveAncestorBlock, 3494 // Return default background color for the document if there is no ancestor 3495 // elements which have background color. 3496 DefaultColorIfNoSpecificBackgroundColor, 3497 }; 3498 using RetrievingBackgroundColorOptions = 3499 EnumSet<RetrievingBackgroundColorOption>; 3500 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 3501 GetCSSBackgroundColorState(bool* aMixed, nsAString& aOutColor, 3502 RetrievingBackgroundColorOptions aOptions); 3503 3504 nsresult GetHTMLBackgroundColorState(bool* aMixed, nsAString& outColor); 3505 3506 /** 3507 * This sets background on the appropriate container element (table, cell,) 3508 * or calls to set the page background. 3509 */ 3510 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 3511 SetBlockBackgroundColorWithCSSAsSubAction(const nsAString& aColor); 3512 MOZ_CAN_RUN_SCRIPT nsresult 3513 SetHTMLBackgroundColorWithTransaction(const nsAString& aColor); 3514 3515 MOZ_CAN_RUN_SCRIPT_BOUNDARY void InitializeSelectionAncestorLimit( 3516 Element& aAncestorLimit) const final; 3517 3518 /** 3519 * Make the given selection span the entire document. 3520 */ 3521 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SelectEntireDocument() final; 3522 3523 /** 3524 * Use this to assure that selection is set after attribute nodes when 3525 * trying to collapse selection at begining of a block node 3526 * e.g., when setting at beginning of a table cell 3527 * This will stop at a table, however, since we don't want to 3528 * "drill down" into nested tables. 3529 */ 3530 MOZ_CAN_RUN_SCRIPT void CollapseSelectionToDeepestNonTableFirstChild( 3531 nsINode* aNode); 3532 /** 3533 * MaybeCollapseSelectionAtFirstEditableNode() may collapse selection at 3534 * proper position to staring to edit. If there is a non-editable node 3535 * before any editable text nodes or inline elements which can have text 3536 * nodes as their children, collapse selection at start of the editing 3537 * host. If there is an editable text node which is not collapsed, collapses 3538 * selection at the start of the text node. If there is an editable inline 3539 * element which cannot have text nodes as its child, collapses selection at 3540 * before the element node. Otherwise, collapses selection at start of the 3541 * editing host. 3542 * 3543 * @param aIgnoreIfSelectionInEditingHost 3544 * This method does nothing if selection is in the 3545 * editing host except if it's collapsed at start of 3546 * the editing host. 3547 * Note that if selection ranges were outside of 3548 * current selection limiter, selection was collapsed 3549 * at the start of the editing host therefore, if 3550 * you call this with setting this to true, you can 3551 * keep selection ranges if user has already been 3552 * changed. 3553 */ 3554 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 3555 MaybeCollapseSelectionAtFirstEditableNode( 3556 bool aIgnoreIfSelectionInEditingHost) const; 3557 3558 /** 3559 * Join aLeftText and aRightText with normalizing white-spaces at the joining 3560 * point if it's required. aRightText must be the next sibling of aLeftText. 3561 */ 3562 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<JoinNodesResult, nsresult> 3563 JoinTextNodesWithNormalizeWhiteSpaces(Text& aLeftText, Text& aRightText); 3564 3565 class BlobReader final { 3566 using AutoEditActionDataSetter = EditorBase::AutoEditActionDataSetter; 3567 3568 public: 3569 MOZ_CAN_RUN_SCRIPT BlobReader(dom::BlobImpl* aBlob, HTMLEditor* aHTMLEditor, 3570 SafeToInsertData aSafeToInsertData, 3571 const EditorDOMPoint& aPointToInsert, 3572 DeleteSelectedContent aDeleteSelectedContent, 3573 const Element& aEditingHost); 3574 3575 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(BlobReader) 3576 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(BlobReader) 3577 3578 MOZ_CAN_RUN_SCRIPT nsresult OnResult(const nsACString& aResult); 3579 nsresult OnError(const nsAString& aErrorName); 3580 3581 private: 3582 ~BlobReader() = default; 3583 3584 RefPtr<dom::BlobImpl> mBlob; 3585 RefPtr<HTMLEditor> mHTMLEditor; 3586 RefPtr<const Element> mEditingHost; 3587 RefPtr<DataTransfer> mDataTransfer; 3588 EditorDOMPoint mPointToInsert; 3589 EditAction mEditAction; 3590 SafeToInsertData mSafeToInsertData; 3591 DeleteSelectedContent mDeleteSelectedContent; 3592 bool mNeedsToDispatchBeforeInputEvent; 3593 }; 3594 3595 void CreateEventListeners() final; 3596 nsresult InstallEventListeners() final; 3597 3598 bool ShouldReplaceRootElement() const; 3599 MOZ_CAN_RUN_SCRIPT void NotifyRootChanged(); 3600 Element* GetBodyElement() const; 3601 3602 /** 3603 * Get the focused node of this editor. 3604 * @return If the editor has focus, this returns the focused node. 3605 * Otherwise, returns null. 3606 */ 3607 nsINode* GetFocusedNode() const; 3608 3609 already_AddRefed<Element> GetInputEventTargetElement() const final; 3610 3611 /** 3612 * Return TRUE if aElement is a table-related elemet and caret was set. 3613 */ 3614 MOZ_CAN_RUN_SCRIPT bool SetCaretInTableCell(dom::Element* aElement); 3615 3616 /** 3617 * HandleTabKeyPressInTable() handles "Tab" key press in table if selection 3618 * is in a `<table>` element. 3619 */ 3620 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult> 3621 HandleTabKeyPressInTable(WidgetKeyboardEvent* aKeyboardEvent); 3622 3623 /** 3624 * InsertPosition is an enum to indicate where the method should insert to. 3625 */ 3626 enum class InsertPosition { 3627 // Before selected cell or a cell containing first selection range. 3628 eBeforeSelectedCell, 3629 // After selected cell or a cell containing first selection range. 3630 eAfterSelectedCell, 3631 }; 3632 3633 /** 3634 * InsertTableCellsWithTransaction() inserts <td> elements at aPointToInsert. 3635 * Note that this simply inserts <td> elements, i.e., colspan and rowspan 3636 * around the cell containing selection are not modified. So, for example, 3637 * adding a cell to rectangular table changes non-rectangular table. 3638 * And if the cell containing selection is at left of row-spanning cell, 3639 * it may be moved to right side of the row-spanning cell after inserting 3640 * some cell elements before it. Similarly, colspan won't be adjusted 3641 * for keeping table rectangle. 3642 * Finally, puts caret into previous cell of the insertion point or the 3643 * first inserted cell if aPointToInsert is start of the row. 3644 * 3645 * @param aPointToInsert The place to insert one or more cell 3646 * elements. The container must be a 3647 * <tr> element. 3648 * @param aNumberOfCellsToInsert Number of cells to insert. 3649 * @return The first inserted cell element and 3650 * start of the last inserted cell element 3651 * as a point to put caret. 3652 */ 3653 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult> 3654 InsertTableCellsWithTransaction(const EditorDOMPoint& aPointToInsert, 3655 int32_t aNumberOfCellsToInsert); 3656 3657 /** 3658 * InsertTableColumnsWithTransaction() inserts cell elements to every rows 3659 * at same column index as the cell specified by aPointToInsert. 3660 * 3661 * @param aNumberOfColumnsToInsert Number of columns to insert. 3662 */ 3663 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertTableColumnsWithTransaction( 3664 const EditorDOMPoint& aPointToInsert, int32_t aNumberOfColumnsToInsert); 3665 3666 /** 3667 * InsertTableRowsWithTransaction() inserts <tr> elements before or after 3668 * aCellElement. When aCellElement spans rows and aInsertPosition is 3669 * eAfterSelectedCell, new rows will be inserted after the most-bottom row 3670 * which contains the cell. 3671 * 3672 * @param aCellElement The cell element pinting where this will 3673 * insert a row before or after. 3674 * @param aNumberOfRowsToInsert Number of rows to insert. 3675 * @param aInsertPosition Before or after the target cell which 3676 * contains first selection range. 3677 */ 3678 MOZ_CAN_RUN_SCRIPT nsresult InsertTableRowsWithTransaction( 3679 Element& aCellElement, int32_t aNumberOfRowsToInsert, 3680 InsertPosition aInsertPosition); 3681 3682 /** 3683 * Insert a new cell after or before supplied aCell. 3684 * Optional: If aNewCell supplied, returns the newly-created cell (addref'd, 3685 * of course) 3686 * This doesn't change or use the current selection. 3687 */ 3688 MOZ_CAN_RUN_SCRIPT nsresult InsertCell(Element* aCell, int32_t aRowSpan, 3689 int32_t aColSpan, bool aAfter, 3690 bool aIsHeader, Element** aNewCell); 3691 3692 /** 3693 * DeleteSelectedTableColumnsWithTransaction() removes cell elements which 3694 * belong to same columns of selected cell elements. 3695 * If only one cell element is selected or first selection range is 3696 * in a cell, removes cell elements which belong to same column. 3697 * If 2 or more cell elements are selected, removes cell elements which 3698 * belong to any of all selected columns. In this case, 3699 * aNumberOfColumnsToDelete is ignored. 3700 * If there is no selection ranges, returns error. 3701 * If selection is not in a cell element, this does not return error, 3702 * just does nothing. 3703 * WARNING: This does not remove <col> nor <colgroup> elements. 3704 * 3705 * @param aNumberOfColumnsToDelete Number of columns to remove. This is 3706 * ignored if 2 ore more cells are 3707 * selected. 3708 */ 3709 MOZ_CAN_RUN_SCRIPT nsresult 3710 DeleteSelectedTableColumnsWithTransaction(int32_t aNumberOfColumnsToDelete); 3711 3712 /** 3713 * DeleteTableColumnWithTransaction() removes cell elements which belong 3714 * to the specified column. 3715 * This method adjusts colspan attribute value if cells spanning the 3716 * column to delete. 3717 * WARNING: This does not remove <col> nor <colgroup> elements. 3718 * 3719 * @param aTableElement The <table> element which contains the 3720 * column which you want to remove. 3721 * @param aRowIndex Index of the column which you want to remove. 3722 * 0 is the first column. 3723 */ 3724 MOZ_CAN_RUN_SCRIPT nsresult DeleteTableColumnWithTransaction( 3725 Element& aTableElement, int32_t aColumnIndex); 3726 3727 /** 3728 * DeleteSelectedTableRowsWithTransaction() removes <tr> elements. 3729 * If only one cell element is selected or first selection range is 3730 * in a cell, removes <tr> elements starting from a <tr> element 3731 * containing the selected cell or first selection range. 3732 * If 2 or more cell elements are selected, all <tr> elements 3733 * which contains selected cell(s). In this case, aNumberOfRowsToDelete 3734 * is ignored. 3735 * If there is no selection ranges, returns error. 3736 * If selection is not in a cell element, this does not return error, 3737 * just does nothing. 3738 * 3739 * @param aNumberOfRowsToDelete Number of rows to remove. This is ignored 3740 * if 2 or more cells are selected. 3741 */ 3742 MOZ_CAN_RUN_SCRIPT nsresult 3743 DeleteSelectedTableRowsWithTransaction(int32_t aNumberOfRowsToDelete); 3744 3745 /** 3746 * DeleteTableRowWithTransaction() removes a <tr> element whose index in 3747 * the <table> is aRowIndex. 3748 * This method adjusts rowspan attribute value if the <tr> element contains 3749 * cells which spans rows. 3750 * 3751 * @param aTableElement The <table> element which contains the 3752 * <tr> element which you want to remove. 3753 * @param aRowIndex Index of the <tr> element which you want to 3754 * remove. 0 is the first row. 3755 */ 3756 MOZ_CAN_RUN_SCRIPT nsresult 3757 DeleteTableRowWithTransaction(Element& aTableElement, int32_t aRowIndex); 3758 3759 /** 3760 * DeleteTableCellWithTransaction() removes table cell elements. If two or 3761 * more cell elements are selected, this removes all selected cell elements. 3762 * Otherwise, this removes some cell elements starting from selected cell 3763 * element or a cell containing first selection range. When this removes 3764 * last cell element in <tr> or <table>, this removes the <tr> or the 3765 * <table> too. Note that when removing a cell causes number of its row 3766 * becomes less than the others, this method does NOT fill the place with 3767 * rowspan nor colspan. This does not return error even if selection is not 3768 * in cell element, just does nothing. 3769 * 3770 * @param aNumberOfCellsToDelete Number of cells to remove. This is ignored 3771 * if 2 or more cells are selected. 3772 */ 3773 MOZ_CAN_RUN_SCRIPT nsresult 3774 DeleteTableCellWithTransaction(int32_t aNumberOfCellsToDelete); 3775 3776 /** 3777 * DeleteAllChildrenWithTransaction() removes all children of aElement from 3778 * the tree. 3779 * 3780 * @param aElement The element whose children you want to remove. 3781 */ 3782 MOZ_CAN_RUN_SCRIPT nsresult 3783 DeleteAllChildrenWithTransaction(Element& aElement); 3784 3785 /** 3786 * Move all contents from aCellToMerge into aTargetCell (append at end). 3787 */ 3788 MOZ_CAN_RUN_SCRIPT nsresult MergeCells(RefPtr<Element> aTargetCell, 3789 RefPtr<Element> aCellToMerge, 3790 bool aDeleteCellToMerge); 3791 3792 /** 3793 * DeleteTableElementAndChildren() removes aTableElement (and its children) 3794 * from the DOM tree with transaction. 3795 * 3796 * @param aTableElement The <table> element which you want to remove. 3797 */ 3798 MOZ_CAN_RUN_SCRIPT nsresult 3799 DeleteTableElementAndChildrenWithTransaction(Element& aTableElement); 3800 3801 MOZ_CAN_RUN_SCRIPT nsresult SetColSpan(Element* aCell, int32_t aColSpan); 3802 MOZ_CAN_RUN_SCRIPT nsresult SetRowSpan(Element* aCell, int32_t aRowSpan); 3803 3804 /** 3805 * Helper used to get nsTableWrapperFrame for a table. 3806 */ 3807 static nsTableWrapperFrame* GetTableFrame(const Element* aTable); 3808 3809 /** 3810 * GetNumberOfCellsInRow() returns number of actual cell elements in the row. 3811 * If some cells appear by "rowspan" in other rows, they are ignored. 3812 * 3813 * @param aTableElement The <table> element. 3814 * @param aRowIndex Valid row index in aTableElement. This method 3815 * counts cell elements in the row. 3816 * @return -1 if this meets unexpected error. 3817 * Otherwise, number of cells which this method found. 3818 */ 3819 int32_t GetNumberOfCellsInRow(Element& aTableElement, int32_t aRowIndex); 3820 3821 /** 3822 * Test if all cells in row or column at given index are selected. 3823 */ 3824 bool AllCellsInRowSelected(Element* aTable, int32_t aRowIndex, 3825 int32_t aNumberOfColumns); 3826 bool AllCellsInColumnSelected(Element* aTable, int32_t aColIndex, 3827 int32_t aNumberOfRows); 3828 3829 bool IsEmptyCell(Element* aCell); 3830 3831 /** 3832 * Most insert methods need to get the same basic context data. 3833 * Any of the pointers may be null if you don't need that datum (for more 3834 * efficiency). 3835 * Input: *aCell is a known cell, 3836 * if null, cell is obtained from the anchor node of the selection. 3837 * Returns NS_EDITOR_ELEMENT_NOT_FOUND if cell is not found even if aCell is 3838 * null. 3839 */ 3840 MOZ_CAN_RUN_SCRIPT nsresult GetCellContext(Element** aTable, Element** aCell, 3841 nsINode** aCellParent, 3842 int32_t* aCellOffset, 3843 int32_t* aRowIndex, 3844 int32_t* aColIndex); 3845 3846 nsresult GetCellSpansAt(Element* aTable, int32_t aRowIndex, int32_t aColIndex, 3847 int32_t& aActualRowSpan, int32_t& aActualColSpan); 3848 3849 MOZ_CAN_RUN_SCRIPT nsresult SplitCellIntoColumns( 3850 Element* aTable, int32_t aRowIndex, int32_t aColIndex, 3851 int32_t aColSpanLeft, int32_t aColSpanRight, Element** aNewCell); 3852 3853 MOZ_CAN_RUN_SCRIPT nsresult SplitCellIntoRows( 3854 Element* aTable, int32_t aRowIndex, int32_t aColIndex, 3855 int32_t aRowSpanAbove, int32_t aRowSpanBelow, Element** aNewCell); 3856 3857 MOZ_CAN_RUN_SCRIPT nsresult CopyCellBackgroundColor(Element* aDestCell, 3858 Element* aSourceCell); 3859 3860 /** 3861 * Reduce rowspan/colspan when cells span into nonexistent rows/columns. 3862 */ 3863 MOZ_CAN_RUN_SCRIPT nsresult FixBadRowSpan(Element* aTable, int32_t aRowIndex, 3864 int32_t& aNewRowCount); 3865 MOZ_CAN_RUN_SCRIPT nsresult FixBadColSpan(Element* aTable, int32_t aColIndex, 3866 int32_t& aNewColCount); 3867 3868 /** 3869 * XXX NormalizeTableInternal() is broken. If it meets a cell which has 3870 * bigger or smaller rowspan or colspan than actual number of cells, 3871 * this always failed to scan the table. Therefore, this does nothing 3872 * when the table should be normalized. 3873 * 3874 * @param aTableOrElementInTable An element which is in a <table> element 3875 * or <table> element itself. Otherwise, 3876 * this returns NS_OK but does nothing. 3877 */ 3878 MOZ_CAN_RUN_SCRIPT nsresult 3879 NormalizeTableInternal(Element& aTableOrElementInTable); 3880 3881 /** 3882 * Fallback method: Call this after using ClearSelection() and you 3883 * failed to set selection to some other content in the document. 3884 */ 3885 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetSelectionAtDocumentStart(); 3886 3887 // Methods for handling plaintext quotations 3888 MOZ_CAN_RUN_SCRIPT nsresult PasteAsPlaintextQuotation( 3889 nsIClipboard::ClipboardType aSelectionType, DataTransfer* aDataTransfer, 3890 const Element& aEditingHost); 3891 3892 enum class AddCites { No, Yes }; 3893 /** 3894 * Insert a string as quoted text, replacing the selected text (if any). 3895 * @param aQuotedText The string to insert. 3896 * @param aAddCites Whether to prepend extra ">" to each line 3897 * (usually true, unless those characters 3898 * have already been added.) 3899 * @param aEditingHost The editing host. 3900 * @return aNodeInserted The node spanning the insertion, if applicable. 3901 * If aAddCites is false, this will be null. 3902 */ 3903 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertAsPlaintextQuotation( 3904 const nsAString& aQuotedText, AddCites aAddCites, 3905 const Element& aEditingHost, nsINode** aNodeInserted = nullptr); 3906 3907 /** 3908 * InsertObject() inserts given object at aPointToInsert. 3909 * 3910 * @param aType one of kFileMime, kJPEGImageMime, kJPGImageMime, 3911 * kPNGImageMime, kGIFImageMime. 3912 */ 3913 MOZ_CAN_RUN_SCRIPT nsresult InsertObject( 3914 const nsACString& aType, nsISupports* aObject, 3915 SafeToInsertData aSafeToInsertData, const EditorDOMPoint& aPointToInsert, 3916 DeleteSelectedContent aDeleteSelectedContent, 3917 const Element& aEditingHost); 3918 3919 class HTMLTransferablePreparer; 3920 nsresult PrepareHTMLTransferable(nsITransferable** aTransferable, 3921 const Element* aEditingHost) const; 3922 3923 enum class HavePrivateHTMLFlavor { No, Yes }; 3924 MOZ_CAN_RUN_SCRIPT nsresult InsertFromTransferableAtSelection( 3925 nsITransferable* aTransferable, const nsAString& aContextStr, 3926 const nsAString& aInfoStr, HavePrivateHTMLFlavor aHavePrivateHTMLFlavor, 3927 const Element& aEditingHost); 3928 3929 /** 3930 * InsertFromDataTransfer() is called only when user drops data into 3931 * this editor. Don't use this method for other purposes. 3932 * 3933 * @param aIndex index of aDataTransfer's item to insert. 3934 */ 3935 MOZ_CAN_RUN_SCRIPT nsresult InsertFromDataTransfer( 3936 const DataTransfer* aDataTransfer, uint32_t aIndex, 3937 nsIPrincipal* aSourcePrincipal, const EditorDOMPoint& aDroppedAt, 3938 DeleteSelectedContent aDeleteSelectedContent, 3939 const Element& aEditingHost); 3940 3941 static HavePrivateHTMLFlavor DataTransferOrClipboardHasPrivateHTMLFlavor( 3942 DataTransfer* aDataTransfer, nsIClipboard* clipboard); 3943 3944 /** 3945 * CF_HTML: 3946 * <https://docs.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format>. 3947 * 3948 * @param[in] aCfhtml a CF_HTML string as defined above. 3949 * @param[out] aStuffToPaste the fragment, excluding context. 3950 * @param[out] aCfcontext the context, excluding the fragment, including a 3951 * marker (`kInsertionCookie`) indicating where the 3952 * fragment begins. 3953 */ 3954 nsresult ParseCFHTML(const nsCString& aCfhtml, char16_t** aStuffToPaste, 3955 char16_t** aCfcontext); 3956 3957 /** 3958 * AutoHTMLFragmentBoundariesFixer fixes both edges of topmost child contents 3959 * which are created with SubtreeContentIterator. 3960 */ 3961 class MOZ_STACK_CLASS AutoHTMLFragmentBoundariesFixer final { 3962 public: 3963 /** 3964 * @param aArrayOfTopMostChildContents 3965 * [in/out] The topmost child contents which will be 3966 * inserted into the DOM tree. Both edges, i.e., 3967 * first node and last node in this array will be 3968 * checked whether they can be inserted into 3969 * another DOM tree. If not, it'll replaces some 3970 * orphan nodes around nodes with proper parent. 3971 */ 3972 explicit AutoHTMLFragmentBoundariesFixer( 3973 nsTArray<OwningNonNull<nsIContent>>& aArrayOfTopMostChildContents); 3974 3975 private: 3976 /** 3977 * EnsureBeginsOrEndsWithValidContent() replaces some nodes starting from 3978 * start or end with proper element node if it's necessary. 3979 * If first or last node of aArrayOfTopMostChildContents is in list and/or 3980 * `<table>` element, looks for topmost list element or `<table>` element 3981 * with `CollectTableAndAnyListElementsOfInclusiveAncestorsAt()` and 3982 * `GetMostDistantAncestorListOrTableElement()`. Then, checks 3983 * whether some nodes are in aArrayOfTopMostChildContents are the topmost 3984 * list/table element or its descendant and if so, removes the nodes from 3985 * aArrayOfTopMostChildContents and inserts the list/table element instead. 3986 * Then, aArrayOfTopMostChildContents won't start/end with list-item nor 3987 * table cells. 3988 */ 3989 enum class StartOrEnd { start, end }; 3990 void EnsureBeginsOrEndsWithValidContent( 3991 StartOrEnd aStartOrEnd, 3992 nsTArray<OwningNonNull<nsIContent>>& aArrayOfTopMostChildContents) 3993 const; 3994 3995 /** 3996 * CollectTableAndAnyListElementsOfInclusiveAncestorsAt() collects list 3997 * elements and table related elements from the inclusive ancestors 3998 * (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor) of aNode. 3999 */ 4000 static void CollectTableAndAnyListElementsOfInclusiveAncestorsAt( 4001 nsIContent& aContent, 4002 nsTArray<OwningNonNull<Element>>& aOutArrayOfListAndTableElements); 4003 4004 /** 4005 * GetMostDistantAncestorListOrTableElement() returns a list or a 4006 * `<table>` element which is in 4007 * aInclusiveAncestorsTableOrListElements and they are actually 4008 * valid ancestor of at least one of aArrayOfTopMostChildContents. 4009 */ 4010 static Element* GetMostDistantAncestorListOrTableElement( 4011 const nsTArray<OwningNonNull<nsIContent>>& aArrayOfTopMostChildContents, 4012 const nsTArray<OwningNonNull<Element>>& 4013 aInclusiveAncestorsTableOrListElements); 4014 4015 /** 4016 * FindReplaceableTableElement() is a helper method of 4017 * EnsureBeginsOrEndsWithValidContent(). If aNodeMaybeInTableElement is 4018 * a descendant of aTableElement, returns aNodeMaybeInTableElement or its 4019 * nearest ancestor whose tag name is `<td>`, `<th>`, `<tr>`, `<thead>`, 4020 * `<tfoot>`, `<tbody>` or `<caption>`. 4021 * 4022 * @param aTableElement Must be a `<table>` element. 4023 * @param aContentMaybeInTableElement A node which may be in aTableElement. 4024 */ 4025 Element* FindReplaceableTableElement( 4026 Element& aTableElement, nsIContent& aContentMaybeInTableElement) const; 4027 4028 /** 4029 * IsReplaceableListElement() is a helper method of 4030 * EnsureBeginsOrEndsWithValidContent(). If aNodeMaybeInListElement is a 4031 * descendant of aListElement, returns true. Otherwise, false. 4032 * 4033 * @param aListElement Must be a list element. 4034 * @param aContentMaybeInListElement A node which may be in aListElement. 4035 */ 4036 bool IsReplaceableListElement(Element& aListElement, 4037 nsIContent& aContentMaybeInListElement) const; 4038 }; 4039 4040 /** 4041 * MakeDefinitionListItemWithTransaction() replaces parent list of current 4042 * selection with <dl> or create new <dl> element and creates a definition 4043 * list item whose name is aTagName. 4044 * 4045 * @param aTagName Must be nsGkAtoms::dt or nsGkAtoms::dd. 4046 */ 4047 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 4048 MakeDefinitionListItemWithTransaction(nsAtom& aTagName); 4049 4050 /** 4051 * FormatBlockContainerAsSubAction() inserts a block element whose name 4052 * is aTagName at selection. If selection is not collapsed and aTagName is 4053 * nsGkAtoms::normal or nsGkAtoms::_empty, this removes block containers. 4054 * 4055 * @param aTagName A block level element name. Must NOT be 4056 * nsGkAtoms::dt nor nsGkAtoms::dd. 4057 * @param aFormatBlockMode Whether HTML formatBlock command or XUL 4058 * paragraphState command. 4059 * @param aEditingHost The editing host. 4060 */ 4061 MOZ_CAN_RUN_SCRIPT nsresult FormatBlockContainerAsSubAction( 4062 const nsStaticAtom& aTagName, FormatBlockMode aFormatBlockMode, 4063 const Element& aEditingHost); 4064 4065 /** 4066 * Increase/decrease the font size of selection. 4067 */ 4068 MOZ_CAN_RUN_SCRIPT nsresult 4069 IncrementOrDecrementFontSizeAsSubAction(FontSize aIncrementOrDecrement); 4070 4071 /** 4072 * Wrap aContent in <big> or <small> element and make children of 4073 * <font size=n> wrap with <big> or <small> too. Note that if there is 4074 * opposite element for aIncrementOrDecrement, their children will be just 4075 * unwrapped. 4076 * 4077 * @param aDir Whether increase or decrease the font size of aContent. 4078 * @param aContent The content node whose font size will be changed. 4079 * @return A suggest point to put caret. 4080 */ 4081 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 4082 SetFontSizeWithBigOrSmallElement(nsIContent& aContent, 4083 FontSize aIncrementOrDecrement); 4084 4085 /** 4086 * Adjust font size of font element children recursively with handling 4087 * <big> and <small> elements. 4088 * 4089 * @param aDir Whether increase or decrease the font size of aContent. 4090 * @param aContent The content node whose font size will be changed. 4091 * All descendants will be handled recursively. 4092 * @return A suggest point to put caret. 4093 */ 4094 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 4095 SetFontSizeOfFontElementChildren(nsIContent& aContent, 4096 FontSize aIncrementOrDecrement); 4097 4098 /** 4099 * Get extended range to select element whose all children are selected by 4100 * aRange. 4101 */ 4102 EditorRawDOMRange GetExtendedRangeWrappingEntirelySelectedElements( 4103 const EditorRawDOMRange& aRange) const; 4104 4105 /** 4106 * Get extended range to select ancestor <a name> elements. 4107 */ 4108 EditorRawDOMRange GetExtendedRangeWrappingNamedAnchor( 4109 const EditorRawDOMRange& aRange) const; 4110 4111 // Declared in HTMLEditorNestedClasses.h and defined in HTMLStyleEditor.cpp 4112 class AutoInlineStyleSetter; 4113 4114 /** 4115 * RemoveStyleInside() removes elements which represent aStyleToRemove 4116 * and removes CSS style. This handles aElement and all its descendants 4117 * (including leaf text nodes) recursively. 4118 * TODO: Rename this to explain that this maybe remove aElement from the DOM 4119 * tree. 4120 * 4121 * @param aSpecifiedStyle Whether the class and style attributes should 4122 * be preserved or discarded. 4123 * @return A suggest point to put caret. 4124 */ 4125 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 4126 RemoveStyleInside(Element& aElement, const EditorInlineStyle& aStyleToRemove, 4127 SpecifiedStyle aSpecifiedStyle); 4128 4129 /** 4130 * CollectEditableLeafTextNodes() collects text nodes in aElement. 4131 */ 4132 void CollectEditableLeafTextNodes( 4133 Element& aElement, nsTArray<OwningNonNull<Text>>& aLeafTextNodes) const; 4134 4135 /** 4136 * IsRemovableParentStyleWithNewSpanElement() checks whether aStyle of parent 4137 * block can be removed from aContent with creating `<span>` element. Note 4138 * that this does NOT check whether the specified style comes from parent 4139 * block or not. 4140 * XXX This may destroy the editor, but using `Result<bool, nsresult>` 4141 * is not reasonable because code for accessing the result becomes 4142 * messy. However, anybody must forget to check `Destroyed()` after 4143 * calling this. Which is the way to smart to make every caller 4144 * must check the editor state? 4145 */ 4146 MOZ_CAN_RUN_SCRIPT Result<bool, nsresult> 4147 IsRemovableParentStyleWithNewSpanElement( 4148 nsIContent& aContent, const EditorInlineStyle& aStyle) const; 4149 4150 /** 4151 * HasStyleOrIdOrClassAttribute() returns true when at least one of 4152 * `style`, `id` or `class` attribute value of aElement is not empty. 4153 */ 4154 static bool HasStyleOrIdOrClassAttribute(Element& aElement); 4155 4156 /** 4157 * Whether the outer window of the DOM event target has focus or not. 4158 */ 4159 bool OurWindowHasFocus() const; 4160 4161 class HTMLWithContextInserter; 4162 4163 /** 4164 * This function is used to insert a string of HTML input optionally with some 4165 * context information into the editable field. The HTML input either comes 4166 * from a transferable object created as part of a drop/paste operation, or 4167 * from the InsertHTML method. We may want the HTML input to be sanitized 4168 * (for example, if it's coming from a transferable object), in which case 4169 * aTrustedInput should be set to false, otherwise, the caller should set it 4170 * to true, which means that the HTML will be inserted in the DOM verbatim. 4171 */ 4172 enum class InlineStylesAtInsertionPoint { 4173 Preserve, // If you want the paste to be affected by local style, e.g., 4174 // for the insertHTML command, use "Preserve" 4175 Clear, // If you want the paste to be keep its own style, e.g., pasting 4176 // from clipboard, use "Clear" 4177 }; 4178 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertHTMLWithContextAsSubAction( 4179 const nsAString& aInputString, const nsAString& aContextStr, 4180 const nsAString& aInfoStr, const nsAString& aFlavor, 4181 SafeToInsertData aSafeToInsertData, const EditorDOMPoint& aPointToInsert, 4182 DeleteSelectedContent aDeleteSelectedContent, 4183 InlineStylesAtInsertionPoint aInlineStylesAtInsertionPoint, 4184 const Element& aEditingHost); 4185 4186 /** 4187 * sets the position of an element; warning it does NOT check if the 4188 * element is already positioned or not and that's on purpose. 4189 * @param aStyledElement [IN] the element 4190 * @param aX [IN] the x position in pixels. 4191 * @param aY [IN] the y position in pixels. 4192 */ 4193 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetTopAndLeftWithTransaction( 4194 nsStyledElement& aStyledElement, int32_t aX, int32_t aY); 4195 4196 /** 4197 * Reset a selected cell or collapsed selection (the caret) after table 4198 * editing. 4199 * 4200 * @param aTable A table in the document. 4201 * @param aRow The row ... 4202 * @param aCol ... and column defining the cell where we will try to 4203 * place the caret. 4204 * @param aSelected If true, we select the whole cell instead of setting 4205 * caret. 4206 * @param aDirection If cell at (aCol, aRow) is not found, search for 4207 * previous cell in the same column (aPreviousColumn) or 4208 * row (ePreviousRow) or don't search for another cell 4209 * (aNoSearch). If no cell is found, caret is place just 4210 * before table; and if that fails, at beginning of 4211 * document. Thus we generally don't worry about the 4212 * return value and can use the 4213 * AutoSelectionSetterAfterTableEdit stack-based object to 4214 * insure we reset the caret in a table-editing method. 4215 */ 4216 MOZ_CAN_RUN_SCRIPT void SetSelectionAfterTableEdit(Element* aTable, 4217 int32_t aRow, int32_t aCol, 4218 int32_t aDirection, 4219 bool aSelected); 4220 4221 void RemoveListenerAndDeleteRef(const nsAString& aEvent, 4222 nsIDOMEventListener* aListener, 4223 bool aUseCapture, ManualNACPtr aElement, 4224 PresShell* aPresShell); 4225 void DeleteRefToAnonymousNode(ManualNACPtr aContent, PresShell* aPresShell); 4226 4227 /** 4228 * RefreshEditingUI() may refresh editing UIs for current Selection, focus, 4229 * etc. If this shows or hides some UIs, it causes reflow. So, this is 4230 * not safe method. 4231 */ 4232 MOZ_CAN_RUN_SCRIPT nsresult RefreshEditingUI(); 4233 4234 /** 4235 * Returns the offset of an element's frame to its absolute containing block. 4236 */ 4237 nsresult GetElementOrigin(Element& aElement, int32_t& aX, int32_t& aY); 4238 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetPositionAndDimensions( 4239 Element& aElement, int32_t& aX, int32_t& aY, int32_t& aW, int32_t& aH, 4240 int32_t& aBorderLeft, int32_t& aBorderTop, int32_t& aMarginLeft, 4241 int32_t& aMarginTop); 4242 4243 bool IsInObservedSubtree(nsIContent* aChild); 4244 4245 void UpdateRootElement(); 4246 4247 /** 4248 * SetAllResizersPosition() moves all resizers to proper position. 4249 * If the resizers are hidden or replaced with another set of resizers 4250 * while this is running, this returns error. So, callers shouldn't 4251 * keep handling the resizers if this returns error. 4252 */ 4253 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetAllResizersPosition(); 4254 4255 /** 4256 * Shows active resizers around an element's frame 4257 * @param aResizedElement [IN] a DOM Element 4258 */ 4259 MOZ_CAN_RUN_SCRIPT nsresult ShowResizersInternal(Element& aResizedElement); 4260 4261 /** 4262 * Hide resizers if they are visible. If this is called while there is no 4263 * visible resizers, this does not return error, but does nothing. 4264 */ 4265 nsresult HideResizersInternal(); 4266 4267 /** 4268 * RefreshResizersInternal() moves resizers to proper position. This does 4269 * nothing if there is no resizing target. 4270 */ 4271 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RefreshResizersInternal(); 4272 4273 ManualNACPtr CreateResizer(int16_t aLocation, nsIContent& aParentContent); 4274 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 4275 SetAnonymousElementPositionWithoutTransaction(nsStyledElement& aStyledElement, 4276 int32_t aX, int32_t aY); 4277 4278 ManualNACPtr CreateShadow(nsIContent& aParentContent, 4279 Element& aOriginalObject); 4280 4281 /** 4282 * SetShadowPosition() moves the shadow element to proper position. 4283 * 4284 * @param aShadowElement Must be mResizingShadow or mPositioningShadow. 4285 * @param aElement The element which has the shadow. 4286 * @param aElementX Left of aElement. 4287 * @param aElementY Top of aElement. 4288 */ 4289 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 4290 SetShadowPosition(Element& aShadowElement, Element& aElement, 4291 int32_t aElementLeft, int32_t aElementTop); 4292 4293 ManualNACPtr CreateResizingInfo(nsIContent& aParentContent); 4294 MOZ_CAN_RUN_SCRIPT nsresult SetResizingInfoPosition(int32_t aX, int32_t aY, 4295 int32_t aW, int32_t aH); 4296 4297 enum class ResizeAt { 4298 eX, 4299 eY, 4300 eWidth, 4301 eHeight, 4302 }; 4303 [[nodiscard]] int32_t GetNewResizingIncrement(int32_t aX, int32_t aY, 4304 ResizeAt aResizeAt) const; 4305 4306 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult StartResizing(Element& aHandle); 4307 int32_t GetNewResizingX(int32_t aX, int32_t aY); 4308 int32_t GetNewResizingY(int32_t aX, int32_t aY); 4309 int32_t GetNewResizingWidth(int32_t aX, int32_t aY); 4310 int32_t GetNewResizingHeight(int32_t aX, int32_t aY); 4311 void HideShadowAndInfo(); 4312 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 4313 SetFinalSizeWithTransaction(int32_t aX, int32_t aY); 4314 void SetResizeIncrements(int32_t aX, int32_t aY, int32_t aW, int32_t aH, 4315 bool aPreserveRatio); 4316 4317 /** 4318 * HideAnonymousEditingUIs() forcibly hides all editing UIs (resizers, 4319 * inline-table-editing UI, absolute positioning UI). 4320 * 4321 * XXX This method is called by the CC, therefore, needs to be a boundary 4322 * method. 4323 */ 4324 MOZ_CAN_RUN_SCRIPT_BOUNDARY void HideAnonymousEditingUIs(); 4325 4326 /** 4327 * HideAnonymousEditingUIsIfUnnecessary() hides all editing UIs if some of 4328 * visible UIs are now unnecessary. 4329 */ 4330 MOZ_CAN_RUN_SCRIPT void HideAnonymousEditingUIsIfUnnecessary(); 4331 4332 /** 4333 * sets the z-index of an element. 4334 * @param aElement [IN] the element 4335 * @param aZorder [IN] the z-index 4336 */ 4337 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 4338 SetZIndexWithTransaction(nsStyledElement& aElement, int32_t aZIndex); 4339 4340 /** 4341 * shows a grabber attached to an arbitrary element. The grabber is an image 4342 * positioned on the left hand side of the top border of the element. Draggin 4343 * and dropping it allows to change the element's absolute position in the 4344 * document. See chrome://editor/content/images/grabber.gif 4345 * @param aElement [IN] the element 4346 */ 4347 MOZ_CAN_RUN_SCRIPT nsresult ShowGrabberInternal(Element& aElement); 4348 4349 /** 4350 * Setting grabber to proper position for current mAbsolutelyPositionedObject. 4351 * For example, while an element has grabber, the element may be resized 4352 * or repositioned by script or something. Then, you need to reset grabber 4353 * position with this. 4354 */ 4355 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RefreshGrabberInternal(); 4356 4357 /** 4358 * hide the grabber if it shown. 4359 */ 4360 MOZ_CAN_RUN_SCRIPT void HideGrabberInternal(); 4361 4362 /** 4363 * CreateGrabberInternal() creates a grabber for moving aParentContent. 4364 * This sets mGrabber to the new grabber. If this returns true, it's 4365 * always non-nullptr. Otherwise, i.e., the grabber is hidden during 4366 * creation, this returns false. 4367 */ 4368 bool CreateGrabberInternal(nsIContent& aParentContent); 4369 4370 MOZ_CAN_RUN_SCRIPT nsresult StartMoving(); 4371 MOZ_CAN_RUN_SCRIPT nsresult SetFinalPosition(int32_t aX, int32_t aY); 4372 void SnapToGrid(int32_t& newX, int32_t& newY) const; 4373 nsresult GrabberClicked(); 4374 MOZ_CAN_RUN_SCRIPT nsresult EndMoving(); 4375 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 4376 GetTemporaryStyleForFocusedPositionedElement(Element& aElement, 4377 nsAString& aReturn); 4378 4379 /** 4380 * Shows inline table editing UI around a <table> element which contains 4381 * aCellElement. This returns error if creating UI is hidden during this, 4382 * or detects another set of UI during this. In such case, callers 4383 * shouldn't keep handling anything for the UI. 4384 * 4385 * @param aCellElement Must be an <td> or <th> element. 4386 */ 4387 MOZ_CAN_RUN_SCRIPT nsresult 4388 ShowInlineTableEditingUIInternal(Element& aCellElement); 4389 4390 /** 4391 * Hide all inline table editing UI. 4392 */ 4393 void HideInlineTableEditingUIInternal(); 4394 4395 /** 4396 * RefreshInlineTableEditingUIInternal() moves inline table editing UI to 4397 * proper position. This returns error if the UI is hidden or replaced 4398 * during moving. 4399 */ 4400 [[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult 4401 RefreshInlineTableEditingUIInternal(); 4402 4403 enum class ContentNodeIs { Inserted, Appended }; 4404 MOZ_CAN_RUN_SCRIPT void DoContentInserted(nsIContent* aChild, 4405 ContentNodeIs aContentNodeIs); 4406 4407 /** 4408 * Returns an anonymous Element of type aTag, 4409 * child of aParentContent. If aIsCreatedHidden is true, the class 4410 * "hidden" is added to the created element. If aClass is not the empty 4411 * string, it becomes the value of the class attribute 4412 * @return a Element 4413 * @param aTag [IN] desired type of the element to create 4414 * @param aParentContent [IN] the parent node of the created anonymous 4415 * element 4416 * @param aClass [IN] contents of the _moz_anonclass attribute 4417 * @param aIsCreatedHidden [IN] a boolean specifying if the class "hidden" 4418 * is to be added to the created anonymous 4419 * element 4420 */ 4421 ManualNACPtr CreateAnonymousElement(nsAtom* aTag, nsIContent& aParentContent, 4422 const nsAString& aClass, 4423 bool aIsCreatedHidden); 4424 4425 /** 4426 * Reads a blob into memory and notifies the BlobReader object when the read 4427 * operation is finished. 4428 * 4429 * @param aBlob The input blob 4430 * @param aGlobal The global object under which the read should happen. 4431 * @param aBlobReader The blob reader object to be notified when finished. 4432 */ 4433 static nsresult SlurpBlob(dom::Blob* aBlob, nsIGlobalObject* aGlobal, 4434 BlobReader* aBlobReader); 4435 4436 /** 4437 * For saving allocation cost in the constructor of 4438 * EditorBase::TopLevelEditSubActionData, we should reuse same RangeItem 4439 * instance with all top level edit sub actions. 4440 * The instance is always cleared when TopLevelEditSubActionData is 4441 * destructed and the class is stack only class so that we don't need 4442 * to (and also should not) add the RangeItem into the cycle collection. 4443 */ 4444 [[nodiscard]] inline already_AddRefed<RangeItem> 4445 GetSelectedRangeItemForTopLevelEditSubAction() const; 4446 4447 /** 4448 * For saving allocation cost in the constructor of 4449 * EditorBase::TopLevelEditSubActionData, we should reuse same nsRange 4450 * instance with all top level edit sub actions. 4451 * The instance is always cleared when TopLevelEditSubActionData is 4452 * destructed, but AbstractRange::mOwner keeps grabbing the owner document 4453 * so that we need to make it in the cycle collection. 4454 */ 4455 [[nodiscard]] inline already_AddRefed<nsRange> 4456 GetChangedRangeForTopLevelEditSubAction() const; 4457 4458 MOZ_CAN_RUN_SCRIPT void DidDoTransaction( 4459 TransactionManager& aTransactionManager, nsITransaction& aTransaction, 4460 nsresult aDoTransactionResult) { 4461 if (mComposerCommandsUpdater) { 4462 RefPtr<ComposerCommandsUpdater> updater(mComposerCommandsUpdater); 4463 updater->DidDoTransaction(aTransactionManager); 4464 } 4465 } 4466 4467 MOZ_CAN_RUN_SCRIPT void DidUndoTransaction( 4468 TransactionManager& aTransactionManager, nsITransaction& aTransaction, 4469 nsresult aUndoTransactionResult) { 4470 if (mComposerCommandsUpdater) { 4471 RefPtr<ComposerCommandsUpdater> updater(mComposerCommandsUpdater); 4472 updater->DidUndoTransaction(aTransactionManager); 4473 } 4474 } 4475 4476 MOZ_CAN_RUN_SCRIPT void DidRedoTransaction( 4477 TransactionManager& aTransactionManager, nsITransaction& aTransaction, 4478 nsresult aRedoTransactionResult) { 4479 if (mComposerCommandsUpdater) { 4480 RefPtr<ComposerCommandsUpdater> updater(mComposerCommandsUpdater); 4481 updater->DidRedoTransaction(aTransactionManager); 4482 } 4483 } 4484 4485 protected: 4486 /** 4487 * IndentListChildWithTransaction() is a helper method of 4488 * Handle(CSS|HTML)IndentAtSelectionInternal(). 4489 * 4490 * @param aSubListElement [in/out] Specify a sub-list element of the 4491 * container of aPointInListElement or nullptr. 4492 * When there is no proper sub-list element to 4493 * move aContentMovingToSubList, this method 4494 * inserts a new sub-list element and update this 4495 * to it. 4496 * @param aPointInListElement A point in a list element whose child should 4497 * be indented. If this method creates new list 4498 * element into the list element, this inserts 4499 * the new list element to this point. 4500 * @param aContentMovingToSubList 4501 * A content node which is a child of a list 4502 * element and should be moved into a sub-list 4503 * element. 4504 * @param aEditingHost The editing host. 4505 * @return A candidate caret position. 4506 */ 4507 [[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> 4508 IndentListChildWithTransaction(RefPtr<Element>* aSubListElement, 4509 const EditorDOMPoint& aPointInListElement, 4510 nsIContent& aContentMovingToSubList, 4511 const Element& aEditingHost); 4512 4513 /** 4514 * Stack based helper class for calling EditorBase::EndTransactionInternal(). 4515 * NOTE: This does not suppress multiple input events. In most cases, 4516 * only one "input" event should be fired for an edit action rather 4517 * than per edit sub-action. In such case, you should use 4518 * EditorBase::AutoPlaceholderBatch instead. 4519 */ 4520 class MOZ_RAII AutoTransactionBatch final { 4521 public: 4522 /** 4523 * @param aRequesterFuncName function name which wants to end the batch. 4524 * This won't be stored nor exposed to selection listeners etc, used only 4525 * for logging. This MUST be alive when the destructor runs. 4526 */ 4527 MOZ_CAN_RUN_SCRIPT explicit AutoTransactionBatch( 4528 HTMLEditor& aHTMLEditor, const char* aRequesterFuncName) 4529 : mHTMLEditor(aHTMLEditor), mRequesterFuncName(aRequesterFuncName) { 4530 MOZ_KnownLive(mHTMLEditor).BeginTransactionInternal(mRequesterFuncName); 4531 } 4532 4533 MOZ_CAN_RUN_SCRIPT ~AutoTransactionBatch() { 4534 MOZ_KnownLive(mHTMLEditor).EndTransactionInternal(mRequesterFuncName); 4535 } 4536 4537 protected: 4538 // The lifetime must be guaranteed by the creator of this instance. 4539 MOZ_KNOWN_LIVE HTMLEditor& mHTMLEditor; 4540 const char* const mRequesterFuncName; 4541 }; 4542 4543 RefPtr<PendingStyles> mPendingStylesToApplyToNewContent; 4544 RefPtr<ComposerCommandsUpdater> mComposerCommandsUpdater; 4545 4546 // Used by TopLevelEditSubActionData::mSelectedRange. 4547 mutable RefPtr<RangeItem> mSelectedRangeForTopLevelEditSubAction; 4548 // Used by TopLevelEditSubActionData::mChangedRange. 4549 mutable RefPtr<nsRange> mChangedRangeForTopLevelEditSubAction; 4550 4551 RefPtr<Runnable> mPendingRootElementUpdatedRunner; 4552 RefPtr<DocumentModifiedEvent> mPendingDocumentModifiedRunner; 4553 4554 // mPaddingBRElementForEmptyEditor should be used for placing caret 4555 // at proper position when editor is empty. 4556 RefPtr<dom::HTMLBRElement> mPaddingBRElementForEmptyEditor; 4557 4558 // This is set only when HandleInsertText appended a collapsible white-space. 4559 RefPtr<dom::Text> mLastCollapsibleWhiteSpaceAppendedTextNode; 4560 4561 // While this instance or its helper class updates the DOM with a DOM API, 4562 // this is set to the wrapper class to call the DOM API. 4563 const AutoDOMAPIWrapperBase* mRunningDOMAPIWrapper = nullptr; 4564 4565 bool mCRInParagraphCreatesParagraph; 4566 4567 // resizing 4568 bool mIsObjectResizingEnabled; 4569 bool mIsResizing; 4570 bool mPreserveRatio; 4571 bool mResizedObjectIsAnImage; 4572 4573 // absolute positioning 4574 bool mIsAbsolutelyPositioningEnabled; 4575 bool mResizedObjectIsAbsolutelyPositioned; 4576 bool mGrabberClicked; 4577 bool mIsMoving; 4578 4579 bool mSnapToGridEnabled; 4580 4581 // inline table editing 4582 bool mIsInlineTableEditingEnabled; 4583 4584 bool mIsCSSPrefChecked; 4585 4586 // resizing 4587 ManualNACPtr mTopLeftHandle; 4588 ManualNACPtr mTopHandle; 4589 ManualNACPtr mTopRightHandle; 4590 ManualNACPtr mLeftHandle; 4591 ManualNACPtr mRightHandle; 4592 ManualNACPtr mBottomLeftHandle; 4593 ManualNACPtr mBottomHandle; 4594 ManualNACPtr mBottomRightHandle; 4595 4596 RefPtr<Element> mActivatedHandle; 4597 4598 ManualNACPtr mResizingShadow; 4599 ManualNACPtr mResizingInfo; 4600 4601 RefPtr<Element> mResizedObject; 4602 4603 int32_t mOriginalX; 4604 int32_t mOriginalY; 4605 4606 int32_t mResizedObjectX; 4607 int32_t mResizedObjectY; 4608 int32_t mResizedObjectWidth; 4609 int32_t mResizedObjectHeight; 4610 4611 int32_t mResizedObjectMarginLeft; 4612 int32_t mResizedObjectMarginTop; 4613 int32_t mResizedObjectBorderLeft; 4614 int32_t mResizedObjectBorderTop; 4615 4616 int32_t mXIncrementFactor; 4617 int32_t mYIncrementFactor; 4618 int32_t mWidthIncrementFactor; 4619 int32_t mHeightIncrementFactor; 4620 4621 int8_t mInfoXIncrement; 4622 int8_t mInfoYIncrement; 4623 4624 // absolute positioning 4625 int32_t mPositionedObjectX; 4626 int32_t mPositionedObjectY; 4627 int32_t mPositionedObjectWidth; 4628 int32_t mPositionedObjectHeight; 4629 4630 int32_t mPositionedObjectMarginLeft; 4631 int32_t mPositionedObjectMarginTop; 4632 int32_t mPositionedObjectBorderLeft; 4633 int32_t mPositionedObjectBorderTop; 4634 4635 RefPtr<Element> mAbsolutelyPositionedObject; 4636 ManualNACPtr mGrabber; 4637 ManualNACPtr mPositioningShadow; 4638 4639 int32_t mGridSize; 4640 4641 // inline table editing 4642 RefPtr<Element> mInlineEditedCell; 4643 4644 ManualNACPtr mAddColumnBeforeButton; 4645 ManualNACPtr mRemoveColumnButton; 4646 ManualNACPtr mAddColumnAfterButton; 4647 4648 ManualNACPtr mAddRowBeforeButton; 4649 ManualNACPtr mRemoveRowButton; 4650 ManualNACPtr mAddRowAfterButton; 4651 4652 void AddPointerClickListener(Element* aElement); 4653 void RemovePointerClickListener(Element* aElement); 4654 4655 bool mDisabledLinkHandling = false; 4656 bool mOldLinkHandlingEnabled = false; 4657 4658 bool mHasBeforeInputBeenCanceled = false; 4659 4660 bool mHasFocus = false; 4661 bool mIsInDesignMode = false; 4662 4663 ParagraphSeparator mDefaultParagraphSeparator; 4664 4665 friend class AlignStateAtSelection; // CollectEditableTargetNodes, 4666 // CollectNonEditableNodes 4667 friend class AutoClonedRangeArray; // RangeUpdaterRef, 4668 // SplitNodeWithTransaction, 4669 // SplitInlineAncestorsAtRangeBoundaries 4670 friend class AutoDOMAPIWrapperBase; // OnDOMAPICallEnd, 4671 // OnDOMAPICallStart, 4672 friend class AutoClonedSelectionRangeArray; // RangeUpdaterRef, 4673 friend class AutoSelectionRestore; 4674 friend class AutoSelectionSetterAfterTableEdit; // SetSelectionAfterEdit 4675 friend class CSSEditUtils; // DoTransactionInternal, HasAttributes, 4676 // RemoveContainerWithTransaction 4677 friend class EditorBase; // ComputeTargetRanges, 4678 // GetChangedRangeForTopLevelEditSubAction, 4679 // GetSelectedRangeItemForTopLevelEditSubAction, 4680 // MaybeCreatePaddingBRElementForEmptyEditor, 4681 // PrepareToInsertBRElement, 4682 // ReflectPaddingBRElementForEmptyEditor, 4683 // RefreshEditingUI, 4684 // mComposerUpdater, mHasBeforeInputBeenCanceled 4685 friend class JoinNodesTransaction; // DidJoinNodesTransaction, DoJoinNodes, 4686 // DoSplitNode, // RangeUpdaterRef 4687 friend class ListElementSelectionState; // CollectEditTargetNodes, 4688 // CollectNonEditableNodes 4689 friend class ListItemElementSelectionState; // CollectEditTargetNodes, 4690 // CollectNonEditableNodes 4691 friend class MoveNodeTransaction; // AllowsTransactionsToChangeSelection, 4692 // CollapseSelectionTo, RangeUpdaterRef 4693 friend class MoveSiblingsTransaction; // AllowsTransactionsToChangeSelection, 4694 // CollapseSelectionTo, RangeUpdaterRef 4695 friend class ParagraphStateAtSelection; // CollectChildren, 4696 // CollectEditTargetNodes, 4697 // CollectListChildren, 4698 // CollectNonEditableNodes, 4699 // CollectTableChildren 4700 friend class SlurpBlobEventListener; // BlobReader 4701 friend class SplitNodeTransaction; // DoJoinNodes, DoSplitNode 4702 friend class TransactionManager; // DidDoTransaction, DidRedoTransaction, 4703 // DidUndoTransaction 4704 friend class 4705 WhiteSpaceVisibilityKeeper; // AutoMoveOneLineHandler 4706 // CanMoveChildren, 4707 // ChangeListElementType, 4708 // DeleteNodeWithTransaction, 4709 // DeleteTextAndTextNodesWithTransaction, 4710 // InsertLineBreak, 4711 // JoinNearestEditableNodesWithTransaction, 4712 // LineBreakType, 4713 // MoveChildrenWithTransaction, 4714 // SplitAncestorStyledInlineElementsAt, 4715 // TreatEmptyTextNodes 4716 }; 4717 4718 /** 4719 * ListElementSelectionState class gets which list element is selected right 4720 * now. 4721 */ 4722 class MOZ_STACK_CLASS ListElementSelectionState final { 4723 public: 4724 ListElementSelectionState() = delete; 4725 ListElementSelectionState(HTMLEditor& aHTMLEditor, ErrorResult& aRv); 4726 4727 bool IsOLElementSelected() const { return mIsOLElementSelected; } 4728 bool IsULElementSelected() const { return mIsULElementSelected; } 4729 bool IsDLElementSelected() const { return mIsDLElementSelected; } 4730 bool IsNotOneTypeListElementSelected() const { 4731 return (mIsOLElementSelected + mIsULElementSelected + mIsDLElementSelected + 4732 mIsOtherContentSelected) > 1; 4733 } 4734 4735 private: 4736 bool mIsOLElementSelected = false; 4737 bool mIsULElementSelected = false; 4738 bool mIsDLElementSelected = false; 4739 bool mIsOtherContentSelected = false; 4740 }; 4741 4742 /** 4743 * ListItemElementSelectionState class gets which list item element is selected 4744 * right now. 4745 */ 4746 class MOZ_STACK_CLASS ListItemElementSelectionState final { 4747 public: 4748 ListItemElementSelectionState() = delete; 4749 ListItemElementSelectionState(HTMLEditor& aHTMLEditor, ErrorResult& aRv); 4750 4751 bool IsLIElementSelected() const { return mIsLIElementSelected; } 4752 bool IsDTElementSelected() const { return mIsDTElementSelected; } 4753 bool IsDDElementSelected() const { return mIsDDElementSelected; } 4754 bool IsNotOneTypeDefinitionListItemElementSelected() const { 4755 return (mIsDTElementSelected + mIsDDElementSelected + 4756 mIsOtherElementSelected) > 1; 4757 } 4758 4759 private: 4760 bool mIsLIElementSelected = false; 4761 bool mIsDTElementSelected = false; 4762 bool mIsDDElementSelected = false; 4763 bool mIsOtherElementSelected = false; 4764 }; 4765 4766 /** 4767 * AlignStateAtSelection class gets alignment at selection. 4768 * XXX This currently returns only first alignment. 4769 */ 4770 class MOZ_STACK_CLASS AlignStateAtSelection final { 4771 public: 4772 AlignStateAtSelection() = delete; 4773 MOZ_CAN_RUN_SCRIPT AlignStateAtSelection(HTMLEditor& aHTMLEditor, 4774 ErrorResult& aRv); 4775 4776 nsIHTMLEditor::EAlignment AlignmentAtSelectionStart() const { 4777 return mFirstAlign; 4778 } 4779 bool IsSelectionRangesFound() const { return mFoundSelectionRanges; } 4780 4781 private: 4782 nsIHTMLEditor::EAlignment mFirstAlign = nsIHTMLEditor::eLeft; 4783 bool mFoundSelectionRanges = false; 4784 }; 4785 4786 /** 4787 * ParagraphStateAtSelection class gets format block types around selection. 4788 */ 4789 class MOZ_STACK_CLASS ParagraphStateAtSelection final { 4790 public: 4791 using FormatBlockMode = HTMLEditor::FormatBlockMode; 4792 4793 ParagraphStateAtSelection() = delete; 4794 /** 4795 * @param aFormatBlockMode Whether HTML formatBlock command or XUL 4796 * paragraphState command. 4797 */ 4798 ParagraphStateAtSelection(HTMLEditor& aHTMLEditor, 4799 FormatBlockMode aFormatBlockMode, ErrorResult& aRv); 4800 4801 /** 4802 * GetFirstParagraphStateAtSelection() returns: 4803 * - nullptr if there is no format blocks nor inline nodes. 4804 * - nsGkAtoms::_empty if first node is not in any format block. 4805 * - a tag name of format block at first node. 4806 * XXX See the private method explanations. If selection ranges contains 4807 * non-format block first, it'll be check after its siblings. Therefore, 4808 * this may return non-first paragraph state. 4809 */ 4810 nsAtom* GetFirstParagraphStateAtSelection() const { 4811 return mIsMixed && mIsInDLElement ? nsGkAtoms::dl 4812 : mFirstParagraphState.get(); 4813 } 4814 4815 /** 4816 * If selected nodes are not in same format node nor only in no-format blocks, 4817 * this returns true. 4818 */ 4819 bool IsMixed() const { return mIsMixed && !mIsInDLElement; } 4820 4821 private: 4822 using EditorType = EditorBase::EditorType; 4823 4824 [[nodiscard]] static bool IsFormatElement(FormatBlockMode aFormatBlockMode, 4825 const nsIContent& aContent); 4826 4827 /** 4828 * AppendDescendantFormatNodesAndFirstInlineNode() appends descendant 4829 * format blocks and first inline child node in aNonFormatBlockElement to 4830 * the last of the array (not inserting where aNonFormatBlockElement is, 4831 * so that the node order becomes randomly). 4832 * 4833 * @param aArrayOfContents [in/out] Found descendant format blocks 4834 * and first inline node in each non-format 4835 * block will be appended to this. 4836 * @param aFormatBlockMode Whether HTML formatBlock command or XUL 4837 * paragraphState command. 4838 * @param aNonFormatBlockElement Must be a non-format block element. 4839 */ 4840 static void AppendDescendantFormatNodesAndFirstInlineNode( 4841 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents, 4842 FormatBlockMode aFormatBlockMode, dom::Element& aNonFormatBlockElement); 4843 4844 /** 4845 * CollectEditableFormatNodesInSelection() collects only editable nodes 4846 * around selection ranges (with 4847 * AutoClonedRangeArray::ExtendRangesToWrapLines() and 4848 * HTMLEditor::CollectEditTargetNodes(), see its document for the detail). 4849 * If it includes list, list item or table related elements, they will be 4850 * replaced their children. 4851 * 4852 * @param aFormatBlockMode Whether HTML formatBlock command or XUL 4853 * paragraphState command. 4854 */ 4855 static nsresult CollectEditableFormatNodesInSelection( 4856 HTMLEditor& aHTMLEditor, FormatBlockMode aFormatBlockMode, 4857 const dom::Element& aEditingHost, 4858 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents); 4859 4860 RefPtr<nsAtom> mFirstParagraphState; 4861 bool mIsInDLElement = false; 4862 bool mIsMixed = false; 4863 }; 4864 4865 } // namespace mozilla 4866 4867 mozilla::HTMLEditor* nsIEditor::AsHTMLEditor() { 4868 MOZ_DIAGNOSTIC_ASSERT(IsHTMLEditor()); 4869 return static_cast<mozilla::HTMLEditor*>(this); 4870 } 4871 4872 const mozilla::HTMLEditor* nsIEditor::AsHTMLEditor() const { 4873 MOZ_DIAGNOSTIC_ASSERT(IsHTMLEditor()); 4874 return static_cast<const mozilla::HTMLEditor*>(this); 4875 } 4876 4877 mozilla::HTMLEditor* nsIEditor::GetAsHTMLEditor() { 4878 return AsEditorBase()->IsHTMLEditor() ? AsHTMLEditor() : nullptr; 4879 } 4880 4881 const mozilla::HTMLEditor* nsIEditor::GetAsHTMLEditor() const { 4882 return AsEditorBase()->IsHTMLEditor() ? AsHTMLEditor() : nullptr; 4883 } 4884 4885 #endif // #ifndef mozilla_HTMLEditor_h