/* -*- 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/. */
class nsDocumentFragment; class nsFrameSelection; class nsHTMLDocument; class nsITransferable; class nsRange; class nsStaticAtom; class nsStyledElement; class nsTableCellFrame; class nsTableWrapperFrame; template <class E> class nsTArray;
namespace mozilla { class AlignStateAtSelection; class AutoSelectionSetterAfterTableEdit; 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
enumclass 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.
****************************************************************************/
/** * @param aDocument The document whose content will be editable.
*/ explicit HTMLEditor(const Document& aDocument);
/** * @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();
/** * 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);
/** * Called when aDocument or aElement becomes editable without focus change. * E.g., when the design mode is enabled or the contenteditable attribute * is set to the focused element.
*/
MOZ_CAN_RUN_SCRIPT nsresult FocusedElementOrDocumentBecomesEditable(
Document& aDocument, Element* aElement);
/** * Called when aDocument or aElement becomes not editable without focus * change. E.g., when the design mode ends or the contenteditable attribute is * removed or set to "false".
*/
MOZ_CAN_RUN_SCRIPT static nsresult FocusedElementOrDocumentBecomesNotEditable(
HTMLEditor* aHTMLEditor, Document& aDocument, Element* aElement);
/** * 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);
/** * PasteNoFormattingAsAction() pastes content in clipboard without any style * information. * * @param aClipboardType nsIClipboard::kGlobalClipboard or * nsIClipboard::kSelectionClipboard. * @param aDispatchPasteEvent Yes if this should dispatch ePaste event * before pasting. Otherwise, No. * @param aDataTransfer The object containing the data to use for the * paste operation. May be nullptr, in which case * this will just get the data from the clipboard. * @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(nsIClipboard::ClipboardType aClipboardType,
DispatchPasteEvent aDispatchPasteEvent,
DataTransfer* aDataTransfer = nullptr,
nsIPrincipal* aPrincipal = nullptr);
/** * 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);
enumclass InsertElementOption { // Delete selection if set, otherwise, insert aElement at start or end of // selection.
DeleteSelection, // Whether split all inline ancestors or not.
SplitAncestorInlineElements,
}; using InsertElementOptions = EnumSet<InsertElementOption>;
MOZ_CAN_RUN_SCRIPT nsresult InsertElementAtSelectionAsAction(
Element* aElement, const InsertElementOptions aOptions,
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);
/** * The Document.execCommand("formatBlock") handler. * * @param aParagraphFormat Must not be an empty string, and the value must * be one of address, article, aside, blockquote, * div, footer, h1, h2, h3, h4, h5, h6, header, * hgroup, main, nav, p, pre, selection, dt or dd.
*/
MOZ_CAN_RUN_SCRIPT nsresult FormatBlockAsAction( const nsAString& aParagraphFormat, nsIPrincipal* aPrincipal = nullptr);
/** * The cmd_paragraphState command handler. * * @param aParagraphFormat Can be empty string. If this is empty string, * this removes ancestor format elements. * Otherwise, the value must be one of p, pre, * h1, h2, h3, h4, h5, h6, address, dt or dl.
*/
MOZ_CAN_RUN_SCRIPT nsresult SetParagraphStateAsAction( const nsAString& aParagraphFormat, 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.
*/ enumclass SelectAllOfCurrentList { Yes, No };
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult MakeOrChangeListAsAction( const nsStaticAtom& 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 mIsCSSPrefChecked; }
/** * 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;
}
/** * returns the deepest absolutely positioned container of the selection * if it exists or null.
*/
MOZ_CAN_RUN_SCRIPT already_AddRefed<Element>
GetAbsolutelyPositionedSelectionContainer() const;
/** * 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);
/** * 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(
nsStaticAtom& aProperty, nsStaticAtom* 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(
nsStaticAtom& aHTMLProperty, nsAtom* aAttribute, const nsAString& aValue, bool* aFirst, bool* aAny, bool* aAll) const;
/** * 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);
/** * 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);
/** * 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.
*/ enumclass 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);
}
/** * Return true if this editor was notified of focus, but has not been notified * of the blur.
*/
[[nodiscard]] bool HasFocus() const { return mHasFocus; }
/** * Return true if this editor is in the designMode.
*/
[[nodiscard]] bool IsInDesignMode() const { return mIsInDesignMode; }
/** * Return true if entire the document is editable (although the document * may have non-editable nodes, e.g., * <body contenteditable><div contenteditable="false"></div></body>
*/ bool EntireDocumentIsEditable() 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);
/** * 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();
/** * Return true if this is in the plaintext mail composer mode of * Thunderbird or something. * NOTE: This is different from contenteditable="plaintext-only"
*/ bool IsPlaintextMailComposer() const { constbool isPlaintextMode =
(mFlags & nsIEditor::eEditorPlaintextMask) != 0;
MOZ_ASSERT_IF(IsTextEditor(), isPlaintextMode); return isPlaintextMode;
}
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.
****************************************************************************/
/** * Return preferred line break when you insert a line break in aNode (if * aNode is a Text node, this assumes that line break will be inserted to * its parent element). * * @param aNode The node where you want to insert a line break. * This should be a inclusive descendant of * aEditingHost because if it's not connected, we can * not refer the proper style information. * @param aEditingHost The editing host.
*/
Maybe<LineBreakType> GetPreferredLineBreakType( const nsINode& aNode, const Element& aEditingHost) const;
/** * InsertLineBreak() creates a <br> element or a Text node which has only * preformatted linefeed and inserts it at 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 aLineBreakType Whether a <br> element or a linefeed should be * used. * @param aPointToInsert The DOM point where a <br> element or a Text * node should be inserted. * @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> or Text node and suggesting point * to put caret with respecting aSelect.
*/
MOZ_CAN_RUN_SCRIPT Result<CreateLineBreakResult, nsresult> InsertLineBreak(
WithTransaction aWithTransaction, LineBreakType aLineBreakType, const EditorDOMPoint& aPointToInsert, EDirection aSelect = eNone);
/** * Delete text in the range in aTextNode. If aTextNode is not editable, this * does nothing. * * @param aTextNode The text node which should be modified. * @param aOffset Start offset of removing text in aTextNode. * @param aLength Length of removing text.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CaretPoint, nsresult>
DeleteTextWithTransaction(dom::Text& aTextNode, uint32_t aOffset,
uint32_t aLength);
/** * Replace text in the range with aStringToInsert. If there is a DOM range * exactly same as the replacing range, it'll be collapsed to * {aTextNode, aOffset} because of the order of deletion and insertion. * Therefore, the callers may need to handle `Selection` even when callers * do not want to update `Selection`.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<InsertTextResult, nsresult>
ReplaceTextWithTransaction(dom::Text& aTextNode, uint32_t aOffset,
uint32_t aLength, const nsAString& aStringToInsert);
/** * Insert aStringToInsert to aPointToInsert. If the point is not editable, * this returns error.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<InsertTextResult, nsresult>
InsertTextWithTransaction(Document& aDocument, const nsAString& aStringToInsert, const EditorDOMPoint& aPointToInsert,
InsertTextTo aInsertTextTo) 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);
/** * 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 adjacent editable text nodes in the range except preformatted * linefeed only nodes.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
CollapseAdjacentTextNodes(nsRange& aRange);
enumclass SplitAtEdges { // SplitNodeDeepWithTransaction() won't split container element // nodes at their edges. I.e., when split point is start or end of // container, it won't be split.
eDoNotCreateEmptyContainer, // SplitNodeDeepWithTransaction() always splits containers even // if the split point is at edge of a container. E.g., if split point is // start of an inline element, empty inline element is created as a new left // node.
eAllowToCreateEmptyContainer,
};
/** * SplitAncestorStyledInlineElementsAtRangeEdges() splits all ancestor inline * elements in the block at aRange if given style matches with some of them. * * @param aRange Ancestor inline elements of the start and end * boundaries will be split. * @param aStyle The style which you want to split. * RemoveAllStyles instance is allowed to split any * inline elements. * @param aSplitAtEdges Whether this should split elements at start or * end of inline elements or not.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitRangeOffResult, nsresult>
SplitAncestorStyledInlineElementsAtRangeEdges(const EditorDOMRange& aRange, const EditorInlineStyle& aStyle,
SplitAtEdges aSplitAtEdges);
/** * SplitAncestorStyledInlineElementsAt() splits ancestor inline elements at * aPointToSplit if specified style matches with them. * * @param aPointToSplit The point to split style at. * @param aStyle The style which you want to split. * RemoveAllStyles instance is allowed to split any * inline elements. * @param aSplitAtEdges Whether this should split elements at start or * end of inline elements or not. * @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 Result<SplitNodeResult, nsresult>
SplitAncestorStyledInlineElementsAt(const EditorDOMPoint& aPointToSplit, const EditorInlineStyle& aStyle,
SplitAtEdges aSplitAtEdges);
/** * 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 aStyleToRemove The style which you want to clear. * @param aSpecifiedStyle Whether the class and style attributes should * be preserved or discarded. * @param aEditingHost The editing host. * @return A candidate position to put caret. If there is * AutoTransactionsConserveSelection instances, this stops * suggesting caret point only in some cases.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
ClearStyleAt(const EditorDOMPoint& aPoint, const EditorInlineStyle& aStyleToRemove,
SpecifiedStyle aSpecifiedStyle, const Element& aEditingHost);
class DocumentModifiedEvent final : public Runnable { public: explicit DocumentModifiedEvent(HTMLEditor& aHTMLEditor)
: Runnable("DocumentModifiedEvent"), mHTMLEditor(aHTMLEditor) {}
/** * OnModifyDocument() is called when the editor is changed. This should * be called only by DocumentModifiedEvent when AutoEditActionDataSetter * instance is in the stack.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
OnModifyDocument(const DocumentModifiedEvent& aRunner);
/** * DoSplitNode() inserts aNewNode and moves all content before or after * aStartOfRightNode to aNewNode. * * @param aStartOfRightNode The point to split. The container will keep * having following or previous content of this. * @param aNewNode The new node called. The previous or following * content of aStartOfRightNode will be moved into * this node.
*/
MOZ_CAN_RUN_SCRIPT Result<SplitNodeResult, nsresult> 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);
/** * 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`...
*/ enumclass CheckSelectionInReplacedElement { Yes, OnlyWhenNotInSameNode };
Result<EditActionResult, nsresult> CanHandleHTMLEditSubAction(
CheckSelectionInReplacedElement aCheckSelectionInReplacedElement =
CheckSelectionInReplacedElement::Yes) 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(const Element& aEditingHost);
/** * 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 `mPendingStylesToApplyToNewContent` 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();
/** * GetInlineStyles() retrieves the style of aElement and modifies each item of * aPendingStyleCacheArray. This might cause flushing layout at retrieving * computed values of CSS properties.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetInlineStyles(
Element& aElement, AutoPendingStyleCacheArray& aPendingStyleCacheArray);
/** * CacheInlineStyles() caches style of aElement into mCachedPendingStyles of * TopLevelEditSubAction. This may cause flushing layout at retrieving * computed value of CSS properties.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
CacheInlineStyles(Element& aElement);
/** * 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 * PendingStyles to proper element node. * * @param aPointToInsertText The point to insert text. * @param aEditingHost The editing host. * @return A suggest point to put caret or unset point.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
CreateStyleForInsertText(const EditorDOMPoint& aPointToInsertText, const Element& aEditingHost);
/** * 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. * @return Candidate caret position where is at inserted * <br> element into the split point.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CaretPoint, nsresult>
HandleInsertParagraphInMailCiteElement(Element& aMailCiteElement, const EditorDOMPoint& aPointToSplit);
/** * HandleInsertBRElement() inserts a <br> element into aPointToBreak. * 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 aPointToBreak 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 Result<CreateElementResult, nsresult>
HandleInsertBRElement(const EditorDOMPoint& aPointToBreak, const Element& aEditingHost);
/** * HandleInsertLinefeed() inserts a linefeed character into aPointToBreak. * * @param aPointToBreak 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& aPointToBreak, const Element& aEditingHost);
/** * Splits inclusive inline ancestors at both start and end of aRangeItem. If * this splits at every point, this modifies aRangeItem to point each split * point (typically, at 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. * @param aBlockInlineCheck [in] Whether this method considers block vs. * inline with computed style or the default style. * @param aEditingHost [in] The editing host. * @param aAncestorLimiter [in/optional] If specified, this stops splitting * ancestors when meets this node. * @return A suggest point to put caret if succeeded, but * it may be unset.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
SplitInlineAncestorsAtRangeBoundaries(
RangeItem& aRangeItem, BlockInlineCheck aBlockInlineCheck, const Element& aEditingHost, const nsIContent* aAncestorLimiter = nullptr);
/** * 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);
/** * 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 inserting * aNewElement into the DOM tree. * * @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; static InitializeInsertingElement InsertNewBRElement;
/** * 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 * 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 Result<CreateElementResult, nsresult>
CreateAndInsertElement(
WithTransaction aWithTransaction, const nsAtom& aTagName, const EditorDOMPoint& aPointToInsert, const InitializeInsertingElement& aInitializer = DoNothingForNewElement);
/** * Callback of CopyAttributes(). * * @param aHTMLEditor The HTML editor. * @param aSrcElement The element which have the attribute. * @param aDestElement The element which will have the attribute. * @param aNamespaceID [in] The namespace ID of aAttrName. * @param aAttrName [in] The attribute name which will be copied. * @param aValue [in/out] The attribute value which will be copied. * Once updated, the new value is used. * @return true if the attribute should be copied, otherwise, * false.
*/ using AttributeFilter = std::function<bool(
HTMLEditor& aHTMLEditor, Element& aSrcElement, Element& aDestElement,
int32_t aNamespaceID, const nsAtom& aAttrName, nsString& aValue)>; static AttributeFilter CopyAllAttributes; static AttributeFilter CopyAllAttributesExceptId; static AttributeFilter CopyAllAttributesExceptDir; static AttributeFilter CopyAllAttributesExceptIdAndDir;
/** * Copy all attributes of aSrcElement to aDestElement as-is. Different from * EditorBase::CloneAttributesWithTransaction(), this does not use * SetAttributeOrEquivalent() nor does not clear existing attributes of * aDestElement. * * @param aWithTransaction Whether recoding with transactions or not. * @param aDestElement The element will have attributes. * @param aSrcElement The element whose attributes will be copied.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult CopyAttributes(
WithTransaction aWithTransaction, Element& aDestElement,
Element& aSrcElement, const AttributeFilter& = CopyAllAttributes);
/** * 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 in aEditingHost. * @param aEditingHost The editing host. * @return When succeeded, SplitPoint() returns * the point to insert the element.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<SplitNodeResult, nsresult>
MaybeSplitAncestorsForInsertWithTransaction( const nsAtom& aTag, const EditorDOMPoint& aStartOfDeepestRightNode, const Element& aEditingHost);
/** * 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 * 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.
*/ enumclass BRElementNextToSplitPoint { Keep, Delete };
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult>
InsertElementWithSplittingAncestorsWithTransaction( const nsAtom& aTagName, const EditorDOMPoint& aPointToInsert,
BRElementNextToSplitPoint aBRElementNextToSplitPoint, const Element& aEditingHost, const InitializeInsertingElement& aInitializer = DoNothingForNewElement);
/** * Split aElementToSplit 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 aElementToSplit An 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 Result<SplitRangeOffFromNodeResult, nsresult>
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.44 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.