/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* 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; enumclass LogLevel; 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 };
/** *TheHTMLeditorimplementation.<br> *UsetoeditHTMLdocumentrepresentedasaDOMtree.
*/ class HTMLEditor final : public EditorBase,
public nsIHTMLEditor,
public nsIHTMLObjectResizer,
public nsIHTMLAbsPosEditor,
public nsITableEditor,
public nsIHTMLInlineTableEditor,
public nsStubMutationObserver,
public nsIEditorMailSupport {
public: /**************************************************************************** *NOTE:DONOTMAKEYOURNEWMETHODSPUBLICIFtheyarecalledbyother *classesunderlibeditorexceptEditorEventListenerand *HTMLEditorEventListenerbecauseeachpublicmethodwhichmayfire *eEditorInputeventwillneedtoinstantiatenewstackclassfor *managinginputtypevalueofeEditorInputandcachesomeobjects *forsmarterhandling.Inotherwords,whenyouaddnewroot *methodtoedittheDOMtree,youcanmakeyournewmethodpublic.
****************************************************************************/
protected: // May be called by friends. /**************************************************************************** *Somefriendclassesareallowedtocallthefollowingprotectedmethods. *However,thosemethodswon'tpreparecachesofsomeobjectswhichare *necessaryforthem.So,ifyoucallthemfromfriendclasses,youneed *tomakesurethatAutoEditActionDataSetteriscreated.
****************************************************************************/
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,
};
/**
* 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;
/** *OurtraditionalformatBlockwassameasXULcmd_paragraphStatecommand. *However,thebehaviorisprettydifferentfromtheothersandaligning *theXULcommandbehaviormaybreakThunderbirdalotbecauseithandles *<blockquote>inaspecialpathand<div>(genericblockelement)isnot *treatedasaformatnodeandthesethingsmaybeusedfordesigning *currentrolesoftheelementsintheemailcomposerofThunderbird. *Therefore,wecreateanewmodeforHTMLFormatBlockCommandtoalign *thebehaviortotheothersbutdoesnotharmThunderbird.
*/ enumclass FormatBlockMode { // Document.execCommand("formatBlock"). Cannot set new format to "normal" // nor "". So, the paths to handle these ones are not used in this mode.
HTMLFormatBlockCommand, // cmd_paragraphState. Can set new format to "normal" or "" to remove // ancestor format blocks.
XULParagraphStateCommand,
};
/** *RetruntrueifthespecifiedlinebreakcanbeinsertedaroundaContent. *IfaContentisanElement,thischeckswhethertheelementcanhavethe *linebreak. *IfaContentisaText,thischeckwhetheritscontainerelementcanhave *thelinebreak.
*/
[ filejava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
public:
java.lang.StringIndexOutOfBoundsException: Range [5, 3) out of bounds for length 5
* If aPointToInsert is between line breaks or block boundaries, this
* puts a <br> element to make an empty line between them.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateLineBreakResult, nsresult>
/** *DeleteTextAndTextNodesWithTransaction()removestextortextnodesin *thegivenrange.
*/ enumclass TreatEmptyTextNodes { // KeepIfContainerOfRangeBoundaries: // Will remove empty text nodes middle of the range, but keep empty // text nodes which are containers of range boundaries.
KeepIfContainerOfRangeBoundaries, // Remove: // Will remove all empty text nodes.
Remove, // RemoveAllEmptyInlineAncestors: // Will remove all empty text nodes and its inline ancestors which // become empty due to removing empty text nodes.
RemoveAllEmptyInlineAncestors,
}; template <typename EditorDOMPointType>
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CaretPoint, nsresult>
DeleteTextAndTextNodesWithTransaction( const EditorDOMPointType& aStartPoint, const EditorDOMPointType& aEndPoint,
TreatEmptyTextNodes aTreatEmptyTextNodes);
/** *CharPointTypeletthefollowinghelpermethodsof *ExtendRangeToDeleteWithNormalizingWhiteSpaces()knowwhattypeof *characterwillbepreviousornextcharpointafterdeletion.
*/ enumclass CharPointType {
TextEnd, // Start or end of the text (hardline break or replaced inline // element)
ASCIIWhiteSpace, // One of ASCII white-spaces (collapsible white-space)
NoBreakingSpace, // NBSP
VisibleChar, // Non-white-space characters
PreformattedChar, // Any character except a linefeed in a preformatted // node.
PreformattedLineBreak, // Preformatted linebreak
};
/** *CharPointDataletthefollowinghelpermethodsof *ExtendRangeToDeleteWithNormalizingWhiteSpaces()knowwhattypeof *characterwillbepreviousornextcharpointandthepointis *insameordifferenttextnodeafterdeletion.
*/ class MOZ_STACK_CLASS CharPointData final {
public:
CharPointData() = delete;
static CharPointData InDifferentTextNode(CharPointType aCharPointType) { return {aCharPointType, true};
} static CharPointData InSameTextNode(CharPointType aCharPointType) { // Let's mark this as in different text node if given one indicates // that there is end of text because it means that adjacent content // from point of text node view is another element. return {aCharPointType, aCharPointType == CharPointType::TextEnd};
}
/**
* GetSelectedElement() returns a "selected"element node. "selected" means:
* - there is only one selection range
* - the range starts from an element node or in an element
* - the range ends at immediately after same element
* - and the range does not include any other element nodes.
* Additionally, only when aTagName is nsGkAtoms::href, this thinks that an
* <a> element which has non-empty "href" attribute includes the range, the
* <a> element is selected.
*
* NOTE: This method is implementation of nsIHTMLEditor.getSelectedElement()
* and comm-central depends on this behavior. Therefore, if you need to use
* this method internally but you need to change, perhaps, you should create
* another method for avoiding breakage of comm-central apps.
*
* @param aTagName The atom of tag name in lower case. Set this to
* result of EditorUtils::GetTagNameAtom() if you have a
* tag name with nsString.
* If nullptr, this returns any element node or nullptr.
* If nsGkAtoms::href, this returns an <a> element which
* has non-empty "href" attribute or nullptr.
* If nsGkAtoms::anchor, this returns an <a> element which
* has non-empty "name" attribute or nullptr.
* Otherwise, returns an element node whose name is
* same as aTagName or nullptr.
* @param aRv Returns error code.
* @return A "selected"element.
*/
already_AddRefed<Element> GetSelectedElement(const nsAtom* aTagName,
ErrorResult& aRv);
/**
* GetFirstTableRowElement() returns the first <tr> element in the most
* nearest ancestor of aTableOrElementInTable or itself.
* When aTableOrElementInTable is neither <table> nor in a <table> element,
* returns NS_ERROR_FAILURE. However, if <table> does not have <tr> element,
* returns nullptr.
*
* @param aTableOrElementInTable <table> element or another element.
* If this is a <table> element, returns
* first <tr> element in it. Otherwise,
* returns first <tr> element in nearest
* ancestor <table> element.
*/
Result<RefPtr<Element>, nsresult> GetFirstTableRowElement(
const Element& aTableOrElementInTable) const;
/**
* GetNextTableRowElement() returns next <tr> element of aTableRowElement.
* This won't cross <table> element boundary but may cross table section
* elements like <tbody>.
* Note that if given element is <tr> but there is no next <tr> element, this
* returns nullptr but does not return error.
*
* @param aTableRowElement A <tr> element.
*/
Result<RefPtr<Element>, nsresult> GetNextTableRowElement(
const Element& aTableRowElement) const;
struct CellData;
/**
* CellIndexes store both row index and column index of a table cell.
*/
struct MOZ_STACK_CLASS CellIndexes final {
int32_t mRow;
int32_t mColumn;
/**
* This constructor initializes mRowIndex and mColumnIndex with indexes of
* aCellElement.
*
* @param aCellElement An <td> or <th> element.
*/
MOZ_CAN_RUN_SCRIPT CellIndexes(Element& aCellElement, PresShell* aPresShell)
: mRow(-1), mColumn(-1) {
Update(aCellElement, aPresShell);
}
/**
* Update mRowIndex and mColumnIndex with indexes of aCellElement.
*
* @param See above.
*/
MOZ_CAN_RUN_SCRIPT void Update(Element& aCellElement,
PresShell* aPresShell);
/**
* This constructor initializes mRowIndex and mColumnIndex with indexes of
* cell element which contains anchor of Selection.
*
* @param aHTMLEditor The editor which creates the instance.
* @param aSelection The Selection for the editor.
*/
MOZ_CAN_RUN_SCRIPT CellIndexes(HTMLEditor& aHTMLEditor,
Selection& aSelection)
: mRow(-1), mColumn(-1) {
Update(aHTMLEditor, aSelection);
}
/**
* Update mRowIndex and mColumnIndex with indexes of cell element which
* contains anchor of Selection.
*
* @param See above.
*/
MOZ_CAN_RUN_SCRIPT void Update(HTMLEditor& aHTMLEditor,
Selection& aSelection);
struct MOZ_STACK_CLASS CellData final {
MOZ_KNOWN_LIVE RefPtr<Element> mElement;
// Current indexes which this is initialized with.
CellIndexes mCurrent;
// First column/row indexes of the cell. When current position is spanned
// from other column/row, this value becomes different from mCurrent.
CellIndexes mFirst;
// Computed rowspan/colspan values which are specified to the cell.
// Note that if the cell has larger rowspan/colspan value than actual
// table size, these values are the larger values.
int32_t mRowSpan = -1;
int32_t mColSpan = -1;
// Effective rowspan/colspan value at the index. For example, if first
// cell element in first row has rowspan="3", then, if this is initialized
// with 0-0 indexes, effective rowspan is 3. However, if this is
// initialized with 1-0 indexes, effective rowspan is 2.
int32_t mEffectiveRowSpan = -1;
int32_t mEffectiveColSpan = -1;
// mIsSelected is set to true if mElement itself or its parent <tr> or
// <table> is selected. Otherwise, e.g., the cell just contains selection
// range, this is set to false.
bool mIsSelected = false;
CellData() = delete;
/**
* This returns an instance which is initialized with a <table> element and
* both row and column index to specify a cell element.
*/
[[nodiscard]] static CellData AtIndexInTableElement(
const HTMLEditor& aHTMLEditor, const Element& aTableElement,
int32_t aRowIndex, int32_t aColumnIndex);
[[nodiscard]] static CellData AtIndexInTableElement(
const HTMLEditor& aHTMLEditor, const Element& aTableElement,
const CellIndexes& aIndexes) {
MOZ_ASSERT(!aIndexes.isErr());
return AtIndexInTableElement(aHTMLEditor, aTableElement, aIndexes.mRow,
aIndexes.mColumn);
}
/**
* Treated as error if fails to compute current index or first index of the
* cell. Note that even if the cell is not found due to no corresponding
* frame at current index, it's not an error situation.
*/
[[nodiscard]] bool isOk() const { return !isErr(); }
[[nodiscard]] bool isErr() const { return mFirst.isErr(); }
/**
* FailedOrNotFound() returns true if this failed to initialize/update
* or succeeded but found no cell element.
*/
[[nodiscard]] bool FailedOrNotFound() const { return isErr() || !mElement; }
/**
* IsSpannedFromOtherRowOrColumn(), IsSpannedFromOtherColumn and
* IsSpannedFromOtherRow() return true if there is no cell element
* at the index because of spanning from other row and/or column.
*/
[[nodiscard]] bool IsSpannedFromOtherRowOrColumn() const {
return mElement && mCurrent != mFirst;
}
[[nodiscard]] bool IsSpannedFromOtherColumn() const {
return mElement && mCurrent.mColumn != mFirst.mColumn;
}
[[nodiscard]] bool IsSpannedFromOtherRow() const {
return mElement && mCurrent.mRow != mFirst.mRow;
}
[[nodiscard]] bool IsNextColumnSpannedFromOtherColumn() const {
return mElement && mCurrent.mColumn + 1 < NextColumnIndex();
}
/**
* NextColumnIndex() and NextRowIndex() return column/row index of
* next cell. Note that this does not check whether there is next
* cell or not actually.
*/
[[nodiscard]] int32_t NextColumnIndex() const {
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mCurrent.mColumn + mEffectiveColSpan;
}
[[nodiscard]] int32_t NextRowIndex() const {
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mCurrent.mRow + mEffectiveRowSpan;
}
/**
* LastColumnIndex() and LastRowIndex() return column/row index of
* column/row which is spanned by the cell.
*/
[[nodiscard]] int32_t LastColumnIndex() const {
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return NextColumnIndex() - 1;
}
[[nodiscard]] int32_t LastRowIndex() const {
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return NextRowIndex() - 1;
}
/**
* NumberOfPrecedingColmuns() and NumberOfPrecedingRows() return number of
* preceding columns/rows if current index is spanned from other column/row.
* Otherwise, i.e., current point is not spanned form other column/row,
* returns 0.
*/
[[nodiscard]] int32_t NumberOfPrecedingColmuns() const {
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mCurrent.mColumn - mFirst.mColumn;
}
[[nodiscard]] int32_t NumberOfPrecedingRows() const {
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mCurrent.mRow - mFirst.mRow;
}
/**
* NumberOfFollowingColumns() and NumberOfFollowingRows() return
* number of remaining columns/rows if the cell spans to other
* column/row.
*/
[[nodiscard]] int32_t NumberOfFollowingColumns() const {
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mEffectiveColSpan - 1;
}
[[nodiscard]] int32_t NumberOfFollowingRows() const {
if (NS_WARN_IF(FailedOrNotFound())) {
return -1;
}
return mEffectiveRowSpan - 1;
}
/**
* TableSize stores and computes number of rows and columns of a <table>
* element.
*/
struct MOZ_STACK_CLASS TableSize final {
int32_t mRowCount;
int32_t mColumnCount;
TableSize() = delete;
/**
* @param aHTMLEditor The editor which creates the instance.
* @param aTableOrElementInTable If a <table> element, computes number
* of rows and columns of it.
* If another element in a <table> element,
* computes number of rows and columns
* of nearest ancestor <table> element.
* Otherwise, i.e., non-<table> element
* not in <table>, returns error.
*/
[[nodiscard]] static Result<TableSize, nsresult> Create(
HTMLEditor& aHTMLEditor, Element& aTableOrElementInTable);
/**
* GetTableCellElementAt() returns a <td> or <th> element of aTableElement
* if there is a cell at the indexes.
*
* @param aTableElement Must be a <table> element.
* @param aCellIndexes Indexes of cell which you want.
* If rowspan and/or colspan is specified 2 or
* larger, any indexes are allowed to retrieve
* the cell in the area.
* @return The cell element if there is in the <table>.
* Returns nullptr without error if the indexes
* are out of bounds.
*/
[[nodiscard]] inline Element* GetTableCellElementAt( Element& aTableElement, const CellIndexes& aCellIndexes) const;
[[nodiscard]] Element* GetTableCellElementAt(Element& aTableElement,
int32_t aRowIndex,
int32_t aColumnIndex) const;
/**
* GetSelectedOrParentTableElement() returns <td>, <th>, <tr> or <table>
* element:
* #1 if the first selection range selects a cell, returns it.
* #2 if the first selection range does not select a cell and
* the selection anchor refers a <table>, returns it.
* #3 if the first selection range does not select a cell and
* the selection anchor refers a <tr>, returns it.
* #4 if the first selection range does not select a cell and
* the selection anchor refers a <td>, returns it.
* #5 otherwise, nearest ancestor <td> or <th> element of the
* selection anchor if there is.
* In #1 and #4, *aIsCellSelected will be set to true (i.e,, when
* a selection range selects a cell element).
*/
Result<RefPtr<Element>, nsresult> GetSelectedOrParentTableElement(
bool* aIsCellSelected = nullptr) const;
/**
* GetFirstSelectedCellElementInTable() returns <td> or <th> element at
* first selection (using GetSelectedOrParentTableElement). If found cell
* element is not in <table> or <tr> element, this returns nullptr.
*/
Result<RefPtr<Element>, nsresult> GetFirstSelectedCellElementInTable() const;
/**
* PasteInternal() pasts text with replacing selected content.
* This tries to dispatch ePaste event first. If its defaultPrevent() is
* called, this does nothing but returns NS_OK.
*
* @param aClipboardType nsIClipboard::kGlobalClipboard or
* nsIClipboard::kSelectionClipboard.
* @param aEditingHost The editing host.
*/
MOZ_CAN_RUN_SCRIPT nsresult
PasteInternal(nsIClipboard::ClipboardType aClipboardType,
DataTransfer* aDataTransfer, const Element& aEditingHost);
/**
* InsertAsCitedQuotationInternal() inserts a <blockquote> element whose
* cite attribute is aCitation and whose content is aQuotedText.
* Note that this shouldn't be called when IsPlaintextMailComposer() is true.
*
* @param aQuotedText HTML source if aInsertHTML is true. Otherwise,
* plain text. This is inserted into new <blockquote>
* element.
* @param aCitation cite attribute value of new <blockquote> element.
* @param aInsertHTML true if aQuotedText should be treated as HTML
* source.
* false if aQuotedText should be treated as plain
* text.
* @param aEditingHost The editing host.
* @param aNodeInserted [OUT] The new <blockquote> element.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertAsCitedQuotationInternal(
const nsAString& aQuotedText, const nsAString& aCitation,
bool aInsertHTML, const Element& aEditingHost, nsINode** aNodeInserted);
/**
* InsertNodeIntoProperAncestorWithTransaction() attempts to insert aNode
* into the document, at aPointToInsert. Checks with strict dtd to see if
* containment is allowed. If not allowed, will attempt to find a parent
* in the parent hierarchy of aPointToInsert.GetContainer() that will accept
* aNode as a child. If such a parent is found, will split the document
* tree from aPointToInsert up to parent, and then insert aNode.
* aPointToInsert is then adjusted to point to the actual location that
* aNode was inserted at. aSplitAtEdges specifies if the splitting process
* is allowed to result in empty nodes.
*
* @param aContent The content node to insert.
* @param aPointToInsert Insertion point.
* @param aSplitAtEdges Splitting can result in empty nodes?
*/
template <typename NodeType>
[[nodiscard]] MOZ_CAN_RUN_SCRIPT
Result<CreateNodeResultBase<NodeType>, nsresult>
InsertNodeIntoProperAncestorWithTransaction(
NodeType& aContent, const EditorDOMPoint& aPointToInsert,
SplitAtEdges aSplitAtEdges);
/**
* InsertTextWithQuotationsInternal() replaces selection with new content.
* First, this method splits aStringToInsert to multiple chunks which start
* with non-linebreaker except first chunk and end with a linebreaker except
* last chunk. Then, each chunk starting with ">" is inserted after wrapping
* with <span _moz_quote="true">, and each chunk not starting with ">" is
* inserted as normal text.
*/
MOZ_CAN_RUN_SCRIPT nsresult InsertTextWithQuotationsInternal(
const nsAString& aStringToInsert, const Element& aEditingHost);
/**
* ReplaceContainerWithTransactionInternal() is implementation of
* ReplaceContainerWithTransaction() and
* ReplaceContainerAndCloneAttributesWithTransaction().
*
* @param aOldContainer The element which will be replaced with new
* element.
* @param aTagName The name of new element node.
* @param aAttribute Attribute name which will be set to the new
* element. This will be ignored if
* aCloneAllAttributes is set to true.
* @param aAttributeValue Attribute value which will be set to
* aAttribute.
* @param aCloneAllAttributes If true, all attributes of aOldContainer will
* be copied to the new element.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult>
ReplaceContainerWithTransactionInternal(Element& aOldContainer,
const nsAtom& aTagName,
const nsAtom& aAttribute,
const nsAString& aAttributeValue,
bool aCloneAllAttributes);
/**
* DeleteSelectionAndCreateElement() creates a element whose name is aTag.
* And insert it into the DOM tree after removing the selected content.
*
* @param aTag The element name to be created.
* @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.
*/
MOZ_CAN_RUN_SCRIPT Result<RefPtr<Element>, nsresult>
DeleteSelectionAndCreateElement(
nsAtom& aTag,
const InitializeInsertingElement& aInitializer = DoNothingForNewElement);
/**
* This method first deletes the selection, if it's not collapsed. Then if
* the selection lies in a CharacterData node, it splits it. If the
* selection is at this point collapsed in a CharacterData node, it's
* adjusted to be collapsed right before or after the node instead (which is
* always possible, since the node was split).
*/
MOZ_CAN_RUN_SCRIPT nsresult DeleteSelectionAndPrepareToCreateNode();
/**
* PrepareToInsertLineBreak() returns a point where a new line break node
* should be inserted. If aPointToInsert points middle of a text node, this
* method splits the text node and returns the point before right node.
*
* @param aLineBreakType Whether you will insert <br> or a preformatted
* linefeed.
* @param aPointToInsert Candidate point to insert new line break node.
* @return Computed point to insert new line break node.
* If something failed, this return error.
*/
MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult> PrepareToInsertLineBreak(
LineBreakType aLineBreakType, const EditorDOMPoint& aPointToInsert);
enum class PreservePreformattedLineBreak : bool { No, Yes };
/**
* If unnecessary line break is there immediately after aPoint, this deletes
* the line break. Note that unnecessary line break means that the line break
* is a padding line break for empty line immediately before a block boundary
* and it's not a placeholder of ancestor inline elements.
*
* @param aNextOrAfterModifiedPoint
* If you inserted something, this should be next
* point or after the inserted content. If you
* deleted something, this should be end of the
* deleted range.
* @param aPreservePreformattedLineBreak
* Whether this method should preserve preformatted
* linefeed or not. If "Yes", this will delete only
* when the unnecessary line break is a <br>.
* @param aPaddingForEmptyBlock
* Treat the line break for empty block is
* unnecessary or not. If this is set to
* "Unnecessary", preformatted line break will be
* deleted if it's a padding for empty block and
* aPreservePreformattedLineBreak is "Yes".
* This should be "Significant" if you use this
* after handling the edit action. So, you can use
* "Unnecessary" only when you are calling this
* before handling the edit action.
* @param aEditingHost The editing host containing
* aNextOrAfterModifiedPoint.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
EnsureNoFollowingUnnecessaryLineBreak(
const EditorDOMPoint& aNextOrAfterModifiedPoint,
PreservePreformattedLineBreak aPreservePreformattedLineBreak,
PaddingForEmptyBlock aPaddingForEmptyBlock, const Element& aEditingHost);
/**
* IndentAsSubAction() indents the content around Selection.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult>
IndentAsSubAction(const Element& aEditingHost);
/**
* OutdentAsSubAction() outdents the content around Selection.
*
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult>
OutdentAsSubAction(const Element& aEditingHost);
/**
* UpdateMetaCharsetWithTransaction() scans all <meta> elements in the
* document and if and only if there is a <meta> element having `httpEquiv`
* attribute and whose value includes `content-type`, updates its `content`
* attribute value to aCharacterSet.
*/
MOZ_CAN_RUN_SCRIPT bool UpdateMetaCharsetWithTransaction(
Document& aDocument, const nsACString& aCharacterSet);
/**
* SetInlinePropertiesAsSubAction() stores new styles with
* mPendingStylesToApplyToNewContent if `Selection` is collapsed. Otherwise,
* applying the styles to all selected contents.
*
* @param aStylesToSet The styles which should be applied to the
* selected content.
* @param aEditingHost The editing host.
*/
template <size_t N>
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertiesAsSubAction(
const AutoTArray<EditorInlineStyleAndValue, N>& aStylesToSet,
const Element& aEditingHost);
/**
* SetInlinePropertiesAroundRanges() applying the styles to the ranges even if
* the ranges are collapsed.
*/
template <size_t N>
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetInlinePropertiesAroundRanges(
AutoClonedRangeArray& aRanges,
const AutoTArray<EditorInlineStyleAndValue, N>& aStylesToSet,
const Element& aEditingHost);
/**
* RemoveInlinePropertiesAsSubAction() removes specified styles from
* mPendingStylesToApplyToNewContent if `Selection` is collapsed. Otherwise,
* removing the style.
*
* @param aStylesToRemove Styles to remove from the selected contents.
* @param aEditingHost The editing host.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RemoveInlinePropertiesAsSubAction(
const nsTArray<EditorInlineStyle>& aStylesToRemove,
const Element& aEditingHost);
/**
* Helper method to call RemoveInlinePropertiesAsSubAction(). If you want to
* remove other elements to remove the style completely, this will append
* related elements of aStyleToRemove and aStyleToRemove itself to the array.
* E.g., nsGkAtoms::strong and nsGkAtoms::b will be appended if aStyleToRemove
* is nsGkAtoms::b.
*/
void AppendInlineStyleAndRelatedStyle(
const EditorInlineStyle& aStyleToRemove,
nsTArray<EditorInlineStyle>& aStylesToRemove) const;
enum class RetrievingBackgroundColorOption {
// Ignore inline styles, i.e., return only background color of ancestor
// blocks.
OnlyBlockBackgroundColor,
// Stop at nearest inclusive ancestor block.
StopAtInclusiveAncestorBlock,
// Return default background color for the document if there is no ancestor
// elements which have background color.
DefaultColorIfNoSpecificBackgroundColor,
};
using RetrievingBackgroundColorOptions =
EnumSet<RetrievingBackgroundColorOption>;
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
GetCSSBackgroundColorState(bool* aMixed, nsAString& aOutColor,
RetrievingBackgroundColorOptions aOptions);
/**
* This sets background on the appropriate container element (table, cell,)
* or calls to set the page background.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
SetBlockBackgroundColorWithCSSAsSubAction(const nsAString& aColor);
MOZ_CAN_RUN_SCRIPT nsresult
SetHTMLBackgroundColorWithTransaction(const nsAString& aColor);
/**
* Make the given selection span the entire document.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SelectEntireDocument() final;
/**
* Use this to assure that selection is set after attribute nodes when
* trying to collapse selection at begining of a block node
* e.g., when setting at beginning of a table cell
* This will stop at a table, however, since we don't want to
* "drill down" into nested tables.
*/
MOZ_CAN_RUN_SCRIPT void CollapseSelectionToDeepestNonTableFirstChild(
nsINode* aNode);
/**
* MaybeCollapseSelectionAtFirstEditableNode() may collapse selection at
* proper position to staring to edit. If there is a non-editable node
* before any editable text nodes or inline elements which can have text
* nodes as their children, collapse selection at start of the editing
* host. If there is an editable text node which is not collapsed, collapses
* selection at the start of the text node. If there is an editable inline
* element which cannot have text nodes as its child, collapses selection at
* before the element node. Otherwise, collapses selection at start of the
* editing host.
*
* @param aIgnoreIfSelectionInEditingHost
* This method does nothing if selection is in the
* editing host except if it's collapsed at start of
* the editing host.
* Note that if selection ranges were outside of
* current selection limiter, selection was collapsed
* at the start of the editing host therefore, if
* you call this with setting this to true, you can
* keep selection ranges if user has already been
* changed.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
MaybeCollapseSelectionAtFirstEditableNode(
bool aIgnoreIfSelectionInEditingHost) const;
/**
* Join aLeftText and aRightText with normalizing white-spaces at the joining
* point if it's required. aRightText must be the next sibling of aLeftText.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<JoinNodesResult, nsresult>
JoinTextNodesWithNormalizeWhiteSpaces(Text& aLeftText, Text& aRightText);
class BlobReader final {
using AutoEditActionDataSetter = EditorBase::AutoEditActionDataSetter;
/**
* Get the focused node of this editor.
* @return If the editor has focus, this returns the focused node.
* Otherwise, returns null.
*/
nsINode* GetFocusedNode() const;
/**
* Return TRUE if aElement is a table-related elemet and caret was set.
*/
MOZ_CAN_RUN_SCRIPT bool SetCaretInTableCell(dom::Element* aElement);
/**
* HandleTabKeyPressInTable() handles "Tab" key press in table if selection
* is in a `<table>` element.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditActionResult, nsresult>
HandleTabKeyPressInTable(WidgetKeyboardEvent* aKeyboardEvent);
/**
* InsertPosition is an enum to indicate where the method should insert to.
*/
enum class InsertPosition {
// Before selected cell or a cell containing first selection range.
eBeforeSelectedCell,
// After selected cell or a cell containing first selection range.
eAfterSelectedCell,
};
/**
* InsertTableCellsWithTransaction() inserts <td> elements at aPointToInsert.
* Note that this simply inserts <td> elements, i.e., colspan and rowspan
* around the cell containing selection are not modified. So, for example,
* adding a cell to rectangular table changes non-rectangular table.
* And if the cell containing selection is at left of row-spanning cell,
* it may be moved to right side of the row-spanning cell after inserting
* some cell elements before it. Similarly, colspan won't be adjusted
* for keeping table rectangle.
* Finally, puts caret into previous cell of the insertion point or the
* first inserted cell if aPointToInsert is start of the row.
*
* @param aPointToInsert The place to insert one or more cell
* elements. The container must be a
* <tr> element.
* @param aNumberOfCellsToInsert Number of cells to insert.
* @return The first inserted cell element and
* start of the last inserted cell element
* as a point to put caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<CreateElementResult, nsresult>
InsertTableCellsWithTransaction(const EditorDOMPoint& aPointToInsert,
int32_t aNumberOfCellsToInsert);
/**
* InsertTableColumnsWithTransaction() inserts cell elements to every rows
* at same column index as the cell specified by aPointToInsert.
*
* @param aNumberOfColumnsToInsert Number of columns to insert.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertTableColumnsWithTransaction(
const EditorDOMPoint& aPointToInsert, int32_t aNumberOfColumnsToInsert);
/**
* InsertTableRowsWithTransaction() inserts <tr> elements before or after
* aCellElement. When aCellElement spans rows and aInsertPosition is
* eAfterSelectedCell, new rows will be inserted after the most-bottom row
* which contains the cell.
*
* @param aCellElement The cell element pinting where this will
* insert a row before or after.
* @param aNumberOfRowsToInsert Number of rows to insert.
* @param aInsertPosition Before or after the target cell which
* contains first selection range.
*/
MOZ_CAN_RUN_SCRIPT nsresult InsertTableRowsWithTransaction( Element& aCellElement, int32_t aNumberOfRowsToInsert,
InsertPosition aInsertPosition);
/**
* Insert a new cell after or before supplied aCell.
* Optional: If aNewCell supplied, returns the newly-created cell (addref'd,
* of course)
* This doesn't change or use the current selection.
*/
MOZ_CAN_RUN_SCRIPT nsresult InsertCell(Element* aCell, int32_t aRowSpan,
int32_t aColSpan, bool aAfter,
bool aIsHeader, Element** aNewCell);
/**
* DeleteSelectedTableColumnsWithTransaction() removes cell elements which
* belong to same columns of selected cell elements.
* If only one cell element is selected or first selection range is
* in a cell, removes cell elements which belong to same column.
* If 2 or more cell elements are selected, removes cell elements which
* belong to any of all selected columns. In this case,
* aNumberOfColumnsToDelete is ignored.
* If there is no selection ranges, returns error.
* If selection is not in a cell element, this does not return error,
* just does nothing.
* WARNING: This does not remove <col> nor <colgroup> elements.
*
* @param aNumberOfColumnsToDelete Number of columns to remove. This is
* ignored if 2 ore more cells are
* selected.
*/
MOZ_CAN_RUN_SCRIPT nsresult
DeleteSelectedTableColumnsWithTransaction(int32_t aNumberOfColumnsToDelete);
/**
* DeleteTableColumnWithTransaction() removes cell elements which belong
* to the specified column.
* This method adjusts colspan attribute value if cells spanning the
* column to delete.
* WARNING: This does not remove <col> nor <colgroup> elements.
*
* @param aTableElement The <table> element which contains the
* column which you want to remove.
* @param aRowIndex Index of the column which you want to remove.
* 0 is the first column.
*/
MOZ_CAN_RUN_SCRIPT nsresult DeleteTableColumnWithTransaction( Element& aTableElement, int32_t aColumnIndex);
/**
* DeleteSelectedTableRowsWithTransaction() removes <tr> elements.
* If only one cell element is selected or first selection range is
* in a cell, removes <tr> elements starting from a <tr> element
* containing the selected cell or first selection range.
* If 2 or more cell elements are selected, all <tr> elements
* which contains selected cell(s). In this case, aNumberOfRowsToDelete
* is ignored.
* If there is no selection ranges, returns error.
* If selection is not in a cell element, this does not return error,
* just does nothing.
*
* @param aNumberOfRowsToDelete Number of rows to remove. This is ignored
* if 2 or more cells are selected.
*/
MOZ_CAN_RUN_SCRIPT nsresult
DeleteSelectedTableRowsWithTransaction(int32_t aNumberOfRowsToDelete);
/**
* DeleteTableRowWithTransaction() removes a <tr> element whose index in
* the <table> is aRowIndex.
* This method adjusts rowspan attribute value if the <tr> element contains
* cells which spans rows.
*
* @param aTableElement The <table> element which contains the
* <tr> element which you want to remove.
* @param aRowIndex Index of the <tr> element which you want to
* remove. 0 is the first row.
*/
MOZ_CAN_RUN_SCRIPT nsresult
DeleteTableRowWithTransaction(Element& aTableElement, int32_t aRowIndex);
/**
* DeleteTableCellWithTransaction() removes table cell elements. If two or
* more cell elements are selected, this removes all selected cell elements.
* Otherwise, this removes some cell elements starting from selected cell
* element or a cell containing first selection range. When this removes
* last cell element in <tr> or <table>, this removes the <tr> or the
* <table> too. Note that when removing a cell causes number of its row
* becomes less than the others, this method does NOT fill the place with
* rowspan nor colspan. This does not return error even if selection is not
* in cell element, just does nothing.
*
* @param aNumberOfCellsToDelete Number of cells to remove. This is ignored
* if 2 or more cells are selected.
*/
MOZ_CAN_RUN_SCRIPT nsresult
DeleteTableCellWithTransaction(int32_t aNumberOfCellsToDelete);
/**
* DeleteAllChildrenWithTransaction() removes all children of aElement from
* the tree.
*
* @param aElement The element whose children you want to remove.
*/
MOZ_CAN_RUN_SCRIPT nsresult
DeleteAllChildrenWithTransaction(Element& aElement);
/**
* Move all contents from aCellToMerge into aTargetCell (append at end).
*/
MOZ_CAN_RUN_SCRIPT nsresult MergeCells(RefPtr<Element> aTargetCell,
RefPtr<Element> aCellToMerge,
bool aDeleteCellToMerge);
/**
* DeleteTableElementAndChildren() removes aTableElement (and its children)
* from the DOM tree with transaction.
*
* @param aTableElement The <table> element which you want to remove.
*/
MOZ_CAN_RUN_SCRIPT nsresult
DeleteTableElementAndChildrenWithTransaction(Element& aTableElement);
/**
* Helper used to get nsTableWrapperFrame for a table.
*/
static nsTableWrapperFrame* GetTableFrame(const Element* aTable);
/**
* GetNumberOfCellsInRow() returns number of actual cell elements in the row.
* If some cells appear by "rowspan" in other rows, they are ignored.
*
* @param aTableElement The <table> element.
* @param aRowIndex Valid row index in aTableElement. This method
* counts cell elements in the row.
* @return -1 if this meets unexpected error.
* Otherwise, number of cells which this method found.
*/
int32_t GetNumberOfCellsInRow(Element& aTableElement, int32_t aRowIndex);
/**
* Test if all cells in row or column at given index are selected.
*/
bool AllCellsInRowSelected(Element* aTable, int32_t aRowIndex,
int32_t aNumberOfColumns);
bool AllCellsInColumnSelected(Element* aTable, int32_t aColIndex,
int32_t aNumberOfRows);
bool IsEmptyCell(Element* aCell);
/**
* Most insert methods need to get the same basic context data.
* Any of the pointers may be null if you don't need that datum (for more
* efficiency).
* Input: *aCell is a known cell,
* if null, cell is obtained from the anchor node of the selection.
* Returns NS_EDITOR_ELEMENT_NOT_FOUND if cell is not found even if aCell is
* null.
*/
MOZ_CAN_RUN_SCRIPT nsresult GetCellContext(Element** aTable, Element** aCell,
nsINode** aCellParent,
int32_t* aCellOffset,
int32_t* aRowIndex,
int32_t* aColIndex);
/**
* XXX NormalizeTableInternal() is broken. If it meets a cell which has
* bigger or smaller rowspan or colspan than actual number of cells,
* this always failed to scan the table. Therefore, this does nothing
* when the table should be normalized.
*
* @param aTableOrElementInTable An element which is in a <table> element
* or <table> element itself. Otherwise,
* this returns NS_OK but does nothing.
*/
MOZ_CAN_RUN_SCRIPT nsresult
NormalizeTableInternal(Element& aTableOrElementInTable);
/**
* Fallback method: Call this after using ClearSelection() and you
* failed to set selection to some other content in the document.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetSelectionAtDocumentStart();
enum class AddCites { No, Yes };
/**
* Insert a string as quoted text, replacing the selected text (if any).
* @param aQuotedText The string to insert.
* @param aAddCites Whether to prepend extra ">" to each line
* (usually true, unless those characters
* have already been added.)
* @param aEditingHost The editing host.
* @return aNodeInserted The node spanning the insertion, if applicable.
* If aAddCites is false, this will be null.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertAsPlaintextQuotation(
const nsAString& aQuotedText, AddCites aAddCites,
const Element& aEditingHost, nsINode** aNodeInserted = nullptr);
/**
* InsertObject() inserts given object at aPointToInsert.
*
* @param aType one of kFileMime, kJPEGImageMime, kJPGImageMime,
* kPNGImageMime, kGIFImageMime.
*/
MOZ_CAN_RUN_SCRIPT nsresult InsertObject(
const nsACString& aType, nsISupports* aObject,
SafeToInsertData aSafeToInsertData, const EditorDOMPoint& aPointToInsert,
DeleteSelectedContent aDeleteSelectedContent,
const Element& aEditingHost);
class HTMLTransferablePreparer;
nsresult PrepareHTMLTransferable(nsITransferable** aTransferable,
const Element* aEditingHost) const;
/**
* InsertFromDataTransfer() is called only when user drops data into
* this editor. Don't use this method for other purposes.
*
* @param aIndex index of aDataTransfer's item to insert.
*/
MOZ_CAN_RUN_SCRIPT nsresult InsertFromDataTransfer(
const DataTransfer* aDataTransfer, uint32_t aIndex,
nsIPrincipal* aSourcePrincipal, const EditorDOMPoint& aDroppedAt,
DeleteSelectedContent aDeleteSelectedContent,
const Element& aEditingHost);
/**
* CF_HTML:
* <https://docs.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format>.
*
* @param[in] aCfhtml a CF_HTML string as defined above.
* @param[out] aStuffToPaste the fragment, excluding context.
* @param[out] aCfcontext the context, excluding the fragment, including a
* marker (`kInsertionCookie`) indicating where the
* fragment begins.
*/
nsresult ParseCFHTML(const nsCString& aCfhtml, char16_t** aStuffToPaste,
char16_t** aCfcontext);
/**
* AutoHTMLFragmentBoundariesFixer fixes both edges of topmost child contents
* which are created with SubtreeContentIterator.
*/
class MOZ_STACK_CLASS AutoHTMLFragmentBoundariesFixer final { public:
/**
* @param aArrayOfTopMostChildContents
* [in/out] The topmost child contents which will be
* inserted into the DOM tree. Both edges, i.e.,
* first node and last node in this array will be
* checked whether they can be inserted into
* another DOM tree. If not, it'll replaces some
* orphan nodes around nodes with proper parent.
*/
explicit AutoHTMLFragmentBoundariesFixer(
nsTArray<OwningNonNull<nsIContent>>& aArrayOfTopMostChildContents);
private:
/**
* EnsureBeginsOrEndsWithValidContent() replaces some nodes starting from
* start or end with proper element node if it's necessary.
* If first or last node of aArrayOfTopMostChildContents is in list and/or
* `<table>` element, looks for topmost list element or `<table>` element
* with `CollectTableAndAnyListElementsOfInclusiveAncestorsAt()` and
* `GetMostDistantAncestorListOrTableElement()`. Then, checks
* whether some nodes are in aArrayOfTopMostChildContents are the topmost
* list/table element or its descendant and if so, removes the nodes from
* aArrayOfTopMostChildContents and inserts the list/table element instead.
* Then, aArrayOfTopMostChildContents won't start/end with list-item nor
* table cells.
*/
enum class StartOrEnd { start, end };
void EnsureBeginsOrEndsWithValidContent(
StartOrEnd aStartOrEnd,
nsTArray<OwningNonNull<nsIContent>>& aArrayOfTopMostChildContents)
const;
/**
* CollectTableAndAnyListElementsOfInclusiveAncestorsAt() collects list
* elements and table related elements from the inclusive ancestors
* (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor) of aNode.
*/
static void CollectTableAndAnyListElementsOfInclusiveAncestorsAt(
nsIContent& aContent,
nsTArray<OwningNonNull<Element>>& aOutArrayOfListAndTableElements);
/**
* GetMostDistantAncestorListOrTableElement() returns a list or a
* `<table>` element which is in
* aInclusiveAncestorsTableOrListElements and they are actually
* valid ancestor of at least one of aArrayOfTopMostChildContents.
*/
static Element* GetMostDistantAncestorListOrTableElement(
const nsTArray<OwningNonNull<nsIContent>>& aArrayOfTopMostChildContents,
const nsTArray<OwningNonNull<Element>>&
aInclusiveAncestorsTableOrListElements);
/**
* FindReplaceableTableElement() is a helper method of
* EnsureBeginsOrEndsWithValidContent(). If aNodeMaybeInTableElement is
* a descendant of aTableElement, returns aNodeMaybeInTableElement or its
* nearest ancestor whose tag name is `<td>`, `<th>`, `<tr>`, `<thead>`,
* `<tfoot>`, `<tbody>` or `<caption>`.
*
* @param aTableElement Must be a `<table>` element.
* @param aContentMaybeInTableElement A node which may be in aTableElement.
*/ Element* FindReplaceableTableElement( Element& aTableElement, nsIContent& aContentMaybeInTableElement) const;
/**
* IsReplaceableListElement() is a helper method of
* EnsureBeginsOrEndsWithValidContent(). If aNodeMaybeInListElement is a
* descendant of aListElement, returns true. Otherwise, false.
*
* @param aListElement Must be a list element.
* @param aContentMaybeInListElement A node which may be in aListElement.
*/
bool IsReplaceableListElement(Element& aListElement,
nsIContent& aContentMaybeInListElement) const;
};
/**
* MakeDefinitionListItemWithTransaction() replaces parent list of current
* selection with <dl> or create new <dl> element and creates a definition
* list item whose name is aTagName.
*
* @param aTagName Must be nsGkAtoms::dt or nsGkAtoms::dd.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
MakeDefinitionListItemWithTransaction(nsAtom& aTagName);
/**
* FormatBlockContainerAsSubAction() inserts a block element whose name
* is aTagName at selection. If selection is not collapsed and aTagName is
* nsGkAtoms::normal or nsGkAtoms::_empty, this removes block containers.
*
* @param aTagName A block level element name. Must NOT be
* nsGkAtoms::dt nor nsGkAtoms::dd.
* @param aFormatBlockMode Whether HTML formatBlock command or XUL
* paragraphState command.
* @param aEditingHost The editing host.
*/
MOZ_CAN_RUN_SCRIPT nsresult FormatBlockContainerAsSubAction(
const nsStaticAtom& aTagName, FormatBlockMode aFormatBlockMode,
const Element& aEditingHost);
/**
* Increase/decrease the font size of selection.
*/
MOZ_CAN_RUN_SCRIPT nsresult
IncrementOrDecrementFontSizeAsSubAction(FontSize aIncrementOrDecrement);
/**
* Wrap aContent in <big> or <small> element and make children of
* <font size=n> wrap with <big> or <small> too. Note that if there is
* opposite element for aIncrementOrDecrement, their children will be just
* unwrapped.
*
* @param aDir Whether increase or decrease the font size of aContent.
* @param aContent The content node whose font size will be changed.
* @return A suggest point to put caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
SetFontSizeWithBigOrSmallElement(nsIContent& aContent,
FontSize aIncrementOrDecrement);
/**
* Adjust font size of font element children recursively with handling
* <big> and <small> elements.
*
* @param aDir Whether increase or decrease the font size of aContent.
* @param aContent The content node whose font size will be changed.
* All descendants will be handled recursively.
* @return A suggest point to put caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
SetFontSizeOfFontElementChildren(nsIContent& aContent,
FontSize aIncrementOrDecrement);
/**
* Get extended range to select element whose all children are selected by
* aRange.
*/
EditorRawDOMRange GetExtendedRangeWrappingEntirelySelectedElements(
const EditorRawDOMRange& aRange) const;
/**
* Get extended range to select ancestor <a name> elements.
*/
EditorRawDOMRange GetExtendedRangeWrappingNamedAnchor(
const EditorRawDOMRange& aRange) const;
// Declared in HTMLEditorNestedClasses.h and defined in HTMLStyleEditor.cpp
class AutoInlineStyleSetter;
/**
* RemoveStyleInside() removes elements which represent aStyleToRemove
* and removes CSS style. This handles aElement and all its descendants
* (including leaf text nodes) recursively.
* TODO: Rename this to explain that this maybe remove aElement from the DOM
* tree.
*
* @param aSpecifiedStyle Whether the class and style attributes should
* be preserved or discarded.
* @return A suggest point to put caret.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
RemoveStyleInside(Element& aElement, const EditorInlineStyle& aStyleToRemove,
SpecifiedStyle aSpecifiedStyle);
/**
* CollectEditableLeafTextNodes() collects text nodes in aElement.
*/
void CollectEditableLeafTextNodes( Element& aElement, nsTArray<OwningNonNull<Text>>& aLeafTextNodes) const;
/**
* IsRemovableParentStyleWithNewSpanElement() checks whether aStyle of parent
* block can be removed from aContent with creating `<span>` element. Note
* that this does NOT check whether the specified style comes from parent
* block or not.
* XXX This may destroy the editor, but using `Result<bool, nsresult>`
* is not reasonable because code for accessing the result becomes
* messy. However, anybody must forget to check `Destroyed()` after
* calling this. Which is the way to smart to make every caller
* must check the editor state?
*/
MOZ_CAN_RUN_SCRIPT Result<bool, nsresult>
IsRemovableParentStyleWithNewSpanElement(
nsIContent& aContent, const EditorInlineStyle& aStyle) const;
/**
* HasStyleOrIdOrClassAttribute() returns true when at least one of
* `style`, `id` or `class` attribute value of aElement is not empty.
*/
static bool HasStyleOrIdOrClassAttribute(Element& aElement);
/**
* Whether the outer window of the DOM event target has focus or not.
*/
bool OurWindowHasFocus() const;
class HTMLWithContextInserter;
/**
* This function is used to insert a string of HTML input optionally with some
* context information into the editable field. The HTML input either comes
* from a transferable object created as part of a drop/paste operation, or
* from the InsertHTML method. We may want the HTML input to be sanitized
* (for example, if it's coming from a transferable object), in which case
* aTrustedInput should be set to false, otherwise, the caller should set it
* to true, which means that the HTML will be inserted in the DOM verbatim.
*/
enum class InlineStylesAtInsertionPoint {
Preserve, // If you want the paste to be affected by local style, e.g.,
// for the insertHTML command, use "Preserve"
Clear, // If you want the paste to be keep its own style, e.g., pasting
// from clipboard, use "Clear"
};
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult InsertHTMLWithContextAsSubAction(
const nsAString& aInputString, const nsAString& aContextStr,
const nsAString& aInfoStr, const nsAString& aFlavor,
SafeToInsertData aSafeToInsertData, const EditorDOMPoint& aPointToInsert,
DeleteSelectedContent aDeleteSelectedContent,
InlineStylesAtInsertionPoint aInlineStylesAtInsertionPoint,
const Element& aEditingHost);
/**
* sets the position of an element; warning it does NOT check if the
* element is already positioned or not and that's on purpose.
* @param aStyledElement [IN] the element
* @param aX [IN] the x position in pixels.
* @param aY [IN] the y position in pixels.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetTopAndLeftWithTransaction(
nsStyledElement& aStyledElement, int32_t aX, int32_t aY);
/**
* Reset a selected cell or collapsed selection (the caret) after table
* editing.
*
* @param aTable A table in the document.
* @param aRow The row ...
* @param aCol ... and column defining the cell where we will try to
* place the caret.
* @param aSelected If true, we select the whole cell instead of setting
* caret.
* @param aDirection If cell at (aCol, aRow) is not found, search for
* previous cell in the same column (aPreviousColumn) or
* row (ePreviousRow) or don't search for another cell
* (aNoSearch). If no cell is found, caret is place just
* before table; and if that fails, at beginning of
* document. Thus we generally don't worry about the
* return value and can use the
* AutoSelectionSetterAfterTableEdit stack-based object to
* insure we reset the caret in a table-editing method.
*/
MOZ_CAN_RUN_SCRIPT void SetSelectionAfterTableEdit(Element* aTable,
int32_t aRow, int32_t aCol,
int32_t aDirection,
bool aSelected);
/**
* RefreshEditingUI() may refresh editing UIs for current Selection, focus,
* etc. If this shows or hides some UIs, it causes reflow. So, this is
* not safe method.
*/
MOZ_CAN_RUN_SCRIPT nsresult RefreshEditingUI();
/**
* Returns the offset of an element's frame to its absolute containing block.
*/
nsresult GetElementOrigin(Element& aElement, int32_t& aX, int32_t& aY);
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult GetPositionAndDimensions( Element& aElement, int32_t& aX, int32_t& aY, int32_t& aW, int32_t& aH,
int32_t& aBorderLeft, int32_t& aBorderTop, int32_t& aMarginLeft,
int32_t& aMarginTop);
bool IsInObservedSubtree(nsIContent* aChild);
void UpdateRootElement();
/**
* SetAllResizersPosition() moves all resizers to proper position.
* If the resizers are hidden or replaced with another set of resizers
* while this is running, this returns error. So, callers shouldn't
* keep handling the resizers if this returns error.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult SetAllResizersPosition();
/**
* Shows active resizers around an element's frame
* @param aResizedElement [IN] a DOM Element
*/
MOZ_CAN_RUN_SCRIPT nsresult ShowResizersInternal(Element& aResizedElement);
/**
* Hide resizers if they are visible. If this is called while there is no
* visible resizers, this does not return error, but does nothing.
*/
nsresult HideResizersInternal();
/**
* RefreshResizersInternal() moves resizers to proper position. This does
* nothing if there is no resizing target.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RefreshResizersInternal();
/**
* SetShadowPosition() moves the shadow element to proper position.
*
* @param aShadowElement Must be mResizingShadow or mPositioningShadow.
* @param aElement The element which has the shadow.
* @param aElementX Left of aElement.
* @param aElementY Top of aElement.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
SetShadowPosition(Element& aShadowElement, Element& aElement,
int32_t aElementLeft, int32_t aElementTop);
/**
* HideAnonymousEditingUIs() forcibly hides all editing UIs (resizers,
* inline-table-editing UI, absolute positioning UI).
*
* XXX This method is called by the CC, therefore, needs to be a boundary
* method.
*/
MOZ_CAN_RUN_SCRIPT_BOUNDARY void HideAnonymousEditingUIs();
/**
* HideAnonymousEditingUIsIfUnnecessary() hides all editing UIs if some of
* visible UIs are now unnecessary.
*/
MOZ_CAN_RUN_SCRIPT void HideAnonymousEditingUIsIfUnnecessary();
/**
* sets the z-index of an element.
* @param aElement [IN] the element
* @param aZorder [IN] the z-index
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
SetZIndexWithTransaction(nsStyledElement& aElement, int32_t aZIndex);
/**
* shows a grabber attached to an arbitrary element. The grabber is an image
* positioned on the left hand side of the top border of the element. Draggin
* and dropping it allows to change the element's absolute position in the
* document. See chrome://editor/content/images/grabber.gif
* @param aElement [IN] the element
*/
MOZ_CAN_RUN_SCRIPT nsresult ShowGrabberInternal(Element& aElement);
/**
* Setting grabber to proper position for current mAbsolutelyPositionedObject.
* For example, while an element has grabber, the element may be resized
* or repositioned by script or something. Then, you need to reset grabber
* position with this.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult RefreshGrabberInternal();
/**
* hide the grabber if it shown.
*/
MOZ_CAN_RUN_SCRIPT void HideGrabberInternal();
/**
* CreateGrabberInternal() creates a grabber for moving aParentContent.
* This sets mGrabber to the new grabber. If this returns true, it's
* always non-nullptr. Otherwise, i.e., the grabber is hidden during
* creation, this returns false.
*/
bool CreateGrabberInternal(nsIContent& aParentContent);
/**
* Shows inline table editing UI around a <table> element which contains
* aCellElement. This returns error if creating UI is hidden during this,
* or detects another set of UI during this. In such case, callers
* shouldn't keep handling anything for the UI.
*
* @param aCellElement Must be an <td> or <th> element.
*/
MOZ_CAN_RUN_SCRIPT nsresult
ShowInlineTableEditingUIInternal(Element& aCellElement);
/**
* RefreshInlineTableEditingUIInternal() moves inline table editing UI to
* proper position. This returns error if the UI is hidden or replaced
* during moving.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
RefreshInlineTableEditingUIInternal();
/**
* Returns an anonymous Element of type aTag,
* child of aParentContent. If aIsCreatedHidden is true, the class
* "hidden" is added to the created element. If aClass is not the empty
* string, it becomes the value of the class attribute
* @return a Element
* @param aTag [IN] desired type of the element to create
* @param aParentContent [IN] the parent node of the created anonymous
* element
* @param aClass [IN] contents of the _moz_anonclass attribute
* @param aIsCreatedHidden [IN] a boolean specifying if the class "hidden"
* is to be added to the created anonymous
* element
*/
ManualNACPtr CreateAnonymousElement(nsAtom* aTag, nsIContent& aParentContent,
const nsAString& aClass,
bool aIsCreatedHidden);
/**
* Reads a blob into memory and notifies the BlobReader object when the read
* operation is finished.
*
* @param aBlob The input blob
* @param aGlobal The global object under which the read should happen.
* @param aBlobReader The blob reader object to be notified when finished.
*/
static nsresult SlurpBlob(dom::Blob* aBlob, nsIGlobalObject* aGlobal,
BlobReader* aBlobReader);
/**
* For saving allocation cost in the constructor of
* EditorBase::TopLevelEditSubActionData, we should reuse same RangeItem
* instance with all top level edit sub actions.
* The instance is always cleared when TopLevelEditSubActionData is
* destructed and the class is stack only class so that we don't need
* to (and also should not) add the RangeItem into the cycle collection.
*/
[[nodiscard]] inline already_AddRefed<RangeItem>
GetSelectedRangeItemForTopLevelEditSubAction() const;
/**
* For saving allocation cost in the constructor of
* EditorBase::TopLevelEditSubActionData, we should reuse same nsRange
* instance with all top level edit sub actions.
* The instance is always cleared when TopLevelEditSubActionData is
* destructed, but AbstractRange::mOwner keeps grabbing the owner document
* so that we need to make it in the cycle collection.
*/
[[nodiscard]] inline already_AddRefed<nsRange>
GetChangedRangeForTopLevelEditSubAction() const;
protected:
/**
* IndentListChildWithTransaction() is a helper method of
* Handle(CSS|HTML)IndentAtSelectionInternal().
*
* @param aSubListElement [in/out] Specify a sub-list element of the
* container of aPointInListElement or nullptr.
* When there is no proper sub-list element to
* move aContentMovingToSubList, this method
* inserts a new sub-list element and update this
* to it.
* @param aPointInListElement A point in a list element whose child should
* be indented. If this method creates new list
* element into the list element, this inserts
* the new list element to this point.
* @param aContentMovingToSubList
* A content node which is a child of a list
* element and should be moved into a sub-list
* element.
* @param aEditingHost The editing host.
* @return A candidate caret position.
*/
[[nodiscard]] MOZ_CAN_RUN_SCRIPT Result<EditorDOMPoint, nsresult>
IndentListChildWithTransaction(RefPtr<Element>* aSubListElement,
const EditorDOMPoint& aPointInListElement,
nsIContent& aContentMovingToSubList,
const Element& aEditingHost);
/**
* Stack based helper class for calling EditorBase::EndTransactionInternal().
* NOTE: This does not suppress multiple input events. In most cases,
* only one "input" event should be fired for an edit action rather
* than per edit sub-action. In such case, you should use
* EditorBase::AutoPlaceholderBatch instead.
*/
class MOZ_RAII AutoTransactionBatch final { public:
/**
* @param aRequesterFuncName function name which wants to end the batch.
* This won't be stored nor exposed to selection listeners etc, used only
* for logging. This MUST be alive when the destructor runs.
*/
MOZ_CAN_RUN_SCRIPT explicit AutoTransactionBatch(
HTMLEditor& aHTMLEditor, const char* aRequesterFuncName)
: mHTMLEditor(aHTMLEditor), mRequesterFuncName(aRequesterFuncName) {
MOZ_KnownLive(mHTMLEditor).BeginTransactionInternal(mRequesterFuncName);
}
protected:
// The lifetime must be guaranteed by the creator of this instance.
MOZ_KNOWN_LIVE HTMLEditor& mHTMLEditor;
const char* const mRequesterFuncName;
};
// Used by TopLevelEditSubActionData::mSelectedRange.
mutable RefPtr<RangeItem> mSelectedRangeForTopLevelEditSubAction;
// Used by TopLevelEditSubActionData::mChangedRange.
mutable RefPtr<nsRange> mChangedRangeForTopLevelEditSubAction;
// mPaddingBRElementForEmptyEditor should be used for placing caret
// at proper position when editor is empty.
RefPtr<dom::HTMLBRElement> mPaddingBRElementForEmptyEditor;
// This is set only when HandleInsertText appended a collapsible white-space.
RefPtr<dom::Text> mLastCollapsibleWhiteSpaceAppendedTextNode;
// While this instance or its helper class updates the DOM with a DOM API,
// this is set to the wrapper class to call the DOM API.
const AutoDOMAPIWrapperBase* mRunningDOMAPIWrapper = nullptr;
friend class AlignStateAtSelection; // CollectEditableTargetNodes,
// CollectNonEditableNodes
friend class AutoClonedRangeArray; // RangeUpdaterRef,
// SplitNodeWithTransaction,
// SplitInlineAncestorsAtRangeBoundaries
friend class AutoDOMAPIWrapperBase; // OnDOMAPICallEnd,
// OnDOMAPICallStart,
friend class AutoClonedSelectionRangeArray; // RangeUpdaterRef,
friend class AutoSelectionRestore;
friend class AutoSelectionSetterAfterTableEdit; // SetSelectionAfterEdit
friend class CSSEditUtils; // DoTransactionInternal, HasAttributes,
// RemoveContainerWithTransaction
friend class EditorBase; // ComputeTargetRanges,
// GetChangedRangeForTopLevelEditSubAction,
// GetSelectedRangeItemForTopLevelEditSubAction,
// MaybeCreatePaddingBRElementForEmptyEditor,
// PrepareToInsertBRElement,
// ReflectPaddingBRElementForEmptyEditor,
// RefreshEditingUI,
// mComposerUpdater, mHasBeforeInputBeenCanceled
friend class JoinNodesTransaction; // DidJoinNodesTransaction, DoJoinNodes,
// DoSplitNode, // RangeUpdaterRef
friend class ListElementSelectionState; // CollectEditTargetNodes,
// CollectNonEditableNodes
friend class ListItemElementSelectionState; // CollectEditTargetNodes,
// CollectNonEditableNodes
friend class MoveNodeTransaction; // AllowsTransactionsToChangeSelection,
// CollapseSelectionTo, RangeUpdaterRef
friend class MoveSiblingsTransaction; // AllowsTransactionsToChangeSelection,
// CollapseSelectionTo, RangeUpdaterRef
friend class ParagraphStateAtSelection; // CollectChildren,
// CollectEditTargetNodes,
// CollectListChildren,
// CollectNonEditableNodes,
// CollectTableChildren
friend class SlurpBlobEventListener; // BlobReader
friend class SplitNodeTransaction; // DoJoinNodes, DoSplitNode
friend class TransactionManager; // DidDoTransaction, DidRedoTransaction,
// DidUndoTransaction
friend class
WhiteSpaceVisibilityKeeper; // AutoMoveOneLineHandler
// CanMoveChildren,
// ChangeListElementType,
// DeleteNodeWithTransaction,
// DeleteTextAndTextNodesWithTransaction,
// InsertLineBreak,
// JoinNearestEditableNodesWithTransaction,
// LineBreakType,
// MoveChildrenWithTransaction,
// SplitAncestorStyledInlineElementsAt,
// TreatEmptyTextNodes
};
/**
* ListElementSelectionState class gets which list element is selected right
* now.
*/
class MOZ_STACK_CLASS ListElementSelectionState final { public:
ListElementSelectionState() = delete;
ListElementSelectionState(HTMLEditor& aHTMLEditor, ErrorResult& aRv);
/**
* ListItemElementSelectionState class gets which list item element is selected
* right now.
*/
class MOZ_STACK_CLASS ListItemElementSelectionState final { public:
ListItemElementSelectionState() = delete;
ListItemElementSelectionState(HTMLEditor& aHTMLEditor, ErrorResult& aRv);
/**
* AlignStateAtSelection class gets alignment at selection.
* XXX This currently returns only first alignment.
*/
class MOZ_STACK_CLASS AlignStateAtSelection final { public:
AlignStateAtSelection() = delete;
MOZ_CAN_RUN_SCRIPT AlignStateAtSelection(HTMLEditor& aHTMLEditor,
ErrorResult& aRv);
/**
* ParagraphStateAtSelection class gets format block types around selection.
*/
class MOZ_STACK_CLASS ParagraphStateAtSelection final { public:
using FormatBlockMode = HTMLEditor::FormatBlockMode;
/**
* GetFirstParagraphStateAtSelection() returns:
* - nullptr if there is no format blocks nor inline nodes.
* - nsGkAtoms::_empty if first node is not in any format block.
* - a tag name of format block at first node.
* XXX See the private method explanations. If selection ranges contains
* non-format block first, it'll be check after its siblings. Therefore,
* this may return non-first paragraph state.
*/
nsAtom* GetFirstParagraphStateAtSelection() const {
return mIsMixed && mIsInDLElement ? nsGkAtoms::dl
: mFirstParagraphState.get();
}
/**
* If selected nodes are not in same format node nor only in no-format blocks,
* this returns true.
*/
bool IsMixed() const { return mIsMixed && !mIsInDLElement; }
private:
using EditorType = EditorBase::EditorType;
/**
* AppendDescendantFormatNodesAndFirstInlineNode() appends descendant
* format blocks and first inline child node in aNonFormatBlockElement to
* the last of the array (not inserting where aNonFormatBlockElement is,
* so that the node order becomes randomly).
*
* @param aArrayOfContents [in/out] Found descendant format blocks
* and first inline node in each non-format
* block will be appended to this.
* @param aFormatBlockMode Whether HTML formatBlock command or XUL
* paragraphState command.
* @param aNonFormatBlockElement Must be a non-format block element.
*/
static void AppendDescendantFormatNodesAndFirstInlineNode(
nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents,
FormatBlockMode aFormatBlockMode, dom::Element& aNonFormatBlockElement);
/**
* CollectEditableFormatNodesInSelection() collects only editable nodes
* around selection ranges (with
* AutoClonedRangeArray::ExtendRangesToWrapLines() and
* HTMLEditor::CollectEditTargetNodes(), see its document for the detail).
* If it includes list, list item or table related elements, they will be
* replaced their children.
*
* @param aFormatBlockMode Whether HTML formatBlock command or XUL
* paragraphState command.
*/
static nsresult CollectEditableFormatNodesInSelection(
HTMLEditor& aHTMLEditor, FormatBlockMode aFormatBlockMode,
const dom::Element& aEditingHost,
nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents);
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 und die Messung sind noch experimentell.