Bug 1772588 [wpt PR 34302] - [wpt] Add test for block-in-inline offsetParent., a...
[gecko.git] / editor / libeditor / HTMLEditorState.cpp
blob818f17b1a223b9993ec33907aa0f71625b78ae12
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 using EditorType = EditorUtils::EditorType;
42 using WalkTreeOption = HTMLEditUtils::WalkTreeOption;
44 /*****************************************************************************
45 * ListElementSelectionState
46 ****************************************************************************/
48 ListElementSelectionState::ListElementSelectionState(HTMLEditor& aHTMLEditor,
49 ErrorResult& aRv) {
50 MOZ_ASSERT(!aRv.Failed());
52 if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
53 aRv.Throw(NS_ERROR_EDITOR_DESTROYED);
54 return;
57 // XXX Should we create another constructor which won't create
58 // AutoEditActionDataSetter? Or should we create another
59 // AutoEditActionDataSetter which won't nest edit action?
60 EditorBase::AutoEditActionDataSetter editActionData(aHTMLEditor,
61 EditAction::eNotEditing);
62 if (NS_WARN_IF(!editActionData.CanHandle())) {
63 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
64 return;
67 AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
68 nsresult rv = aHTMLEditor.CollectEditTargetNodesInExtendedSelectionRanges(
69 arrayOfContents, EditSubAction::eCreateOrChangeList,
70 HTMLEditor::CollectNonEditableNodes::No);
71 if (NS_FAILED(rv)) {
72 NS_WARNING(
73 "HTMLEditor::CollectEditTargetNodesInExtendedSelectionRanges("
74 "eCreateOrChangeList, CollectNonEditableNodes::No) failed");
75 aRv = EditorBase::ToGenericNSResult(rv);
76 return;
79 // Examine list type for nodes in selection.
80 for (const auto& content : arrayOfContents) {
81 if (!content->IsElement()) {
82 mIsOtherContentSelected = true;
83 } else if (content->IsHTMLElement(nsGkAtoms::ul)) {
84 mIsULElementSelected = true;
85 } else if (content->IsHTMLElement(nsGkAtoms::ol)) {
86 mIsOLElementSelected = true;
87 } else if (content->IsHTMLElement(nsGkAtoms::li)) {
88 if (dom::Element* parent = content->GetParentElement()) {
89 if (parent->IsHTMLElement(nsGkAtoms::ul)) {
90 mIsULElementSelected = true;
91 } else if (parent->IsHTMLElement(nsGkAtoms::ol)) {
92 mIsOLElementSelected = true;
95 } else if (content->IsAnyOfHTMLElements(nsGkAtoms::dl, nsGkAtoms::dt,
96 nsGkAtoms::dd)) {
97 mIsDLElementSelected = true;
98 } else {
99 mIsOtherContentSelected = true;
102 if (mIsULElementSelected && mIsOLElementSelected && mIsDLElementSelected &&
103 mIsOtherContentSelected) {
104 break;
109 /*****************************************************************************
110 * ListItemElementSelectionState
111 ****************************************************************************/
113 ListItemElementSelectionState::ListItemElementSelectionState(
114 HTMLEditor& aHTMLEditor, ErrorResult& aRv) {
115 MOZ_ASSERT(!aRv.Failed());
117 if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
118 aRv.Throw(NS_ERROR_EDITOR_DESTROYED);
119 return;
122 // XXX Should we create another constructor which won't create
123 // AutoEditActionDataSetter? Or should we create another
124 // AutoEditActionDataSetter which won't nest edit action?
125 EditorBase::AutoEditActionDataSetter editActionData(aHTMLEditor,
126 EditAction::eNotEditing);
127 if (NS_WARN_IF(!editActionData.CanHandle())) {
128 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
129 return;
132 AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
133 nsresult rv = aHTMLEditor.CollectEditTargetNodesInExtendedSelectionRanges(
134 arrayOfContents, EditSubAction::eCreateOrChangeList,
135 HTMLEditor::CollectNonEditableNodes::No);
136 if (NS_FAILED(rv)) {
137 NS_WARNING(
138 "HTMLEditor::CollectEditTargetNodesInExtendedSelectionRanges("
139 "eCreateOrChangeList, CollectNonEditableNodes::No) failed");
140 aRv = EditorBase::ToGenericNSResult(rv);
141 return;
144 // examine list type for nodes in selection
145 for (const auto& content : arrayOfContents) {
146 if (!content->IsElement()) {
147 mIsOtherElementSelected = true;
148 } else if (content->IsAnyOfHTMLElements(nsGkAtoms::ul, nsGkAtoms::ol,
149 nsGkAtoms::li)) {
150 mIsLIElementSelected = true;
151 } else if (content->IsHTMLElement(nsGkAtoms::dt)) {
152 mIsDTElementSelected = true;
153 } else if (content->IsHTMLElement(nsGkAtoms::dd)) {
154 mIsDDElementSelected = true;
155 } else if (content->IsHTMLElement(nsGkAtoms::dl)) {
156 if (mIsDTElementSelected && mIsDDElementSelected) {
157 continue;
159 // need to look inside dl and see which types of items it has
160 DefinitionListItemScanner scanner(*content->AsElement());
161 mIsDTElementSelected |= scanner.DTElementFound();
162 mIsDDElementSelected |= scanner.DDElementFound();
163 } else {
164 mIsOtherElementSelected = true;
167 if (mIsLIElementSelected && mIsDTElementSelected && mIsDDElementSelected &&
168 mIsOtherElementSelected) {
169 break;
174 /*****************************************************************************
175 * AlignStateAtSelection
176 ****************************************************************************/
178 AlignStateAtSelection::AlignStateAtSelection(HTMLEditor& aHTMLEditor,
179 ErrorResult& aRv) {
180 MOZ_ASSERT(!aRv.Failed());
182 if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
183 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
184 return;
187 // XXX Should we create another constructor which won't create
188 // AutoEditActionDataSetter? Or should we create another
189 // AutoEditActionDataSetter which won't nest edit action?
190 EditorBase::AutoEditActionDataSetter editActionData(aHTMLEditor,
191 EditAction::eNotEditing);
192 if (NS_WARN_IF(!editActionData.CanHandle())) {
193 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
194 return;
197 if (aHTMLEditor.IsSelectionRangeContainerNotContent()) {
198 NS_WARNING("Some selection containers are not content node, but ignored");
199 return;
202 // For now, just return first alignment. We don't check if it's mixed.
203 // This is for efficiency given that our current UI doesn't care if it's
204 // mixed.
205 // cmanske: NOT TRUE! We would like to pay attention to mixed state in
206 // [Format] -> [Align] submenu!
208 // This routine assumes that alignment is done ONLY by `<div>` elements
209 // if aHTMLEditor is not in CSS mode.
211 if (NS_WARN_IF(!aHTMLEditor.GetRoot())) {
212 aRv.Throw(NS_ERROR_FAILURE);
213 return;
216 OwningNonNull<dom::Element> bodyOrDocumentElement = *aHTMLEditor.GetRoot();
217 EditorRawDOMPoint atBodyOrDocumentElement(bodyOrDocumentElement);
219 const nsRange* firstRange = aHTMLEditor.SelectionRef().GetRangeAt(0);
220 mFoundSelectionRanges = !!firstRange;
221 if (!mFoundSelectionRanges) {
222 NS_WARNING("There was no selection range");
223 aRv.Throw(NS_ERROR_FAILURE);
224 return;
226 EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
227 if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
228 aRv.Throw(NS_ERROR_FAILURE);
229 return;
231 MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
233 nsIContent* editTargetContent = nullptr;
234 // If selection is collapsed or in a text node, take the container.
235 if (aHTMLEditor.SelectionRef().IsCollapsed() ||
236 atStartOfSelection.IsInTextNode()) {
237 editTargetContent = atStartOfSelection.GetContainerAsContent();
238 if (NS_WARN_IF(!editTargetContent)) {
239 aRv.Throw(NS_ERROR_FAILURE);
240 return;
243 // If selection container is the `<body>` element which is set to
244 // `HTMLDocument.body`, take first editable node in it.
245 // XXX Why don't we just compare `atStartOfSelection.GetChild()` and
246 // `bodyOrDocumentElement`? Then, we can avoid computing the
247 // offset.
248 else if (atStartOfSelection.IsContainerHTMLElement(nsGkAtoms::html) &&
249 atBodyOrDocumentElement.IsSet() &&
250 atStartOfSelection.Offset() == atBodyOrDocumentElement.Offset()) {
251 editTargetContent = HTMLEditUtils::GetNextContent(
252 atStartOfSelection, {WalkTreeOption::IgnoreNonEditableNode},
253 aHTMLEditor.ComputeEditingHost());
254 if (NS_WARN_IF(!editTargetContent)) {
255 aRv.Throw(NS_ERROR_FAILURE);
256 return;
259 // Otherwise, use first selected node.
260 // XXX Only for retreiving it, the following block treats all selected
261 // ranges. `HTMLEditor` should have
262 // `GetFirstSelectionRangeExtendedToHardLineStartAndEnd()`.
263 else {
264 AutoTArray<RefPtr<nsRange>, 4> arrayOfRanges;
265 aHTMLEditor.GetSelectionRangesExtendedToHardLineStartAndEnd(
266 arrayOfRanges, EditSubAction::eSetOrClearAlignment);
268 AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
269 nsresult rv = aHTMLEditor.CollectEditTargetNodes(
270 arrayOfRanges, arrayOfContents, EditSubAction::eSetOrClearAlignment,
271 HTMLEditor::CollectNonEditableNodes::Yes);
272 if (NS_FAILED(rv)) {
273 NS_WARNING(
274 "HTMLEditor::CollectEditTargetNodes(eSetOrClearAlignment, "
275 "CollectNonEditableNodes::Yes) failed");
276 aRv.Throw(NS_ERROR_FAILURE);
277 return;
279 if (arrayOfContents.IsEmpty()) {
280 NS_WARNING(
281 "HTMLEditor::CollectEditTargetNodes(eSetOrClearAlignment, "
282 "CollectNonEditableNodes::Yes) returned no contents");
283 aRv.Throw(NS_ERROR_FAILURE);
284 return;
286 editTargetContent = arrayOfContents[0];
289 const RefPtr<dom::Element> maybeNonEditableBlockElement =
290 HTMLEditUtils::GetInclusiveAncestorElement(
291 *editTargetContent, HTMLEditUtils::ClosestBlockElement);
292 if (NS_WARN_IF(!maybeNonEditableBlockElement)) {
293 aRv.Throw(NS_ERROR_FAILURE);
294 return;
297 if (aHTMLEditor.IsCSSEnabled() &&
298 CSSEditUtils::IsCSSEditableProperty(maybeNonEditableBlockElement, nullptr,
299 nsGkAtoms::align)) {
300 // We are in CSS mode and we know how to align this element with CSS
301 nsAutoString value;
302 // Let's get the value(s) of text-align or margin-left/margin-right
303 DebugOnly<nsresult> rvIgnored =
304 CSSEditUtils::GetComputedCSSEquivalentToHTMLInlineStyleSet(
305 *maybeNonEditableBlockElement, nullptr, nsGkAtoms::align, value);
306 if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
307 aRv.Throw(NS_ERROR_EDITOR_DESTROYED);
308 return;
310 NS_WARNING_ASSERTION(
311 NS_SUCCEEDED(rvIgnored),
312 "CSSEditUtils::GetComputedCSSEquivalentToHTMLInlineStyleSet(nsGkAtoms::"
313 "align, "
314 "eComputed) failed, but ignored");
315 if (value.EqualsLiteral("center") || value.EqualsLiteral("-moz-center") ||
316 value.EqualsLiteral("auto auto")) {
317 mFirstAlign = nsIHTMLEditor::eCenter;
318 return;
320 if (value.EqualsLiteral("right") || value.EqualsLiteral("-moz-right") ||
321 value.EqualsLiteral("auto 0px")) {
322 mFirstAlign = nsIHTMLEditor::eRight;
323 return;
325 if (value.EqualsLiteral("justify")) {
326 mFirstAlign = nsIHTMLEditor::eJustify;
327 return;
329 // XXX In RTL document, is this expected?
330 mFirstAlign = nsIHTMLEditor::eLeft;
331 return;
334 for (nsIContent* containerContent :
335 editTargetContent->InclusiveAncestorsOfType<nsIContent>()) {
336 // If the node is a parent `<table>` element of edit target, let's break
337 // here to materialize the 'inline-block' behaviour of html tables
338 // regarding to text alignment.
339 if (containerContent != editTargetContent &&
340 containerContent->IsHTMLElement(nsGkAtoms::table)) {
341 return;
344 if (CSSEditUtils::IsCSSEditableProperty(containerContent, nullptr,
345 nsGkAtoms::align)) {
346 nsAutoString value;
347 DebugOnly<nsresult> rvIgnored = CSSEditUtils::GetSpecifiedProperty(
348 *containerContent, *nsGkAtoms::textAlign, value);
349 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
350 "CSSEditUtils::GetSpecifiedProperty(nsGkAtoms::"
351 "textAlign) failed, but ignored");
352 if (!value.IsEmpty()) {
353 if (value.EqualsLiteral("center")) {
354 mFirstAlign = nsIHTMLEditor::eCenter;
355 return;
357 if (value.EqualsLiteral("right")) {
358 mFirstAlign = nsIHTMLEditor::eRight;
359 return;
361 if (value.EqualsLiteral("justify")) {
362 mFirstAlign = nsIHTMLEditor::eJustify;
363 return;
365 if (value.EqualsLiteral("left")) {
366 mFirstAlign = nsIHTMLEditor::eLeft;
367 return;
369 // XXX
370 // text-align: start and end aren't supported yet
374 if (!HTMLEditUtils::SupportsAlignAttr(*containerContent)) {
375 continue;
378 nsAutoString alignAttributeValue;
379 containerContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::align,
380 alignAttributeValue);
381 if (alignAttributeValue.IsEmpty()) {
382 continue;
385 if (alignAttributeValue.LowerCaseEqualsASCII("center")) {
386 mFirstAlign = nsIHTMLEditor::eCenter;
387 return;
389 if (alignAttributeValue.LowerCaseEqualsASCII("right")) {
390 mFirstAlign = nsIHTMLEditor::eRight;
391 return;
393 // XXX This is odd case. `<div align="justify">` is not in any standards.
394 if (alignAttributeValue.LowerCaseEqualsASCII("justify")) {
395 mFirstAlign = nsIHTMLEditor::eJustify;
396 return;
398 // XXX In RTL document, is this expected?
399 mFirstAlign = nsIHTMLEditor::eLeft;
400 return;
404 /*****************************************************************************
405 * ParagraphStateAtSelection
406 ****************************************************************************/
408 ParagraphStateAtSelection::ParagraphStateAtSelection(HTMLEditor& aHTMLEditor,
409 ErrorResult& aRv) {
410 if (NS_WARN_IF(aHTMLEditor.Destroyed())) {
411 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
412 return;
415 // XXX Should we create another constructor which won't create
416 // AutoEditActionDataSetter? Or should we create another
417 // AutoEditActionDataSetter which won't nest edit action?
418 EditorBase::AutoEditActionDataSetter editActionData(aHTMLEditor,
419 EditAction::eNotEditing);
420 if (NS_WARN_IF(!editActionData.CanHandle())) {
421 aRv = EditorBase::ToGenericNSResult(NS_ERROR_EDITOR_DESTROYED);
422 return;
425 if (aHTMLEditor.IsSelectionRangeContainerNotContent()) {
426 NS_WARNING("Some selection containers are not content node, but ignored");
427 return;
430 AutoTArray<OwningNonNull<nsIContent>, 64> arrayOfContents;
431 nsresult rv =
432 CollectEditableFormatNodesInSelection(aHTMLEditor, arrayOfContents);
433 if (NS_FAILED(rv)) {
434 NS_WARNING(
435 "ParagraphStateAtSelection::CollectEditableFormatNodesInSelection() "
436 "failed");
437 aRv.Throw(rv);
438 return;
441 // We need to append descendant format block if block nodes are not format
442 // block. This is so we only have to look "up" the hierarchy to find
443 // format nodes, instead of both up and down.
444 for (int32_t i = arrayOfContents.Length() - 1; i >= 0; i--) {
445 auto& content = arrayOfContents[i];
446 nsAutoString format;
447 if (HTMLEditUtils::IsBlockElement(content) &&
448 !HTMLEditUtils::IsFormatNode(content)) {
449 // XXX This RemoveObject() call has already been commented out and
450 // the above comment explained we're trying to replace non-format
451 // block nodes in the array. According to the following blocks and
452 // `AppendDescendantFormatNodesAndFirstInlineNode()`, replacing
453 // non-format block with descendants format blocks makes sense.
454 // arrayOfContents.RemoveObject(node);
455 ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
456 arrayOfContents, *content->AsElement());
460 // We might have an empty node list. if so, find selection parent
461 // and put that on the list
462 if (arrayOfContents.IsEmpty()) {
463 const auto atCaret =
464 aHTMLEditor.GetFirstSelectionStartPoint<EditorRawDOMPoint>();
465 if (NS_WARN_IF(!atCaret.IsSet())) {
466 aRv.Throw(NS_ERROR_FAILURE);
467 return;
469 nsIContent* content = atCaret.GetContainerAsContent();
470 if (NS_WARN_IF(!content)) {
471 aRv.Throw(NS_ERROR_FAILURE);
472 return;
474 arrayOfContents.AppendElement(*content);
477 dom::Element* bodyOrDocumentElement = aHTMLEditor.GetRoot();
478 if (NS_WARN_IF(!bodyOrDocumentElement)) {
479 aRv.Throw(NS_ERROR_FAILURE);
480 return;
483 for (auto& content : Reversed(arrayOfContents)) {
484 nsAtom* paragraphStateOfNode = nsGkAtoms::_empty;
485 if (HTMLEditUtils::IsFormatNode(content)) {
486 MOZ_ASSERT(content->NodeInfo()->NameAtom());
487 paragraphStateOfNode = content->NodeInfo()->NameAtom();
489 // Ignore non-format block node since its children have been appended
490 // the list above so that we'll handle this descendants later.
491 else if (HTMLEditUtils::IsBlockElement(content)) {
492 continue;
494 // If we meet an inline node, let's get its parent format.
495 else {
496 for (nsINode* parentNode = content->GetParentNode(); parentNode;
497 parentNode = parentNode->GetParentNode()) {
498 // If we reach `HTMLDocument.body` or `Document.documentElement`,
499 // there is no format.
500 if (parentNode == bodyOrDocumentElement) {
501 break;
503 if (HTMLEditUtils::IsFormatNode(parentNode)) {
504 MOZ_ASSERT(parentNode->NodeInfo()->NameAtom());
505 paragraphStateOfNode = parentNode->NodeInfo()->NameAtom();
506 break;
511 // if this is the first node, we've found, remember it as the format
512 if (!mFirstParagraphState) {
513 mFirstParagraphState = paragraphStateOfNode;
514 continue;
516 // else make sure it matches previously found format
517 if (mFirstParagraphState != paragraphStateOfNode) {
518 mIsMixed = true;
519 break;
524 // static
525 void ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
526 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents,
527 dom::Element& aNonFormatBlockElement) {
528 MOZ_ASSERT(HTMLEditUtils::IsBlockElement(aNonFormatBlockElement));
529 MOZ_ASSERT(!HTMLEditUtils::IsFormatNode(&aNonFormatBlockElement));
531 // We only need to place any one inline inside this node onto
532 // the list. They are all the same for purposes of determining
533 // paragraph style. We use foundInline to track this as we are
534 // going through the children in the loop below.
535 bool foundInline = false;
536 for (nsIContent* childContent = aNonFormatBlockElement.GetFirstChild();
537 childContent; childContent = childContent->GetNextSibling()) {
538 bool isBlock = HTMLEditUtils::IsBlockElement(*childContent);
539 bool isFormat = HTMLEditUtils::IsFormatNode(childContent);
540 // If the child is a non-format block element, let's check its children
541 // recursively.
542 if (isBlock && !isFormat) {
543 ParagraphStateAtSelection::AppendDescendantFormatNodesAndFirstInlineNode(
544 aArrayOfContents, *childContent->AsElement());
545 continue;
548 // If it's a format block, append it.
549 if (isFormat) {
550 aArrayOfContents.AppendElement(*childContent);
551 continue;
554 MOZ_ASSERT(!isBlock);
556 // If we haven't found inline node, append only this first inline node.
557 // XXX I think that this makes sense if caller of this removes
558 // aNonFormatBlockElement from aArrayOfContents because the last loop
559 // of the constructor can check parent format block with
560 // aNonFormatBlockElement.
561 if (!foundInline) {
562 foundInline = true;
563 aArrayOfContents.AppendElement(*childContent);
564 continue;
569 // static
570 nsresult ParagraphStateAtSelection::CollectEditableFormatNodesInSelection(
571 HTMLEditor& aHTMLEditor,
572 nsTArray<OwningNonNull<nsIContent>>& aArrayOfContents) {
573 nsresult rv = aHTMLEditor.CollectEditTargetNodesInExtendedSelectionRanges(
574 aArrayOfContents, EditSubAction::eCreateOrRemoveBlock,
575 HTMLEditor::CollectNonEditableNodes::Yes);
576 if (NS_FAILED(rv)) {
577 NS_WARNING(
578 "HTMLEditor::CollectEditTargetNodesInExtendedSelectionRanges("
579 "eCreateOrRemoveBlock, CollectNonEditableNodes::Yes) failed");
580 return rv;
583 // Pre-process our list of nodes
584 for (int32_t i = aArrayOfContents.Length() - 1; i >= 0; i--) {
585 OwningNonNull<nsIContent> content = aArrayOfContents[i];
587 // Remove all non-editable nodes. Leave them be.
588 if (!EditorUtils::IsEditableContent(content, EditorType::HTML)) {
589 aArrayOfContents.RemoveElementAt(i);
590 continue;
593 // Scan for table elements. If we find table elements other than table,
594 // replace it with a list of any editable non-table content. Ditto for
595 // list elements.
596 if (HTMLEditUtils::IsAnyTableElement(content) ||
597 HTMLEditUtils::IsAnyListElement(content) ||
598 HTMLEditUtils::IsListItem(content)) {
599 aArrayOfContents.RemoveElementAt(i);
600 aHTMLEditor.CollectChildren(content, aArrayOfContents, i,
601 HTMLEditor::CollectListChildren::Yes,
602 HTMLEditor::CollectTableChildren::Yes,
603 HTMLEditor::CollectNonEditableNodes::Yes);
606 return NS_OK;
609 } // namespace mozilla