Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_HTMLEditor_h
#define mozilla_HTMLEditor_h
#include "mozilla/Attributes.h"
#include "mozilla/ComposerCommandsUpdater.h"
#include "mozilla/CSSEditUtils.h"
#include "mozilla/EditorBase.h"
#include "mozilla/EditorForwards.h"
#include "mozilla/EditorUtils.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/HTMLEditHelpers.h"
#include "mozilla/ManualNAC.h"
#include "mozilla/Result.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/BlobImpl.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/File.h"
#include "nsAttrName.h"
#include "nsCOMPtr.h"
#include "nsIDocumentObserver.h"
#include "nsIDOMEventListener.h"
#include "nsIEditorMailSupport.h"
#include "nsIHTMLAbsPosEditor.h"
#include "nsIHTMLEditor.h"
#include "nsIHTMLInlineTableEditor.h"
#include "nsIHTMLObjectResizer.h"
#include "nsITableEditor.h"
#include "nsPoint.h"
#include "nsStubMutationObserver.h"
#include "nsTArray.h"
class nsDocumentFragment;
class nsFrameSelection;
class nsHTMLDocument;
class nsITransferable;
class nsIClipboard;
class nsRange;
class nsStaticAtom;
class nsStyledElement;
class nsTableCellFrame;
class nsTableWrapperFrame;
namespace mozilla {
class AlignStateAtSelection;
class AutoSelectionSetterAfterTableEdit;
class AutoSetTemporaryAncestorLimiter;
class EmptyEditableFunctor;
class ListElementSelectionState;
class ListItemElementSelectionState;
class ParagraphStateAtSelection;
class ResizerSelectionListener;
class Runnable;
template <class T>
class OwningNonNull;
namespace dom {
class AbstractRange;
class Blob;
class DocumentFragment;
class Event;
class HTMLBRElement;
class MouseEvent;
class StaticRange;
} // namespace dom
namespace widget {
struct IMEState;
} // namespace widget
enum class ParagraphSeparator { div, p, br };
/**
* The HTML editor implementation.<br>
* Use to edit HTML document represented as a DOM tree.
*/
class HTMLEditor final : public EditorBase,
public nsIHTMLEditor,
public nsIHTMLObjectResizer,
public nsIHTMLAbsPosEditor,
public nsITableEditor,
public nsIHTMLInlineTableEditor,
public nsStubMutationObserver,
public nsIEditorMailSupport {
public:
/****************************************************************************
* NOTE: DO NOT MAKE YOUR NEW METHODS PUBLIC IF they are called by other
* classes under libeditor except EditorEventListener and
* HTMLEditorEventListener because each public method which may fire
* eEditorInput event will need to instantiate new stack class for
* managing input type value of eEditorInput and cache some objects
* for smarter handling. In other words, when you add new root
* method to edit the DOM tree, you can make your new method public.
****************************************************************************/
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLEditor, EditorBase)
// nsStubMutationObserver overrides
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
// nsIHTMLEditor methods
NS_DECL_NSIHTMLEDITOR
// nsIHTMLObjectResizer methods (implemented in HTMLObjectResizer.cpp)
NS_DECL_NSIHTMLOBJECTRESIZER
// nsIHTMLAbsPosEditor methods (implemented in HTMLAbsPositionEditor.cpp)
NS_DECL_NSIHTMLABSPOSEDITOR
// nsIHTMLInlineTableEditor methods (implemented in HTMLInlineTableEditor.cpp)
NS_DECL_NSIHTMLINLINETABLEEDITOR
// nsIEditorMailSupport methods
NS_DECL_NSIEDITORMAILSUPPORT
// nsITableEditor methods
NS_DECL_NSITABLEEDITOR
// nsISelectionListener overrides
NS_DECL_NSISELECTIONLISTENER
HTMLEditor();
/**
* @param aDocument The document whose content will be editable.
* @param aComposerCommandsUpdater The composer command updater.
* @param aFlags Some of nsIEditor::eEditor*Mask flags.
*/
MOZ_CAN_RUN_SCRIPT nsresult
Init(Document& aDocument, ComposerCommandsUpdater& aComposerCommandsUpdater,
uint32_t aFlags);
/**
* PostCreate() should be called after Init, and is the time that the editor
* tells its documentStateObservers that the document has been created.
*/
MOZ_CAN_RUN_SCRIPT nsresult PostCreate();
/**
* PreDestroy() is called before the editor goes away, and gives the editor a
* chance to tell its documentStateObservers that the document is going away.
*/
MOZ_CAN_RUN_SCRIPT void PreDestroy();
static HTMLEditor* GetFrom(nsIEditor* aEditor) {
return aEditor ? aEditor->GetAsHTMLEditor() : nullptr;
}
static const HTMLEditor* GetFrom(const nsIEditor* aEditor) {
return aEditor ? aEditor->GetAsHTMLEditor() : nullptr;
}
[[nodiscard]] bool GetReturnInParagraphCreatesNewParagraph() const;
// EditorBase overrides
MOZ_CAN_RUN_SCRIPT NS_IMETHOD BeginningOfDocument() final;
NS_IMETHOD GetDocumentCharacterSet(nsACString& aCharacterSet) final;
MOZ_CAN_RUN_SCRIPT NS_IMETHOD
SetDocumentCharacterSet(const nsACString& aCharacterSet) final;
bool IsEmpty() const final;
bool CanPaste(int32_t aClipboardType) const final;
using EditorBase::CanPaste;
MOZ_CAN_RUN_SCRIPT nsresult PasteTransferableAsAction(
nsITransferable* aTransferable, nsIPrincipal* aPrincipal = nullptr) final;
MOZ_CAN_RUN_SCRIPT NS_IMETHOD DeleteNode(nsINode* aNode) final;
MOZ_CAN_RUN_SCRIPT NS_IMETHOD InsertLineBreak() final;
/**
* PreHandleMouseDown() and PreHandleMouseUp() are called before
* HTMLEditorEventListener handles them. The coming event may be
* non-acceptable event.
*/
void PreHandleMouseDown(const dom::MouseEvent& aMouseDownEvent);
void PreHandleMouseUp(const dom::MouseEvent& aMouseUpEvent);
/**
* PreHandleSelectionChangeCommand() and PostHandleSelectionChangeCommand()
* are called before or after handling a command which may change selection
* and/or scroll position.
*/
void PreHandleSelectionChangeCommand(Command aCommand);
void PostHandleSelectionChangeCommand(Command aCommand);
MOZ_CAN_RUN_SCRIPT nsresult
HandleKeyPressEvent(WidgetKeyboardEvent* aKeyboardEvent) final;
Element* GetFocusedElement() const final;
bool IsActiveInDOMWindow() const final;
dom::EventTarget* GetDOMEventTarget() const final;
[[nodiscard]] Element* FindSelectionRoot(const nsINode& aNode) const final;
bool IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent) const final;
nsresult GetPreferredIMEState(widget::IMEState* aState) final;
MOZ_CAN_RUN_SCRIPT nsresult
OnFocus(const nsINode& aOriginalEventTargetNode) final;
nsresult OnBlur(const dom::EventTarget* aEventTarget) final;
/**
* GetBackgroundColorState() returns what the background color of the
* selection.
*
* @param aMixed true if there is more than one font color
* @param aOutColor Color string. "" is returned for none.
*/
MOZ_CAN_RUN_SCRIPT nsresult GetBackgroundColorState(bool* aMixed,
nsAString& aOutColor);
MOZ_CAN_RUN_SCRIPT NS_IMETHOD Paste(int32_t aClipboardType) final {
const nsresult rv = HTMLEditor::PasteAsAction(aClipboardType, true);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"HTMLEditor::PasteAsAction() failed");
return rv;
}
MOZ_CAN_RUN_SCRIPT nsresult
PasteAsAction(int32_t aClipboardType, bool aDispatchPasteEvent,
nsIPrincipal* aPrincipal = nullptr) final;
/**
* PasteNoFormattingAsAction() pastes content in clipboard without any style
* information.
*
* @param aSelectionType nsIClipboard::kGlobalClipboard or
* nsIClipboard::kSelectionClipboard.
* @param aPrincipal Set subject principal if it may be called by
* JS. If set to nullptr, will be treated as
* called by system.
*/
MOZ_CAN_RUN_SCRIPT nsresult PasteNoFormattingAsAction(
int32_t aSelectionType, nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult
PasteAsQuotationAsAction(int32_t aClipboardType, bool aDispatchPasteEvent,
nsIPrincipal* aPrincipal = nullptr) final;
bool CanPasteTransferable(nsITransferable* aTransferable) final;
MOZ_CAN_RUN_SCRIPT nsresult
InsertLineBreakAsAction(nsIPrincipal* aPrincipal = nullptr) final;
/**
* InsertParagraphSeparatorAsAction() is called when user tries to separate
* current paragraph with Enter key press in HTMLEditor or something.
*
* @param aPrincipal Set subject principal if it may be called by
* JS. If set to nullptr, will be treated as
* called by system.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
InsertParagraphSeparatorAsAction(nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult
InsertElementAtSelectionAsAction(Element* aElement, bool aDeleteSelection,
nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult InsertLinkAroundSelectionAsAction(
Element* aAnchorElement, nsIPrincipal* aPrincipal = nullptr);
/**
* CreateElementWithDefaults() creates new element whose name is
* aTagName with some default attributes are set. Note that this is a
* public utility method. I.e., just creates element, not insert it
* into the DOM tree.
* NOTE: This is available for internal use too since this does not change
* the DOM tree nor undo transactions, and does not refer Selection,
* etc.
*
* @param aTagName The new element's tag name. If the name is
* one of "href", "anchor" or "namedanchor",
* this creates an <a> element.
* @return Newly created element.
*/
MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> CreateElementWithDefaults(
const nsAtom& aTagName);
/**
* Indent or outdent content around Selection.
*
* @param aPrincipal Set subject principal if it may be called by
* JS. If set to nullptr, will be treated as
* called by system.
*/
MOZ_CAN_RUN_SCRIPT nsresult
IndentAsAction(nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult
OutdentAsAction(nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult SetParagraphFormatAsAction(
const nsAString& aParagraphFormat, nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult AlignAsAction(const nsAString& aAlignType,
nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult RemoveListAsAction(
const nsAString& aListType, nsIPrincipal* aPrincipal = nullptr);
/**
* MakeOrChangeListAsAction() makes selected hard lines list element(s).
*
* @param aListElementTagName The new list element tag name. Must be
* nsGkAtoms::ul, nsGkAtoms::ol or
* nsGkAtoms::dl.
* @param aBulletType If this is not empty string, it's set
* to `type` attribute of new list item
* elements. Otherwise, existing `type`
* attributes will be removed.
* @param aSelectAllOfCurrentList Yes if this should treat all of
* ancestor list element at selection.
*/
enum class SelectAllOfCurrentList { Yes, No };
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MakeOrChangeListAsAction(
nsAtom& aListElementTagName, const nsAString& aBulletType,
SelectAllOfCurrentList aSelectAllOfCurrentList,
nsIPrincipal* aPrincipal = nullptr);
/**
* If aTargetElement is a resizer, start to drag the resizer. Otherwise, if
* aTargetElement is the grabber, start to handle drag gester on it.
*
* @param aMouseDownEvent A `mousedown` event fired on aTargetElement.
* @param aEventTargetElement The target element being pressed. This must
* be same as explicit original event target of
* aMouseDownEvent.
*/
MOZ_CAN_RUN_SCRIPT nsresult StartToDragResizerOrHandleDragGestureOnGrabber(
dom::MouseEvent& aMouseDownEvent, Element& aEventTargetElement);
/**
* If the editor is handling dragging a resizer, handling drag gesture on
* the grabber or dragging the grabber, this finalize it. Otherwise,
* does nothing.
*
* @param aClientPoint The final point of the drag.
*/
MOZ_CAN_RUN_SCRIPT nsresult
StopDraggingResizerOrGrabberAt(const CSSIntPoint& aClientPoint);
/**
* If the editor is handling dragging a resizer, handling drag gesture to
* start dragging the grabber or dragging the grabber, this method updates
* it's position.
*
* @param aClientPoint The new point of the drag.
*/
MOZ_CAN_RUN_SCRIPT nsresult
UpdateResizerOrGrabberPositionTo(const CSSIntPoint& aClientPoint);
/**
* IsCSSEnabled() returns true if this editor treats styles with style
* attribute of HTML elements. Otherwise, if this editor treats all styles
* with "font style elements" like <b>, <i>, etc, and <blockquote> to indent,
* align attribute to align contents, returns false.
*/
bool IsCSSEnabled() const {
return !IsMailEditor() && mCSSEditUtils &&
mCSSEditUtils->IsCSSPrefChecked();
}
/**
* Enable/disable object resizers for <img> elements, <table> elements,
* absolute positioned elements (required absolute position editor enabled).
*/
MOZ_CAN_RUN_SCRIPT void EnableObjectResizer(bool aEnable) {
if (mIsObjectResizingEnabled == aEnable) {
return;
}
AutoEditActionDataSetter editActionData(
*this, EditAction::eEnableOrDisableResizer);
if (NS_WARN_IF(!editActionData.CanHandle())) {
return;
}
mIsObjectResizingEnabled = aEnable;
RefreshEditingUI();
}
bool IsObjectResizerEnabled() const { return mIsObjectResizingEnabled; }
Element* GetResizerTarget() const { return mResizedObject; }
/**
* Enable/disable inline table editor, e.g., adding new row or column,
* removing existing row or column.
*/
MOZ_CAN_RUN_SCRIPT void EnableInlineTableEditor(bool aEnable) {
if (mIsInlineTableEditingEnabled == aEnable) {
return;
}
AutoEditActionDataSetter editActionData(
*this, EditAction::eEnableOrDisableInlineTableEditingUI);
if (NS_WARN_IF(!editActionData.CanHandle())) {
return;
}
mIsInlineTableEditingEnabled = aEnable;
RefreshEditingUI();
}
bool IsInlineTableEditorEnabled() const {
return mIsInlineTableEditingEnabled;
}
/**
* Enable/disable absolute position editor, resizing absolute positioned
* elements (required object resizers enabled) or positioning them with
* dragging grabber.
*/
MOZ_CAN_RUN_SCRIPT void EnableAbsolutePositionEditor(bool aEnable) {
if (mIsAbsolutelyPositioningEnabled == aEnable) {
return;
}
AutoEditActionDataSetter editActionData(
*this, EditAction::eEnableOrDisableAbsolutePositionEditor);
if (NS_WARN_IF(!editActionData.CanHandle())) {
return;
}
mIsAbsolutelyPositioningEnabled = aEnable;
RefreshEditingUI();
}
bool IsAbsolutePositionEditorEnabled() const {
return mIsAbsolutelyPositioningEnabled;
}
/**
* returns the deepest absolutely positioned container of the selection
* if it exists or null.
*/
MOZ_CAN_RUN_SCRIPT already_AddRefed<Element>
GetAbsolutelyPositionedSelectionContainer() const;
Element* GetPositionedElement() const { return mAbsolutelyPositionedObject; }
/**
* extracts the selection from the normal flow of the document and
* positions it.
*
* @param aEnabled [IN] true to absolutely position the selection,
* false to put it back in the normal flow
* @param aPrincipal Set subject principal if it may be called by
* JS. If set to nullptr, will be treated as
* called by system.
*/
MOZ_CAN_RUN_SCRIPT nsresult SetSelectionToAbsoluteOrStaticAsAction(
bool aEnabled, nsIPrincipal* aPrincipal = nullptr);
/**
* returns the absolute z-index of a positioned element. Never returns 'auto'
* @return the z-index of the element
* @param aElement [IN] the element.
*/
MOZ_CAN_RUN_SCRIPT int32_t GetZIndex(Element& aElement);
/**
* adds aChange to the z-index of the currently positioned element.
*
* @param aChange [IN] relative change to apply to current z-index
* @param aPrincipal Set subject principal if it may be called by
* JS. If set to nullptr, will be treated as
* called by system.
*/
MOZ_CAN_RUN_SCRIPT nsresult
AddZIndexAsAction(int32_t aChange, nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult SetBackgroundColorAsAction(
const nsAString& aColor, nsIPrincipal* aPrincipal = nullptr);
/**
* SetInlinePropertyAsAction() sets a property which changes inline style of
* text. E.g., bold, italic, super and sub.
* This automatically removes exclusive style, however, treats all changes
* as a transaction.
*
* @param aPrincipal Set subject principal if it may be called by
* JS. If set to nullptr, will be treated as
* called by system.
*/
MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertyAsAction(
nsAtom& aProperty, nsAtom* aAttribute, const nsAString& aValue,
nsIPrincipal* aPrincipal = nullptr);
/**
* GetInlineProperty() gets aggregate properties of the current selection.
* All object in the current selection are scanned and their attributes are
* represented in a list of Property object.
* TODO: Make this return Result<Something> instead of bool out arguments.
*
* @param aHTMLProperty the property to get on the selection
* @param aAttribute the attribute of the property, if applicable.
* May be null.
* Example: aHTMLProperty=nsGkAtoms::font,
* aAttribute=nsGkAtoms::color
* @param aValue if aAttribute is not null, the value of the
* attribute. May be null.
* Example: aHTMLProperty=nsGkAtoms::font,
* aAttribute=nsGkAtoms::color,
* aValue="0x00FFFF"
* @param aFirst [OUT] true if the first text node in the
* selection has the property
* @param aAny [OUT] true if any of the text nodes in the
* selection have the property
* @param aAll [OUT] true if all of the text nodes in the
* selection have the property
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlineProperty(
nsAtom* aHTMLProperty, nsAtom* aAttribute, const nsAString& aValue,
bool* aFirst, bool* aAny, bool* aAll) const;
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlinePropertyWithAttrValue(
nsAtom* aHTMLProperty, nsAtom* aAttribute, const nsAString& aValue,
bool* aFirst, bool* aAny, bool* aAll, nsAString& outValue);
/**
* RemoveInlinePropertyAsAction() removes a property which changes inline
* style of text. E.g., bold, italic, super and sub.
*
* @param aHTMLProperty Tag name whcih represents the inline style you want
* to remove. E.g., nsGkAtoms::strong, nsGkAtoms::b,
* etc. If nsGkAtoms::href, <a> element which has
* href attribute will be removed.
* If nsGkAtoms::name, <a> element which has non-empty
* name attribute will be removed.
* @param aAttribute If aHTMLProperty is nsGkAtoms::font, aAttribute should
* be nsGkAtoms::fase, nsGkAtoms::size, nsGkAtoms::color
* or nsGkAtoms::bgcolor. Otherwise, set nullptr.
* Must not use nsGkAtoms::_empty here.
* @param aPrincipal Set subject principal if it may be called by JS. If
* set to nullptr, will be treated as called by system.
*/
MOZ_CAN_RUN_SCRIPT nsresult RemoveInlinePropertyAsAction(
nsStaticAtom& aHTMLProperty, nsStaticAtom* aAttribute,
nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult
RemoveAllInlinePropertiesAsAction(nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult
IncreaseFontSizeAsAction(nsIPrincipal* aPrincipal = nullptr);
MOZ_CAN_RUN_SCRIPT nsresult
DecreaseFontSizeAsAction(nsIPrincipal* aPrincipal = nullptr);
/**
* GetFontColorState() returns foreground color information in first
* range of Selection.
* If first range of Selection is collapsed and there is a cache of style for
* new text, aIsMixed is set to false and aColor is set to the cached color.
* If first range of Selection is collapsed and there is no cached color,
* this returns the color of the node, aIsMixed is set to false and aColor is
* set to the color.
* If first range of Selection is not collapsed, this collects colors of
* each node in the range. If there are two or more colors, aIsMixed is set
* to true and aColor is truncated. If only one color is set to all of the
* range, aIsMixed is set to false and aColor is set to the color.
* If there is no Selection ranges, aIsMixed is set to false and aColor is
* truncated.
*
* @param aIsMixed Must not be nullptr. This is set to true
* if there is two or more colors in first
* range of Selection.
* @param aColor Returns the color if only one color is set to
* all of first range in Selection. Otherwise,
* returns empty string.
* @return Returns error only when illegal cases, e.g.,
* Selection instance has gone, first range
* Selection is broken.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
GetFontColorState(bool* aIsMixed, nsAString& aColor);
/**
* Detach aComposerCommandsUpdater from this.
*/
void Detach(const ComposerCommandsUpdater& aComposerCommandsUpdater);
nsStaticAtom& DefaultParagraphSeparatorTagName() const {
return HTMLEditor::ToParagraphSeparatorTagName(mDefaultParagraphSeparator);
}
ParagraphSeparator GetDefaultParagraphSeparator() const {
return mDefaultParagraphSeparator;
}
void SetDefaultParagraphSeparator(ParagraphSeparator aSep) {
mDefaultParagraphSeparator = aSep;
}
static nsStaticAtom& ToParagraphSeparatorTagName(
ParagraphSeparator aSeparator) {
switch (aSeparator) {
case ParagraphSeparator::div:
return *nsGkAtoms::div;
case ParagraphSeparator::p:
return *nsGkAtoms::p;
case ParagraphSeparator::br:
return *nsGkAtoms::br;
default:
MOZ_ASSERT_UNREACHABLE("New paragraph separator isn't handled here");
return *nsGkAtoms::div;
}
}
/**
* Modifies the table containing the selection according to the
* activation of an inline table editing UI element
* @param aUIAnonymousElement [IN] the inline table editing UI element
*/
MOZ_CAN_RUN_SCRIPT nsresult
DoInlineTableEditingAction(const Element& aUIAnonymousElement);
/**
* GetInclusiveAncestorByTagName() looks for an element node whose name
* matches aTagName from aNode or anchor node of Selection to <body> element.
*
* @param aTagName The tag name which you want to look for.
* Must not be nsGkAtoms::_empty.
* If nsGkAtoms::list, the result may be <ul>, <ol> or
* <dl> element.
* If nsGkAtoms::td, the result may be <td> or <th>.
* If nsGkAtoms::href, the result may be <a> element
* which has "href" attribute with non-empty value.
* If nsGkAtoms::anchor, the result may be <a> which
* has "name" attribute with non-empty value.
* @param aContent Start node to look for the result.
* @return If an element which matches aTagName, returns
* an Element. Otherwise, nullptr.
*/
Element* GetInclusiveAncestorByTagName(const nsStaticAtom& aTagName,
nsIContent& aContent) const;
/**
* Compute editing host for aContent. If this editor isn't active in the DOM
* window, this returns nullptr.
*/
enum class LimitInBodyElement { No, Yes };
[[nodiscard]] Element* ComputeEditingHost(
const nsIContent& aContent,
LimitInBodyElement aLimitInBodyElement = LimitInBodyElement::Yes) const {
return ComputeEditingHostInternal(&aContent, aLimitInBodyElement);
}
/**
* Compute editing host for the focus node of the Selection. If this editor
* isn't active in the DOM window, this returns nullptr.
*/
[[nodiscard]] Element* ComputeEditingHost(
LimitInBodyElement aLimitInBodyElement = LimitInBodyElement::Yes) const {
return ComputeEditingHostInternal(nullptr, aLimitInBodyElement);
}
/**
* Retruns true if we're in designMode.
*/
bool IsInDesignMode() const;
/**
* Basically, this always returns true if we're for `contenteditable` or
* `designMode` editor in web apps. However, e.g., Composer of SeaMonkey
* can make the editor not tabbable.
*/
bool IsTabbable() const { return IsInteractionAllowed(); }
/**
* NotifyEditingHostMaybeChanged() is called when new element becomes
* contenteditable when the document already had contenteditable elements.
*/
void NotifyEditingHostMaybeChanged();
/** Insert a string as quoted text
* (whose representation is dependant on the editor type),
* replacing the selected text (if any).
*
* @param aQuotedText The actual text to be quoted
* @parem aNodeInserted Return the node which was inserted.
*/
MOZ_CAN_RUN_SCRIPT // USED_BY_COMM_CENTRAL
nsresult
InsertAsQuotation(const nsAString& aQuotedText, nsINode** aNodeInserted);
MOZ_CAN_RUN_SCRIPT nsresult InsertHTMLAsAction(
const nsAString& aInString, nsIPrincipal* aPrincipal = nullptr);
/**
* Refresh positions of resizers. If you change size of target of resizers,
* you need to refresh position of resizers with calling this.
*/
MOZ_CAN_RUN_SCRIPT nsresult RefreshResizers();
protected: // May be called by friends.
/****************************************************************************
* Some friend classes are allowed to call the following protected methods.
* However, those methods won't prepare caches of some objects which are
* necessary for them. So, if you call them from friend classes, you need
* to make sure that AutoEditActionDataSetter is created.
****************************************************************************/
enum class WithTransaction { No, Yes };
friend std::ostream& operator<<(std::ostream& aStream,
WithTransaction aWithTransaction) {
aStream << "WithTransaction::"
<< (aWithTransaction == WithTransaction::Yes ? "Yes" : "No");
return aStream;
}
/**
* InsertBRElement() creates a <br> element and inserts it before
* aPointToInsert.
*
* @param aWithTransaction Whether the inserting is new element is undoable
* or not. WithTransaction::No is useful only when
* the new element is inserted into a new element
* which has not been connected yet.
* @param aPointToInsert The DOM point where should be <br> node inserted
* before.
* @param aSelect If eNone, returns a point to put caret which is
* suggested by InsertNodeTransaction.
* If eNext, returns a point after the new <br>
* element.
* If ePrevious, returns a point at the new <br>
* element.
* @return The new <br> node and suggesting point to put
* caret which respects aSelect.
*/
MOZ_CAN_RUN_SCRIPT CreateElementResult InsertBRElement(
WithTransaction aWithTransaction, const EditorDOMPoint& aPointToInsert,
EDirection aSelect = eNone);
/**
* DeleteTextWithTransaction() removes text in the range from aTextNode if
* it's modifiable. Note that this not an override of same method of
* EditorBase.
*
* @param aTextNode The text node which should be modified.
* @param aOffset Start offset of removing text in aTextNode.
* @param aLength Length of removing text.
*/
MOZ_CAN_RUN_SCRIPT nsresult DeleteTextWithTransaction(dom::Text& aTextNode,
uint32_t aOffset,
uint32_t aLength);
/**
* ReplaceTextWithTransaction() replaces text in the range with
* aStringToInsert.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult ReplaceTextWithTransaction(
dom::Text& aTextNode, uint32_t aOffset, uint32_t aLength,
const nsAString& aStringToInsert);
/**
* InsertTextWithTransaction() inserts aStringToInsert at aPointToInsert.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
InsertTextWithTransaction(Document& aDocument,
const nsAString& aStringToInsert,
const EditorDOMPoint& aPointToInsert) final;
/**
* CopyLastEditableChildStyles() clones inline container elements into
* aPreviousBlock to aNewBlock to keep using same style in it.
*
* @param aPreviousBlock The previous block element. All inline
* elements which are last sibling of each level
* are cloned to aNewBlock.
* @param aNewBlock New block container element. All children of
* this is deleted first.
* @param aEditingHost The editing host.
* @return If succeeded, returns a suggesting point to put
* caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
CopyLastEditableChildStylesWithTransaction(Element& aPreviousBlock,
Element& aNewBlock,
const Element& aEditingHost);
/**
* RemoveBlockContainerWithTransaction() removes aElement from the DOM tree
* but moves its all children to its parent node and if its parent needs <br>
* element to have at least one line-height, this inserts <br> element
* automatically.
*
* @param aElement Block element to be removed.
* @return If succeeded, returns a suggesting point to put
* caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
RemoveBlockContainerWithTransaction(Element& aElement);
MOZ_CAN_RUN_SCRIPT nsresult RemoveAttributeOrEquivalent(
Element* aElement, nsAtom* aAttribute, bool aSuppressTransaction) final;
MOZ_CAN_RUN_SCRIPT nsresult SetAttributeOrEquivalent(
Element* aElement, nsAtom* aAttribute, const nsAString& aValue,
bool aSuppressTransaction) final;
using EditorBase::RemoveAttributeOrEquivalent;
using EditorBase::SetAttributeOrEquivalent;
/**
* Returns container element of ranges in Selection. If Selection is
* collapsed, returns focus container node (or its parent element).
* If Selection selects only one element node, returns the element node.
* If Selection is only one range, returns common ancestor of the range.
* XXX If there are two or more Selection ranges, this returns parent node
* of start container of a range which starts with different node from
* start container of the first range.
*/
Element* GetSelectionContainerElement() const;
/**
* DeleteTableCellContentsWithTransaction() removes any contents in cell
* elements. If two or more cell elements are selected, this removes
* all selected cells' contents. Otherwise, this removes contents of
* a cell which contains first selection range. This does not return
* error even if selection is not in cell element, just does nothing.
*/
MOZ_CAN_RUN_SCRIPT nsresult DeleteTableCellContentsWithTransaction();
/**
* extracts an element from the normal flow of the document and
* positions it, and puts it back in the normal flow.
* @param aElement [IN] the element
* @param aEnabled [IN] true to absolutely position the element,
* false to put it back in the normal flow
*/
MOZ_CAN_RUN_SCRIPT nsresult SetPositionToAbsoluteOrStatic(Element& aElement,
bool aEnabled);
/**
* adds aChange to the z-index of an arbitrary element.
* @param aElement [IN] the element
* @param aChange [IN] relative change to apply to current z-index of
* the element
* @return The new z-index of the element
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<int32_t, nsresult>
AddZIndexWithTransaction(nsStyledElement& aStyledElement, int32_t aChange);
/**
* Join together any adjacent editable text nodes in the range.
*/
MOZ_CAN_RUN_SCRIPT nsresult CollapseAdjacentTextNodes(nsRange& aRange);
static bool HasAttributes(Element* aElement) {
MOZ_ASSERT(aElement);
uint32_t attrCount = aElement->GetAttrCount();
return attrCount > 1 ||
(1 == attrCount &&
!aElement->GetAttrNameAt(0)->Equals(nsGkAtoms::mozdirty));
}
static dom::Element* GetLinkElement(nsINode* aNode);
/**
* Helper routines for font size changing.
*/
enum class FontSize { incr, decr };
[[nodiscard]] MOZ_CAN_RUN_SCRIPT CreateElementResult
RelativeFontChangeOnTextNode(FontSize aDir, Text& aTextNode,
uint32_t aStartOffset, uint32_t aEndOffset);
/**
* @return A suggest point to put caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
SetInlinePropertyOnNode(nsIContent& aContent, nsAtom& aProperty,
nsAtom* aAttribute, const nsAString& aValue);
/**
* SplitAncestorStyledInlineElementsAtRangeEdges() splits all ancestor inline
* elements in the block at both aStartPoint and aEndPoint if given style
* matches with some of them.
*
* @param aStartPoint Start of range to split ancestor inline elements.
* @param aEndPoint End of range to split ancestor inline elements.
* @param aProperty The style tag name which you want to split. Set
* nullptr if you want to split any styled elements.
* @param aAttribute Attribute name if aProperty has some styles like
* nsGkAtoms::font.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitRangeOffResult
SplitAncestorStyledInlineElementsAtRangeEdges(
const EditorDOMPoint& aStartPoint, const EditorDOMPoint& aEndPoint,
nsAtom* aProperty, nsAtom* aAttribute);
/**
* SplitAncestorStyledInlineElementsAt() splits ancestor inline elements at
* aPointToSplit if specified style matches with them.
*
* @param aPointToSplit The point to split style at.
* @param aProperty The style tag name which you want to split.
* Set nullptr if you want to split any styled
* elements.
* @param aAttribute Attribute name if aProperty has some styles
* like nsGkAtoms::font.
* @return The result of SplitNodeDeepWithTransaction()
* with topmost split element. If this didn't
* find inline elements to be split, Handled()
* returns false.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitNodeResult
SplitAncestorStyledInlineElementsAt(const EditorDOMPoint& aPointToSplit,
nsAtom* aProperty, nsAtom* aAttribute);
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlinePropertyBase(
nsAtom& aHTMLProperty, nsAtom* aAttribute, const nsAString* aValue,
bool* aFirst, bool* aAny, bool* aAll, nsAString* outValue) const;
/**
* ClearStyleAt() splits parent elements to remove the specified style.
* If this splits some parent elements at near their start or end, such
* empty elements will be removed. Then, remove the specified style
* from the point and returns DOM point to put caret.
*
* @param aPoint The point to clear style at.
* @param aProperty An HTML tag name which represents a style.
* Set nullptr if you want to clear all styles.
* @param aAttribute Attribute name if aProperty has some styles like
* nsGkAtoms::font.
* @param aSpecifiedStyle Whether the class and style attributes should
* be preserved or discareded.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditResult
ClearStyleAt(const EditorDOMPoint& aPoint, nsAtom* aProperty,
nsAtom* aAttribute, SpecifiedStyle aSpecifiedStyle);
MOZ_CAN_RUN_SCRIPT nsresult SetPositionToAbsolute(Element& aElement);
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
SetPositionToStatic(Element& aElement);
/**
* OnModifyDocument() is called when the editor is changed. This should
* be called only by runnable in HTMLEditor::OnDocumentModified() to call
* HTMLEditor::OnModifyDocument() with AutoEditActionDataSetter instance.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult OnModifyDocument();
/**
* DoSplitNode() inserts aNewNode (left node before the container of
* aStartOfRightNode (right node), and moves all content before
* aStartOfRightNode to aNewNode.
*
* @param aStartOfRightNode The point to split. Its container will be
* the right node, i.e., becomes aNewNode's
* next sibling. And the point will be start
* of the right node.
* @param aNewNode The new node called as left node, so, this
* becomes the container of all previous content
* before aPointToSplit.
*/
MOZ_CAN_RUN_SCRIPT SplitNodeResult
DoSplitNode(const EditorDOMPoint& aStartOfRightNode, nsIContent& aNewNode);
/**
* DoJoinNodes() merges contents in aContentToRemove to aContentToKeep and
* remove aContentToRemove from the DOM tree. aContentToRemove and
* aContentToKeep must have same parent. Additionally, if one of
* aContentToRemove or aContentToKeep is a text node, the other must be a
* text node.
*
* @param aContentToKeep The node that will remain after the join.
* @param aContentToRemove The node that will be joined with aContentToKeep.
* There is no requirement that the two nodes be of
* the same type.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
DoJoinNodes(nsIContent& aContentToKeep, nsIContent& aContentToRemove);
/**
* Routines for managing the preservation of selection across
* various editor actions.
*/
bool ArePreservingSelection() const;
void PreserveSelectionAcrossActions();
MOZ_CAN_RUN_SCRIPT nsresult RestorePreservedSelection();
void StopPreservingSelection();
/**
* Called when JoinNodesTransaction::DoTransaction() did its transaction.
* Note that this is not called when undoing nor redoing.
*
* @param aTransaction The transaction which did join nodes.
* @param aDoJoinNodesResult Result of the doing join nodes.
*/
MOZ_CAN_RUN_SCRIPT void DidJoinNodesTransaction(
const JoinNodesTransaction& aTransaction, nsresult aDoJoinNodesResult);
protected: // edit sub-action handler
/**
* CanHandleHTMLEditSubAction() checks whether there is at least one
* selection range or not, and whether the first range is editable.
* If it's not editable, `Canceled()` of the result returns true.
* If `Selection` is in odd situation, returns an error.
*
* XXX I think that `IsSelectionEditable()` is better name, but it's already
* in `EditorBase`...
*/
EditActionResult CanHandleHTMLEditSubAction() const;
/**
* EnsureCaretNotAfterInvisibleBRElement() makes sure that caret is NOT after
* padding `<br>` element for preventing insertion after padding `<br>`
* element at empty last line.
* NOTE: This method should be called only when `Selection` is collapsed
* because `Selection` is a pain to work with when not collapsed.
* (no good way to extend start or end of selection), so we need to
* ignore those types of selections.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
EnsureCaretNotAfterInvisibleBRElement();
/**
* MaybeCreatePaddingBRElementForEmptyEditor() creates padding <br> element
* for empty editor if there is no children.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
MaybeCreatePaddingBRElementForEmptyEditor();
/**
* EnsureNoPaddingBRElementForEmptyEditor() removes padding <br> element
* for empty editor if there is.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
EnsureNoPaddingBRElementForEmptyEditor();
/**
* ReflectPaddingBRElementForEmptyEditor() scans the tree from the root
* element and sets mPaddingBRElementForEmptyEditor if exists, or otherwise
* nullptr. Can be used to manage undo/redo.
*/
[[nodiscard]] nsresult ReflectPaddingBRElementForEmptyEditor();
/**
* PrepareInlineStylesForCaret() consider inline styles from top level edit
* sub-action and setting it to `mTypeInState` and clear inline style cache
* if necessary.
* NOTE: This method should be called only when `Selection` is collapsed.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult PrepareInlineStylesForCaret();
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult HandleInsertText(
EditSubAction aEditSubAction, const nsAString& aInsertionString,
SelectionHandling aSelectionHandling) final;
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertDroppedDataTransferAsAction(
AutoEditActionDataSetter& aEditActionData,
dom::DataTransfer& aDataTransfer, const EditorDOMPoint& aDroppedAt,
nsIPrincipal* aSourcePrincipal) final;
/**
* GetInlineStyles() retrieves the style of aNode and modifies each item of
* aStyleCacheArray. This might cause flushing layout at retrieving computed
* values of CSS properties.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
GetInlineStyles(nsIContent& aContent, AutoStyleCacheArray& aStyleCacheArray);
/**
* CacheInlineStyles() caches style of aContent into mCachedInlineStyles of
* TopLevelEditSubAction. This may cause flushing layout at retrieving
* computed value of CSS properties.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
CacheInlineStyles(nsIContent& aContent);
/**
* ReapplyCachedStyles() restores some styles which are disappeared during
* handling edit action and it should be restored. This may cause flushing
* layout at retrieving computed value of CSS properties.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult ReapplyCachedStyles();
/**
* CreateStyleForInsertText() sets CSS properties which are stored in
* TypeInState to proper element node.
*
* @param aPointToInsertText The point to insert text.
* @return A suggest point to put caret or unset point.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
CreateStyleForInsertText(const EditorDOMPoint& aPointToInsertText);
/**
* GetMostDistantAncestorMailCiteElement() returns most-ancestor mail cite
* element. "mail cite element" is <pre> element when it's in plaintext editor
* mode or an element with which calling HTMLEditUtils::IsMailCite() returns
* true.
*
* @param aNode The start node to look for parent mail cite elements.
*/
Element* GetMostDistantAncestorMailCiteElement(const nsINode& aNode) const;
/**
* HandleInsertParagraphInMailCiteElement() splits aMailCiteElement at
* aPointToSplit.
*
* @param aMailCiteElement The mail-cite element which should be split.
* @param aPointToSplit The point to split.
* @param aEditingHost The editing host.
* @return Candidate caret position where is at inserted
* <br> element into the split point.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
HandleInsertParagraphInMailCiteElement(Element& aMailCiteElement,
const EditorDOMPoint& aPointToSplit,
const Element& aEditingHost);
/**
* HandleInsertBRElement() inserts a <br> element into aInsertToBreak.
* This may split container elements at the point and/or may move following
* <br> element to immediately after the new <br> element if necessary.
*
* @param aInsertToBreak The point where new <br> element will be
* inserted before.
* @param aEditingHost Current active editing host.
* @return If succeeded, returns new <br> element and
* candidate caret point.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT CreateElementResult HandleInsertBRElement(
const EditorDOMPoint& aInsertToBreak, const Element& aEditingHost);
/**
* HandleInsertLinefeed() inserts a linefeed character into aInsertToBreak.
*
* @param aInsertToBreak The point where new linefeed character will be
* inserted before.
* @param aEditingHost Current active editing host.
* @return A suggest point to put caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
HandleInsertLinefeed(const EditorDOMPoint& aInsertToBreak,
const Element& aEditingHost);
/**
* SplitParentInlineElementsAtRangeEdges() splits parent inline nodes at both
* start and end of aRangeItem. If this splits at every point, this modifies
* aRangeItem to point each split point (typically, right node).
*
* @param aRangeItem [in/out] One or two DOM points where should be
* split. Will be modified to split point if
* they're split.
* @return A suggest point to put caret if succeeded, but
* it may be unset.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
SplitParentInlineElementsAtRangeEdges(RangeItem& aRangeItem);
/**
* SplitElementsAtEveryBRElement() splits before all <br> elements in
* aMostAncestorToBeSplit. All <br> nodes will be moved before right node
* at splitting its parent. Finally, this returns left node, first <br>
* element, next left node, second <br> element... and right-most node.
*
* @param aMostAncestorToBeSplit Most-ancestor element which should
* be split.
* @param aOutArrayOfNodes First left node, first <br> element,
* Second left node, second <br> element,
* ...right-most node. So, all nodes
* in this list should be siblings (may be
* broken the relation by mutation event
* listener though). If first <br> element
* is first leaf node of
* aMostAncestorToBeSplit, starting from
* the first <br> element.
* @return A suggest point to put caret if
* succeeded, but it may unset.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
SplitElementsAtEveryBRElement(
nsIContent& aMostAncestorToBeSplit,
nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents);
/**
* MaybeSplitElementsAtEveryBRElement() calls SplitElementsAtEveryBRElement()
* for each given node when this needs to do that for aEditSubAction.
* If split a node, it in aArrayOfContents is replaced with split nodes and
* <br> elements.
*
* @return A suggest point to put caret if
* succeeded, but it may unset.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
MaybeSplitElementsAtEveryBRElement(
nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents,
EditSubAction aEditSubAction);
/**
* CreateRangeIncludingAdjuscentWhiteSpaces() creates an nsRange instance
* which may be expanded from the given range to include adjuscent
* white-spaces. If this fails handling something, returns nullptr.
*/
template <typename EditorDOMRangeType>
already_AddRefed<nsRange> CreateRangeIncludingAdjuscentWhiteSpaces(
const EditorDOMRangeType& aRange);
template <typename EditorDOMPointType1, typename EditorDOMPointType2>
already_AddRefed<nsRange> CreateRangeIncludingAdjuscentWhiteSpaces(
const EditorDOMPointType1& aStartPoint,
const EditorDOMPointType2& aEndPoint);
/**
* GetChildNodesOf() returns all child nodes of aParent with an array.
*/
static void GetChildNodesOf(
nsINode& aParentNode,
nsTArray<OwningNonNull<nsIContent>>& aOutArrayOfContents) {
MOZ_ASSERT(aOutArrayOfContents.IsEmpty());
aOutArrayOfContents.SetCapacity(aParentNode.GetChildCount());
for (nsIContent* childContent = aParentNode.GetFirstChild(); childContent;
childContent = childContent->GetNextSibling()) {
aOutArrayOfContents.AppendElement(*childContent);
}
}
/**
* Try to get parent list element at `Selection`. This returns first find
* parent list element of common ancestor of ranges (looking for it from
* first range to last range).
*/
Element* GetParentListElementAtSelection() const;
/**
* GetRangeExtendedToHardLineEdgesForBlockEditAction() returns an extended
* range if aRange should be extended before handling a block level editing.
* If aRange start and/or end point <br> or something non-editable point, they
* should be moved to nearest text node or something where the other methods
* easier to handle edit action.
*/
[[nodiscard]] Result<EditorRawDOMRange, nsresult>
GetRangeExtendedToHardLineEdgesForBlockEditAction(
const nsRange* aRange, const Element& aEditingHost) const;
/**
* InitializeInsertingElement is a callback type of methods which inserts
* an element into the DOM tree. This is called immediately before or
* after inserting aNewElement into the DOM tree (depending on
* "editor.initialize_element_before_connect" pref whether this is called
* before or after inserting the element).
*
* @param aHTMLEditor The HTML editor which modifies the DOM tree.
* @param aNewElement The new element which will be or was inserted into
* the DOM tree.
* @param aPointToInsert The position aNewElement will be or was inserted.
*/
using InitializeInsertingElement =
std::function<nsresult(HTMLEditor& aHTMLEditor, Element& aNewElement,
const EditorDOMPoint& aPointToInsert)>;
static InitializeInsertingElement DoNothingForNewElement;
/**
* Create an element node whose name is aTag at before aPointToInsert. When
* this succeed to create an element node, this inserts the element to
* aPointToInsert.
*
* @param aWithTransaction Whether the inserting is new element is undoable
* or not. WithTransaction::No is useful only when
* the new element is inserted into a new element
* which has not been connected yet.
* @param aTagName The element name to create.
* @param aPointToInsert The insertion point of new element.
* If this refers end of the container or after,
* the transaction will append the element to the
* container.
* Otherwise, will insert the element before the
* child node referred by this.
* Note that this point will be invalid once this
* method inserts the new element.
* @param aInitializer A function to initialize the new element before
* or after (depends on the pref) connecting the
* element into the DOM tree. Note that this should
* not touch outside given element because doing it
* would break range updater's result.
* @return The created new element node and candidate caret
* position.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT CreateElementResult CreateAndInsertElement(
WithTransaction aWithTransaction, nsAtom& aTagName,
const EditorDOMPoint& aPointToInsert,
const InitializeInsertingElement& aInitializer = DoNothingForNewElement);
/**
* MaybeSplitAncestorsForInsertWithTransaction() does nothing if container of
* aStartOfDeepestRightNode can have an element whose tag name is aTag.
* Otherwise, looks for an ancestor node which is or is in active editing
* host and can have an element whose name is aTag. If there is such
* ancestor, its descendants are split.
*
* Note that this may create empty elements while splitting ancestors.
*
* @param aTag The name of element to be inserted
* after calling this method.
* @param aStartOfDeepestRightNode The start point of deepest right node.
* This point must be descendant of
* active editing host.
* @return When succeeded, SplitPoint() returns
* the point to insert the element.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitNodeResult
MaybeSplitAncestorsForInsertWithTransaction(
nsAtom& aTag, const EditorDOMPoint& aStartOfDeepestRightNode);
/**
* InsertElementWithSplittingAncestorsWithTransaction() is a wrapper of
* MaybeSplitAncestorsForInsertWithTransaction() and CreateAndInsertElement().
* I.e., will create an element whose tag name is aTagName and split ancestors
* if it's necessary, then, insert it.
*
* @param aTagName The tag name which you want to insert new
* element at aPointToInsert.
* @param aPointToInsert The insertion point. New element will be
* inserted before here.
* @param aBRElementNextToSplitPoint
* Whether <br> element should be deleted or
* kept if and only if a <br> element follows
* split point.
* @param aEditingHost The editing host with which we're handling it.
* @param aInitializer A function to initialize the new element before
* or after (depends on the pref) connecting the
* element into the DOM tree. Note that this should
* not touch outside given element because doing it
* would break range updater's result.
* @return If succeeded, returns the new element node and
* suggesting point to put caret.
*/
enum class BRElementNextToSplitPoint { Keep, Delete };
[[nodiscard]] MOZ_CAN_RUN_SCRIPT CreateElementResult
InsertElementWithSplittingAncestorsWithTransaction(
nsAtom& aTagName, const EditorDOMPoint& aPointToInsert,
BRElementNextToSplitPoint aBRElementNextToSplitPoint,
const Element& aEditingHost,
const InitializeInsertingElement& aInitializer = DoNothingForNewElement);
/**
* SplitRangeOffFromBlock() splits aBlockElement at two points, before
* aStartOfMiddleElement and after aEndOfMiddleElement. If they are very
* start or very end of aBlockElement, this won't create empty block.
*
* @param aBlockElement A block element which will be split.
* @param aStartOfMiddleElement Start node of middle block element.
* @param aEndOfMiddleElement End node of middle block element.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitRangeOffFromNodeResult
SplitRangeOffFromBlock(Element& aBlockElement,
nsIContent& aStartOfMiddleElement,
nsIContent& aEndOfMiddleElement);
/**
* RemoveBlockContainerElementWithTransactionBetween() splits the nodes
* at aStartOfRange and aEndOfRange, then, removes the middle element which
* was split off from aBlockContainerElement and moves the ex-children to
* where the middle element was. I.e., all nodes between aStartOfRange and
* aEndOfRange (including themselves) will be unwrapped from
* aBlockContainerElement.
*
* @param aBlockContainerElement The node which will be split.
* @param aStartOfRange The first node which will be unwrapped
* from aBlockContainerElement.
* @param aEndOfRange The last node which will be unwrapped from
* aBlockContainerElement.
* @return The left content is new created left
* element of aBlockContainerElement.
* The right content is split element,
* i.e., must be aBlockContainerElement.
* The middle content is nullptr since
* removing it is the job of this method.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitRangeOffFromNodeResult
RemoveBlockContainerElementWithTransactionBetween(
Element& aBlockContainerElement, nsIContent& aStartOfRange,
nsIContent& aEndOfRange);
/**
* WrapContentsInBlockquoteElementsWithTransaction() inserts at least one
* <blockquote> element and moves nodes in aArrayOfContents into new
* <blockquote> elements. If aArrayOfContents includes a table related element
* except <table>, this calls itself recursively to insert <blockquote> into
* the cell.
*
* @param aArrayOfContents Nodes which will be moved into created
* <blockquote> elements.
* @param aEditingHost The editing host.
* @return A suggest of caret position if succeeded. It
* may be unset if there is no suggestion.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
WrapContentsInBlockquoteElementsWithTransaction(
const nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents,
const Element& aEditingHost);
/**
* RemoveBlockContainerElementsWithTransaction() removes all format blocks,
* table related element, etc in aArrayOfContents from the DOM tree. If
* aArrayOfContents has a format node, it will be removed and its contents
* will be moved to where it was.
* If aArrayOfContents has a table related element, <li>, <blockquote> or
* <div>, it will be removed and its contents will be moved to where it was.
*
* @return A suggest point to put caret if succeeded, but it may be
* unset if there is no suggestion.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
RemoveBlockContainerElementsWithTransaction(
const nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents);
/**
* CreateOrChangeBlockContainerElement() formats all nodes in aArrayOfContents
* with block elements whose name is aBlockTag.
* If aArrayOfContents has an inline element, a block element is created and
* the inline element and following inline elements are moved into the new
* block element.
* If aArrayOfContents has <br> elements, they'll be removed from the DOM
* tree and new block element will be created when there are some remaining
* inline elements.
* If aArrayOfContents has a block element, this calls itself with children
* of the block element. Then, new block element will be created when there
* are some remaining inline elements.
*
* @param aArrayOfContents Must be descendants of a node.
* @param aBlockTag The element name of new block elements.
* @param aEditingHost The editing host.
* @return May suggest a point to put caret if succeeded.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
CreateOrChangeBlockContainerElement(
nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents, nsAtom& aBlockTag,
const Element& aEditingHost);
/**
* FormatBlockContainerWithTransaction() is implementation of "formatBlock"
* command of `Document.execCommand()`. This applies block style or removes
* it.
*
* @param aSelectionRanges The ranges which are cloned by selection or
* updated from it with doing something before
* calling this.
* @param aBlockType New block tag name.
* If nsGkAtoms::normal or nsGkAtoms::_empty,
* RemoveBlockContainerElementsWithTransaction() will be
* called.
* If nsGkAtoms::blockquote,
* WrapContentsInBlockquoteElementsWithTransaction() will
* be called.
* Otherwise, CreateOrChangeBlockContainerElement() will be
* called.
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult FormatBlockContainerWithTransaction(
AutoRangeArray& aSelectionRanges, nsAtom& aBlockType,
const Element& aEditingHost);
/**
* InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary() determines if
* aPointToInsert is start of a hard line and end of the line (i.e, the
* line is empty) and the line ends with block boundary, inserts a `<br>`
* element.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
InsertBRElementIfHardLineIsEmptyAndEndsWithBlockBoundary(
const EditorDOMPoint& aPointToInsert);
/**
* Insert padding `<br>` element for empty last line into aElement if
* aElement is a block element and empty.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
InsertPaddingBRElementForEmptyLastLineIfNeeded(Element& aElement);
/**
* This method inserts a padding `<br>` element for empty last line if
* selection is collapsed and container of the range needs it.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
MaybeInsertPaddingBRElementForEmptyLastLineAtSelection();
/**
* SplitParagraphWithTransaction() splits the parent block, aParentDivOrP, at
* aStartOfRightNode.
*
* @param aParentDivOrP The parent block to be split. This must be <p>
* or <div> element.
* @param aStartOfRightNode The point to be start of right node after
* split. This must be descendant of
* aParentDivOrP.
* @param aMayBecomeVisibleBRElement
* Next <br> element of the split point if there
* is. Otherwise, nullptr. If this is not nullptr,
* the <br> element may be removed if it becomes
* visible.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitNodeResult
SplitParagraphWithTransaction(Element& aParentDivOrP,
const EditorDOMPoint& aStartOfRightNode,
dom::HTMLBRElement* aMayBecomeVisibleBRElement);
/**
* HandleInsertParagraphInParagraph() does the right thing for Enter key
* press or 'insertParagraph' command in aParentDivOrP. aParentDivOrP will
* be split **around** aCandidatePointToSplit. If this thinks that it should
* be handled to insert a <br> instead, this returns "not handled".
*
* @param aParentDivOrP The parent block. This must be <p> or <div>
* element.
* @param aCandidatePointToSplit
* The point where the caller want to split
* aParentDivOrP. However, in some cases, this is not
* used as-is. E.g., this method avoids to create new
* empty <a href> in the right paragraph. So this may
* be adjusted to proper position around it.
* @param aEditingHost The editing host.
* @return If the caller should default to inserting <br>
* element, returns "not handled".
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitNodeResult
HandleInsertParagraphInParagraph(Element& aParentDivOrP,
const EditorDOMPoint& aCandidatePointToSplit,
const Element& aEditingHost);
/**
* HandleInsertParagraphInHeadingElement() handles insertParagraph command
* (i.e., handling Enter key press) in a heading element. This splits
* aHeadingElement element at aPointToSplit. Then, if right heading element
* is empty, it'll be removed and new paragraph is created (its type is
* decided with default paragraph separator).
*
* @param aHeadingElement The heading element to be split.
* @param aPointToSplit The point to split aHeadingElement.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT SplitNodeResult
HandleInsertParagraphInHeadingElement(Element& aHeadingElement,
const EditorDOMPoint& aPointToSplit);
/**
* HandleInsertParagraphInListItemElement() handles insertParagraph command
* (i.e., handling Enter key press) in a list item element.
*
* @param aListItemElement The list item which has the following point.
* @param aPointToSplit The point to split aListItemElement.
* @param aEditingHost The editing host.
* @return A candidate position to put caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
HandleInsertParagraphInListItemElement(Element& aListItemElement,
const EditorDOMPoint& aPointToSplit,
const Element& aEditingHost);
/**
* InsertParagraphSeparatorAsSubAction() handles insertPargraph commad
* (i.e., handling Enter key press) with the above helper methods.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
InsertParagraphSeparatorAsSubAction(const Element& aEditingHost);
/**
* InsertLineBreakAsSubAction() inserts a new <br> element or a linefeed
* character at selection. If there is non-collapsed selection ranges, the
* selected ranges is deleted first.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertLineBreakAsSubAction();
/**
* ChangeListElementType() replaces child list items of aListElement with
* new list item element whose tag name is aNewListItemTag.
* Note that if there are other list elements as children of aListElement,
* this calls itself recursively even though it's invalid structure.
*
* @param aListElement The list element whose list items will be
* replaced.
* @param aNewListTag New list tag name.
* @param aNewListItemTag New list item tag name.
* @return New list element or an error code if it fails.
* New list element may be aListElement if its
* tag name is same as aNewListTag.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT CreateElementResult ChangeListElementType(
Element& aListElement, nsAtom& aListType, nsAtom& aItemType);
/**
* ChangeSelectedHardLinesToList() converts selected ranges to specified
* list element. If there is different type of list elements, this method
* converts them to specified list items too. Basically, each hard line
* will be wrapped with a list item element. However, only when `<p>`
* element is selected, its child `<br>` elements won't be treated as
* hard line separators. Perhaps, this is a bug.
* NOTE: This method creates AutoSelectionRestorer. Therefore, each caller
* need to check if the editor is still available even if this returns
* NS_OK.
*
* @param aListElementTagName The new list element tag name.
* @param aListItemElementTagName The new list item element tag name.
* @param aBulletType If this is not empty string, it's set
* to `type` attribute of new list item
* elements. Otherwise, existing `type`
* attributes will be removed.
* @param aSelectAllOfCurrentList Yes if this should treat all of
* ancestor list element at selection.
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
ChangeSelectedHardLinesToList(nsAtom& aListElementTagName,
nsAtom& aListItemElementTagName,
const nsAString& aBulletType,
SelectAllOfCurrentList aSelectAllOfCurrentList,
const Element& aEditingHost);
/**
* MakeOrChangeListAndListItemAsSubAction() handles create list commands with
* current selection. If
*
* @param aListElementOrListItemElementTagName
* The new list element tag name or
* new list item tag name.
* If the former, list item tag name will
* be computed automatically. Otherwise,
* list tag name will be computed.
* @param aBulletType If this is not empty string, it's set
* to `type` attribute of new list item
* elements. Otherwise, existing `type`
* attributes will be removed.
* @param aSelectAllOfCurrentList Yes if this should treat all of
* ancestor list element at selection.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT EditActionResult
MakeOrChangeListAndListItemAsSubAction(
nsAtom& aListElementOrListItemElementTagName,
const nsAString& aBulletType,
SelectAllOfCurrentList aSelectAllOfCurrentList);
/**
* DeleteTextAndTextNodesWithTransaction() removes text or text nodes in
* the given range.
*/
enum class TreatEmptyTextNodes {
// KeepIfContainerOfRangeBoundaries:
// Will remove empty text nodes middle of the range, but keep empty
// text nodes which are containers of range boundaries.
KeepIfContainerOfRangeBoundaries,