Bumping manifests a=b2g-bump
[gecko.git] / editor / libeditor / nsHTMLEditUtils.cpp
blob7537573567605c6351efb6add352de8e8bae65bc
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/ArrayUtils.h" // for ArrayLength
7 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc
8 #include "mozilla/dom/Element.h" // for Element, nsINode
9 #include "nsAString.h" // for nsAString_internal::IsEmpty
10 #include "nsCOMPtr.h" // for nsCOMPtr, operator==, etc
11 #include "nsCaseTreatment.h"
12 #include "nsDebug.h" // for NS_PRECONDITION, etc
13 #include "nsEditor.h" // for nsEditor
14 #include "nsError.h" // for NS_SUCCEEDED
15 #include "nsGkAtoms.h" // for nsGkAtoms, nsGkAtoms::a, etc
16 #include "nsHTMLEditUtils.h"
17 #include "nsHTMLTags.h"
18 #include "nsIAtom.h" // for nsIAtom
19 #include "nsIDOMHTMLAnchorElement.h" // for nsIDOMHTMLAnchorElement
20 #include "nsIDOMNode.h" // for nsIDOMNode
21 #include "nsNameSpaceManager.h" // for kNameSpaceID_None
22 #include "nsLiteralString.h" // for NS_LITERAL_STRING
23 #include "nsString.h" // for nsAutoString
24 #include "nsTextEditUtils.h" // for nsTextEditUtils
26 using namespace mozilla;
28 ///////////////////////////////////////////////////////////////////////////
29 //
30 bool
31 nsHTMLEditUtils::IsBig(nsIDOMNode* aNode)
33 return nsEditor::NodeIsType(aNode, nsGkAtoms::big);
37 ///////////////////////////////////////////////////////////////////////////
38 // IsInlineStyle true if node is an inline style
39 //
40 bool
41 nsHTMLEditUtils::IsInlineStyle(nsIDOMNode* aNode)
43 NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsInlineStyle");
44 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
45 return node && IsInlineStyle(node);
48 bool
49 nsHTMLEditUtils::IsInlineStyle(nsINode* aNode)
51 MOZ_ASSERT(aNode);
52 nsIAtom* nodeAtom = aNode->Tag();
53 return (nodeAtom == nsGkAtoms::b)
54 || (nodeAtom == nsGkAtoms::i)
55 || (nodeAtom == nsGkAtoms::u)
56 || (nodeAtom == nsGkAtoms::tt)
57 || (nodeAtom == nsGkAtoms::s)
58 || (nodeAtom == nsGkAtoms::strike)
59 || (nodeAtom == nsGkAtoms::big)
60 || (nodeAtom == nsGkAtoms::small)
61 || (nodeAtom == nsGkAtoms::sub)
62 || (nodeAtom == nsGkAtoms::sup)
63 || (nodeAtom == nsGkAtoms::font);
66 ///////////////////////////////////////////////////////////////////////////
67 // IsFormatNode true if node is a format node
68 //
69 bool
70 nsHTMLEditUtils::IsFormatNode(nsIDOMNode* aNode)
72 NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsFormatNode");
73 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
74 return node && IsFormatNode(node);
77 bool
78 nsHTMLEditUtils::IsFormatNode(nsINode* aNode)
80 MOZ_ASSERT(aNode);
81 nsIAtom* nodeAtom = aNode->Tag();
82 return (nodeAtom == nsGkAtoms::p)
83 || (nodeAtom == nsGkAtoms::pre)
84 || (nodeAtom == nsGkAtoms::h1)
85 || (nodeAtom == nsGkAtoms::h2)
86 || (nodeAtom == nsGkAtoms::h3)
87 || (nodeAtom == nsGkAtoms::h4)
88 || (nodeAtom == nsGkAtoms::h5)
89 || (nodeAtom == nsGkAtoms::h6)
90 || (nodeAtom == nsGkAtoms::address);
93 ///////////////////////////////////////////////////////////////////////////
94 // IsNodeThatCanOutdent true if node is a list, list item, or blockquote
96 bool
97 nsHTMLEditUtils::IsNodeThatCanOutdent(nsIDOMNode* aNode)
99 NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsNodeThatCanOutdent");
100 nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode);
101 return (nodeAtom == nsGkAtoms::ul)
102 || (nodeAtom == nsGkAtoms::ol)
103 || (nodeAtom == nsGkAtoms::dl)
104 || (nodeAtom == nsGkAtoms::li)
105 || (nodeAtom == nsGkAtoms::dd)
106 || (nodeAtom == nsGkAtoms::dt)
107 || (nodeAtom == nsGkAtoms::blockquote);
110 ///////////////////////////////////////////////////////////////////////////
112 bool
113 nsHTMLEditUtils::IsSmall(nsIDOMNode* aNode)
115 return nsEditor::NodeIsType(aNode, nsGkAtoms::small);
119 /********************************************************
120 * helper methods from nsHTMLEditRules
121 ********************************************************/
123 ///////////////////////////////////////////////////////////////////////////
124 // IsHeader: true if node an html header
126 bool
127 nsHTMLEditUtils::IsHeader(nsIDOMNode* aNode)
129 NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsHeader");
130 nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode);
131 return (nodeAtom == nsGkAtoms::h1)
132 || (nodeAtom == nsGkAtoms::h2)
133 || (nodeAtom == nsGkAtoms::h3)
134 || (nodeAtom == nsGkAtoms::h4)
135 || (nodeAtom == nsGkAtoms::h5)
136 || (nodeAtom == nsGkAtoms::h6);
140 ///////////////////////////////////////////////////////////////////////////
141 // IsParagraph: true if node an html paragraph
143 bool
144 nsHTMLEditUtils::IsParagraph(nsIDOMNode* aNode)
146 return nsEditor::NodeIsType(aNode, nsGkAtoms::p);
150 ///////////////////////////////////////////////////////////////////////////
151 // IsHR: true if node an horizontal rule
153 bool
154 nsHTMLEditUtils::IsHR(nsIDOMNode* aNode)
156 return nsEditor::NodeIsType(aNode, nsGkAtoms::hr);
160 ///////////////////////////////////////////////////////////////////////////
161 // IsListItem: true if node an html list item
163 bool
164 nsHTMLEditUtils::IsListItem(nsIDOMNode* aNode)
166 NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsListItem");
167 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
168 return node && IsListItem(node);
171 bool
172 nsHTMLEditUtils::IsListItem(nsINode* node)
174 MOZ_ASSERT(node);
175 nsCOMPtr<nsIAtom> nodeAtom = node->Tag();
176 return (nodeAtom == nsGkAtoms::li)
177 || (nodeAtom == nsGkAtoms::dd)
178 || (nodeAtom == nsGkAtoms::dt);
182 ///////////////////////////////////////////////////////////////////////////
183 // IsTableElement: true if node an html table, td, tr, ...
185 bool
186 nsHTMLEditUtils::IsTableElement(nsIDOMNode* aNode)
188 NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::IsTableElement");
189 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
190 return node && IsTableElement(node);
193 bool
194 nsHTMLEditUtils::IsTableElement(nsINode* node)
196 MOZ_ASSERT(node);
197 nsCOMPtr<nsIAtom> nodeAtom = node->Tag();
198 return (nodeAtom == nsGkAtoms::table)
199 || (nodeAtom == nsGkAtoms::tr)
200 || (nodeAtom == nsGkAtoms::td)
201 || (nodeAtom == nsGkAtoms::th)
202 || (nodeAtom == nsGkAtoms::thead)
203 || (nodeAtom == nsGkAtoms::tfoot)
204 || (nodeAtom == nsGkAtoms::tbody)
205 || (nodeAtom == nsGkAtoms::caption);
208 ///////////////////////////////////////////////////////////////////////////
209 // IsTableElementButNotTable: true if node an html td, tr, ... (doesn't include table)
211 bool
212 nsHTMLEditUtils::IsTableElementButNotTable(nsIDOMNode* aNode)
214 NS_PRECONDITION(aNode, "null node passed to nsHTMLEditor::IsTableElementButNotTable");
215 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
216 return node && IsTableElementButNotTable(node);
219 bool
220 nsHTMLEditUtils::IsTableElementButNotTable(nsINode* aNode)
222 MOZ_ASSERT(aNode);
223 nsCOMPtr<nsIAtom> nodeAtom = aNode->Tag();
224 return (nodeAtom == nsGkAtoms::tr)
225 || (nodeAtom == nsGkAtoms::td)
226 || (nodeAtom == nsGkAtoms::th)
227 || (nodeAtom == nsGkAtoms::thead)
228 || (nodeAtom == nsGkAtoms::tfoot)
229 || (nodeAtom == nsGkAtoms::tbody)
230 || (nodeAtom == nsGkAtoms::caption);
233 ///////////////////////////////////////////////////////////////////////////
234 // IsTable: true if node an html table
236 bool
237 nsHTMLEditUtils::IsTable(nsIDOMNode* aNode)
239 return nsEditor::NodeIsType(aNode, nsGkAtoms::table);
242 bool
243 nsHTMLEditUtils::IsTable(nsINode* aNode)
245 return aNode && aNode->IsElement() && aNode->Tag() == nsGkAtoms::table;
248 ///////////////////////////////////////////////////////////////////////////
249 // IsTableRow: true if node an html tr
251 bool
252 nsHTMLEditUtils::IsTableRow(nsIDOMNode* aNode)
254 return nsEditor::NodeIsType(aNode, nsGkAtoms::tr);
258 ///////////////////////////////////////////////////////////////////////////
259 // IsTableCell: true if node an html td or th
261 bool
262 nsHTMLEditUtils::IsTableCell(nsIDOMNode* aNode)
264 NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsTableCell");
265 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
266 return node && IsTableCell(node);
269 bool
270 nsHTMLEditUtils::IsTableCell(nsINode* node)
272 MOZ_ASSERT(node);
273 nsCOMPtr<nsIAtom> nodeAtom = node->Tag();
274 return (nodeAtom == nsGkAtoms::td)
275 || (nodeAtom == nsGkAtoms::th);
279 ///////////////////////////////////////////////////////////////////////////
280 // IsTableCell: true if node an html td or th
282 bool
283 nsHTMLEditUtils::IsTableCellOrCaption(nsIDOMNode* aNode)
285 NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsTableCell");
286 nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode);
287 return (nodeAtom == nsGkAtoms::td)
288 || (nodeAtom == nsGkAtoms::th)
289 || (nodeAtom == nsGkAtoms::caption);
293 ///////////////////////////////////////////////////////////////////////////
294 // IsList: true if node an html list
296 bool
297 nsHTMLEditUtils::IsList(nsIDOMNode* aNode)
299 NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsList");
300 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
301 return node && IsList(node);
304 bool
305 nsHTMLEditUtils::IsList(nsINode* node)
307 MOZ_ASSERT(node);
308 nsCOMPtr<nsIAtom> nodeAtom = node->Tag();
309 return (nodeAtom == nsGkAtoms::ul)
310 || (nodeAtom == nsGkAtoms::ol)
311 || (nodeAtom == nsGkAtoms::dl);
315 ///////////////////////////////////////////////////////////////////////////
316 // IsOrderedList: true if node an html ordered list
318 bool
319 nsHTMLEditUtils::IsOrderedList(nsIDOMNode* aNode)
321 return nsEditor::NodeIsType(aNode, nsGkAtoms::ol);
325 ///////////////////////////////////////////////////////////////////////////
326 // IsUnorderedList: true if node an html unordered list
328 bool
329 nsHTMLEditUtils::IsUnorderedList(nsIDOMNode* aNode)
331 return nsEditor::NodeIsType(aNode, nsGkAtoms::ul);
335 ///////////////////////////////////////////////////////////////////////////
336 // IsBlockquote: true if node an html blockquote node
338 bool
339 nsHTMLEditUtils::IsBlockquote(nsIDOMNode* aNode)
341 return nsEditor::NodeIsType(aNode, nsGkAtoms::blockquote);
345 ///////////////////////////////////////////////////////////////////////////
346 // IsPre: true if node an html pre node
348 bool
349 nsHTMLEditUtils::IsPre(nsIDOMNode* aNode)
351 return nsEditor::NodeIsType(aNode, nsGkAtoms::pre);
355 ///////////////////////////////////////////////////////////////////////////
356 // IsImage: true if node an html image node
358 bool
359 nsHTMLEditUtils::IsImage(nsIDOMNode* aNode)
361 return nsEditor::NodeIsType(aNode, nsGkAtoms::img);
364 bool
365 nsHTMLEditUtils::IsLink(nsIDOMNode *aNode)
367 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
368 return node && IsLink(node);
371 bool
372 nsHTMLEditUtils::IsLink(nsINode* aNode)
374 MOZ_ASSERT(aNode);
376 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor = do_QueryInterface(aNode);
377 if (anchor)
379 nsAutoString tmpText;
380 if (NS_SUCCEEDED(anchor->GetHref(tmpText)) && !tmpText.IsEmpty()) {
381 return true;
384 return false;
387 bool
388 nsHTMLEditUtils::IsNamedAnchor(nsIDOMNode *aNode)
390 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
391 return node && IsNamedAnchor(node);
394 bool
395 nsHTMLEditUtils::IsNamedAnchor(nsINode* aNode)
397 MOZ_ASSERT(aNode);
398 if (!aNode->IsElement() || !aNode->AsElement()->IsHTML(nsGkAtoms::a)) {
399 return false;
402 nsAutoString text;
403 return aNode->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::name,
404 text) && !text.IsEmpty();
408 ///////////////////////////////////////////////////////////////////////////
409 // IsDiv: true if node an html div node
411 bool
412 nsHTMLEditUtils::IsDiv(nsIDOMNode* aNode)
414 return nsEditor::NodeIsType(aNode, nsGkAtoms::div);
418 ///////////////////////////////////////////////////////////////////////////
419 // IsMozDiv: true if node an html div node with type = _moz
421 bool
422 nsHTMLEditUtils::IsMozDiv(nsIDOMNode* aNode)
424 if (IsDiv(aNode) && nsTextEditUtils::HasMozAttr(aNode)) return true;
425 return false;
428 bool
429 nsHTMLEditUtils::IsMozDiv(nsINode* aNode)
431 MOZ_ASSERT(aNode);
432 return aNode->Tag() == nsGkAtoms::div &&
433 nsTextEditUtils::HasMozAttr(GetAsDOMNode(aNode));
437 ///////////////////////////////////////////////////////////////////////////
438 // IsMailCite: true if node an html blockquote with type=cite
440 bool
441 nsHTMLEditUtils::IsMailCite(nsIDOMNode* aNode)
443 NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsMailCite");
444 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
445 return node && IsMailCite(node);
448 bool
449 nsHTMLEditUtils::IsMailCite(nsINode* aNode)
451 MOZ_ASSERT(aNode);
453 // don't ask me why, but our html mailcites are id'd by "type=cite"...
454 if (aNode->IsElement() &&
455 aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
456 NS_LITERAL_STRING("cite"),
457 eIgnoreCase)) {
458 return true;
461 // ... but our plaintext mailcites by "_moz_quote=true". go figure.
462 if (aNode->IsElement() &&
463 aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozquote,
464 NS_LITERAL_STRING("true"),
465 eIgnoreCase)) {
466 return true;
469 return false;
473 ///////////////////////////////////////////////////////////////////////////
474 // IsFormWidget: true if node is a form widget of some kind
476 bool
477 nsHTMLEditUtils::IsFormWidget(nsIDOMNode* aNode)
479 NS_PRECONDITION(aNode, "null node passed to nsHTMLEditUtils::IsFormWidget");
480 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
481 return node && IsFormWidget(node);
484 bool
485 nsHTMLEditUtils::IsFormWidget(nsINode* aNode)
487 MOZ_ASSERT(aNode);
488 nsCOMPtr<nsIAtom> nodeAtom = aNode->Tag();
489 return (nodeAtom == nsGkAtoms::textarea)
490 || (nodeAtom == nsGkAtoms::select)
491 || (nodeAtom == nsGkAtoms::button)
492 || (nodeAtom == nsGkAtoms::output)
493 || (nodeAtom == nsGkAtoms::keygen)
494 || (nodeAtom == nsGkAtoms::progress)
495 || (nodeAtom == nsGkAtoms::meter)
496 || (nodeAtom == nsGkAtoms::input);
499 bool
500 nsHTMLEditUtils::SupportsAlignAttr(nsIDOMNode* aNode)
502 NS_PRECONDITION(aNode, "null node passed to nsHTMLEditUtils::SupportsAlignAttr");
503 nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode);
504 return (nodeAtom == nsGkAtoms::hr)
505 || (nodeAtom == nsGkAtoms::table)
506 || (nodeAtom == nsGkAtoms::tbody)
507 || (nodeAtom == nsGkAtoms::tfoot)
508 || (nodeAtom == nsGkAtoms::thead)
509 || (nodeAtom == nsGkAtoms::tr)
510 || (nodeAtom == nsGkAtoms::td)
511 || (nodeAtom == nsGkAtoms::th)
512 || (nodeAtom == nsGkAtoms::div)
513 || (nodeAtom == nsGkAtoms::p)
514 || (nodeAtom == nsGkAtoms::h1)
515 || (nodeAtom == nsGkAtoms::h2)
516 || (nodeAtom == nsGkAtoms::h3)
517 || (nodeAtom == nsGkAtoms::h4)
518 || (nodeAtom == nsGkAtoms::h5)
519 || (nodeAtom == nsGkAtoms::h6);
522 // We use bitmasks to test containment of elements. Elements are marked to be
523 // in certain groups by setting the mGroup member of the nsElementInfo struct
524 // to the corresponding GROUP_ values (OR'ed together). Similarly, elements are
525 // marked to allow containment of certain groups by setting the
526 // mCanContainGroups member of the nsElementInfo struct to the corresponding
527 // GROUP_ values (OR'ed together).
528 // Testing containment then simply consists of checking whether the
529 // mCanContainGroups bitmask of an element and the mGroup bitmask of a
530 // potential child overlap.
532 #define GROUP_NONE 0
534 // body, head, html
535 #define GROUP_TOPLEVEL (1 << 1)
537 // base, link, meta, script, style, title
538 #define GROUP_HEAD_CONTENT (1 << 2)
540 // b, big, i, s, small, strike, tt, u
541 #define GROUP_FONTSTYLE (1 << 3)
543 // abbr, acronym, cite, code, datalist, del, dfn, em, ins, kbd, mark, rb, rp
544 // rt, rtc, ruby, samp, strong, var
545 #define GROUP_PHRASE (1 << 4)
547 // a, applet, basefont, bdo, br, font, iframe, img, map, meter, object, output,
548 // picture, progress, q, script, span, sub, sup
549 #define GROUP_SPECIAL (1 << 5)
551 // button, form, input, label, select, textarea
552 #define GROUP_FORMCONTROL (1 << 6)
554 // address, applet, article, aside, blockquote, button, center, del, dir, div,
555 // dl, fieldset, figure, footer, form, h1, h2, h3, h4, h5, h6, header, hgroup,
556 // hr, iframe, ins, main, map, menu, nav, noframes, noscript, object, ol, p,
557 // pre, table, section, ul
558 #define GROUP_BLOCK (1 << 7)
560 // frame, frameset
561 #define GROUP_FRAME (1 << 8)
563 // col, tbody
564 #define GROUP_TABLE_CONTENT (1 << 9)
566 // tr
567 #define GROUP_TBODY_CONTENT (1 << 10)
569 // td, th
570 #define GROUP_TR_CONTENT (1 << 11)
572 // col
573 #define GROUP_COLGROUP_CONTENT (1 << 12)
575 // param
576 #define GROUP_OBJECT_CONTENT (1 << 13)
578 // li
579 #define GROUP_LI (1 << 14)
581 // area
582 #define GROUP_MAP_CONTENT (1 << 15)
584 // optgroup, option
585 #define GROUP_SELECT_CONTENT (1 << 16)
587 // option
588 #define GROUP_OPTIONS (1 << 17)
590 // dd, dt
591 #define GROUP_DL_CONTENT (1 << 18)
593 // p
594 #define GROUP_P (1 << 19)
596 // text, whitespace, newline, comment
597 #define GROUP_LEAF (1 << 20)
599 // XXX This is because the editor does sublists illegally.
600 // ol, ul
601 #define GROUP_OL_UL (1 << 21)
603 // h1, h2, h3, h4, h5, h6
604 #define GROUP_HEADING (1 << 22)
606 // figcaption
607 #define GROUP_FIGCAPTION (1 << 23)
609 // picture members (img, source)
610 #define GROUP_PICTURE_CONTENT (1 << 24)
612 #define GROUP_INLINE_ELEMENT \
613 (GROUP_FONTSTYLE | GROUP_PHRASE | GROUP_SPECIAL | GROUP_FORMCONTROL | \
614 GROUP_LEAF)
616 #define GROUP_FLOW_ELEMENT (GROUP_INLINE_ELEMENT | GROUP_BLOCK)
618 struct nsElementInfo
620 #ifdef DEBUG
621 eHTMLTags mTag;
622 #endif
623 uint32_t mGroup;
624 uint32_t mCanContainGroups;
625 bool mIsContainer;
626 bool mCanContainSelf;
629 #ifdef DEBUG
630 #define ELEM(_tag, _isContainer, _canContainSelf, _group, _canContainGroups) \
631 { eHTMLTag_##_tag, _group, _canContainGroups, _isContainer, _canContainSelf }
632 #else
633 #define ELEM(_tag, _isContainer, _canContainSelf, _group, _canContainGroups) \
634 { _group, _canContainGroups, _isContainer, _canContainSelf }
635 #endif
637 static const nsElementInfo kElements[eHTMLTag_userdefined] = {
638 ELEM(a, true, false, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
639 ELEM(abbr, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
640 ELEM(acronym, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
641 ELEM(address, true, true, GROUP_BLOCK,
642 GROUP_INLINE_ELEMENT | GROUP_P),
643 ELEM(applet, true, true, GROUP_SPECIAL | GROUP_BLOCK,
644 GROUP_FLOW_ELEMENT | GROUP_OBJECT_CONTENT),
645 ELEM(area, false, false, GROUP_MAP_CONTENT, GROUP_NONE),
646 ELEM(article, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
647 ELEM(aside, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
648 ELEM(audio, false, false, GROUP_NONE, GROUP_NONE),
649 ELEM(b, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
650 ELEM(base, false, false, GROUP_HEAD_CONTENT, GROUP_NONE),
651 ELEM(basefont, false, false, GROUP_SPECIAL, GROUP_NONE),
652 ELEM(bdo, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
653 ELEM(bgsound, false, false, GROUP_NONE, GROUP_NONE),
654 ELEM(big, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
655 ELEM(blockquote, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
656 ELEM(body, true, true, GROUP_TOPLEVEL, GROUP_FLOW_ELEMENT),
657 ELEM(br, false, false, GROUP_SPECIAL, GROUP_NONE),
658 ELEM(button, true, true, GROUP_FORMCONTROL | GROUP_BLOCK,
659 GROUP_FLOW_ELEMENT),
660 ELEM(canvas, false, false, GROUP_NONE, GROUP_NONE),
661 ELEM(caption, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT),
662 ELEM(center, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
663 ELEM(cite, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
664 ELEM(code, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
665 ELEM(col, false, false, GROUP_TABLE_CONTENT | GROUP_COLGROUP_CONTENT,
666 GROUP_NONE),
667 ELEM(colgroup, true, false, GROUP_NONE, GROUP_COLGROUP_CONTENT),
668 ELEM(content, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT),
669 ELEM(data, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
670 ELEM(datalist, true, false, GROUP_PHRASE,
671 GROUP_OPTIONS | GROUP_INLINE_ELEMENT),
672 ELEM(dd, true, false, GROUP_DL_CONTENT, GROUP_FLOW_ELEMENT),
673 ELEM(del, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT),
674 ELEM(dfn, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
675 ELEM(dir, true, false, GROUP_BLOCK, GROUP_LI),
676 ELEM(div, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
677 ELEM(dl, true, false, GROUP_BLOCK, GROUP_DL_CONTENT),
678 ELEM(dt, true, true, GROUP_DL_CONTENT, GROUP_INLINE_ELEMENT),
679 ELEM(em, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
680 ELEM(embed, false, false, GROUP_NONE, GROUP_NONE),
681 ELEM(fieldset, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
682 ELEM(figcaption, true, false, GROUP_FIGCAPTION, GROUP_FLOW_ELEMENT),
683 ELEM(figure, true, true, GROUP_BLOCK,
684 GROUP_FLOW_ELEMENT | GROUP_FIGCAPTION),
685 ELEM(font, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
686 ELEM(footer, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
687 ELEM(form, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
688 ELEM(frame, false, false, GROUP_FRAME, GROUP_NONE),
689 ELEM(frameset, true, true, GROUP_FRAME, GROUP_FRAME),
690 ELEM(h1, true, false, GROUP_BLOCK | GROUP_HEADING,
691 GROUP_INLINE_ELEMENT),
692 ELEM(h2, true, false, GROUP_BLOCK | GROUP_HEADING,
693 GROUP_INLINE_ELEMENT),
694 ELEM(h3, true, false, GROUP_BLOCK | GROUP_HEADING,
695 GROUP_INLINE_ELEMENT),
696 ELEM(h4, true, false, GROUP_BLOCK | GROUP_HEADING,
697 GROUP_INLINE_ELEMENT),
698 ELEM(h5, true, false, GROUP_BLOCK | GROUP_HEADING,
699 GROUP_INLINE_ELEMENT),
700 ELEM(h6, true, false, GROUP_BLOCK | GROUP_HEADING,
701 GROUP_INLINE_ELEMENT),
702 ELEM(head, true, false, GROUP_TOPLEVEL, GROUP_HEAD_CONTENT),
703 ELEM(header, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
704 ELEM(hgroup, true, false, GROUP_BLOCK, GROUP_HEADING),
705 ELEM(hr, false, false, GROUP_BLOCK, GROUP_NONE),
706 ELEM(html, true, false, GROUP_TOPLEVEL, GROUP_TOPLEVEL),
707 ELEM(i, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
708 ELEM(iframe, true, true, GROUP_SPECIAL | GROUP_BLOCK,
709 GROUP_FLOW_ELEMENT),
710 ELEM(image, false, false, GROUP_NONE, GROUP_NONE),
711 ELEM(img, false, false, GROUP_SPECIAL | GROUP_PICTURE_CONTENT, GROUP_NONE),
712 ELEM(input, false, false, GROUP_FORMCONTROL, GROUP_NONE),
713 ELEM(ins, true, true, GROUP_PHRASE | GROUP_BLOCK, GROUP_FLOW_ELEMENT),
714 ELEM(kbd, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
715 ELEM(keygen, false, false, GROUP_FORMCONTROL, GROUP_NONE),
716 ELEM(label, true, false, GROUP_FORMCONTROL, GROUP_INLINE_ELEMENT),
717 ELEM(legend, true, true, GROUP_NONE, GROUP_INLINE_ELEMENT),
718 ELEM(li, true, false, GROUP_LI, GROUP_FLOW_ELEMENT),
719 ELEM(link, false, false, GROUP_HEAD_CONTENT, GROUP_NONE),
720 ELEM(listing, false, false, GROUP_NONE, GROUP_NONE),
721 ELEM(main, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
722 ELEM(map, true, true, GROUP_SPECIAL, GROUP_BLOCK | GROUP_MAP_CONTENT),
723 ELEM(mark, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
724 ELEM(marquee, false, false, GROUP_NONE, GROUP_NONE),
725 ELEM(menu, true, true, GROUP_BLOCK, GROUP_LI | GROUP_FLOW_ELEMENT),
726 ELEM(menuitem, false, false, GROUP_NONE, GROUP_NONE),
727 ELEM(meta, false, false, GROUP_HEAD_CONTENT, GROUP_NONE),
728 ELEM(meter, true, false, GROUP_SPECIAL, GROUP_FLOW_ELEMENT),
729 ELEM(multicol, false, false, GROUP_NONE, GROUP_NONE),
730 ELEM(nav, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
731 ELEM(nobr, false, false, GROUP_NONE, GROUP_NONE),
732 ELEM(noembed, false, false, GROUP_NONE, GROUP_NONE),
733 ELEM(noframes, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
734 ELEM(noscript, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
735 ELEM(object, true, true, GROUP_SPECIAL | GROUP_BLOCK,
736 GROUP_FLOW_ELEMENT | GROUP_OBJECT_CONTENT),
737 // XXX Can contain self and ul because editor does sublists illegally.
738 ELEM(ol, true, true, GROUP_BLOCK | GROUP_OL_UL,
739 GROUP_LI | GROUP_OL_UL),
740 ELEM(optgroup, true, false, GROUP_SELECT_CONTENT,
741 GROUP_OPTIONS),
742 ELEM(option, true, false,
743 GROUP_SELECT_CONTENT | GROUP_OPTIONS, GROUP_LEAF),
744 ELEM(output, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
745 ELEM(p, true, false, GROUP_BLOCK | GROUP_P, GROUP_INLINE_ELEMENT),
746 ELEM(param, false, false, GROUP_OBJECT_CONTENT, GROUP_NONE),
747 ELEM(picture, true, false, GROUP_SPECIAL, GROUP_PICTURE_CONTENT),
748 ELEM(plaintext, false, false, GROUP_NONE, GROUP_NONE),
749 ELEM(pre, true, true, GROUP_BLOCK, GROUP_INLINE_ELEMENT),
750 ELEM(progress, true, false, GROUP_SPECIAL, GROUP_FLOW_ELEMENT),
751 ELEM(q, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
752 ELEM(rb, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
753 ELEM(rp, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
754 ELEM(rt, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
755 ELEM(rtc, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
756 ELEM(ruby, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
757 ELEM(s, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
758 ELEM(samp, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
759 ELEM(script, true, false, GROUP_HEAD_CONTENT | GROUP_SPECIAL,
760 GROUP_LEAF),
761 ELEM(section, true, true, GROUP_BLOCK, GROUP_FLOW_ELEMENT),
762 ELEM(select, true, false, GROUP_FORMCONTROL, GROUP_SELECT_CONTENT),
763 ELEM(shadow, true, false, GROUP_NONE, GROUP_INLINE_ELEMENT),
764 ELEM(small, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
765 ELEM(source, false, false, GROUP_PICTURE_CONTENT, GROUP_NONE),
766 ELEM(span, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
767 ELEM(strike, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
768 ELEM(strong, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
769 ELEM(style, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF),
770 ELEM(sub, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
771 ELEM(sup, true, true, GROUP_SPECIAL, GROUP_INLINE_ELEMENT),
772 ELEM(table, true, false, GROUP_BLOCK, GROUP_TABLE_CONTENT),
773 ELEM(tbody, true, false, GROUP_TABLE_CONTENT, GROUP_TBODY_CONTENT),
774 ELEM(td, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT),
775 ELEM(textarea, true, false, GROUP_FORMCONTROL, GROUP_LEAF),
776 ELEM(tfoot, true, false, GROUP_NONE, GROUP_TBODY_CONTENT),
777 ELEM(th, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT),
778 ELEM(thead, true, false, GROUP_NONE, GROUP_TBODY_CONTENT),
779 ELEM(template, false, false, GROUP_NONE, GROUP_NONE),
780 ELEM(time, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
781 ELEM(title, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF),
782 ELEM(tr, true, false, GROUP_TBODY_CONTENT, GROUP_TR_CONTENT),
783 ELEM(track, false, false, GROUP_NONE, GROUP_NONE),
784 ELEM(tt, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
785 ELEM(u, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
786 // XXX Can contain self and ol because editor does sublists illegally.
787 ELEM(ul, true, true, GROUP_BLOCK | GROUP_OL_UL,
788 GROUP_LI | GROUP_OL_UL),
789 ELEM(var, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
790 ELEM(video, false, false, GROUP_NONE, GROUP_NONE),
791 ELEM(wbr, false, false, GROUP_NONE, GROUP_NONE),
792 ELEM(xmp, false, false, GROUP_NONE, GROUP_NONE),
794 // These aren't elements.
795 ELEM(text, false, false, GROUP_LEAF, GROUP_NONE),
796 ELEM(whitespace, false, false, GROUP_LEAF, GROUP_NONE),
797 ELEM(newline, false, false, GROUP_LEAF, GROUP_NONE),
798 ELEM(comment, false, false, GROUP_LEAF, GROUP_NONE),
799 ELEM(entity, false, false, GROUP_NONE, GROUP_NONE),
800 ELEM(doctypeDecl, false, false, GROUP_NONE, GROUP_NONE),
801 ELEM(markupDecl, false, false, GROUP_NONE, GROUP_NONE),
802 ELEM(instruction, false, false, GROUP_NONE, GROUP_NONE),
804 ELEM(userdefined, true, false, GROUP_NONE, GROUP_FLOW_ELEMENT)
807 bool
808 nsHTMLEditUtils::CanContain(int32_t aParent, int32_t aChild)
810 NS_ASSERTION(aParent > eHTMLTag_unknown && aParent <= eHTMLTag_userdefined,
811 "aParent out of range!");
812 NS_ASSERTION(aChild > eHTMLTag_unknown && aChild <= eHTMLTag_userdefined,
813 "aChild out of range!");
815 #ifdef DEBUG
816 static bool checked = false;
817 if (!checked) {
818 checked = true;
819 int32_t i;
820 for (i = 1; i <= eHTMLTag_userdefined; ++i) {
821 NS_ASSERTION(kElements[i - 1].mTag == i,
822 "You need to update kElements (missing tags).");
825 #endif
827 // Special-case button.
828 if (aParent == eHTMLTag_button) {
829 static const eHTMLTags kButtonExcludeKids[] = {
830 eHTMLTag_a,
831 eHTMLTag_fieldset,
832 eHTMLTag_form,
833 eHTMLTag_iframe,
834 eHTMLTag_input,
835 eHTMLTag_select,
836 eHTMLTag_textarea
839 uint32_t j;
840 for (j = 0; j < ArrayLength(kButtonExcludeKids); ++j) {
841 if (kButtonExcludeKids[j] == aChild) {
842 return false;
847 // Deprecated elements.
848 if (aChild == eHTMLTag_bgsound) {
849 return false;
852 // Bug #67007, dont strip userdefined tags.
853 if (aChild == eHTMLTag_userdefined) {
854 return true;
857 const nsElementInfo& parent = kElements[aParent - 1];
858 if (aParent == aChild) {
859 return parent.mCanContainSelf;
862 const nsElementInfo& child = kElements[aChild - 1];
863 return (parent.mCanContainGroups & child.mGroup) != 0;
866 bool
867 nsHTMLEditUtils::IsContainer(int32_t aTag)
869 NS_ASSERTION(aTag > eHTMLTag_unknown && aTag <= eHTMLTag_userdefined,
870 "aTag out of range!");
872 return kElements[aTag - 1].mIsContainer;