Bug 1700051: part 30) Narrow scope of `newOffset`. r=smaug
[gecko.git] / editor / libeditor / HTMLEditorState.cpp
blob5450a97e3d1d50ed0a00a4db83421f08fb372381
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"
9 #include <algorithm>
10 #include <utility>
12 #include "HTMLEditUtils.h"
13 #include "WSRunObject.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/CSSEditUtils.h"
17 #include "mozilla/EditAction.h"
18 #include "mozilla/EditorUtils.h"
19 #include "mozilla/OwningNonNull.h"
20 #include "mozilla/dom/Element.h"
21 #include "mozilla/dom/Selection.h"
23 #include "nsAString.h"
24 #include "nsAlgorithm.h"
25 #include "nsAtom.h"
26 #include "nsDebug.h"
27 #include "nsError.h"
28 #include "nsGkAtoms.h"
29 #include "nsIContent.h"
30 #include "nsINode.h"
31 #include "nsRange.h"
32 #include "nsString.h"
33 #include "nsStringFwd.h"
34 #include "nsTArray.h"
36 // NOTE: This file was split from:
37 // https://searchfox.org/mozilla-central/rev/c409dd9235c133ab41eba635f906aa16e050c197/editor/libeditor/HTMLEditSubActionHandler.cpp
39 namespace mozilla {
41 /*****************************************************************************
42 * ListElementSelectionState
43 ****************************************************************************/
45 ListElementSelectionState::ListElementSelectionState(HTMLEditor& aHTMLEditor,
46 ErrorResult& aRv) {
47 MOZ_ASSERT(!aRv.Failed());
49 if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
50 aRv.Throw(NS_ERROR_EDITOR_DESTROYED);
51 return;
54 // XXX Should we create another constructor which won't create
55 // AutoEditActionDataSetter? Or should we create another
56 // AutoEditActionDataSetter which won't nest edit action?
57 EditorBase::AutoEditActionDataSetter editActionData(aHTMLEditor,
58 EditAction::eNotEditing);
59 if (NS_WARN_IF(!editActionData.CanHandle())) {
60 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
61 return;
64 AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
65 nsresult rv = aHTMLEditor.CollectEditTargetNodesInExtendedSelectionRanges(
66 arrayOfContents, EditSubAction::eCreateOrChangeList,
67 HTMLEditor::CollectNonEditableNodes::No);
68 if (NS_FAILED(rv)) {
69 NS_WARNING(
70 "HTMLEditor::CollectEditTargetNodesInExtendedSelectionRanges("
71 "eCreateOrChangeList, CollectNonEditableNodes::No) failed");
72 aRv = EditorBase::ToGenericNSResult(rv);
73 return;
76 // Examine list type for nodes in selection.
77 for (const auto& content : arrayOfContents) {
78 if (!content->IsElement()) {
79 mIsOtherContentSelected = true;
80 } else if (content->IsHTMLElement(nsGkAtoms::ul)) {
81 mIsULElementSelected = true;
82 } else if (content->IsHTMLElement(nsGkAtoms::ol)) {
83 mIsOLElementSelected = true;
84 } else if (content->IsHTMLElement(nsGkAtoms::li)) {
85 if (dom::Element* parent = content->GetParentElement()) {
86 if (parent->IsHTMLElement(nsGkAtoms::ul)) {
87 mIsULElementSelected = true;
88 } else if (parent->IsHTMLElement(nsGkAtoms::ol)) {
89 mIsOLElementSelected = true;
92 } else if (content->IsAnyOfHTMLElements(nsGkAtoms::dl, nsGkAtoms::dt,
93 nsGkAtoms::dd)) {
94 mIsDLElementSelected = true;
95 } else {
96 mIsOtherContentSelected = true;
99 if (mIsULElementSelected && mIsOLElementSelected && mIsDLElementSelected &&
100 mIsOtherContentSelected) {
101 break;
106 /*****************************************************************************
107 * ListItemElementSelectionState
108 ****************************************************************************/
110 ListItemElementSelectionState::ListItemElementSelectionState(
111 HTMLEditor& aHTMLEditor, ErrorResult& aRv) {
112 MOZ_ASSERT(!aRv.Failed());
114 if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
115 aRv.Throw(NS_ERROR_EDITOR_DESTROYED);
116 return;
119 // XXX Should we create another constructor which won't create
120 // AutoEditActionDataSetter? Or should we create another
121 // AutoEditActionDataSetter which won't nest edit action?
122 EditorBase::AutoEditActionDataSetter editActionData(aHTMLEditor,
123 EditAction::eNotEditing);
124 if (NS_WARN_IF(!editActionData.CanHandle())) {
125 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
126 return;
129 AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
130 nsresult rv = aHTMLEditor.CollectEditTargetNodesInExtendedSelectionRanges(
131 arrayOfContents, EditSubAction::eCreateOrChangeList,
132 HTMLEditor::CollectNonEditableNodes::No);
133 if (NS_FAILED(rv)) {
134 NS_WARNING(
135 "HTMLEditor::CollectEditTargetNodesInExtendedSelectionRanges("
136 "eCreateOrChangeList, CollectNonEditableNodes::No) failed");
137 aRv = EditorBase::ToGenericNSResult(rv);
138 return;
141 // examine list type for nodes in selection
142 for (const auto& content : arrayOfContents) {
143 if (!content->IsElement()) {
144 mIsOtherElementSelected = true;
145 } else if (content->IsAnyOfHTMLElements(nsGkAtoms::ul, nsGkAtoms::ol,
146 nsGkAtoms::li)) {
147 mIsLIElementSelected = true;
148 } else if (content->IsHTMLElement(nsGkAtoms::dt)) {
149 mIsDTElementSelected = true;
150 } else if (content->IsHTMLElement(nsGkAtoms::dd)) {
151 mIsDDElementSelected = true;
152 } else if (content->IsHTMLElement(nsGkAtoms::dl)) {
153 if (mIsDTElementSelected && mIsDDElementSelected) {
154 continue;
156 // need to look inside dl and see which types of items it has
157 DefinitionListItemScanner scanner(*content->AsElement());
158 mIsDTElementSelected |= scanner.DTElementFound();
159 mIsDDElementSelected |= scanner.DDElementFound();
160 } else {
161 mIsOtherElementSelected = true;
164 if (mIsLIElementSelected && mIsDTElementSelected && mIsDDElementSelected &&
165 mIsOtherElementSelected) {
166 break;
171 /*****************************************************************************
172 * AlignStateAtSelection
173 ****************************************************************************/
175 AlignStateAtSelection::AlignStateAtSelection(HTMLEditor& aHTMLEditor,
176 ErrorResult& aRv) {
177 MOZ_ASSERT(!aRv.Failed());
179 if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
180 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
181 return;
184 // XXX Should we create another constructor which won't create
185 // AutoEditActionDataSetter? Or should we create another
186 // AutoEditActionDataSetter which won't nest edit action?
187 EditorBase::AutoEditActionDataSetter editActionData(aHTMLEditor,
188 EditAction::eNotEditing);
189 if (NS_WARN_IF(!editActionData.CanHandle())) {
190 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
191 return;
194 if (aHTMLEditor.IsSelectionRangeContainerNotContent()) {
195 NS_WARNING("Some selection containers are not content node, but ignored");
196 return;
199 // For now, just return first alignment. We don't check if it's mixed.
200 // This is for efficiency given that our current UI doesn't care if it's
201 // mixed.
202 // cmanske: NOT TRUE! We would like to pay attention to mixed state in
203 // [Format] -> [Align] submenu!
205 // This routine assumes that alignment is done ONLY by `<div>` elements
206 // if aHTMLEditor is not in CSS mode.
208 if (NS_WARN_IF(!aHTMLEditor.GetRoot())) {
209 aRv.Throw(NS_ERROR_FAILURE);
210 return;
213 OwningNonNull<dom::Element> bodyOrDocumentElement = *aHTMLEditor.GetRoot();
214 EditorRawDOMPoint atBodyOrDocumentElement(bodyOrDocumentElement);
216 const nsRange* firstRange = aHTMLEditor.SelectionRef().GetRangeAt(0);
217 mFoundSelectionRanges = !!firstRange;
218 if (!mFoundSelectionRanges) {
219 NS_WARNING("There was no selection range");
220 aRv.Throw(NS_ERROR_FAILURE);
221 return;
223 EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
224 if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
225 aRv.Throw(NS_ERROR_FAILURE);
226 return;
228 MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
230 nsIContent* editTargetContent = nullptr;
231 // If selection is collapsed or in a text node, take the container.
232 if (aHTMLEditor.SelectionRef().IsCollapsed() ||
233 atStartOfSelection.IsInTextNode()) {
234 editTargetContent = atStartOfSelection.GetContainerAsContent();
235 if (NS_WARN_IF(!editTargetContent)) {
236 aRv.Throw(NS_ERROR_FAILURE);
237 return;
240 // If selection container is the `<body>` element which is set to
241 // `HTMLDocument.body`, take first editable node in it.
242 // XXX Why don't we just compare `atStartOfSelection.GetChild()` and
243 // `bodyOrDocumentElement`? Then, we can avoid computing the
244 // offset.
245 else if (atStartOfSelection.IsContainerHTMLElement(nsGkAtoms::html) &&
246 atBodyOrDocumentElement.IsSet() &&
247 atStartOfSelection.Offset() == atBodyOrDocumentElement.Offset()) {
248 editTargetContent = aHTMLEditor.GetNextEditableNode(atStartOfSelection);
249 if (NS_WARN_IF(!editTargetContent)) {
250 aRv.Throw(NS_ERROR_FAILURE);
251 return;
254 // Otherwise, use first selected node.
255 // XXX Only for retreiving it, the following block treats all selected
256 // ranges. `HTMLEditor` should have
257 // `GetFirstSelectionRangeExtendedToHardLineStartAndEnd()`.
258 else {
259 AutoTArray<RefPtr<nsRange>, 4> arrayOfRanges;
260 aHTMLEditor.GetSelectionRangesExtendedToHardLineStartAndEnd(
261 arrayOfRanges, EditSubAction::eSetOrClearAlignment);
263 AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
264 nsresult rv = aHTMLEditor.CollectEditTargetNodes(
265 arrayOfRanges, arrayOfContents, EditSubAction::eSetOrClearAlignment,
266 HTMLEditor::CollectNonEditableNodes::Yes);
267 if (NS_FAILED(rv)) {
268 NS_WARNING(
269 "HTMLEditor::CollectEditTargetNodes(eSetOrClearAlignment, "
270 "CollectNonEditableNodes::Yes) failed");
271 aRv.Throw(NS_ERROR_FAILURE);
272 return;
274 if (arrayOfContents.IsEmpty()) {
275 NS_WARNING(
276 "HTMLEditor::CollectEditTargetNodes(eSetOrClearAlignment, "
277 "CollectNonEditableNodes::Yes) returned no contents");
278 aRv.Throw(NS_ERROR_FAILURE);
279 return;
281 editTargetContent = arrayOfContents[0];
284 RefPtr<dom::Element> blockElementAtEditTarget =
285 HTMLEditUtils::GetInclusiveAncestorBlockElement(*editTargetContent);
286 if (NS_WARN_IF(!blockElementAtEditTarget)) {
287 aRv.Throw(NS_ERROR_FAILURE);
288 return;
291 if (aHTMLEditor.IsCSSEnabled() &&
292 CSSEditUtils::IsCSSEditableProperty(blockElementAtEditTarget, nullptr,
293 nsGkAtoms::align)) {
294 // We are in CSS mode and we know how to align this element with CSS
295 nsAutoString value;
296 // Let's get the value(s) of text-align or margin-left/margin-right
297 DebugOnly<nsresult> rvIgnored =
298 CSSEditUtils::GetComputedCSSEquivalentToHTMLInlineStyleSet(
299 *blockElementAtEditTarget, nullptr, nsGkAtoms::align, value);
300 if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
301 aRv.Throw(NS_ERROR_EDITOR_DESTROYED);
302 return;
304 NS_WARNING_ASSERTION(
305 NS_SUCCEEDED(rvIgnored),
306 "CSSEditUtils::GetComputedCSSEquivalentToHTMLInlineStyleSet(nsGkAtoms::"
307 "align, "
308 "eComputed) failed, but ignored");
309 if (value.EqualsLiteral("center") || value.EqualsLiteral("-moz-center") ||
310 value.EqualsLiteral("auto auto")) {
311 mFirstAlign = nsIHTMLEditor::eCenter;
312 return;
314 if (value.EqualsLiteral("right") || value.EqualsLiteral("-moz-right") ||
315 value.EqualsLiteral("auto 0px")) {
316 mFirstAlign = nsIHTMLEditor::eRight;
317 return;
319 if (value.EqualsLiteral("justify")) {
320 mFirstAlign = nsIHTMLEditor::eJustify;
321 return;
323 // XXX In RTL document, is this expected?
324 mFirstAlign = nsIHTMLEditor::eLeft;
325 return;
328 for (nsIContent* containerContent :
329 editTargetContent->InclusiveAncestorsOfType<nsIContent>()) {
330 // If the node is a parent `<table>` element of edit target, let's break
331 // here to materialize the 'inline-block' behaviour of html tables
332 // regarding to text alignment.
333 if (containerContent != editTargetContent &&
334 containerContent->IsHTMLElement(nsGkAtoms::table)) {
335 return;
338 if (CSSEditUtils::IsCSSEditableProperty(containerContent, nullptr,
339 nsGkAtoms::align)) {
340 nsAutoString value;
341 DebugOnly<nsresult> rvIgnored = CSSEditUtils::GetSpecifiedProperty(
342 *containerContent, *nsGkAtoms::textAlign, value);
343 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
344 "CSSEditUtils::GetSpecifiedProperty(nsGkAtoms::"
345 "textAlign) failed, but ignored");
346 if (!value.IsEmpty()) {
347 if (value.EqualsLiteral("center")) {
348 mFirstAlign = nsIHTMLEditor::eCenter;
349 return;
351 if (value.EqualsLiteral("right")) {
352 mFirstAlign = nsIHTMLEditor::eRight;
353 return;
355 if (value.EqualsLiteral("justify")) {
356 mFirstAlign = nsIHTMLEditor::eJustify;
357 return;
359 if (value.EqualsLiteral("left")) {
360 mFirstAlign = nsIHTMLEditor::eLeft;
361 return;
363 // XXX
364 // text-align: start and end aren't supported yet
368 if (!HTMLEditUtils::SupportsAlignAttr(*containerContent)) {
369 continue;
372 nsAutoString alignAttributeValue;
373 containerContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::align,
374 alignAttributeValue);
375 if (alignAttributeValue.IsEmpty()) {
376 continue;
379 if (alignAttributeValue.LowerCaseEqualsASCII("center")) {
380 mFirstAlign = nsIHTMLEditor::eCenter;
381 return;
383 if (alignAttributeValue.LowerCaseEqualsASCII("right")) {
384 mFirstAlign = nsIHTMLEditor::eRight;
385 return;
387 // XXX This is odd case. `<div align="justify">` is not in any standards.
388 if (alignAttributeValue.LowerCaseEqualsASCII("justify")) {
389 mFirstAlign = nsIHTMLEditor::eJustify;
390 return;
392 // XXX In RTL document, is this expected?
393 mFirstAlign = nsIHTMLEditor::eLeft;
394 return;
398 /*****************************************************************************
399 * ParagraphStateAtSelection
400 ****************************************************************************/
402 ParagraphStateAtSelection::ParagraphStateAtSelection(HTMLEditor& aHTMLEditor,
403 ErrorResult& aRv) {
404 if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
405 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
406 return;
409 // XXX Should we create another constructor which won't create
410 // AutoEditActionDataSetter? Or should we create another
411 // AutoEditActionDataSetter which won't nest edit action?
412 EditorBase::AutoEditActionDataSetter editActionData(aHTMLEditor,
413 EditAction::eNotEditing);
414 if (NS_WARN_IF(!editActionData.CanHandle())) {
415 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
416 return;
419 if (aHTMLEditor.IsSelectionRangeContainerNotContent()) {
420 NS_WARNING("Some selection containers are not content node, but ignored");
421 return;
424 AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
425 nsresult rv =
426 CollectEditableFormatNodesInSelection(aHTMLEditor, arrayOfContents);
427 if (NS_FAILED(rv)) {
428 NS_WARNING(
429 "ParagraphStateAtSelection::CollectEditableFormatNodesInSelection() "
430 "failed");
431 aRv.Throw(rv);
432 return;
435 // We need to append descendant format block if block nodes are not format
436 // block. This is so we only have to look "up" the hierarchy to find
437 // format nodes, instead of both up and down.
438 for (int32_t i = arrayOfContents.Length() - 1; i >= 0; i--) {
439 auto& content = arrayOfContents[i];
440 nsAutoString format;
441 if (HTMLEditUtils::IsBlockElement(content) &&
442 !HTMLEditUtils::IsFormatNode(content)) {
443 // XXX This RemoveObject() call has already been commented out and
444 // the above comment explained we're trying to replace non-format
445 // block nodes in the array. According to the following blocks and
446 // `AppendDescendantFormatNodesAndFirstInlineNode()`, replacing
447 // non-format block with descendants format blocks makes sense.
448 // arrayOfContents.RemoveObject(node);
449 ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
450 arrayOfContents, *content->AsElement());
454 // We might have an empty node list. if so, find selection parent
455 // and put that on the list
456 if (arrayOfContents.IsEmpty()) {
457 EditorRawDOMPoint atCaret(
458 EditorBase::GetStartPoint(aHTMLEditor.SelectionRef()));
459 if (NS_WARN_IF(!atCaret.IsSet())) {
460 aRv.Throw(NS_ERROR_FAILURE);
461 return;
463 nsIContent* content = atCaret.GetContainerAsContent();
464 if (NS_WARN_IF(!content)) {
465 aRv.Throw(NS_ERROR_FAILURE);
466 return;
468 arrayOfContents.AppendElement(*content);
471 dom::Element* bodyOrDocumentElement = aHTMLEditor.GetRoot();
472 if (NS_WARN_IF(!bodyOrDocumentElement)) {
473 aRv.Throw(NS_ERROR_FAILURE);
474 return;
477 for (auto& content : Reversed(arrayOfContents)) {
478 nsAtom* paragraphStateOfNode = nsGkAtoms::_empty;
479 if (HTMLEditUtils::IsFormatNode(content)) {
480 MOZ_ASSERT(content->NodeInfo()->NameAtom());
481 paragraphStateOfNode = content->NodeInfo()->NameAtom();
483 // Ignore non-format block node since its children have been appended
484 // the list above so that we'll handle this descendants later.
485 else if (HTMLEditUtils::IsBlockElement(content)) {
486 continue;
488 // If we meet an inline node, let's get its parent format.
489 else {
490 for (nsINode* parentNode = content->GetParentNode(); parentNode;
491 parentNode = parentNode->GetParentNode()) {
492 // If we reach `HTMLDocument.body` or `Document.documentElement`,
493 // there is no format.
494 if (parentNode == bodyOrDocumentElement) {
495 break;
497 if (HTMLEditUtils::IsFormatNode(parentNode)) {
498 MOZ_ASSERT(parentNode->NodeInfo()->NameAtom());
499 paragraphStateOfNode = parentNode->NodeInfo()->NameAtom();
500 break;
505 // if this is the first node, we've found, remember it as the format
506 if (!mFirstParagraphState) {
507 mFirstParagraphState = paragraphStateOfNode;
508 continue;
510 // else make sure it matches previously found format
511 if (mFirstParagraphState != paragraphStateOfNode) {
512 mIsMixed = true;
513 break;
518 // static
519 void ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
520 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents,
521 dom::Element& aNonFormatBlockElement) {
522 MOZ_ASSERT(HTMLEditUtils::IsBlockElement(aNonFormatBlockElement));
523 MOZ_ASSERT(!HTMLEditUtils::IsFormatNode(&aNonFormatBlockElement));
525 // We only need to place any one inline inside this node onto
526 // the list. They are all the same for purposes of determining
527 // paragraph style. We use foundInline to track this as we are
528 // going through the children in the loop below.
529 bool foundInline = false;
530 for (nsIContent* childContent = aNonFormatBlockElement.GetFirstChild();
531 childContent; childContent = childContent->GetNextSibling()) {
532 bool isBlock = HTMLEditUtils::IsBlockElement(*childContent);
533 bool isFormat = HTMLEditUtils::IsFormatNode(childContent);
534 // If the child is a non-format block element, let's check its children
535 // recursively.
536 if (isBlock && !isFormat) {
537 ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
538 aArrayOfContents, *childContent->AsElement());
539 continue;
542 // If it's a format block, append it.
543 if (isFormat) {
544 aArrayOfContents.AppendElement(*childContent);
545 continue;
548 MOZ_ASSERT(!isBlock);
550 // If we haven't found inline node, append only this first inline node.
551 // XXX I think that this makes sense if caller of this removes
552 // aNonFormatBlockElement from aArrayOfContents because the last loop
553 // of the constructor can check parent format block with
554 // aNonFormatBlockElement.
555 if (!foundInline) {
556 foundInline = true;
557 aArrayOfContents.AppendElement(*childContent);
558 continue;
563 // static
564 nsresult ParagraphStateAtSelection::CollectEditableFormatNodesInSelection(
565 HTMLEditor& aHTMLEditor,
566 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents) {
567 nsresult rv = aHTMLEditor.CollectEditTargetNodesInExtendedSelectionRanges(
568 aArrayOfContents, EditSubAction::eCreateOrRemoveBlock,
569 HTMLEditor::CollectNonEditableNodes::Yes);
570 if (NS_FAILED(rv)) {
571 NS_WARNING(
572 "HTMLEditor::CollectEditTargetNodesInExtendedSelectionRanges("
573 "eCreateOrRemoveBlock, CollectNonEditableNodes::Yes) failed");
574 return rv;
577 // Pre-process our list of nodes
578 for (int32_t i = aArrayOfContents.Length() - 1; i >= 0; i--) {
579 OwningNonNull<nsIContent> content = aArrayOfContents[i];
581 // Remove all non-editable nodes. Leave them be.
582 if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
583 aArrayOfContents.RemoveElementAt(i);
584 continue;
587 // Scan for table elements. If we find table elements other than table,
588 // replace it with a list of any editable non-table content. Ditto for
589 // list elements.
590 if (HTMLEditUtils::IsAnyTableElement(content) ||
591 HTMLEditUtils::IsAnyListElement(content) ||
592 HTMLEditUtils::IsListItem(content)) {
593 aArrayOfContents.RemoveElementAt(i);
594 aHTMLEditor.CollectChildren(content, aArrayOfContents, i,
595 HTMLEditor::CollectListChildren::Yes,
596 HTMLEditor::CollectTableChildren::Yes,
597 HTMLEditor::CollectNonEditableNodes::Yes);
600 return NS_OK;
603 } // namespace mozilla