1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "HTMLEditor.h"
12 #include "AutoRangeArray.h"
13 #include "CSSEditUtils.h"
14 #include "EditAction.h"
15 #include "EditorUtils.h"
16 #include "HTMLEditHelpers.h"
17 #include "HTMLEditUtils.h"
18 #include "WSRunObject.h"
20 #include "mozilla/Assertions.h"
21 #include "mozilla/OwningNonNull.h"
22 #include "mozilla/dom/Element.h"
23 #include "mozilla/dom/Selection.h"
25 #include "nsAString.h"
26 #include "nsAlgorithm.h"
30 #include "nsGkAtoms.h"
31 #include "nsIContent.h"
35 #include "nsStringFwd.h"
38 // NOTE: This file was split from:
39 // https://searchfox.org/mozilla-central/rev/c409dd9235c133ab41eba635f906aa16e050c197/editor/libeditor/HTMLEditSubActionHandler.cpp
43 using EditorType
= EditorUtils::EditorType
;
44 using WalkTreeOption
= HTMLEditUtils::WalkTreeOption
;
46 /*****************************************************************************
47 * ListElementSelectionState
48 ****************************************************************************/
50 ListElementSelectionState::ListElementSelectionState(HTMLEditor
& aHTMLEditor
,
52 MOZ_ASSERT(!aRv
.Failed());
54 if (NS_WARN_IF(aHTMLEditor
.Destroyed())) {
55 aRv
.Throw(NS_ERROR_EDITOR_DESTROYED
);
59 // XXX Should we create another constructor which won't create
60 // AutoEditActionDataSetter? Or should we create another
61 // AutoEditActionDataSetter which won't nest edit action?
62 EditorBase::AutoEditActionDataSetter
editActionData(aHTMLEditor
,
63 EditAction::eNotEditing
);
64 if (NS_WARN_IF(!editActionData
.CanHandle())) {
65 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
69 Element
* editingHostOrRoot
= aHTMLEditor
.ComputeEditingHost();
70 if (!editingHostOrRoot
) {
71 // This is not a handler of editing command so that if there is no active
72 // editing host, let's use the <body> or document element instead.
73 editingHostOrRoot
= aHTMLEditor
.GetRoot();
74 if (!editingHostOrRoot
) {
79 AutoTArray
<OwningNonNull
<nsIContent
>, 64> arrayOfContents
;
81 AutoRangeArray
extendedSelectionRanges(aHTMLEditor
.SelectionRef());
82 extendedSelectionRanges
.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
83 EditSubAction::eCreateOrChangeList
, *editingHostOrRoot
);
84 nsresult rv
= extendedSelectionRanges
.CollectEditTargetNodes(
85 aHTMLEditor
, arrayOfContents
, EditSubAction::eCreateOrChangeList
,
86 AutoRangeArray::CollectNonEditableNodes::No
);
89 "AutoRangeArray::CollectEditTargetNodes(EditSubAction::"
90 "eCreateOrChangeList, CollectNonEditableNodes::No) failed");
91 aRv
= EditorBase::ToGenericNSResult(rv
);
96 // Examine list type for nodes in selection.
97 for (const auto& content
: arrayOfContents
) {
98 if (!content
->IsElement()) {
99 mIsOtherContentSelected
= true;
100 } else if (content
->IsHTMLElement(nsGkAtoms::ul
)) {
101 mIsULElementSelected
= true;
102 } else if (content
->IsHTMLElement(nsGkAtoms::ol
)) {
103 mIsOLElementSelected
= true;
104 } else if (content
->IsHTMLElement(nsGkAtoms::li
)) {
105 if (dom::Element
* parent
= content
->GetParentElement()) {
106 if (parent
->IsHTMLElement(nsGkAtoms::ul
)) {
107 mIsULElementSelected
= true;
108 } else if (parent
->IsHTMLElement(nsGkAtoms::ol
)) {
109 mIsOLElementSelected
= true;
112 } else if (content
->IsAnyOfHTMLElements(nsGkAtoms::dl
, nsGkAtoms::dt
,
114 mIsDLElementSelected
= true;
116 mIsOtherContentSelected
= true;
119 if (mIsULElementSelected
&& mIsOLElementSelected
&& mIsDLElementSelected
&&
120 mIsOtherContentSelected
) {
126 /*****************************************************************************
127 * ListItemElementSelectionState
128 ****************************************************************************/
130 ListItemElementSelectionState::ListItemElementSelectionState(
131 HTMLEditor
& aHTMLEditor
, ErrorResult
& aRv
) {
132 MOZ_ASSERT(!aRv
.Failed());
134 if (NS_WARN_IF(aHTMLEditor
.Destroyed())) {
135 aRv
.Throw(NS_ERROR_EDITOR_DESTROYED
);
139 // XXX Should we create another constructor which won't create
140 // AutoEditActionDataSetter? Or should we create another
141 // AutoEditActionDataSetter which won't nest edit action?
142 EditorBase::AutoEditActionDataSetter
editActionData(aHTMLEditor
,
143 EditAction::eNotEditing
);
144 if (NS_WARN_IF(!editActionData
.CanHandle())) {
145 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
149 Element
* editingHostOrRoot
= aHTMLEditor
.ComputeEditingHost();
150 if (!editingHostOrRoot
) {
151 // This is not a handler of editing command so that if there is no active
152 // editing host, let's use the <body> or document element instead.
153 editingHostOrRoot
= aHTMLEditor
.GetRoot();
154 if (!editingHostOrRoot
) {
159 AutoTArray
<OwningNonNull
<nsIContent
>, 64> arrayOfContents
;
161 AutoRangeArray
extendedSelectionRanges(aHTMLEditor
.SelectionRef());
162 extendedSelectionRanges
.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
163 EditSubAction::eCreateOrChangeList
, *editingHostOrRoot
);
164 nsresult rv
= extendedSelectionRanges
.CollectEditTargetNodes(
165 aHTMLEditor
, arrayOfContents
, EditSubAction::eCreateOrChangeList
,
166 AutoRangeArray::CollectNonEditableNodes::No
);
168 NS_WARNING_ASSERTION(
170 "AutoRangeArray::CollectEditTargetNodes(EditSubAction::"
171 "eCreateOrChangeList, CollectNonEditableNodes::No) failed");
172 aRv
= EditorBase::ToGenericNSResult(rv
);
177 // examine list type for nodes in selection
178 for (const auto& content
: arrayOfContents
) {
179 if (!content
->IsElement()) {
180 mIsOtherElementSelected
= true;
181 } else if (content
->IsAnyOfHTMLElements(nsGkAtoms::ul
, nsGkAtoms::ol
,
183 mIsLIElementSelected
= true;
184 } else if (content
->IsHTMLElement(nsGkAtoms::dt
)) {
185 mIsDTElementSelected
= true;
186 } else if (content
->IsHTMLElement(nsGkAtoms::dd
)) {
187 mIsDDElementSelected
= true;
188 } else if (content
->IsHTMLElement(nsGkAtoms::dl
)) {
189 if (mIsDTElementSelected
&& mIsDDElementSelected
) {
192 // need to look inside dl and see which types of items it has
193 DefinitionListItemScanner
scanner(*content
->AsElement());
194 mIsDTElementSelected
|= scanner
.DTElementFound();
195 mIsDDElementSelected
|= scanner
.DDElementFound();
197 mIsOtherElementSelected
= true;
200 if (mIsLIElementSelected
&& mIsDTElementSelected
&& mIsDDElementSelected
&&
201 mIsOtherElementSelected
) {
207 /*****************************************************************************
208 * AlignStateAtSelection
209 ****************************************************************************/
211 AlignStateAtSelection::AlignStateAtSelection(HTMLEditor
& aHTMLEditor
,
213 MOZ_ASSERT(!aRv
.Failed());
215 if (NS_WARN_IF(aHTMLEditor
.Destroyed())) {
216 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
220 // XXX Should we create another constructor which won't create
221 // AutoEditActionDataSetter? Or should we create another
222 // AutoEditActionDataSetter which won't nest edit action?
223 EditorBase::AutoEditActionDataSetter
editActionData(aHTMLEditor
,
224 EditAction::eNotEditing
);
225 if (NS_WARN_IF(!editActionData
.CanHandle())) {
226 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
230 if (aHTMLEditor
.IsSelectionRangeContainerNotContent()) {
231 NS_WARNING("Some selection containers are not content node, but ignored");
235 // For now, just return first alignment. We don't check if it's mixed.
236 // This is for efficiency given that our current UI doesn't care if it's
238 // cmanske: NOT TRUE! We would like to pay attention to mixed state in
239 // [Format] -> [Align] submenu!
241 // This routine assumes that alignment is done ONLY by `<div>` elements
242 // if aHTMLEditor is not in CSS mode.
244 if (NS_WARN_IF(!aHTMLEditor
.GetRoot())) {
245 aRv
.Throw(NS_ERROR_FAILURE
);
249 OwningNonNull
<dom::Element
> bodyOrDocumentElement
= *aHTMLEditor
.GetRoot();
250 EditorRawDOMPoint
atBodyOrDocumentElement(bodyOrDocumentElement
);
252 const nsRange
* firstRange
= aHTMLEditor
.SelectionRef().GetRangeAt(0);
253 mFoundSelectionRanges
= !!firstRange
;
254 if (!mFoundSelectionRanges
) {
255 NS_WARNING("There was no selection range");
256 aRv
.Throw(NS_ERROR_FAILURE
);
259 EditorRawDOMPoint
atStartOfSelection(firstRange
->StartRef());
260 if (NS_WARN_IF(!atStartOfSelection
.IsSet())) {
261 aRv
.Throw(NS_ERROR_FAILURE
);
264 MOZ_ASSERT(atStartOfSelection
.IsSetAndValid());
266 nsIContent
* editTargetContent
= nullptr;
267 // If selection is collapsed or in a text node, take the container.
268 if (aHTMLEditor
.SelectionRef().IsCollapsed() ||
269 atStartOfSelection
.IsInTextNode()) {
270 editTargetContent
= atStartOfSelection
.GetContainerAs
<nsIContent
>();
271 if (NS_WARN_IF(!editTargetContent
)) {
272 aRv
.Throw(NS_ERROR_FAILURE
);
276 // If selection container is the `<body>` element which is set to
277 // `HTMLDocument.body`, take first editable node in it.
278 // XXX Why don't we just compare `atStartOfSelection.GetChild()` and
279 // `bodyOrDocumentElement`? Then, we can avoid computing the
281 else if (atStartOfSelection
.IsContainerHTMLElement(nsGkAtoms::html
) &&
282 atBodyOrDocumentElement
.IsSet() &&
283 atStartOfSelection
.Offset() == atBodyOrDocumentElement
.Offset()) {
284 editTargetContent
= HTMLEditUtils::GetNextContent(
285 atStartOfSelection
, {WalkTreeOption::IgnoreNonEditableNode
},
286 BlockInlineCheck::Unused
, aHTMLEditor
.ComputeEditingHost());
287 if (NS_WARN_IF(!editTargetContent
)) {
288 aRv
.Throw(NS_ERROR_FAILURE
);
292 // Otherwise, use first selected node.
293 // XXX Only for retrieving it, the following block treats all selected
294 // ranges. `HTMLEditor` should have
295 // `GetFirstSelectionRangeExtendedToHardLineStartAndEnd()`.
297 Element
* editingHostOrRoot
= aHTMLEditor
.ComputeEditingHost();
298 if (!editingHostOrRoot
) {
299 // This is not a handler of editing command so that if there is no active
300 // editing host, let's use the <body> or document element instead.
301 editingHostOrRoot
= aHTMLEditor
.GetRoot();
302 if (!editingHostOrRoot
) {
306 AutoRangeArray
extendedSelectionRanges(aHTMLEditor
.SelectionRef());
307 extendedSelectionRanges
.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
308 EditSubAction::eSetOrClearAlignment
, *editingHostOrRoot
);
310 AutoTArray
<OwningNonNull
<nsIContent
>, 64> arrayOfContents
;
311 nsresult rv
= extendedSelectionRanges
.CollectEditTargetNodes(
312 aHTMLEditor
, arrayOfContents
, EditSubAction::eSetOrClearAlignment
,
313 AutoRangeArray::CollectNonEditableNodes::Yes
);
316 "AutoRangeArray::CollectEditTargetNodes(eSetOrClearAlignment, "
317 "CollectNonEditableNodes::Yes) failed");
318 aRv
.Throw(NS_ERROR_FAILURE
);
321 if (arrayOfContents
.IsEmpty()) {
323 "AutoRangeArray::CollectEditTargetNodes(eSetOrClearAlignment, "
324 "CollectNonEditableNodes::Yes) returned no contents");
325 aRv
.Throw(NS_ERROR_FAILURE
);
328 editTargetContent
= arrayOfContents
[0];
331 const RefPtr
<dom::Element
> maybeNonEditableBlockElement
=
332 HTMLEditUtils::GetInclusiveAncestorElement(
333 *editTargetContent
, HTMLEditUtils::ClosestBlockElement
,
334 BlockInlineCheck::UseHTMLDefaultStyle
);
335 if (NS_WARN_IF(!maybeNonEditableBlockElement
)) {
336 aRv
.Throw(NS_ERROR_FAILURE
);
340 if (aHTMLEditor
.IsCSSEnabled() && EditorElementStyle::Align().IsCSSSettable(
341 *maybeNonEditableBlockElement
)) {
342 // We are in CSS mode and we know how to align this element with CSS
344 // Let's get the value(s) of text-align or margin-left/margin-right
345 DebugOnly
<nsresult
> rvIgnored
= CSSEditUtils::GetComputedCSSEquivalentTo(
346 *maybeNonEditableBlockElement
, EditorElementStyle::Align(), value
);
347 if (NS_WARN_IF(aHTMLEditor
.Destroyed())) {
348 aRv
.Throw(NS_ERROR_EDITOR_DESTROYED
);
351 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
352 "CSSEditUtils::GetComputedCSSEquivalentTo("
353 "EditorElementStyle::Align()) failed, but ignored");
354 if (value
.EqualsLiteral(u
"center") || value
.EqualsLiteral(u
"-moz-center") ||
355 value
.EqualsLiteral(u
"auto auto")) {
356 mFirstAlign
= nsIHTMLEditor::eCenter
;
359 if (value
.EqualsLiteral(u
"right") || value
.EqualsLiteral(u
"-moz-right") ||
360 value
.EqualsLiteral(u
"auto 0px")) {
361 mFirstAlign
= nsIHTMLEditor::eRight
;
364 if (value
.EqualsLiteral(u
"justify")) {
365 mFirstAlign
= nsIHTMLEditor::eJustify
;
368 // XXX In RTL document, is this expected?
369 mFirstAlign
= nsIHTMLEditor::eLeft
;
373 for (Element
* const containerElement
:
374 editTargetContent
->InclusiveAncestorsOfType
<Element
>()) {
375 // If the node is a parent `<table>` element of edit target, let's break
376 // here to materialize the 'inline-block' behaviour of html tables
377 // regarding to text alignment.
378 if (containerElement
!= editTargetContent
&&
379 containerElement
->IsHTMLElement(nsGkAtoms::table
)) {
383 if (EditorElementStyle::Align().IsCSSSettable(*containerElement
)) {
385 DebugOnly
<nsresult
> rvIgnored
= CSSEditUtils::GetSpecifiedProperty(
386 *containerElement
, *nsGkAtoms::textAlign
, value
);
387 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
388 "CSSEditUtils::GetSpecifiedProperty(nsGkAtoms::"
389 "textAlign) failed, but ignored");
390 if (!value
.IsEmpty()) {
391 if (value
.EqualsLiteral("center")) {
392 mFirstAlign
= nsIHTMLEditor::eCenter
;
395 if (value
.EqualsLiteral("right")) {
396 mFirstAlign
= nsIHTMLEditor::eRight
;
399 if (value
.EqualsLiteral("justify")) {
400 mFirstAlign
= nsIHTMLEditor::eJustify
;
403 if (value
.EqualsLiteral("left")) {
404 mFirstAlign
= nsIHTMLEditor::eLeft
;
408 // text-align: start and end aren't supported yet
412 if (!HTMLEditUtils::SupportsAlignAttr(*containerElement
)) {
416 nsAutoString alignAttributeValue
;
417 containerElement
->GetAttr(nsGkAtoms::align
, alignAttributeValue
);
418 if (alignAttributeValue
.IsEmpty()) {
422 if (alignAttributeValue
.LowerCaseEqualsASCII("center")) {
423 mFirstAlign
= nsIHTMLEditor::eCenter
;
426 if (alignAttributeValue
.LowerCaseEqualsASCII("right")) {
427 mFirstAlign
= nsIHTMLEditor::eRight
;
430 // XXX This is odd case. `<div align="justify">` is not in any standards.
431 if (alignAttributeValue
.LowerCaseEqualsASCII("justify")) {
432 mFirstAlign
= nsIHTMLEditor::eJustify
;
435 // XXX In RTL document, is this expected?
436 mFirstAlign
= nsIHTMLEditor::eLeft
;
441 /*****************************************************************************
442 * ParagraphStateAtSelection
443 ****************************************************************************/
445 ParagraphStateAtSelection::ParagraphStateAtSelection(
446 HTMLEditor
& aHTMLEditor
, FormatBlockMode aFormatBlockMode
,
448 if (NS_WARN_IF(aHTMLEditor
.Destroyed())) {
449 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
453 // XXX Should we create another constructor which won't create
454 // AutoEditActionDataSetter? Or should we create another
455 // AutoEditActionDataSetter which won't nest edit action?
456 EditorBase::AutoEditActionDataSetter
editActionData(aHTMLEditor
,
457 EditAction::eNotEditing
);
458 if (NS_WARN_IF(!editActionData
.CanHandle())) {
459 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
463 if (aHTMLEditor
.IsSelectionRangeContainerNotContent()) {
464 NS_WARNING("Some selection containers are not content node, but ignored");
468 if (MOZ_UNLIKELY(!aHTMLEditor
.SelectionRef().RangeCount())) {
469 aRv
.Throw(NS_ERROR_FAILURE
);
473 const Element
* const editingHostOrBodyOrDocumentElement
= [&]() -> Element
* {
474 if (Element
* editingHost
= aHTMLEditor
.ComputeEditingHost()) {
477 return aHTMLEditor
.GetRoot();
479 if (!editingHostOrBodyOrDocumentElement
||
480 !HTMLEditUtils::IsSimplyEditableNode(
481 *editingHostOrBodyOrDocumentElement
)) {
485 AutoTArray
<OwningNonNull
<nsIContent
>, 64> arrayOfContents
;
486 nsresult rv
= CollectEditableFormatNodesInSelection(
487 aHTMLEditor
, aFormatBlockMode
, *editingHostOrBodyOrDocumentElement
,
491 "ParagraphStateAtSelection::CollectEditableFormatNodesInSelection() "
497 // We need to append descendant format block if block nodes are not format
498 // block. This is so we only have to look "up" the hierarchy to find
499 // format nodes, instead of both up and down.
500 for (size_t index
: Reversed(IntegerRange(arrayOfContents
.Length()))) {
501 OwningNonNull
<nsIContent
>& content
= arrayOfContents
[index
];
502 if (HTMLEditUtils::IsBlockElement(content
,
503 BlockInlineCheck::UseHTMLDefaultStyle
) &&
504 !HTMLEditor::IsFormatElement(aFormatBlockMode
, content
)) {
505 // XXX This RemoveObject() call has already been commented out and
506 // the above comment explained we're trying to replace non-format
507 // block nodes in the array. According to the following blocks and
508 // `AppendDescendantFormatNodesAndFirstInlineNode()`, replacing
509 // non-format block with descendants format blocks makes sense.
510 // arrayOfContents.RemoveObject(node);
511 ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
512 arrayOfContents
, aFormatBlockMode
, *content
->AsElement());
516 // We might have an empty node list. if so, find selection parent
517 // and put that on the list
518 if (arrayOfContents
.IsEmpty()) {
520 aHTMLEditor
.GetFirstSelectionStartPoint
<EditorRawDOMPoint
>();
521 if (NS_WARN_IF(!atCaret
.IsInContentNode())) {
523 "We've already checked whether there is a selection range, "
524 "but we have no range right now.");
525 aRv
.Throw(NS_ERROR_FAILURE
);
528 arrayOfContents
.AppendElement(*atCaret
.ContainerAs
<nsIContent
>());
531 for (auto& content
: Reversed(arrayOfContents
)) {
532 const Element
* formatElement
= nullptr;
533 if (HTMLEditor::IsFormatElement(aFormatBlockMode
, content
)) {
534 formatElement
= content
->AsElement();
536 // Ignore inline contents since its children have been appended
537 // the list above so that we'll handle this descendants later.
538 // XXX: It's odd to ignore block children to consider the mixed state.
539 else if (HTMLEditUtils::IsBlockElement(
540 content
, BlockInlineCheck::UseHTMLDefaultStyle
)) {
543 // If we meet an inline node, let's get its parent format.
545 for (Element
* parentElement
: content
->AncestorsOfType
<Element
>()) {
546 // If we reach `HTMLDocument.body` or `Document.documentElement`,
547 // there is no format.
548 if (parentElement
== editingHostOrBodyOrDocumentElement
) {
551 if (HTMLEditor::IsFormatElement(aFormatBlockMode
, *parentElement
)) {
552 MOZ_ASSERT(parentElement
->NodeInfo()->NameAtom());
553 formatElement
= parentElement
;
559 auto FormatElementIsInclusiveDescendantOfFormatDLElement
= [&]() {
560 if (aFormatBlockMode
== FormatBlockMode::XULParagraphStateCommand
) {
563 if (!formatElement
) {
566 for (const Element
* const element
:
567 formatElement
->InclusiveAncestorsOfType
<Element
>()) {
568 if (element
->IsHTMLElement(nsGkAtoms::dl
)) {
571 if (element
->IsAnyOfHTMLElements(nsGkAtoms::dd
, nsGkAtoms::dt
)) {
574 if (HTMLEditUtils::IsFormatElementForFormatBlockCommand(
582 // if this is the first node, we've found, remember it as the format
583 if (!mFirstParagraphState
) {
584 mFirstParagraphState
= formatElement
585 ? formatElement
->NodeInfo()->NameAtom()
587 mIsInDLElement
= FormatElementIsInclusiveDescendantOfFormatDLElement();
590 mIsInDLElement
&= FormatElementIsInclusiveDescendantOfFormatDLElement();
591 // else make sure it matches previously found format
592 if ((!formatElement
&& mFirstParagraphState
!= nsGkAtoms::_empty
) ||
594 !formatElement
->IsHTMLElement(mFirstParagraphState
))) {
602 void ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
603 nsTArray
<OwningNonNull
<nsIContent
>>& aArrayOfContents
,
604 FormatBlockMode aFormatBlockMode
, dom::Element
& aNonFormatBlockElement
) {
605 MOZ_ASSERT(HTMLEditUtils::IsBlockElement(
606 aNonFormatBlockElement
, BlockInlineCheck::UseHTMLDefaultStyle
));
608 !HTMLEditor::IsFormatElement(aFormatBlockMode
, aNonFormatBlockElement
));
610 // We only need to place any one inline inside this node onto
611 // the list. They are all the same for purposes of determining
612 // paragraph style. We use foundInline to track this as we are
613 // going through the children in the loop below.
614 bool foundInline
= false;
615 for (nsIContent
* childContent
= aNonFormatBlockElement
.GetFirstChild();
616 childContent
; childContent
= childContent
->GetNextSibling()) {
617 const bool isBlock
= HTMLEditUtils::IsBlockElement(
618 *childContent
, BlockInlineCheck::UseHTMLDefaultStyle
);
619 const bool isFormat
=
620 HTMLEditor::IsFormatElement(aFormatBlockMode
, *childContent
);
621 // If the child is a non-format block element, let's check its children
623 if (isBlock
&& !isFormat
) {
624 ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
625 aArrayOfContents
, aFormatBlockMode
, *childContent
->AsElement());
629 // If it's a format block, append it.
631 aArrayOfContents
.AppendElement(*childContent
);
635 MOZ_ASSERT(!isBlock
);
637 // If we haven't found inline node, append only this first inline node.
638 // XXX I think that this makes sense if caller of this removes
639 // aNonFormatBlockElement from aArrayOfContents because the last loop
640 // of the constructor can check parent format block with
641 // aNonFormatBlockElement.
644 aArrayOfContents
.AppendElement(*childContent
);
651 nsresult
ParagraphStateAtSelection::CollectEditableFormatNodesInSelection(
652 HTMLEditor
& aHTMLEditor
, FormatBlockMode aFormatBlockMode
,
653 const Element
& aEditingHost
,
654 nsTArray
<OwningNonNull
<nsIContent
>>& aArrayOfContents
) {
656 AutoRangeArray
extendedSelectionRanges(aHTMLEditor
.SelectionRef());
657 extendedSelectionRanges
.ExtendRangesToWrapLinesToHandleBlockLevelEditAction(
658 aFormatBlockMode
== FormatBlockMode::HTMLFormatBlockCommand
659 ? EditSubAction::eFormatBlockForHTMLCommand
660 : EditSubAction::eCreateOrRemoveBlock
,
662 nsresult rv
= extendedSelectionRanges
.CollectEditTargetNodes(
663 aHTMLEditor
, aArrayOfContents
,
664 aFormatBlockMode
== FormatBlockMode::HTMLFormatBlockCommand
665 ? EditSubAction::eFormatBlockForHTMLCommand
666 : EditSubAction::eCreateOrRemoveBlock
,
667 AutoRangeArray::CollectNonEditableNodes::Yes
);
670 "AutoRangeArray::CollectEditTargetNodes("
671 "CollectNonEditableNodes::Yes) failed");
676 // Pre-process our list of nodes
677 for (size_t index
: Reversed(IntegerRange(aArrayOfContents
.Length()))) {
678 OwningNonNull
<nsIContent
> content
= aArrayOfContents
[index
];
680 // Remove all non-editable nodes. Leave them be.
681 if (!EditorUtils::IsEditableContent(content
, EditorType::HTML
)) {
682 aArrayOfContents
.RemoveElementAt(index
);
686 // Scan for table elements. If we find table elements other than table,
687 // replace it with a list of any editable non-table content. Ditto for
689 if (HTMLEditUtils::IsAnyTableElement(content
) ||
690 HTMLEditUtils::IsAnyListElement(content
) ||
691 HTMLEditUtils::IsListItem(content
)) {
692 aArrayOfContents
.RemoveElementAt(index
);
693 HTMLEditUtils::CollectChildren(
694 content
, aArrayOfContents
, index
,
695 {CollectChildrenOption::CollectListChildren
,
696 CollectChildrenOption::CollectTableChildren
});
702 } // namespace mozilla