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
.ExtendRangesToWrapLines(
83 EditSubAction::eCreateOrChangeList
,
84 BlockInlineCheck::UseHTMLDefaultStyle
, *editingHostOrRoot
);
85 nsresult rv
= extendedSelectionRanges
.CollectEditTargetNodes(
86 aHTMLEditor
, arrayOfContents
, EditSubAction::eCreateOrChangeList
,
87 AutoRangeArray::CollectNonEditableNodes::No
);
90 "AutoRangeArray::CollectEditTargetNodes(EditSubAction::"
91 "eCreateOrChangeList, CollectNonEditableNodes::No) failed");
92 aRv
= EditorBase::ToGenericNSResult(rv
);
97 // Examine list type for nodes in selection.
98 for (const auto& content
: arrayOfContents
) {
99 if (!content
->IsElement()) {
100 mIsOtherContentSelected
= true;
101 } else if (content
->IsHTMLElement(nsGkAtoms::ul
)) {
102 mIsULElementSelected
= true;
103 } else if (content
->IsHTMLElement(nsGkAtoms::ol
)) {
104 mIsOLElementSelected
= true;
105 } else if (content
->IsHTMLElement(nsGkAtoms::li
)) {
106 if (dom::Element
* parent
= content
->GetParentElement()) {
107 if (parent
->IsHTMLElement(nsGkAtoms::ul
)) {
108 mIsULElementSelected
= true;
109 } else if (parent
->IsHTMLElement(nsGkAtoms::ol
)) {
110 mIsOLElementSelected
= true;
113 } else if (content
->IsAnyOfHTMLElements(nsGkAtoms::dl
, nsGkAtoms::dt
,
115 mIsDLElementSelected
= true;
117 mIsOtherContentSelected
= true;
120 if (mIsULElementSelected
&& mIsOLElementSelected
&& mIsDLElementSelected
&&
121 mIsOtherContentSelected
) {
127 /*****************************************************************************
128 * ListItemElementSelectionState
129 ****************************************************************************/
131 ListItemElementSelectionState::ListItemElementSelectionState(
132 HTMLEditor
& aHTMLEditor
, ErrorResult
& aRv
) {
133 MOZ_ASSERT(!aRv
.Failed());
135 if (NS_WARN_IF(aHTMLEditor
.Destroyed())) {
136 aRv
.Throw(NS_ERROR_EDITOR_DESTROYED
);
140 // XXX Should we create another constructor which won't create
141 // AutoEditActionDataSetter? Or should we create another
142 // AutoEditActionDataSetter which won't nest edit action?
143 EditorBase::AutoEditActionDataSetter
editActionData(aHTMLEditor
,
144 EditAction::eNotEditing
);
145 if (NS_WARN_IF(!editActionData
.CanHandle())) {
146 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
150 Element
* editingHostOrRoot
= aHTMLEditor
.ComputeEditingHost();
151 if (!editingHostOrRoot
) {
152 // This is not a handler of editing command so that if there is no active
153 // editing host, let's use the <body> or document element instead.
154 editingHostOrRoot
= aHTMLEditor
.GetRoot();
155 if (!editingHostOrRoot
) {
160 AutoTArray
<OwningNonNull
<nsIContent
>, 64> arrayOfContents
;
162 AutoRangeArray
extendedSelectionRanges(aHTMLEditor
.SelectionRef());
163 extendedSelectionRanges
.ExtendRangesToWrapLines(
164 EditSubAction::eCreateOrChangeList
,
165 BlockInlineCheck::UseHTMLDefaultStyle
, *editingHostOrRoot
);
166 nsresult rv
= extendedSelectionRanges
.CollectEditTargetNodes(
167 aHTMLEditor
, arrayOfContents
, EditSubAction::eCreateOrChangeList
,
168 AutoRangeArray::CollectNonEditableNodes::No
);
170 NS_WARNING_ASSERTION(
172 "AutoRangeArray::CollectEditTargetNodes(EditSubAction::"
173 "eCreateOrChangeList, CollectNonEditableNodes::No) failed");
174 aRv
= EditorBase::ToGenericNSResult(rv
);
179 // examine list type for nodes in selection
180 for (const auto& content
: arrayOfContents
) {
181 if (!content
->IsElement()) {
182 mIsOtherElementSelected
= true;
183 } else if (content
->IsAnyOfHTMLElements(nsGkAtoms::ul
, nsGkAtoms::ol
,
185 mIsLIElementSelected
= true;
186 } else if (content
->IsHTMLElement(nsGkAtoms::dt
)) {
187 mIsDTElementSelected
= true;
188 } else if (content
->IsHTMLElement(nsGkAtoms::dd
)) {
189 mIsDDElementSelected
= true;
190 } else if (content
->IsHTMLElement(nsGkAtoms::dl
)) {
191 if (mIsDTElementSelected
&& mIsDDElementSelected
) {
194 // need to look inside dl and see which types of items it has
195 DefinitionListItemScanner
scanner(*content
->AsElement());
196 mIsDTElementSelected
|= scanner
.DTElementFound();
197 mIsDDElementSelected
|= scanner
.DDElementFound();
199 mIsOtherElementSelected
= true;
202 if (mIsLIElementSelected
&& mIsDTElementSelected
&& mIsDDElementSelected
&&
203 mIsOtherElementSelected
) {
209 /*****************************************************************************
210 * AlignStateAtSelection
211 ****************************************************************************/
213 AlignStateAtSelection::AlignStateAtSelection(HTMLEditor
& aHTMLEditor
,
215 MOZ_ASSERT(!aRv
.Failed());
217 if (NS_WARN_IF(aHTMLEditor
.Destroyed())) {
218 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
222 // XXX Should we create another constructor which won't create
223 // AutoEditActionDataSetter? Or should we create another
224 // AutoEditActionDataSetter which won't nest edit action?
225 EditorBase::AutoEditActionDataSetter
editActionData(aHTMLEditor
,
226 EditAction::eNotEditing
);
227 if (NS_WARN_IF(!editActionData
.CanHandle())) {
228 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
232 if (aHTMLEditor
.IsSelectionRangeContainerNotContent()) {
233 NS_WARNING("Some selection containers are not content node, but ignored");
237 // For now, just return first alignment. We don't check if it's mixed.
238 // This is for efficiency given that our current UI doesn't care if it's
240 // cmanske: NOT TRUE! We would like to pay attention to mixed state in
241 // [Format] -> [Align] submenu!
243 // This routine assumes that alignment is done ONLY by `<div>` elements
244 // if aHTMLEditor is not in CSS mode.
246 if (NS_WARN_IF(!aHTMLEditor
.GetRoot())) {
247 aRv
.Throw(NS_ERROR_FAILURE
);
251 OwningNonNull
<dom::Element
> bodyOrDocumentElement
= *aHTMLEditor
.GetRoot();
252 EditorRawDOMPoint
atBodyOrDocumentElement(bodyOrDocumentElement
);
254 const nsRange
* firstRange
= aHTMLEditor
.SelectionRef().GetRangeAt(0);
255 mFoundSelectionRanges
= !!firstRange
;
256 if (!mFoundSelectionRanges
) {
257 NS_WARNING("There was no selection range");
258 aRv
.Throw(NS_ERROR_FAILURE
);
261 EditorRawDOMPoint
atStartOfSelection(firstRange
->StartRef());
262 if (NS_WARN_IF(!atStartOfSelection
.IsSet())) {
263 aRv
.Throw(NS_ERROR_FAILURE
);
266 MOZ_ASSERT(atStartOfSelection
.IsSetAndValid());
268 nsIContent
* editTargetContent
= nullptr;
269 // If selection is collapsed or in a text node, take the container.
270 if (aHTMLEditor
.SelectionRef().IsCollapsed() ||
271 atStartOfSelection
.IsInTextNode()) {
272 editTargetContent
= atStartOfSelection
.GetContainerAs
<nsIContent
>();
273 if (NS_WARN_IF(!editTargetContent
)) {
274 aRv
.Throw(NS_ERROR_FAILURE
);
278 // If selection container is the `<body>` element which is set to
279 // `HTMLDocument.body`, take first editable node in it.
280 // XXX Why don't we just compare `atStartOfSelection.GetChild()` and
281 // `bodyOrDocumentElement`? Then, we can avoid computing the
283 else if (atStartOfSelection
.IsContainerHTMLElement(nsGkAtoms::html
) &&
284 atBodyOrDocumentElement
.IsSet() &&
285 atStartOfSelection
.Offset() == atBodyOrDocumentElement
.Offset()) {
286 editTargetContent
= HTMLEditUtils::GetNextContent(
287 atStartOfSelection
, {WalkTreeOption::IgnoreNonEditableNode
},
288 BlockInlineCheck::Unused
, aHTMLEditor
.ComputeEditingHost());
289 if (NS_WARN_IF(!editTargetContent
)) {
290 aRv
.Throw(NS_ERROR_FAILURE
);
294 // Otherwise, use first selected node.
295 // XXX Only for retrieving it, the following block treats all selected
296 // ranges. `HTMLEditor` should have
297 // `GetFirstSelectionRangeExtendedToHardLineStartAndEnd()`.
299 Element
* editingHostOrRoot
= aHTMLEditor
.ComputeEditingHost();
300 if (!editingHostOrRoot
) {
301 // This is not a handler of editing command so that if there is no active
302 // editing host, let's use the <body> or document element instead.
303 editingHostOrRoot
= aHTMLEditor
.GetRoot();
304 if (!editingHostOrRoot
) {
308 AutoRangeArray
extendedSelectionRanges(aHTMLEditor
.SelectionRef());
309 extendedSelectionRanges
.ExtendRangesToWrapLines(
310 EditSubAction::eSetOrClearAlignment
,
311 BlockInlineCheck::UseHTMLDefaultStyle
, *editingHostOrRoot
);
313 AutoTArray
<OwningNonNull
<nsIContent
>, 64> arrayOfContents
;
314 nsresult rv
= extendedSelectionRanges
.CollectEditTargetNodes(
315 aHTMLEditor
, arrayOfContents
, EditSubAction::eSetOrClearAlignment
,
316 AutoRangeArray::CollectNonEditableNodes::Yes
);
319 "AutoRangeArray::CollectEditTargetNodes(eSetOrClearAlignment, "
320 "CollectNonEditableNodes::Yes) failed");
321 aRv
.Throw(NS_ERROR_FAILURE
);
324 if (arrayOfContents
.IsEmpty()) {
326 "AutoRangeArray::CollectEditTargetNodes(eSetOrClearAlignment, "
327 "CollectNonEditableNodes::Yes) returned no contents");
328 aRv
.Throw(NS_ERROR_FAILURE
);
331 editTargetContent
= arrayOfContents
[0];
334 const RefPtr
<dom::Element
> maybeNonEditableBlockElement
=
335 HTMLEditUtils::GetInclusiveAncestorElement(
336 *editTargetContent
, HTMLEditUtils::ClosestBlockElement
,
337 BlockInlineCheck::UseHTMLDefaultStyle
);
338 if (NS_WARN_IF(!maybeNonEditableBlockElement
)) {
339 aRv
.Throw(NS_ERROR_FAILURE
);
343 if (aHTMLEditor
.IsCSSEnabled() && EditorElementStyle::Align().IsCSSSettable(
344 *maybeNonEditableBlockElement
)) {
345 // We are in CSS mode and we know how to align this element with CSS
347 // Let's get the value(s) of text-align or margin-left/margin-right
348 DebugOnly
<nsresult
> rvIgnored
= CSSEditUtils::GetComputedCSSEquivalentTo(
349 *maybeNonEditableBlockElement
, EditorElementStyle::Align(), value
);
350 if (NS_WARN_IF(aHTMLEditor
.Destroyed())) {
351 aRv
.Throw(NS_ERROR_EDITOR_DESTROYED
);
354 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
355 "CSSEditUtils::GetComputedCSSEquivalentTo("
356 "EditorElementStyle::Align()) failed, but ignored");
357 if (value
.EqualsLiteral(u
"center") || value
.EqualsLiteral(u
"-moz-center") ||
358 value
.EqualsLiteral(u
"auto auto")) {
359 mFirstAlign
= nsIHTMLEditor::eCenter
;
362 if (value
.EqualsLiteral(u
"right") || value
.EqualsLiteral(u
"-moz-right") ||
363 value
.EqualsLiteral(u
"auto 0px")) {
364 mFirstAlign
= nsIHTMLEditor::eRight
;
367 if (value
.EqualsLiteral(u
"justify")) {
368 mFirstAlign
= nsIHTMLEditor::eJustify
;
371 // XXX In RTL document, is this expected?
372 mFirstAlign
= nsIHTMLEditor::eLeft
;
376 for (Element
* const containerElement
:
377 editTargetContent
->InclusiveAncestorsOfType
<Element
>()) {
378 // If the node is a parent `<table>` element of edit target, let's break
379 // here to materialize the 'inline-block' behaviour of html tables
380 // regarding to text alignment.
381 if (containerElement
!= editTargetContent
&&
382 containerElement
->IsHTMLElement(nsGkAtoms::table
)) {
386 if (EditorElementStyle::Align().IsCSSSettable(*containerElement
)) {
388 DebugOnly
<nsresult
> rvIgnored
= CSSEditUtils::GetSpecifiedProperty(
389 *containerElement
, *nsGkAtoms::textAlign
, value
);
390 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
391 "CSSEditUtils::GetSpecifiedProperty(nsGkAtoms::"
392 "textAlign) failed, but ignored");
393 if (!value
.IsEmpty()) {
394 if (value
.EqualsLiteral("center")) {
395 mFirstAlign
= nsIHTMLEditor::eCenter
;
398 if (value
.EqualsLiteral("right")) {
399 mFirstAlign
= nsIHTMLEditor::eRight
;
402 if (value
.EqualsLiteral("justify")) {
403 mFirstAlign
= nsIHTMLEditor::eJustify
;
406 if (value
.EqualsLiteral("left")) {
407 mFirstAlign
= nsIHTMLEditor::eLeft
;
411 // text-align: start and end aren't supported yet
415 if (!HTMLEditUtils::SupportsAlignAttr(*containerElement
)) {
419 nsAutoString alignAttributeValue
;
420 containerElement
->GetAttr(nsGkAtoms::align
, alignAttributeValue
);
421 if (alignAttributeValue
.IsEmpty()) {
425 if (alignAttributeValue
.LowerCaseEqualsASCII("center")) {
426 mFirstAlign
= nsIHTMLEditor::eCenter
;
429 if (alignAttributeValue
.LowerCaseEqualsASCII("right")) {
430 mFirstAlign
= nsIHTMLEditor::eRight
;
433 // XXX This is odd case. `<div align="justify">` is not in any standards.
434 if (alignAttributeValue
.LowerCaseEqualsASCII("justify")) {
435 mFirstAlign
= nsIHTMLEditor::eJustify
;
438 // XXX In RTL document, is this expected?
439 mFirstAlign
= nsIHTMLEditor::eLeft
;
444 /*****************************************************************************
445 * ParagraphStateAtSelection
446 ****************************************************************************/
448 ParagraphStateAtSelection::ParagraphStateAtSelection(
449 HTMLEditor
& aHTMLEditor
, FormatBlockMode aFormatBlockMode
,
451 if (NS_WARN_IF(aHTMLEditor
.Destroyed())) {
452 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
456 // XXX Should we create another constructor which won't create
457 // AutoEditActionDataSetter? Or should we create another
458 // AutoEditActionDataSetter which won't nest edit action?
459 EditorBase::AutoEditActionDataSetter
editActionData(aHTMLEditor
,
460 EditAction::eNotEditing
);
461 if (NS_WARN_IF(!editActionData
.CanHandle())) {
462 aRv
= EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED
);
466 if (aHTMLEditor
.IsSelectionRangeContainerNotContent()) {
467 NS_WARNING("Some selection containers are not content node, but ignored");
471 if (MOZ_UNLIKELY(!aHTMLEditor
.SelectionRef().RangeCount())) {
472 aRv
.Throw(NS_ERROR_FAILURE
);
476 const Element
* const editingHostOrBodyOrDocumentElement
= [&]() -> Element
* {
477 if (Element
* editingHost
= aHTMLEditor
.ComputeEditingHost()) {
480 return aHTMLEditor
.GetRoot();
482 if (!editingHostOrBodyOrDocumentElement
||
483 !HTMLEditUtils::IsSimplyEditableNode(
484 *editingHostOrBodyOrDocumentElement
)) {
488 AutoTArray
<OwningNonNull
<nsIContent
>, 64> arrayOfContents
;
489 nsresult rv
= CollectEditableFormatNodesInSelection(
490 aHTMLEditor
, aFormatBlockMode
, *editingHostOrBodyOrDocumentElement
,
494 "ParagraphStateAtSelection::CollectEditableFormatNodesInSelection() "
500 // We need to append descendant format block if block nodes are not format
501 // block. This is so we only have to look "up" the hierarchy to find
502 // format nodes, instead of both up and down.
503 for (size_t index
: Reversed(IntegerRange(arrayOfContents
.Length()))) {
504 OwningNonNull
<nsIContent
>& content
= arrayOfContents
[index
];
505 if (HTMLEditUtils::IsBlockElement(content
,
506 BlockInlineCheck::UseHTMLDefaultStyle
) &&
507 !HTMLEditor::IsFormatElement(aFormatBlockMode
, content
)) {
508 // XXX This RemoveObject() call has already been commented out and
509 // the above comment explained we're trying to replace non-format
510 // block nodes in the array. According to the following blocks and
511 // `AppendDescendantFormatNodesAndFirstInlineNode()`, replacing
512 // non-format block with descendants format blocks makes sense.
513 // arrayOfContents.RemoveObject(node);
514 ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
515 arrayOfContents
, aFormatBlockMode
, *content
->AsElement());
519 // We might have an empty node list. if so, find selection parent
520 // and put that on the list
521 if (arrayOfContents
.IsEmpty()) {
523 aHTMLEditor
.GetFirstSelectionStartPoint
<EditorRawDOMPoint
>();
524 if (NS_WARN_IF(!atCaret
.IsInContentNode())) {
526 "We've already checked whether there is a selection range, "
527 "but we have no range right now.");
528 aRv
.Throw(NS_ERROR_FAILURE
);
531 arrayOfContents
.AppendElement(*atCaret
.ContainerAs
<nsIContent
>());
534 for (auto& content
: Reversed(arrayOfContents
)) {
535 const Element
* formatElement
= nullptr;
536 if (HTMLEditor::IsFormatElement(aFormatBlockMode
, content
)) {
537 formatElement
= content
->AsElement();
539 // Ignore inline contents since its children have been appended
540 // the list above so that we'll handle this descendants later.
541 // XXX: It's odd to ignore block children to consider the mixed state.
542 else if (HTMLEditUtils::IsBlockElement(
543 content
, BlockInlineCheck::UseHTMLDefaultStyle
)) {
546 // If we meet an inline node, let's get its parent format.
548 for (Element
* parentElement
: content
->AncestorsOfType
<Element
>()) {
549 // If we reach `HTMLDocument.body` or `Document.documentElement`,
550 // there is no format.
551 if (parentElement
== editingHostOrBodyOrDocumentElement
) {
554 if (HTMLEditor::IsFormatElement(aFormatBlockMode
, *parentElement
)) {
555 MOZ_ASSERT(parentElement
->NodeInfo()->NameAtom());
556 formatElement
= parentElement
;
562 auto FormatElementIsInclusiveDescendantOfFormatDLElement
= [&]() {
563 if (aFormatBlockMode
== FormatBlockMode::XULParagraphStateCommand
) {
566 if (!formatElement
) {
569 for (const Element
* const element
:
570 formatElement
->InclusiveAncestorsOfType
<Element
>()) {
571 if (element
->IsHTMLElement(nsGkAtoms::dl
)) {
574 if (element
->IsAnyOfHTMLElements(nsGkAtoms::dd
, nsGkAtoms::dt
)) {
577 if (HTMLEditUtils::IsFormatElementForFormatBlockCommand(
585 // if this is the first node, we've found, remember it as the format
586 if (!mFirstParagraphState
) {
587 mFirstParagraphState
= formatElement
588 ? formatElement
->NodeInfo()->NameAtom()
590 mIsInDLElement
= FormatElementIsInclusiveDescendantOfFormatDLElement();
593 mIsInDLElement
&= FormatElementIsInclusiveDescendantOfFormatDLElement();
594 // else make sure it matches previously found format
595 if ((!formatElement
&& mFirstParagraphState
!= nsGkAtoms::_empty
) ||
597 !formatElement
->IsHTMLElement(mFirstParagraphState
))) {
605 void ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
606 nsTArray
<OwningNonNull
<nsIContent
>>& aArrayOfContents
,
607 FormatBlockMode aFormatBlockMode
, dom::Element
& aNonFormatBlockElement
) {
608 MOZ_ASSERT(HTMLEditUtils::IsBlockElement(
609 aNonFormatBlockElement
, BlockInlineCheck::UseHTMLDefaultStyle
));
611 !HTMLEditor::IsFormatElement(aFormatBlockMode
, aNonFormatBlockElement
));
613 // We only need to place any one inline inside this node onto
614 // the list. They are all the same for purposes of determining
615 // paragraph style. We use foundInline to track this as we are
616 // going through the children in the loop below.
617 bool foundInline
= false;
618 for (nsIContent
* childContent
= aNonFormatBlockElement
.GetFirstChild();
619 childContent
; childContent
= childContent
->GetNextSibling()) {
620 const bool isBlock
= HTMLEditUtils::IsBlockElement(
621 *childContent
, BlockInlineCheck::UseHTMLDefaultStyle
);
622 const bool isFormat
=
623 HTMLEditor::IsFormatElement(aFormatBlockMode
, *childContent
);
624 // If the child is a non-format block element, let's check its children
626 if (isBlock
&& !isFormat
) {
627 ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
628 aArrayOfContents
, aFormatBlockMode
, *childContent
->AsElement());
632 // If it's a format block, append it.
634 aArrayOfContents
.AppendElement(*childContent
);
638 MOZ_ASSERT(!isBlock
);
640 // If we haven't found inline node, append only this first inline node.
641 // XXX I think that this makes sense if caller of this removes
642 // aNonFormatBlockElement from aArrayOfContents because the last loop
643 // of the constructor can check parent format block with
644 // aNonFormatBlockElement.
647 aArrayOfContents
.AppendElement(*childContent
);
654 nsresult
ParagraphStateAtSelection::CollectEditableFormatNodesInSelection(
655 HTMLEditor
& aHTMLEditor
, FormatBlockMode aFormatBlockMode
,
656 const Element
& aEditingHost
,
657 nsTArray
<OwningNonNull
<nsIContent
>>& aArrayOfContents
) {
659 AutoRangeArray
extendedSelectionRanges(aHTMLEditor
.SelectionRef());
660 extendedSelectionRanges
.ExtendRangesToWrapLines(
661 aFormatBlockMode
== FormatBlockMode::HTMLFormatBlockCommand
662 ? EditSubAction::eFormatBlockForHTMLCommand
663 : EditSubAction::eCreateOrRemoveBlock
,
664 BlockInlineCheck::UseHTMLDefaultStyle
, aEditingHost
);
665 nsresult rv
= extendedSelectionRanges
.CollectEditTargetNodes(
666 aHTMLEditor
, aArrayOfContents
,
667 aFormatBlockMode
== FormatBlockMode::HTMLFormatBlockCommand
668 ? EditSubAction::eFormatBlockForHTMLCommand
669 : EditSubAction::eCreateOrRemoveBlock
,
670 AutoRangeArray::CollectNonEditableNodes::Yes
);
673 "AutoRangeArray::CollectEditTargetNodes("
674 "CollectNonEditableNodes::Yes) failed");
679 // Pre-process our list of nodes
680 for (size_t index
: Reversed(IntegerRange(aArrayOfContents
.Length()))) {
681 OwningNonNull
<nsIContent
> content
= aArrayOfContents
[index
];
683 // Remove all non-editable nodes. Leave them be.
684 if (!EditorUtils::IsEditableContent(content
, EditorType::HTML
)) {
685 aArrayOfContents
.RemoveElementAt(index
);
689 // Scan for table elements. If we find table elements other than table,
690 // replace it with a list of any editable non-table content. Ditto for
692 if (HTMLEditUtils::IsAnyTableElement(content
) ||
693 HTMLEditUtils::IsAnyListElement(content
) ||
694 HTMLEditUtils::IsListItem(content
)) {
695 aArrayOfContents
.RemoveElementAt(index
);
696 HTMLEditUtils::CollectChildren(
697 content
, aArrayOfContents
, index
,
698 {CollectChildrenOption::CollectListChildren
,
699 CollectChildrenOption::CollectTableChildren
});
705 } // namespace mozilla